{"meta":{"title":"构建 CI 服务器","intro":"使用状态 API 构建您自己的 CI 系统。","product":"REST API","breadcrumbs":[{"href":"/zh/rest","title":"REST API"},{"href":"/zh/rest/guides","title":"指南"},{"href":"/zh/rest/guides/building-a-ci-server","title":"构建 CI 服务器"}],"documentType":"article"},"body":"# 构建 CI 服务器\n\n使用状态 API 构建您自己的 CI 系统。\n\n你可以使用 REST API 将提交与测试服务关联起来，这样你所做的每次推送都可以接受测试，并体现在 GitHub 拉取请求中。 有关相关终结点的详细信息，请参阅 [提交状态的 REST API 端点](/zh/rest/commits/statuses)。\n\n本指南将使用该 API 来演示您可以使用的设置。\n在我们的场景中，我们将：\n\n* 在拉取请求被打开时运行我们的 CI 套件（我们将 CI 状态设置为待处理）。\n* 在 CI 完成后，我们将相应地设置拉取请求的状态。\n\n我们的 CI 系统和主机服务器将是我们想象中的虚拟物。 它们可能是 Travis、Jenkins 或其他完全不同的工具。 本指南的重点是设置和配置负责管理通信的服务器。\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/building-a-ci-server) 存储库中下载此项目的完整源代码。\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这些事件 GitHub 会在发生相关操作时发送到服务器。 现在将服务器更新为仅处理当前的 Pull Request 情况：\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\"] == \"opened\"\n      process_pull_request(@payload[\"pull_request\"])\n    end\n  end\nend\n\nhelpers do\n  def process_pull_request(pull_request)\n    puts \"It's #{pull_request['title']}\"\n  end\nend\n```\n\n这是怎么回事？\nGitHub 发出的每个事件都附带一个 `X-GitHub-Event` HTTP 标头。 我们现在只关注公关活动。 我们将从其中获取信息的有效负载，并返回标题字段。 在理想情况下，我们的服务器会关注每次更新拉取请求时的情况（而不仅仅是打开时的情况）。 这将确保每个新推送都通过 CI 测试。\n但就此演示而言，我们只需关注打开它时会发生什么。\n\n要测试此概念验证，请在测试存储库的分支中进行一些更改，然后打开拉取请求。 您的服务器应该会做出相应的响应！\n\n## 处理状态\n\n服务器就位后，我们就可以开始实现第一个要求，即设置（和更新）CI 状态。 请注意，随时更新服务器时，你可以单击“重新传递”\\*\\*\\*\\* 发送相同的有效负载。 不需要每次进行更改时都发出新的拉取请求！\n\n由于我们正在与 GitHub API 交互，因此我们将使用 [Octokit.rb](https://github.com/octokit/octokit.rb) 来管理交互。 我们将使用[a personal access token](/zh/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)来配置该客户端：\n\n```ruby\n# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!\n# Instead, set and test environment variables, like below\nACCESS_TOKEN = ENV['MY_PERSONAL_TOKEN']\n\nbefore do\n  @client ||= Octokit::Client.new(:access_token => ACCESS_TOKEN)\nend\n```\n\n之后，我们只需更新 GitHub 上的拉取请求，明确说明我们是在 CI 上进行处理：\n\n```ruby\ndef process_pull_request(pull_request)\n  puts \"Processing pull request...\"\n  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')\nend\n```\n\n我们在这里做三件非常基本的事情：\n\n* 我们正在查找存储库的全名\n* 我们正在查找拉取请求的最后一个 SHA\n* 将状态设置为“待处理”\n\n就这么简单！ 从这里，你可以运行任何需要的进程来执行测试套件。 也许你会将代码传递给 Jenkins，或者通过其 API 调用另一个 Web 服务，例如 [Travis](https://api.travis-ci.com/docs/)。 之后，请务必再次更新状态。 在本示例中，我们只需将其设置为 `\"success\"`：\n\n```ruby\ndef process_pull_request(pull_request)\n  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')\n  sleep 2 # do busy work...\n  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'success')\n  puts \"Pull request processed!\"\nend\n```\n\n## 结束语\n\n在GitHub，我们使用了 [Janky](https://github.com/github/janky) 版本来管理 CI 多年。\n基本流程本质上与我们上面构建的服务器完全相同。\n在GitHub，我们：\n\n* 在创建或更新（通过 Janky）时触发 Jenkins\n* 等待关于 CI 状态的响应\n* 如果代码为绿色，我们将合并拉取请求\n\n所有这些通信都会流回我们的聊天室。 使用此示例并不需要构建自己的 CI 设置。\n始终可以依赖于 [GitHub 集成](https://github.com/integrations)。"}