SREのクラシマです。
弊社ではGitHub - technote-space/get-diff-action: GitHub Actions to get git diffを多用していたのですが、2023年11月にarchivedになってしまいました。
同actionではnode16を利用していることもあり、git diffコマンドに書き換えることにしました。
ついでなので、terraformと一緒に利用しているecspressoについて、tfcmtのようにecspresso diffがPull Request上で見られると便利だね、ということで同僚が作ったecspresso diff actionについても紹介します。
ecspresso diff action全容
--- name: Diff task definition on: pull_request: paths: - 'ecspresso/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: HEAD_REF: ${{ github.head_ref }} START_COMMENT: "### ecspresso diff" jobs: get-target-dirs: name: get target dirs permissions: contents: read pull-requests: write runs-on: ubuntu-latest timeout-minutes: 5 outputs: target-dirs: ${{ steps.target-dirs.outputs.dirs }} steps: - name: Checkout timeout-minutes: 3 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 sparse-checkout: | ecspresso - name: Get target dirs timeout-minutes: 2 id: target-dirs run: | set -xv diff_dirs=$(git diff --diff-filter=AMRCD \ --name-only \ "origin/$GITHUB_BASE_REF..${GITHUB_REF/refs\//}" \ -- ecspresso \ | xargs dirname \ | sort -u \ | jq -cnR '[inputs | select(length > 0)]' ) echo "#diff : ${diff_dirs}" echo "dirs=${diff_dirs}" >> "$GITHUB_OUTPUT" - name: Comment remove PR timeout-minutes: 1 env: GH_TOKEN: ${{ secrets.PAT }} run: | set -xv remove_comment_ids=$(gh api \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ --jq '.[] | select(.user.login == "torana-dev-user") | select(.body | startswith("${{ env.START_COMMENT }}\n")) | .id') if [ -n "$remove_comment_ids" ]; then for comment_id in $remove_comment_ids; do gh api \ --method DELETE \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "repos/${{ github.repository }}/issues/comments/$comment_id" done fi diff: runs-on: ubuntu-latest permissions: id-token: write contents: read pull-requests: write needs: [get-target-dirs] timeout-minutes: 10 strategy: matrix: target-dir: ${{ fromJson(needs.get-target-dirs.outputs.target-dirs) }} steps: - name: Checkout if: needs.get-target-dirs.outputs.target-dirs != '[]' && needs.get-target-dirs.outputs.target-dirs != '' timeout-minutes: 3 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 sparse-checkout: | ecspresso - uses: aquaproj/aqua-installer@fd2089d1f56724d6456f24d58605e6964deae124 # v2.3.2 with: aqua_version: v2.25.1 - name: Configure AWS credentials timeout-minutes: 1 uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 with: role-to-assume: arn:aws:iam::123456789012:role/github-actions-oidc-role aws-region: ap-northeast-1 - name: Get service name id: get-service-name run: | service_name=$(echo "${{ matrix.target-dir }}" | awk -F'/' '{print $3"-"$2}') echo "service_name=$service_name" >> "$GITHUB_OUTPUT" - name: ecspresso diff id: ecspresso-diff timeout-minutes: 2 working-directory: ${{ matrix.target-dir }} run: | set -xv # イメージタグは既存の値を使う(差分を無視するため) actual_image_tag=$(aws ecs describe-task-definition \ --task-definition "${{ steps.get-service-name.outputs.service_name }}" \ | jq -r '.taskDefinition.containerDefinitions[0].image | match(":(.*)$").captures[0].string') diff=$(IMAGE_TAG="$actual_image_tag" ecspresso diff --config ecspresso.yml) { echo "diff<<EOF" echo "$diff" echo "EOF" } >> "$GITHUB_OUTPUT" - name: Comment on PR timeout-minutes: 1 env: GH_TOKEN: ${{ secrets.PAT }} run: | set -xv comment="${{ env.START_COMMENT }} #### Target - ${{ matrix.target-dir }} " # shellcheck disable=SC2078 if [ ${{ contains(steps.ecspresso-diff.outputs.diff, '+') }} ] || [ ${{ contains(steps.ecspresso-diff.outputs.diff, '-') }} ]; then diff='${{ format( '<details><summary>Show Diff</summary> ```diff {0} ``` </details>', steps.ecspresso-diff.outputs.diff ) }}' else diff='``` No changes ```' fi gh pr comment "${{ env.HEAD_REF }}" --body "$comment""$diff"
get-diff-actionからの切り替え
修正前はこう↓でした。
get-diff-actionを通ると、env.GIT_DIFFに変更差分のファイルが設定されるので、そこから変更差分のディレクトリに変換していました。
steps: - name: Checkout timeout-minutes: 3 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 sparse-checkout: | ecspresso - uses: technote-space/get-diff-action@f27caffdd0fb9b13f4fc191c016bb4e0632844af # v6 - name: Get target dirs timeout-minutes: 2 id: target-dirs run: | diff_dirs=$(echo ${{ env.GIT_DIFF }} \ | tr ' ' '\n' \ | grep "ecspresso/" \ | xargs -I{} dirname {} \ | sort -u \ | tr "\n" " " \ | jq -cR 'split("\n") | map(select(length > 0))' ) echo "#diff : ${diff_dirs}"
ecspresso deployは別のworkflowにしているのでこれで良いのですが、terraform用のworkflowはpush時にapplyするようにしているので、更に一工夫いります。
github.eventのbeforeとafterには、それぞれマージ前とマージ後のcommit hashが入っているので、これを使います。
- name: target branch id: target_branch run: | set -xv branch="" from="" to="" if [ "$GITHUB_EVENT_NAME" = "push" ]; then branch="$GITHUB_REF_NAME" from="${{ toJSON(github.event.before) }}" to="${{ toJSON(github.event.after) }}" else branch="$GITHUB_BASE_REF" from="origin/$GITHUB_BASE_REF" to="${GITHUB_REF/refs\//}" fi { echo "branch=$branch" echo "from=$from" echo "to=$to" } >> "$GITHUB_OUTPUT" - name: get diff run: | set -xv diff=$(git diff --diff-filter="AMRCD" \ --name-only \ "${{ steps.target_branch.outputs.from }}...${{ steps.target_branch.outputs.to }}" \ -- "**.tf" \ | xargs) echo "GIT_DIFF=$diff" >> "$GITHUB_ENV"
Pull Requestコメントの書き換え
tfcmtでは、複数回terraform planした場合にPull Requestのコメントを上書きしてくれる機能があり、大変便利です。
tfcmt で Terraform の CI/CD を改善する
我々も同じように、と考えたのですがなかなか難しかったので(debugもむずい)、もっと雑に対応しました。
ghコマンドでPRについているecspresso diffで付与したコメントを全部取得して順繰り削除、もう一回コメントし直す、というものです。
ecspresso diff
ecspressoのinstallはGitHub - aquaproj/aqua: Declarative CLI Version manager written in Go. Support Lazy Install, Registry, and continuous update with Renovate. CLI version is switched seamlesslyを使用し、ecspressoのディレクトリ構成は↓こんな感じです。
ECSクラスタ名、ECSサービス名は<service_name>-<env_name>で固定しています。(e.g. hoge-prd, fuga-stgなど)
❯ tree -d ecspresso ecspresso ├── prd │ └── service_name └── stg └── service_name 5 directories
実行結果はこんな感じです。
まとめ
無事に脱get-diff-actionできました。
単純な書き換えで済むと思っていたのですが、想像以上にget-diff-actionは利用者に使いやすく作ってあったのだと痛感しました。改めてお礼申し上げます。
本当はclassic PATを使ってる部分もいい感じにしたかったのですが、GitHub App移行の道が険しく断念中です。
permissionsをうまいことやればsecrets.GITHUB_TOKENへの切り替えはできそうなんですが...。