Continuous Integration with Jenkins, Part 2

Share this article

In the first part of this article I presented the case for Continuous Integration. Now we’ll install and set Jenkins up to monitor the automatic building of a simple demonstration PHP application. We’ll begin with the demo project, then address Jenkins’ installation, and finally set it up and watch it running.

Demonstration Project

The demo project is based on the User class and its test script used by Michelle Sanver in her article Getting Started with PHPUnit. To keep things organized, I created a directory called workspace in my home directory and set it up like this:

workspace has two subdirectories: .git which is automatically created and used by Git, and builds, the stable version folder.

The development file is User.php under workspace whereas the stable file has the same name but is in workspace/builds. The goal is to be able to make changes to workspace/User.php, test those changes, and if they pass then copy that file to workspace/builds.

The following lines create directories workspace and workspace/builds and initializes the Git repository:

jajeronymo@desktop:~$ mkdir -p workspace/builds
jajeronymo@desktop:~$ cd workspace
jajeronymo@desktop:~/workspace$ git init
Initialized empty Git repository in /home/jajeronymo/workspace/.git/

For convenience, I’ll reproduce Michelle’s classes that are to be respectively saved as workspace/User.php and workspace/UserTest.php here:

<?php
class User {
    protected $name;
    public function getName() {
        return $this->name;
    }
    public function setName($name) {
        $this->name = $name;
    }
    public function talk() {
        return "Hello world!";
    }
}
<?php
require_once "User.php";
class UserTest extends PHPUnit_Framework_TestCase
{
    public function testTalk() {
        $user = new User();
        $expected = "Hello world!";
        $actual = $user->talk();
        $this->assertEquals($expected, $actual);
    }
}

To start a PHPUnit test:

jajeronymo@desktop:~/workspace$ phpunit UnitTest UserTest.php
PHPUnit 3.6.10 by Sebastian Bergmann. 
. 
Time: 0 seconds, Memory: 3.25Mb 
OK (1 test, 1 assertion) 

As you know, the dot in the second line of the output means that the User.php passed the test.

And this line adds User.php to the Git repository:

jajeronymo@desktop:~/workspace$ git add User.php

Adding a file merely tells Git it exists. To record it as the the latest version we must commit it:

jajeronymo@desktop:~/workspace$ git commit -m "First Version." User.php

Since the initial version of User.php passed the test, it is currently the stable one so we have to copy it to the builds folder. This can be done manually, using either a windowed file manager, or on the command line like this, but really it’d be better if it were done by some automated means, especially for large projects where copying files can be tedious and error prone.

Apache Ant looks for a file called build.xml on the project home folder and runs a series of operations described in XML format, like this example that copies the latest User.php to the builds folder:

<project name="user">
 <copy file="User.php" tofile="builds/User.php"/>
</project>

Save the above to workspace/build.xml and run:

jajeronymo@desktop:~/workspace$ ant -v 

Ant should return an output that ends similarly to:

[copy] Copying 1 file to /home/prospero/workspace/builds 
[copy] Copying /home/jajeronymo/workspace/User.php to /home/jajeronymo/workspace/builds/User.php 

At this stage, a verified copy of User.php is at the stable version folder ready to be sent to a production server. To further automate the process, we can add the test to the Ant script:

<project name="user">
 <exec executable="phpunit" failonerror="true">
  <arg line=" UnitTest UserTest.php" />
 </exec>
 <copy file="User.php" tofile="builds/User.php"/>
</project>

Save the above again and run Ant:

jajeronymo@desktop:~/workspace$ ant -v 

Now we’ve ended up with two simple tasks for our simplified Continuous Integration: committing versions to Git and running Ant to test code which if approved will be copied to the current stable build folder. So, why do we need Jenkins after all?

The Need to Automate and Monitor

To answer that, I’ll use an IDE or text editor to make a small change in User.php, say, add a few extra exclamation marks to “Hello World!”. As soon as I’m through, I’ll commit the new version to Git repository:

jajeronymo@desktop:~/workspace$ git commit -m "Added a few bangs." User.php
[master d2c96bb] Added a few bangs. 
 1 files changed, 1 insertions(+), 1 deletions(-) 
jajeronymo@desktop:~/workspace$ ant -v 

But wait a minute! Wasn’t I supposed to test the change before committing the changes? Sure I was! Let’s do that now before someone grabs an untested commit!

Since the test expects User.php output string to be exactly “Hello World!”, it will return an error:

     [exec] 
     [exec] There was 1 failure: 
     [exec] 
     [exec] 1) UserTest::testTalk 
     [exec] Failed asserting that two strings are equal. 
     [exec] --- Expected 
     [exec] +++ Actual 
     [exec] @@ @@ 
     [exec] -'Hello world!' 
     [exec] +'Hello world!!!' 
     [exec] 
     [exec] /home/prospero/workspace/UserTest.php:15 
     [exec] /usr/bin/phpunit:46 
     [exec] 
     [exec] FAILURES! 
     [exec] Tests: 1, Assertions: 1, Failures: 1. 

BUILD FAILED 
/home/prospero/workspace/build.xml:4: exec returned: 1 

Not good… I’d better go back to User.php and removing those extra bangs! Once that’s done, I’ll run Ant again before committing. But that’s when the phone rings, an urgent matter requires my attention, and User.php slips out of my mind. That’s why we need Jenkins! People get distracted; computers don’t.

Jenkins main job is not so much automating tasks as it is to monitor them. In fact, automation of builds is not much more than a cron job which any decent OS can handle. Jenkins of course makes it easy to set up such jobs, called “triggers”. But it has several other features related to identifying who is responsible for the changes that are causing test to fail, alerting project managers, showing reports on the possible problems and much more. That’s where its usefulness comes from.

Setting Up Jenkins

Jenkins is available from their website as a Java web application archive file (.war) or as precompiled packages for several popular operating systems. Using the WAR, just put it onto a suitable folder, say ~/jenkins and run:

jajeronymo@server:~/jenkins$ java -jar jenkins.war

That starts Winstone, a very light servlet container (a sort of server for Java apps that works with Java more or less the same way a webserver works with HTML and PHP). The terminal lists several actions until ending with:

INFO: Jenkins is fully up and running

Jenkins has a web interface that listens on port 8080. Pointing your browser to http://localhost:8080 you should see a screen like this:

To create our demonstration project, click on “New Job”. A form will appear. Enter a name for the job (I’ll enter “Demo”) and select “Build a free-style software project”.

Then click on OK and a long list of options appear below on the same form.

Look for Build Triggers and check Build periodically. A text box called “Schedule” will show up. Write “*/5 * * * *” in it. This tells Jenkins to run the build procedure every 5 minutes (the syntax used here is the same as used by cron).

Go to “Build” and click on “Add build step”. A list of options appear. Select “Invoke Ant” and click on the “Advanced” button that will appear. This button reveals four text boxes. In the second one, “Build file” write the absolute path to the build.xml file used by Ant. In my case, this is /home/jajeronymo/workspace/build.xml.

Navigate to the end of the options list and click on “Save”. That’s it! From now on, Jenkins will run the Ant script every 5 minutes which by its turn tests any changes to User.php and, if no errors are found, copies it over to the build folder.

From the list of jobs that now appears on Jenkin’s initial page, select our test job. On the left side of the window you can see the build list. (If it is still empty you can click on “Build now” on the menu above it to force the first build).

If you have corrected the problem in User.php, it should pass the test. As soon as the build is over, Jenkins adds a link with its number and time of execution to the list, and beside the link you can see a ball – blue for success and red for failure.

Clicking on the link shows a menu where you can elect to see the changes made and the user who requested the build. Clicking on the ball prints Ant’s console output. As time passes, builds accumulate. You can play with User.php, purposefully breaking and fixing and see how it reflects on the builds list and on the console’s output.

As long as it is left running, Jenkins will relentlessly run the test and if no problems are found overwrite the file in build with the newest stable version of User.php.

Summary

As said in the first part, the goal of this article was create an understanding of the operational CI problems Jenkins can solve for a development team. Our example was oversimplified but with this and the other mentioned articles, the interested reader have the tools to create much more sophisticate and useful Ant scripts. On this, please check out http://jenkins-php.org/ where a comprehensive template for multiple tasks related to PHP development can be found.

Image via Charles Taylor / Shutterstock

J Armando JeronymoJ Armando Jeronymo
View Author

Armando Jeronymo has a B.Sc. in Electronics Engineering from Rio de Janeiro's Ursuline University and a US Associate degree in Risk Management. He started coding in a TI-59 calculator in the early 80's. He then learned and forgot Sinclair Basic, Fortran, Algol and MS Basic. For ten years he did not write code but worked with insurance and reinsurance in his native Brazil and London. Then he went back to programming to find out everything was different. He began by getting to know HTML to create some material on aviation instruction. He then learned Delphi's Pascal in order to create some financial applications. He began experimenting with server-side languages about the turn of the Century, first with ASP but soon moving to PHP. He wrote his first MySQL web application in about 2004. He switched to Linux in 2005 (Ubuntu 5.10 to be more exact) and has been developing LAMP systems since then. He is a Private Pilot and, whenever possible, a FlightGear flyer. He loves classical music and jazz (including Bossa Nova) and old movies. Besides computing he is professionally interested in business management and a follower of Peter Drucker's teaching. Married and father of a girl he is also an active member of his Rotary Club.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week