Azure ReposリポジトリのmainブランチにPush制限を設定する

# はじめに
タイトル通りです。
検証する機会がありましたので、備忘として残しておきます。

# 前提
* gitはインストール済み
* ターミナルはwsl2を利用

# 作業
## 1. Azure Reposリポジトリ作成
まずは以下のドキュメントの手順に従って検証用のリポジトリを作成します。

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/create-new-repo?view=azure-devops#create-a-repo-using-the-web-portal

リポジトリ名は `test-repo` としました。
ブランチはデフォルトのmainブランチのみです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/12848126-5691-45bc-b298-f23e1de172c6.png)

## 2. git clone
作成したリポジトリのHTTPS URLをコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/52cfa245-91e3-459c-837c-80d84422ade8.png)

ローカルのターミナルの作業ディレクトリで以下のコマンドを実行してgit cloneします。
この時Passwordの入力を求められるので、「Clone Repository」ページに表示されたパスワードを入力します。(ここは環境に合わせて適宜読み替えてください)

```
git clone https://xxxxxxx@dev.azure.com/xxxxxxx/dhanda/_git/test-repo
```

実行結果は以下です。README.mdファイルの存在まで確認してます。

```
$ git clone https://xxxxxxx@dev.azure.com/xxxxxxx/dhanda/_git/test-repo
Cloning into 'test-repo'...
Password for 'https://xxxxxxx@dev.azure.com/xxxxxxx/dhanda/_git/test-repo':
remote: Azure Repos
remote: Found 3 objects to send. (26 ms)
Unpacking objects: 100% (3/3), 755 bytes | 755.00 KiB/s, done.

$ cd test-repo
$ ls -l
total 4
-rw-r--r-- 1 dhanda dhanda 985 Feb 13 15:51 README.md
```

## 3. ファイルの修正と直接Push

デフォルトで作成されているREADME.mdファイルにコメントを追加します。
(一番下から3行、行頭に+が付いているところが追加箇所です)

```
$ vi README.md
$ git diff
diff --git a/README.md b/README.md
index 0ca446a..a2f458f 100644
--- a/README.md
+++ b/README.md
@@ -17,4 +17,7 @@ TODO: Explain how other users and developers can contribute to make your code be
 If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
 - [ASP.NET Core](https://github.com/aspnet/Home)
 - [Visual Studio Code](https://github.com/Microsoft/vscode)
-- [Chakra Core](https://github.com/Microsoft/ChakraCore)
\ No newline at end of file
+- [Chakra Core](https://github.com/Microsoft/ChakraCore)^M
+^M
+# Add Comment^M
+コメントを追加^M
```

以下のコマンドでmainブランチに直接Pushします。

```
git add .
git commit -m "add comment"
git push origin main
```

実行結果は以下です。エラーなく正常終了しています。

```
$ git add .
$ git commit -m "add comment"
[main 7d6e3c5] add comment
 1 file changed, 4 insertions(+), 1 deletion(-)

$ git push origin main
Password for 'https://xxxxxxx@dev.azure.com/xxxxxxx/dhanda/_git/test-repo':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 309 bytes | 309.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Analyzing objects... (3/3) (72 ms)
remote: Validating commits... (1/1) done (0 ms)
remote: Storing packfile... done (30 ms)
remote: Storing index... done (33 ms)
To https://dev.azure.com/xxxxxxx/dhanda/_git/test-repo
   6f7af73..7d6e3c5  main -> main
```

Azure DevOpsのWebポータルからREADME.mdを確認すると、最下部にPushした内容が反映されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/6d250cb1-3993-42ec-a88a-3330245d8c1e.png)

## 4. 直接Pushの制限設定
特定ブランチへの直接Pushを制限するにはブランチポリシーを利用します。

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=browser#configure-branch-policies

ブランチポリシーはブランチ毎に設定が可能です。
### 4-1. ブランチポリシーの設定画面に遷移
以下の手順を実施してブランチポリシーの設定画面に遷移します。

1. Branchesでブランチポリシーを設定したいブランチを確認
1. ブランチ名右側のケバブメニュー(Kebab Menu)から`Branch policies`を選択

※ちなみに縦三点のメニューのことをケバブメニューと呼ぶのは初めて知りました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/e82f48d6-8b76-415f-bf3f-be67c27a8b73.png)

表示された画面には他にも設定項目はありますが、設定するのは一番上での「Branch Policies」項目のみです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/7a25a6b5-d15d-4887-a30e-a7545d8c6bf3.png)

ブランチポリシーの設定項目は以下4つがありますが、設定するのは一番上の`Require a minimum number of reviewers`のみです。

|Setting|日本語訳|説明|
|-|-|-|
|Require a minimum number of reviewers|最小レビュアー数を要求する|プルリクエストに対して、指定した数のレビュアーの承認を要求します。|
|Check for linked work items|リンクされた作業項目を確認|プルリクエストにリンクされた作業項目を確認することで、追跡可能性を促進します。|
|Check for comment resolution|コメントの解決を確認|プルリクエストで、すべてのコメントが解決されていることを確認します。|
|Limit merge types|マージタイプを制限|プルリクエストが完了した際に利用可能なマージの種類を制限することで、ブランチの履歴を管理します。|

`Require a minimum number of reviewers`ではレビュー時の最小人数を設定する項目で、指定された数のレビュー担当者がコードを承認しないとこのブランチに反映できないようにできます。

> ソフトウェア開発プロジェクトには、コード レビューが重要です。 チームが PR を確実にレビューして承認するために、最少数のレビュー担当者からの承認を要求できます。 基本ポリシーでは、指定された数のレビュー担当者が拒否なくコードを承認する必要があります。

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=browser#require-a-minimum-number-of-reviewers

### 4-2. `Require a minimum number of reviewers`を有効化
`Require a minimum number of reviewers`のトグルボタンを押下して、設定を有効化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/e28a0046-354d-4cc2-a2fa-5219547c5c1d.png)

詳細な設定項目として以下5つが表示されます。

|Setting|日本語訳|
|-|-|
|Minimum number of reviewers|最低限必要なレビュアーの人数|
|Allow requestors to approve their own changes|リクエスト者に自身の変更の承認を許可する|
|Prohibit the most recent pusher from approving their own changes|直近のプッシュを行った人に自身の変更の承認を禁止する|
|Allow completion even if some reviewers vote to wait or reject|一部のレビュアーが保留または却下に投票した場合でも、完了を許可する|
|When new changes are pushed|新しい変更がプッシュされた場合|

最低限必要なのは上2つの項目のみで、それぞれ次のように設定します。

* `Minimum number of reviewers`：1
* `Allow requestors to approve their own changes`：:white_check_mark:

最終的な設定内容は以下になります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/e83f3e72-8fbe-4ff7-afb0-2ccc74e014e1.png)

これで直接Pushの制限設定は完了です。

## 5. 直接Pushの制限確認
### 5-1. ローカルからの直接Pushの場合
先ほどと同じようにREADME.mdファイルをローカルで修正します。

```
$ vi README.md
$ git diff
diff --git a/README.md b/README.md
index a2f458f..4090e80 100644
--- a/README.md
+++ b/README.md
@@ -21,3 +21,6 @@ If you want to learn more about creating good readme files then refer the follow

 # Add Comment
 コメントを追加
+^M
+# Add Comment2^M
+2つ目のコメントを追加^M

```

同じようにgit pushを実施してみますが、`(TF402455: Pushes to this branch are not permitted; you must use a pull request to update this branch.)`というエラーでPushに失敗しました。
メッセージにもある通り、mainブランチの更新にはプルリクエストを利用する必要があります。

```
$ git add .
$ git commit -m "add comment2"
[main 7618cc2] add comment2
 1 file changed, 3 insertions(+)

$ git push origin main
Password for 'https://xxxxxxx@dev.azure.com/xxxxxxx/dhanda/_git/test-repo':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 320 bytes | 320.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Analyzing objects... (3/3) (84 ms)
remote: Validating commits... (1/1) done (100 ms)
remote: Storing packfile... done (29 ms)
remote: Storing index... done (36 ms)
To https://dev.azure.com/xxxxxxx/dhanda/_git/test-repo
 ! [remote rejected] main -> main (TF402455: Pushes to this branch are not permitted; you must use a pull request to update this branch.)
error: failed to push some refs to 'https://dev.azure.com/xxxxxxx/dhanda/_git/test-repo'
```

### 5-2. Webポータルからの直接編集の場合
Webポータルからは直接編集も可能なので、そちらからも試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/2586b354-fde2-4b85-a098-ce972535968d.png)

同じようにコメントを追加して、Commitボタンを押下します。
※Webポータルの場合はPushではなくCommitになります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/1e9e8046-c1b6-4486-a7d2-f99005976b83.png)

次の画面で再度Commitボタンを押下しましたが、先ほどと同じエラーが発生してCommitに失敗しました。
Webポータルからの直接編集のケースでも有効みたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/67548a05-9f41-4b96-80e2-39ca803d4e7b.png)

### 5.3. PR・マージの実施
add_commentブランチを新規で作成して、mainブランチへのプルリクエストを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/5db4cfaa-1b28-4ccf-97ef-c27bbc27e77f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/8ce7e9ae-1bda-4176-b8ae-f1ded5d0f24c.png)

同じユーザーで右上の"Approve"を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/bbcb455e-d446-46d5-9efc-24ce6d56e4f5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/1c6ce877-bbc0-4c1f-bd7d-716c6941cde3.png)

プルリクエストがマージされて、変更内容が反映されたことが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/846b3bf6-3eb5-4990-82b6-3f37e7019439.png)

## 余談
ブランチポリシーの`Allow requestors to approve their own changes`を設定しないとどうなるでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/9a1560dd-69aa-4453-ae45-7dd787139103.png)

ローカルからの直接Pushは同じように拒否されます。

```
$ vi README.md
$ git diff
diff --git a/README.md b/README.md
index 4090e80..12f5cb5 100644
--- a/README.md
+++ b/README.md
@@ -24,3 +24,6 @@ If you want to learn more about creating good readme files then refer the follow

 # Add Comment2
 2つ目のコメントを追加
+^M
+# Add Comment3^M
+3つ目のコメントを追加^M

$ git add .
$ git commit -m "add comment3"
[main 68dd7e6] add comment3
 1 file changed, 3 insertions(+)
$ git push origin main
Password for 'https://xxxxxxx@dev.azure.com/xxxxxxx/dhanda/_git/test-repo':
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 291 bytes | 291.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Analyzing objects... (3/3) (19 ms)
remote: Validating commits... (1/1) done (13 ms)
remote: Storing packfile... done (34 ms)
remote: Storing index... done (29 ms)
To https://dev.azure.com/xxxxxxx/dhanda/_git/test-repo
 ! [remote rejected] main -> main (TF402455: Pushes to this branch are not permitted; you must use a pull request to update this branch.)
error: failed to push some refs to 'https://dev.azure.com/xxxxxxx/dhanda/_git/test-repo'
```

Webポータルからの直接編集も同じように拒否されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/a9558a67-43f9-41c4-a5b9-9d71dab35ebc.png)

違いが出るのはプルリクエストの承認時になります。
先ほどと同じようにmainブランチへのプルリクエストを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/60ad1dc0-1e37-41b8-8f25-0bff1f1ab027.png)

同じように"Approve"を押下しますが、今度は自動でマージまでは行われませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440403/718f652e-9d7f-4b6d-9326-0e937d004f35.png)

理由は以下ドキュメントに記載されています。

> PR の作成者がその承認に投票できるようにするには、[要求者が自分の変更を承認することを許可する] を選択します。 選択しない場合、作成者は PR の [承認] に投票することはできますが、その投票はレビュー担当者の最少数にはカウントされません。

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/branch-policies?view=azure-devops&tabs=browser#require-a-minimum-number-of-reviewers

`Allow requestors to approve their own changes`のチェックを付けない場合、自身が作成したプルリクエストへの承認(Approve)操作をすること自体はできますが、レビュー担当者としてはカウントされません。
ブランチポリシーで設定した`Minimum number of reviewers`で1人のレビュー担当者の承認を条件としているため、マージが行われなかった、という理由になります。

本番フェーズであれば設定したほうが良さそうですが、開発フェーズの場合は基本的にチェックを付けても問題ないかと思います。

# おわりに
少しわかりづらいですが、設定自体はそこまで難しくないので、どう設定すればよいかわかっていれば今後は困らなそうです。

この記事がどなたかの参考になれば幸いです。