Introducing the Jenkinsfile for CI

The term "pipeline" is used in a CI server to describe the steps used to process the source code, from building it and testing it, to eventually deploying it to production. In the case of a packaged application such as this one, you may want to manage the deployment to production separately, though it is technically feasible to use the Package Push API to automate your package releases to your customers.

For this section, we will focus on the build and testing part of the pipeline, using a script file known as Jenkinsfile. This uses the Groovy programming language (http://groovy-lang.org/syntax.html) to define the stages in the pipeline to perform and how they interact with the Salesforce DX CLI. It is common for CI servers to provide visualizations of such pipelines. The following screenshot shows the pipeline we will be focusing on in this section:

The preceding screenshot shows that the Push to Test Scratch Org step is currently running.

Source-driven configuration. If you have used CI servers in the past, you might be familiar with configuring the preceding pipeline manually through the setup screens your CI server provides. Due to the ever-growing complexities of managing cloud-based configurations, it has become popular to store configuration in the form of source code. This allows a UI to generate the source code driving the pipeline and also provides the same benefits in terms of revision control and reuse experienced when developing the application code itself. 

In the following code snippet, the Jenkins file included in the root of the sample code folder for this chapter is shown. The preceding stages and the Salesforce DX commands embedded within it are shown in bold:

#!groovy
import groovy.json.JsonSlurperClassic
node {

// Test Scratch Org Username
def SFDC_USERNAME

// Path to SFDX CLI (configured via Custom Tools)
def toolbelt = tool 'toolbelt'

stage('Checkout Source') {
checkout scm
}

withCredentials([file(
credentialsId: env.JWT_CRED_ID_DH,
variable: 'jwt_key_file')]) {

stage('Create Test Scratch Org') {

// Authorizate with DevHub via JWT grant
rc = sh returnStatus: true,
script: "${toolbelt}/sfdx force:auth:jwt:grant
--clientid ${env.CONNECTED_APP_CONSUMER_KEY_DH}
--username ${env.HUB_ORG_DH}
--jwtkeyfile ${jwt_key_file}
--instanceurl ${env.SFDC_HOST_DH}"
if (rc != 0)
{ error 'hub org authorization failed' }

// Create Scratch Org and determine login username
rmsg = sh returnStdout: true,
script: "${toolbelt}/sfdx force:org:create
--targetdevhubusername ${env.HUB_ORG_DH}
--definitionfile config/project-scratch-def.json
--json"
def robj = new JsonSlurperClassic().parseText(rmsg)
if (robj.status != 0)
{ error 'org creation failed: ' + robj.message }
SFDC_USERNAME=robj.result.username
}

stage('Push To Test Scratch Org') {

// Push code via sfdx force:source:push
rc = sh returnStatus: true,
script: "${toolbelt}/sfdx force:source:push
--targetusername ${SFDC_USERNAME}"
if (rc != 0) {
error 'push failed'
}
}

stage('Run Tests') {

// Create test output directory, run tests
sh "mkdir -p tests/${env.BUILD_NUMBER}"

// Run Apex Tests
rc = sh returnStatus: true,
script: "${toolbelt}/sfdx force:apex:test:run
--testlevel RunLocalTests
--outputdir tests/${env.BUILD_NUMBER}
--resultformat junit
--targetusername ${SFDC_USERNAME}"

// Run Lightning Web Component Tests
env.NODEJS_HOME = "${tool 'node'}"
env.PATH="${env.NODEJS_HOME}/bin:${env.PATH}"
sh 'npm install'
rc = sh returnStatus: true, script: 'npm run test:unit'

// Have Jenkins capture the test results
junit keepLongStdio: true,
testResults: 'tests/**/*-junit.xml'
junit keepLongStdio: true,
testResults: 'junit.xml'
}

stage('Delete Test Org') {

// Delete Test Scratch Org
rc = sh returnStatus: true,
script: "${toolbelt}/sfdx force:org:delete
--targetusername ${SFDC_USERNAME}"
if (rc != 0) {
error 'org delete failed'
}
}
}
}

full walk-through of the Jenkinsfile syntax is outside the scope of this chapter. Extensive documentation can be found online (https://jenkins.io/doc/book/pipeline/syntax/). The following are some key aspects to consider from the preceding pipeline configuration in respect of Salesforce DX integration:

  • When you first started using Salesforce DX in Chapter 1, Building and Publishing Your Application, you had to manually authorize with your Salesforce DX Dev Hub org by responding to the usual login page from Salesforce with your username and password. In this case, the authorization is automated via the JWT authorization process and the sfdx force:auth:jwt:grant command. This command does not require the CI server to know a username and password for the Dev Hub (a good security practice). Instead, it uses a private certificate to identify itself. The configuration of this will be discussed further in the next section.
  • Commands are run via the sh command. The sh command returns the numeric value of 0 for success; all other values represent a failure of some kind.

  • Salesforce DX commands can return JSON-formatted responses through the use of the --json parameter. The sh command returns this into a variable for the Groovy code to parse and extract details such as the username of the scratch org. This username is used later in the pipeline to deploy the code and run tests.
  • Both the Apex and Lightning Web Components test executions are configured to output test results in JUnit XML format. The built-in junit command is then used to channel this information into a single test results page, shown as follows.
  • Finally, the scratch org is explicitly deleted.

The following screenshot shows how the test results have been integrated into the Jenkins CI UI. We will see later in this chapter how test failures are presented:

In the next section, we will go through the steps to set up the GitHub repository you created earlier in this chapter with the preceding pipeline configuration.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.183.24