
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:-
Snapshot
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
Release
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)
springBootApp-1.12.0-RELEASE.jar
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: https://xxxx/repository/releases/io/polarpoint/spring/springBootApp/1.12.0-RELEASE/springBootApp-1.12.0-RELEASE.jar. 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:
#!/bin/groovy @Library(‘pipeline-library@development') import io.polarpoint.workflow.* /* use the global workflow library */
With the library loaded we can specify the pipeline we wish to use.
properties([ 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 Repository:packages
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.
References
Shared Libraries
https://jenkins.io/doc/book/pipeline/shared-libraries/index.htmlNexus Repository
https://www.sonatype.com/nexus-repository-ossSemantic Versioning
https://github.com/vdurmont/semver4j