PR駆動でGitHub Actionsを動かし、その中で行ったterraform fmtを自動でpushさせてPRを自動更新するようにしました。
これで常に*.tfファイルが綺麗な状態で保たれます。
terraform fmt結果をpushする方法
こんな感じのワークフローファイルを書きました。
name: terraform fmt
on:
pull_request:
paths:
- '**.tf'
jobs:
terraform_fmt:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- uses: hashicorp/setup-terraform@v2
with:
terraform_version: "1.6.2"
- name: terraform fmt -check
id: fmt_check
continue-on-error: true
run: terraform fmt -check -recursive
- name: fmt, commit, and push
if: steps.fmt_check.outcome == 'failure'
run: |
terraform fmt -recursive
git config user.name __YOUR_NAME__
git config user.email __YOUR_EMAIL__
git add *.tf
git commit -m "[auto] terraform fmt -recursive"
git push origin ${{ github.head_ref }}
いくつか説明を書きます。
説明1: contents: writeを設定している
fmt結果をコミットしてプッシュするので、contents: writeの権限が必要です。
説明2: actions/checkout時にブランチを指定している
actions/checkout@v4の箇所でwithを使ってブランチを指定しています。指定しているブランチはプルリクエストを出しているブランチです。
github.head_refについてはGitHub Actions内で参照できるコンテキストです。詳細は githubコンテキスト で確認できます。
ブランチをわざわざ指定しているのは detached head を避けるためです。以下に補足があるので、ご存じの方は飛ばしてくださいませ。
補足:actions/checkoutの挙動
actions/checkout@v4のREADMEを見ると$GITHUB_SHAが指すものをフェッチしてくると書かれています。
この$GITHUB_SHAは既定の環境変数というドキュメントを見てみると以下のように書かれています。
ワークフローをトリガーしたコミットSHA。このコミットSHAの値は、ワークフローをトリガーしたイベントによって異なります。
ではon.pull_requestでワークフローをトリガーしたときにはどんなコミットが値に入っているかというと、pull_requestのイベントのドキュメントを覗いてみるとこのように書かれています。
このイベントの GITHUB_SHA が pull request マージ ブランチの最後のマージ コミットであることに注意してください。
「最後のマージコミット」というのがイマイチ伝わりませんが、これは仮想的にプルリクエストのブランチをターゲットにマージしたときの、マージコミットを意味します。ようは未来のマージコミットです。検索力が足りずこれの原典ドキュメントに当たれなかったのですが、以下のstackoverflowも参考になると思います。
https://stackoverflow.com/questions/68061051/get-commit-sha-in-github-actions
actions/checkout@v4のログを見ても、確かに /usr/bin/git checkout --progress --force refs/remotes/pull/5/mergeというコマンドが吐かれており、謎のrefs/remotes/pull/5/mergeというブランチをチェックアウトしていることが分かりますが、このブランチこそが仮想的にマージを試し、マージコミットが生成されたブランチです。
さて、謎ブランチの未来のマージコミットがチェックアウトされていることが分かりましたが、これだとこの後terraform fmtした後に、プルリクエストを出したブランチにpushできずに困ってしまいますよね。
そこで、withオプションでプルリクエストのヘッドブランチを指定することでこの問題を解決しています。
説明3: fmt -check時にcontinue-on-errorを指定している
terraform fmt -check -recursive時にcontinue-on-error: trueを設定しています。仮にfmtのチェックに引っ掛かっても次のstepに進みます。
説明4: fmt -checkがエラーの場合だけコミット&プッシュしている
if: steps.fmt_check.outcome == 'failure'を設定してfmt -checkがエラーの時だけ処理を行います。これにより、何もfmtされなかった場合はコミットもプッシュも行いません。
ここではgitコマンドをたくさん実行していますが、このような便利アクションも存在するようです。
https://github.com/stefanzweifel/git-auto-commit-action
終わりに
terraform fmt -checkだけでフォーマットに問題があったらCIを失敗させるのでもいいですが、1人開発なのでfmtを自動化しました。これでfmtを気にせずに開発をすることができそうです。