開発環境・ツール

GitHub Actionsのbotがpushしてもデプロイが動かない理由|GITHUB_TOKENの再帰防止とworkflow_dispatchでの回避法

GitHub Actionsで「定期実行でデータを取り込み、コミットしてpushし、そのpushをきっかけにデプロイする」という流れを組もうとしました。ところが、ワークフローの中からGITHUB_TOKENでpushしたコミットでは、デプロイ用のワークフローが起動しないことが、公式ドキュメントを確認してわかりました。この記事では、その理由と、シークレットを増やさずに自動デプロイまでつなぐ方法をまとめます。

この記事は2026年10月時点のGitHub公式ドキュメントの記述をもとにしています。

スポンサーリンク

結論:GITHUB_TOKENのpushは他のワークフローを起動しない。workflow_dispatchなら起動できる

先に結論です。

  • GITHUB_TOKENを使って行った操作によるイベントは、新しいワークフローの実行を作りません。ワークフローが自分自身を呼び続ける「再帰」を防ぐための仕様です。
  • ただしworkflow_dispatchとrepository_dispatchは例外で、GITHUB_TOKENから送っても実行が作られます。
  • そこで、pushのあとにgh workflow runでデプロイ用ワークフローを明示的に起動すれば、新しいシークレットを増やさずに自動化できます。
permissions:
  contents: write  # コミットを push するため
  actions: write   # workflow_dispatch を送るため

steps:
  # (データを取り込んでコミット・push する処理)

  - name: Trigger deploy
    if: steps.sync.outputs.changed == 'true'
    env:
      GH_TOKEN: ${{ github.token }}
    run: gh workflow run deploy-staging.yml --ref staging

以下で、やりたかったこと・原因・実装・注意点を順に説明します。

やりたかったこと

  1. 定期実行(schedule)で外部のデータを取り込む
  2. 差分があればstagingブランチにコミットし、GITHUB_TOKENでpushする
  3. そのpushをきっかけに、確認環境へのデプロイ用ワークフロー(on: push)が動く

デプロイ用ワークフローは、人がstagingにマージしたときに動くようpushで起動する設定でした。botがpushしても同じように動くと考えていました。

まず考えたこと:[skip ci]を外せば動くのでは?

取り込み用ワークフローは、もともとコミットメッセージに[skip ci]を付けていました。[skip ci]はそのコミットによるワークフローの実行を止める指定なので、これを外せばデプロイが動くと考えました。

しかし、公式ドキュメントを確認すると、[skip ci]を外しても、GITHUB_TOKENによるpushではデプロイは動かないことがわかりました。止めている原因が別のところにあったためです。

原因:GITHUB_TOKENによるイベントは新しい実行を作らない

GitHubの公式ドキュメントには、次のように書かれています(要約)。

  • リポジトリのGITHUB_TOKENを使って行った操作によるイベントは、新しいワークフローの実行を作らない
  • 例:ワークフローがGITHUB_TOKENでコードをpushしても、リポジトリにpushで起動するワークフローがあっても動かない
  • これは、意図しない再帰的な実行を防ぐため

例外として挙げられているのは次のとおりです。

GITHUB_TOKENで起きたイベント 新しい実行が作られるか
push 作られない
workflow_dispatch 作られる
repository_dispatch 作られる
pull_request(opened / synchronize / reopened) 作られるが、承認待ちの状態になる
pull_request(それ以外の種類) 作られない

つまり、botがpushしてもon: pushのワークフローは動きません。一方で、workflow_dispatchを送れば、相手のワークフローは起動します。

解決方法は2つ

方法A:PATやGitHub Appのトークンでpushする

公式ドキュメントで案内されている方法です。GitHub Appのインストールトークンか個人用アクセストークン(PAT)でpushすれば、on: pushのワークフローも動きます。ただし、トークンをシークレットとして保存し、期限や権限を管理し続ける必要があります。

方法B:workflow_dispatchを送る(今回採用)

GITHUB_TOKENのまま、pushのあとにデプロイ用ワークフローをworkflow_dispatchで起動します。新しいシークレットが要らないので、今回はこちらを選びました。

実装

1. デプロイ用ワークフローにworkflow_dispatchを追加する

# .github/workflows/deploy-staging.yml
name: Deploy staging

on:
  workflow_dispatch:

concurrency:
  group: deploy-staging
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: staging  # 確認環境に固定
    steps:
      - uses: actions/checkout@<コミットSHA> # vX.Y.Z
      # (ビルドとデプロイの処理)

2. 取り込み用ワークフローで、差分があったときだけ起動する

# .github/workflows/sync.yml
name: Sync

on:
  schedule:
    - cron: "0 1 * * *"
  workflow_dispatch:

jobs:
  sync:
    runs-on: ubuntu-latest
    permissions:
      contents: write  # push のため
      actions: write   # workflow_dispatch を送るため
    steps:
      - uses: actions/checkout@<コミットSHA> # vX.Y.Z
        with:
          ref: staging  # 取り込み先のブランチ

      # (データを取り込む処理)

      - name: Commit if changed
        id: sync
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add data/
          # ステージした内容だけを見る(data/ 以外の変更に反応しない)
          if ! git diff --cached --quiet; then
            git commit -m "chore: sync data"
            git push origin HEAD:staging
            echo "changed=true" >> "$GITHUB_OUTPUT"
          else
            echo "No changes."
          fi

      # 差分があったときだけデプロイを起動する
      - name: Trigger deploy
        if: steps.sync.outputs.changed == 'true'
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh workflow run deploy-staging.yml --ref staging

ポイントは3つです。

  1. actions: writeを足す:workflow_dispatchを送るAPIにはActionsの書き込み権限が必要です。ワークフロー全体ではなく、このジョブにだけ付けます。
  2. $GITHUB_OUTPUTで差分の有無を渡す:差分がない日はデプロイが走りません。
  3. ghにGH_TOKENを渡す:GitHubホストのランナーにはghが入っています。
スポンサーリンク

注意点1:workflow_dispatchはデフォルトブランチにファイルがないと動かない

公式ドキュメントには、workflow_dispatchはワークフローのファイルがデフォルトブランチにある場合だけ起動すると書かれています。

deploy-staging.ymlを作業ブランチやstagingに置いただけでは、gh workflow runは失敗します。デフォルトブランチ(多くはmain)にマージされてから動くようになる点に注意してください。

ただし、デフォルトブランチで決まるのは「起動できるかどうか」だけです。実際に動くのは、--ref stagingで指定したブランチ上のdeploy-staging.ymlです。デフォルトブランチのファイルの中身が動くわけではありません。

同じく、scheduleで動くワークフローもデフォルトブランチのファイルが使われます。取り込み用ワークフローの変更も、デフォルトブランチにマージされるまで定期実行には反映されません。

注意点2:本番デプロイにはworkflow_dispatchを付けない

ここで「本番用のデプロイワークフローにもworkflow_dispatchを付ければ、同じ方法で起動できる」と考えるかもしれません。しかし、workflow_dispatchを付けると、Actionsの画面に 「Run workflow」ボタンが表示され、書き込み権限のある人なら手動で実行できるようになります。

本番を「mainへのマージ(=レビューを通ったもの)」でだけデプロイする運用にしている場合、手動実行のボタンはレビューを通さずに本番へデプロイする抜け道になります。

そこで今回は、次のように分けました。

ワークフロー 起動のきっかけ デプロイ先
deploy.yml(既存) push(staging / main) 確認環境 / 本番
deploy-staging.yml(新規) workflow_dispatchのみ 確認環境のみ(固定)

確認環境への自動デプロイは新しいワークフローで行い、本番は従来どおりマージでしか動かない形を保っています。

2つのワークフローが同時に確認環境へデプロイしないよう、concurrencyのgroupをそろえます。ただし既存のdeploy.ymlは確認環境と本番の両方で使うので、groupを固定の名前にすると本番のデプロイまで確認環境と同じ列に並んでしまいます。既存側はブランチごとに分け、stagingのときにだけ同じ名前になるようにします。

# deploy.yml(既存:確認環境と本番の両方)
concurrency:
  group: deploy-${{ github.ref_name }}  # staging → deploy-staging / main → deploy-main
  cancel-in-progress: false

# deploy-staging.yml(新規:確認環境のみ)
concurrency:
  group: deploy-staging                 # deploy.yml の staging と同じ列に並ぶ
  cancel-in-progress: false

確認方法

  1. 取り込み用ワークフローをActionsの画面から手動実行(workflow_dispatch)する
  2. ログで次の順に進んでいるか確認する
    – コミットとpushが成功している
    – 「Trigger deploy」のステップが実行されている(差分がない場合はスキップされる)
  3. Actionsの一覧に、デプロイ用ワークフローの新しい実行が現れているか確認する

よくある質問

Q. [skip ci]を外せば動きますか?

A. 動きません。[skip ci]はそのコミットによる実行を止める指定ですが、GITHUB_TOKENによるpushはもともと新しい実行を作りません。[skip ci]の有無にかかわらず、デプロイ用ワークフローは起動しません。

Q. 本番でも手動実行のボタンを残したい場合は?

A. 本番のワークフローにもworkflow_dispatchが必要なら、Environmentsの保護ルールで守る方法があります。本番用の環境に承認者を指定すれば、手動で起動しても承認されるまでデプロイは止まります。あわせて、その環境にデプロイできるブランチをmainに限定しておくと、別のブランチから本番へデプロイされることも防げます。

Q. botが作ったプルリクエストでCIは動きますか?

A. 2026年10月時点の公式ドキュメントでは、GITHUB_TOKENで作成・更新したプルリクエストのCIは、承認待ちの状態で作られ、書き込み権限のある人が承認すると動くと書かれています。承認なしで自動的に動かしたい場合は、GitHub Appのトークンなどを使います。

まとめ

  • GITHUB_TOKENでpushしても、on: pushのワークフローは起動しない(再帰を防ぐための仕様)
  • workflow_dispatchとrepository_dispatchは例外。pushのあとにgh workflow runで起動すれば、新しいシークレットなしで自動化できる
  • workflow_dispatchを送るにはactions: writeが必要。また、ワークフローのファイルがデフォルトブランチにある必要がある
  • 本番デプロイにworkflow_dispatchを付けると、レビューを通さない手動デプロイの抜け道になる。確認環境専用のワークフローに分ける

同じところで止まっている方の参考になれば幸いです。

参考

スポンサーリンク