How can I associate artifact versions with a specific Jenkins build?

Article ID:360032961591
3 minute readKnowledge base

Issue

After building an artifact, you want an easy way to associate the artifact’s version number with the Jenkins job that built it. This allows you to refer back to the logs and other data connected to that artifact version.

Resolution

It is common for Jenkins users to want a way to associate the version of an artifact with the specific Jenkins job that built it. Often, they will try to take the Jenkins build number, which increments each time the job runs, and use that as part of their artifact versioning scheme. For example, build #57 will generate version 1.57 of an artifact. This practice has some pitfalls, however. For example, when teams increment the major version number of their project, they would likely want to start their minor version back at 1. Or, when working with branches which are then merged to controller, the branch build numbers will not be in sync with the controller build number. Further, if an entirely new Jenkins job is created to build a project, the build numbers will reset again. These challenges lead users to want to manipulate the Jenkins build numbers from within their jobs, and this is not really possible or desired. Jenkins expects build numbers to be an immutable, incrementing integer; nothing more. Although build numbers look like an attractive source of values for artifact versions, there are better alternatives that won’t cause these headaches.

First, we recommend that version numbers be generated and updated by your build process. Semantic versioning is a common scheme, widely accepted in the software industry. Most build tools, such as Maven, Gradle, Rake, and others, have plugins that allow them to generate semantic version numbers for every build. This version number should be maintained in a file, and committed back to the SCM system. If commits to your SCM are set up to trigger Jenkins jobs, this could cause an endless loop of builds. To avoid this, you may need to configure your Freestyle jobs with the "Polling ignores commits from certain users" option, use the Ignore Committer Strategy plugin for Pipeline Multibranch jobs, or use another similar option for whatever job trigger solution you are using. The idea is to have Jenkins ignore commits from itself.

With automatic semantic versioning in place, you can then populate the build description with the version number you generate. In Pipeline jobs you would have a step that reads the version number into a variable. If the version is stored by itself in a file, you can just read that:

def myVersion = readFile('version.txt')

If you need to do some additional parsing with shell commands, you can capture the output of a shell step. This is a general example - you would need to craft a shell command or group of commands that results in a clean output based on your environment:

def myVersion = sh(script:"grep version pom.xml", returnStdout: true).trim() as String

Once you have the version number in a variable, set the current build description to that value:

currentBuild.description = myVersion

If you have configured your job to delete old build history, you may want to retain builds that successfully generated an artifact (or successfully deployed to production, etc.). The following Pipeline step would save the build forever:

currentBuild.keepLog = true

With the version stored in the build description, you can use Jenkins’s search function to locate an artifact version’s build by searching for the version number. The global search would find all builds that match that version, or you can search a specific job’s history from the job’s page. The number will also appear in the 'Build History' list for the job.