22時に寝ようと思って2時に寝る。

備忘録や日記を書いてます。きょうは早く寝よう。

CircleCI - コミットメッセージに特定の文字列が含まれていれば、ある処理を行う

はじめに

この記事では、CircleCI で「コミットメッセージに特定の文字列が含まれていれば、ある処理を行いたい」場合の実現方法について紹介します。結論としては、circleci-agent step halt コマンドを利用することで比較的シンプルに実現できます。

今回は、CircleCI 2.1 で動作確認を行っています。

実現したかったこと

これまで、iOS プロジェクトにおいて以下のルールでワークフローを定義していました。

  • master 以外のブランチは、Unit test を実行する
  • master ブランチは、Unit test に加えて、UI testApp Store Connect へのデプロイ を実行する

これに加えて、新たに以下のルールを追加しました。

  • master 以外のブランチは、コミットメッセージに「kick-ui-test」という文字列が含まれていれば、 Unit test のあとに UI test を実行する

定義したワークフローの全体像

f:id:azuuun:20200427105238p:plain
図: master ブランチ以外の Workflow
f:id:azuuun:20200427105329p:plain
図: master ブランチの Workflow

yaml の全体像

※ 各ジョブ内で行っている処理(呼び出している commands)については省略して例示します。

version: 2.1

jobs:
  unit_test:
    <<: *defaults
    steps:
      - unit-test

  run_ui_test_as_needed:
    <<: *defaults
    steps:
      - checkout
      - run:
          name: Decide whether to run ui-test, depending on the content of the commit message.
          command: |
            COMMIT_MESSAGE=$(git log -1 HEAD --pretty=format:%s)
            TRIGGER_MATCH_PATTERN="^.*kick-ui-test.*$"
            if [[ ${COMMIT_MESSAGE} =~ ${TRIGGER_MATCH_PATTERN} ]]; then
              echo "Continue to run ui_test, as the commit message contains kick-ui-test."
            else
              echo "Since the commit message does not include kick-ui-test, this job will be successfully terminated."
              circleci-agent step halt
            fi
      - ui_test

  ui_test:
    <<: *defaults
    steps:
      - ui_test

  deploy_appstore:
    <<: *defaults
    steps:
      - deploy

workflows:
  build-workflow:
    jobs:
      - unit_test
      - run_ui_test_as_needed:
          requires:
            - unit_test
          filters:
            branches:
              ignore: 
                - master
      - ui_test:
          filters:
            branches:
              only:
                - master
      - deploy_appstore:
          requires:
            - unit_test
            - ui_test
          filters:
            branches:
              only:
               - master

run_ui_test_as_needed ジョブについて

このジョブで 「コミットメッセージに「kick-ui-test」という文字列が含まれていれば、 Unit test のあとに UI test を実行する」という制御を実現しています。

run_ui_test_as_needed:
  <<: *defaults
  steps:
    - checkout
    - run:
        name: Decide whether to run ui-test, depending on the content of the commit message.
        command: |
          COMMIT_MESSAGE=$(git log -1 HEAD --pretty=format:%s)
          TRIGGER_MATCH_PATTERN="^.*kick-ui-test.*$"
          if [[ ${COMMIT_MESSAGE} =~ ${TRIGGER_MATCH_PATTERN} ]]; then
            echo "Continue to run ui_test, as the commit message contains kick-ui-test."
          else
            echo "Since the commit message does not include kick-ui, this job will be successfully terminated."
            circleci-agent step halt
          fi
    - ui_test

主に shell script で以下の処理を行っています。

  1. 直近のコミットメッセージを取得
  2. 正規表現で、特定したい文字列(今回であれば kick-ui-test)を定義
  3. 1. で取得したコミットメッセージに特定文字列が含まれているかを判定
    1. もし、含まれていれば何もしないことを echo するだけで、次のステップへ進む
    2. もし、含まれていなければその時点でジョブを正常終了する
  4. ui_test を実行する

3.2 で、ジョブを正常終了させる際には、circleci-agent step halt コマンドを利用しています。このコマンドは、ジョブを失敗させずに終了させることができるもので、ジョブを条件付きで実行する必要がある今回のようなケースに便利です。

Ending a Job from within a step | CircleCI

さいごに

今回は、master ブランチ以外(機能の実装途中など)でも手軽に ui_test を実行したいケースがあり、run_ui_test_as_needed job を追加しました。

CircleCI 2.0 から次のジョブを続行する前に手動による承認操作を待つ制御が可能になりましたが、仕組み上、手動で承認する手間が発生するため、今回のようにコミットする時点で特定ジョブの実行を制御できると便利だと感じました。

参考