🐈

【GitHub Actions】編集されたディレクトリに応じてラベルを付与する

に公開

はじめに

最近になってTerraformを触る機会が少し増えてきました。そのリポジトリはdevelopment, staging, productionのそれぞれのディレクトリがありました。

.
└── environments
    ├── development
    │   ├── main.tf
    │   └── xxx.tf
    ├── staging
    │   ├── main.tf
    │   └── xxx.tf
    └── production
        ├── main.tf
        └── xxx.tf

この構成では環境ごとに別々のPRが立てられます。その中で自分がレビューしている、または出しているPRがいったいどの環境のものなのかわからなくなることがよくありました。そのためGitHub Actionsを活用して自動で環境ラベルを貼るようにしました。

実装方法

特定のディレクトリに変更があった際にActionを実行したいときは、多くの場合GitHub Actionのpathフィルターを活用することで実現ができると思います。

https://docs.github.com/ja/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow#using-filters-to-target-specific-paths-for-pull-request-or-push-events

今回の目的は「PRで編集されたディレクトリに応じてラベルが付与された状態にする」です。これには2つの要件があります。

  • 特定のディレクトリ配下のファイルが編集されていた場合、ラベルを付与する
  • 特定のディレクトリ配下のファイルが編集されなくなった場合、ラベルを削除する

pathフィルターでは、2つ目の要件を達成できないとは言いませんが煩雑になってしまいます。そのため今回は別の方法を考えます。

Labelerを使う

一番お手軽に実現ができるのは、Labeler Actionを使う方法です。
https://github.com/actions/labeler

Readmeや、記事が多くあるのでこちらを使う場合は別の記事を参照してください。

https://dev.classmethod.jp/articles/github-actions-pull-request-labeler/

https://qiita.com/Ry0xi/items/cf65be4917496be97870

https://qiita.com/ucan-lab/items/4b02dec969df399253f7

今回はもう少し細かいstepのカスタマイズなどをしたいため、こちらのActionは使いませんでした。

もうちょっと自分でカスタムできるようにする

まずはファイルの変更検知が必要です。paths-filter Actionを利用しました。

https://github.com/dorny/paths-filter

変更を検知して、ラベルを付与したいディレクトリをmatrixとして以下のように利用できます。

name: Detect Changes
on:
  pull_request:
    types:
      - opened
      - synchronize
  workflow_dispatch:

jobs:
  detect:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        env: [development, staging, production]
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: changes
        with:
          filters: |
            env:
              - 'environments/${{ matrix.env }}/**'

      - name: 
        if: steps.changes.outputs.env == 'true'
        # environments/${{ matrix.env }} 配下のファイルが変更されたときの処理

development, staging, productionの3つの環境において、並列にjobを実行します。path-filter Actionを使うと、ステップ単位でファイルの変更の有無を参照できます。

次に必要なのはラベルの付与、削除です。今回はghコマンドを使用しました。

    # 省略
    env:
      GITHUB_REPO: ${{ github.repository }}
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      PR_NUMBER: ${{ github.event.number }}
    # 中略
      - name: add label
        if: steps.changes.outputs.env == 'true'
        run: |
          gh pr edit "$PR_NUMBER" --add-label ${{ matrix.env }}

      - name: remove label
        if: steps.changes.outputs.env == 'false'
        run: |
          gh pr edit "$PR_NUMBER" --remove-label ${{ matrix.env }}

このコマンドでは、ラベルが事前に存在していない場合はfailします。development, staging, productionというラベルを作成しておきました。

ghコマンドを利用することで簡単にラベルが付与できます。環境変数を適切に渡してあげてください。複数のstepで利用するため、jobに対して環境変数を定義しておきました。

最終的にできたもの

こちらになります

name: Environment Label
on:
  pull_request:
    types:
      - opened
      - synchronize
  workflow_dispatch:

jobs:
  label:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        env: [development, staging, production]
    env:
      GITHUB_REPO: ${{ github.repository }}
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      PR_NUMBER: ${{ github.event.number }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: changes
        with:
          filters: |
            env:
              - 'environments/${{ matrix.env }}/**'

      - name: add label
        if: steps.changes.outputs.env == 'true'
        run: |
          gh pr edit "$PR_NUMBER" --add-label ${{ matrix.env }}

      - name: remove label
        if: steps.changes.outputs.env == 'false'
        run: |
          gh pr edit "$PR_NUMBER" --remove-label ${{ matrix.env }}

個人的には結構簡潔に書けて満足です。

応用してみる

ラベルが付与されたらSlack通知する

こんな感じで拡張できます。

https://github.com/slackapi/slack-github-action#usage-1

# 省略
      - name: add label
        if: steps.changes.outputs.env == 'true'
        run: |
          gh pr edit "$PR_NUMBER" --add-label ${{ matrix.env }}

      - name: Post to a Slack channel
        if: steps.changes.outputs.env == 'true'
        uses: slackapi/slack-github-action@v2.0.0
        with:
          method: chat.postMessage
          token: ${{ secrets.SLACK_BOT_TOKEN }}
          payload: |
            channel: ${{ secrets.SLACK_CHANNEL_ID }}
            text: "howdy <@channel>!"

      - name: remove label
        if: steps.changes.outputs.env == 'false'
        run: |
          gh pr edit "$PR_NUMBER" --remove-label ${{ matrix.env }}

Discussion