BuildMaster Documentation

Integrating Jenkins with BuildMaster

  • Last Modified: 2020-01-09

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:

  • Save time and resources on retraining - if you're already familiar with Jenkins and the processes you've set up, you won't have to spend time learning something new
  • Reuse complex automations - processes like automated testing are complex to develop and are often fragile; moving these established, sensitive processes to a new system like BuildMaster will require some degree of effort that might be better spent elsewhere
  • Keep your existing infrastructure - you can use the Jenkins infrastructure that you've already built and is providing you with value

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.

Jenkins "Build Configurations" vs. BuildMaster "Builds"

While both Jenkins and BuildMaster use the term "build," the concepts are a bit different.

  • A "build" in Jenkins is an execution of a project (also known as a job). A project defines which repository the collection of logs, test results, and files will be created from, how they'll be created, who will be notified, etc. when a developer commits new code to a source control repository. These are intended for engineers and may not have any relation to a business application or component, and may not be understandable by testing or operations departments
  • A BuildMaster build is a broader concept. It represents a new version of an application or component that may be released to production using a pipeline with various stages that represent your software delivery process. Builds have a lifecycle visible not only to business stakeholders, but which can be approved at different stages in the process and are organized under releases and applications that testers, operations, and managers understand.

Installing the Jenkins Extension

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.

Connecting to Jenkins Using Resource Credentials

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.

Importing Jenkins Artifacts

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.

Example: 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.

How-To Summary: Use Import Artifact Operations

  1. Specify a Resource Credential, which tells the operation which Jenkins server to connect to.
  2. Specify a Job Name, which tells the operation where to look for a build.
  3. Specify a Build Number, or reference a special build name, which tells the operation which build to import artifacts from.
  4. Specify the Artifact name to use in BuildMaster once it is captured from the /job///artifact/zip/archive.zip endpoint.

Automatically Queuing Builds in Jenkins

You can queue builds in Jenkins using the Jenkins::Queue-Build operation. There a few reasons you may want to do this:

  • Create different builds in Jenkins that are automatically triggered by check-in
  • A "Release Configuration" build that instructs the compiler to optimize for speed and does not include debugging symbols
  • A "Release Branch" build that uses a different source control branch to create the build
  • Inject contextual information from BuildMaster (release number, etc.) into the application code after it's checked-out but prior to being built by Jenkins; you can then display this information once the application is running in an environment
  • Link from Jenkins back to the application or build in BuildMaster, so you can have cross-links in both system
  • Execute a job in Jenkins that performs automated tests after performing a deployment from BuildMaster

Example: Using the Jenkins::Queue-Build Operation

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&param2=value,
    JenkinsBuildNumber => $JenkinsBuildNumber
);

This example will also pass the buildType=release as an additional parameter and wait for the build to be completed.

How-To Summary: Use the Queue Operation

  1. Specify a Resource Credential, which tells the operation which server to connect to.
  2. Specify a Job Name, which tells the operation where to look for a build.
  3. Specify Additional Parameters that will be used by Jenkins in query string format; these are often be passed to the tools Jenkins uses, such as MSBuild or Maven.

Special Build Names

BuildMaster can reference Jenkins builds using the following special build names instead of direct build numbers:

  • lastSuccessfulBuild - compilation reported no errors
  • lastStableBuild � latest successful build and no publisher reports it as unstable
  • lastBuild - 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.

Branches in Jenkins

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
 ); 

Pushing Artifacts from Jenkins to an Artifact Repository

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.

Capturing Jenkins Build Numbers

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:

  • From a debugging perspective, this saves you wasted time trying to figure out which code was deployed and saves frustration when the deployed application doesn't seem to match the code
  • From an auditing perspective, this lets you see exactly who made which changes to a deployed application and when those changes were made

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:

  1. Specify an output parameter on the appropriate operation (such as import artifact or queue build); the operation will them set the value of runtime variable to Jenkins's build number;
  2. Set a build configuration variable from the runtime variable; because runtime variables only exist during build time, capturing this as a build variable will create a permanent record on the build

Example: Capturing a Queued Build Number

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.

Deploying Jenkins Artifacts

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
);

Prompt for Build Number at Build Time

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:

  • Jenkins Job Name
  • Jenkins Build Number

Refer to the Choosing a Specific Jenkins Build with BuildMaster tutorial for more information.

Automatically Importing into BuildMaster after CI Build

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.

The Release & Build Deployment API

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.

Example: Triggering an Importing Artifact Build Using PowerShell

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" 

The Jenkins BuildMaster Plugin

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.

Example: Triggering a BuildMaster Build from Jenkins

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)
            }
         }
      }
   }
}

Steps:

  1. buildMasterWithApplicationRelease queries BuildMaster for the next build number for the selected release and populates several environment variables for use later in the script. Combining the release and build number variables is a great way to create a version number for your application binaries.
  2. buildMasterCreateBuild triggers a build in BuildMaster. In this example, it would be a pipeline that has been created using the Jenkins Template. The variables passed to BuildMaster allow the build to call back and import the artifact from Jenkins.
  3. buildMasterDeployBuildToStage has only been placed here to demo the feature. This is not something that typically would be performed after a build, but it might be used after running some automated tests to notify BuildMaster to automatically advance to the next stage.

Is this documentation incorrect or incomplete? Help us by contributing!

This documentation is licensed under CC-BY-SA-4.0 and stored in GitHub.

Generated from commit bd2ce6f3 on master