{"meta":{"title":"执行部署","intro":"使用部署 REST API，您可以构建与您的服务器和第三方应用程序交互的自定义工具。","product":"REST API","breadcrumbs":[{"href":"/zh/rest","title":"REST API"},{"href":"/zh/rest/guides","title":"指南"},{"href":"/zh/rest/guides/delivering-deployments","title":"执行部署"}],"documentType":"article"},"body":"# 执行部署\n\n使用部署 REST API，您可以构建与您的服务器和第三方应用程序交互的自定义工具。\n\n您可以使用 REST API 将托管在 GitHub 上的项目部署到您自己的服务器上。 有关用于管理部署和状态的终结点的详细信息，请参阅 [适用于部署的 REST API 终结点](/zh/rest/deployments)。 还可以使用 REST API 在代码登陆默认分支时协调部署。 有关详细信息，请参阅“[构建 CI 服务器](/zh/rest/guides/building-a-ci-server)”。\n\n本指南将使用 REST API 来演示你可以使用的设置。\n在我们的场景中，我们将：\n\n* 合并拉取请求.\n* 在 CI 完成后，我们将相应地设置拉取请求的状态。\n* 合并拉取请求后，我们将在服务器上运行部署。\n\n我们的 CI 系统和主机服务器将是我们想象中的虚拟物。 它们可能是 Heroku、Amazon 或其他完全不同的东西。 本指南的重点是设置和配置负责管理通信的服务器。\n\n如果尚未下载，请务必[下载 `ngrok`](https://ngrok.com/)，并了解如何[使用它](https://ngrok.com/docs/getting-started/)。 我们发现它在将本地应用程序公开给 Internet 方面是一款非常有用的工具。\n\n> \\[!NOTE]\n> 或者，可以使用 Webhook 转发来配置本地环境以接收 Webhook。 有关详细信息，请参阅“[使用 GitHub CLI 转发 Webhooks 进行测试](/zh/webhooks/testing-and-troubleshooting-webhooks/using-the-github-cli-to-forward-webhooks-for-testing)”。\n\n注意：可以在 [platform-samples](https://github.com/github/platform-samples/tree/master/api/ruby/delivering-deployments) 存储库中下载此项目的完整源代码。\n\n## 编写服务器\n\n我们将编写一个简单的 Sinatra 应用程序，来验证我们的本地连接是否正常工作。\n首先编写以下代码：\n\n```ruby\nrequire 'sinatra'\nrequire 'json'\n\npost '/event_handler' do\n  payload = JSON.parse(params[:payload])\n  \"Well, it worked!\"\nend\n```\n\n（如果你不熟悉 Sinatra 的工作原理，建议你阅读 [Sinatra 指南](http://www.sinatrarb.com/)。）\n\n启动此服务器。 默认情况下，Sinatra 在端口 `4567` 上启动，因此你还需要配置 `ngrok` 以开始监听。\n\n为了使此服务器正常工作，我们需要使用 web 挂钩来设置一个仓库。 Web 挂钩应配置为在创建或合并拉取请求时触发。\n\n继续创建一个您可以自由支配的仓库。 我们可以推荐 [@octocat 的 Spoon/Knife 存储库](https://github.com/octocat/Spoon-Knife)吗？\n\n之后，你将在自己的存储库中创建新的 Webhook，向其馈送 `ngrok` 提供给你的 URL，并选择 `application/x-www-form-urlencoded` 作为内容类型。\n\n单击“更新 Webhook”。 应该会看到响应 `Well, it worked!`。\n很好！ 单击“让我选择单个事件”，然后选择以下项：\n\n* 部署\n* 部署状态\n* 拉取请求\n\n这些事件 GitHub 会在发生相关操作时发送到服务器。 我们现在将把服务器配置为\\_仅\\_在拉取请求合并时进行处理：\n\n```ruby\npost '/event_handler' do\n  @payload = JSON.parse(params[:payload])\n\n  case request.env['HTTP_X_GITHUB_EVENT']\n  when \"pull_request\"\n    if @payload[\"action\"] == \"closed\" && @payload[\"pull_request\"][\"merged\"]\n      puts \"A pull request was merged! A deployment should start now...\"\n    end\n  end\nend\n```\n\n这是怎么回事？\nGitHub 发出的每个事件都附带一个 `X-GitHub-Event` HTTP 标头。 我们现在只关注公关活动。 当合并拉取请求（其状态为 `closed` 且 `merged` 为 `true`）时，我们将启动部署。\n\n要测试此概念验证，请在测试存储库的分支中进行一些更改，打开拉取请求，然后合并它。 您的服务器应该会做出相应的响应！\n\n## 处理部署\n\n服务器已就位，代码在接受审查，拉取请求已合并，现在我们需要部署项目。\n\n我们将首先修改事件侦听器，以便在拉取请求被合并时对其进行处理，并开始关注部署：\n\n```ruby\nwhen \"pull_request\"\n  if @payload[\"action\"] == \"closed\" && @payload[\"pull_request\"][\"merged\"]\n    start_deployment(@payload[\"pull_request\"])\n  end\nwhen \"deployment\"\n  process_deployment(@payload)\nwhen \"deployment_status\"\n  update_deployment_status\nend\n```\n\n根据拉取请求中的信息，我们将首先填写 `start_deployment` 方法：\n\n```ruby\ndef start_deployment(pull_request)\n  user = pull_request['user']['login']\n  payload = JSON.generate(:environment => 'production', :deploy_user => user)\n  @client.create_deployment(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], {:payload => payload, :description => \"Deploying my sweet branch\"})\nend\n```\n\n部署可以附加一些元数据，格式为 `payload` 和 `description`。 尽管这些值是可选的，但对用于记录和表示信息很有帮助。\n\n创建新部署时，将触发完全独立的事件。 这就是为什么我们在 `switch` 的事件处理程序中有一个新的 `deployment` 案例。 在触发部署时，你可以根据此信息得到通知。\n\n部署可能需要很长时间，因此我们需要侦听各种事件，例如部署的创建时间以及部署所处的状态。\n\n让我们模拟一个能够完成某些工作的部署，并注意它对输出的影响。 首先，让我们完成 `process_deployment` 方法：\n\n```ruby\ndef process_deployment\n  payload = JSON.parse(@payload['payload'])\n  # you can send this information to your chat room, monitor, pager, etc.\n  puts \"Processing '#{@payload['description']}' for #{payload['deploy_user']} to #{payload['environment']}\"\n  sleep 2 # simulate work\n  @client.create_deployment_status(\"repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}\", 'pending')\n  sleep 2 # simulate work\n  @client.create_deployment_status(\"repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}\", 'success')\nend\n```\n\n最后，我们将模拟将状态信息存储为控制台输出：\n\n```ruby\ndef update_deployment_status\n  puts \"Deployment status for #{@payload['id']} is #{@payload['state']}\"\nend\n```\n\n我们来分析一下发生了什么。\n`start_deployment` 会创建一个新部署，这会触发 `deployment` 事件。 从那里，我们将调用 `process_deployment` 模拟正在进行的工作。 在该处理过程中，我们还会调用 `create_deployment_status`，这样接收方就可以知道发生了什么情况，因为我们将状态切换为 `pending`。\n\n部署完成后，我们将状态设置为 `success`。\n\n## 结束语\n\n在 GitHub 上，我们多年来一直使用一个版本 `Heaven` 来管理我们的部署。 共同流程本质上与我们上面构建的服务器基本相同：\n\n* 等待 CI 检查状态的响应（成功或失败）\n* 如果所需的检查成功，则合并拉取请求\n* `Heaven` 获取合并的代码，并将其部署到过渡服务器和生产服务器\n* 与此同时，`Heaven` 还会通过会议室中的 [Hubot](https://github.com/github/hubot) 向每个人通知构建情况\n\n就这么简单！ 使用此示例并不需要构建自己的部署设置。\n始终可以依赖于 [GitHub 集成](https://github.com/integrations)。"}