章節 ▾ 第二版

6.5 GitHub - 指令碼化 GitHub

指令碼化 GitHub

現在我們已經介紹了 GitHub 的所有主要功能和工作流程,但是任何大型團隊或專案都可能有想要進行的自定義或想要整合的外部服務。

幸運的是,GitHub 在很多方面都非常易於定製。在本節中,我們將介紹如何使用 GitHub 的鉤子系統及其 API,讓 GitHub 按照我們想要的方式工作。

服務和鉤子

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

服務

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

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

您可以從幾十種服務中選擇,其中大多數都是與其他商業和開源系統的整合。它們大多用於持續整合服務、錯誤和問題跟蹤器、聊天室系統和文件系統。我們將逐步介紹如何設定一個非常簡單的服務:電子郵件鉤子。如果您從“新增服務”下拉選單中選擇“email”,您將看到一個類似 電子郵件服務配置 的配置螢幕。

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

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

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

鉤子

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

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

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

Web hook configuration
圖 131. Web hook 配置

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

讓我們看一個您可能需要設定來處理 Web hook 的 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 嘗試為該 Web hook 進行的最後幾次傳遞。對於每個鉤子,您可以深入瞭解其傳遞時間、是否成功,以及請求和響應的 body 和 headers。這使得測試和除錯鉤子變得異常容易。

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

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

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

GitHub API

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

這時 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,或者想檢視或與私有內容進行互動,您就需要進行身份驗證。

有幾種身份驗證方法。您可以使用簡單的使用者名稱和密碼進行基本身份驗證,但通常最好使用個人訪問令牌。您可以從設定頁面的“Applications”選項卡生成此令牌。

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

它會詢問您要為該令牌授予哪些許可權範圍以及一個描述。請確保使用一個好的描述,這樣當您的指令碼或應用程式不再使用時,您就可以放心地刪除該令牌。

GitHub 只會向您顯示一次令牌,所以請務必將其複製。現在您可以在指令碼中使用此令牌進行身份驗證,而不是使用使用者名稱和密碼。這樣做的好處是您可以限制操作的範圍,並且令牌是可撤銷的。

這還有增加速率限制的好處。如果不進行身份驗證,您每小時最多隻能進行 60 次請求。如果您進行身份驗證,則每小時最多可以進行 5,000 次請求。

所以,讓我們使用它來評論我們的一個問題。假設我們想在特定問題 #6 上留下評論。為此,我們必須向 repos/<user>/<repo>/issues/<num>/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、建立和更改標籤、訪問提交資料、建立新的提交和分支、開啟、關閉或合併 Pull Request、建立和編輯團隊、在 Pull Request 的程式碼行上發表評論、搜尋網站等等。

更改 Pull Request 的狀態

最後還有一個例子,我們來看一下,因為它對於處理 Pull Request 非常有用。每個提交都可以關聯一個或多個狀態,並且有一個 API 用於新增和查詢這些狀態。

大多數持續整合和測試服務都利用此 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 hook 處理程式中,我們遍歷剛剛推送的每個提交,在提交訊息中查詢“Signed-off-by”字串,最後我們透過 HTTP POST 到 /repos/<user>/<repo>/statuses/<commit_sha> API 端點,並附帶狀態資訊。

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

如果有人在 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/