您可以使用 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。
点击 Update webhook。您应该看到响应体为 Well, it worked!。太好了!点击 Let me select individual events.,并选择以下内容
- 部署
- 部署状态
- 拉取请求
这些是 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 集成。