跳至主要内容

构建 CI 服务器

使用 Status API 构建自己的 CI 系统。

您可以使用 REST API 将提交与测试服务绑定在一起,以便您每次推送都可以进行测试并在 GitHub 拉取请求中表示。有关相关端点的更多信息,请参阅“提交状态的 REST API 端点”。

本指南将使用该 API 演示您可以使用的设置。在我们的场景中,我们将

  • 在打开拉取请求时运行我们的 CI 套件(我们将 CI 状态设置为待处理)。
  • CI 完成后,我们将相应地设置拉取请求的状态。

我们的 CI 系统和主机服务器将是我们想象中的产物。它们可以是 Travis、Jenkins 或其他任何东西。本指南的核心是设置和配置管理通信的服务器。

如果您还没有,请下载 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"] == "opened"
      process_pull_request(@payload["pull_request"])
    end
  end
end

helpers do
  def process_pull_request(pull_request)
    puts "It's #{pull_request['title']}"
  end
end

发生了什么?GitHub 发送的每个事件都附加了一个 X-GitHub-Event HTTP 头。我们现在只关心 PR 事件。从那里,我们将获取信息负载,并返回标题字段。在理想情况下,我们的服务器会关注每次拉取请求更新,而不仅仅是打开时。这将确保每次新推送都通过 CI 测试。但对于这个演示,我们只关心它何时打开。

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

使用状态

我们的服务器到位后,我们就可以开始我们的第一个需求,即设置(和更新)CI 状态。请注意,您可以随时更新服务器,然后点击 重新发送 以发送相同的负载。每次更改时无需创建新的拉取请求!

由于我们正在与 GitHub API 交互,我们将使用 Octokit.rb 来管理我们的交互。我们将使用 个人访问令牌 配置该客户端。

# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
ACCESS_TOKEN = ENV['MY_PERSONAL_TOKEN']

before do
  @client ||= Octokit::Client.new(:access_token => ACCESS_TOKEN)
end

之后,我们只需要更新 GitHub 上的拉取请求,以明确我们正在 CI 上进行处理。

def process_pull_request(pull_request)
  puts "Processing pull request..."
  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')
end

这里我们做了三个非常基本的事情

  • 我们正在查找存储库的完整名称
  • 我们正在查找拉取请求的最后一个 SHA
  • 我们将状态设置为“pending”

就是这样!从这里,您可以运行任何需要的流程来执行您的测试套件。也许您要将代码传递给 Jenkins,或者通过其 API 调用另一个 Web 服务,例如 Travis。之后,您需要再次更新状态。在我们的示例中,我们将它设置为 "success"

def process_pull_request(pull_request)
  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')
  sleep 2 # do busy work...
  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'success')
  puts "Pull request processed!"
end

结论

在 GitHub,我们多年来一直使用 Janky 的一个版本来管理我们的 CI。基本流程与我们上面构建的服务器基本相同。在 GitHub,我们

  • 在创建或更新拉取请求时向 Jenkins 发出请求(通过 Janky)
  • 等待 CI 状态的响应
  • 如果代码是绿色的,我们将合并拉取请求

所有这些通信都回传到我们的聊天室。您无需构建自己的 CI 设置即可使用此示例。您始终可以依赖 GitHub 集成