⛔️

Approve されてもラベルを付けるまでマージできなくする方法

2023/05/22に公開

この記事では、Approveされてもラベルを付けるまでマージできないようにする方法について解説します。

背景

とあるプロジェクトで、 GitFeatureFlow というフローを採用しています。

このフローでは、mainブランチからトピックブランチを切って開発し、開発が終わったらmainブランチに向けてプルリクエストを作成します。その後、プログラムのレビューをレビュアーに依頼します。しかし、 Approveされてもすぐにマージできません。QAを依頼し、QAが通過してからマージできます。

この運用において1点懸念がありました。それは、Approveされたプルリクエストはワンクリックでマージできてしまうので、QA未完了の機能が誤ってマージされる可能性があることです。

そこで、私はApproveされてもラベルを付けるまでマージできないようにする仕組みを作りました。これにより、QA未完了の機能がマージされるミスを防ぐことができます。同じようなケースで悩んでいる方の参考になれば幸いです。

実際の動作

実際に動作しているプルリクエストは下記から閲覧できます。

ラベルを付けていない例

https://github.com/bicstone/temp-label-checker/pull/1

GitHubのスクリーンショット。ラベルを付けていないのでマージがブロックされている
ラベルを付けていないのでマージがブロックされている

ラベルを付けた例

https://github.com/bicstone/temp-label-checker/pull/2

GitHubのスクリーンショット。ラベルを付けているのでマージができる
ラベルを付けているのでマージができる

方針

  • 特定のラベルを付けるまではBranch Protection ruleでマージできないようにする
  • セキュリティ上のリスクを軽減するため、GitHub Appを用いずに自前で実装する

ワークフローの作成

そこで、次のようなワークフローを作成しました。

特定のラベル (この例では マージOK) が付けられたら、プルリクエストの Label Check checkにsuccessのステータスを付けます。一方、特定のラベルが付けられていない場合は、プルリクエストのchecksにpendingのステータスを付けます。

name: Pull Request

on:
  pull_request:
    types:
      - synchronize
      - labeled
      - unlabeled
      - opened
      - reopened

permissions:
  statuses: write

jobs:
  ready-deploy:
    runs-on: ubuntu-latest
    name: Label Check
    steps:
      - name: Set status check to success
        if: "contains(github.event.pull_request.labels.*.name, 'マージOK')"
        run: |
          curl --request POST \
            --url https://api.github.com/repos/${{github.repository}}/statuses/${{github.event.pull_request.head.sha}} \
            --header 'authorization: Bearer ${{secrets.GITHUB_TOKEN}}' \
            --header 'content-type: application/json' \
            --data '{
              "context": "Label Check",
              "state": "success",
              "description": "マージできます"
            }'
      - name: Set status check to pending
        if: "!contains(github.event.pull_request.labels.*.name, 'マージOK')"
        run: |
          curl --request POST \
            --url https://api.github.com/repos/${{github.repository}}/statuses/${{github.event.pull_request.head.sha}} \
            --header 'authorization: Bearer ${{secrets.GITHUB_TOKEN}}' \
            --header 'content-type: application/json' \
            --data '{
              "context": "Label Check",
              "state": "pending",
              "description": "\"マージOK\" ラベルを付けるとマージできます"
            }'

on は、プルリクエストの特定のイベントで発火するように設定しています。ちなみに synchronize は新しいコミットが追加された時に発火します。コミットにstatusが紐づいているため、コミットが増えたら再度statusを付け直す必要があるためです。

https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#pull_request

permissions には statuses: write を設定しています。デフォルトの GITHUB_TOKEN はステータスを書き換える権限がないため、この設定が必要です。逆に contents など他の権限は不要です。

https://docs.github.com/ja/actions/using-workflows/workflow-syntax-for-github-actions#permissions

step では、statusのREST APIにPOSTすることでステータスを付けています。

https://docs.github.com/ja/rest/commits/statuses?apiVersion=2022-11-28

Branch Protection rule の設定

無事に Label Check checkが付けられるようになったので、次はBranch Protection ruleの設定をします。

Branch Protection ruleは、特定のブランチに対して、マージの条件を設定できます。今回は、Label Check checkがsuccessになっていることをマージの条件にします。

Settings > Branches > Branch protection rulesから、Branch name patternに main を設定して、Require status checks to pass before mergingの項目に Label Check を追加します。

ちなみに、Require branches to be up to date before mergingは、プルリクエストをマージする前に、最新のmainをpullしなければならない制約です。この機能はGitFeatureFlowを使用している場合、マージしてから先にマージされた作業との競合が発覚するリスクを軽減します。オンにしておくのがオススメです。

GitHubのスクリーンショット。Branch protection rule の設定画面
Branch protection rule の設定画面

https://docs.github.com/ja/github/administering-a-repository/about-protected-branches

Terraformを用いる場合は次のように設定します。

resource "github_branch_protection_v3" "repo" {
  repository = github_repository.repo.name
  branch     = "main"

  required_status_checks {
    contexts = ["Label Check"]
    strict   = true  # Require branches to be up to date before merging
  }
}

まとめ

Approveされても特定のラベルを付けるまでマージできないようにする方法について紹介しました。シンプルなワークフローを書いて、開発者体験を低下させることなくより安全に開発を進められる仕組みを作れました。

Discussion