特定のプレフィックスを持つブランチのみマージを許可したい

## はじめに

Gitのブランチ運用は[Gitflow](https://www.atlassian.com/ja/git/tutorials/comparing-workflows/gitflow-workflow)や[GitHub Flow](https://docs.github.com/ja/get-started/quickstart/github-flow)が有名ですが、プロジェクトによって様々なブランチの運用方法があると思います。
ブランチが多くなると管理が複雑になってきて、事故が起こる可能性が増えます。特に、私はいつかマージ先を間違えてしまいそうだなーと思っていました。
そこで、特定のprefixを持つブランチのみマージを許可するやり方を教わったので、こちらにまとめたいと思います。

## 実現方法
- GitHub Actions上で[Danger JS](https://danger.systems/js/)を使用し、プルリクエストのhead branch(変更を加えた取り込みたいブランチ)名をチェックし、適切なブランチ名でなければCIを失敗させる。
- GitHubのブランチ保護ルールで、マージ時にGitHub Actionsのチェックを必須にする。

## Dangerとは
Dangerは、プルリクエストの体裁を自動でチェックできるツールです。例えば、
- 変更した行数が規定数を超えていないか
- PRのタイトルに任意の文字列（例えば"WIP"）が含まれていないか
- 任意のファイル（例えばCHANGELOG.md）が更新されているか
などをCI上でチェックすることができます。

今回はDangerを使って、プルリクエスト時にブランチ名をチェックして、プレフィックス`release/`を持つブランチのみをmainブランチにマージできるようにします。

Dangerには[Ruby版](https://danger.systems/ruby/)、[Javascript版](https://danger.systems/js/)、[Swift版](https://danger.systems/swift/)があるようですが、今回はJavascript版を使いました。それぞれ文法が異なるようなので、詳しくは公式リファレンスを参照ください。

## .github/workflows/ci.ymlの作成

GitHub Actions用のymlファイルを作成します。

設定は以下を参考にしました。
- [GETTING STARTED WITH DANGER JS](https://danger.systems/js/guides/getting_started.html)
- [DangerとGitHub ActionsでPull Request時に最低限のレビューを自動化する](https://qiita.com/ut0n/items/7fbb0f8977d43de88792)

```.github/workflows/ci.yml
name: Danger

on:
  pull_request

jobs:
  danger:
    name: Danger
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      # dangerのインストールにnode v14以上が必要なので準備
      - name: Prepare Node
        uses: actions/setup-node@v1
        with:
          node-version: 18
    
      - name: Install yarn
        run: npm install -g yarn
     
      # package.jsonがない場合は作成.ルートディレクトリにある場合は不要.
      - name: Initialize yarn
        run: yarn init -y
        
      # package.jsonにdangerを追加
      - name: Add danger to package.json
        run: yarn add danger --dev
       
      # パッケージをインストール
      - name: Install danger
        run: yarn
        
      # dangerを実行.GitHubリポジトリにアクセスするためGITHUB_TOKENが必要.
      - name: Run Danger
        run: yarn danger ci
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

### dangerfile.jsの準備

プルリクエストのどのような情報をチェックするかは`dangerfile.js`に記載していきます。Dangerは自動でルートディレクトリにある`dangerfile.js`を参照するので、作成します。
「プレフィックス`release/`を持つブランチのみをmainブランチにマージできる」を実現するには、以下のように記述します。

```dangerfile.js
import { danger } from 'danger';

const baseBranchName = danger.github.pr.base.ref
const headBranchName = danger.github.pr.head.ref

if (baseBranchName === 'main' && !headBranchName.startsWith('release/')) {
  fail(baseBranchName + ' branch cannot be merged with ' + headBranchName + ' branch.')
}
```

`danger.github.pr`で、プルリクエストに関する情報をもつオブジェクトを取得できます。`head.ref`で取り込みたいブランチ名、`base.ref`でマージ先のベースブランチ名を取得します。
ベースブランチが`main`かつ、ヘッドブランチが`release/`で始まらない場合、CIを失敗させます。

CIを失敗させるには`fail()`を呼び出します。引数には失敗時に残すメッセージを記載します。

### ブランチ保護ルールの設定

GitHubのリポジトリページのSettings > Branches > Add ruleを選択します。

![スクリーンショット 2022-10-19 7.32.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/5ce446ab-68af-ffff-1429-fe9c51b2312e.png)

2つめの`Require status checks to pass before merging`にチェックを入れてCreateボタンをクリックします。

![スクリーンショット 2022-10-19 7.39.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/2385dd78-6a4a-134f-b578-71e3bd229ce6.png)


これにて設定は完了です。試しに、mainブランチに`feature/use_danger`ブランチをマージするプルリクエストを作成してみます。

![スクリーンショット 2022-10-19 8.01.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/9f2dfe9e-a2b2-c10b-6717-8ca8dba0615f.png)

CIが失敗し、図のようにfail()で渡したメッセージがコメントの形で残ります。（何度も失敗した場合は、コメントの編集という形で上書きされていくようです）

今度はブランチ名を変えて、`release/`でプルリクエストを出してみます。All greenとなり、マージができるようになりました。

![スクリーンショット 2022-10-19 7.47.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584114/0af3ebe7-3721-82a2-a145-9f0828121cf3.png)

### 最後に

今回は表題を実現する最小限の設定を行いました。現状`dangerfile.js`の文法が間違っているときCIが通ってしまうため、ESLintなどの静的解析もあったほうがよさそうです。
Dangerはブランチ名チェックだけでなく、PRタイトルや変更コード行数などの複雑なチェックを自動化できます。Dangerを使いこなして、レビューを効率よくしていきたいなと思いました。