Jenkins CI: An Introduction for PHP Developers

Many of the projects I work on, are tied to some simple home-brew deployment and integrity checks (which I recently discussed at PHP North-East: Automated Deployment talk). These are some nice, simple scripts which quick take code out of version control, move things around, apply database schema changes, and deploy.

For the projects I tend to work on, this approach works well, however these techniques can be further enhanced, by being combined with a Continuous Integration service, such as Jenkins CI. This allows continuous quality control checks to be made against a code base, and allows for deployment scripts to be executed provided a build was successful, and depending on the output of these quality control checks, the build can be marked as failed, stable or unstable.

Through this blog post, I'll go through the steps involved in installing Jenkins, and configuring it with:

  • Ant build scripts
  • SimpleTest
  • PHPDocumentor
  • PHP Code Sniffer

I'm writing this blog entry primarily for two reasons: first in the hope that others find it useful; and secondly so I can remember what to do next time I set it up, or run into one of the problems I've mentioned below!

Install Jenkins

Most of the servers I manage and work with on a day-to-day basis are Ubuntu, so the instructions below are for Ubuntu.

First, we need to get the key for the package repository.

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -

Then we need to edit our sources list

sudo nano /etc/apt/sources.list

and make it aware of the Jenkins repository, by adding the following to the bottom of the file

deb http://pkg.jenkins-ci.org/debian binary/

Next we update the package list

sudo apt-get update

And finally, install Jenkins

sudo apt-get install jenkins

Now that Jenkins is installed, we can access it: http://yourserverurl:8080

Creating your first "job"

Since we will go through more of the build process later, all we need to do is click New Job, enter a name, select free-style software project, and then tell Jenkins where that project lives in version control, and what our login details are.

Within version controlled code, it seems common place / best practice to create a folder called build containing as much of your build focused files / code as possible.

Build with ant

Apache Ant is a tool for performing application builds, allowing the build process to be defined using XML. Ant can be used with Jenkins CI to accomplish a range of different things; so far, I've primarily used it to create, delete and move files around, run various commands during the build process, and to specify the ordering of these tasks. I hadn't used Ant for about 4 years when I used it a little with Java, and I've got to say, I wish I'd kept using it - its a great tool!

Enabling Ant

By default (or at least for my server) Jenkins isn't setup to use Ant. You can point it to an Ant script, but it won't run because it doesn't have Ant installed. To setup Ant, we simply go to Manage Jenkins --> Configure System, enter a name under Ant, set it to Install automatically, and select the version to install, and click Save.

When our build process is next run, Ant will be installed automatically.

Ant build scripts

If we tell Jenkins to Invoke Ant as part of the build process, it will automatically look for build.xml in our source code, and use that; unless we tell it otherwise of course. In other areas of this blog post I'll talk about getting PHPDocumentor and PHP CodeSniffer working with Ant, so for now I'll just focus on the file and folder manipulation. Ant is extremely capable, and there are loads of things it can do - so be sure to check out the manual for more details (http://ant.apache.org/manual/index.html).

For my typical projects, the first stage is to remove any existing PHPDocumentation, remove any existing test results (these are left over from previous builds unless we instructed Jenkins to revert a workspace before doing an update and a new build), recreate these folders, rename the configuration file containing database and other config details related to the CI server to be the main config file (after a build tests correctly, I then have a second job to rebuild the same project but swap in different configuration files, and move them onto a server once the build is complete), and finally move a live file (containing a file path constant) out of the way, and replace it with one containing settings for the CI server.

These two files are needed by my code to run the unit tests, the first so it can connect to the database, and the second so that they work correctly from the command line. With all of this in place, the build.xml file looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<project name="My Project" default="build" basedir=".">
 <property name="source" value="trunk"/>

 <target name="prepstructure" description="Prepare the structure of the codebase">
  <delete dir="${basedir}/build/docs"/>
  <delete dir="${basedir}/build/logs"/>
  <delete dir="${basedir}/build/testresults"/>
  <mkdir dir="${basedir}/build/docs"/>
  <mkdir dir="${basedir}/build/logs"/>
  <mkdir dir="${basedir}/build/testresults"/>
  <move file="${source}/config.jenkins.php" tofile="${source}/config.php"/>
  <move file="${basedir}/tests/const-xml.php" tofile="${basedir}/tests/const-xml.dev.php"/>
  <move file="${basedir}/tests/const-xml.jenkins.php" tofile="${basedir}/tests/const-xml.php"/>
 </target>
	
 <target name="build" depends="prepstructure"/>
</project>


Hopefully, with the descriptive nature of the Ant build scripts, this should be fairly self explanatory. To ensure Jenkins runs the Ant build script, we simply configure the job, select Invoke Ant as a new build step to add:

Finally, we select the default Ant installation which we setup.

Unit testing: SimpleTest

I tend to prefer the SimpleTest testing suite over PHPUnit (the fact that PHPUnit is a massive pain to install on Windows is one of them; though its much easier to get it working with Jenkins!), it also provides a nice web based interface to test results. Unfortunately, this involved a little big of research and trial and error to get working, but I managed it in the end.

XML Output

By default, I used the SimpleTest autorun file, combined with a TestSuite class. The autorun file executes the tests and outputs them to the browser, this was structured like so.

require_once('../simpletest/autorun.php'); 
class AllTests extends TestSuite {
     function AllTests() {
              $this->TestSuite('All tests');
              $this->addFile( TESTS_PATH . 'database_test.php');    
              $this->addFile( TESTS_PATH . 'database2_test.php');
	} 
}

To get this to output as XML, we need to have the tests run when we tell it, and we need to tell it to use the built in SimpleTest XML Reporting class.

require_once( TESTS_PATH . '../simpletest/unit_tester.php');
require_once( TESTS_PATH. '../simpletest/reporter.php');
require_once( TESTS_PATH . '../simpletest/xml.php');

class AllTests extends TestSuite {
    function AllTests() {
        $this->TestSuite('All tests');
        $this->addFile( TESTS_PATH . 'database_test.php');
        $this->addFile( TESTS_PATH . 'database2_test.php');
    }
}
$at = new AllTests();
$at->run(new XMLReporter());

That's the first challenge over with. For this to be usable in our Jenkins CI Server, we need to export this XML as a file of a specific format. Initially, I tried doing this with ant, however you can't (as far as I could tell from my research and trail and error) pipe the output of one command to another, so it wouldn't save the output of php running the tests into a new file.

Converting the XML

There is a very useful post on the Emory Libraries Tech know-how blog about converting the output of our unit tests into a format that Jenkins CI can work with.

The main point of note from that post is the simpletest_to_junit.xsl file they have developed, you will need a copy of that file to continue.

#!/bin/bash
php tests/xml_tests.php > build/testresults/results.xml
xsltproc tests/simpletest_to_junit.xsl build/testresults/results.xml > build/testresults/results-1.xml

Note: Ensure that the file you pipe the output of xsltproc to is different to the file it accepts as input, if the two files are the same, you will get permission denied errors and the build will fail. I wasted 20 minutes on that problem!

Test during the build

This shell script is part of the execution build process, I've just added it directly into Jenkins, but you can always add it as a file in your projects version control, and have Jenkins run it from there.

Publish test results after the build is complete

Within the Post-build Actions for the job in Jenkins, we can now tick "Publish JUnit test result report" and point it to the location of our test reports:

Now when a project builds, the Weather Report for our job also includes Test results as part of its weighting, and jobs will be flagged as unstable if tests fail.

Within the job view screen we also get a pretty cool graph trending the results of tests against recent builds. I had some fun breaking my builds to get the graph below!

PHP Code Sniffer

PHPCS is something I've heard quite a bit about, but have never used, and I can't say I'm a huge fan of it just yet, though I found it handy for pointing out certain code issues, and to maintain a constistent coding style throughout. I don't agree with a few things it complains about, so I just tell it to shut up about those with Exclusions!

Installing PHP CodeSniffer

Before we actually install PHP CodeSniffer, first goto Manage Jenkins ? Manage Plugins and install the Checkstyle Plug-in. This is used to provide detailed information from the CodeSniffer report in the Jenkins job screen. When I installed this plugin, once Jenkins restarted all of my Jobs had vanished - so do this before setting up and configuring the job that will use it!

CodeSniffer is easily installed through PEAR, so if you don't have PEAR installed, do that first:

sudo apt-get install php-pear

Then install PHP CS itself:

sudo pear install PHP_CodeSniffer

We will need to create a configuration file for PHP CS, so that it knows what to validate our code against; I tried at first ignoring this and using the built in ones, however they seemed to contain errors which meant PHPCS wouldn't run. After a quick search I found a standard configuration file, from which I removed one section which caused it to fail to run, and I added an exclusion so that it would allow code to be formatted using tabs instead of spaces.

<?xml version="1.0"?>
<ruleset name="PHP_CodeSniffer">
 <description>The coding standards for PHP_CodeSniffer.</description>

 <!-- Include the PEAR standard with some exclusions -->
 <rule ref="PEAR">
 	<exclude name="Generic.WhiteSpace.DisallowTabIndent"/> 
 </rule>
</ruleset>

This gets saved in the build folder of our version controlled code.

To run PHPCS as part of the build process, we simply add the following target to our Ant build script, which tells Jenkins to run PHPCS, set the report style as checkstyle, save the report in our build/logs folder, and run the codesniffer against our trunk folder.

 <target name="phpcs" description="Generate checkstyle.xml using PHP_CodeSniffer">
  <exec executable="phpcs">
   <arg line="--report=checkstyle --report-file=${basedir}/build/logs/checkstyle.xml --standard=build/phpcs.xml ${source}/" />
  </exec>
 </target>

Our build target line in this script should be amended to ensure this section is ran.

<target name="build" depends="preparestructure,phpcs"/>

Note: PHPCS, and some of the other QA tools take some time to run, and can be set to run in parallel, the sample Ant build script from Jenkins-php.org is very useful for this.

PHPDocumentor

Like CodeSniffer PHPDocumentor is easily installed through PEAR, so if you don't have PEAR installed, do that first:

sudo apt-get install php-pear

Then install PHPDocumentor

sudo pear install PHPDocumentor

And finally, add to our Ant script that we want to generate PHPDocs, and where we want them to be saved.

<target name="phpdoc" description="Generate API documentation">
 <exec executable="phpdoc">
  <arg line="-d ${source} -t ${basedir}/build/docs" />
 </exec>
</target>

Again, we need to update our build target to run this too.

<target name="build" depends="preparestructure,phpcs,phpdoc"/>

Another Gotcha

If your Job name has spaces in it, you will find many commands run through Ant will trip up on those with the build scripts as they are. I wasted half an hour on this one, even though it is obvious!

Deployment

There are a number of ways that Jenkins can then be linked into the deployment process. As you can probably tell from this, Jenkins doesn't actually do much in itself, it simply provides a system for managing projects, centrally storing reports and build information, and thanks to its plug-in architecture and support for Ant, allows us to drop in new reports, tests and processes to it.

From my initial investigations, one good approach for working deployment into this, is to have a second Job which can only be executed once the first has build successfully. The second job works on the same code base, but changes the file-rejiging, and through post-build processes transfers the code to the deployment server and instructs that server (over SSH) to update the website symlink to point to the new code, or even to call a build script on the remote server.

I've not done too much looking into this - so I'll hopefully follow up with a more deployment focused entry with respect to Jenkins.

Up and running!

So there you have it, Jenkins CI is installed with a few tools to get you going, and with support for SimpleTest. I'm planning to look more into the code QA tools, and integrating with deployment workflows soon, after which I'll follow up on this blog post.

Further reading, links and references

During my Internet travels, I found the following sites helpful:

Posted by Michael on 26th Mar 2011 at 08:08

Comments

Have you had a look at the build pipeline plugin so that you can chain your jobs into a build (aka deployment) pipeline? http://www.centrumsystems.com.au/blog/?p=121

Posted by Geoff Bullen on 31st March 2011 at 01:01

Hi Geoff, thanks for your comment. I have installed the build pipeline plugin, but I've not yet done too much with deployment. I'll be investigating more at the weekend :-)

Posted by Michael Peacock on 31st March 2011 at 10:10

Great writeup. I wish I had found this earlier. As I also use simpletest and getting the xml into the correct format had been difficult. Simpletest 1.1.0 has a junit xml extension, I grabbed it from the svn repo, and fixed it up as it doesn't produce the correct format. Thanks again for the useful post.

Posted by Mark Donohoe on 30th June 2011 at 20:08

Hi, did you manage to find a good way of deploy php apps? I managed to build and test a app git repot. Now I need to pushit to another server.

Posted by catalin on 8th August 2013 at 19:07

We are a gaggle of volunteers and opening a brand new scheme in our community. Your website provided us with useful info to work on. You've performed an impressive task and our entire group can be grateful to you.

Posted by Lindsey on 29th January 2014 at 11:11

Post a comment