DevOps | Continuous Integration, Continuous Delivery and Continuous Deployment using Jenkins and Maven2 repositories


DevOps | Continuous Integration, Continuous Delivery and Continuous Deployment…

Historically the Maven repository format provided a mechanism to easily resolve dependencies and store artefacts for Java applications supporting users of Apache Maven, Apache Ant/Ivy, Aether, Gradle, and others. However it can be utilised to offer a robust artefact store for other artefacts we wish to store such as zip files as part of our Continuous Delivery pipelines.

Maven Repository Format

Maven repository format can be configured for each repository as follows:-


Continuous development is typically performed with snapshot versions supported by the Snapshot version policy. These version values have to end with -SNAPSHOT. This allows repeated uploads where the actual number used is composed of a date/timestamp and an enumerator and the retrieval can still use the -SNAPSHOT version string.

i.e. springBootApp-1.12.0-SNAPSHOT.jar


A Maven repository can be configured for final release artefacts with only one artefact allowed per version. ( the -RELEASE part isn’t originally part of the Maven2 repository format)


Any efforts to try to update this version result in an error.

Failed to deploy artifacts: Could not transfer artifact io.polarpoint.spring:springBootApp:jar:1.12.0-RELEASE 
from/to releases (https://xxxxx/repository/releases): Failed to transfer file: 
Return code is: 400, ReasonPhrase:Repository does not allow updating assets: releases.

Maven in Continuous Integration and Deployment

Using the Maven repository format to store our artefacts and using a modified version of git flow we can start to ensure we have only one stable build (-RELEASE) when the development branch is merged to the master branch.

Semantic Versioning

Semantic Versioning is a convention used to provide a meaning to versions. The Semantic Versioning concept is simple: all versions have 3 digits: x.y.z.

Our global shared library maintains the version numbers for all artefacts, to a set of rules:

  • increment the major version when you make breaking changes
  • increment the minor version when you add functionality in a backward-compatible manner
  • increment the patch version when you make backward-compatible bug fixes

Jenkins Global Libraries

The requirement for a common pipeline that can be used in multiple projects does not only emerge in microservice architectures. 

It’s advantageous in ensuring projects and workflows follow a set blueprint maintaining a consistent and repeatable process and eliminating duplicate code.

Using libraries

To access shared libraries, the Jenkinsfile needs to use the @Library annotation, specifying the library’s name:



import io.polarpoint.workflow.*

/* use the global workflow library


With the library loaded we can specify the pipeline we wish to use.


 	buildDiscarder(logRotator(artifactDaysToKeepStr: '30', artifactNumToKeepStr: '10', daysToKeepStr: '30', numToKeepStr: '10')),

 	[$class: 'RebuildSettings', autoRebuild: true, rebuildDisabled: false]


def configuration = "pipelines/conf/configuration.json"
invokePipeline('springBootApp', configuration )

InvokePipeline() is loaded from the Global shared library and the following stages are loaded.


if (env.BRANCH_NAME =~ /^master/) {

    echo("[Pipeline] master branch being build:" + application)

    versioningWorkflow(configurationContext, 'master',scmVars)

} else if (env.BRANCH_NAME =~ /^(development$|hotfix\/)/) {

    echo("[Pipeline] development or hotfix branch being build and tagged:" + application)

    versioningWorkflow(configurationContext, env.BRANCH_NAME,scmVars)

} else if (env.BRANCH_NAME =~ /(SB|NT)-\d*/) {

    echo("[Pipeline] feature branch being built:" + application)

    buildWorkflow(configurationContext, env.BRANCH_NAME, scmVars)


Feature branches builds call buildWorkflow() containing stages such as unit tests, code coverage, code quality and quality gates.

A Pull Request is raised requiring a peer review before being merged to the development. The merge to the development branch triggers Jenkins to execute the versioningWorkflow() which runs through tests before git tags the artefacts and updates the version number of the artefact.

14:43:48 + git add pipelines/conf/configuration.json
[Pipeline] sh
14:43:51 + git commit -m [jenkins-versioned] v1.12.0 + jenkins-development-257
[Pipeline] sh
14:43:53 + git tag -a v1.12.0 -m v1.12.0
[Pipeline] sh
14:43:55 + git push https://****:****@xxxxx/springBootApp.git
14:44:02 To https://xxxxxx/springBootApp.git
14:44:02    06c8098..203930e  development -> development
14:44:02 + git push https://****:****@xxxx/springBootApp.git --tags
14:44:09 To https://****:****@xxxxx/springBootApp.git
14:44:09  * [new tag]         v1.12.0 -> v1.12.0

A snapshot release of the build artefact is stored as a Maven2 snapshot.

Uploading artifact springBootApp-1.12.0-SNAPSHOT.jar started....

GroupId: io.polarpoint.spring

ArtifactId: springBootApp

Classifier: null

Type: jar

Version: 1.12.0-SNAPSHOT

File: springBootApp-1.12.0-SNAPSHOT.jar


Once all tests are completed we can confidently merge development to master to create a final release.

git checkout development

Merge it with the master branch with “ours” strategy.  

git merge -s ours master

Checkout to the master branch

git checkout master

Merge the development branch to master

git merge development 
git push

The merge to the master branch triggers Jenkins to execute the versioningWorkflow()which runs through tests before uploading the stable version of our code.

Although we are running the versioningWorkflow() on the master branch we only apply versioning for development or hotfix branches.

Using Jenkins it is possible to create commonly used pipelines for some of the more mundane but essential stages of Continuous Integration.


Shared Libraries

Nexus Repository

Semantic Versioning

Leave a Reply

Your email address will not be published.