Azure Devopsで特定のブランチ（例：main）に対するマージを特定のブランチ(例：dev)からのみに限定する。

# 概要
ブランチ戦略にはいくつかパターンがあると思います。
例えば、本番環境を守るために、本番環境のブランチへのマージは特定のブランチに限定したいといったことが考えられます

今回Azure Devopsを使用する場合にどうそれを実現するか？をパイプラインを使用した一例で紹介します。

# 準備
実装にあたり、以下前準備を行います。
- Azure Devops上にリポジトリを準備します。今回は`main-branch-protection`とういリポジトリを作成します。
    - 今回はREADEMEだけの超絶シンプルなプロジェクトを作成するので、リポジトリ作成時に「Add a READEME」にチェックをいれておきます
- 今回は以下のようなシンプルな構成を想定します。そのため、`dev`ブランチを作成し、デフォルトのブランチに設定します。
![Frame 1 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/dd701ae6-779f-5c96-9f21-7f8c0a53ed35.png)
    - サイドメニューの「Repos」の中から、「Branches」を選択。
    - 「New branch」選択。
    - Nameに`dev`と入力し「create」。
    - 作成されたdevブランチの右端三点リーダをクリック。「Set as default branch」を選択し、デフォルトのブランチに設定。

# 実装
今回やりたいことを実現するためにやる手順は大きく以下の二通りです。
- mainブランチへのプルリクエストの際に走らせるPipelineの作成（このパイプラインでdev以外からのプルリクを禁止する設定をします。）
- 上記パイプラインを走らせるポリシーをmainブランチに設定。

### Pipelineの作成
- Azure Devopsのサイドメニューの「Pipelines」を選択。
- New Pipelineを選択。
- コードを選択
    - 今回は「Azure Repos Git」を使用。
    - 使用するリポジトリに先ほど作成した`main-branch-protection`を指定。
- パイプラインのテンプレート選択。なんでもよいので（あとで丸っと書き直す）、「Starter Pipeline」を選択。
- ymlファイル名をわかりやすいものに変更。今回は`main-code-merge-check.yml`とします。
- ymlファイルの中身を以下に変更。
```yml
steps:
  - script: '"Error: Code should be merged from dev to the main branch"'
    failOnStderr: true
    # スクリプトの表示名
    displayName: 'Check right Branch Script'
    # neは「以外」を表す。つまり、下はdevブランチ以外から、mainブランチにプルリクエストしようとしたときに弾く設定。
    condition: ne(variables['System.PullRequest.SourceBranch'], 'refs/heads/dev')
```
- 最後に「save」→ コミット。
- ルートディレクトリに`main-code-merge-check.yml`ができていることを確認。
- デフォルトではパイプライン名がリポジトリ名と同じ`main-branch-protection`となっているので、わかりやすいように名前を変更します。。
    - サイドメニューの「Pipelines」選択。
    - タブを「all」にし、先ほど作成したパイプラインを探す（もしくはブランチ名からフィルターする）
    - 右端の三点リーダーから「Renama/move」を選択。
    - 分かりやすいようにNameをリポジトリ名＋ymlファイル名に変更。以下の通りにする。`main-branch-protection-main-code-merge-check`。

これでパイプライン側の作成はOKです。

### mainブランチにポリシー設定
先ほど作成したパイプラインを走らせるポリシーを保護するブランチ（main）に設定します。
- サイドメニューの「Repos」の「Branches」を選択。
- mainブランチの右側三点マークから、「Branch Policies」を選択。
- 「Build Validation」の「＋マーク」をクリック。
- 「Build pipliene」に先ほど作成した`main-branch-protection-main-code-merge-check`を選択し、save。

これで、mainブランチへのプルリクエストの際
- devブランチなら許可。
- devブランチ以外なら拒否。
するようにし、mainブランチへのマージをdevブランチだけに限定するような設定を行えたはずです。

# 動作検証
実際に設定が上手くいっているか確認します。
### devブランチ以外から失敗を確認
- リポジトリをローカルにクローン。
- 任意のブランチ作成。ここでは`fix/readme`とします。変更内容はなんでも良いので、READEMEを以下に変更します。
```md
# ブランチ限定テスト
- ブランチ限定テストです
```
- `git push`実行し、プルリクエストを作成します。デフォルトでターゲットブランチはdevになっていますが、検証のため、以下のようにmainブランチに変更し、作成します。
![スクリーンショット 2023-02-12 143727.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/11a9eb80-5ce6-1537-e9a9-993fe20ce29d.png)
- すると以下のようにパイプラインが走り、失敗してマージができないことが確認できます。
![Group 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/75bed9a4-a57d-cd6f-0ff0-66143cf3888d.png)
- 参考までに以下、パイプラインの実行結果中身です。きちんと先ほど設定したものが走っていることが確認できます。
![Group 2 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/c08f5eca-2205-ab23-5f8f-25901174d729.png)


### devブランチからはmainブランチにマージできることを確認
- 一旦、先ほどの`fix/readme`ブランチをdevブランチにマージします。そのために以下手順を踏みます。
    - 先ほどの画面から右上の三点リーダーを選択。
    - 「Change target branch」を選択し、ターゲットブランチをdevブランチに変更し、マージ。
    - （この時、devブランチへのマージは特段問題ないので、エラーならずマージができるようになります。）
- dev→main用にマージするためのプルリクエスト作成
    - 左サイドメニューの「Repos」から、「Pull requests」を選択。
    - 「New pull requests」を選択。
    - 以下のようにターゲットブランチをdev->mainになるよう設定しcreate。（タイトルは任意で良い。）
    ![Group 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/e397c33e-a930-7f11-951f-9cb468bbe4d3.png)
    - すると先ほどと異なり、パイプラインが通過し、マージできるようになっています。
    ![Group 2 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883992/64b7c922-839e-3491-5427-ff33a10d0bc9.png)

以上問題なく、想定通りの挙動となり、mainブランチに対してはdevブランチからだけマージできるような設定を行えました。
是非、ご参考ください。