GitHub のブランチ保護を使用してリスキーなマージを防止する

せっかく Github を使用して、プルリクエストをレビュー、指摘しても、権限設定の問題で、権限さえあれば [Merge pull request] ボタンを押せてしまう。
結果、マージ先でテストを失敗したり、コード品質落ちてしまったり。
あるあるですね。

今回は、Github のブランチ保護機能を使用して、このようなリスキーなマージを機械的に防げるよう、設定をしてみます。

# 環境

以前、[SonarQube と TravisCI を使って Github でのコードレビューを自動化する](http://qiita.com/yo1000/items/740f0f775a5ba3753d5f) で使用した環境を流用します。

- Github (2016-02-15 現在)
- Travis CI (2016-02-15 現在)
- Maven 3.2.5
- ORACLE Java 1.8.0_31

```
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=192m; support was removed in 8.0
Apache Maven 3.2.5 (12a6b3acb947671f09b81f49094c53f426d8cea1; 2014-12-14T17:29:23+00:00)
Maven home: /usr/local/maven
Java version: 1.8.0_31, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-8-oracle/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.13.0-40-generic", arch: "amd64", family: "unix"
```

# 事前準備

まず、事前に保護したいブランチを Github 上で設定していきます。
Github 上で、プロジェクト > [Settings] と進みます。

![スクリーンショット 2016-02-15 16.48.00.png](https://qiita-image-store.s3.amazonaws.com/0/47993/8a94a3f6-f366-3e78-dc03-34ca7478edce.png)

サイドバーから、[Branches] へ進みます。

![スクリーンショット 2016-02-15 15.47.22.png](https://qiita-image-store.s3.amazonaws.com/0/47993/e90612b3-c3b5-c3ef-c9a5-ffec61979c4f.png)

## Protected branches を選択

保護したいブランチを選択します。
今回は "master" を選択します。

![スクリーンショット 2016-02-15 15.47.46.png](https://qiita-image-store.s3.amazonaws.com/0/47993/56abb0c0-c27c-51ac-7bea-3f5cf5189960.png)

## Branch protection for を設定

以下3つの設定にチェックを入れます。

- Protect this branch
- Require status checks to pass before merging
- Include administrators

加えて、[Status checks found in the last week for this repository] の中から、ステータス チェック プロセスを選択します。
ここで選択された、ステータス チェックをクリアしないと、プルリクエスト上で [Merge pull request] ボタンが有効にならなくなります。

![スクリーンショット 2016-02-15 15.48.13.png](https://qiita-image-store.s3.amazonaws.com/0/47993/c79c5717-4a44-d76c-7da7-6826dfac11ad.png)

なお、ここで選択できるものが何もない場合は、一度ビルドをかけるなどして、対象リポジトリへ、ステータスの更新をしてみると良いです。
ここには 直近1週間のうちに、ステータスを更新しにきたプロセスの一覧が表示されるようです。

# ビルド構成

引き続き、ビルドを構成。
TravisCI を使用するので、プロジェクトルートに `.travis.yml` を作成、配置します。
以下のようなファイルを作成します。

```yaml:.travis.yml
language: java
jdk:
  - oraclejdk8
```

# 動作確認

それでは、実際にプルリクエストを出してみます。
今回は Spock を使用してテストを書いてみました。
https://github.com/yo1000/fizzbuzz/pull/3

```groovy:FizzBuzzTest.groovy
import spock.lang.Specification
import spock.lang.Unroll

/**
 * Created by yoichi.kikuchi on 2016/02/15.
 */
@Unroll
class FizzBuzzTest extends Specification {
    def "fizz - number が #number の場合 '#expect'"() {
        def fizzBuzz = new FizzBuzz()

        expect:
        fizzBuzz.fizz(number) == expect

        where:

        number || expect
        0      || 'fizz'
        1      || ''
        2      || ''
        3      || 'fizz'
        6      || 'fizz'
        -1     || ''
        -3     || 'fizz'
    }
}
```

テストをプッシュしてみると、TravisCI がビルドを始めました。
ビルドが終わるまで、ステータスは "pending" となり、[Merge pull request] がクリックできなくなります。

<img width="773" alt="スクリーンショット 2016-02-15 15.06.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/47993/41167c1d-abb2-9bf6-030b-fbed9d64e3b3.png">

今回プッシュしたテストは成功したため、ビルド後、無事に [Merge pull request] が有効化されました。

<img width="775" alt="スクリーンショット 2016-02-15 15.18.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/47993/fd17ddf0-3f28-a8e8-1d08-efe333d970c2.png">

ここで、テストを壊してみます。
`where` ブロックの最後を、`-2` に変更してみます。

```groovy:FizzBuzzTest.groovy
import spock.lang.Specification
import spock.lang.Unroll

/**
 * Created by yoichi.kikuchi on 2016/02/15.
 */
@Unroll
class FizzBuzzTest extends Specification {
    def "fizz - number が #number の場合 '#expect'"() {
        def fizzBuzz = new FizzBuzz()

        expect:
        fizzBuzz.fizz(number) == expect

        where:

        number || expect
        0      || 'fizz'
        1      || ''
        2      || ''
        3      || 'fizz'
        6      || 'fizz'
        -1     || ''
        -2     || 'fizz'
    }
}
```

TravisCI では、狙い通り "BUILD FAILURE" になりました。

<img width="795" alt="スクリーンショット 2016-02-15 15.44.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/47993/b4a95a70-de8b-f6b0-4e76-5d7c7a29bfa6.png">

Github にもテスト失敗が連携されました。
TravisCI アイコンの横に :x: がマークされ、[Merge pull request] ボタンも再び無効化されました。
よさそうですね！

![スクリーンショット 2016-02-15 15.45.49.png](https://qiita-image-store.s3.amazonaws.com/0/47993/dde0b6ba-1fd0-cc10-12f9-6e08d76d170a.png)

こんな具合で、リスキーなコードのマージを防止することができるようになります。

[Branch protection for を設定](#branch-protection-for-を設定) で紹介した、[Status checks found in the last week for this repository] の設定で、"sonarqube" を選択すると、静的解析により課題が出ていないことを確認できるまで、マージさせないようにしたりもでき、コード品質の低下したブランチの取り込みを拒否するようなことも可能です。

今回使用したブランチのステータスは、API を直接キックすることでも設定できるので、独自のマージ可否ルールを設けて、いろいろとカスタマイズすることもできそうです。
https://developer.github.com/v3/repos/statuses/

べんり！