My External Storage

May 31, 2021 - 3 minute read - Comments - github

GitHub API v3でPrivateリポジトリのPull Requestのdiffを取得する

GitHub API v3でPull Requestの情報をごにょごにょしようとしたらハマったのでメモ。
GitHub API v3のPull RequestのJSONに含まれるdiff_urlは役に立たない。Acceptヘッダーでレスポンスが変化する。

TL;DR

サンプルコードは以下。

hc = oauth2.NewClient(ctx, oauth2.StaticTokenSource(
    &oauth2.Token{AccessToken: cfg.GitHubToken},
))

cli := github.NewClient(hc)
pr, _, _ := cli.PullRequests.Get(ctx, cfg.Owner, cfg.Repository, cfg.PullRequestNumber)
url := pr.GetURL()

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
req.Header.Add("Accept", "application/vnd.github.v3.diff")

resp, err := hc.Do(req)

Pull Requestのdiffを取得したい

GitHub API v3を利用してPull Requestの情報を取得すると、次のようなJSONが取得できる。

{
  "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347",
  "id": 1,
  "node_id": "MDExOlB1bGxSZXF1ZXN0MQ==",
  "html_url": "https://github.com/octocat/Hello-World/pull/1347",
  "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff",
  ....
}

ここで、diff_urlに含まれるURLを取得すると、diffファイルが取得できる。
たとえば以下のような内容だ。

diff --git a/inspect.go b/inspect.go
new file mode 100644
index 0000000..9bfc74a
--- /dev/null
+++ b/inspect.go
@@ -0,0 +1,51 @@
+package nrseg
+
+import (
+	"errors"
+	"go/ast"
+	"go/parser"
+	"go/token"
...

これをgo-githubで実装すると以下のようになるだろう。 privateリポジトリでも利用できるようにOAuth2で認証を付けておく。

import (
  "github.com/google/go-github/v35/github"
  "golang.org/x/oauth2"
)

// ...

hc := oauth2.NewClient(ctx, oauth2.StaticTokenSource(
  &oauth2.Token{AccessToken: GitHubToken},
))

cli := github.NewClient(hc)
pr, _, _ := cli.PullRequests.Get(ctx, "budougumi0617", "nrseg", 14)
durl := pr.GetDiffURL()
resp, err := hc.Get(pr.GetDiffURL())

しかし、OAuth2で認証を付けGet a pull requestは成功する状態でもdiffファイルの取得で404になる現象が発生した。

diff_urlはprivateリポジトリでは使えない

調査したところ、diff_urlに含まれるURLはprivateリポジトリでは利用できないようだった。
ではどうすると言うかというとメディアタイプを指定することでGet a pull requestのAPIエンドポイントからdiff情報が取得できた…

実はこのことはAPIドキュメントを読むとちゃんと記載があった。

Pass the appropriate media type to fetch diff and patch formats.

改めてgo-githubで実装すると、diffを取得するコードは次のようになる。

hc = oauth2.NewClient(ctx, oauth2.StaticTokenSource(
    &oauth2.Token{AccessToken: cfg.GitHubToken},
))

cli := github.NewClient(hc)
pr, _, _ := cli.PullRequests.Get(ctx, cfg.Owner, cfg.Repository, cfg.PullRequestNumber)
url := pr.GetURL()

req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
req.Header.Add("Accept", "application/vnd.github.v3.diff")

resp, err := hc.Do(req)

終わりに

diff_urlってパラメータは何のためにあるんだという気持ちになった。OSSのリポジトリ(publicリポジトリ)だと使えるけれど…
そして今回もドキュメントはちゃんと読もうね&動作確認ちゃんとしようね案件だった。
JSONの構造だけみて油断してはいけない。また、publicリポジトリでテストしていたので認証周りのトラップに気づけなかった。使いたい環境でしっかりと動作確認すべきだった。

参考

関連記事