跳至主要内容

交付部署

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

您可以使用 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,并且 mergedtrue)时,我们将启动部署。

为了测试这个概念验证,请在测试仓库的分支中进行一些更改,打开一个拉取请求并将其合并。您的服务器应该做出相应的响应!

使用部署

我们的服务器已就位,代码正在审查,我们的拉取请求已合并,我们希望我们的项目能够部署。

我们将首先修改我们的事件监听器,以便在拉取请求合并时处理它们,并开始关注部署。

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 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 集成