HTTPS SSH

Maven Feature Branch Extension

Simplifies the use of feature branches with Maven in a GitFlow style workflow.

Why

Using feature branches in a GitFlow style workflow with continuous integration can be complicated. The version of the artifacts needs to be different when on a feature branch if all builds deploy to the same snapshot repository, otherwise builds will overwrite one another. This means that the first commit on the feature branch needs to change the version number. Developers need to remember to do this, or you need a tool to handle this. This also has to be undone in the last commit or you run the risk of changing the version number of the develop branch after a merge. Add in branching two or more dependent projects at once and it becomes even more complicated and error prone.

How

This Maven extension addresses the problem from the other direction. Instead of changing the version number when on a feature branch, we change the repository. Each feature is deployed into a subdirectory, based on their branch name, in a remote repository that is only for feature branches. There is no risk of artifacts being overwritten. Version numbers do not change. Branching and merging with Git stays simple (like it was meant to be!).

The extension gets the current Git branch and resolves a property in the URL of the repository so that artifacts can be stored and retrieved correctly. It also manages caching and fetching of artifacts to the local repository so that artifacts are taken from the feature branch repository, if they exist, when working from a feature branch.

An extension is needed to handle the Git branch and URL evaluation because Maven repositories are evaluated and created before the Maven lifecycle starts. Therefore it is too late to handle this with a normal plugin.

Getting Started

These instructions will get you up and running with the extension in your build process.

Prerequisites

  • Maven 3.3.1 or higher is required to use the extension.
  • Maven 3.3.1 requires JDK 1.7

Install

Download the project and build and deploy it to your remote repository.

In your project, create a .mvn folder and place extensions.xml in that folder.

Setup

These are the changes you will need to make to your current configuration.

Create new repository

Add a new snapshot repository to your binary repository manager for the feature branch snapshots. It will need a custom layout instead of the normal Maven 2 layout. This is because we will segregate each feature branch's artifacts into their own directory. Note the name when you create the repository. We named our "libs-snapshot-feature-local". Select "Suppress POM Consistency Checks" for the repository. This is necessary due to the non-standard layout used.

We use Artifactory to manage our repositories. These are our values for the custom layout:

  • Layout Name - Feature-branch-layout
  • Artifact Path Pattern - (feature/[jiraKey<(?:[A-Z]+)-(?:[0-9]+)>]/)[orgPath]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).[ext]
  • Distinctive Descriptor Path Pattern - (feature/[jiraKey<(?:[A-Z]+)-(?:[0-9]+)>]/)[orgPath]/[module]/[baseRev](-[folderItegRev])/[module]-[baseRev](-[fileItegRev])(-[classifier]).pom
  • Folder Integration Revision RegExp - SNAPSHOT
  • File Integration Revision RegExp - SNAPSHOT|(?:(?:[0-9]{8}.[0-9]{6})-(?:[0-9]+))

This appends "feature/<key>" to the beginning of the standard Maven 2 layout. The second part of our features is a JIRA key for the story. Replace the JIRA regex, [jiraKey<(?:[A-Z]+)-(?:[0-9]+)>], with something else if it does not fit your needs.

Add the feature repository to settings.xml

Following the name used above you will have something like:

<repository>
    <id>libs-feature</id>
    <name>libs-feature</name>
    <url>http://artifactory-server.com/artifactory/libs-snapshot-feature-local/${git.branch}</url>
    <releases>
        <enabled>false</enabled>
    </releases>
    <snapshots>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
        <checksumPolicy>warn</checksumPolicy>
    </snapshots>
</repository>

Note the property at the end of the url, ${git.branch}. Any property name may be used here. In the next step the property is set so it will be passed on to the plugin.

Configuration properties

Configuration properties need to be set in your settings.xml: features.repositoryName - (Required) the name of the feature branch repository in settings.xml features.branchProperty - (Required) the property that will evaluate to the current Git branch * dotGitDirectory - (Optional) the location of your .git folder. If not set it will first look in ${project.basedir}/.git then move up the chain of parent folders until it finds the .git folder or fails. You probably do not need to set this.

Example from this setup: <properties> <features.repositoryName>libs-feature</features.repositoryName> <features.branchProperty>git.branch</features.branchProperty> </properties>

Deploy to the feature branch repository during CI builds.

There are a number of ways to do this. In our case we use a profile that is enabled for feature branch builds. The profile looks like this.

<profile>
    <id>FEATURE</id>
    <distributionManagement>
        <snapshotRepository>
            <name>libs-snapshot</name>
            <id>${feature.repository.id}</id>
            <url>${feature.repository.url}</url>
        </snapshotRepository>
    </distributionManagement>
</profile>

In the properties section of the pom, we set the two references properties.

<feature.repository.id>hce-libs-feature</feature.repository.id>       
<feature.repository.url>http://artifactory-server.com/artifactory/libs-snapshot-feature-local/${git.branch}</feature.repository.url>

Notice that the property ${git.branch} is used at the end of the URL. This URL is used for the deployment. The previous one in settings.xml is used for pulling.

Enable this profile in your feature branch builds. In this case it would look like mvn clean deploy -U -P FEATURE.

Use

After the setup, use of the extension should be seamless. If a develop is on a feature branch Maven will pull from the feature repository first. If they are not on a feature branch then nothing will be found there and it will pull from the normal snapshot repository.

CI Builds

Make sure that your CI tool does not checkout the Git branch in detached head mode. The extension must be able to get the branch name. * Jenkins: Using the scm step, you have to provide the git.branch argument manually. Pass “-Dgit.branch=${env.BRANCH_NAME}” to the build as a workaround.

License

Apache License 2.0

Acknowledgments