GitHub是全球最大的程式碼託管平台,許多軟體資源都可以在該網站上取得。有些人如筆者就喜歡除了把程式原始碼上傳到GitHub外,也把已經編譯好的二進制檔案也一併上傳到GitHub替每個倉庫(Repository)所提供的「Release」區,這樣一來不想自行編譯原始碼的使用者就可以直接到「Release」區中找到對應平台已經編譯好的二進制檔案來直接下載使用。



以OpenH264這個H.264的開源函式庫為例,其GitHub倉庫網址如下:

https://github.com/cisco/openh264

我們可以在其「Release」區下,找到各個版本已經官方編譯好的函式庫和重新打包過的原始碼專案。如下圖:

linux-github-latest-release-download

其中,libopenh264-1.8.0-linux64.4.so.bz2就是Linux x86_64所使用的動態函式庫,我們可以將其下載下來,就能在Linux x86_64上直接使用。

然而每次都要用網頁瀏覽器開啟GitHub來找到這個頁面來載最新版的檔案好像也挺麻煩的,在懶惰蟲的趨使下,就必須要想個方式能夠直接在Linux環境下,以指令的方式下載到GitHub倉庫上最新發佈出來的檔案。

GitHub提供的 /releases/latest HTTP API

GitHub有提供許多方便的HTTP API,其中的/releases/latest可以協助我們做到這件事。HTTP請求的網址格式如下:

GET https://api.github.com/repos/<user>/<repo>/releases/latest

以剛才提到的OpenH264為例,就是:

GET https://api.github.com/repos/cisco/openh264/releases/latest

這支API的回傳值大概如以下這樣(已刪減冗長訊息):

{
    "url": "https://api.github.com/repos/cisco/openh264/releases/11663903",
    "assets_url": "https://api.github.com/repos/cisco/openh264/releases/11663903/assets",
    "upload_url": "https://uploads.github.com/repos/cisco/openh264/releases/11663903/assets{?name,label}",
    "html_url": "https://github.com/cisco/openh264/releases/tag/v1.8.0",
    "id": 11663903,
    "node_id": "MDc6UmVsZWFzZTExNjYzOTAz",
    "tag_name": "v1.8.0",
    "target_commitish": "master",
    "name": "Release version 1.8.0",
    "draft": false,
    "author": {
        "login": "GuangweiWang",
        "id": 11676737,
        "node_id": "MDQ6VXNlcjExNjc2NzM3",
        "avatar_url": "https://avatars2.githubusercontent.com/u/11676737?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/GuangweiWang",
        "html_url": "https://github.com/GuangweiWang",
        "followers_url": "https://api.github.com/users/GuangweiWang/followers",
        "following_url": "https://api.github.com/users/GuangweiWang/following{/other_user}",
        "gists_url": "https://api.github.com/users/GuangweiWang/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/GuangweiWang/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/GuangweiWang/subscriptions",
        "organizations_url": "https://api.github.com/users/GuangweiWang/orgs",
        "repos_url": "https://api.github.com/users/GuangweiWang/repos",
        "events_url": "https://api.github.com/users/GuangweiWang/events{/privacy}",
        "received_events_url": "https://api.github.com/users/GuangweiWang/received_events",
        "type": "User",
        "site_admin": false
    },
    "prerelease": false,
    "created_at": "2018-06-27T01:36:43Z",
    "published_at": "2018-06-27T02:03:37Z",
    "assets": [
        {
            "url": "https://api.github.com/repos/cisco/openh264/releases/assets/7676582",
            "id": 7676582,
            "node_id": "MDEyOlJlbGVhc2VBc3NldDc2NzY1ODI=",
            "name": "libopenh264-1.8.0-linux64.4.so.bz2",
            "label": null,
            "uploader": {
                "login": "GuangweiWang",
                "id": 11676737,
                "node_id": "MDQ6VXNlcjExNjc2NzM3",
                "avatar_url": "https://avatars2.githubusercontent.com/u/11676737?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/GuangweiWang",
                "html_url": "https://github.com/GuangweiWang",
                "followers_url": "https://api.github.com/users/GuangweiWang/followers",
                "following_url": "https://api.github.com/users/GuangweiWang/following{/other_user}",
                "gists_url": "https://api.github.com/users/GuangweiWang/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/GuangweiWang/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/GuangweiWang/subscriptions",
                "organizations_url": "https://api.github.com/users/GuangweiWang/orgs",
                "repos_url": "https://api.github.com/users/GuangweiWang/repos",
                "events_url": "https://api.github.com/users/GuangweiWang/events{/privacy}",
                "received_events_url": "https://api.github.com/users/GuangweiWang/received_events",
                "type": "User",
                "site_admin": false
            },
            "content_type": "application/x-bzip2",
            "state": "uploaded",
            "size": 529202,
            "download_count": 1122,
            "created_at": "2018-06-27T01:45:48Z",
            "updated_at": "2018-06-27T01:45:57Z",
            "browser_download_url": "https://github.com/cisco/openh264/releases/download/v1.8.0/libopenh264-1.8.0-linux64.4.so.bz2"
        },
        {
            "url": "https://api.github.com/repos/cisco/openh264/releases/assets/7676588",
            "id": 7676588,
            "node_id": "MDEyOlJlbGVhc2VBc3NldDc2NzY1ODg=",
            "name": "libopenh264-1.8.0-linux64.4.so.sig.bz2",
            "label": null,
            "uploader": {
                "login": "GuangweiWang",
                "id": 11676737,
                "node_id": "MDQ6VXNlcjExNjc2NzM3",
                "avatar_url": "https://avatars2.githubusercontent.com/u/11676737?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/GuangweiWang",
                "html_url": "https://github.com/GuangweiWang",
                "followers_url": "https://api.github.com/users/GuangweiWang/followers",
                "following_url": "https://api.github.com/users/GuangweiWang/following{/other_user}",
                "gists_url": "https://api.github.com/users/GuangweiWang/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/GuangweiWang/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/GuangweiWang/subscriptions",
                "organizations_url": "https://api.github.com/users/GuangweiWang/orgs",
                "repos_url": "https://api.github.com/users/GuangweiWang/repos",
                "events_url": "https://api.github.com/users/GuangweiWang/events{/privacy}",
                "received_events_url": "https://api.github.com/users/GuangweiWang/received_events",
                "type": "User",
                "site_admin": false
            },
            "content_type": "application/x-bzip2",
            "state": "uploaded",
            "size": 418,
            "download_count": 119,
            "created_at": "2018-06-27T01:46:23Z",
            "updated_at": "2018-06-27T01:46:25Z",
            "browser_download_url": "https://github.com/cisco/openh264/releases/download/v1.8.0/libopenh264-1.8.0-linux64.4.so.sig.bz2"
        }
    ],
    "tarball_url": "https://api.github.com/repos/cisco/openh264/tarball/v1.8.0",
    "zipball_url": "https://api.github.com/repos/cisco/openh264/zipball/v1.8.0"
}

其中,tarball_urlzipball_url欄位是GitHub替每個Release主動提供的快照下載網址,而assets欄位是GitHub倉庫的擁有者自行在建立Release時所額外加入的檔案。assets欄位中的每個檔案的browser_download_url欄位,正是該檔案的下載網址。

curl指令

我們可以利用Linux發行版中常會內建的curl指令來調用GitHub提供的這支API。如果環境中沒有安裝curl的話,基於Debian的Linux發行版可以使用以下指令來安裝:

sudo apt install curl

紅帽系的Linux發行版則可以使用以下指令來安裝curl

sudo dnf install curl

curl指令的使用方式如下:

curl -s https://api.github.com/repos/cisco/openh264/releases/latest

curl指令的-s參數可以讓curl指令只輸出URL資源的主體(body)資料。

linux-github-latest-release-download

sed指令

接著我們可以利用Linux作業系統中內建的sed指令來處理curl指令呼叫GitHub的API所回傳的JSON資料。

例如要抓取browser_download_url欄位中的網址,sed指令可以這樣寫:

sed -r -n 's/.*"browser_download_url": *"(.*)".*//p'

sed指令的-r參數可以讓\1發揮作用(也就是正規表示式的群組功能);-n參數可以讓輸出結果只保留/p要輸出的部份(此處就是\1)。

結合curl指令,寫法如下:

curl -s https://api.github.com/repos/cisco/openh264/releases/latest | sed -r -n 's/.*"browser_download_url": *"(.*)".*//p'

linux-github-latest-release-download

如上圖,由於每個Release中可能會有超過一個的額外檔案,因此我們必須要確保這些網址所連結到的檔案資源都是我們需要下載,不然就得繼續再過濾。這部份可以再使用grep指令來達成,或者我們也可以修改剛才寫好的sed指令,在(.*)內加入更嚴苛的條件,例如:

curl -s https://api.github.com/repos/cisco/openh264/releases/latest | sed -r -n 's/.*"browser_download_url": *"(.*-linux64\..*\.so\.bz2)".*//p'

linux-github-latest-release-download

如上圖,只剩下一個網址了!

wget指令

最後可以利用Linux發行版中常會內建的wget指令來下載這個網址所連結到的檔案。如果環境中沒有安裝wget的話,基於Debian的Linux發行版可以使用以下指令來安裝:

sudo apt install wget

紅帽系的Linux發行版則可以使用以下指令來安裝wget

sudo dnf install wget

有關於wget更詳細的介紹可以參考這篇文章:

https://magiclen.org/wget/

例如要用標準輸入(stdin)中取得的網址來下載檔案,指令可以這樣寫:

wget -q -i -

wget指令的-q參數可以讓wget在下載時不印出一堆資訊;-i參數可以指定一個檔案路徑或是用-表示標準輸入,來輸入多筆要進行檔案下載的網址。

最後結合curl指令和sed指令,寫法如下:

curl -s https://api.github.com/repos/cisco/openh264/releases/latest | sed -r -n 's/.*"browser_download_url": *"(.*-linux64\..*\.so\.bz2)".*//p' | wget -q -i -

如此一來就能用一行指令把GitHub倉庫上最新發佈的檔案給下載下來了!

linux-github-latest-release-download