Skip to main content

交付部署

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

你可以使用 REST API 将托管在 GitHub Enterprise Cloud 上的项目部署到你拥有的服务器上。 有关用于管理部署和状态的终结点的详细信息,请参阅“适用于部署的 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 Enterprise Cloud 会将这些事件发送到我们的服务器。 我们将服务器配置为刚好在立即合并拉取请求时处理:

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 Enterprise Cloud 发送的每个事件都附有 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 集成