Protected Branches機能で柔軟なワークフローを構築する

2016年12月21日(水)
池田 尚史

さて前回の記事では、GitHub Flowのごくごく基本的な部分についておさらいしてみました。基本ということで、CIやデプロイについては触れず、レビュー後にすぐマージする形でお話をしました。

GitHub社が実際に実施しているGitHub Flowにおいては、レビュー後にすぐマージするのではなく、マージ前に当該Pull Requestブランチを本番環境にデプロイするのが基本的な流れになります。もちろん、本番環境以外にも複数のテスト用環境が用意されており、場合によって複数のテスト環境で動作を確認してから本番環境へデプロイする場合も多くあります。また、デプロイフローは自動化されており、ブランチを本番環境にデプロイする前に、自動的にCIが実行され、コードが正しく動作するかがチェックされます。デプロイ後に本番環境にて問題なく動作することを確認後、masterブランチにマージします。逆に動作に不審な点があった場合にはマージせず、代わりにmasterを本番環境にデプロイし直すことで巻き戻しを行います。この流れについて詳細はぜひこちらの記事をご覧ください。

Deploying branches to GitHub.com

弊社が実施しているフローは、弊社に最適化したフローです。もちろんほとんどの作業が自動化され1日に何百回とデプロイできるフローですから、自信を持っておすすめできるフローではあります。とは言え、このフローを実現するためには整備しなければならないことが大量にあり、いきなり実現できるものではありません。弊社もここに至るまでに数年を費やしていますし、今も継続的に改善している最中です。

ソフトウェア開発には様々な形態があり、目指すゴールやサイクルも違います。最適なワークフローもまた、組織によって、扱っているソフトウェアの種類によって、様々です。

GitHubには、様々なワークフローの構築を助けるような、種々の便利な機能が用意されています。今回はそのうちのいくつかを紹介したいと思います。

ブランチを保護する(Protected Branches)

ワークフローをどう構築するか考える際に、多くの組織でどうするべきか議論になるのがブランチの扱いです。例えばGitHub Flowで開発している場合に、Pull Requestを介さずにmasterブランチに直接Pushしてしまうと、レビューもされずテストもされないコードがmasterに入ってしまうことになります。これはGitHub Flowの大原則である"masterは常に本番環境にデプロイ可能な状態を保つ"というルールにも反します。前述の通り、本番環境の巻き戻しをする際にmasterをデプロイし直すことで担保することから考えても、絶対に侵してはならないルールの1つです。

このような問題を扱うために用意されたのが2015年の秋頃にGitHub.comに追加された"Protected Branches"という機能です。GitHub Enterpriseにはバージョン2.4から追加されています。

この機能は、様々な機能を内包しているのですが、シンプルに言うとまず下記の2つの好ましくない動作から当該ブランチを保護できます。

  • force-push
  • ブランチの削除

force-pushとはコマンドで言うとgit push -fのことです。これができてしまうと、例えばリモートブランチの歴史の書き換えのようなチーム開発の際に好ましくないオペレーションが実行できてしまいますが、Protected Branchesによってこれを完全に防止できます。またこの機能によって、masterブランチのような重要なブランチを削除されることも防げます。

リポジトリ毎に設定できるようになっており、次の図のようにSettingタブからBranchesメニューをクリックするとアクセスできます。

Screen Shot 2016-12-02 at 2.38.54 PM.png

この画面でデフォルトブランチの変更と、Protected Branchesの設定ができます。デフォルトブランチは特に変える必要なければmasterのままにしておきましょう。

Protected Branchesの設定において、まず保護したいブランチを"Choose a branch..."と書かれたプルダウンリストから選びます。ここではmasterを選びましょう。すると次のような画面になるので、"Protected this branch"というチェックボタンをチェックして、"Save changes"を押して保存しましょう。

Screen Shot 2016-12-02 at 2.57.14 PM.png

これがProtected Branchesの基本的な機能ですが、実は他にも付随機能があります。

レビューを必須とする

先程の画面の"Protect this branch"のチェックボックスの下に"Require pull request reviews before merging"というチェックボックスがあるのに気づいたでしょうか。

このチェックボックスをOnにすると、マージの前にレビューを通過することが必須条件になります。

ここでいう"レビューを通過する"というのは、今年秋に発表した新しいコードレビュー機能のことを指します。GitHub Enterpriseにおいては最新のバージョン2.8よりお使いいただけます。

この機能では、提案されたPull Requestに対してコメントを付けた後、レビュー全体の総評を"Comment"、"Approve"、"Request changes"の3種類のステータスによって提案者に知らせることができます。Protected Branchesにおいて、レビューを必須にする設定を行うと、レビュワーのうちの誰かがステータスを"Approve"にしない限りマージできなくなります。

※12月8日、新たにレビュワーを指定できる機能がGitHub.comに追加されました。この機能はあくまでレビュワーを指定できるのみであり、必ずしも指定したレビュワーがApproveしなくとも、誰かがApproveすればマージ可能になる点は従来通りですのでご注意ください。GitHub Enterpriseにはまだ搭載されていませんのでその点もご了承ください。

なおマージといいましたが、実質的には当該ブランチのPushそのものを保護していますので、仮にPull Requestを使わず、masterブランチに直接Pushしようとしても同様に拒否されます。言い換えると、必ずPull Requestを使うことと、必ずレビューをすることを強制することができます。

たとえば「Pull Requestを使わずに勝手にmasterにコードをPushされて困る」といった状況があるのであれば、この機能を使うことで簡単に防止できます。

なお"Include administrators"にチェックを入れると、当該リポジトリに対してAdmin権限を持っているユーザーに対しても、レビューなしのPushを禁ずることができます。

ステータスチェックを必須とする(Required status)

次にProtected Branchesにおいて利用できるのがRequired Statusの機能です。これは"Require status checks to pass before merging"をOnにすることで設定可能です。すると次のようにチェックボックス以下が展開されます。

Screen Shot 2016-12-02 at 5.46.01 PM.png

"Include administrators"は先程と同様にAdminであってもこのチェックを必須とするかどうかの設定です。

"Require branches to be up to date before merging"はデフォルトでOnになっていますが、これはPull RequestブランチがBaseブランチに対してUp to date(最新状態)になっていることを必須にするかどうかです。たとえばBaseブランチがmasterブランチであったとして、当該Pull Requestブランチをレビューしている間に、他のPull Requestがmasterにマージされたことで、masterブランチが当初より先に進んでしまったようなケースをどうするかの設定です。通常は、進んでしまったmasterの内容を当該ブランチにマージして、再度テストとレビューを実施します。そうでないと現在テスト/レビュー中の当該ブランチが間違いなく最新であることを担保できないためです。前述の通り、この設定はデフォルトではOnになっており、Onの場合はマージの際に次のような画面が出てきます。

Screen Shot 2016-12-02 at 5.54.06 PM.png

"This branch is out-of-date with the base branch"と表示され、"Merge pull request"ボタンが押せない状態になっているのが分かるでしょうか。この表示が出ている場合は、"Update branch"というボタンを押して、Baseブランチの内容を当該ブランチにマージしてからでないと、masterにマージできません(なお、状況によってはコンフリクトが起きるためにこのボタンが使えない場合もあります。その際にはローカルPC上で手動でマージを行う必要があります)。

"Require branches to be up to date before merging"をOffにすると、このチェックが必須でなくなります。通常はおすすめしませんが、ワークフローによっては別のフェーズで品質を担保するため、ここではチェックを掛けなくてよいというケースも存在します。そういう場合はこのチェックを外します(典型的には長期間存続するようなブランチ運用をされていて、さらに自動テストが長時間に渡るために、頻繁にUp to dateしていると何回も長時間のテストを実行する羽目になる、などのケースがあります)。

さて、最後が本丸のStatus checkになります。先程の画面をもう一度見てみましょう。

Screen Shot 2016-12-02 at 5.46.01 PM.png

"Sorry, we couldn't find any status checks..."と表示されているかと思います。Status checkを有効にするには、まずStatus APIを使って、当該ブランチのコミットに対してStatusのアップデートを最低一回行う必要があります。そうしないとこの画面上にリストアップされてきません。

次の画面が、一度実行して、リストアップされた状態です。

Screen Shot 2016-12-02 at 6.08.27 PM.png

ここでは4つリストアップされています。左端にあるチェックボックスをチェックすることで、各々のチェックを必須のものとするか、任意にするかを決められます。

必須にしたものは、必ずStatusがSuccessにならないとマージできなくなります。ここでは"CI-by-Jenkins"と"Manager Approval"が必須になっています。

"CI-by-Jenkins"は、その名前から分かる通り、ここではJenkinsによる自動テストを表してます。この名前はStatus APIによって自由に設定が可能です。Status checkで使えるCIサービスとしては他にも、TravisCI、CircleCIなど多数あります。

Jenkinsの設定方法についてはここでは詳しく述べませんが、Jenkinsのバージョン2系をお使いで、JenkinsFileをつかったPipeLineを組まれているのであれば、GitHub Organization Folder Pluginが設定も簡単でおすすめです。Jenkins2系のセットアップ時にデフォルトでインストールを指定できます。GitHub Enterpriseともよく動作します。Jenkins1系であれば、GitHub pull request builder pluginを使うのが一般的です。

TravisCIとCircleCIをGitHub.comと一緒にお使いの場合、Integrations Directoryから連携ボタンを押すことで簡単に連携を実現することも可能です。GitHub Enterpriseの場合は少々手作業が発生しますが、近い感覚で設定できます。

また、Status APIを使えば、独自のチェックを追加することも可能です。詳細はぜひこちらの実装ガイドをご覧ください。

なおこちらもレビューのときと同様に、チェックを通過しないとマージできないといいましたが、実質的には当該ブランチのPushそのものを保護していますので、仮にPull Requestを使わず、チェックを通過していないコードをmasterブランチに直接Pushしようとしても同様に拒否されます。

マージできる人/チームを制限する

最後がこの機能です。当該ブランチへのマージを特定の人およびチームに制限するという機能です。この機能は今年の3月頃GitHub.comに追加されました。GitHub Enterpriseにおいてはバージョン2.6より利用可能になっています。

この機能は個人リポジトリには設定できず、Organization配下のリポジトリのみ設定可能な機能になります。

Organization配下のリポジトリの場合、次の画面のようにチェックボックスが従来に加えて1つ追加されています。

Screen Shot 2016-12-02 at 5.37.22 PM.png

"Restrict who can push to this branch"にチェックを入れると次のように検索ダイアログが開きますので、権限を付与したい人を直接入力するか、付与したいチームを入力してください。

Screen Shot 2016-12-02 at 6.29.28 PM.png

この設定によって、マージの最終承認者を厳格に決めたい、と言ったニーズに応えることができます。

これも他と同様に、マージだけでなく実質的には当該ブランチのPushそのものを保護していますので、当該メンバーに含まれないユーザーはmasterへのPush自体できなくなります。

まとめ

さて、今回はワークフロー構築を助ける機能として、主にProtected Branchesというブランチを保護する機能を見てきました。

まとめると、次のようなことができるようになります。

  • Protected Branchesを設定することで:
    • force-pushとブランチ削除を防止できる
    • マージ前のレビューを必須にできる
    • マージ前のCI等によるチェックを必須にできる
    • マージできる人/チームを制限できる

これらをうまく組み合わせることで、不意に意図しないコードがmasterに混ざり込んでしまうことを防止でき、より安全を担保しながらGitHub Flowのような軽量なワークフローを実践することが可能になります。安全の担保度合いについては、組織やプロジェクトの性格に応じて、ゆるくすることも固く引き締めることも可能になっています。

Pull RequestをベースとしたGitHub Flowは、うまく運用すれば社内の開発者文化をよりオープンにし、自由闊達なコラボレーションを可能にします。まさにインナーソースの醸成に役に立ちます。一方で自由すぎて不安であるとの声があったのも事実で、今回紹介した機能をうまく使えれば、より安全を担保したい場合にも対応できます。

GitHub Flowをベースに、より皆さんのプロジェクトにあったワークフローを模索してみてください。

次回はPull Request単位でのRevertやビジュアルなBlame機能などについて触れたいと思います。

ソフトウェア開発者。大学卒業後、ITコンサルタントとしてキャリアをスタート。その後コンサルタントからプログラマーに転身し、パッケージソフトウェア開発、Webサービス開発を経て、2015年現在GitHubにてソリューションエンジニアとして働いている。著書に『チーム開発実践入門』(2014年 技術評論社)がある。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています