Jenkins is the world's leading open-source automation server, used by companies large and small to implement continuous integration (CI) and continuous delivery (CD).
The Jenkins project estimates that the number of users at over fifteen million, and for good reason: Jenkins is an easy tool to get started with and provides hundreds of plugins developed by numerous contributors to support building, deploying and automating any project. But it isn't an end-to-end CI/CD solution: Jenkins was originally designed as a CI tool, and its CD functionality is bolted on, rather than a first-class solution.
BuildMaster is designed to continuously deliver your applications and components from source code to production, but unlike monolith CI/CD platforms like Azure DevOps or GitLab, you can work the tools and processes you're already using, including a tight integration with Jenkins. BuildMaster adds to security to Jenkins and makes it easy to set up builds and automated tests without the effort of retraining users.
While BuildMaster can certainly perform continuous integration and build automation, there are a lot of good reasons to continue to use Jenkins:
A large percentage of customers use BuildMaster alongside Jenkins in exactly this manner and for these exact reasons.
**See it live! See an example of Jenkins integration by creating a new application using the Jenkins Import template.
While both Jenkins and BuildMaster use the term "build," the concepts are a bit different.
Integrating Jenkins with BuildMaster is easy: Simply navigate to the Admin > Extensions page in your instance of BuildMaster and click on the Jenkins extension to install it.
If your instance doesn't have internet access, you can manually install the Jenkins extension after downloading the Jenkins Extension Package.
A Jenkins Resource Credential is used to connect BuildMaster to your Jenkins server. You can create as many resource credentials as you need, at both the system- and application-level, which means you can connect to multiple Jenkins servers and share those connections across applications, letting your teams continue working with their various instances of Jenkins.
These credentials are effectively a username and password (or API token), so we recommend creating a specific account with the minimum amount of privileges required to interact with Jenkins, typically the permissions to the specific project or build configuration. Public servers (i.e., open-source) or internal servers that are firewalled off that don't require authentication can simply use guest authentication by leaving the username and password empty.
To connect to a standalone instance of Jenkins, make sure the Jenkins Server URL, username, and API token of the resource credential are configured. The API token can be obtained from Jenkins by selecting a user from the People page, opening the Configure option, and adding a token for the selected user.
Jenkins can be configured to not only compile your application's source code and run automated tests against it but also to capture "artifacts" of that process. These artifacts typically include whatever files the compiler output, i.e., your compiled application.
Importing an artifact ensures BuildMaster will be able to deploy it to future stages whether Jenkins is accessible or not, and you can capture these artifacts in BuildMaster using the Jenkins::Import-Artifact
operation.
Here's how to import the profit-calc-web.zip
artifact from the last successful build within the ProfitCalc
project (using the v8builds build configuration):
Jenkins::Import-Artifact
(
Credentials: Jenkins,
Job: DemoJob,
BuildNumber: lastSuccessful,
Artifact: archive.zip,
JenkinsBuildNumber => $JenkinsBuildNumber
);
Note that you can also capture the actual build number using the JenkinsBuildNumber
output variable, which will help create a visible association between the BuildMaster build and Jenkins. And like all operations in OtterScript, you specify variables for any of these parameters.
You can queue builds in Jenkins using the Jenkins::Queue-Build
operation. There a few reasons you may want to do this:
Here's how to queue a build within the ProfitCalc
project (using the v8builds build configuration):
Jenkins::Queue-Build
(
Credentials: Jenkins,
Job: DemoJob,
AdditionalParameters: buildType=release¶m2=value,
JenkinsBuildNumber => $JenkinsBuildNumber
);
This example will also pass the buildType=release
as an additional parameter and wait for the build to be completed.
BuildMaster can reference Jenkins builds using the following special build names instead of direct build numbers:
lastSuccessfulBuild
- compilation reported no errorslastStableBuild
� latest successful build and no publisher reports it as unstablelastBuild
- the latest build, including in-progress builds (should rarely be referenced in BuildMaster)lastCompletedBuild
� the latest build that has finished, no matter what the result (should rarely be referenced in BuildMaster)Additionally, builds may be referenced by their Jenkins build number as opposed to name. Generally speaking, you should avoid using a hard-coded build number in OtterScript. Instead, you should capture the actual build number as a variable, and use that instead.
Jenkins has support for builds built from feature branches, and both the import and queue operations support using these branches.
For example, you can import artifacts from the last successful calculation-fix
build on the ProfitCalc
project by specifying the Branch parameter on the operation.
Jenkins::Import-Artifact
(
Credentials: KramericaJenkins,
Project: ProfitCalc,
BuildConfiguration: v8builds,
Branch: calculation-fix,
Artifact: profit-calc-web.zip
);
To push artifacts from Jenkins to BuildMaster or an artifact repository like ProGet refer to the Trigger from External Systems documentation to see the variety of ways this can be accomplished.
Whether you prompt for a build number or ask Jenkins for the lastSuccessfulBuild
, BuildMaster lets you capture and link this back to the Jenkins build number.
Capturing this reference at build time is very important so you can see exactly what code was used to create the build artifacts. This carries multiple benefits:
BuildMaster gives you a lot of flexibility in capturing this information. Although you could always manually dig through the execution logs to find this information, there's a simpler approach:
This example uses both the Jenkins::Queue-Build
operation and the Set-BuildVariable
operation to queue a build in Jenkins and then set a configuration variable.
Jenkins::Queue-Build
(
Credentials: Jenkins,
Job: DemoJob,
AdditionalParameters: token=TOKEN&buildType=release,
JenkinsBuildNumber => $JenkinsBuildNumber
)
Set-BuildVariable JenkinsBuildNumber
(
Value: $ JenkinsBuildNumber
);
Once this variable is captured, a variable value renderer can be used to link $JenkinsBuildNumber
back to Jenkins on the Build Overview page, for example:
<a class="ci-icon jenkins" href="$JenkinsUrl(Jenkins, $JenkinsJobName, $JenkinsBuildNumber, $JenkinsBranchName)" target="_blank">Build #$JenkinsBuildNumber</a>
This example assumes you are using a Jenkins Credential named 'Jenkins'. The $JenkinsBranchName
variable is optional but included to demonstrate usage in the event that you use multi-branch builds in Jenkins. The extension also provides a JenkinsBranchName
variable function that will return an empty string when not overridden by a variable of the same name. This means that this example will also work for applications that don't have a JenkinsBranchName
.
Once an artifact is captured via the optional queuing followed by "import" operations in a build plan, future stages simply need to use the Deploy-Artifact
operation to deploy to any number of servers or targets:
Deploy-Artifact archive.zip
(
To: E:\Websites\ProfitCalc
);
With release templates you can build a deployment pipeline that prompts for build numbers and/or job names based on data directly from Jenkins. When creating the release template, the following list variable source options are available:
Refer to the Choosing a Specific Jenkins Build with BuildMaster tutorial for more information.
At the end of a build process in Jenkins, you may wish to automatically import those artifacts into BuildMaster so you can smoothly move these CI builds into the CD pipeline for testing and release. This can be programmatically done either using the Release & Build Deployment API or the Jenkins BuildMaster Plugin.
When you want to specify the build number to import from, simply specify that as a variable (e.g., $JenkinsBuildNumber
) when making the API request. That will automatically create a build-scoped variable, which can then be used in your OtterScript plans to import artifacts from that build.
This example also demonstrates triggering a build from a multi-branch job.
# The BuildMaster APIs used here are documented at https://docs.inedo.com/docs/buildmaster/reference/api/release-and-build
$ErrorActionPreference="Stop"
$buildmasterUrl = "http://inedo:8622"
$apiKey = "<secret>"
$applicationName = "JenkinsExtension - Demo MultiBranch"
# These two variables should be populated from the Jenkins variables BRANCH_NAME and BUILD_NUMBER
$jenkinsBranchName = "branch1"
$jenkinsBuildNumber = "lastSuccessfulBuild"
function Invoke-BuildMasterAPI($api, $body) {
$response = Invoke-WebRequest "${buildmasterUrl}/$api" `
-ContentType "application/json" `
-Method POST `
-Headers @{"X-ApiKey" = "$apiKey"} `
-Body "$body" `
-UseBasicParsing
return ($response.Content | ConvertFrom-Json)
}
# Get latest release number
$releases = Invoke-BuildMasterAPI "api/releases" @"
{
"applicationName": "$applicationName",
"status": "ACTIVE"
}
"@
$releaseNumber = $releases[0].number
# Trigger Build - passing in variables
"Create build for '$applicationName', release $releaseNumber"
$build = Invoke-BuildMasterAPI "api/releases/builds/create" @"
{
"applicationName": "$applicationName",
"releaseNumber": "$releaseNumber",
"`$JenkinsBranchName": "$jenkinsBranchName",
"`$JenkinsBuildNumber": "$jenkinsBuildNumber"
}
"@
$buildNumber = $build.number
# Advance to first stage (this is the same as checking the "Automatically advance to <stage name> stage" option in PowerBuilder create build screen)
"Advance build $buildNumber to the first stage"
$deploy = Invoke-BuildMasterAPI "api/releases/builds/deploy" @"
{
"applicationName": "$applicationName",
"releaseNumber": "$releaseNumber",
"buildNumber": "$buildNumber"
}
"@
"Successfully triggered the '$($deploy.pipelineStageName)' stage"
While the APIs provide a convenient method of triggering a build from any system, in Jenkins, we also have the option of using the BuildMaster Jenkins Plugin, which makes triggering builds and promotions from Jenkins even easier. The plugin also has some more advanced features that would be difficult to replicate in a script without it getting overly complicated.
Setup instructions for the plugin are in the Jenkins Wiki Page.
A BuildMaster build can be triggered using the following pipeline script:
pipeline {
agent any
stages {
stage('Build') {
steps {
buildMasterWithApplicationRelease(applicationId: 'JenkinsExtension - Demo') {
bat label: 'Build artifact', script: 'echo "This is Jenkins build %BUILD_NUMBER% for BuildMaster Application \'%BUILDMASTER_APPLICATION_NAME%\' (#%BUILDMASTER_APPLICATION_ID%) Release %BUILDMASTER_RELEASE_NUMBER% - Build %BUILDMASTER_NEXT_BUILD_NUMBER%" > Example.txt'
archiveArtifacts 'Example.txt'
// Jenkins declarative pipeline script has a somewhat restricted syntax. Unfortunately to return package
// number you need to wrap this in a script block
// See: https://jenkins.io/doc/book/pipeline/syntax/#script
script {
BUILDMASTER_BUILD_NUMBER = buildMasterCreateBuild(applicationId: BUILDMASTER_APPLICATION_ID, releaseNumber: BUILDMASTER_RELEASE_NUMBER, deployToFirstStage: [waitUntilCompleted: true], variables: "JenkinsJobName=$JOB_NAME\nJenkinsBuildNumber=$BUILD_NUMBER")
}
buildMasterDeployBuildToStage(stage: 'Integration', applicationId: BUILDMASTER_APPLICATION_ID, releaseNumber: BUILDMASTER_RELEASE_NUMBER, buildNumber: BUILDMASTER_BUILD_NUMBER)
}
}
}
}
}
Is this documentation incorrect or incomplete? Help us by contributing!
This documentation is licensed under CC-BY-SA-4.0 and stored in GitHub.