Jenkinsfile のウォークスルー
このウォークスルーでは、sfdx-jenkins-package Jenkinsfile を利用します。ここでは、Jenkinsfile の構造、Jenkins パイプライン DSL、Groovy プログラミング言語に精通していることを前提としています。このウォークスルーでは、Salesforce CLI とスクラッチ組織を使用する Jenkins パイプラインの実装について説明します。使用されるコマンドに関する CLI コマンドリファレンスを参照してください。
このワークフローは、Jenkinsfile のフェーズに最も緊密に対応しています。
- 変数の定義
- ソースコードのチェックアウト
- withCredentials コマンドでのすべてのフェーズのラップ
- withEnv コマンドでのすべてのフェーズのラップ
- Dev Hub 組織の認証とスクラッチ組織の作成
- ソースの転送と権限セットの割り当て
- Apex テストの実行
- スクラッチ組織の削除
- パッケージの作成
- スクラッチ組織の作成と情報の表示
- パッケージのインストール、単体テストの実行、スクラッチ組織の削除
変数の定義
def キーワードを使用して、Salesforce CLI コマンドで必要な変数を定義します。Jenkins 環境で以前に設定した環境変数に対応する各変数を割り当てます。
1def SF_CONSUMER_KEY=env.SF_CONSUMER_KEY
2def SERVER_KEY_CREDENTALS_ID=env.SERVER_KEY_CREDENTALS_ID
3def TEST_LEVEL='RunLocalTests'
4def PACKAGE_NAME='0Ho1U000000CaUzSAK'
5def PACKAGE_VERSION
6def SF_INSTANCE_URL = env.SF_INSTANCE_URL ?: "https://login.salesforce.com"SF_USERNAME 変数を定義します。ただし、その値は設定しません。この作業は後で行います。
1def SF_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 を使用すると、そのコードブロック内にすべてのフェーズが配置されます。
この例では、変数 SERVER_KEY_CREDENTALS_ID に JWT キーファイルのログイン情報 ID を保存します。以前に SERVER_KEY_CREDENTALS_ID を定義して、対応する環境変数に設定しました。withCredentials コマンドでは、ログイン情報ストアから秘密ファイルの内容が取得されて、一時的な場所に配置されます。この場所は、変数 server_key_file に保存されています。安全に非公開鍵を指定するには、auth:jwt コマンドで server_key_file 変数を使用します。
1withCredentials([file(credentialsId: SERVER_KEY_CREDENTALS_ID, variable: 'server_key_file')])
2 # all stages will go here
3}withEnv コマンドでのすべてのフェーズのラップ
Jenkins ジョブを実行するときは、ファイルの保存場所を理解しておくと役立ちます。覚えておくべきディレクトリは、主にワークスペースディレクトリとホームディレクトリの 2 つです。ワークスペースディレクトリは、各ジョブに対して一意ですが、ホームディレクトリは、すべてのジョブで同一です。
withCredentials コマンドは、ジョブの間に JWT キーファイルを Jenkins ワークスペースに保存します。ただし、Salesforce CLI auth コマンドは、認証ファイルをホームディレクトリに保存します。これらの認証ファイルは、ジョブの期間外も保持されます。
この設定は、単一のジョブを実行するときは問題ありませんが、複数のジョブを実行するときは問題になる場合があります。同じ Dev Hub ユーザまたは別の Salesforce ユーザを使用して複数のジョブを実行した場合は、どうなるでしょうか? CLI が Dev Hub に認証済みユーザとして接続を試みた場合、トークンの更新が失敗します。CLI は、現在のジョブの withCredentials に関係なく、すでに存在しない別のワークスペースの JWT キーファイルの使用を試みるためです。
withEnv を使用して、ホームディレクトリがワークスペースディレクトリと同じになるように設定した場合、認証ファイルは、ジョブごとに一意となります。ジョブごとに一意の認証ファイルを作成すると、各ジョブは自分が作成した認証ファイルにしかアクセスできないため、セキュリティも向上します。
withEnv を使用すると、そのコードブロック内にすべてのフェーズが配置されます。
1withEnv(["HOME=${env.WORKSPACE}"]) {
2 # all stages will go here
3}Dev Hub 組織の認証とスクラッチ組織の作成
この sfdx-jenkins-package の例で使用されているフェーズは、Dev Hub 組織を認証するフェーズとスクラッチ組織を作成するフェーズの 2 つです。
1// -------------------------------------------------------------------------
2// Authorize the Dev Hub org with JWT key and give it an alias.
3// -------------------------------------------------------------------------
4
5stage('Authorize DevHub') {
6 rc = command "${toolbelt}/sfdx auth:jwt:grant --instanceurl ${SF_INSTANCE_URL} --clientid ${SF_CONSUMER_KEY} --username ${SF_USERNAME} --jwtkeyfile ${server_key_file} --setdefaultdevhubusername --setalias HubOrg"
7 if (rc != 0) {
8 error 'Salesforce dev hub org authorization failed.'
9 }
10}
11
12
13// -------------------------------------------------------------------------
14// Create new scratch org to test your code.
15// -------------------------------------------------------------------------
16
17stage('Create Test Scratch Org') {
18 rc = command "${toolbelt}/sfdx force:org:create --targetdevhubusername HubOrg --setdefaultusername --definitionfile config/project-scratch-def.json --setalias ciorg --wait 10 --durationdays 1"
19 if (rc != 0) {
20 error 'Salesforce test scratch org creation failed.'
21 }
22}Dev Hub 組織を認証するには、auth:jwt:grant を使用します。
このステップは 1 回のみ実行すれば済みますが、Jenkins ジョブを実行するたびに Jenkinsfile に追加して認証することをお勧めします。これにより、認証の欠落が原因で Jenkins ジョブが中止されていないことを常に確認できます。通常、複数回認証してもほとんど悪影響はありませんが、スクラッチ組織の各エディションの API コール制限は適用されるので注意が必要です。
auth:jwt:grant コマンドのパラメータを使用して、認証する Dev Hub 組織に関する情報を提供します。--clientid、--username、--instanceurl パラメータの値は、それぞれ以前に定義した SF_CONSUMER_KEY、HubOrg、SF_INSTANCE_URL 環境変数です。--jwtkeyfile パラメータの値は、前のセクションで withCredentials コマンドを使用して設定した server_key_file 変数です。--setdefaultdevhubusername パラメータでは、この HubOrg がスクラッチ組織を作成するときのデフォルトの Dev Hub 組織であることを指定します。
force:org:create CLI コマンドを使用して、スクラッチ組織を作成します。この例では、CLI コマンドで config/project-scratch-def.json ファイル (プロジェクトディレクトリを基準とする相対パス) を使用して、スクラッチ組織を作成します。--json パラメータで、出力が JSON 形式であることを指定します。--setdefaultusername パラメータで新しいスクラッチ組織をデフォルトとして設定します。
force:org:create コマンドの JSON 出力を解析する Groovy コードで、組織の作成の一環として自動生成されたユーザ名を抽出します。SF_USERNAME 変数に保存されているこのユーザ名は、ソースの転送や権限セットの割り当てなどを行う CLI コマンドで使用されます。
ソースの転送と権限セットの割り当て
新しいスクラッチ組織にメタデータを入力しましょう。この例では、force:source:push コマンドを使用して、ソースを組織にアップロードします。ソースには、Salesforce アプリケーションを構成するすべての要素 (Apex クラス、テストクラス、権限セット、レイアウト、トリガ、カスタムオブジェクトなど) が含まれます。
1// -------------------------------------------------------------------------
2// Push source to test scratch org.
3// -------------------------------------------------------------------------
4
5stage('Push To Test Scratch Org') {
6 rc = command "${toolbelt}/sfdx force:source:push --targetusername ciorg"
7 if (rc != 0) {
8 error 'Salesforce push to test scratch org failed.'
9 }
10}前のフェーズで force:org:create コマンドによって出力された自動生成のユーザ名が含まれる SF_USERNAME 変数を再度コールします。このコードでは、この変数を --targetusername パラメータの引数として使用し、新しいスクラッチ組織のユーザ名を指定します。
force:source:push コマンドでは、プロジェクトで検出されるすべての Salesforce 関連のファイルが転送されます。.forceignore ファイルをリポジトリに追加して、組織に転送し��いファイルをリストします。
Apex テストの実行
ソースコードとテストソースがスクラッチ組織に転送されたので、force:apex:test:run コマンドを実行して Apex テストを実行します。
1// -------------------------------------------------------------------------
2// Run unit tests in test scratch org.
3// -------------------------------------------------------------------------
4
5stage('Run Tests In Test Scratch Org') {
6 rc = command "${toolbelt}/sfdx force:apex:test:run --targetusername ciorg --wait 10 --resultformat tap --codecoverage --testlevel ${TEST_LEVEL}"
7 if (rc != 0) {
8 error 'Salesforce unit test run in test scratch org failed.'
9 }
10}さまざまなパラメータを force:apex:test:run CLI コマンドに指定できます。例:
- --testlevel ${TEST_LEVEL} オプションでは、インストールされた管理パッケージからのテストを除き、スクラッチ組織のすべてのテストが実行されます。また、RunLocalTests を指定してローカルテストのみを実行したり、RunSpecifiedTests を指定して特定の Apex テストまたはスイートのみを実行したり、RunAllTestsInOrg を指定して組織のすべてのテストを実行したりできます。
- --resultformat tap オプションでは、コマンド出力が Test Anything Protocol (TAP) 形式であることを指定します。ファイルに書き込まれるテスト結果は、JUnit および JSON 形式のままです。
- --targetusername ciorg オプションでは、スクラッチ組織にアクセスするためのユーザ名 (SF_USERNAME の値) を指定します。
force:apex:test:run コマンドでは、JUnit 形式でテスト結果が書き込まれます。
スクラッチ組織の削除
Salesforce は、作成から指定の日数が経過したスクラッチ組織を削除する権利を留保します。force:org:delete を使用するパイプラインのフェーズを作成して、テストの完了時に明示的にスクラッチ組織を削除することもできます。このクリーンアップにより、リソースをより適切に管理できます。
1// -------------------------------------------------------------------------
2// Delete package install scratch org.
3// -------------------------------------------------------------------------
4
5stage('Delete Package Install Scratch Org') {
6 rc = command "${toolbelt}/sfdx force:org:delete --targetusername installorg --noprompt"
7 if (rc != 0) {
8 error 'Salesforce package install scratch org deletion failed.'
9 }
10}パッケージの作成
次に、パッケージを作成します。パッケージ化に詳しくないのであれば、パッケージを、メタデータで満たされたコンテナだと考えてください。そこには、関連する機能、カスタマイズ、スキーマのセットが含まれています。パッケージを使用してメタデータを Salesforce 組織間で移動できます。パッケージを作成したら、メタデータを追加して、新しいパッケージバージョンを作成します。
1// -------------------------------------------------------------------------
2// Create package version.
3// -------------------------------------------------------------------------
4
5stage('Create Package Version') {
6 if (isUnix()) {
7 output = sh returnStdout: true, script: "${toolbelt}/sfdx force:package:version:create --package ${PACKAGE_NAME} --installationkeybypass --wait 10 --json --targetdevhubusername HubOrg"
8 } else {
9 output = bat(returnStdout: true, script: "${toolbelt}/sfdx force:package:version:create --package ${PACKAGE_NAME} --installationkeybypass --wait 10 --json --targetdevhubusername HubOrg").trim()
10 output = output.readLines().drop(1).join(" ")
11}
12
13 // Wait 5 minutes for package replication.
14 sleep 300
15
16 def jsonSlurper = new JsonSlurperClassic()
17 def response = jsonSlurper.parseText(output)
18
19 PACKAGE_VERSION = response.result.SubscriberPackageVersionId
20
21 response = null
22
23 echo ${PACKAGE_VERSION}
24}スクラッチ組織の作成と情報の表示
以前いつスクラッチ組織を作成したかを覚えていますか? パッケージをインストールする新しいスクラッチ組織を作成し、そのスクラッチ組織に関する情報を表示してみましょう。
1// -------------------------------------------------------------------------
2// Create new scratch org to install package to.
3// -------------------------------------------------------------------------
4
5stage('Create Package Install Scratch Org') {
6 rc = command "${toolbelt}/sfdx force:org:create --targetdevhubusername HubOrg --setdefaultusername --definitionfile config/project-scratch-def.json --setalias installorg --wait 10 --durationdays 1"
7 if (rc != 0) {
8 error 'Salesforce package install scratch org creation failed.'
9 }
10}
11
12
13// -------------------------------------------------------------------------
14// Display install scratch org info.
15// -------------------------------------------------------------------------
16
17stage('Display Install Scratch Org') {
18 rc = command "${toolbelt}/sfdx force:org:display --targetusername installorg"
19 if (rc != 0) {
20 error 'Salesforce install scratch org display failed.'
21 }
22}パッケージのインストール、単体テストの実行、スクラッチ組織の削除
最後に、パッケージをスクラッチ組織にインストールし、単体テストを実行して、スクラッチ組織を削除します。これで終わりです。
1// -------------------------------------------------------------------------
2// Install package in scratch org.
3// -------------------------------------------------------------------------
4
5stage('Install Package In Scratch Org') {
6 rc = command "${toolbelt}/sfdx force:package:install --package ${PACKAGE_VERSION} --targetusername installorg --wait 10"
7 if (rc != 0) {
8 error 'Salesforce package install failed.'
9 }
10}
11
12
13// -------------------------------------------------------------------------
14// Run unit tests in package install scratch org.
15// -------------------------------------------------------------------------
16
17stage('Run Tests In Package Install Scratch Org') {
18 rc = command "${toolbelt}/sfdx force:apex:test:run --targetusername installorg --resultformat tap --codecoverage --testlevel ${TEST_LEVEL} --wait 10"
19 if (rc != 0) {
20 error 'Salesforce unit test run in pacakge install scratch org failed.'
21 }
22}
23
24
25// -------------------------------------------------------------------------
26// Delete package install scratch org.
27// -------------------------------------------------------------------------
28
29stage('Delete Package Install Scratch Org') {
30 rc = command "${toolbelt}/sfdx force:org:delete --targetusername installorg --noprompt"
31 if (rc != 0) {
32 error 'Salesforce package install scratch org deletion failed.'
33 }
34}