In any decent sized project, the more times you commit code, the bigger the chance is that you will break something. Call it the law of unintended consequences or the butterfly effect, but the fact is that in complex systems, we cannot anticipate all the effects of a change. To deal with this we can either adopt the “release and pay” method of software control, or we can setup systems to help us find the consequences of our actions, so we that can correct them before they become a problem. We usually call these systems Continuous Integration systems.

In this article, we will look at how to set up a Continuous Integration system for PHP using phpUnderControl. After reading this article, you should have a good understanding of the pieces involved in a Continuous Integration system and be able to set up and run your own.

What is Continuous Integration ?

Thoroughly explaining Continuous Integration would require an article of its own or possibly a whole book. A good one-sentence definition of Integration is “Delivering a working system”, so Continuous Integration means “Continuously delivering a working system”; after every change made to the code the system can be proven to work.

Obviously, manually deploying and testing a system after every change is a very tedious job; however tedious jobs are what computers are really good at, and Continuous Integration systems are the programs that can do the work for us.

phpUnderControl ?

phpUnderControl is an add-on application for CruiseControl, a Continuous Integration system written in Java. It combines a number of popular PHP development tools like PHPUnit, phpDocumentor and PHP_CodeSniffer.

It can run unit tests, generate the API documentation, check the code for compliance with coding standards, and present you with a web interface to review the results. It can even turn on the lights.

Setting up phpUnderControl

For this tutorial, we will use Ubuntu Server as the reference platform, but the instructions should translate easily to other Linuxes, BSDs or even Windows. You should have a dedicated machine available, but if you don’t, it is possible can also install it on a machine used for other tasks.

We start with a basic Ubuntu Server installation, with the LAMP en OpenSSH servers selected.

As mentioned above, phpUnderControl is an add-on for CruiseControl, which is a Java application, so we install that first:

PHPUnit needs xDebug to generate the code coverage reports:

PEAR is the recommended way to install PHPUnit:

This also installs the php5-cli package which you need to run PHPUnit.

For the graphs we need the ezComponents Graph library:

Now we get to phpUnderControl itself:

phpUnderControl is still in beta, and by default PEAR does not install components in beta status, so we have to use config-set to make it install.

And, of course, cruisecontrol itself.

To unzip the file we need unzip:

Now we can install CruiseControl:

To make it easy to upgrade add a link to the current version:

Configure CruiseControl for phpUnderControl by running the install script:

Install the example project:

Subversion is not strictly necessary for cruisecontrol, but it is needed for the example below:

To start, CruiseControl needs the JAVA_HOME variable set. To set it add the lone below to your /etc/environment.

Additionally, to set JAVA_HOME in your current shell, execute the following command.

We are now ready to start CruiseControl:

If you use the -E option, you retain your own environment variables. You need the JAVA_HOME for cruisecontrol.sh to work properly. You can also run cruisecontrol.sh as a regular user, but then you have to make sure you own the whole CruiseControl tree: (Don’t do this while CruiseControl is still running.)

The -R option makes the command recursive. This ensures that the ownership of the link is changed along with all files beneath it. The -L options ensures that if there are symbolic links to directories that they are traversed also.

Configuring phpUnderControl for a sample project.

We can now fire up a browser and go to http://[ip-address of ci server]:8080/cruisecontrol and it should show something like this:

phpUnderControl Startup Screen

Don’t worry if you see error messages in the ‘coverage’ and ‘metrics’ tags about missing files in the ‘artifacts’ directory. The configuration of the installed example project is not quite correct. You can find a fix here.

We can now stop CruiseControl with:

Setting up a new project

Now you’re ready to set up a new project. In this example I will assume that you have used my small sample project called my_project. I have described how to set up that small project in an appendix after the end of this tutorial. The project resides in a local SVN repository: /home/[your username]/svn/my_project/trunk.

In order to add a new project to CruiseControl you have to do three main things:

  • Create a project configuration.
  • Set up a directory structure.
  • Add the project to the main configuration.

Cruisecontrol projects live in the subdirectory projects/ in the cruisecontrol/ directory, so the first step is to create a project directory:

Then make a build.xml file there with the following content:

The structure of a project section of build.xml is always the same, a target element with a name attribute that contains an exec element.

The exec element has an executable attribute that points to the program to run and a dir attribute that determines the directory in which the program is executed. Within the exec element is an arg element that holds the command line argument.

So the checkout tagged in the exec element below

is equivalent to:

A detailed description of the build.xml file can be found here.

This file lists all the steps needed to get a working system to test. Each step is called a ‘target’, and a target can depend on other targets to be finished before they can be executed.

The first step is to update the code:

This will run ‘svn update’ in the projects/my_project/source directory. To get our project files there, we need to a checkout first:

You can now test your build.xml file:

Ant is the program that actually runs the builds. The output should look like this:

Buildfile: build.xml

Now we can add the other targets:

PhpDocumentor:

An explanation for the various arguments can be found by typing

The -tb argument makes sure phpdoc uses a template that looks good with phpUnderControl. Phpdoc stores its output in the build/api directory, which you will have to create manually in the my_project/ directory.

And as with the checkout target, you can check if it is correct by using Ant again:

PHPUnit:

This target puts its output in build/logs and build/coverage, which also have to be created manually:

The failonerror=on is needed to signal a failed build when phpunit returns an error. (It returns an error whenever it detects failed test). The command line arguments for PHPUnit are covered extensively in the PHPUnit documentation.

PhpCodeSniffer:

The command line arguments are documented in the php-codesniffer manual. A particularly useful parameter is –ignore, which gives you the option to ignore certain files when sniffing the code. Very useful if you have included external code which does not comply to your standards.

Putting the build.xml file together

Now we need to combine the targets to one target

And to make sure the build target is the default target, we add the default attribute:

The final build.xml file should look like this:

Configuring CruiseControl

The central configuration file for CruiseControl is config.xml, which in our example can be found in /opt/cruisecontrol/config.xml For a detailed overview of the options available for this file, check the Cruisecontrol documentation. I am going to configure our project so that it periodically runs the build, even if nothing has changed.

First, rename the old config.xml file and replace it with an empty config.xml file:

The buildafterfailed=false attribute tells CruiseControl not to attempt a build if the previous one has failed and no changes have been found. If your tests depend on an external source that can fail, like a SOAP service or a database, it might be useful to set this value to ‘true’.

Then add the modification set:

The modificationset element lists the checks to find if anything has changed in the project. In this case we don’t check and always build.

Now we tell CruiseControl what to do:

In the schedule element is a list with tasks to do on each build. In this case, run Ant on the build.xml file we just created. The ant element also has an optional target attribute, where you can choose the target to build. The interval attributes specifies how many seconds to wait between builds. To enable the front-end to show the status of the build, we have to add a listener:

The listener element holds elements that can communicate the status of the build to other parts of the system. In this case the currentbuildstatus puts the status in an HTML snippet that the CruiseControl webserver can read.

Now we need a place to put all the information gathered during the build process:

To publish the results of the build we need a publisher:

The two arifactpublishers take the information from the build and puts them in a publicly accessible directory. The first one takes the files from build/api, which holds the api documentation generated by phpDocumentor. The second one is for the code coverage information. The execute element generates the nice-looking graphs for your manager.

The total config.xml file should look like this:

You can now test your phpUndercontrol system.

To start CruiseControl:

And to stop it:

If you start up your browser go to http://[ip-address of ci server]:8080/cruisecontrol And click on the my_project link, you should see something similar to this:

phpUnderControl My Project Overview

Now we’re going to change the configuration file to only do the build after we have made change in the code.

To enable CruiseControl to check SVN we first have to add a plugin to the project:

And change the modification set:

The quietperiod attribute tells CruiseControl to wait with running a build for a minute after the last change to the repository. This gives you the opportunity to commit parts to the code without running the risk of breaking the build while you are still busy committing.

The final config.xml file should look like this:

If you now run CruiseControl for a while, you will see that the last build time will not change. When you make a change to the code, commit it and wait for a minute or two, you will see that a new build has been run.

Two nice features

In this section I will briefly describe two of the very many nice features available in CruiseControl, sending out an email when things break, and setting up and tearing down a database.

Sending mails when things break

You can make CruiseControl send an email on build failure by adding a publisher to config.xml:

This sends out an email to you and your boss when the build breaks and one when it’s fixed.

Setting up a database

CruiseControl can set up and tear down a database for you. Below the set-up of the database. The tear-down is left as an exercise.

Simply add a new target:

And add the target to de dependencies of the build target:

This uses the java mysql library, so we have to install that:

To make the target work you also have to add a db-create.sql file to your project. I used this for the example:

And you can check if it is working by calling the target from Ant:

In this example we hardcode the classpath in the target, but you can also add the CLASSPATH environment variable.

See the MySQL documentation for details.

Conclusion

You now have a working Continuous Integration system. Systems like this always take time to get setup and working properly. Now however, you have a powerful tool that will help you create better software faster. The return on investment in your time will be stable code and your client’s confidence in your work. Go and play around with phpUnderControl and discover more of its features and uses; spend the time to understand the system and customize it for your specific needs. The time you spend will be well worth it when you go to roll your next version into production.

If you are interested in Continuous Integration and want to read more about it, Continuous Integration by Paul M. Duval is a very good book on the subject.

 

 

 

Appendix: Setting up a small project SVN repository

Here is a quick way to set up a small SVN repository with two a small PHP class and a testclass. First, create a svn repository in your own home directory:

Put these two files in my_project/trunk.

Then import the files to your newly created repository:

And check if your repository works:

You should now have a directory ‘source’ in your home directory with both .php files.