ブランチ保護ルールの進化系！リポジトリルールセット

次世代のブランチ保護ルール「リポジトリルールセット」がパブリックベータになりましたね！
ブランチ保護ルールとの違いは以下の点に注目してみると分かりやすいかと思います。

|  | ブランチ保護ルール | リポジトリルールセット |
|-----------|------------|------------|
| 対象 | ブランチ | ブランチおよびタグ |
| 1つの対象に複数のルールを同時に適用 | できない | できる |
| 閲覧に必要なロール | 管理者 | 読み取り |
| 作成、編集および削除に必要なロール | 管理者 | 管理者 |
| ルールの無効化 | できない（削除はできる） | できる |

## 1. 対象

ブランチ保護ルールでは名前の通り対象はブランチに限られていましたが、リポジトリルールセットではタグを対象に出来るようになりました。
これにより、リリースにもルールを適用できるようになりました。

## 2. 1つの対象に複数のルールを同時に適用

### ブランチ保護ルール

ブランチ保護ルールでは1つの対象（ブランチ）に対して、ルールは1つしか適用できません。
1つの対象に影響を与える複数のルールがある場合は優先度が計算され、最も高いルールのみが適用されます。

優先度は高いものから

1. 特定のブランチ名
1. `*`, `?`, `]`などの特殊文字が含まれたパターン

となっており、同一優先度の中では`作成された日付が古いもの`が最も優先されます。

#### 優先度の動作確認

##### 特殊文字が含まれたパターンよりも特定のブランチ名が優先される

![スクリーンショット 2023-04-18 12.08.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526241/89836f17-f105-a33f-2db4-777bee3cc510.png)

##### 同一優先度の中では作成日時が古いものが最も優先される

リストの中で上にあるものが古いルールです。
![スクリーンショット 2023-04-18 12.12.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526241/9095e1c8-5178-032c-e3bc-61e558831736.png)

### リポジトリルールセット

ルールセットには優先度の概念がなく、かわりに階層化という仕組みがあります。

#### 階層化

複数のルールセットが同じブランチまたはタグを対象とする場合、各ルールセットは集約されます。
そして集約された各ルールセットに同じルールが異なる内容で定義されていた場合、`最も制限の厳しいルール`が適用されます。

階層化はルールセット同士だけでなく、同じブランチまたはタグを対象とする保護ルールも階層化の対象となります。

##### 階層化の動作確認

|  | ブランチ保護ルール1 | ブランチ保護ルール2 | リポジトリルールセット |
|-----------|------------|------------|------------|
| ターゲットパターン | mai* | main* | ma* |
| 作成順 | 1 | 2 | 3 |
| 必要な承認の数 | 1 | 2 | 3 |

![スクリーンショット 2023-04-18 13.47.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526241/23c6e2ee-5be0-83be-67fb-3c835bc06542.png)
ブランチ保護ルールの優先度に従えば必要な承認の数は`1`となるはずですが、
階層化の仕組みが働き、必要な承認の数は`3`となっています。

では、リポジトリルールセットの制限を緩めてみた場合はどうなるのでしょうか。

|  | ブランチ保護ルール1 | ブランチ保護ルール2 | リポジトリルールセット |
|-----------|------------|------------|------------|
| ターゲットパターン | mai* | main* | ma* |
| 作成順 | 1 | 2 | 3 |
| 必要な承認の数 | 1 | 2 | 0 |

![スクリーンショット 2023-04-18 13.51.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526241/e8396bce-182e-13d1-13c6-ba2895c32b8b.png)
もっとも厳しい`必要な承認の数`ルールは`ブランチ保護ルール2`の`2`ですが、実際には`1`となっています。
ここから、ブランチ保護ルールを優先度で絞り込んだ上でリポジトリルールセットと集約されていることが分かります。

## 3. 閲覧に必要なロール

ブランチ保護ルールは閲覧にも管理者またはリポジトリルールの編集権限を持つカスタムロールが必要となります。
一方、リポジトリルールセットは読み取りロールがあればルールセットを閲覧できます。

> Anyone with read access to a repository can view the active rulesets for the repository. This means a developer can understand why they have hit a rule, or an auditor can check the security constraints for the repository, without requiring admin access to the repository.
> （リポジトリへの読み取りアクセス権を持つユーザーは、リポジトリのアクティブなルールセットを表示できます。これは、開発者がルールにヒットした理由を理解できること、または監査人がリポジトリへの管理者アクセスを必要とせずに、リポジトリのセキュリティ制約を確認できることを意味します。）

[引用元](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets#:~:text=Anyone%20with%20read%20access%20to%20a%20repository%20can%20view%20the%20active%20rulesets%20for%20the%20repository.%20This%20means%20a%20developer%20can%20understand%20why%20they%20have%20hit%20a%20rule%2C%20or%20an%20auditor%20can%20check%20the%20security%20constraints%20for%20the%20repository%2C%20without%20requiring%20admin%20access%20to%20the%20repository.)

## 4. 作成、編集および削除に必要なロール

ブランチ保護ルール、リポジトリルールセットのどちらも管理者またはリポジトリルールの編集権限を持つカスタムロールが必要となります。

## 5. ルールの無効化

### ブランチ保護ルール

ブランチ保護ルールには有効・無効を切り替える機能がないため、無効化するには以下の方法が考えられます。

1. ルールを削除する
2. ルールを編集し、個別の内容をオフにする
3. より優先度の高いルールを作成する

1、2は単純ですが破壊的です。[GitHubアプリ](https://github.com/apps/settings)を利用するなどして設定をコード化することが望ましいでしょう。
3は非破壊的ですが、既に優先度が最高のルールに対しては無力です。

### リポジトリルールセット

リポジトリルールセットにはステータスがあり、簡単に有効/無効（評価）を切り替えることができます。
![スクリーンショット 2023-04-18 14.25.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526241/ec4feb37-e484-9c6b-fff0-6601d3a24c2c.png)

## 余談

ブランチ保護ルールの[REST API](https://docs.github.com/ja/rest/branches/branch-protection?apiVersion=2022-11-28)はブランチリソースに対して紐づくため、以下の欠点がありました。

- 存在しているブランチに対してのみ定義できる
- `*`などの特殊文字を使用できない

[GraphQL API](https://docs.github.com/ja/graphql/reference/mutations#createbranchprotectionrule)で上記の欠点は改善されていたのですが、GraphQL APIはグローバルノードIDとユーザーフレンドリーなIDとのギャップがあり、銀の弾丸ではなかったように感じました。（[IDの変換に関わるOSSのバグ](https://github.com/octokit/auth-app.js/issues/374)などもあり）

しかしこちらもリポジトリルールセットの[REST API](https://docs.github.com/ja/rest/repos/rules?apiVersion=2022-11-28)では、リポジトリにルールセットが紐づいている自然な構造のおかげでとても使いやすくなっておりました！ありがとうGitHub！

## 参考

https://github.blog/changelog/2023-04-17-introducing-repository-rules-public-beta/

https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets

https://docs.github.com/ja/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule

https://docs.github.com/ja/organizations/managing-user-access-to-your-organizations-repositories/repository-roles-for-an-organization