Everett Toews

Helping you shave narwhals

Semantic Version Comparison in a Jenkins pipeline

18 Feb 2020

Versioning is fundamental to reasoning about software development and deployment. It’s not just the software that you release that needs to be versioned. All of the supporting components need to be versioned too, such as database schemas, message formats, protocols, APIs, dependencies, etc. If you don’t version all of these components, you’re at a severe disadvantage when it comes to understanding what’s in a release or debugging a problem.

One popular type of versioning is known as semantic versioning (semver). Semantic versioning is “a simple set of rules and requirements that dictate how version numbers are assigned and incremented.” It allows people to communicate meaning using a particular format for the version they assign to the release of a component. Whether or not you’re a fan of semver, it’s very useful to have a well-known and commonly understood specification for your versions. It also makes it possible to compare one version with another of the same component.

Often times, the purpose of Jenkins pipelines is to deploy software. That is to say, to change the current version of a software component to the desired version. You may want to roll forward by increasing the version of the component, you may want to roll backward by decreasing the version of the component, or you may want to do nothing at all if the version hasn’t changed. Depending on the component being deployed, you may need to take different actions based on whether the desired version is greater than, equal to, or less than the current version.

I’ve written the small library method semver_compare that allows you to compare semantic versions in a Jenkins pipeline easily. What better way to learn how to use it than by trying it out right away!

Versions

At the time of writing, the versions of all of the software used in this post are:

  • Docker Desktop: 4.1.1
  • Docker: 20.10.8
  • Jenkins: Long-Term Support (LTS)

Install Jenkins

To make it convenient to install Jenkins, I’m using Docker Desktop to get it up and running.

This command will run a Docker container in interactive mode (you’ll be able to see the log output in your terminal) and the container will be removed when you kill the command with a Ctrl+C. However, any changes you make to Jenkins while it’s running will be saved in the jenkins_home volume so you can always run it again and pick up where you left off.

docker run --interactive --tty --rm \
  --volume jenkins_home:/var/jenkins_home \
  --publish 8080:8080 --publish 50000:50000 \
  jenkins/jenkins:lts

Open Jenkins at http://localhost:8080/.

Note: If you want to start over from scratch, kill the command with Ctrl+C, delete the volume with docker volume rm jenkins_home, and run the command above again.

Setup Jenkins

You’ll need to go through a wizard to setup Jenkins.

  1. On the Unlock Jenkins screen:
    • Find the password in the Jenkins log output under “Please use the following password to proceed to installation”
    • Enter the password into the Administrator password field
  2. Click Install suggested plugins
  3. On the Create First Admin User screen:
    • Username: [pick-a-username]
    • Password: [pick-a-password]
    • Full name: [pick-a-full-name]
    • E-mail address: [pick-an-email-address]
  4. On the Instance Configuration screen:
    • Jenkins URL: http://localhost:8080/

Configure Global Pipeline Libraries

To make use of the method, you’ll need to configure a global pipeline library.

  1. Click Manage Jenkins
  2. Click Configure System
  3. Scroll down to the Global Pipeline Libraries section and click Add
    1. Name: semver-compare-lib
    2. Default version: master
    3. Load implicitly: Unchecked
    4. Allow default version to be overridden: Checked
    5. Include @Library changes in job recent changes: Unchecked
    6. Retrieval method: Modern SCM
    7. Source Code Management: Git
    8. Git: https://github.com/etoews/jenkins-semver-compare.git
  4. Save

Create Pipeline

Create a pipeline to run the example pipeline.

  1. Click New Item
    1. Enter an item name: semver-compare
    2. Click Pipeline
  2. OK

Configure Pipeline

Configure the example pipeline.

  1. Scroll down to the Pipeline section
    1. Definition: Pipeline script from SCM
    2. SCM: Git
    3. Repository URL: https://github.com/etoews/jenkins-semver-compare.git
  2. Save

Run Pipeline

On the Pipeline screen:

  1. Click Build Now
  2. Wait a moment for the pipeline to complete
  3. Under Build History click #1
  4. Click Console Output

Pipeline Results

The pipeline result is the output of running this Jenkinsfile.

First of all, notice how the Jenkinsfile itself is using a particular version of the global pipeline library with the line @Library('semver-compare-lib@1.0.0') at the very top. Version all the things!

Then the pipeline runs through successive version comparisons. Some output is printed based on whether the desired version is greater than, equal to, or less than the current version. If you were to use this in your own pipeline, you could take different actions depending on each case.

Finally, the last pair of versions don’t conform to the semver specification. Some error handling is invoked and a helpful error message is printed.

Coda

Versioning your software components is absolutely fundamental to running a stable and resilient system that can be reasoned about. Using semantic versioning has many advantages including the ability to compare one version with another of the same component. This post showed you how you can easily make those comparisons in a Jenkins pipeline. Go ahead and give it a try in your own pipelines.

Note: The library method uses SemanticVersion.groovy to do the comparison. The source code for that class was copied from How to implement a single class Java parser for semantic versioning with correct precedence ordering with my own alterations to make it work in a Jenkins pipeline. The source code was copied and modified as permitted by the MIT License. My thanks go out to the original author Patrick Ahlbrecht.