Люблю я всякие автоматизации процессов, вот дошла очередь и до генерации документации в проекте Bluz
Постановка задачи
Возникла необходимость генерировать и публиковать документацию к проекту Bluz, который размещён на GitHub, в автоматическом режиме, ибо “ручками” сие делать уже не с руки.
GitHub
В качестве хостинга документации будем использовать мощности самого GitHub’а, в частности – GitHub Pages, так что первое что потребуется сделать – создать репозиторий вида bluzphp/bluzphp.github.io, где bluzphp – имя организации (а может быть именем пользователя).
Travis
Данная статья предназначена для тех, кто уже знаком с Travis CI, но до генерации и автоматической публикации “руки не дошли”, так что руководство для начинающих вам надо будет поискать на другом ресурсе, хотя в дальнейшем может появиться и тут ;)
Если же с английским вы дружны, то лучше чем официальное руководство трудно будет что-то найти.
TravisCI уже использовался в проекте, так что осталось дело за малым – генерировать документацию, и заливать её в репозиторий на GitHub.
Для генерации документации будем использовать PHPDocumentor, добавить его в сборку можно двумя способами – используя composer – позволяет контролировать версию, но с зависимостями могут возникнуть проблемы, да и медленно это – для данного варианта необходимо добавить в composer.json
следующую зависимость:
1 2 3 4 5 | { "require-dev" : { "phpdocumentor/phpdocumentor" : "2.*" } } |
как альтернатива – скачивать phar-архив:
1 | wget http: //phpdoc .org /phpDocumentor .phar |
Затем вносим изменения в файл .travis.yml
, это будет выглядеть следующим образом (если установка была через composer):
1 2 3 4 | before_script: - mkdir docs after_script: - php vendor /bin/phpdoc -d . /src -t . /docs |
или так (если использовался phar архив):
1 2 3 4 5 | before_script: - mkdir docs after_script: - wget http: //phpdoc .org /phpDocumentor .phar - php phpDocumentor.phar -d . /src -t . /docs |
Директорию
docs
можно создать “на лету” как в данном примере, или создать её в самом репозитории как это сделано в моём проекте
PHPDocumentor
После запуска билда в папке docs
будет создан статический сайт, который будет выглядеть как-то так (шаблон clean):
Для более тонкой настройки можно использовать конфигурационный файл phpdoc.xml
, приведу пример моей конфигурации с пояснениями:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <? xml version = "1.0" encoding = "UTF-8" ?> < phpdoc > <!-- Название проекта --> < title >Bluz</ title > < parser > <!-- Рабочая директория, тут будет хранится кеш --> < target >phpdoc</ target > < encoding >utf8</ encoding > < markers > < item >TODO</ item > < item >FIXME</ item > </ markers > < extensions > < extension >php</ extension > < extension >phtml</ extension > </ extensions > < default-package-name >Bluz</ default-package-name > </ parser > < transformer > <!-- Директория, где будет конечный результат генерации --> < target >docs</ target > </ transformer > < transformations > <!-- Шаблон на выбор: responsive, responsive-twig, checkstyle, clean, zend, new-black --> < template name = "responsive-twig" /> </ transformations > < files > <!-- Директория с исходным кодом --> < directory >src</ directory > </ files > </ phpdoc > |
GitHub
Так, документация есть, теперь её необходимо залить в репозиторий bluzphp/bluzphp.github.io. Для этого нам потребуется создать Access Token в своём профиле на GitHub:
Скопируйте его и никому не показывайте, если есть сомнения, что кто-то посягнул на него – перегенируйте токен заново.
Travis
Возвращаемся к Travis – теперь нам нужно установить консольное приложение travis, и с его помощью мы закодируем наш токен, чтобы никто его не смог подсмотреть в нашем конфигурационном файле:
1 2 | gem install travis travis encrypt GITHUB_TOKEN=secretvalue -r bluzphp /framework |
Полученный таким образом ключ необходимо добавить в ваш конфигурационный файл travis.yml
:
1 2 3 | env : global: - secure: "encryptedsecretvalue" |
Подробнее разобраться в происходящей тут магии вам поможет официальная документация по шифрованию ключей и переменным окружения
Если вдруг вы не можете понять, почему не устанавливаются глобально ваши зашифрованные переменные, то поясню для таких же невнимательных как и я – эта фича не работает для pull-запросов из форков
Идём дальше – теперь необходимо с использованием данного ключа обновить наш репозиторий. Для удобства всех дальнейших манипуляции с документацией был создан отдельный bash скрипт .travis.sh
, а в .travis.yml
оставлен лишь его запуск:
1 2 | after_script: - bash .travis.sh |
В bash скрипте:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # скачиваем phpDocumentor wget http: //phpdoc .org /phpDocumentor .phar # и запускаем его в "тихом" режиме # по умолчанию, он подхватит конфигурационный файл из корня проекта php phpDocumentor.phar --quiet # перенесём документацию в домашнюю директорию cp -R docs $HOME /docs-latest cd $HOME # настраиваем git git config --global user.email "travis@travis-ci.org" git config --global user.name "travis-ci" git config --global push.default simple # клонируем репозиторий страниц # вот тут мы и будем использовать наш шифрованный токен git clone --quiet https: // ${GITHUB_TOKEN}@github.com /bluzphp/bluzphp .github.io > /dev/null cd bluzphp.github.io # очищаем git rm -rf ./ > /dev/null # переносим документацию cp -Rf $HOME /docs-latest/ * ./ # добавляем все файлы в репозиторий git add -f . # комит git commit -m "PHPDocumentor (Travis Build: $TRAVIS_BUILD_NUMBER@$TRAVIS_TAG)" # пуш git push -fq origin > /dev/null |
Теперь чуть-чуть ограничим билды для которых будет обновляться документация следующим условием:
1 2 3 4 | if [ "$TRAVIS_REPO_SLUG" == "bluzphp/framework" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_PHP_VERSION" == "7.0" ]; then # всё что выше будет тут # ... fi |
Полный список переменных окружения на Travis есть в документации – Default Environment Variables
Всё готово – запускаем билд и смотрим на то, что получилось… и вот тут меня PHPDocumentor разочаровал, не знает он ничего о функциях с переменным числом аргументов (хотя если быть придирчивым, то проблема всё же в используемой версии библиотеки PHP-Parser), ошибки непонятные показывал, ну и ругался сильно, так что пошёл я за альтернативами, хотя вариант рабочий, и грех было о нём не рассказать.
PHPDox
Альтернатива нашлась сразу – PHPDox, скачиваем и поехали – начнём всё с конфигурации по умолчанию:
1 2 3 4 | # скачиваем phar архив wget http: //phpdox .de /releases/phpdox .phar # генерируем шаблонный конфиг php phpdox.phar --skel > phpdox.xml |
Полученный таким образом файл был добавлен в репозиторий проекта с мелкими правками. Для миграции CI на PHPDox потребовалось лишь слегка исправить .travis.sh
:
1 2 3 4 5 6 7 | # было wget http: //phpdoc .org /phpDocumentor .phar php phpDocumentor.phar --quiet # стало wget http: //phpdox .de /releases/phpdox .phar php phpdox.phar |
Результат получился вполне приемлемый, но PHPDox умеет большее – у него есть поддержка PHPUnit, PHPCS, PHPMD, PHPLoc и Git’a, так что я решил “прокачать” систему по полной.
Из замеченных недостатков – проблема с форматированием примеров кода записанного в doc-блоках
Git
Начну с самого простого – для добавление поддержки Git’a достаточно добавить соответствующий раздел в phpdox.xml
:
1 2 3 4 5 6 7 8 | < generator output = "${basedir}/docs" > < enrich base = "${basedir}" > < source type = "git" > < git binary = "/usr/bin/git" /> < history enabled = "true" limit = "15" cache = "${phpDox.project.workdir}/gitlog.xml" /> </ source > </ enrich > </ generator > |
В результате данной манипуляции в документации появится возможность просматривать историю изменений на основе комментариев к комитам (вы же пишите комментарии в каждом комите?).
PHPUnit
Тесты уже были в проекте, но я напомню как их подключать:
1 2 3 4 5 | { "require-dev" : { "phpunit/phpunit" : "~4.7" , } } |
Запуск тестов претерпел небольшие изменения – добавлена генерация покрытия в XML (на данный момент для PHP 7.0 xdebug автоматом не устанавливается, так что “если чё” – устанавливайте через PECL):
1 2 3 4 | before_script: - mkdir .reports script: - php vendor /bin/phpunit --configuration . /phpunit .xml.dist --coverage-clover=.reports /clover .xml --coverage-xml=.reports /coverage |
Конфигурация PHPDox так же была изменена:
1 2 3 4 5 6 7 | < generator output = "${basedir}/docs" > < enrich base = "${basedir}" > < source type = "phpunit" > < coverage path = ".reports/coverage" /> </ source > </ enrich > </ generator > |
PHP_CodeSniffer
Утилита анализа кода PHP_CodeSniffer уже использовалась в проекте, подключается она через composer:
1 2 3 4 5 | { "require-dev" : { "squizlabs/php_codesniffer" : "~2.3" } } |
Конфигурационный файл для проверки исходного кода и тестов на соответствие стандартам PSR1 и PSR2 выглядит вот так:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | <? xml version = "1.0" ?> < ruleset name = "Bluz" > < description >The coding standard for Bluz project</ description > <!-- где берём сорцы --> < file >./src</ file > < file >./tests/src</ file > <!-- игнорируем файлы в этих директориях --> < exclude-pattern >./tests/src/Fixtures</ exclude-pattern > < exclude-pattern >./tests/src/Common/Fixtures</ exclude-pattern > <!-- используем перечисленные стандарты --> < rule ref = "PSR1" /> < rule ref = "PSR2" /> </ ruleset > |
Запускаем:
1 2 3 | script: # Code style - php vendor /bin/phpcs --report=xml --report- file =.reports /phpcs .xml |
Подключаем результаты к PHPDox:
1 2 3 4 5 6 7 8 | < generator output = "${basedir}/docs" > < enrich base = "${basedir}" > <!-- PHP Code Sniffer --> < source type = "phpcs" > < file name = ".reports/phpcs.xml" /> </ source > </ enrich > </ generator > |
У меня билд “ломается”, если со стандартами есть проблемы, у вас же поведение CI может отличаться от моего, и тогда проблемные места будут отображены в документации (наверное :)
PHPLoc
Подключаем:
1 2 3 4 5 | { "require-dev" : { "phploc/phploc" : "*" } } |
Запускаем:
1 2 3 | script: # Lines of code and etc - php vendor /bin/phploc --log-xml=.reports /phploc .xml src |
Анализируем результаты в PHPDox:
1 2 3 4 5 6 7 8 | < generator output = "${basedir}/docs" > < enrich base = "${basedir}" > <!-- PHPLoc --> < source type = "phploc" > < file name = ".reports/phploc.xml" /> </ source > </ enrich > </ generator > |
PHPMessDetector
Подключаем:
1 2 3 4 5 | { "require-dev" : { "phpmd/phpmd" : "*" } } |
Запускаем:
1 2 3 | script: # Mess detection - php vendor /bin/phpmd . /src xml codesize,unusedcode,naming --reportfile .reports /phpmd .xml || true |
Тут пришлось пойти на хитрость – в случае если phpmd находит какие-то нарушения правил (к примеру слишком короткое имя переменной), то он вернёт exit code равный 2, что “ломает” билд, поэтому пришлось его заглушить, а все найденные проблемы “скормить” в документацию.
Анализируем результаты в PHPDox:
1 2 3 4 5 6 7 8 | < generator output = "${basedir}/docs" > < enrich base = "${basedir}" > <!-- PHPMessDetector --> < source type = "pmd" > < file name = ".reports/phpmd.xml" /> </ source > </ enrich > </ generator > |
Результат
Вот что у меня получилось в результате всех описанных манипуляций – смотрите и щупайте – http://bluzphp.github.io/.
Исходные файлы конфигураций доступны в репозитории на GitHub:
Один момент
Есть у данного решения один недостаток – нет в паблике версионирования документации (логи изменений не в счёт), чтобы решить данную проблему, нужно не так много – добавлять документацию для каждого тега (потребуется переменная $TRAVIS_TAG
), а в репозитории документации подхватывать все новые директории (вроде как функционала страниц хватит для этого), но это пока отложено в долгий ящик.
P.S. При написании данного руководства использовались материалы из статьи Publier automatiquement une PHPDoc sur GitHub avec Travis CI