Skip to main content

交付部署

使用部署 REST API,您可以构建与您的服务器和第三方应用程序交互的自定义工具。

你可以使用 REST API 将托管在 GitHub 上的项目部署到你拥有的服务器上。 有关用于管理部署和状态的终结点的详细信息,请参阅“适用于部署的 REST API 终结点”。 还可以使用 REST API 在代码登陆默认分支时协调部署。 有关详细信息,请参阅“构建 CI 服务器”。

本指南将使用 REST API 来演示你可以使用的设置。 在我们的场景中,我们将:

  • 合并拉取请求.
  • 在 CI 完成后,我们将相应地设置拉取请求的状态。
  • 合并拉取请求后,我们将在服务器上运行部署。

我们的 CI 系统和主机服务器将是我们想象中的虚拟物。 它们可能是 Heroku、Amazon 或其他完全不同的东西。 本指南的重点是设置和配置负责管理通信的服务器。

如果尚未下载,请务必下载 ngrok,并了解如何使用它。 我们发现它在将本地应用程序公开给 Internet 方面是一款非常有用的工具。

注意:或者,可以使用 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 以开始监听。

为了使此服务器正常工作,我们需要使用 web 挂钩来设置一个仓库。 Web 挂钩应配置为在创建或合并拉取请求时触发。

继续创建一个您可以自由支配的仓库。 我们可以推荐 @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 标头。 我们现在只关注拉取请求事件。 当合并拉取请求(其状态为 closedmergedtrue)时,我们将启动部署。

要测试此概念验证,请在测试存储库的分支中进行一些更改,打开拉取请求,然后合并它。 您的服务器应该会做出相应的响应!

处理部署

服务器已就位,代码在接受审查,拉取请求已合并,现在我们需要部署项目。

我们将首先修改事件侦听器,以便在拉取请求被合并时对其进行处理,并开始关注部署:

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

部署可以附加一些元数据,格式为 payloaddescription。 尽管这些值是可选的,但对用于记录和表示信息很有帮助。

创建新部署时,将触发完全独立的事件。 这就是为什么我们在 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 集成