リクルートライフスタイルにおける Frontend Ops の取り組みとその中で開発した OSS 「status-back」の紹介

Airシリーズのフロントエンジニア日野澤 (@kt3k) です。今日はリクルートライフスタイル Airシリーズのフロントエンドチームで行なっている Frontend Ops の取り組みを紹介させて頂きます。また、その過程で開発した OSS ツール status-back について紹介します。

(なお本稿では、Frontend Ops とは Frontend における DevOps の取り組み、すなわち各種ツールチェインや自動化手法を用いて、フロントエンドにおける開発及びデリバリーパフォーマンスの向上を行う手法及びその実践と定義します。参考1, 参考2)

Jenkins による lint / テスト / モックビルドの自動化

Airフロントエンドチームでは、数年前から Jenkins を用いて、フロントエンドの各種ルーチン作業の自動化を行なっています。初期には、製品のフロントエンドモックのビルドだけを主に自動化していましたが、今では、lint / ユニットテスト / e2e テスト / storybook などのデザイン見本のビルド / リリース用アセットの生成など、あらゆる場面で Jenkins を活用しています。

Airウエイトフロントエンドのビルドパイプライン。lint、stylelint、storybook(2セット)、テスト、モックビルドの6つの観点で Pull Request を検証している様子

CI からマルチステータスを返す

古くからある Jenkins プラグインのGitHub PR ビルダーを使うと、1つの CI ビルドに対して 1つのステータスを返すことが出来ます。1つの CI ビルドに対して1ステータスというのは、人気のある CI プラットフォームの TravisCircle CI でも基本的には同様です。

しかし、フロントエンドにおける CI の用途が多様化するにつれて 1ビルド 1ステータスしか返せないのはだんだんと不便になってきました。例えば、モックビルド / lint / ユニットテスト で CI を使っているプロジェクトがあった時とします。1ステータスしか返らない場合だと、CI から失敗ステータスが返って来た時に、それが、モックの失敗か、lint の失敗か、テストの失敗かが分かりません。3つの観点がそれぞれ別のステータスで github に返って来れば、開発者は自分のコードの何が問題だったかを、Pull Request ページを一目見るだけで分かります。

1ステータスしか返さない CI の例、これでは開発者は何が問題だったのか、ビルドの詳細ページ行かないと分からない。たかだか数クリックの問題だが、継続的に蓄積すれば大きな差となる。

Jenkins から複数のステータスを返すために、以前は、PR ビルダーを設定した Jenkins ジョブ自体を複数作るという対応の仕方 (すなわち、モック用ジョブ、lint 用ジョブ、テスト用ジョブを全て別のジョブとして作るやり方) をしていた時期もありましたが、設定量が多くなりすぎるため、ジョブ自体のメンテナンスに問題が生じていました。また、複数のジョブがそれぞれ workspace を占有するため、各ジョブの node_modules 以下が占有するディスク量が増大し、Jenkins サーバー自体のスケールにも問題がありました。

そんな中で出てきたのが Jenkins 2 のパイプライン機能です。パイプライン機能自体は、多様な使い方が出来る機能の集合ですが、中でも使いやすいのは Declarative Pipeline と言われる新しい文法の Jenkinsfile を用いた、1 ジョブ中に複数の stage を定義出来る設定方法です。

Airフロントエンドチームでは、Declartive Pipeline を用いて、1ビルドの中に複数の stage を定義し、各 stage が個別のビルドステータスを返すという設定を主に用いて、初期と比べると相当に快適な CI 環境で開発を行なっています。

各コミットに複数のビルドステータスが返って来ている様子。なお、各ステータスには URL が設定でき、モックであればモックページ、lint であればコンソール出力、storybook であれば、Jenkins 上にホストした storybook のトップページにリンクしている。

status-back

status-back は CI 環境から簡単に今チェックアウトされている commit に対して github のステータスを設定できるツールです(node.js で実装されています)。同様のツールに commit-status (node.js)、github-commit-status (Go言語) 、githubNotify (Jenkins プラグイン) などがあります。

Airフロントエンドチームでは設定の簡単さからこれまで commit-status を主に使っていましたが、commit-status は GitHub Enterprise への対応が手薄で(Airフロントエンドチームではほとんどのソースコードを GitHub Enterprise で管理しています)、最初はそもそも GitHub Enterprise での使用は不可能でしたが、筆者が Pull Request を送り、なんとか GHE から利用可能な状態に修正されました。ただ、やはり、そもそも Circle CITravis での利用をメインに考えて作られたツールのため、設定の記述が煩雑になってしまいます。

commit-status 自体はそこまで複雑なツールではないため、いっそのことより汎用性の高いインターフェースで作り直してしまった方が、各プロジェクトのパイプライン定義がスッキリ記述でき、メンテナンス性も上がるという判断から、status-back を開発しました。

status-back を用いると、1 stage の定義は典型的に以下のようになります。(Jenkinsfile での stage の記述です)

Jenkinsfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    stage('lint') {
      environment {
        STATUS_CONTEXT = 'jenkins/lint'
      }
      steps {
        sh 'npx status-back -p "Linting..." $BUILD_URL'
        sh 'npm run lint'
      }
      post {
        success {
          sh 'npx status-back -s "Lint success!" $BUILD_URL'
        }
        failure {
          sh 'npx status-back -f "Lint failed!" $BUILD_URL'
        }
      }
    }

commit-status を使っていた頃は以下のような記述になっていました。

Jenkinsfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    stage('lint') {
      steps {
        sh 'npm run ci:status -- pending jenkins/lint "Lint中" $BUILD_URL'
        sh 'npm run lint'
      }
      post {
        success {
          sh 'npm run ci:status -- success jenkins/lint "Lint OK" $BUILD_URL'
        }
        failure {
          sh 'npm run ci:status -- failure jenkins/lint "Lint Error" $BUILD_URL'
        }
      }
    }

package.json

1
2
3
4
5
6
7
{
  ...
  "scripts": {
    ...
    "ci:status": "CIRCLE_SHA1=`git rev-parse HEAD` commit-status",
  }
}

記述が煩雑な上、Circle CI 用の環境変数 CIRCLE_SHA1 をわざと定義していたり、git rev-parse のようなあまり一般的でない git のサブコマンドを利用していたりしていたりして、この設定自体に秘伝のタレ感がありますね。ステータスのコンテキスト jenkins/lint が毎回重複して記述されているため、ここでスペルミスをすると、タスクの開始時にペンディングにしたコンテキストと別なコンテキストを success / failure にしてしまい混乱が生じるなどのデメリットがありましたが、それらの問題を status-back では解決しています。

今後の展望

今回は Frontend Ops の取り組みの中から、 CI でマルチステータスを返す取り組みについて紹介しました。Airフロントエンドチームでは、他にも様々な自動化、デリバリー効率化の取り組みを行なっており、最近では、e2e テストの cypress 移行や、CI 環境の Drone 移行などに取り組んでいます。