Open15

Merge Queue有効時のTerraform Apply

tjuntjun

前提

GitHubでPull Requestを作ったらTerraform Planを行い、ApproveをもらってマージしたらTerraform ApplyするようなGitHub ActionのWorkflow.

main branchへのpushをtriggerにして、場合によっては paths フィルターも書いてtriggerの条件を書く。

on:
  push:
    branches:
      - main
    paths:
      - terraform関連のファイル

課題

このmain pushでのtriggerは、merge queueを有効にするとtriggerされないことがある。

merge queueがどのように動作するかは↓に書いてあってまだ正確に理解できていないけど、観測した限りmerge queueの先頭にいないでmergeされるとmain push イベントがtriggerされない
https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue#how-merge-queues-work

Shunsuke SuzukiShunsuke Suzuki

main pushでのtriggerは、merge queueを有効にするとtriggerされないことがある

これ厳しいですね。 GitHub の Support に問い合わせてみると良いかも。

Shunsuke SuzukiShunsuke Suzuki

Merge Queue って 複数の PR をまとめてマージする際に 1 つのコミットでマージするんでしたっけ?
それとも PR ごとに commit 別?
squash merge だとまとめられそうな気もする。
コミットが 1 つになると、 tfaction は 1 merge commit = 1 PR を想定しているので期待通り動かないような (main への push で apply workflow を実行する場合、 commit に関連する PR を取得して変更された Root Module のリストを取得しますが、その際に関連する PR が複数あっても最初の PR しか参照されない) 気がします。
自分の方でも確認します。

tjuntjun

自分がいろいろ調べた限りではそういう仕様と理解だったんですが、一応聞いてみようと思います

tjuntjun

そもそもtfaction以前にworkflowがtriggerされないんですよね。pathsの条件消しても同様です

Shunsuke SuzukiShunsuke Suzuki

あぁ、そうですね。 workflow が trigger されない問題trigger されても複数 PR まとめてマージされると正常に動かない の 2 つの問題がありますね。

tjuntjun

サポートに聞いたところの回答(Copilotからの回答)は以下のような感じでした

When using the Merge Queue feature, the behavior you're observing with the on push: main trigger is expected and related to how the Merge Queue operates. Here's why:

When a pull request is added to the Merge Queue, GitHub creates temporary branches (with a prefix like gh-readonly-queue/{base_branch}) to validate the changes in the pull request. Once the required checks pass for these temporary branches, the changes are merged into the target branch (e.g., main). However, this merge process does not trigger workflows configured with on push: main because the merge is performed by the Merge Queue system, and it bypasses the typical push event.

To ensure your workflows are triggered appropriately when using the Merge Queue, you need to update your GitHub Actions workflows to include the merge_group event as an additional trigger. This event is specifically designed to handle workflows in the context of a Merge Queue. Here's an example of how you can configure your workflow:

on:
push:
branches:
- main
merge_group:
By adding the merge_group event, your workflows will be triggered when the Merge Queue processes pull requests and merges them into the target branch. This ensures that your workflows run as expected, even when using the Merge Queue.

If you also use third-party CI providers, you may need to configure them to recognize and handle the temporary branches created by the Merge Queue. These branches have a special prefix (gh-readonly-queue/{base_branch}), and your CI configuration should be updated to run checks on these branches.

Let me know if you need further clarification or assistance with setting this up!

Shunsuke SuzukiShunsuke Suzuki

おー、ありがとうございます。

this merge process does not trigger workflows configured with on push: main because the merge is performed by the Merge Queue system, and it bypasses the typical push event.

なるほど。知りませんでした。
一般論として、 merge_group は複数回実行されることがあるから、マージ後に 1 回だけ実行したいっていう場合は独自に仕組みを作らないといけないですね。

tjuntjun

そうですね、複数回の実行も課題になりそうです。

tjuntjun

Apply workflow がTriggerされないと困るので、確実に実行したい場合にはmerge_groupイベントのtriggerを使うべき、ということになる。しかし、triggerをmain pushからmerge_queueに書き換えただけではうまくいかない部分がある。

1. merge_queueのtriggerにpaths の条件は書けない。

terraformだけ管理するrepositoryにおいてはあまり問題ではないが、monorepoでさまざまなコードが入っているrepositoryですべてのPRのmerge_groupイベントに対してterraform applyが走るのはあまりよくない。https://github.com/dorny/paths-filter などを使って、関係ないPRの場合はすぐに終了するようにしている。
これは大きな問題ではない。

2. applyに失敗するとmerge queueから戻される

merge_group triggerで実行されるworkflowは、失敗するとmerge queueから戻されてPRはマージされない。これは一つのstateしか触っていない場合には便利な挙動で、applyの失敗の原因を修正して再度approveもらってmerge queueに入れればよい。
しかし複数のstateを触っている場合は問題があって、一部のコードはapplyされて一部のコードはapplyされていない状態でPRとしは戻されるので、成功した部分においてはmainのコードとstateがずれてしまう。そのまま他のPRが同じstateに関わる変更を行うと、applyされた変更が巻き戻ることになってしまう。
また、moduleの変更で多くのstateを一気に更新したい場合に、どこか一つでもapplyエラーが出ていると何度も戻されて全然マージできない、ということもある。

 continue-on-error: ${{ strategy.job-total > 1 }}

のような条件を追加して、並列でterraform applyを実行している場合にはapply失敗してもstepとしては成功にしてしまうことで、マージを進めることができる。

つまり、

  • terraform applyが一つだけ走るときは、apply失敗時はjobを失敗にしてmergeさせずに戻すことで元のPR上で直してもらう
  • applyが2つ以上走るときにはapply失敗してもjobを成功にしてマージする
    とするのがよさそうな気がしている。

3. tfactionのfollow up PRの仕組みが使えない

上記の課題があるので、applyが2つ以上走ってエラーになったapplyについては tfactionのfollow up PRを利用して解消したい。
https://suzuki-shunsuke.github.io/tfaction/docs/feature/follow-up-pr

しかしやってみて気付いたのは、merge_groupイベントでfollow up PRを作ると、merge前のmainに対してコードを追加してfollow up PRを作成することになるので、その後mergeして進んだコードを取り込まないとPRの変更が入っていないコードでplanを実行してしまう。そのため、apply失敗したのにplanでNo Changeとなるのはなぜ?みたいになってしまう。mergeしたあとのmainでfollow up PRを作る仕組みを別途作るか、follow up PRで進んだmainを自動で取り込む、などやらないとだめかも

Shunsuke SuzukiShunsuke Suzuki

terraform applyが一つだけ走るときは、apply失敗時はjobを失敗にしてmergeさせずに戻すことで元のPR上で直してもらう

1 つだけの場合であっても apply が失敗している以上乖離は発生している可能性があるので 1 か複数かはあまり関係ないような気もします。
勿論複数の場合のほうが解消に時間がかかったり、同時に PR が作られやすかったりして問題になりやすいというのはある気はします。

tjuntjun

たしかに1つのapplyでも部分的にapplyされることはあるのでその通りですね