commitメッセージに特定文言を含む場合にAutifyを実行する仕組みをGitHub Actionsで実装してみた

※この記事は「[Autifyアドベントカレンダー2022](https://qiita.com/advent-calendar/2022/autify)」14日目の記事です。

## 作ろうとしたきっかけ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652652/289f1944-1df7-0ab0-9708-627b6de78cb4.png)

じゃあ作ってみよう！ と思って作ったのがこちらです。

## 必要なもの
- GitHub Actions

## ざっくりの動き
1. 開発中のcommitメッセージに`autify`と`${機能名}`を含めて`git push`する
1. GitHub Actionsのワークフローが　「commitメッセージに`autify`を含むこと」を検知
1. テスト環境にfeatureブランチを反映
1. `${機能名}`からAutifyのテストプランIDを特定
1. Autifyのテストプランを実行

## 実装してみた

```yaml
specifiy-test-plan:
    runs-on: ubuntu-latest
    # 1. commitメッセージにautifyを含む場合だけjobを実行する。それ以外のときは何もしない
    if: contains(${{ github.event.head_commit.message }}, 'autify')

    steps:
    # 2. 正規表現を使ってcommitメッセージに含まれる機能名を取得する
    - name: Get Commit Message
      uses: actions-ecosystem/action-regex-match@v2
      id: regex-match
      with:
        text: ${{ github.event.head_commit.message }}
        regex: '".*autify(.*?)"'

    # 3. 2.で取得した機能名からテストプランIDを特定する
    - name: Specify Test Plan ID
      id: plan-match
      if: ${{ success() && steps.regex-match.outputs.group1 }}
      run: |
        case '${{ steps.regex-match.outputs.group1 }}' in
        *機能A*)
          ID=11111;;
        *機能B*)
          ID=22222;;
        *機能C*)
          ID=33333;;
        *)
          echo "::error::Commit message must contain a kind of function name."
          exit 1;;
        esac
        echo "PLAN_ID=$ID" >> GITHUB_OUTPUT

# 4. テスト環境を構築する（詳細は省略します）
test-environment-deploy:
    needs: specifiy-test-plan
    uses: ./.github/workflows/deploy.yml
    secrets: inherit

# 5. Autifyを実行
autify:
    runs-on: ubuntu-latest
    needs: [ specifiy-test-plan, test-environment-deploy ]

    steps:
    - name: Run Autify
      uses: autifyhq/actions-web-test-run@v2
      with:
        access-token: ${{ secrets.AUTIFY_WEB_ACCESS_TOKEN }}
        autify-test-url: https://app.autify.com/projects/00/test_plan/${{ needs.specifiy-test-plan.outputs.PLAN_ID }}
```

## 解説
### 1. commitメッセージにAutifyを含む場合だけjobを実行する。それ以外のときは何もしない
```yaml
    if: contains(${{ github.event.head_commit.message }}, 'autify')
```
- `if:`がtrueの時だけjobを実行
- `contains(search, item)`は、`search`に`item`が含まれる場合にtrueを返す
    - [Expressions(contains) - GitHub Docs](https://docs.github.com/ja/actions/learn-github-actions/expressions#contains)
- `${{ github.event.head_commit.message }}`で直近のcommitメッセージが取得できる
- つまり、commitメッセージに`autify`を含む時だけjobを実行する

### 2. 正規表現を使ってcommitメッセージに含まれる機能名を取得する
```yaml
    - name: Get Commit Message
      uses: actions-ecosystem/action-regex-match@v2
      id: regex-match
      with:
        text: ${{ github.event.head_commit.message }}
        regex: '".*autify(.*?)"'
```
- 2022年12月現在、GitHub Actionsは正規表現を利用できないため、[actions-ecosystem/action-regex-match](https://github.com/marketplace/actions/actions-ecosystem-action-regex-match)という外部のactionsを利用
    - `text`に指定した文字列から、`regex`に指定した正規表現に合致する文字列を抜き取ることができる
        - `text`
            - 下記のようなcommitメッセージが入ってくることを想定
                - eg.） "test: autify 機能A"
        - `regex`
            - `'".*autify(.*?)"'`
                - `"`は、commitメッセージを囲んでいるダブルクォーテーションを示す
                - `.*`は、autifyの前後にある文字列で、どんな文字種でも・何文字でも良い。
                - `(.*)`とカッコで囲むことで、欲しい文字列だけを抜き取ることができる
- 正規表現を手元で試すなら、下記のサイトがおすすめ
    - https://regex101.com/

### 3. 2.で取得した機能名からテストプランIDを特定する

```yaml
    - name: Specify Test Plan ID
      id: plan-match
      if: ${{ success() && steps.regex-match.outputs.group1 }}
      run: |
        case '${{ steps.regex-match.outputs.group1 }}' in
        *機能A*)
          ID=11111;;
        *機能B*)
          ID=22222;;
        *)
          echo "::error::Commit message must contain a kind of function name."
          exit 1;;
        esac
        echo "PLAN_ID=$ID" >> GITHUB_OUTPUT
```
- shellでcase文を書いて、正規表現で取得した機能名に応じて、shell変数$IDにテストプランIDを代入しています
- `${{ steps.regex-match.outputs.group1 }}`は、先ほど正規表現のうち`(.*)`に合致した文字列、つまり機能名が入っている想定です
- もしどの機能名にも当てはまらない場合は、エラーメッセージを出力して`exit 1`で処理を終了します

### 4. テスト環境を構築する
```yaml
test-environment-deploy:
    needs: specifiy-test-plan
    uses: ./.github/workflows/deploy.yml
    secrets: inherit
```

- `on: workflow_call`を設定した別ワークフローを呼び出しています
    - [Reusing workflows - GitHub Docs](https://docs.github.com/ja/actions/using-workflows/reusing-workflows)
- `secrets: inherit`はworkflow_callで呼び出した別ワークフロー内でも同じ権限でsecretsを扱うことができるおまじないです
    - [Workflow syntax for GitHub Actions - GitHub Docs](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsecretsinherit)
- git pushした対象のブランチ名は`${{ github.ref_name }}`で取得することができます
- デプロイ対象のテスト環境による部分だと思いますので、詳細は割愛します

### 5. Autifyを実行
- 詳細は公式に詳しい説明が掲載されていますので、そちらをご覧ください！
    - [GitHub Actions連携 - Autify University](https://help.autify.com/docs/ja/github-actions-integration)
    - [autifyhq/actions-web-test-run - GitHub](https://github.com/autifyhq/actions-web-test-run)
- [autifyhq/web-run-test-plan-action](https://github.com/autifyhq/web-run-test-plan-action)は古いバージョンであり、メンテナンスが終了しているようです。

## おまけ
2022年12月現在、Autify CLIがリリースされており、ターミナルからAutifyを実行できます。そのため、もうこの仕組み自体は不要になりましたが、自前で作るより便利になっています！（嬉しい）
詳しくは[Autify アドベントカレンダー2022 の2日目の記事](https://qiita.com/meikos/items/80b2a5b1e869b816c829)をご覧ください！