Deployment APIは、GitHub Enterprise Serverにホストされたプロジェクトが、あなたのサーバーで起動できるようにします。 Combined with the Status API, you'll be able to coordinate your deployments the moment your code lands on the default branch.
このAPIでは、ステータスAPIを使って、利用できる設定を示します。 このシナリオでは、以下を行います。
- ププルリクエストをマージします。
- CIが終了したら、それに応じてプルリクエストのステータスを設定します。
- プルリクエストがマージされたら、サーバーでデプロイメントを実行します。
このCIシステムとホストサーバーは、想像上のものです。 Herokuでも、Amazonでも、何でも構いません。 このガイドのポイントは、通信を管理するサーバーを設定し、構成することにあります。
まだngrokをダウンロードしていない場合はダウンロードし、その使いかたを学びましょう。 これはローカル接続を公開するために非常に役立つツールだと思います。
注釈: このプロジェクトの完全なソースコードは、platform-samplesリポジトリからダウンロードできます。
サーバーを書く
ローカル接続が機能していることを証明するための、簡単なSinatraアプリケーションを書きます。 まずは以下のソースから始めましょう。
require 'sinatra'
require 'json'
post '/event_handler' do
payload = JSON.parse(params[:payload])
"Well, it worked!"
end
(シナトラの仕組みに詳しくない方は、Sinatraのガイドを読むことをお勧めします。)
このサーバーを起動してください。 デフォルトでは、Sinatraはポート4567
で起動するため、このポートもリッスンを開始するようngrokを設定するとよいでしょう。
このサーバーが機能するには、webhookでリポジトリを設定する必要があります。 プルリクエストが作成やマージされるたびに、webhookが起動するよう設定すべきです。 なんでも好きにして構わないようなリポジトリを作成しましょう。 @octocat's Spoon/Knifeリポジトリなどはどうでしょうか。 その後、リポジトリ内に新しいwebhookを作成し、ngrokが提供したURLを指定し、コンテンツタイプとしてapplication/x-www-form-urlencoded
を選択します。
Update webhook(webhookの更新)をクリックしてください。 本文にWell, it worked!
というレスポンスが表示されるはずです。 これでうまくいきました。 [Let me select individual events]をクリックし、以下を選択します。
- デプロイメント
- デプロイメントステータス
- プルリクエスト
これらは、関係するアクションが発生するごとにGitHub Enterprise Serverがこのサーバーに送信するイベントです。 ここではプルリクエストがマージされたときの処理だけを処理するようサーバーを設定します。
post '/event_handler' do
@payload = JSON.parse(params[:payload])
case request.env['HTTP_X_GITHUB_EVENT']
when "pull_request"
if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
puts "A pull request was merged! A deployment should start now..."
end
end
end
さて、ここで起こっていることを説明しましょう。 GitHub Enterprise Serverが送信するすべてのイベントには、X-GitHub-Event
HTTPヘッダが添付されています。 ここではPRイベントのみに注目しましょう。 プルリクエストがマージされると (ステータスがclosed
となり、merged
がtrue
になると)、デプロイメントを開始します。
この概念実証を試すため、テストリポジトリのブランチで何か変更を行い、プルリクエストを開いてマージします。 そうすると、サーバーはそれに応じてレスポンスを返すはずです。
デプロイメントを扱う
サーバーの準備が整い、コードがレビューされ、プルリクエストがマージされたので、プロジェクトをデプロイしたいと思います。
まず、イベントリスナーを修正し、マージされたときにプルリクエストを処理して、デプロイメントの待機を開始することから始めましょう。
when "pull_request"
if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
start_deployment(@payload["pull_request"])
end
when "deployment"
process_deployment(@payload)
when "deployment_status"
update_deployment_status
end
プルリクエストからの情報に基づき、start_deployment
メソッドを書き込むことから始めます。
def start_deployment(pull_request)
user = pull_request['user']['login']
payload = JSON.generate(:environment => 'production', :deploy_user => user)
@client.create_deployment(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], {:payload => payload, :description => "Deploying my sweet branch"})
end
デプロイメントには、payload
およびdescription
の形式で、一部のメタデータを添付できます。 これらの値はオプションですが、ログの記録や情報の表示に役立ちます。
新しいデプロイメントが作成されると、まったく別のイベントがトリガーされます。 ですから、deployment
のために、イベントハンドラーのswitch
に新たなcaseを用意します。 この情報を使用して、デプロイメントがトリガーされたときに通知を受け取ることができます。
デプロイメントにはかなり時間がかかる場合があるため、デプロイメントがいつ作成されたか、デプロイメントのステータスなどのさまざまなイベントをリッスンしたいと思います。
何かの作業をするデプロイメントをシミュレートし、その影響を出力として通知しましょう。 まず、process_deployment
メソッドを完成させます。
def process_deployment
payload = JSON.parse(@payload['payload'])
# you can send this information to your chat room, monitor, pager, etc.
puts "Processing '#{@payload['description']}' for #{payload['deploy_user']} to #{payload['environment']}"
sleep 2 # simulate work
@client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'pending')
sleep 2 # simulate work
@client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'success')
end
最後に、ステータス情報の保存をコンソールの出力としてシミュレートします。
def update_deployment_status
puts "Deployment status for #{@payload['id']} is #{@payload['state']}"
end
ここの処理を細かく説明しましょう。 新しいデプロイメントがstart_deployment
により作成され、それがdeployment
イベントをトリガーします。 そこからprocess_deployment
を呼び出して、実行中の作業をシミュレートします。 この処理の間にcreate_deployment_status
も呼び出し、ステータスをpending
に切り替えることで受信側に状態を通知します。
デプロイメントが完了後、ステータスをsuccess
に設定します。
おわりに
GitHubでは長年、デプロイメントを管理するためHeavenの特定のバージョンを使用してきました。 その基本的なフローは、上記で構築してきたサーバーと本質的にまったく同じです。 GitHubでは、以下を実行しています。
- CIのステータスについてレスポンスを待つ
- コードが緑色なら、プルリクエストにマージする
- Heavenはマージされたコードを取り込み、本番サーバーとステージングサーバーにデプロイする
- その間にHeavenは、当社のチャットルームに居座っているHubotを通じて全員にビルドについて通知する
これで完了です。 この例を使用するために、独自のデプロイメントを構築する必要はありません。 いつでもGitHubインテグレーションに頼ることができます。