Jenkins & Git branches

Jenkins CI is well known open source continuous integration server, and a damn good one in my opinion. I guess the biggest issue is to get to know all the plugins available, a fun time indeed 🙂 Anyways, since the switch to Git/Gitlab, I needed a setup that would enable the team to use the CI environment in full. The idea was to allow CI environment to build all the branches, not just master (release, develop, whichever is your flavor of the day) branches. Manually setting up Jenkins projects for all the Git branches was out of question.

A little background first. The projects are mainly Java / Maven and there are a lot of dependencies. One rule that is most important for Jenkins environment was that the developers need to keep their tasks / features in separate Git branches. This would prevent clashes between developers, but still allow them to work through the entire stack if needed. Jenkins was to be used as continuous feature testing environment so all those branches had to find their place in the CI stack too.

Ideally, the solution was to satisfy the following requirements:

  • a single branch that spans several projects should be built and referenced correctly (Maven dependencies)
  • only master (develop) and releases branches are to be published on Artifactory, (short lived) features should not fill it up
  • preferably a single Jenkins project, because of:
    • resources when building on the same machine (e.g. building several branches for the same base project)
    • no need to clutter the views
    • build history should show the branch built
  • should be able to build the entire feature stack across different slave nodes

So, we introduced the parameterized builds. Each Git project must have a single Jenkins project that is configured like this:

  • This build is parameterized checked, with a single parameter “BRANCH_NAME_TO_BUILD”, and the default value “master” (or “develop” or what ever you use).

  • Block build when upstream project is building checked – this is not really related to this workflow but is a good practice nevertheless, prevents building projects while dependencies are built
  • Git repositories and branches set to track the Git repository, and to build branch from the above parameters setup. All using Git plugin.

  • Deploy artifacts to Artifactory set to filter out snapshots

  • Deploy artifacts to Maven repository

A specific thing about the setup, that might not work for you, is the Artifactory / Maven part. The policy is that only release artifacts are allowed in Artifactory. This reduces the noise and keeps the Artifactory slick (it is getting rather big anyway). The problem is with building dependencies for feature branches that are always snapshots. For this to work, and to be able to use different nodes, you still need something like Artifactory. If building on a single node, you can just put “install” as a Maven goal and you’d get the dependency on that computer. For multiple nodes, one idea was to tell all the nodes about each others maven repositories, but that seamed like too much maintenance.

So, the workaround was to create a webdav Apache folder that Jenkins could use to put all builds into. And, that same repository was referenced in Maven settings on each of the nodes, and on all of the developers machines too. This enabled Jenkins to “know” about all the feature branches artifacts, while not putting too much strain on Artifactory. And, that Maven repository can be cleaned periodically without peril.

This setup is pretty much it. With it you get to build a specified branch at any time. The feature branches build nicely throughout the entire stack and you have a single project on Jenkins that prevents concurrent builds of the same code base (so resource issue is no longer valid).

Still, nothing is perfect and there are a few gotchas:

  • if you are using some wall plugin, you can’t really tell the status of the project since they show only the last build status, which can be feature or not – this can be a good thing if you decide to treat broken feature builds as a bad thing 🙂
  • you get to build only one branch at the time, for many commits on the same project, you could wait a long time for Jenkins to build your commit
  • Jenkins can’t really decide correctly on Maven dependencies since at one point in time a project might reference some feature snapshot (from pom.xml) whilst at other time it might reference the release version on the same project

The last point is the most problematic one. It will prevent Maven to correctly compute project dependencies which will directly influence the upstream/downstream build triggers in Jenkins. You’ll probably have to manually start builds for such situations or just commit changes again. If developers in your team are a bit disciplined, this might not be an issue after all. A nice idea on how to avoid this problem is to create a separate Jenkins project for each branch, automatically as noted here. I am in the process of enabling this support for Gitlab Jenkins plugin so stay tuned 🙂

Advertisements
Tagged , , ,

12 thoughts on “Jenkins & Git branches

  1. […] to Jenkins & Git branches post, if you want to make it play nicely with M2 Release plugin, just configure the Jenkins project […]

  2. elvanja says:

    In case you are having trouble performing releases with M2 Release plugin from a non master branch, the https://shcatula.wordpress.com/2013/07/19/parametrized-jenkins-releases-from-non-master-branches/ contains a fix.

  3. Thank you for your post.

    I think you also can setup Jenkins to build every branch by playing with wildcards in the Branches to build – Branch Specifier (blank for default)” field.

    You can type things like:
    **
    or
    master, develop, feature**

    http://stackoverflow.com/questions/15344955/hudson-jenkins-git-build-all-branches

    Cheers

    • elvanja says:

      Hi, yes, that is true.

      However, this setup resolves the following also:

      • you want to build only the branch that has been committed
      • you want to be build a specific branch manually

      For the first case, it can happen that there are commits to multiple branches at the same time.
      In that case, I’d want Jenkins to build exactly the branch matching the data from Gitlab payload, otherwise I’d get branch A built and in build details, Jenkins would point me to branch B commits.

      For the second case, it relates just to the requirement to build a specific branch manually.
      Hence, branch specifier must be entered somewhere.
      I found it cumbersome to configure the Jenkins project each time, and using parameters solves this nicely.

      Hope this helps!
      Thanks for reading and commenting 🙂

  4. PR says:

    Will this work for tags as well?

    • elvanja says:

      Hi Raste, do I understand you correctly that you are asking if described builds will be triggered by tags? If so, that will not work as a trigger with most Git servers I am aware of. That is simply because tagging a commit does not trigger anything on Git server side. But, maybe you can approach this in one of two ways:

      • add parameter for tag, configure Git section to receive the tag (nothing is the default)
      • use Maven release plugin that actually does all this for you – increases the version, tags the build/commit and releases it all
  5. Affi says:

    If I build a a project in eclipse using maven, then what all files I need to put in the gitlab so that jenkin can successfully execute it, e.g. only .java and pom.xml file uploading to gitlab should be sufficient for the jenkins to build the project. Can you please help me understand it?

    • elvanja says:

      Usually, you would want to setup everything on Jenkins before using the hook.
      E.g. this would be my flow:

      • make sure my maven project is working locally, e.g. mvn clean install
      • create a new maven project on Jenkins
      • set it up to use Git as source and point it to project’s Gitlab clone url
      • keep configuring Jenkins project until manual build on Jenkins works

      At this point, everything should run on Jenkins manually. You have plenty of information lying around that can help with setting up maven project on Jenkins. When this is done, you are ready to use the hook. Then you just need to configure Gitlab project (add hook url to it) and it should work just fine. The hook plugin just exposes a method for Gitlab hook to send a trigger to Jenkins, saying “hey, I have something new, build it”. You need to make Jenkins work manually before this step. Hope this helps!

      • Affi says:

        Thanks elvanja for your detailed information. I have set up everything and its working fine locally, only issue is connectivity to gitlab. Hopefully I would be able to fix that as well..

      • elvanja says:

        Sure thing 🙂
        Btw, if you can build projects from Gitlab on Jenkins, that means your Jenkins can see Gitlab without issues (Git connectivity at least). For the hook to work, you just need to add Jenkins public URL to hook and your Gitlab server needs to be able to connect to it. Good luck!

  6. Ron Smits says:

    Nice write up. It does not however touch on another problem you can have when doing feature branch development. When several feature branches exist for one project, how does Jenkins build two artifacts of the same project? something like project.version-BRANCH-SNAPSHOT?

    • elvanja says:

      Hi Ron,
      that is usually solved by being careful to set feature version in pom.xml. For example project.version-feature-x-SNAPSHOT. That way, each feature branch has it’s own artifact.

      The problem is that developers need to maintain this manually, but even there there are workarounds/helpers. For example, in Stash (Git) you can create a plugin that will check if committed pom.xml has appropriate version for feature (non master) branches. You can’t do this in Jenkins, but you can prevent errors if you can control your Git server.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: