I want to look at how to get a very simple PHP project setup in Jenkins using the standard suite of tools and templates as demonstrated on php-jenkins.org.

If you do not have Jenkins installed or are not sure you have your environment setup correctly, please checkout my previous tutorial Getting Started With Jenkins for PHP.

A quick checklist

Before we start, make sure you have your environment and Jenkins installation up and running and appropriately configured.

  • You have a JVM and Jenkins installed running at http://localhost:8080
  • You have the following Jenkins plugins installed
    • checkstyle
    • cloverphp
    • dry
    • htmlpublisher
    • jdepend
    • plot
    • pmd
    • violations
    • xunit
  • You have the following PEAR packages installed
    • pdepend/PHP_Depend
    • phpmd/PHP_PMD
    • phpunit/phpcpd
    • phpunit/phploc
    • PHPDocumentor
    • PHP_CodeSniffer
    • phpunit/PHP_CodeBrowser
    • phpunit/PHPUnit
    • phpunit/ppw
  • You have Ant installed
  • You have Git installed and are familiar with its basic use
  • You have the Zend_Framework installed globally as a PEAR package

If you don't have the above installed please refer to the Getting Started With Jenkins for PHP tutorial.

If you're not familar with Git, have a quick read of git-scm.org or refer to my Git cheatsheet.

To install the Zend Framework as a PEAR package see my post on Installing Zend Framework from PEAR.

Create your project

I'm basing this tutorial on a sample Zend Freamework application, you're free to use whatever codebase you like. But for the purposes of this tutorial I'm using this project as a starting point: [email protected]:ajbonner/Bookings.git.

$ git clone https://github.com/ajbonner/Bookings.git ./bookings
  $ cd bookings
  $ ppw --name 'Bookings' --source ./application .

This will generate an ant build file with some general defaults for PHP_CodeSniffer and PHP Mess Detector along with a standard phpunit configuration file.

First thing, we'll need to make a small amendment to the phpunit configuration file, since we're testing a Zend Framework application, we need to make sure PHPUnit loads a custom bootstrap before running tests. Open up the generated phpunit.xml.dist file and change the opening phpunit element to look like this:

<phpunit backupGlobals="false"

Our build environment is now configured correctly to run Zend Framework Test Cases, we have our ant build file setup and some defaults configured for our code coverage and analysis tools. Let's add our new build and phpunit configuration files to git and commit our changes. Then we can run ant and see what happens.

$ git add build.xml phpunit.xml.dist
  $ git commit -m 'Add build and test configuration files'
  $ ant
  >> Buildfile: /home/aaron/Sites/Bookings/build.xml
  >> clean:
  >>    [delete] Deleting directory /home/aaron/Sites/Bookings/build/api
  >>    [delete] Deleting directory /home/aaron/Sites/Bookings/build/code-browser
  >>    [delete] Deleting directory /home/aaron/Sites/Bookings/build/coverage
  >>    [delete] Deleting directory /home/aaron/Sites/Bookings/build/logs
  >>    [delete] Deleting directory /home/aaron/Sites/Bookings/build/pdepend
  >>     [mkdir] Created dir: /home/aaron/Sites/Bookings/build/api
  >>     [mkdir] Created dir: /home/aaron/Sites/Bookings/build/code-browser
  >>     [mkdir] Created dir: /home/aaron/Sites/Bookings/build/coverage
  >>     [mkdir] Created dir: /home/aaron/Sites/Bookings/build/logs
  >>     [mkdir] Created dir: /home/aaron/Sites/Bookings/build/pdepend
  >> parallelTasks:
  >> phpcpd:
  >> pdepend:
  >>      [exec] PHP_Depend 0.10.5 by Manuel Pichler
  >>      [exec] 
  >>      [exec] Parsing source files:
  >>      [exec] ....                                                             4
  >>      [exec] 
  >>      [exec] Executing Coupling-Analyzer:
  >>      [exec]                                                                 18
  >>      [exec] 
  >>      [exec] Executing CyclomaticComplexity-Analyzer:
  >>      [exec] .                                                               24
  >>      [exec] 
  >>      [exec] Executing Dependency-Analyzer:
  >>      [exec]                                                                 17
  >>      [exec] 
  >>      [exec] Executing Inheritance-Analyzer:
  >>      [exec]                                                                  5
  >>      [exec] 
  >>      [exec] Executing NodeCount-Analyzer:
  >>      [exec]                                                                 14
  >>      [exec] 
  >>      [exec] Executing NodeLoc-Analyzer:
  >>      [exec] phpcpd 1.3.2 by Sebastian Bergmann.
  >>      [exec] 
  >>      [exec] 0.00% duplicated lines out of 119 total lines of code.
  >>      [exec] 
  >>      [exec] Time: 0 seconds, Memory: 2.75Mb
  >>      [exec] .                                                               22
  >>      [exec] 
  >>      [exec] Generating pdepend log files, this may take a moment.
  >>      [exec] 
  >>      [exec] Time: 00:00; Memory: 9.50Mb
  >> phpcs:
  >> phpmd:
  >>      [exec] Result: 1
  >> phploc:
  >>      [exec] phploc 1.6.1 by Sebastian Bergmann.
  >>      [exec] 
  >>      [exec] Directories:                                          1
  >>      [exec] Files:                                                4
  >>      [exec] 
  >>      [exec] Lines of Code (LOC):                                119
  >>      [exec]   Cyclomatic Complexity / Lines of Code:           0.08
  >>      [exec] Comment Lines of Code (CLOC):                        18
  >>      [exec] Non-Comment Lines of Code (NCLOC):                  101
  >>      [exec] 
  >>      [exec] Namespaces:                                           0
  >>      [exec] Interfaces:                                           0
  >>      [exec] Classes:                                              4
  >>      [exec]   Abstract:                                           0 (0.00%)
  >>      [exec]   Concrete:                                           4 (100.00%)
  >>      [exec]   Average Class Length (NCLOC):                      22
  >>      [exec] Methods:                                              9
  >>      [exec]   Scope:
  >>      [exec]     Non-Static:                                       9 (100.00%)
  >>      [exec]     Static:                                           0 (0.00%)
  >>      [exec]   Visibility:
  >>      [exec]     Public:                                           8 (88.89%)
  >>      [exec]     Non-Public:                                       1 (11.11%)
  >>      [exec]   Average Method Length (NCLOC):                     10
  >>      [exec]   Cyclomatic Complexity / Number of Methods:       1.89
  >>      [exec] 
  >>      [exec] Anonymous Functions:                                  0
  >>      [exec] Functions:                                            0
  >>      [exec] 
  >>      [exec] Constants:                                            0
  >>      [exec]   Global constants:                                   0
  >>      [exec]   Class constants:                                    0
  >> phpunit:
  >>      [exec] PHPUnit 3.5.14 by Sebastian Bergmann.
  >>      [exec] 
  >>      [exec] ...
  >>      [exec] .
  >>      [exec] 
  >>      [exec] Time: 1 second, Memory: 19.25Mb
  >>      [exec] 
  >>             OK (4 tests, 16 assertions)
  >>      [exec] Writing code coverage data to XML file, this may take a moment.
  >>      [exec] 
  >>      [exec] Generating code coverage report, this may take a moment.
  >> phpcb:
  >> build:
  >> Total time: 3 seconds

If you have everything setup correctly you will have a build directory with a number of artifacts: code coverage analysis, code style reports, dependency analysis, etc. You can look at these directly but it's much more convienient to use Jenkins to view these artifacts.

Importing a Project into Jenkins

We have a working build and we are producing a number of artifacts representing certain aspects of our software. Time now to import the project into Jenkins and have this process ran automatically.

Step 1 - Create a New Jenkins Project

As part of getting our environment ready for Jenkins, we installed a PHP Project Template. To create our new Project, we copy this template, and give our project a name. Choose whatever you like, but for the purposes of this tutorial, I've chosen 'Bookings'.

Step 2 - Configure your New Project

You're now presented with a (at first glance) daunting configuration page. We'll work our way down the page, for the most part we don't need to make major changes.

Firstly the pdepend task generates a pair of SVG images, in order to have these display on the project dashboard we need to substitute 'job-name' to 'Bookings' (or whatever you chose for your project name) in the embed tags in the description field.

Next, uncheck 'disable build', naturally we want to perform builds!

The project will be retrieved from the git repository we created during the initial build configuration. Pass in the absolute filesystem path to your repository, for example mine is file:///home/aaron/Sites/Bookings.

That's it. You now have setup the project in Jenkins and defined a source. The remaining details can be left at their defaults. Scroll down to the bottom of the page and click the save button.

Step 3 - Build the Project

The project needs to first do a build before the project workspace can be initialised. On the left hand project menu is a 'build now' link. Click that and Jenkins will checkout a copy of our project from git then process the ant build.xml we defined.

Once the build is complete you can click on the build and take in all the dianostic and code analysis artifacts generated during the build.

Possibly, you may run into an issue building your project from git. A common cause is Jenkins being unable to find a user identity for its operating system user. Please see my post Cannot Build Git Project in Jenkins if this happens to you.

What Next?

Continuous integration isn't about pretty graphs and pithy build summaries. It's about maintaining working software and highlighting issues as soon as possible. Jenkins has a wealth of plugins that help this process, but perhaps the first one to checkout is the email functionality, letting your development team know immediately if something is broken.


Once you have Jenkins up and running you can manage most administrative tasks with a handy cli jar file or by using simple http requests.

Assuming you're running your server on port 8080, obtain the cli jar file like this:

$ wget http://localhost:8080/jnlpJars/jenkins-cli.jar

To see a list of available commands simply run the tool with the help argument:

$ java -jar jenkins-cli.jar -s http://localhost:8080 help
  > build
  >   Builds a job, and optionally waits until its completion.
  > clear-queue
  >   Clears the build queue
  > connect-node
  >   Reconnect to a node
  > copy-job
  >   Copies a job
  > ...

In recent versions of Jenkins, typical server operations have been decoupled from the cli tool and are now issued using simple http requests. For example to reload Jenkins instance's configuration, you would just fire a http request at it like this:

$ curl http://localhost:8080/reload

There are three commands of this sort:

  • reload Reload server configuration
  • restart Restart the server
  • exit Close the server down

Issue these in the format:

$ curl http://[jenkins-server]/[command]
Tags: jenkins cli

I just wanted to write up a quick tutorial for installing Jenkins CI on modern flavours of Ubuntu.

There's two options, you can download a pre-prepped specific package and install using aptitude or synaptic, _or_ you can set-up an update site to ensure you're always notified of updates.

I prefer the latter and used the following steps:

$ wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
  $ sudo echo 'deb http://pkg.jenkins-ci.org/debian binary/' >> /etc/apt/sources.list
  $ sudo apt-get update
  $ sudo apt-get install jenkins

By default, the Jenkins packages attempt to start up a java web container running on port 8080. To change this, open up /etc/default/jenkins and look for the following:

# port for HTTP connector (default 8080; disable with -1)

Change the port number to whatever works for you and (re)start the service

$ sudo service jenkins restart
Tags: jenkins

PHP projects are becoming ever larger and with that size comes complexity that can be difficult to manage.

Typically a PHP project will start off small, some basic webpage views, maybe a few forms, and likely, some sort of search functionality. This is pretty basic and if things need to change, you can normally change it in place, directly on the web-server and without too much grief.

At some point though entropy takes its toll and incremental changes have so may unintended side-effects it's no longer feasible to safely make edits in-place. A quick hack directly on the web-server stops being a 'quick win', and more like a game of Russian Roulette.

This is where unit and integration testing comes to the fore. A safety net to protect us when we start changing an application. But often getting a test environment set-up and representative of the live system is a lot of work in itself, and the temptation to just give into the 'Inner Pig' is too great. That is, to not bother running any tests, and just cross fingers.

Jenkins makes this a lot easier by doing much of the heavy lifting of building and running tests. It will check that code complies with defined style conventions. It can also check for common coding smells (copy/paste, duplication, long methods, large classes, poor expressions) and it can run custom scripts depending on the success of unit and integration tests.

Essentially Jenkins ensures that any changes that go into source control, do not 'break the build' in a process known as Continuous Integration.

Installation is very easy. Visit the Jenkin's site, follow the instructions for your platform and you'll have a Jenkins's install running (by default) at http://localhost:8080. I have also written a small installation tutorial: Installing Jenkins on Ubuntu/Debian Systems.

To get Jenkins initially dressed up for PHP follow the jenkins-php.org site upto and including the setup the Pear packages and Jenkins plugins.

You may or may not run into trouble with these instructions. For the most part it worked fine for me, however I chose to use the cli-tool and the plugin repository was not initialised. To get up and running, I had to force the plugin list to refresh manually.

Jenkins plugin list

Manage Jenkins > Manage Plugins > Click the Advanced Tab

If you're like me and want git or svn scm access, you'll want to install these plugins as well, as they are not included in the list on the jenkins-php instruction page.

Once the PEAR packages and Jenkins Plugins are installed, you're now ready to start preparing your application for Continuous Integration.

The initial configuration can be quite terse, as you will need an initial ant build file, and sample configurations for PHP Code Sniffer and the other code analysis tools. Thankfully Sebastian Bergmann - author of PHPUnit and much of the Jenkins PHP suite of tools - has developed a project wizard utility to simplify these initial configuration steps.

Install the PHP Project Wizard from the PHPUnit channel:

$ sudo pear install phpunit/ppw

Once installed, you can change into your project dir and run it with a few arguments to setup your initial build environment.

$ ppw --name 'My Project' --source ./lib --tests ./tests

You can also specify arguments defining default rulesets for PHP Code Sniffer and PHP Mess Detector. Omitting these arguments sees ppw select some sane defaults for you.

Now install the Jenkins PHP Job Template:

$ cd path/to/jenkins/jobs
  $ git clone git://github.com/sebastianbergmann/php-jenkins-template.git php-template
  $ chown -R jenkins:nogroup php-template/
  $ curl http://localhost:8080/reload # set this to be the path:port to your jenkins server

Jenkins and your system are now ready to manage a PHP Project. Please see my tutorial on Setting up a PHP Project in Jenkins for how to setup your first project in Jenkins.