虎の穴ラボ技術ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

GitHub Actions: 自動フォーマット後にCIが動かない問題を解決する方法

こんにちは!虎の穴ラボの鷺山です。

「GitHub Actionsでソースコードを自動フォーマットして、その変更をGITHUB_TOKENを使ってPushする」ことは広く採用されている仕組みだと思います。

しかし、GitHub Actionsには「GITHUB_TOKENによるPushでは別のワークフローが自動起動されない」という制約があります。

GITHUB_TOKEN によってトリガーされたイベントでは (中略)、新しいワークフロー実行は作成されません。

ワークフローのトリガー - GitHub Docs

そのため「自動フォーマットのPush後、別のビルド用ワークフローを自動実行させる」ことが、そのままではできません。 その結果、ブランチの最新ビルドステータスが不明になってしまい、開発時に不便な状況になります。

この課題を解決するため、この記事ではGITHUB_TOKENによるPush後に、別のワークフローを自動起動させる方法をご紹介します。

変更前のワークフロー

まずソースコードを自動フォーマットしてPushするGitHub Actionsワークフローの例を示します。
今回は例として、PythonのコードをBlackで自動フォーマットするワークフローを扱います。

▼自動フォーマット用ワークフロー (変更前)

# .github/workflows/auto-format-by-black.yml
name: Blackによる自動フォーマット

on:
  push:
    branches: [main, develop]
    paths: ["**/*.py"]
  pull_request:
    paths: ["**/*.py"]

permissions:
  contents: write

jobs:
  format:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref || github.ref_name }}
      - uses: actions/setup-python@v5
      - name: Blackのインストール
        run: pip install black
      - name: Blackで自動フォーマットを実行
        run: black .
      - name: 差分をチェック
        id: check-diff
        run: git diff --exit-code || echo "has_changes=true" >> $GITHUB_OUTPUT
      - name: 差分をPush
        if: steps.check-diff.outputs.has_changes == 'true'
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add .
          git commit -m "Blackによる自動フォーマット"
          git push

このワークフローはPythonファイル (*.py) の変更時に実行されます。
なお、ワークフロー中にGITHUB_TOKENは明記されていませんが、actions/checkoutで内部的に使用されています。

今回は、この自動フォーマットのPushによってコードベースが変更された際に、以下のビルド用ワークフローを起動させたい場面を考えます。

▼ビルド用ワークフロー (変更前)

# .github/workflows/build.yml
name: ビルド

on:
  push:
    branches: [main, develop]
    paths: ["**/*.py"]
  pull_request:
    paths: ["**/*.py"]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      ... # 各ビルドステップ

このワークフローも、Pythonファイル (*.py) の変更時に実行される設定になっています。

しかし、自動フォーマットによってPythonファイル (*.py) が変更されても、このビルド用ワークフローは自動実行されません。 これは前述のGITHUB_TOKENによるPushでは別のワークフローが自動起動されないというGitHub Actionsの制約によるものです。

変更後のワークフロー

自動フォーマット用ワークフローでGITHUB_TOKENによるPushを行った後にビルド用ワークフローを起動させるには、それぞれのワークフローを以下のように変更します。

▼自動フォーマット用ワークフロー (変更後)

# .github/workflows/auto-format-by-black.yml
name: Blackによる自動フォーマット

...

permissions:
  contents: write
  actions: write # 👈 Actionsの実行権限を追加

jobs:
  format:
    ...
    steps:
      ...

      # 👇 ビルドを起動するステップを追加
      - name: ビルドを起動
        if: steps.check-diff.outputs.has_changes == 'true'
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            // GitHub APIを使って workflow_dispatch イベントをトリガー
            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'build.yml',                        // 起動したいワークフローのファイル名
              ref: '${{ github.head_ref || github.ref_name }}' // ワークフローを起動するブランチ
            })

💡ポイント

  • permissions:actions: writeを追加し、別のワークフローを起動できるようにします。
  • ワークフローを起動するAPI createWorkflowDispatch()を実行するステップを追加します。
    • 明示的なワークフロー起動であれば、GITHUB_TOKENによるトリガーであっても制約を受けずに起動できます。
    • workflow_idに起動したいワークフローのファイル名(build.yml)を指定します。
    • refにワークフローを起動するブランチを指定します。ここでは自動フォーマットが行われたブランチを指定します。

▼ビルド用ワークフロー (変更後)

# .github/workflows/build.yml
name: ビルド

on:
  ...
  workflow_dispatch: # 👈 ここを追加

...

💡ポイント

  • on:workflow_dispatchを追加し、 外部からのワークフロー起動を受け付けるようにします。

ビルドステータスを更新する

以上の変更で、GITHUB_TOKENによるPushから別のワークフローを自動起動させることができるようになります。

しかし、workflow_dispatchによるワークフロー起動ではコミットのビルドステータス (成功✅ or 失敗❌️) の反映が行われません。 これではブランチの最新ビルドステータスが分かりにくいため、ビルド用ワークフローにステータス更新APIを明示的に実行するステップを追加します。

▼ビルド用ワークフロー (さらに変更後)

# .github/workflows/build.yml
name: ビルド

...

permissions:
  ...
  statuses: write # 👈 ステータスの変更権限を追加

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      ...

      # 👇 ステータスを更新するステップを追加
      - name: ステータス更新 (workflow_dispatch 用)
        if: always() && github.event_name == 'workflow_dispatch'
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const state = '${{ job.status }}' === 'success' ? 'success' : 'failure';

            await github.rest.repos.createCommitStatus({
              owner: context.repo.owner,
              repo: context.repo.repo,
              sha: context.sha,
              state: state,
              target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
              context: '${{ github.workflow }} / ${{ github.job }} (${{ github.event_name }})',
              description: state.charAt(0).toUpperCase() + state.slice(1),
            });

💡ポイント

  • permissions:statuses: writeを追加し、コミットステータスを更新できるようにします。
  • コミットのステータスを更新するAPI createCommitStatus()を実行するステップを追加します。
    • ビルドの成功・失敗に応じた結果がコミットのステータス (state) に反映されるように設定します。
    • トリガーイベントがworkflow_dispatchの場合のみ実行されるようにif:文で制御します。

これにより、GITHUB_TOKENによるPushから別のワークフローを自動起動させつつ、その後のビルドステータスも更新させることができるようになります。

ビルドステータスを更新
ビルドステータスを更新

まとめ

この記事では、GitHub Actionsのworkflow_dispatchとGitHub APIを組み合わせることで、GITHUB_TOKENによるPush後に別のワークフローを起動させる方法をご紹介しました。
これにより、自動フォーマット後のコードでCIを自動実行し、ビルドステータスを常に最新の状態に保つことができます。

同じような課題で悩んでいた方の参考になれば幸いです!

採用情報

虎の穴ラボでは一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧ください。
toranoana-lab.co.jp