この REST API を使うと、GitHub でホストされているプロジェクトを、自分が所有するサーバーに配置できます。 デプロイと状態を管理するエンドポイントの詳細については、「デプロイ用の REST API エンドポイント」を参照してください。 また、この REST API を使うと、コードが既定のブランチに配置されたときに配置を調整できます。 詳しくは、「CIサーバーの構築」を参照してください。
このガイドでは、REST API を使って、利用できる設定を示します。 このシナリオでは、以下を行います。
- Pull Requestをマージします。
- CIが終了したら、それに応じてプルリクエストのステータスを設定します。
- プルリクエストがマージされたら、サーバーでデプロイメントを実行します。
このCIシステムとホストサーバーは、想像上のものです。 Heroku でも、Amazon でも、何でも構いません。 このガイドのポイントは、通信を管理するサーバーを設定し、構成することにあります。
まだ行っていない場合は必ず ngrok
をダウンロードし、その使い方をご確認ください。 これは、ローカル アプリケーションをインターネットに公開するために非常に便利なツールであることがわかりました。
メモ: または、Webhook 転送を使って、Webhook を受信するようにローカル環境を設定することもできます。 詳しくは、「GitHub CLI を使用して Webhook をテスト用に転送する」を参照してください。
注: このプロジェクトの完全なソース コードは、platform-samples リポジトリからダウンロードできます。
サーバーを書く
ローカル接続が機能していることを証明するための、簡単なSinatraアプリケーションを書きます。 まずは以下のソースから始めましょう。
require 'sinatra'
require 'json'
post '/event_handler' do
payload = JSON.parse(params[:payload])
"Well, it worked!"
end
(Sinatra のしくみに詳しくない場合は、Sinatra ガイドを読むことをお勧めします。)
このサーバーを起動してください。 既定では、Sinatra はポート 4567
で起動するため、これのリッスンも開始するよう ngrok
を構成します。
このサーバーが機能するには、webhookでリポジトリを設定する必要があります。 プルリクエストが作成やマージされるたびに、webhookが起動するよう設定すべきです。
なんでも好きにして構わないようなリポジトリを作成しましょう。 @octocat の Spoon/Knife リポジトリなどはどうでしょうか。
その後、お使いのリポジトリ内に新しい Webhook を作成し、ngrok
で提供された URL を指定し、コンテンツ タイプとして application/x-www-form-urlencoded
を選びます。
[Webhook の更新] をクリックします。 Well, it worked!
という本文の応答が表示されます。
すばらしい。 [個々のイベントの選択] をクリックして、次のイベントを選択します。
- デプロイ
- [デプロイ ステータス]
- Pull Request
これらは、関係するアクションが発生するたびに、GitHub によってサーバーに送信されるイベントです。 ここではプルリクエストがマージされたときに_だけ_処理するようにサーバーを設定します。
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 によって送信されるすべてのイベントには、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
ケースがあります。 この情報を使用して、デプロイメントがトリガーされたときに通知を受け取ることができます。
デプロイメントにはかなり時間がかかる場合があるため、さまざまなイベント (デプロイメントがいつ作成されたか、デプロイメントの状態など) をリッスンしたいと思います。
何らかの作業が行われるデプロイメントをシミュレートして、出力に対する影響を確認してみましょう。 まず、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
ここの処理を細かく説明しましょう。 deployment
イベントをトリガーする start_deployment
によって、新しいデプロイが作成されます。 そこから、process_deployment
を呼び出して、進行中の作業をシミュレートします。 この処理中に create_deployment_status
も呼び出して、状態を pending
に切り替えることで、受信者に状況を通知します。
デプロイメントが完了したら、状態を success
に設定します。
まとめ
GitHub では長年、デプロイを管理するためにあるバージョンの Heaven を使用してきました。 一般的なフローは、上記で構築したサーバーと本質的に同じです。
- CIチェックのステータスに対する応答(成功もしくは失敗)を待つ
- 必要なチェックが成功していれば、Pull Requestをマージする
- Heavenはマージされたコードを取り込み、ステージング及びプロダクションサーバーにデプロイする
- その間に Heaven では、当社のチャット ルームにいる Hubot を通じて全員にビルドについて通知する
これで完了です。 この例を使用するために、独自のデプロイメントを構築する必要はありません。 いつでも GitHub 統合を使用することができます。