您可以使用 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
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 的一个版本来管理我们的部署。一个常见的流程与我们上面构建的服务器基本相同。
- 等待 CI 检查状态的响应(成功或失败)。
- 如果所需的检查成功,则合并拉取请求。
- Heaven 获取合并后的代码,并将其部署到暂存和生产服务器。
- 同时,Heaven 还会通过我们聊天室中的 Hubot 通知每个人有关构建的信息。
就是这样!您不需要构建自己的部署设置来使用此示例。您始终可以依赖 GitHub 集成。