Эту статью написал мой добрый приятель и бывший коллега fred, работающий в команде программистов над большим и сложным проектом, который должен работать 24х7. Если кто-то решит пригласить его на хабр — с удовольствием вышлю его email по хабрапочте. Пожелания и комментарии приветствуются, а я обязуюсь передавать ответы автора в меру своих возможностей.
Меня давно посещала мысль запустить CI-сервер для рабочего проекта. База модульных тестов уже достаточно внушительных размеров, а количество людей в проекте немного увеличилось. Можно было бы наблюдать за тем, как изменяется покрытие тестами кода и соблюдаются стандарты кодирования. И наказывать провинившихся. Шучу.
Некоторое время назад была прочитана книжка Непрерывная интеграция. Улучшение качества программного обеспечения и снижение риска и статья Quality Assurance Tools for PHP, которые и послужили отправной точкой.
Сервер непрерывной интеграции с некоторой периодичностью опрашивает репозиторий проекта на наличие изменений. Если изменения найдены, то он
В качество интеграционного сервера был выбран CruiseControl вместе с плагином для php — http://phpundercontrol.org/.
Если вы ставите CruiseControl в другой операционной системе, то действия примерно такие-же: установить java, распаковать CruiseControl, сделать сценарии запуска и остановки сервера.
Для запуска самого CruiseControl нам надо установить Java-машину:
и прописать её в переменных окружения:
Сначала создаем в системе пользователя, из-под которого будет запускаться CruiseControl:
К сожалению свежих .deb пакетов с CruiseControl я не нашел, а у самого пока руки не дошли сделать, поэтому качаем последнюю версию и распаковываем в директорию по желанию:
и делаем владельцем директории нашего пользователя:
Берем шелл-скрипт из руководства по установке CruiseControl в unix. и сохраняем его в файл /etc/init.d/cruisecontrol .
Теперь можно запустить CC и проверить, что он работает:
Веб-интерфейс должен быть доступен по адресу http://yourhostname:8080/
Остановить сервер можно соответственно:
Переходим к установке php-части.
У вас наверняка уже стоит xdebug и pear, но если нет:
Если у вас достаточно много кода и тестов, стоит увеличить количество выделяемой php-процессу памяти:
Теперь устанавливаем пакеты для документирования, тестирования, и оценки кода и сам phpUnderControl:
Если вы ставите все на отдельной машине, то не забудьте поставить все библиотеки, от которых зависит ваш проект. Заодно можно проверить, что phpunit, phpcs, phpdoc выполняются с теми наборами параметров, которые указаны в build.xml.
Делаем phpUnderControl плагином CruiseControl’а, указав в качестве аргумента директорию, где установлен последний:
Открываем конфигурационный файл config.xml, находящийся в корневой директории CruiseControl и заменяем его следующим:
Я перенес все пути к файлам и директориям в переменные на самый верх конфигурационного файла, чтобы при надобности было легко их поправить в одном месте.
В качестве сборщика я использовал ant (он явно указан в config.xml в ноде /cruisecontrol/project/schedule), который мне немного знаком. Если ваш проект уже использует phing (что более логично для php-проектов, чем ant), то CruiseControl его отлично поддерживает.
В ноде /cruisecontrol/project/publishers можно кроме обработки результатов тестов и метрик можно также посылать в случае неудачных сборок (возможно и удачных тоже) уведомления заинтересованным лицам на почту/в жаббер.
Подробное описание конфигурационного файла можно найти по ссылке CruiseControl Configuration Reference
Переходим к самому сценарию сборки проекта.
Все переменные с путями были переданы из config.xml
Я решил сразу положить build.xml в репозиторий, чтобы в будущем его было легче обновлять.
Не забудьте правильно указать путь к нему из config.xml (атрибут buildfile в ноде /cruisecontrol/project/schedule/ant)
Теперь выгружаем в папку /opt/cruisecontrol/projects/yourProjectName/source из репозитория ваш проект и запускаем CruiseControl. Все!
При наличии warning/notice, Codesniffer не подавляет их и в результате получается невалидный xml файл, который CruiseControl не может прочитать. Вообще при любом непонятном поведении можно посмотреть лог /opt/cruisecontrol/cruisecontrol.sh
В результате работы сервера непрерывной интеграции мы получаем следующие преимущества:
Меня давно посещала мысль запустить CI-сервер для рабочего проекта. База модульных тестов уже достаточно внушительных размеров, а количество людей в проекте немного увеличилось. Можно было бы наблюдать за тем, как изменяется покрытие тестами кода и соблюдаются стандарты кодирования. И наказывать провинившихся. Шучу.
Некоторое время назад была прочитана книжка Непрерывная интеграция. Улучшение качества программного обеспечения и снижение риска и статья Quality Assurance Tools for PHP, которые и послужили отправной точкой.
Сервер непрерывной интеграции с некоторой периодичностью опрашивает репозиторий проекта на наличие изменений. Если изменения найдены, то он
- генерирует документацию в формате phpdoc
- проверяет код на соответствие стандартам кодирования
- запускает тесты
- по результатам тестов и анализа кода строит метрики, а также динамику изменений этих метрик
- уведомляет всех заинтересованных в случае успешной или неуспешной сборки
В качество интеграционного сервера был выбран CruiseControl вместе с плагином для php — http://phpundercontrol.org/.
Установка CruiseControl на debian-like машине
Если вы ставите CruiseControl в другой операционной системе, то действия примерно такие-же: установить java, распаковать CruiseControl, сделать сценарии запуска и остановки сервера.
Для запуска самого CruiseControl нам надо установить Java-машину:
sudo apt-get install sun-java6-bin
и прописать её в переменных окружения:
export JAVA_HOME=/usr/lib/jvm/java-6-sun
Сначала создаем в системе пользователя, из-под которого будет запускаться CruiseControl:
sudo adduser cruisecontrol
К сожалению свежих .deb пакетов с CruiseControl я не нашел, а у самого пока руки не дошли сделать, поэтому качаем последнюю версию и распаковываем в директорию по желанию:
s.galkin@java-galkin:/opt$ sudo wget http://sourceforge.net/projects/cruisecontrol/files/CruiseControl/2.8.2/cruisecontrol-bin-2.8.2.zip/download s.galkin@java-galkin:/opt$ sudo unzip cruisecontrol-bin-2.8.2.zip s.galkin@java-galkin:/opt$ sudo mv cruisecontrol-bin-2.8.2 cruisecontrol
и делаем владельцем директории нашего пользователя:
s.galkin@java-galkin:/opt$ sudo chown cruisecontrol /opt/cruisecontrol -R
Берем шелл-скрипт из руководства по установке CruiseControl в unix. и сохраняем его в файл /etc/init.d/cruisecontrol .
Теперь можно запустить CC и проверить, что он работает:
s.galkin@java-galkin:/opt$ sudo /etc/init.d/cruisecontrol start
Веб-интерфейс должен быть доступен по адресу http://yourhostname:8080/
Остановить сервер можно соответственно:
s.galkin@java-galkin:/opt$ sudo /etc/init.d/cruisecontrol stop
Переходим к установке php-части.
У вас наверняка уже стоит xdebug и pear, но если нет:
sudo apt-get install php5-xdebug sudo apt-cache search php pear
Если у вас достаточно много кода и тестов, стоит увеличить количество выделяемой php-процессу памяти:
max_execution_time = 0 memory_limit = 512M
Установка php-пакетов
Теперь устанавливаем пакеты для документирования, тестирования, и оценки кода и сам phpUnderControl:
pear install PhpDocumentor pear install PHP_CodeSniffer pear channel-discover pear.phpunit.de pear install phpunit/PHPUnit pear channel-discover components.ez.no pear install --alldeps phpunit/phpUnderControl-beta
Если вы ставите все на отдельной машине, то не забудьте поставить все библиотеки, от которых зависит ваш проект. Заодно можно проверить, что phpunit, phpcs, phpdoc выполняются с теми наборами параметров, которые указаны в build.xml.
Делаем phpUnderControl плагином CruiseControl’а, указав в качестве аргумента директорию, где установлен последний:
phpuc install /opt/cruisecontrol
Настройка проекта
Открываем конфигурационный файл config.xml, находящийся в корневой директории CruiseControl и заменяем его следующим:
<cruisecontrol>
<property name="ant.dir" value="apache-ant-1.7.0"/>
<property name="work.dir" value="/opt/cruisecontrol"/>
<property name="logs.dir" value="${work.dir}/logs/${project.name}" />
<property name="artifacts.dir" value="${work.dir}/artifacts/${project.name}" />
<property name="project.dir" value="${work.dir}/projects/${project.name}" />
<property name="project.code.dir" value="${project.dir}/source"/>
<property name="project.build.dir" value="${project.dir}/build"/>
<property name="project.logs.dir" value="${project.build.dir}/logs"/>
<property name="project.coverage.dir" value="${project.build.dir}/coverage"/>
<property name="project.api.dir" value="${project.build.dir}/api"/>
<property name="project.build.file" value="${project.code.dir}/misc/ci/build.xml"/>
<property name="status.file" value="${logs.dir}/${project.name}/status.txt"/>
<project name="yourProjectName">
<listeners>
<currentbuildstatuslistener file="${status.file}"/>
</listeners>
<bootstrappers>
<svnbootstrapper localWorkingCopy="${project.code.dir}"/>
</bootstrappers>
<modificationset>
<svn localWorkingCopy="${project.code.dir}"/>
</modificationset>
<schedule interval="300">
<ant anthome="${ant.dir}" buildfile="${project.build.file}">
<property name="project.code.dir" value="${project.code.dir}/"/>
<property name="project.build.dir" value="${project.build.dir}"/>
<property name="project.logs.dir" value="${project.logs.dir}"/>
<property name="project.api.dir" value="${project.api.dir}"/>
<property name="project.coverage.dir" value="${project.coverage.dir}"/>
</ant>
</schedule>
<log dir="${logs.dir}">
<merge dir="${project.logs.dir}"/>
</log>
<publishers>
<currentbuildstatuspublisher file="${logs.dir}/buildstatus.txt"/>
<artifactspublisher dir="${project.coverage.dir}" dest="${artifacts.dir}" subdirectory="coverage" />
<artifactspublisher dir="${project.api.dir}" dest="${artifacts.dir}" subdirectory="api" />
<execute command="phpuc graph ${logs.dir} ${artifacts.dir}" />
</publishers>
</project>
</cruisecontrol>
Я перенес все пути к файлам и директориям в переменные на самый верх конфигурационного файла, чтобы при надобности было легко их поправить в одном месте.
В качестве сборщика я использовал ant (он явно указан в config.xml в ноде /cruisecontrol/project/schedule), который мне немного знаком. Если ваш проект уже использует phing (что более логично для php-проектов, чем ant), то CruiseControl его отлично поддерживает.
В ноде /cruisecontrol/project/publishers можно кроме обработки результатов тестов и метрик можно также посылать в случае неудачных сборок (возможно и удачных тоже) уведомления заинтересованным лицам на почту/в жаббер.
Подробное описание конфигурационного файла можно найти по ссылке CruiseControl Configuration Reference
Переходим к самому сценарию сборки проекта.
<project name="yourProjectName" default="build">
<target name="prepare">
<delete dir="${project.build.dir}"/>
<mkdir dir="${project.logs.dir}"/>
</target>
<target name="phpdoc">
<exec executable="phpdoc" dir="${project.build.dir}" failonerror="false">
<arg line="
-o HTML:frames:DOM/earthli
-ti '${ant.project.name} documentation'
-q
-t ${project.api.dir}
-d ${project.code.dir}
"/>
</exec>
</target>
<target name="phpcs">
<exec executable="phpcs" dir="${project.build.dir}" failonerror="false" output="${project.logs.dir}/checkstyle.xml">
<arg line="
--report=checkstyle
--standard=PEAR
${project.code.dir}
"/>
</exec>
</target>
<target name="phpunit">
<exec executable="phpunit" dir="${project.build.dir}" failonerror="true">
<arg line="
--log-xml ${project.logs.dir}/phpunit.xml
--log-pmd ${project.logs.dir}/phpunit.pmd.xml
--log-metrics ${project.logs.dir}/phpunit.metrics.xml
--coverage-xml ${project.logs.dir}/phpunit.coverage.xml
--coverage-html ${project.coverage.dir}
phpucAllTests ${project.code.dir}/utests/AllTests.php
"/>
</exec>
</target>
<target name="build" depends="prepare,phpdoc,phpcs,phpunit"/>
</project>
Все переменные с путями были переданы из config.xml
<ant anthome="${ant.dir}" buildfile="${project.build.file}">
<property name="project.code.dir" value="${project.code.dir}/"/>
<property name="project.build.dir" value="${project.build.dir}"/>
<property name="project.logs.dir" value="${project.logs.dir}"/>
<property name="project.api.dir" value="${project.api.dir}"/>
<property name="project.coverage.dir" value="${project.coverage.dir}"/>
</ant>
Я решил сразу положить build.xml в репозиторий, чтобы в будущем его было легче обновлять.
Не забудьте правильно указать путь к нему из config.xml (атрибут buildfile в ноде /cruisecontrol/project/schedule/ant)
Теперь выгружаем в папку /opt/cruisecontrol/projects/yourProjectName/source из репозитория ваш проект и запускаем CruiseControl. Все!
Существующие проблемы
При наличии warning/notice, Codesniffer не подавляет их и в результате получается невалидный xml файл, который CruiseControl не может прочитать. Вообще при любом непонятном поведении можно посмотреть лог /opt/cruisecontrol/cruisecontrol.sh
Итог
В результате работы сервера непрерывной интеграции мы получаем следующие преимущества:
- все разработчики своевременно уведомляются о неудачном построении
- динамика изменений количества тестов
- динамика изменений процента кода, покрытого тестами
- анализ покрытия тестами конкретных классов
- нарушение стандартов кодирования
- актуальную документацию по внутреннему api
- статический анализ кода: слишком длинные методы, слишком сложная условная логика, …