13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Jenkins で Salesforce の CI を回したいときの Tips

Last updated at Posted at 2020-03-27

:writing_hand: この記事は Salesforce 開発者向けブログキャンペーンへのエントリー記事です。

はじめに

こんにちは。このエントリでは、Jenkins で Salesforce の CI 環境を構築するときにいつも私がこだわってるポイントを紹介します。

まったくの初心者のかたにはピンとこない内容かもしれません :thinking::grey_question::grey_question:

使いたいプラグイン

私が必ずインストールするプラグインたちです。超おすすめ :grinning:

「おい、これも忘れてるぞ :punch::angry:」というものがあれば、ぜひコメント欄へ!!

Locale

https://plugins.jenkins.io/locale/

まずは見た目を英語にします!(慣れましょう :dark_sunglasses:
そうすると、設定方法やエラー内容がわからないときにググったときにヒットしやすくなります。どうしても日本語だと情報が限られますからね。

image.png

NodeJS

https://plugins.jenkins.io/nodejs/

便利なコマンドをたくさん使いたいので、Salesforce 開発であっても、Node.js は必須です。

image.png

Jenkins Pipeline 内の使用例
def call() {
  pipeline {
    tools {
      // Node.js を使います
      nodejs nodejsLTS
    }
    stages {
      stage('Install') {
        steps {
          script {
            // npm 経由で Salesforce CLI をインストールします
            sh """
            npm install --global sfdx-cli@7.52.0
            """
          }
        }
      }
    }
  }
}

Blue Ocean

https://plugins.jenkins.io/blueocean/

Salesforce でいう LEX みたいなかんじで、Classic よりも見た目がスタイリッシュになります。Jenkins Pipeline の Stage ごとにログを見たいときに便利です。ただし万能ではありませんので、実際には Classic とは行ったり来たりします。

image.png

Checkstyle

https://plugins.jenkins.io/checkstyle/

Salesforce の Aura や LWC の JavaScript を ESLint で静的解析した結果をいろいろな軸で見たいときに便利です。

image.png

Jenkins Pipeline 内の使用例
def call() {
  pipeline {
    stages {
      stage('Code Analysis') {
        steps {
          script {
            // ESLint の結果ファイル `logs/eslint.xml` を読み込みます
            step([
              $class: "CheckStylePublisher",
              pattern: "logs/eslint.xml",
              unstableTotalAll: "0",
              usePreviousBuildAsReference: true
            ])
          }
        }
      }
    }
  }
}

PMD

https://plugins.jenkins.io/pmd/

Salesforce の Apex コード を Apex PMD で静的解析した結果をいろいろな軸で見たいときに便利です。

image.png

Jenkins Pipeline 内の使用例
def call() {
  pipeline {
    stages {
      stage('Code Analysis') {
        steps {
          script {
            // Apex PMD の結果ファイル `logs/pmd.xml` を読み込みます
            step([
              $class: "PmdPublisher",
              pattern: "logs/pmd.xml",
              unstableTotalAll: "0",
              usePreviousBuildAsReference: true
            ])
          }
        }
      }
    }
  }
}

Pipeline Utility Steps

https://plugins.jenkins.io/pipeline-utility-steps/

Jenkins Pipeline 内で更に使いたいメソッドがあるときに便利です。たとえば、zip とか readJSON とかですね。

Jenkins Pipeline 内の使用例
def call() {
  pipeline {
    stages {
      stage('Unit Test') {
        steps {
          script {
            sh """
            sfdx force:apex:test:run --targetusername dev \
            --codecoverage --testlevel RunLocalTests \
            --resultformat json --json --verbose \
            > logs/apextest.json \
            || true
            """
            // .json ファイルを読み取ります
            def json = readJSON(file: "logs/apextest.json")
            def SFDX_TEST_RUN_ID = json.result.summary.testRunId

            // do something...

          }
        }
      }
    }
  }
}

Pipeline: Multibranch with defaults

https://plugins.jenkins.io/pipeline-multibranch-defaults/

Multibranch Pipeline を作成する際に、追跡したいリポジトリから Jenkinsfile を分離したいときに便利です。

:thinking: そもそも分離できると何が嬉しいんでしょうね? 開発の規模が大きくなると Salesforce 開発チームとは別に Jenkins おじさんの面倒を見る専属チームを切り出したくなるものですが、そんなときに便利です。Salesforce 以外にも Java 開発もしていて、Jenkins サーバーは共用したい、とか。これはちょっと上級者向けですかね。

image.png

image.png

image.png

image.png

使いたいツール

Salesforce CLI

Salesforce に対して何かいろいろ操作したいので、必須です。常に最新版をインストールするのは危険なので、インストール時にはバージョンを指定し、定期的にバージョンアップしましょう。

npm install --global sfdx-cli@7.52.0

sfpowerkit

Accenture Global の有志メンバが開発している SFDX プラグインです。いろいろなコマンドが用意されているのですが、主に Apex PMD を実行するためにインストールします。

echo y | sfdx plugins:install sfpowerkit

https://github.com/Accenture/sfpowerkit

Yarn

Node Module の管理は npm ではなくて Yarn を使います。何と言っても npm install よりも yarn install のほうが高速なので。

たとえば prettier を実行したい場合、npm であれば

npm run prettier

と書くところですが、yarn の場合は

yarn prettier

となります。シンプルですね。

Yarn のインストールを npm 経由で実施しているサンプルコードが散見されますが、Yarn 公式サイト で非推奨とされていますので私は採用しません。

npm install --global yarn

jq

groovy コード内では readJSON がありますが、Shell Script コード内で JSON を操作したい場合に重宝します。

インストール方法
def call() {
  pipeline {
    stages {
      stage('Install') {
        steps {
          script {
            sh """
            # 環境変数 を追加します
            echo 'export YARN_PATH=$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin' >> ~/.bash_profile
            echo 'export JQ_PATH=$HOME/tools/bin' >> ~/.bash_profile
            echo 'export PATH=\$YARN_PATH:\$JQ_PATH:$PATH' >> ~/.bash_profile

            # Yarn をインストールします
        if ! test -e \$HOME/.yarn ; then
           curl -o- -L https://yarnpkg.com/install.sh | bash
        fi

            # jq をインストールします
            if ! test -e \$JQ_PATH/jq ; then
              mkdir -p \$JQ_PATH
              curl -o \$JQ_PATH/jq http://stedolan.github.io/jq/download/linux64/jq
              chmod +x \$JQ_PATH/jq
            fi

            # PATH を通します
        . ~/.bash_profile
        which yarn
        which jq

            # Salesforce CLI をインストールします
            npm install --global sfdx-cli@7.52.0

            # sfpowerkit をインストールします
            echo y | sfdx plugins:install sfpowerkit

            # Node Modules をインストールします
            yarn install
            """
          }
        }
      }
    }
  }
}

Prettier

リポジトリ内のコードを綺麗に保つために、コードフォーマットは欠かせません。フォーマットされていないコードが存在するかどうかを prettier --check で検知し、警告を通知するようにします。

package.json
{
  "devDependencies": {
    "@prettier/plugin-xml": "0.7.2",
    "prettier": "1.19.1",
    "prettier-plugin-apex": "1.3.0"
  },
  "scripts": {
    "prettier:check": "prettier --check \"**/*.{component,css,cls,cmp,html,js,json,md,page,trigger,xml}\"",
  }
}

設定したい SFDX 環境変数

上記にも少し出てきましたが、他にも設定したい環境変数があるんです。

SFDX_API_VERSION

Apex バージョンをデフォルトで固定します。

echo 'export SFDX_API_VERSION=48.0' >> ~/.bash_profile

SFDX_LOG_LEVEL

Debug Log Level をデフォルトで固定します。

echo 'export SFDX_LOG_LEVEL=fatal' >> ~/.bash_profile

SFDX_MDAPI_TEMP_DIR

sfdx force:source:deploy コマンドを実行中に、メタデータを旧フォーマットで保持しておくディレクトリ名です。

echo 'export SFDX_MDAPI_TEMP_DIR=mdapiTemp' >> ~/.bash_profile

SFDX_JSON_TO_STDOUT

Salesforce CLI コマンドが失敗した場合のメッセージを stderr ではなく stdout に出します。stdout をログファイルに書き出してるときに便利です。

echo 'export SFDX_JSON_TO_STDOUT=true' >> ~/.bash_profile

SFDX_USE_GENERIC_UNIX_KEYCHAIN

Jenkins から Salesforce へ認証する際に必要です。

echo 'export SFDX_USE_GENERIC_UNIX_KEYCHAIN=true' >> ~/.bash_profile

SFDX_IMPROVED_CODE_COVERAGE

Spring '20 新機能 :four_leaf_clover:

echo 'export SFDX_IMPROVED_CODE_COVERAGE=true' >> ~/.bash_profile

https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_sf_cli_code_coverage_apextests.htm

どんな感じでカバレッジに変化があるか検証してみました。
AccountControllerTest.clsContactControllerTest.cls はカバレッジ 100% ずつにして、全体としてはカバレッジ 92% として、Apex テストを実行した結果を整理しました。

テスト対象 true false
TestAccountController.cls testRunCoverage: 100%
orgWideCoverage: 92%
testRunCoverage: 92%
orgWideCoverage: 92%
TestContactController.cls testRunCoverage: 100%
orgWideCoverage: 92%
testRunCoverage: 92%
orgWideCoverage: 92%
TestAccountController.cls,
TestContactController.cls
testRunCoverage: 100%
orgWideCoverage: 92%
testRunCoverage: 92%
orgWideCoverage: 92%
RunLocalTests testRunCoverage: 92%
orgWideCoverage: 92%
testRunCoverage: 92%
orgWideCoverage: 92%
RunAllTestsInOrg testRunCoverage: 92%
orgWideCoverage: 92%
testRunCoverage: 92%
orgWideCoverage: 92%

SFDX_IMPROVED_CODE_COVERAGE=true の場合にちゃんと 100% になってます :clap:
(これまではバグっぽい挙動だったんですね、気づかなかったです :weary:

SFDX_USE_PROGRESS_BAR

Spring '20 新機能 :four_leaf_clover:

ローカルで sfdx force:source:deploy コマンドを実行しているときは、進捗状況をバーで見たいので true にするのですが、Jenkins では意味をなさないので、無効化します。

echo 'export SFDX_USE_PROGRESS_BAR=false' >> ~/.bash_profile

https://releasenotes.docs.salesforce.com/ja-jp/spring20/release-notes/rn_sf_cli_progress_bar.htm

Salesforce との認証方式

Web ベースフロー sfdx force:auth:sfdxurl:store と JWT ベースフロー sfdx force:auth:jwt:grant があり、どちらでもいいケースも多いのですが、原則として JWT を推奨します。その理由は、JWT ベースフローじゃないとだめなパターンが 1 個あるためです。

  • Salesforce 側に IP アドレス制限をしていて、Jenkins サーバーの IP アドレスを固定していない場合、Web ベースフローだと認証失敗してしまう

あえて Web ベースフローを採用するケースを考えるとすれば、証明書が準備できていないけど Jenkins を動かし始めたくて一時的に IP アドレス制限も外せる、というケースでしょうか。たしかに証明書や接続アプリケーションを準備する必要がないのですぐ試せる点は優れてると思いますし、仕事ではなく趣味で開発する範囲でなら十分でしょう。

デプロイコマンドの謎仕様に注意

デプロイには DeployQuick Deploy があります。また、デプロイの前にはデプロイ検証 (Validate) をすることが通例です。

Deploy
# デプロイ一発
sfdx force:source:deploy \
--testlevel RunLocalTests --manifest manifest/package.xml --targetusername dev --json --verbose
Validate & Quick Deploy
# まずはデプロイ検証して jq で <Job ID> を取得
sfdx force:source:deploy --checkonly \
--testlevel RunLocalTests --manifest manifest/package.xml --targetusername dev --json --verbose \
| jq -r '.result.id'
# 検証 OK ならクイックデプロイ
sfdx force:source:deploy --validateddeployrequestid <Job ID>

どちらを採用するのが良いかというと、後者です。
コマンドの実行結果は JSON 構造になっており、そこから jq で idsuccess などの値を取得して、成功なのか失敗なのかを判定して、次の処理に繋げたいのですが、Deploy の実行結果には、その大事な情報が含まれていないのです!(:point_left: これが謎仕様)

以前は含まれていたはずなのですが、Progress Bar が表示される新機能の裏に隠れて、いつの間にか含まれなくなってしまいました。復活を願っています :pray:

ローカルで実行する分には手動で調整できるので Deploy でも構わないのですが、Jenkins で自動化するには、Quick Deploy じゃないとだめだということを覚えておいてください。

種別 id success
Deploy
Validate :white_check_mark: :white_check_mark:
Quick Deploy :white_check_mark: :white_check_mark:
Deploy 実行結果
{
  "status": 0,
  "result": {
    "deployedSource": [
      {
        "state": "Add",
        "fullName": "LWC_Recipes",
        "type": "CustomApplication",
        "filePath": "force-app/main/default/applications/LWC_Recipes.app-meta.xml"
      },
      ...
    ]
  }
}
Validate 実行結果
{
  "status": 0,
  "result": {
    "checkOnly": true,
    "completedDate": "2020-03-27T06:25:01.000Z",
    "createdBy": "005xxxxxxxxxxxx",
    "createdByName": "User User",
    "createdDate": "2020-03-27T06:24:34.000Z",
    "details": {
      "componentSuccesses": [
        {
          "changed": "false",
          "componentType": "CustomApplication",
          "created": "false",
          "createdDate": "2020-03-27T06:25:00.000Z",
          "deleted": "false",
          "fileName": "sdx_sourceDeploy_xxxxxxxxxxxxx/applications/LWC_Recipes.app",
          "fullName": "LWC_Recipes",
          "id": "02uxxxxxxxxxxxxxxx",
          "success": "true"
        },
        ...
    },
    "done": true,
    "id": "0Afxxxxxxxxxxxxxxx",
    "ignoreWarnings": false,
    "lastModifiedDate": "2020-03-27T06:25:01.000Z",
    "numberComponentErrors": 0,
    "numberComponentsDeployed": 167,
    "numberComponentsTotal": 167,
    "numberTestErrors": 0,
    "numberTestsCompleted": 6,
    "numberTestsTotal": 6,
    "rollbackOnError": true,
    "runTestsEnabled": true,
    "startDate": "2020-03-27T06:24:35.000Z",
    "status": "Succeeded",
    "success": true
  }
}
Quick Deploy 実行結果
{
  "status": 0,
  "result": {
    "checkOnly": false,
    "completedDate": "2020-03-27T06:52:36.000Z",
    "createdBy": "005xxxxxxxxxxxx",
    "createdByName": "User User",
    "createdDate": "2020-03-27T06:52:03.000Z",
    "details": {
      "componentSuccesses": [
        {
          "changed": "false",
          "componentType": "CustomApplication",
          "created": "false",
          "createdDate": "2020-03-27T06:25:00.000Z",
          "deleted": "false",
          "fileName": "sdx_sourceDeploy_xxxxxxxxxxxxx/applications/LWC_Recipes.app",
          "fullName": "LWC_Recipes",
          "id": "02uxxxxxxxxxxxxxxx",
          "success": "true"
        },
        ...
    },
    "done": true,
    "id": "0Afxxxxxxxxxxxxxxx",
    "ignoreWarnings": false,
    "lastModifiedDate": "2020-03-27T06:52:36.000Z",
    "numberComponentErrors": 0,
    "numberComponentsDeployed": 167,
    "numberComponentsTotal": 167,
    "numberTestErrors": 0,
    "numberTestsCompleted": 0,
    "numberTestsTotal": 0,
    "rollbackOnError": true,
    "runTestsEnabled": true,
    "startDate": "2020-03-27T06:52:03.000Z",
    "status": "Succeeded",
    "success": true
  }
}

さいごに

ようこそ #DontStopDeploying の世界へ :sunglasses:

これであなたも Jenkins マニアの仲間入りです :thumbsup:
そして Jenkins 以外にもいろいろな CI ツールが世の中にはありますので、折を見ていろいろと戯れていきましょう。

それではまた :wave:

13
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?