Monday, January 7, 2013

Continuous Integration With Jenkins

In this post I'll be describing how to integrate Jenkins with a Maven project with a remote source code repository on Github.

Download and Run Jenkins
Starting up continuous integration with Jenkins is extremely simple thanks to the Winstone servlet container.  All you need to do is download jenkens.war at and start it up with the command "java -jar jenkins.war".  You're now up and running on localhost port 8080.  Starting Jenkins will create a new folder in the home directory of the user running Jenkins named ".jenkins".  This folder will contain all of the jobs you create, plugins you install, a workspace for all the source it needs to build, and everything else it needs to run.

It is likely that Jenkins will be needed outside of your local environment so you could run it behind something like Apache as explained at the following URL:

Install Plugins
To use Jenkins with Maven you will need the Maven Integration plugin which comes with Jenkins already.

To use Jenkins with Github you will need the GitHub plugin which will also download the Jenkins GIT plugin and github-api plugin.  To ensure you have these plugins, go to the "Manage Jenkins" link and then click on the "Manage Plugins" link.

Check to ensure you have the plugins mentioned above installed

If you do not have any of the four required plugins, download them by going to the "Available" tab and placing a checkmark next to each plugin you need and then click the "Install without restart" or "Download now and install after restart".  Then restart Jenkins by using the "Prepare for Shutdown" link in the "Manage Jenkins" menu and then stop the process running Jenkins.  By clicking the "Prepare for Shutdown" link it will prevent Jenkins from kicking off new builds so that Jenkins can be safely shutdown after any currently executing builds finish.  Unfortunately, Jenkins doesn't shutdown the server automatically after builds finish so you'll have to either kill the process yourself or stop the process using whatever shutdown script you may have created.

Create a new job
Once Jenkins is back up you will need to create a new job to run your build.  In the Jenkins menu click the "New Job" link.  This will bring up a form that allows you to specify the job name and what type of project you have so that it can setup the appropriate configuration for your project.  Since we're assuming a Maven project for this post, we'll choose "Build a maven2/3 project" and give the job the name "Maven Test Build Job" and then hit "OK".

Configuring the Git Repo
Next you will need to specify where your source code is located if it is in a version control repository.  In this case we're going to select "Git".  *Ignore the error below, it should not appear if you entered a valid URL and it is able to connect to the repository.

You will need to fill out the "Github project" field with the URL to your repository and project select "Git" under "Source Code Management". Then fill out the Repository URL.  If you want to be able to commit changes back to the repository then you will need to specify the SSH URL to the repository as the "Repository URL".  Also, be sure that the machine you're setting up Jenkins on has an SSH key for authentication setup on Github.  Go to for more information about setting up your SSH key if you do not already have one setup.

Polling for changes in Repository
If you want Jenkins to automatically perform a build whenever a new commit occurs in the repository, you'll need to setup Jenkins to poll Github for changes.  Thankfully, this is also very easy to do.  You just check the "Poll SCM" box under "Build Triggers" and set a cron-like schedule to poll at a set interval as shown below.

Maven Build Settings
Under the "Build" section you will need to specify the location of your pom.xml and which goals and options you wish Maven to perform.  The pom.xml location is relative to your Jenkins workspace.  Jenkins creates a workspace for each job.  So you can go to the .jenkins folder and look for a folder in
"workspace/Maven Test Build Job".  This is where your Git repo will be downloaded to.

If you don't need to push anything back to the repository after each build, then you are done.  However, if you do then there is some more work you need to do.

(Optional) Commiting After Build
One of the quirky things about using the Github Plugin is that even if you specify the branch you want to build, the workspace will be set to "No Branch".  You can test this by going to your .jenkins/workspace/<your-repo-name> directory and doing a "git branch".  Notice the "*" isn't next to the branch you think it should be, it's next to no branch.

To remedy the situation you'll need to add a shell command to the "Pre Steps" section so that it does a checkout of the branch you want to commit to before it does the build.  Under "Pre Steps", select "Add pre-build step" and then pick "Execute Windows batch command" or "Execute shell".  A box will appear that will allow you type in your script.  Type in "git checkout <your-branch-name>".

Next you will need to add a "Post Step" that will commit the changes to the repository.  Under "Post Steps" choose if you want to run it only if the build succeeds, if it succeeds or is unstable, or regardless of the build result.  Then click "Add post-build step".  Again, choose "Execute Windows batch command" or "Execute shell".  A box will appear that will allow you type in your script.  Type in the following:

    git commit -m "<your comment>"
    git push

(Optional) Executing another job if current job is successful
In cases where the success of the current build should kick off another job, you'll need to setup a "Post-build Action".  Go under "Post-build Actions" and click the "Add post-build action" button.  Select "Build other projects" from the drop down menu.  This will prompt for a project to build.  Enter the name of the job you want this job to kickoff when successful.

Save your changes and your job is now ready to run.  Click the "Build Now" link in the Jenkins menu to kickoff the job.