This article was written by my good friend and former colleague of fred, who works in a team of programmers on a large and complex project, which should work 24x7. If someone decides to invite him to Habr - I will gladly send him an email at habraposhta. Suggestions and comments are welcome, and I pledge to convey the answers of the author to the best of my ability.
I had a thought to launch a CI server for a working project for a long time. The base of unit tests is already quite impressive, and the number of people in the project has increased slightly. It would be possible to observe how the test coverage of the code changes and the coding standards are followed. And punish the guilty. Just kidding
Some time ago, the book
Continuous Integration was read
. Improving software quality and reducing risk and
Quality Assurance Tools for PHP , which served as the starting point.
The server of continuous integration with some frequency polls the project repository for changes. If changes are found, then it
- generates documentation in phpdoc format
- checks code for compliance with coding standards
- runs tests
- based on the results of tests and code analysis, it builds metrics, as well as the dynamics of changes of these metrics
- notifies all interested in the case of successful or unsuccessful assembly
CruiseControl was chosen as an integration server along with a plugin for php -
http://phpundercontrol.org/ .
Installing CruiseControl on a debian-like machine
If you put CruiseControl in another operating system, then the actions are approximately the same: install java, unpack CruiseControl, make scripts to start and stop the server.
To run the CruiseControl itself, we need to install a Java machine:
')
sudo apt-get install sun-java6-bin
and set it in environment variables:
export JAVA_HOME = / usr / lib / jvm / java-6-sun
First, create a user on the system, from which CruiseControl will be launched:
sudo adduser cruisecontrol
Unfortunately, I haven’t found any fresh .deb packages with CruiseControl, but I haven’t gotten my hands right yet, so download the latest version and unpack it into the directory at will:
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
and make the owner of our user directory:
s.galkin@java-galkin: / opt $ sudo chown cruisecontrol / opt / cruisecontrol -R
Take the shell script from
the CruiseControl installation guide in unix. and save it to the /etc/init.d/cruisecontrol file.
Now you can run CC and check that it works:
s.galkin@java-galkin: / opt $ sudo /etc/init.d/cruisecontrol start
The web interface should be accessible at
http: // yourhostname: 8080 /You can stop the server accordingly:
s.galkin@java-galkin: / opt $ sudo /etc/init.d/cruisecontrol stop
Go to the installation of php-parts.
You probably already have xdebug and pear, but if not:
sudo apt-get install php5-xdebug
sudo apt-cache search php pear
If you have a lot of code and tests, you should increase the amount of allocated php-process memory:
max_execution_time = 0
memory_limit = 512M
Installing php-packages
Now install the packages for documenting, testing, and evaluating the code and phpUnderControl itself:
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
If you put everything on a separate machine, do not forget to put all the libraries on which your project depends. At the same time, you can check that phpunit, phpcs, phpdoc are executed with the parameter sets specified in build.xml.
Make phpUnderControl with the CruiseControl plugin, specifying the directory where the last one is installed as an argument:
phpuc install / opt / cruisecontrol
Project Setup
Open the config.xml configuration file located in the root directory of CruiseControl and replace it with the following:
<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 >
I transferred all the paths to files and directories to variables at the very top of the configuration file so that if necessary it was easy to fix them in one place.
As a collector, I used ant (it is explicitly specified in config.xml in the node
/ cruisecontrol / project / schedule ), which I am slightly familiar with. If your project already uses phing (which is more logical for php projects than ant), then CruiseControl perfectly supports it.
In the
/ cruisecontrol / project / publishers node, in addition to processing test results and metrics, you can also send in case of unsuccessful assemblies (possibly successful too) notifications to interested persons by mail / to gabber.
Detailed description of the configuration file can be found at the link
CruiseControl Configuration ReferenceMoving on to the project build script itself.
<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 >
All path variables were transferred from 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 >
I decided to immediately put the build.xml in the repository to make it easier to update in the future.
Do not forget to correctly specify the path to it from config.xml (buildfile attribute in node
/ cruisecontrol / project / schedule / ant )
Now we
upload your project to the
/ opt / cruisecontrol / projects / yourProProNameName / source folder from the repository and launch CruiseControl. Everything!
Existing problems
In the presence of warning / notice, Codesniffer does not suppress them and as a result, an invalid xml file is obtained, which CruiseControl cannot read. In general, for any strange behavior, you can see the log
/opt/cruisecontrol/cruisecontrol.shTotal
As a result of the continuous integration server, we get the following benefits:
- all developers are timely notified of unsuccessful construction
- dynamics of changes in the number of tests
- dynamics of changes in the percentage of code covered by tests
- analysis of test coverage of specific classes
- coding standards violation
- current documentation on internal api
- static code analysis: too long methods, too complex conditional logic, ...
useful links
- The CruiseControl Best Practices Series
- Quality Assurance Tools for PHP
- RunningCruiseControlFromUnixInit
- Setting up phpUnderControl
- phpUnderControl
- CruiseControl Configuration Reference
- Run CodeSniffer with coding standards in a non-standard directory