Handling SNAPSHOT and RELEASE builds with BND

A while back, we reactivated our Nexus and wanted to use bnd to release to an OBR and an the Nexus at the same time. As usual, we have an automated build handled by jenkins, which does a snapshot build from our develop branch and a release build from changes to the master branch. With the Nexus came the requirement to have SNAPSHOT Versions for the continuous build. On First glance bnd provides a mechanism to handle deployment to a nexus and replacement of a SNAPSHOT qualifier.

What needs to be done?

  1. Your bundles need to be versioned in the bnd files like: 1.0.0-SNAPSHOT or 1.0.0.SNAPSHOT (could be case insensitive, but we have never tried it)

  2. Place the following directive in you build.bnd or any project bnd files: “-snapshot: foo”. This will result in a build version of 1.0.0.foo. A “-snapshot:” would result in 1.0.0 or “-snapshot: ${tstamp}” in 1.0.0.

  3. Add a nexus Repository to your build.bnd e.g. like this:

    -plugin.nexus.release: \
    	aQute.bnd.repository.maven.provider.MavenBndRepository;\
    		snapshotUrl=http://yourURL/nexus/repository/maven-snapshots/;\
    		releaseUrl=http://yourURL/nexus/repository/maven-releases/;\
    		index=${.}/nexus.maven;\
    		name='Nexus'
       
    -releaserepo: Nexus //add another repos separated by , to release to more than one repo at the same time
    

If a release build is triggered now, the properly versioned bundles would now land in the specified Nexus and any additional specified repository.

Is there a catch?

To our knowledge, bnd does not discriminate between a snaphot build and a release build. Thus it would never deploy to the snapshot repository and will always replace the snapshot qualifier. This in turn would result in the Nexus complaining about putting a release Version in a snapshot repo. Fortunately we found a solution for it and the following steps have to be taken.

We have the following structure in our cnf project:

cnf\
   \build.bnd
   \releng\
          \release.bnd
          \snapshot.bnd

First we have to get bnd to distinguish between a release and a snapshot release and the bnd file to include. In our case the snapshot build will release to the Nexus snapshot repo and a local release repo, which is then copied to a location where it can be accessed from the outside world. For the release build, we have bnd do a release to the Nexus and an OBR containing all previous released versions as well. The location of the release OBR is set via a variable at build time and acts therefore as our marker for the release build. The variable is named: release.dir

Thus the build.bnd needs something as follows:

#will be true if relase.dir is set
-include: ${if;${def;release.dir};\ 
              ${.}/releng/release.bnd;\
              ${.}/releng/snapshot.bnd\
          }

The snapshot and release bnd files are straight forward:

release.bnd

#Always add a qualifier to -plugin to avoid overwriting plugin definitions in any other bnd file
-plugin.nexus.release: \
	aQute.bnd.repository.maven.provider.MavenBndRepository;\
		snapshotUrl=http://<yourHost>/nexus/repository/maven-snapshots/;\
		releaseUrl=http://<yourHost>/nexus/repository/maven-releases/;\
		index=${.}/nexus.maven;\
		name='Nexus'
-plugin.Release: \
	aQute.bnd.deployer.repository.LocalIndexedRepo; \
		name = Release; \
		pretty = true; \
		local = ${release.dir}
				
#We have to substitude the -SNAPSHOT with an empty String for releases				
-snapshot: 
-releaserepo: Nexus,Release

snapshot.bnd

#Always add a qualifier to -plugin to avoid overwriting plugin definition in any other bnd file
#The Snapshot repo is sold to bnd as the release repo
-plugin.nexus.release: \
	aQute.bnd.repository.maven.provider.MavenBndRepository;\
		releaseUrl=http://<yourHost>/nexus/repository/maven-snapshots/;\
		index=${.}/nexus.maven;\
		name='Nexus'
-plugin.Release: \
	aQute.bnd.deployer.repository.LocalIndexedRepo; \
		name = Release; \
		pretty = true; \
		local = ${build}/release
				
-releaserepo: Nexus,Release

# The -snapshot will not be mentioned, so the qualifier in the bundle versions will stay as they are. 
# Note the Nexus will complain if your versions of any bundle will not have the proper qualifier.

Now a Snapshot build can be triggered via: ./gradlew clean build release and a release build via ./gradlew clean build release -Drelease.dir=

We Hope this helps 😉

by Ilenia Salvadori, Mark Hoffmann, Jürgen Albert