チーム開発での github の protected branch と status check を使った安定的運用

# はじめに

以前は github で直接的に `git push --force` を禁止する方法はありませんでした。誤った force push によりコミットが失われる事故を防ぐため、[GitHub上の大事な中央ブランチをgit push --forceの恐怖から守るgit hookスクリプト](http://qiita.com/smd8122/items/7067cb34200302b5df12) のようなノウハウが用いられてきました。

最近では、protected branch の機能が導入・拡充され、通常の github の機能だけで安全に運用できるようになってきています。また、チームのリポジトリで時々必要となる、安定性のためにマージできる人を限定することもできるようになりました。

- [Protected branches and required status checks](https://github.com/blog/2051-protected-branches-and-required-status-checks)
- [Protected Branches Improvements](https://github.com/blog/2137-protected-branches-improvements)
- [Update to Protected Branches API Preview](https://developer.github.com/changes/2016-06-27-protected-branches-api-update/)

この記事では、これらの機能を使った設定例を紹介します。

# 目標とする状態

- 開発者はトピックブランチを push することができる
- トピックブランチを master にマージできるのはマージ権限を持つ人だけである
- CI によるテストとレビューにパスしないとマージできない
- master の最新(HEAD)に対する変更以外でもマージできるようにする
 - さらに強固にするには最新に対する変更以外はマージできないようにすることもできます。というかそちらがデフォルト。複数人が同時に作業しているリポジトリだと、ちょっと厳しい制限なので私のところではマージ可としています。

# team の設定

チーム開発ということで、Organization を使っている前提です。 

1. Organization に予め team を作っておく
2. 対象とするリポジトリに team のアクセス権を付与する (以下の図)

![teams.png](https://qiita-image-store.s3.amazonaws.com/0/114219/900f5979-77c4-bb66-8982-2201aa8324a9.png)

ここでは、

- developer: 開発者全員が属する team
- reviewer: レビューとマージ権限を持つメンバーが属する team

としています。両方とも Write 権限を付与しておきます。
Write 権限がないと、マージするためのトピックブランチを作ることもできなくなってしまいます。

# protected branch / status check の設定

statuses とは、コミット (commitid) に対してつけられる状態です。キー(context)として任意の文字列、値として pending/success/error/failure のいずれかの値を持ちます。status check を required にしておくことで、status が success にならなければマージできない、という設定にできます。

protected branch の設定は Web の設定画面から行ってもよいのですが、予め status check が行われていないと設定ができないという、ちょっと微妙な仕様なので、ここでは API で設定してみます。以下のような json file を用意しておいて、curl で [protected branch の API](https://developer.github.com/changes/2016-06-27-protected-branches-api-update/) を叩くだけです。

````protect.json 
{ 
  "required_status_checks": { 
    "include_admins": true, 
    "strict": false, 
    "contexts": [ 
      "test", 
      "review" 
    ] 
  }, 
  "restrictions": { 
    "users": [ 
    ], 
    "teams": [ 
      "reviewer" 
    ] 
  } 
}
````

- strict を true にすれば、最新に対する変更以外はマージできないようにできます。

````bash:sample.sh
$ curl -H "Accept: application/vnd.github.loki-preview+json" \
  -H "Authorization: token XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
  https://api.github.com/repos/:owner/:repo/branches/:branch/protection \
  -X PUT -d @protect.json
````

アクセストークンの取得については省略します。

- `:owner` github ユーザ名 or Organization 名
- `:repo` リポジトリ名
- `:branch` 設定対象のブランチ

設定画面を確認して、意図通りの設定になっていることを確認します。
これで設定はおわりです。

![protected_branch.png](https://qiita-image-store.s3.amazonaws.com/0/114219/4ca46acf-6677-c2bc-0058-705793c515e0.png)

# Pull Request

上記の設定をしたリポジトリに Pull Request を発行すると、以下の図のように require にした status が「Required」と表示され、「Merge pull request」ボタンは押せない状態になります。

![pullrequest.png](https://qiita-image-store.s3.amazonaws.com/0/114219/b404ac8c-49ce-49f2-bcaa-03cdf44b1705.png)

ここで、このふたつの status に success を設定してやれば、マージできるようになります。status の設定は API で行います。

````status.json
{
  "state": "success",
  "target_url": "https://example.com/build/status",
  "description": "OK",
  "context": "review"
}
````

target_url に指定した URL は Pull Request の画面からのリンクに使われます。Jenkins の結果などにしておくと便利です。

````bash:sample.sh
$ curl -H "Authorization: token XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
  https://api.github.com/repos/:owner/:repo/statuses/:sha \
  -X POST -d @status.json
````

- `:owner` github ユーザ名 or Organization 名
- `:repo` リポジトリ名
- `:sha` status をつける commitid

実際の運用では Jenkins や bot にこの API をたたかせることになります。例えば私のチームでは、Pull Request に「review pass」と書き込むと、bot が "review" status を success にするように設定しています。

無事に required な status が success になると、「Merge pull request」ボタンが押せるようになります。

![pullrequest_ok.png](https://qiita-image-store.s3.amazonaws.com/0/114219/3a91ea3f-526a-f747-f5bb-43194b4e3199.png)

# まとめ

- protected branch の設定によって、チーム開発で github のブランチを安定的に運用することができるようになりました。

# 追記 (2017/12/6)

Pull Request レビューについては GitHub 公式の機能として追加されましたのでこちらを使うほうがよいでしょう。
レビューで Approve されないとマージできない、といった設定もできます。
ドキュメント: https://help.github.com/articles/about-pull-request-reviews/