A shared library for Jenkins pipelines providing classes and methods supporting SEMantic versioning and Continuous Integration.
Install this as a shared pipeline library for Jenkins. See https://jenkins.io/doc/book/pipeline/shared-libraries/
For a detailed description, see Stainless AI Dev Flow.
Summary:
- All released versions are released from a single "master" branch
- All released versions have tags
- All other artifacts are prerelease versions
- Each build creates a unique artifact name based on project name, branch name, build number and semantic version
This library helps enforce these points, calculating version and artifact names automatically and throwing errors when required conditions are not met.
Features:
- Parses and understand common semantic version formats in tags and branches like vX.Y.Z and projectName-vX.Y.Z
- Supports prefixes like prefix@X.Y.X and prefix-vX.Y.Z
- Automatically extracts and checks commit hashes against tags to ensure release builds are correct
- Supports ordering of version both by time applied and natural semantic ordering
- Automatically ties artifact versions to branch versions where appropriate
What this library does NOT do:
- Manage branches and tags to ensure versions are what you WANT. Learn the method and do it manually, or use a tool.
In the configuration of your pipeline: Pipeline Libraries
- Name: 'jenkins-semci'
- Default version: 'master'
- Source Code Management: Modern SCM: GitHub (select credentials and set URL here)
Include the library in your plugin:
@Library('jenkins-semci')
import ai.stainless.jenkins.ReleaseManager
// other imports
You can then use the library's objects in your pipeline:
def releaseManager = new ReleaseManager(this)
pipeline {
// my pipeline
}
Class that parses and represents a semantic version. Defaults to 0.0.1
. Supports prefixes and the common delimeters @
and v
. For example, the following are
all valid, parse-able semantic versions:
1.2.3
1.2.3-prerelease.alpha.1
v1.2.3
@1.2.3
myproject-v1.2.3
myproject@1.2.3-prerelease.alpha.1
/my/path/before/my/prefix-v1.2.3
Properties:
def path // prefix path, optional slash delimited path with last element being prefix
def prefix // prefix, a string identifier before the version
def prefixDelim = '-' // delimiter to separate the prefix and version in artifact names
def preReleaseDelim = '-' // delimited to separate the version and prerelease string in artifact names
def v = null // expected version delimiter, currently supports 'v' and '@'
int major = 0 // the major version
int minor = 0 // the minor version
int patch = 1 // the patch version
def prerelease // the prerelease string
Methods:
void bumpPatch() // bump the patch version in this Semver
void bumpMinor() // bump minor and set patch to 0
void bumpMajor() // bump major and set minor and patch to 0
static Semver parse( // parse the supplied semverString and return a Semver object.
String semverString, // If ignoreErrors = false throw IllegalArgumentExecption when a parse error occurs
boolean ignoreErrors = false)
static Semver fromRef( // parse the supplied GitHub ref into a Semver object. Supports branches, tags and remotes.
String ref, // the path variable will contain the github ref path.
boolean ignoreErrors = false)
String artifactName() // Return a string representing this artifact in a filename-friendly manner.
String versionString() // Return only the version if a prefix is present
Map toMap() // Return a map with the property values (filtered to remove variables like "class", etc).
int compareTo(Semver o) // Implements Comparable against other semantic versions
String toString() // Return a String representation of the object
String toStringWithPattern(String withPattern) // Return a String representation of the version using the specified pattern
Calling toString() on this class will use a default formatter to render the Semver as a String. The default format is:
"M.m.p'-'?P'+'?B"
See SemverFormatter below for more details.
There is also a toStringWithPattern(withPattern) variant that allows you to provide a custom pattern string at call time.
A class that can format a Semver using customizable templates, similar to the way DateFormatter works in Java.
* Symbol Meaning
* _____ _______
* M Major version number
* m Minor version number
* p Patch version number
* P Prerelease
* B Build metadata
* . Semantic version separator
* ' Quote for static text
* ? Omit previous literal if next field is empty, e.g., '-'?B Will omit the dash if B is empty
For example,
def s = new Semver(prerelease: 'mybranch', buildMetadata: 'mybuildnumber')
assert SemverFormatter.ofPattern("M.m.p'-'P'-SNAPSHOT'").format(s) == '0.1.0-mybranch-SNAPSHOT'
A class that can be imported into a Jenkins pipeline. Due to Jenkins Groovy sandbox restrictions, Semver objects are difficult
to use directly in the pipeline so this class returns String
objects.
The ReleaseManager uses GitHub branch and tag data to determine what the version of the build is. For a detailed explanation of the CI/CD rules that inspired these tools, see Stainless AI Dev Flow
You can pass the values returned by these methods into your build script to set the filename, for example:
pipeline {
steps {
step {
sh "./myscript --artifactName=${releaseManager.artifactName()} --version=${releaseManager.artifactVersion()}"
}
}
}
The release manager will examine the build properties, git repo branch and tags and return the expected artifact name and version. For example, if we are working in a repo called "example" with a Jenkins job of "example":
Branch | Last Tag (by time) | Last Tag (by version) | BUILD_NUMBER | artifactName() | artifactVersion() |
---|---|---|---|---|---|
master | (none) | (none) | (any) | ERROR: No tag | ERROR: No tag |
develop | (none) | (none) | 1 | example-0.0.1-1-SNAPSHOT | 0.0.1-1-SNAPSHOT |
master | v1.2.3 | v1.2.3 | (any) | example-1.2.3 | 1.2.3 |
master | myprefix@1.2.3 | myprefix@1.2.3 | (any) | myprefix-1.2.3 | 1.2.3 |
develop | myprefix-v1.2.3 | myprefix-v1.2.3 | (any) | myprefix-1.3.0-develop-SNAPSHOT | 1.3.0-develop-SNAPSHOT |
v2.0.0 | v1.9.5 | v1.9.5 | (any) | example-2.0.0 | 2.0.0 |
origin/myprefix@2.0.0 | myprefix@2.0.6 | myprefix@2.0.6 | (any) | myprefix-2.0.6 | 2.0.6 |
origin/myprefix@2.1.0 | myprefix@2.0.6 | (any) | myprefix-2.1.0 | 2.1.0 | |
origin/myprefix@2.0.1 | (any) | (any) | (any) | ERROR: Invalid branch name | ERROR: Invalid branch name |
Methods:
ReleaseManager(def script) // Constructor
String artifactName() // Returns the artifact name. Uses the semver prefix if exists, otherwise, the Jenkins JOB_NAME
String artifactVersion() // Returns the latest artifact version according to the GitHub branch and tag data
String artifactVersion(String withPattern) // Returns the latest artifact version according to the GitHub branch and tag data using the specified pattern
You can customize the prerelease
and buildMetadata
fields of the semantic version generated by the ReleaseManager
by setting the prerelease
and buildMetadata
fields to a GString template mapping variables from the Jenkins script
environment. For example:
def releaseManager = new ReleaseManager(this)
releaseManager.setPrerelease('%BRANCH_NAME%')
releaseManager.setBuildMetadata('%BUILD_NUMBER%')
This configuration will create versions of the form 1.2.3-mybranch+buildnumber
. You can append custom strings, just
ensure they adhere to the semantic versioning standard. Any strings surrounded by '%' will be treated as variables, as
of this writing, only %BUILD_NUMBER% and %BRANCH_NAME% are supported.