您可以使用 REST API 将托管在 GitHub 上的项目部署到您自己的服务器上。有关管理部署和状态的端点的更多信息,请参阅“部署的 REST API 端点”。您还可以使用 REST API 在代码到达默认分支时协调您的部署。有关更多信息,请参阅“构建 CI 服务器”。
本指南将使用 REST API 演示您可以使用的设置。在我们的场景中,我们将
- 合并拉取请求。
- 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!
。太棒了!点击**让我选择单个事件**,然后选择以下内容
- 部署
- 部署状态
- 拉取请求
这些是 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
让我们分解一下正在发生的事情。start_deployment
创建了一个新的部署,它会触发deployment
事件。从那里,我们调用process_deployment
来模拟正在进行的工作。在该处理过程中,我们还调用create_deployment_status
,这使接收者能够了解正在发生的事情,因为我们将状态切换到pending
。
部署完成后,我们将状态设置为success
。
结论
在 GitHub,我们多年来一直使用Heaven的版本来管理我们的部署。常见的流程与我们上面构建的服务器基本相同
- 等待对 CI 检查状态的响应(成功或失败)
- 如果所需的检查成功,则合并拉取请求
- Heaven 获取合并后的代码,并将其部署到登台和生产服务器
- 同时,Heaven 还通过位于我们聊天室中的Hubot通知每个人有关构建的信息
就是这样!您无需构建自己的部署设置即可使用此示例。您始终可以依靠GitHub 集成。