🪖

GitHub ActionsでPull RequestのHelm Chartの差分をコメントする

2024/09/12に公開

TL;DR;

以下のyamlファイルの ${{ your-chart-directory }}${{ your-chart-name }} の部分を書き換えてご利用ください。

values.yaml の指定についてはお好みで変更してください。

name: CI

on:
  pull_request:
    paths:
      - ${{ your-chart-directory }}/**

jobs:
  diff:
    name: Diff
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: azure/setup-helm@v4

      - name: Diff
        working-directory: ${{ your-chart-directory }}
        run: |
          git switch ${{ github.base_ref }}
          helm template -f values.yaml --output-dir /tmp/base/ .
          git switch ${{ github.head_ref }}
          helm template -f values.yaml --output-dir /tmp/head/ .
          diff -r -U 1000000 -N /tmp/base/ /tmp/head/ >> /tmp/diff || true

      - name: Delete Old Comment
        uses: actions/github-script@v7
        with:
          script: |
            opts = github.rest.issues.listComments.endpoint.merge({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              per_page: 100,
            })
            const comments = await github.paginate(opts)
            for(const comment of comments) {
              if (comment.user.login === "github-actions[bot]" && comment.body.startsWith("### Diff ${{ your-chart-name }}")) {
                github.rest.issues.deleteComment({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  comment_id: comment.id,
                })
              }
            }

      - name: Update Pull Request
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require("fs")
            const diff = fs.readFileSync("/tmp/diff", "utf-8")
            const output = `### Diff ${{ matrix.key }}
            <details><summary>Show Diff</summary>

            \`\`\`diff
            ${diff}
            \`\`\`

            </details>`;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            })

モチベーション

helm chartを変更するPR(Pull Request)を作った時に、CIでどんな差分なのかコメントしてもらいたかったから。

成果物

TL;DR;の通り

解説

helmや差分の出し方の部分にフォーカスします。

git switch ${{ github.base_ref }}
helm template -f values.yaml --output-dir /tmp/base/ .
git switch ${{ github.head_ref }}
helm template -f values.yaml --output-dir /tmp/head/ .
diff -r -U 1000000 -N /tmp/base/ /tmp/head/ >> /tmp/diff || true

git switch しているので actions/checkoutfetch-depth パラメータが必要です。
流れとしては以下です。

  1. baseブランチ(マージ先ブランチ)に切り替え
  2. helm template/tmp/base に出力
  3. headブランチ(マージ元ブランチ)に切り替え
  4. helm tempalte/tmp/head に出力
  5. 2と4の差分をファイルに出力

diffのオプション

-r: ディレクトリを再起的に比較
-U 1000000: 差分の前後100万行を出力。今回は省略をさせないために指定しています。謎の定数を使いたくない人は diffコマンドでファイル全体を表示する の方法を使うと良いと思います。
-N: ファイルがない場合は空のファイルとして比較を行う。ファイル自体の追加/削除の差分を出すために使っています。

diff ~~~ || true としているのは、差分があった時 diff のexit codeが1になりエラー扱いで中断されるのを防ぐためです。

差分のあったリソースだけ出力したい

helm template--output-dir オプションを使うことで、レンダリングしたマニフェストファイルを指定したディレクト以下に 個別のファイル として出力します。

普通に helm template すると、レンダリングされたマニフェストファイルが --- で区切られた1つのyamlとして出力されてしまい、 diff で差分があったリソースだけを取り出すことが難しくなります。( 逆にレンダリングされたマニフェストファイル全体を見たいなら普通に helm template して diff -U 1000000 をコメントするようにしてください )
--output-dir オプションで個別のファイルに出力し、 diff-r オプションを使うことで、差分のあるリソースだけを抜き出してコメントすることができます。(1つのファイルに複数リソース書いているとまとめて出力されるので、正確にはリソース単位ではなくファイル単位です)

余談

差分の渡し方として $GITHUB_OUTPUT を使って ${{ steps.diff.outputs.diff }} みたいなことをすると差分の中に ${HOGE} みたいな文字列があるとそれをtempalte literalとして展開しようとしてエラーになります。

Discussion