🐧

tfaction で Renovate の PR が no change じゃないのに勝手にマージされるという問い合わせを解決した件

2025/01/18に公開

tfaction のユーザーの方が、 Renovate の PR に diff があるのに勝手にマージされてしまうと X で呟いていたので調べて解決しました。
結論を最初に言うと tfaction のバグではなく、 hashicorp/setup-terraform の wrapper のバグが原因でした。

なお、本記事はユーザーの方から 今回の知見などは適宜公開して問題ない と承諾を頂いた上で書いています。

tfaction は 自分が OSS として開発している GitHub Actions で Terraform Workflow を構築するための Framework です。

https://github.com/suzuki-shunsuke/tfaction

tfaction はデフォルトで Renovate が生成した PR の terraform plan の結果が No Change でない場合、 CI が失敗するようになっています。
こうすることで変更が勝手にマージされて terraform apply されるのを防ぐことが出来ます。

https://suzuki-shunsuke.github.io/tfaction/docs/feature/renovate

しかし、なぜかこれが機能せず、 CI が成功してそのままマージされてしまうという話でした。

結論: setup-terraform の wrapper の Bug

先述の通り、 hashicorp/setup-terraform を使っていて wrapper が有効になっていたのが原因でした。
wrapper を無効化すると解決しました。

setup-terraform は terraform をインストールする action ですが、デフォルトで terraform コマンドの wrapper をインストールします。
wrapper 経由で Terraform が実行されるようになり、 terraform コマンドの stdout, stderr, exit code を action の output として参照することが出来るようになります。

https://github.com/hashicorp/setup-terraform

Installing a wrapper script to wrap subsequent calls of the terraform binary and expose its STDOUT, STDERR, and exit code as outputs named stdout, stderr, and exitcode respectively.
(This can be optionally skipped if subsequent steps in the same job do not need to access the results of Terraform commands.)

ドキュメントにも書いてあるとおり、後続の step が terraform コマンドの stdout, stderr, exit code を参照しない場合、 wrapper は必要ありません。
そして wrapper には exit code を正しく伝搬しない既知のバグがあります。

https://github.com/hashicorp/setup-terraform/issues/9
https://github.com/hashicorp/setup-terraform/issues/328

このため、 terraform plan -detailed-exitcode の exit code が本来 2 になるところが 0 になっており、今回の問題が発生しました。

https://developer.hashicorp.com/terraform/cli/commands/plan#detailed-exitcode

-detailed-exitcode - Returns a detailed exit code when the command exits. When provided, this argument changes the exit codes and their meanings to provide more granular information about what the resulting plan contains:
0 = Succeeded with empty diff (no changes)
1 = Error
2 = Succeeded with non-empty diff (changes present)

調査の流れ

つぶやきをみかけてから解決に至るまでの流れを書いておきます。

通常こういうのは tfaction に issue を立ててもらってそこでやり取りします。
専用の Issue Template もあります。
当然 Issue 上では英語でやり取りします(日本語で書かないでください)。

https://github.com/suzuki-shunsuke/tfaction/issues/new?template=support-request.yml

今回は X 上でやりとりしました。

つぶやき: tfaction が renovate で diff あっても merge してしまう

幾つかユーザーに質問して問題の切り分けを行いました。

Q. diff があると tfaction の CI が失敗するだが、失敗してるか? 失敗してるのに merge される場合は branch ruleset などの問題かもしれない
A. 成功している

Q. Renovate を GitHub Actions で self host してると tfaction-root.yaml で renovate_login を設定する必要がある
A. 公式の GitHub App renovate[bot] を使っている

自分の環境では再現しないことを確認しました。

Q. renovate-change って label がついてると CI が失敗せずにそのままマージされるが、 label ついてる?
A. ついてない

Q. diff があれば必ず再現してる?それとも特定のディレクトリ、 PR でだけで再現する?
A. 必ず再現している

ここまで質問しても原因がわからなかったので、 tfaction の terraform plan action にデバッグログを出力する patch を当てたものを使ってもらってもらい、ログを確認しました。

https://github.com/suzuki-shunsuke/tfaction/pull/2248

関係ない部分は省略
- name: Plan
  uses: suzuki-shunsuke/tfaction/plan@pr/2248
  env:
    TFACTION_DEBUG: "true"

この機能はまだリリースしてませんが、トラブルシューティングには便利なのでいずれリリースするかもしれません。

ログを確認してもらったところ、どうも tfcmt plan -- terraform plan -detailed-exitcode の exit code が 0 になっているようでした。
これは terraform plan が no change でない以上、妙な話です。

terraform をどのようにインストールしているのか確認させてもらったところ setup-terraform を使っていて、しかも wrapper が有効になっていました (wrapper はデフォルトで有効です)。
自分は aqua で Terraform を管理するので setup-terraform は使いませんが、 setup-terraform がデフォルトで wrapper を差し込んでいるのは知っていました。
なので wrapper が exit code を正しく伝搬できていないのではと思い issue を探したところ、幾つか issue がありました。
そこで wrapper を無効化してみたところ問題が解決しました。

tfaction は aqua を使うことを前提としているので、 Terraform も aqua を使って管理するのが良いと思います。
ただし必須ではないですし、他のツールで管理しても特に問題はありません。

Discussion