Jenkinsfile のウォークスルー
ここでは、Jenkinsfile の構造、Jenkins パイプライン DSL、Groovy プログラミング言語に精通していることを前提としています。このウォークスルーでは、Salesforce DX 情報にのみ焦点を当てます。使用されるコマンドに関する Salesforce DX コマンドリファレンスを参照してください。
この Salesforce DX ワークフローは、Jenkinsfile のフェーズに最も緊密に対応しています。
- 変数の定義
- ソースコードのチェックアウト
- withCredentials コマンドでのすべてのフェーズのラップ
- Dev Hub 組織の認証とスクラッチ組織の作成
- ソースの転送と権限セットの割り当て
- Apex テストの実行
- スクラッチ組織の削除
変数の定義
def キーワードを使用して、Salesforce DX CLI コマンドで必要な変数を定義します。Jenkins 環境で以前に設定した環境変数に対応する各変数を割り当てます。
1def HUB_ORG=env.HUB_ORG_DH
2def SFDC_HOST = env.SFDC_HOST_DH
3def JWT_KEY_CRED_ID = env.JWT_CRED_ID_DH
4def CONNECTED_APP_CONSUMER_KEY=env.CONNECTED_APP_CONSUMER_KEY_DHSFDC_USERNAME 変数を定義します。ただし、その値は設定しません。この作業は後で行います。
1def SFDC_USERNAME必須ではありませんが、Jenkins Global Tool Configuration を使用して、CLI インストールディレクトリを指し示す toolbelt カスタムツールを作成していることを前提としています。Jenkinsfile で、ツールコマンドを使用して、toolbelt 変数の値をこのカスタムツールに設定します。
1def toolbelt = tool 'toolbelt'これで、${toolbelt}/sfdx を使用して Jenkinsfile で Salesforce CLI 実行可能ファイルを参照できるようになります。
ソースコードのチェックアウト
コードをテストする前に、バージョン管理システム (VCS) リポジトリから適切なバージョンまたはブランチを取得します。この例では、checkout scm Jenkins コマンドを使用します。ここでは、適切な VCS リポジトリにアクセスして正しいブランチをチェックアウトする環境を Jenkins システム管理者がすでに設定していることを前提としています。
1stage('checkout source') {
2 // when running in multi-branch job, one must issue this command
3 checkout scm
4 }withCredentials コマンドでのすべてのフェーズのラップ
以前にログイン情報インターフェースを使用して、JWT 非公開鍵ファイルを Jenkins 秘密ファイルとして保存しました。そのため、Jenkinsfile の本文で withCredentials コマンドを使用して秘密ファイルにアクセスする必要があります。withCredentials コマンドでは、ログイン情報エントリの名前を指定できます。その後、このエントリはログイン情報ストアから抽出されて、変数を介して囲まれたコードに提供されます。withCredentials を使用すると、そのコードブロック内にすべてのフェーズが配置されます。
この例では、変数 JWT_KEY_CRED_ID に JWT キーファイルのログイン情報 ID を保存します。以前に JWT_KEY_CRED_ID を定義して、対応する環境変数に設定しました。withCredentials コマンドでは、ログイン情報ストアから秘密ファイルのコンテンツが取得されて、一時的な場所に配置されます。この場所は、変数 jwt_key_file に保存されています。安全に非公開鍵を指定するには、force:auth:jwt コマンドで jwt_key_file 変数を使用します。
1withCredentials([file(credentialsId: JWT_KEY_CRED_ID, variable: 'jwt_key_file')]) {
2 # all stages will go here
3}Dev Hub 組織の認証とスクラッチ組織の作成
dreamhouse-sfdx の例では、1 つのフェーズを使用して、Dev Hub 組織を認証し、スクラッチ組織を作成します。
1stage('Create Scratch Org') {
2
3 rc = sh returnStatus: true, script: "${toolbelt}/sfdx force:auth:jwt:grant --clientid ${CONNECTED_APP_CONSUMER_KEY} --username ${HUB_ORG} --jwtkeyfile ${jwt_key_file} --setdefaultdevhubusername --instanceurl ${SFDC_HOST}"
4 if (rc != 0) { error 'hub org authorization failed' }
5
6 // need to pull out assigned username
7 rmsg = sh returnStdout: true, script: "${toolbelt}/sfdx force:org:create --definitionfile config/project-scratch-def.json --json --setdefaultusername"
8 printf rmsg
9 def jsonSlurper = new JsonSlurperClassic()
10 def robj = jsonSlurper.parseText(rmsg)
11 if (robj.status != "ok") { error 'org creation failed: ' + robj.message }
12 SFDC_USERNAME=robj.username
13 robj = null
14
15}force:auth:jwt:grant CLI コマンドを使用して Dev Hub 組織を認証します。
このステップは 1 回のみ実行すれば済みますが、Jenkins ジョブを実行するたびに Jenkinsfile に追加して認証することをお勧めします。これにより、認証の欠落が原因で Jenkins ジョブが中止されていないことを常に確認できます。通常、複数回認証してもほとんど悪影響はありませんが、スクラッチ組織の各エディションの API コール制限は適用されるので注意が必要です。
force:auth:jwt:grant コマンドのパラメータを使用して、認証する Dev Hub 組織に関する情報を提供します。--clientid、--username、--instanceurl パラメータの値は、それぞれ以前に定義した CONNECTED_APP_CONSUMER_KEY、HUB_ORG、SFDC_HOST 環境変数です。--jwtkeyfile パラメータの値は、前のセクションで withCredentials コマンドを使用して設定した jwt_key_file 変数です。--setdefaultdevhubusername パラメータでは、この HUB_ORG がスクラッチ組織を作成するときのデフォルトの Dev Hub 組織であることを指定します。
force:org:create CLI コマンドを使用して、スクラッチ組織を作成します。この例では、CLI コマンドで config/project-scratch-def.json ファイル (プロジェクトディレクトリを基準とする相対パス) を使用して、スクラッチ組織を作成します。--json パラメータで、出力が JSON 形式であることを指定します。--setdefaultusername パラメータで新しいスクラッチ組織をデフォルトとして設定します。
force:org:create コマンドの JSON 出力を解析する Groovy コードで、組織の作成の一環として自動生成されたユーザ名を抽出します。SFDC_USERNAME 変数に保存されているこのユーザ名は、ソースの転送や権限セットの割り当てなどを行う CLI コマンドで使用されます。
ソースの転送と権限セットの割り当て
新しいスクラッチ組織にメタデータを入力しましょう。この例では、force:source:push コマンドを使用して、ソースを組織にアップロードします。ソースには、Salesforce アプリケーションを構成するすべての要素 (Apex クラス、テストクラス、権限セット、レイアウト、トリガ、カスタムオブジェクトなど) が含まれます。
1stage('Push To Test Org') {
2
3 rc = sh returnStatus: true, script: "${toolbelt}/sfdx force:source:push --targetusername ${SFDC_USERNAME}"
4 if (rc != 0) {
5 error 'push all failed'
6 }
7 // assign permset
8 rc = sh returnStatus: true, script: "${toolbelt}/sfdx force:user:permset:assign --targetusername ${SFDC_USERNAME} --permsetname DreamHouse"
9 if (rc != 0) {
10 error 'push all failed'
11 }
12}前のフェーズで force:org:create コマンドによって出力された自動生成のユーザ名が含まれる SFDC_USERNAME 変数を再度コールします。このコードでは、この変数を --targetusername パラメータの引数として使用し、新しいスクラッチ組織のユーザ名を指定します。
force:source:push コマンドでは、プロジェクトで検出されるすべての Salesforce 関連のファイルが転送されます。.forceignore ファイルをリポジトリに追加して、組織に転送しないファイルをリストします。
この例では、メタデータを転送したら、force:user:permset:assign コマンドを使用して、権限セット (DreamHouse) を SFDC_USERNAME ユーザに割り当てます。この権限セットが記述された XML ファイルは、転送の一環として組織にアップロードされています。
Apex テストの実行
ソースコードとテストソースがスクラッチ組織に転送されたので、force:apex:test:run コマンドを実行して Apex テストを実行します。
1stage('Run Apex Test') {
2 sh "mkdir -p ${RUN_ARTIFACT_DIR}"
3 timeout(time: 120, unit: 'SECONDS') {
4 rc = sh returnStatus: true, script: "${toolbelt}/sfdx force:apex:test:run --testlevel RunLocalTests --outputdir ${RUN_ARTIFACT_DIR} --resultformat tap --targetusername ${SFDC_USERNAME}"
5 if (rc != 0) {
6 error 'apex test run failed'
7 }
8 }
9}さまざまなパラメータを force:apex:test:run CLI コマンドに指定できます。例:
- --testlevel RunLocalTests オプションでは、インストールされた管理パッケージからのテストを除き、スクラッチ組織のすべてのテストが実行されます。また、RunSpecifiedTests を指定して特定の Apex テストまたはスイートのみを実行したり、RunAllTestsInOrg を指定して組織のすべてのテストを実行したりできます。
- --outputdir オプションでは、RUN_ARTIFACT_DIR 変数を使用して、テスト結果を書き込むディレクトリを指定します。テスト結果は、JUnit および JSON 形式で生成されます。
- --resultformat tap オプションでは、コマンド出力が Test Anything Protocol (TAP) 形式であることを指定します。ファイルに書き込まれるテスト結果は、JUnit および JSON 形式のままです。
- --targetusername オプションでは、スクラッチ組織にアクセスするためのユーザ名 (SFDC_USERNAME の値) を指定します。
force:apex:test:run コマンドでは、JUnit 形式でテスト結果が書き込まれます。次の例のように業界標準ツールを使用して結果を収集できます。
1stage('collect results') {
2 junit keepLongStdio: true, testResults: 'tests/**/*-junit.xml'
3 }スクラッチ組織の削除
Salesforce は、作成から指定の日数が経過したスクラッチ組織を削除する権利を留保します。force:org:delete を使用するパイプラインのフェーズを作成して、テストの完了時に明示的にスクラッチ組織を削除することもできます。このクリーンアップにより、リソースをより適切に管理できます。
1stage('Delete Test Org') {
2
3 timeout(time: 120, unit: 'SECONDS') {
4 rc = sh returnStatus: true, script: "${toolbelt}/sfdx force:org:delete --targetusername ${SFDC_USERNAME} --noprompt"
5 if (rc != 0) {
6 error 'org deletion request failed'
7 }
8 }
9 }