Getting started with Continuous Integration

Filed under: Continuous integration, Java, — Tags: Hudson, Maven, Mercurial, Test automation — Thomas Sundberg — 2009-04-22

A quick Continuous Integration introduction example.

Set up a simple project

The most important thing with continuous integration is that we need a build system that will build whatever we need from one source in one step. It is essential that we have all resources we need available either from a version control system or some other way where we can rely on them to always be available. I will use two sources in this example, I will use official Maven repositories and I will use source code available in a Mercurial repository.

Start with creating a project directory.

We need one directory called src and one Maven pom.xml in it.

Edit the pom.xml and add the simple project definition below:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.agical.experimental</groupId>
    <version>1.0-SNAPSHOT</version>
    <artifactId>CI-Demo</artifactId>
    <name>Continuous Integration Demo</name>
    <packaging>jar</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-idea-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <downloadJavadocs>true</downloadJavadocs>
                    <downloadSources>true</downloadSources>
                    <jdkLevel>1.5</jdkLevel>
                    <jdkName>1.5</jdkName>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

I define that we require Java 1.5, we want to create Intellij Idea project files and we want to download both source and javadoc for all dependencies. And finally we want a dependency to JUnit, we always do since we want to be able to develop our code using TDD, Test Driven Development.

Lets create the Intellij Idea project files:

mvn idea:idea

All dependencies should be downloaded. You will see a download failure since no javadoc is available for the JUnit 4.5 release. You should be able to start Intellij Idea and continue editing the files from Idea.

Add the project to a version control system – Mercurial

We want to add the project to a version control system, VCS. Hudson may pick up changes on a file system, but we aim for more people to be able to share the same code base so a VCS is the way to go.

Perform

hg init

in the project directory you created earlier.

Next step is to add the files, in this case just the pom.xml. There is no need to add the Intellij Idea project files. These can be re-created at any time and will contain system specific stuff that shouldn't be version controlled.

A simple way to make sure that we never tries to add the Idea project files is to create a ignore file in the repository.

Create:

.hgignore

with this content:

syntax: glob
*.ipr
*.iml
*.iws

Add the files created to Mercurial with the command

hg addremove

And commit the changes with

hg commit -m "Initial version"

That's it, we have done enough to be able to pick up any changes in this project with a Continuous Integration server.

Serve the version control system to the network

To be able to pick up any changes to the repository, we need to make the repository available on a network. A simple way to do this is to simply perform the command serve as

hg serve

That's it folks. The repository is available on the network!

Browse to it in

http://localhost:8000

Set up a Continuous Integration server

To set up a Continuous Integration server you need to download it and start it. Download it from https://hudson.dev.java.net

Save the hudson.war wherever you want, it really doesn't matter. All settings Hudson will save will be saved in it's workspace. On a Win XP host, this will be

Documents and Settings<user name>.hudson

Start Hudson

java -jar hudson.war

Browse to Hudson on the adress

http://localhost:8080

You will face a start page similar to this: Hudson start page

We need to start with configuring Hudson before we can add a job that will try to build our project. Follow the link “Manage Hudson” and then “Configure Hudson” Add a JDK and Maven. On my system the result looks like: Hudson Global Configuration

Save the changes.

Now we need to install a Mercurial plugin so Hudson can pick up any changes from a Mercurial repository. Follow the link “Manage plugins” and click on the “Available” tab and select the Mercurial plugin. Hudson Plugin Managment

And click install button on the lower, right corner. Wait for a while until the plugin has been downloaded and installed. We need to restart Hudson now so the Mercurial plugin will be found.

Add a new job by following the link ”New job”

I will name my job CI-Demo and it is a maven2 project. The result looks like this:

the rest of the job settings are set below: Hudson Job Definition

I define that we want to use Mercurial as Source Code management and the address to the repository, the same address we browsed to earlier.

http://localhost:8000/

Then I define that we want to use polling of the repository as build trigger and define a schedule for it. I want any changes to be picked up pretty fast so I define the schedule to poll every minute.

Finally I want to define which pom to use and what target to invoke in it. We want to build, test and create a deliverable so I want to call clean first and then install.

That's it, save the new job.

Add stuff to the project

We should be done with all the infrastructure of creating a repository and setting up a build server to build the project. Lets add a small piece of code and see if the changes are picked up.

Since we always want to develop any project test driven, lets start with a simple test.

I want to create them in a Maven file structure so lets create these directories:

src --- main --- java
     +- test --- java

Re create the Idea project files to make sure that Idea picks up the new directories and treats them properly

mvn idea:idea

Add a test class src/test/java/com/agical/experimental/MirrorTest.java

with this content:

package com.agical.experimental;

import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.core.Is.is;

public class MirrorTest {

    @Test
    public void verifyMirror() {
        Mirror mirror = new Mirror();
        String expected = "Hello World!";

        String actual = mirror.reflect(expected);

        assertThat(actual, is(expected));
    }
}

This will of course not even compile so there is no need to add it to the version control system. Lets make sure that it compiles before we add. It is a well known fact that if it compiles, it works. Or?

Add

src/main/java/com/agical/experimental/Mirror.java

with this content:

package com.agical.experimental;

public class Mirror {
    public String reflect(String reflection) {
        return null;
    }
}

This will compile. Lets add it to Mercurial and wait for Hudson to pick up the change and perform a build.

hg addremove
hg commit -m "Added a test"

For some unknown reason, Hudson may need a initial build triggered manually. Trigger a build by pressing the build now button to the right of the job.

The build will of course fail. We knew that, but we wanted to se a failed job from Hudson.

Lets change the production code so it will pass.

package com.agical.experimental;

public class Mirror {
    public String reflect(String reflection) {
        return reflection;
    }
}

Before we commit this version, lets build first.

mvn clean install

And as expected, we get a successful build.

Commit this and wait for Hudson to pick up the change:

hg commit -m "Fixed the production code"

Resources



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS