tl;dr;
タイトルが全て
検証内容
サンプルコード
GitHub Actionsで使える(事前定義済みの)環境変数 *1を列挙するだけのシンプルなワークフローです
on: - push - pull_request jobs: show_env: runs-on: ubuntu-latest steps: - run: env | grep GITHUB_ | sort
https://github.com/sue445/github_actions_sandbox_20210106/blob/master/.github/workflows/sandbox.yml
masterブランチに普通にpushした時
コミットグラフはこんな感じ
https://github.com/sue445/github_actions_sandbox_20210106/runs/1656991711 には
GITHUB_SHA=f308d17b80228b432d841e96624b583f632d2411
と出ているので、 master
ブランチ *2 のHEADと一致していることが分かると思います。(分かる)
PullRequestに対してpushした場合
検証用のPR https://github.com/sue445/github_actions_sandbox_20210106/pull/1
on
で push
と pull_request
を指定してるのでそれぞれイベントが発火します。
この時点でコミットグラフはこんな感じ
pushイベントの結果
https://github.com/sue445/github_actions_sandbox_20210106/pull/1/checks?check_run_id=1657017337 には
GITHUB_SHA=8d1396316903bd747589ff2a81930e659cca1a4d
と出ているので、 tmp/pr_test
ブランチのHEADと一致しています。(分かる)
pull_requestイベントの結果
https://github.com/sue445/github_actions_sandbox_20210106/pull/1/checks?check_run_id=1657017535
GITHUB_SHA=9b0ee21ee4668fab1d29961f60108b9ef3c1946b
え!!!???
解説
https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#push によるとpushイベントでの GITHUB_SHA
は
Commit pushed, unless deleting a branch (when it's the default branch)
とのこと。
https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#pull_request によるとpull_requestイベントでの GITHUB_SHA
は
Last merge commit on the GITHUB_REF branch
とのこと。
これしか書いていなくていまいち意味が分からなかったのですが id:r7kamura さんが RuboCop Problem Matchers で
pull_request イベントを起点に動かす場合、actions/checkout はそのPull Requestが生成しようとしているmerge commitをチェックアウトする。
と書いてるように、この GITHUB_SHA
はそのPull Requestで生成しようとしてるmerge commitのSHAだと思われます。
ちなみにこの挙動はTravis CIと同じような仕様です。
2021/01/08 追記
- Travis CI以外にもCircleCIやJenkinsのGithub Pull Request Builderが同じ挙動
- BitriseはGitHubのPullRequestのマージコミットではなく、内部でマージした時のマージコミットをcheckoutしてる
- WerckerはブランチのHEADを使ってる
トノコト
自分が知っているものではBitriseはpull-reqトリガーで設定すると同様だったと思います。後はJenkinsもGithub Pull Request Builderプラグインを使用してpull-reqのwebhookを受けた場合は同じだったと思います
— Kenta.Kase (@Kesin11) 2021年1月7日
すみません今手元でBitriseのログを見直したところ、Bitriseはまた微妙に違う挙動でgithubのマージコミットをcheckoutしているわけではなくてgit clone後に自前でmergeしていました。その上でGIT_CLONE_COMMIT_HASHという環境変数にはブランチHEADのコミットハッシュが入っていました
— Kenta.Kase (@Kesin11) 2021年1月7日
ありがとうございます。
— sue445 (@sue445) 2021年1月7日
気になって手持ちのリポジトリ調べたのですが、WerckerはPRでもブランチのHEADをcheckoutしてたけどCircleCIはマージコミットのSHAをコミットしてました。公式ドキュメントはどっちもマージコミットに関して言及してなかったのでブランチのHEADを使ってると思ってました…
GITHUB_SHAが異なることで何が困るか
ほとんどのケースではそれほど困ることはないと思います。*3
しかしジョブの中で GITHUB_SHA
を使おうとした場合に注意が必要です。
具体的には tfnotify でTerraformの実行結果をPull Requestのコメントに出そうとした時に、pull_requestイベントを使うとPullRequestと直接紐付かない前述のマージコミットに対してコメントがつくため、初見だと謎のコミットに対してコメントが付く状態になります。(PullRequest上に登場するコミットに対してコメントがつけばPullRequest上で表示されます)
余談:tfnotifyでpull_requestイベントの時にもPullRequestにコメントをつけたい
半年くらいずっと例の謎コミットにコメントがついて困ってたのですがつい先日下記で解決しました。
- name: terraform plan run: | # NOTE: tfnotify uses GITHUB_SHA, but GITHUB_SHA can't be override in env if [ -n "$PR_HEAD_SHA" ]; then export GITHUB_SHA=$PR_HEAD_SHA fi terraform plan -input=false | tfnotify plan env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
実際にPull Requestに対してtfnotifyのコメントがついた図
ちなみにコード中のコメントでも書いていますが
env: GITHUB_SHA: ${{ github.event.pull_request.head.sha }}
のように書いても GITHUB_SHA
を上書きできないので、別名として取得してジョブの中で代入して上書きするしかないです。
FAQ
Q. だったらpull_requestは不要では?
業務リポジトリのように開発者全員が同一リポジトリ内でPullRequestをやり取りする場合にはpushイベントだけで問題ないです。
しかしpushイベントだとforkされたリポジトリからPullRequestがきた時にイベントが発火しなくてCIのジョブが実行されないのでOSSのリポジトリで困ります。(経験済)
かといって今回のサンプルみたいに
on: - push - pull_request
で書くとPullRequestがきた時に2つジョブが実行されて無駄です。
色々検証した結果個人リポジトリだと下記に落ち着きました。
on: push: branches: - master pull_request: types: - opened - synchronize - reopened
40個以上のリポジトリで上記設定を半年以上使ってますが今の所不満はないです。
今の心境
GitHub Actionsの仕様を理解した結果、何も分からなくなってしまった...
— sue445 (@sue445) 2021年1月6日
*1:公式の説明は https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables
*2:最近GitHubではmainブランチがデフォルトになってますが毎回両方併記のは大変なので本エントリでは masterブランチで統一します
*3:トピックブランチを作った時点でのmasterブランチとPullRequestを出した時のmasterブランチが大きく剥離してるとローカルではテストが通るのにCIだとテストがコケるという事象は発生するかもしれません