章節 ▾ 第二版

6.5 GitHub - 指令碼化 GitHub

指令碼化 GitHub

至此,我們已經涵蓋了 GitHub 的所有主要功能和工作流程,但是任何大型團隊或專案都可能需要進行定製或整合外部服務。

幸運的是,GitHub 在許多方面都非常容易“黑進”。在本節中,我們將介紹如何使用 GitHub 的鉤子系統及其 API 來使 GitHub 按照我們希望的方式工作。

服務與鉤子

GitHub 倉庫管理中的“鉤子與服務”部分是讓 GitHub 與外部系統互動的最簡單方式。

服務

首先,我們來看看服務。鉤子和服務整合都可以在倉庫的“設定”部分找到,我們之前在那裡查看了新增協作者和更改專案預設分支。在“Webhooks and Services”選項卡下,您會看到類似於服務與鉤子配置部分的內容。

Services and Hooks configuration section
圖 129. 服務與鉤子配置部分

有幾十種服務可供選擇,其中大部分是與其他商業和開源系統的整合。它們大多用於持續整合服務、bug 和問題跟蹤器、聊天室系統和文件系統。我們將演示如何設定一個非常簡單的鉤子:電子郵件鉤子。如果您從“新增服務”下拉列表中選擇“電子郵件”,您將獲得一個配置螢幕,如電子郵件服務配置所示。

Email service configuration
圖 130. 電子郵件服務配置

在這種情況下,如果我們點選“新增服務”按鈕,每次有人推送到倉庫時,我們指定的電子郵件地址都會收到一封電子郵件。服務可以監聽許多不同型別的事件,但大多數只監聽推送事件,然後利用這些資料做一些事情。

如果您正在使用的系統希望與 GitHub 整合,您應該在此處檢查是否存在現有的服務整合。例如,如果您使用 Jenkins 來執行程式碼庫的測試,您可以啟用 Jenkins 內建服務整合,以便每次有人推送到您的倉庫時都觸發一次測試執行。

鉤子

如果您需要更具體的功能,或者您想與此列表中未包含的服務或網站整合,則可以改用更通用的鉤子系統。GitHub 倉庫鉤子非常簡單。您指定一個 URL,GitHub 將在您想要的任何事件發生時向該 URL 釋出一個 HTTP 有效負載。

通常,其工作方式是您可以設定一個小型 Web 服務來偵聽 GitHub 鉤子有效負載,然後在收到資料時對其進行處理。

要啟用鉤子,請點選服務與鉤子配置部分中的“新增 Webhook”按鈕。這將帶您進入一個類似於Web 鉤子配置的頁面。

Web hook configuration
圖 131. Web 鉤子配置

Web 鉤子的配置非常簡單。在大多數情況下,您只需輸入一個 URL 和一個秘密金鑰,然後點選“新增 Webhook”。有幾個選項可以選擇您希望 GitHub 向您傳送有效負載的事件——預設情況下,只有在有人將新程式碼推送到您倉庫的任何分支時,才會收到 `push` 事件的有效負載。

讓我們看一個您可能設定來處理 Web 鉤子的小型 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     'tchacon@example.com'
      to       'tchacon@example.com'
      subject  'Scott Changed the File'
      body     "ALARM"
    end
  end
end

在這裡,我們獲取 GitHub 傳遞給我們的 JSON 有效負載,並查詢是誰推送的,他們推送到了哪個分支,以及所有被推送的提交中涉及了哪些檔案。然後我們對照我們的標準進行檢查,如果匹配,則傳送一封電子郵件。

為了開發和測試類似這樣的功能,在設定鉤子的同一螢幕上,您會有一個方便的開發者控制檯。您可以檢視 GitHub 嘗試為該 Webhook 進行的最後幾次傳遞。對於每個鉤子,您可以深入瞭解其傳遞時間、是否成功以及請求和響應的主體和頭部資訊。這使得測試和除錯您的鉤子變得異常簡單。

Web hook debugging information
圖 132. Web 鉤子除錯資訊

此功能的另一個優點是,您可以輕鬆地重新傳遞任何有效負載以測試您的服務。

有關如何編寫 Webhook 以及您可以監聽的所有不同事件型別的更多資訊,請訪問 GitHub 開發者文件:https://docs.github.com/en/webhooks-and-events/webhooks/about-webhooks

GitHub API

服務和鉤子為您提供了一種接收倉庫事件推送通知的方式,但如果您需要有關這些事件的更多資訊怎麼辦?如果您需要自動化諸如新增協作者或標記問題等操作怎麼辦?

這就是 GitHub API 派上用場的地方。GitHub 擁有大量的 API 端點,可以以自動化方式完成您在網站上幾乎所有能做的事情。在本節中,我們將學習如何認證並連線到 API,如何在問題上發表評論,以及如何透過 API 更改拉取請求的狀態。

基本用法

您能做的最基本的事情是對不需要認證的端點進行簡單的 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*
"
}

評論問題

然而,如果您想在網站上執行操作,例如評論 Issue 或 Pull Request,或者如果您想檢視或與私人內容互動,則需要進行身份驗證。

有幾種認證方式。您可以使用只包含使用者名稱和密碼的基本認證,但通常更好的做法是使用個人訪問令牌。您可以從設定頁面的“應用程式”選項卡生成此令牌。

Generate your access token from the “Applications” tab of your settings page
圖 133. 從設定頁面的“應用程式”選項卡生成您的訪問令牌

它會詢問您此令牌所需的範圍和描述。請務必使用良好的描述,這樣當您的指令碼或應用程式不再使用時,您可以放心地刪除該令牌。

GitHub 只會向您顯示一次令牌,所以請務必複製它。現在您可以使用此令牌在指令碼中進行身份驗證,而不是使用使用者名稱和密碼。這很好,因為您可以限制您想要做的事情的範圍,並且令牌是可撤銷的。

這還有一個額外的好處,可以提高您的速率限制。如果沒有認證,您將被限制為每小時 60 個請求。如果您進行認證,則每小時可以發出多達 5,000 個請求。

所以讓我們用它來評論我們的一個問題。假設我們想在特定問題(問題 #6)上留下評論。為此,我們必須向 `repos///issues//comments` 傳送 HTTP POST 請求,並將我們剛剛生成的令牌作為 Authorization 頭部。

$ 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 時非常有用。每個提交可以關聯一個或多個狀態,並且有一個 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

希望這相當簡單易懂。在這個 Web 鉤子處理程式中,我們遍歷剛剛推送的每個提交,在提交訊息中查詢字串“Signed-off-by”,最後透過 HTTP 向 `/repos///statuses/` API 端點 POST 狀態。

在這種情況下,您可以傳送一個狀態('success'、'failure'、'error')、發生的描述、使用者可以訪問以獲取更多資訊的目標 URL,以及一個“上下文”,以防單個提交有多個狀態。例如,一個測試服務可能會提供一個狀態,而像這樣的驗證服務也可能會提供一個狀態——“上下文”欄位是它們區分的方式。

如果有人在 GitHub 上打開了一個新的 Pull Request,並且設定了此鉤子,您可能會看到類似於透過 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://docs.github.com/

scroll-to-top