Git
章节 ▾ 第二版

6.5 GitHub - 使用 GitHub 进行脚本编写

使用 GitHub 进行脚本编写

现在我们已经介绍了 GitHub 的所有主要功能和工作流程,但是任何大型团队或项目都可能需要进行自定义或集成外部服务。

幸运的是,GitHub 在很多方面都非常易于修改。在本节中,我们将介绍如何使用 GitHub 钩子系统及其 API 来使 GitHub 按照我们想要的方式工作。

服务和钩子

GitHub 仓库管理中的“钩子”和“服务”部分是让 GitHub 与外部系统交互的最简单方法。

服务

首先,我们来看一下“服务”。“钩子”和“服务”集成都可以在仓库的“设置”部分找到,我们之前在其中查看了添加协作者和更改项目的默认分支。在“Webhooks 和服务”选项卡下,您将看到类似于 服务和钩子配置部分 的内容。

Services and Hooks configuration section
图 129. 服务和钩子配置部分

您可以选择数十种服务,其中大多数是与其他商业和开源系统集成。它们大多用于持续集成服务、错误和问题跟踪器、聊天室系统和文档系统。我们将逐步设置一个非常简单的服务,即电子邮件钩子。如果您从“添加服务”下拉列表中选择“电子邮件”,您将获得一个类似于 电子邮件服务配置 的配置屏幕。

Email service configuration
图 130. 电子邮件服务配置

在这种情况下,如果我们点击“添加服务”按钮,我们指定的电子邮件地址将在每次有人向仓库推送时收到一封电子邮件。服务可以侦听许多不同类型的事件,但大多数服务仅侦听推送事件,然后对该数据执行某些操作。

如果您正在使用的系统想要与 GitHub 集成,则应在此处查看是否有可用的现有服务集成。例如,如果您使用 Jenkins 在代码库上运行测试,则可以在每次有人向您的仓库推送时启用 Jenkins 内置服务集成以启动测试运行。

钩子

如果您需要更具体的东西,或者想要与此列表中未包含的服务或站点集成,则可以使用更通用的钩子系统。GitHub 仓库钩子非常简单。您指定一个 URL,GitHub 将在您想要的任何事件上将 HTTP 有效负载发布到该 URL。

通常,其工作原理是您可以设置一个小型 Web 服务来侦听 GitHub Hook 的有效负载,并在收到数据时对其进行处理。

要启用 Hook,请单击服务和 Hook 配置部分中的“添加 Webhook”按钮。这将带您到一个类似于Webhook 配置的页面。

Web hook configuration
图 131. Webhook 配置

Webhook 的配置非常简单。在大多数情况下,您只需输入一个 URL 和一个密钥,然后点击“添加 Webhook”即可。您可以选择希望 GitHub 为哪些事件发送有效负载——默认情况下,仅在有人将新代码推送到存储库的任何分支时(即 push 事件),才会收到有效负载。

让我们来看一个您可能设置的用于处理 Webhook 的小型 Web 服务示例。我们将使用 Ruby Web 框架 Sinatra,因为它非常简洁,您应该能够轻松地了解我们的操作。

假设我们希望在特定人员将特定文件推送到项目的特定分支时收到一封电子邮件。我们可以使用如下代码轻松实现这一点

require 'sinatra'
require 'json'
require 'mail'

post '/payload' do
  push = JSON.parse(request.body.read) # parse the JSON

  # gather the data we're looking for
  pusher = push["pusher"]["name"]
  branch = push["ref"]

  # get a list of all the files touched
  files = push["commits"].map do |commit|
    commit['added'] + commit['modified'] + commit['removed']
  end
  files = files.flatten.uniq

  # check for our criteria
  if pusher == 'schacon' &&
     branch == 'ref/heads/special-branch' &&
     files.include?('special-file.txt')

    Mail.deliver do
      from     '[email protected]'
      to       '[email protected]'
      subject  'Scott Changed the File'
      body     "ALARM"
    end
  end
end

在这里,我们获取 GitHub 传递给我们的 JSON 有效负载,并查找谁推送了它,他们推送到哪个分支以及所有推送提交中触及了哪些文件。然后,我们根据我们的条件进行检查,如果匹配则发送电子邮件。

为了开发和测试这样的内容,您可以在设置 Hook 的同一屏幕中使用一个不错的开发者控制台。您可以查看 GitHub 尝试为该 Webhook 执行的最后几次传递。对于每个 Hook,您可以深入了解其传递时间、是否成功以及请求和响应的正文和标头。这使得测试和调试 Hook 变得非常容易。

Web hook debugging information
图 132. Webhook 调试信息

另一个很棒的功能是,您可以重新传递任何有效负载以轻松测试您的服务。

有关如何编写 Webhook 以及您可以侦听的所有不同事件类型的更多信息,请访问 GitHub 开发者文档https://githubdocs.cn/en/webhooks-and-events/webhooks/about-webhooks

GitHub API

服务和 Hook 使您可以接收有关存储库中发生的事件的推送通知,但如果您需要有关这些事件的更多信息怎么办?如果您需要自动化某些操作(例如添加协作者或标记问题)怎么办?

这就是 GitHub API 发挥作用的地方。GitHub 拥有大量的 API 端点,可以以自动化方式执行您在网站上可以执行的几乎所有操作。在本节中,我们将学习如何进行身份验证并连接到 API,如何评论问题以及如何通过 API 更改 Pull Request 的状态。

基本用法

您可以执行的最基本操作是对不需要身份验证的端点进行简单的 GET 请求。这可能是用户或开源项目上的只读信息。例如,如果我们想了解有关名为“schacon”的用户的信息,我们可以运行如下命令

$ curl https://api.github.com/users/schacon
{
  "login": "schacon",
  "id": 70,
  "avatar_url": "https://avatars.githubusercontent.com/u/70",
# …
  "name": "Scott Chacon",
  "company": "GitHub",
  "following": 19,
  "created_at": "2008-01-27T17:19:28Z",
  "updated_at": "2014-06-10T02:37:23Z"
}

有许多这样的端点可以获取有关组织、项目、问题、提交的信息——几乎所有您可以在 GitHub 上公开看到的内容。您甚至可以使用 API 呈现任意 Markdown 或查找 .gitignore 模板。

$ curl https://api.github.com/gitignore/templates/Java
{
  "name": "Java",
  "source": "*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
"
}

评论问题

但是,如果您想在网站上执行操作(例如评论问题或 Pull Request)或查看或交互私有内容,则需要进行身份验证。

有几种身份验证方法。您可以使用仅包含用户名和密码的基本身份验证,但通常最好使用个人访问令牌。您可以在设置页面中的“应用程序”选项卡中生成此令牌。

Generate your access token from the “Applications” tab of your settings page
图 133. 从设置页面中的“应用程序”选项卡生成您的访问令牌

它会询问您想要为此令牌授予哪些范围以及描述。请确保使用良好的描述,以便在脚本或应用程序不再使用时,您可以放心地删除令牌。

GitHub 仅会向您显示一次令牌,因此请务必复制它。您现在可以在脚本中使用它进行身份验证,而不是使用用户名和密码。这很好,因为您可以限制您想要执行的操作范围,并且令牌是可撤销的。

这还有助于提高您的速率限制。在未进行身份验证的情况下,您将限制为每小时 60 个请求。如果进行身份验证,则每小时最多可以发出 5,000 个请求。

因此,让我们使用它来对我们其中一个问题发表评论。假设我们想对特定问题(问题 #6)发表评论。为此,我们必须对 repos/<user>/<repo>/issues/<num>/comments 发出 HTTP POST 请求,并将我们刚刚生成的令牌作为授权标头。

$ curl -H "Content-Type: application/json" \
       -H "Authorization: token TOKEN" \
       --data '{"body":"A new comment, :+1:"}' \
       https://api.github.com/repos/schacon/blink/issues/6/comments
{
  "id": 58322100,
  "html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100",
  ...
  "user": {
    "login": "tonychacon",
    "id": 7874698,
    "avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2",
    "type": "User",
  },
  "created_at": "2014-10-08T07:48:19Z",
  "updated_at": "2014-10-08T07:48:19Z",
  "body": "A new comment, :+1:"
}

现在,如果您转到该问题,您会看到我们刚刚成功发布的评论,如从 GitHub API 发布的评论所示。

A comment posted from the GitHub API
图 134. 从 GitHub API 发布的评论

您可以使用 API 执行您在网站上可以执行的几乎所有操作——创建和设置里程碑、为问题和 Pull Request 分配人员、创建和更改标签、访问提交数据、创建新的提交和分支、打开、关闭或合并 Pull Request、创建和编辑团队、评论 Pull Request 中的代码行、搜索网站等等。

更改 Pull Request 的状态

我们将看最后一个示例,因为如果您使用 Pull Request,它确实很有用。每个提交都可以有一个或多个与其关联的状态,并且有一个 API 可以添加和查询该状态。

大多数持续集成和测试服务都利用此 API 对推送做出反应,通过测试已推送的代码,然后报告该提交是否通过了所有测试。您还可以使用它来检查提交消息是否格式正确,提交者是否遵循了所有贡献准则,提交是否有效签名——任何数量的事情。

假设您在存储库上设置了一个 Webhook,它会命中一个小型 Web 服务,该服务检查提交消息中的 Signed-off-by 字符串。

require 'httparty'
require 'sinatra'
require 'json'

post '/payload' do
  push = JSON.parse(request.body.read) # parse the JSON
  repo_name = push['repository']['full_name']

  # look through each commit message
  push["commits"].each do |commit|

    # look for a Signed-off-by string
    if /Signed-off-by/.match commit['message']
      state = 'success'
      description = 'Successfully signed off!'
    else
      state = 'failure'
      description = 'No signoff found.'
    end

    # post status to GitHub
    sha = commit["id"]
    status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}"

    status = {
      "state"       => state,
      "description" => description,
      "target_url"  => "http://example.com/how-to-signoff",
      "context"     => "validate/signoff"
    }
    HTTParty.post(status_url,
      :body => status.to_json,
      :headers => {
        'Content-Type'  => 'application/json',
        'User-Agent'    => 'tonychacon/signoff',
        'Authorization' => "token #{ENV['TOKEN']}" }
    )
  end
end

希望这很容易理解。在此 Webhook 处理程序中,我们查看刚刚推送的每个提交,在提交消息中查找字符串“Signed-off-by”,最后通过 HTTP 向 /repos/<user>/<repo>/statuses/<commit_sha> API 端点发布状态。

在这种情况下,您可以发送状态('success'、'failure'、'error')、发生情况的描述、用户可以访问以获取更多信息的 target URL 以及“context”(如果单个提交有多个状态)。例如,测试服务可能会提供状态,而像这样的验证服务也可能会提供状态——“context”字段是它们如何区分的。

如果有人在 GitHub 上打开新的 Pull Request 并且设置了此 Hook,您可能会看到类似于通过 API 的提交状态的内容。

Commit status via the API
图 135. 通过 API 的提交状态

您现在可以在消息中包含“Signed-off-by”字符串的提交旁边看到一个小绿色的勾号,以及作者忘记签名的提交旁边的红色叉号。您还可以看到 Pull Request 获取分支上最后一个提交的状态,并在失败时向您发出警告。如果您将此 API 用于测试结果,这非常有用,因此您不会意外合并最后一个提交失败的测试。

Octokit

尽管在这些示例中我们几乎所有操作都是通过 curl 和简单的 HTTP 请求完成的,但存在一些开源库,它们可以以更惯用的方式提供此 API。在撰写本文时,支持的语言包括 Go、Objective-C、Ruby 和 .NET。有关这些库的更多信息,请查看https://github.com/octokit,因为它们为您处理了许多 HTTP 操作。

希望这些工具可以帮助您自定义和修改 GitHub 以更好地满足您的特定工作流程。有关整个 API 的完整文档以及常见任务指南,请查看https://githubdocs.cn/

scroll-to-top