GitHub + Jenkins で、全てのプルリクエストに対してレビューとテストを必須にする

More than 1 year has passed since last update.

最近関わった某OSSの真似をして、GitHubプロジェクトでプルリクエスト(PR)のレビューと自動テストを必須にしたいと思い、ドキュメントを適当にググりながら設定してたのですが、書いてる内容が古かったりしてやたらこんがらがり、1日仕事になってしまいました。

ということで、自分はこう設定したというメモを残します。まぁこれもほっとくとすぐ古くなりそうですが。

やりたいことは、以下の通り。


  • masterブランチPR発行時、然るべき人にレビューのリクエストが飛ぶようにする

  • 同時に、Jenkinsでテストも実行される

  • レビューとテスト、両方が通らないとmasterにマージできないようにする


まずはレビューを必須に

レビューを必須にするのは、GitHubだけで設定可能です。以下説明。

GitHubプロジェクトを作るところは省略します。ここでは、bonotake/rvtest なるリポジトリをこさえたとします。


コードオーナーを設定する

現在のGitHubは、プロジェクト内のコードのそれぞれにオーナーを設定できます。これは、CODEOWNERSなるファイルを所定の場所(以下の例では.github以下)に置くことで有効になります。

詳しくは公式ドキュメント(英語)を参考にしてもらうとして、ここではこんな感じでファイルを書きます。こう書くと、リポジトリ内の全てのコードのオーナーにGitHubアカウントbonotakeが設定されます。ここには個人アカウントのほか、Organizationアカウントの場合はその中で作ったチームも指定できます。

スクリーンショット 2017-11-23 18.27.09.png

そして、このファイルをmasterブランチにコミットしてプッシュ。

その他、一揃い必要な(PRする必要のない)ファイルは一緒に、今のうちにプッシュしときます。

以降の設定すると、masterへの直接プッシュは(設定解除しない限り)基本できなくなるので。


masterブランチをプロテクトする

GitHubの、プロジェクトのページに飛んで、"Settings"をクリック。

左側のメニューから”Branches"を選びます。

Protected Branchesなる欄があるので、そこの”Choose a branch..."でmasterを選択。

"Branch protection for master"なるページに遷移するので、そこで、


  1. "Protect this branch" にチェック

  2. サブアイテム"Require pull request reviews before merging"にチェック

  3. さらにその下の"Require review from Code Owners"にチェック

  4. とりあえず画面下の"Save changes"をクリック

これで、masterブランチに対するPRが発行されると、コードオーナーにレビューがリクエストされて、そのオーナーのうちの誰かがレビュー・承認しない限り、PRをマージできなくなります。

試しにPRしてみると、こんな感じ。

スクリーンショット 2017-11-23 19.02.19.png

"Code owner review required"となって、bonotake にレビューがリクエストされます。

ついでにいうと、このとき bonotake にはリクエストメールが飛びます。

でbonotakeがこのPRのページを開くと、右上に"Add your review"のボタンが。

スクリーンショット 2017-11-23 19.06.30.png

押すと、レビューが入力できるウィンドウが現れます。

スクリーンショット 2017-11-23 19.08.37.png

コメントを入れられるフォームと、その下に3つの選択肢が出ますが、



  • Comment: とりあえずコメント


  • Approve: PR承認


  • Request changes: PR却下

です。

ここのフォームにコメント入力すると全体へのコメントになりますけど、コードの行ごとにコメント入れることもできます。

で、承認すると、こんな感じでマージ可能に。

スクリーンショット 2017-11-23 19.15.56.png


自動テストも必須に

こっからはJenkinsも使います。Jenkinsサーバそのもののセットアップは省略。


GitHub側:botアカウントの作成

JenkinsがアクセスするためのbotアカウントをGitHubに作成します。

そして、プロジェクトの"Settings"から、"Collaborators"メニューを選択、作ったbotアカウントをcollaboratorに追加します。

botアカウントにinvitationメールが送られるので、そっち側でログインし直してaccept、ログアウトして、元のアカウントでログインし直します。


GitHub側:Webhookの設定

"Settings"から、"Webhooks"メニューを選択。

"Add webhook"ボタンを押して、遷移してで出てきたフォームに入力していきます。

スクリーンショット 2017-11-23 23.30.48.png



  • Payload URL: "http://[Jenkinsサーバ]/ghprbhook/"を入力


  • Which event would you like to trigger this webhook?: "Let me select individual events."を選択

さらに、その下に出てくる項目で、


  • "Pull request"にチェック

  • "Push"のチェックを外す

とします。

スクリーンショット 2017-11-23 19.32.21.png

最後に、”Add webhook"ボタンを押します。

ここで注意なんですが、Jenkinsサーバにアカウントを設定して、ログインしないと各種操作ができない設定にしている場合、上記の設定をしてもダメな場合があります。

そのときは、最初に入れるURLを"http://[ユーザID]:[パスワード]@[Jenkinsサーバ]/ghprbhook/"とするとうまくいきました。

セキュリティ的にどうなの、という気がしなくもないですが、これ以外のよりセキュアな方法が見つからず…… ありました。

上では空欄にしてたんですが、Secretの欄に、適当な文字列を入れてください。長めのランダムな文字列が良いと思います。

この文字列を「合言葉」として、Jenkins側にも設定すれば、ユーザIDとパスワードをURLに直書きする必要はないです。

あと、パスワードの代わりにトークンを用いる方法もあるようです。詳しくはこちらの記事を参照。


Jenkins側:botアカウントの設定

今度はJenkinsの設定。

あらかじめ、Git plugin, GitHub plugin, GitHub Pull Request Builder plugin の3種はインストールしておきます。(「Jenkinsの管理」→「プラグインの管理」からできます。)

そして、さっき作ったbotアカウントをJenkins側に設定します。

「Jenkinsの設定」→「システムの設定」を選択し、遷移した画面の「GitHub Pull Request Builder」欄で



  • GitHub Server API URL: "https://api.github.com" と入力


  • Credentials:


    1. 「追加」ボタン→「Jenkins」をクリック

    2. 現れたダイアログの「ユーザー名」「パスワード」に、さっき作ったbotのアカウント名とパスワードを入力、「追加」ボタンをクリック

    3. プルダウンで、botアカウントが選べるようになるので、選択。

    4. 「Test Credentials...」で、GitHubに接続できるか試せます。ここは省略しますが、一通り試しといたほうがいいでしょう。




  • Description: なんか区別のつく文字列を。ここでは"bot"とだけ入力。

[追記]さっきGitHubのWebhookで"Secret"に文字列を入れた場合、Credentialsの欄、"Shared Secret"のところに同じ文字列を入力します。[追記終わり]

以上やって、「保存」ボタンをクリック。


Jenkins側:ジョブの設定

続いて、ジョブを作ります。とりあえず今回は「フリースタイル・プロジェクトのビルド」でジョブを作成。

ジョブの設定画面で、


  • GitHub projectにチェック



  • 「ソースコード管理」で"Git"を選択


    • 「リポジトリURL」欄に、リポジトリのURL(https://github.com/bonotake/rvtest.git とか)を入力

    • 「高度な設定…」ボタンを押して、「Refspec」欄に "+refs/pull/*:refs/remotes/origin/pr/*" と入力

    • 「ビルドするブランチ」欄を空欄にここに${ghprbSourceBranch}(PRのソースになってるブランチを指定)と入れます。本来は${sha1}(PRを指定)と入れれば良いはずなんですが、僕の環境ではどうしてもうまく動きませんでした。



  • 「ビルド・トリガ」で"GitHub Pull Request Builder"にチェック


    • GitHub API Credentials に "https://api.github.com:bot" (コロン以降はさっきの"Description"に入れた文字列)を選択

    • 更に、"Use github hooks for build triggering" にチェック。ここにチェック入れなかった場合は、cronでJenkins側から定期的にポーリングされます。さっきのGitHub側のWebhookが要らなくなる代わり、Jenkins側の負荷が多少増えるし、PR発行とJenkins起動にタイムラグが発生する場合があるので、ここではチェックを入れます。

    • 「高度な設定」を開き、"White list" にPRしそうなGitHubユーザのアカウント名をあらかじめ入力。ここで指定した人からのPRでのみJenkinsが走ります。

    • → あるいは、"List of organizations. Their members will be whitelisted"にOrganizationアカウントを指定すれば、そのOrganisationに所属する任意のユーザのPRからJenkinsが起動します。

    • → あるいは、"Build every pull request automatically without asking (Dangerous!)."にチェックを入れれば、どんな人のPRからでもJenkinsが走るように。ただし、悪意あるユーザが変なPRをすれば、そのコードでJenkinsサーバを自由に動かせてしまうので、要注意。

    • 同じく「高度な設定」、"Whitelist Target Branches:"に master と入力。これで、masterブランチに対するPRだけがJenkinsビルドの対象になります。



  • 「ビルド」で「ビルド手順の追加」をクリック、「シェルの実行」を選択


    • ここに、具体的に実行するテストスクリプトを入力します。詳細は割愛。各人ご自由に。



以上終わったら、「保存」をクリック。


GitHub側:実際にPRをしてみる

実はまだ設定終わってないんですけど、いっぺんPRをやってみないと先に進めないという、若干バッドノウハウ感の漂う手順が存在するので、とりあえずPRします。内容はとりあえずなんでもいいです。

うまくいくと、このPRでJenkinsが起動して、ステータスがPRの画面に表示されます。

スクリーンショット 2017-11-23 22.35.09.png

こんな感じで、×がついてる(テスト失敗)んですが、その結果は関係なく、レビューが通っていればマージできる状態。

これを、Jenkinsのビルドが失敗すればマージできない状態にします。


GitHub側:テストを必須に

"Settings"から"Branches"メニューを開き、"Protected branches"のところ、先にmasterが選択されてますが、その右横のEditボタンを押します。

ここで、さっきチェックしなかったRequire status checks to pass before mergingにチェック。

"Status checks found in the last week for this repository"のところ、"default"という選択肢が出てますが、これがJenkinsのビルドによるチェックです。この欄が、一回Jenkinsを起動させないと現れないので、まず先にPRをしてみる必要があるのでした。

ということで、この"default"にチェック。右横に"Required"の文字が現れます。

スクリーンショット 2017-11-23 23.11.13.png

画面下の"Save changes"ボタンを押し、先程のPRに戻ってみると、表示がちょっと変わっています。

スクリーンショット 2017-11-23 23.16.49.png

Administratorならマージできるんですが、基本はダメよ状態に。

ここで、ちゃんとテストの通るコミットをしなおしてやると、

スクリーンショット 2017-11-23 23.24.01.png

無事マージできるようになりましたとさ。

[追記]

でも、テストやってるのに表示が"default"って何だかわからないし、テスト失敗してるのに"Finished."って出るのも何だか変だし……と思われるかもしれません。

その場合、ジョブの設定で「ビルド・トリガ」欄の"GitHub Pull Request Builder"以下にある"Trigger Setup..."ボタンを押して、出て来る入力フォームに以下の要領で入力してください。



  • Commit Status Context: "default"の代わりに表示するジョブの名称


  • Commit Status Build Result: ジョブの実行結果(SUCCESS/ERROR/FAILURE)のそれぞれに応じて表示するメッセージ(「追加」ボタンで各メッセージを追加できます)

例えばこんな感じ。

スクリーンショット 2017-11-24 11.04.59.png

ジョブの名称を"test"にして、あと、SUCCESS/ERROR/FAILUREそれぞれにメッセージを変えて入れてます。

で、これを設定後、改めて適当にPRをし(汗)、Require status checks... のところの設定をやり直してください("default"のチェックを外し、"test"にチェックを入れる)。

ジョブごとに名称やメッセージを変えられるので、複数のジョブを定義して、複数のテストを並列実行させ、それぞれの成功を必須にする、なんてこともできます。

注意してほしいのですが、この設定はジョブ単位でやってください。システム単位でもできますが(「Jenkinsの管理」→「システムの設定」)、まともに動作しないので触らない方が無難です。


参考記事とか