go-github
go-github 是一个用于访问 GitHub API v3 的 Go 客户端库。
目前,go-github 需要 Go 1.13 或更高版本。go-github 遵循 Go 的版本支持策略。我们尽量不破坏旧版本的 Go,但由于工具限制,我们并不总是测试旧版本。
如果你对使用 GraphQL API v4 感兴趣,推荐使用 shurcooL/githubv4 库。
安装
go-github 兼容模块模式下的现代 Go 版本,安装 Go 后:
go get github.com/google/go-github/v64
将解析并将包及其依赖项添加到当前开发模块中。
或者,如果在包中使用导入,也可以实现相同的效果:
import "github.com/google/go-github/v64/github"
然后运行不带参数的 go get
。
最后,要使用此仓库的最新版本,请使用以下命令:
go get github.com/google/go-github/v64@master
使用
import "github.com/google/go-github/v64/github" // 启用 go modules(GO111MODULE=on 或在 GOPATH 外)
import "github.com/google/go-github/github" // 禁用 go modules
构造一个新的 GitHub 客户端,然后使用客户端上的各种服务来访问 GitHub API 的不同部分。例如:
client := github.NewClient(nil)
// 列出用户 "willnorris" 的所有组织
orgs, _, err := client.Organizations.List(context.Background(), "willnorris", nil)
一些 API 方法有可选参数可以传递。例如:
client := github.NewClient(nil)
// 列出 "github" 组织的公共仓库
opt := &github.RepositoryListByOrgOptions{Type: "public"}
repos, _, err := client.Repositories.ListByOrg(context.Background(), "github", opt)
客户端的服务将 API 分为逻辑块,并对应于 https://docs.github.com/en/rest 的 GitHub API 文档结构。
注意:使用 context 包,可以轻松地将取消信号和截止时间传递给客户端的各种服务以处理请求。如果没有可用的上下文,可以使用 context.Background()
作为起点。
更多示例代码片段,请查看 example 目录。
身份验证
使用 WithAuthToken
方法配置客户端,以使用 OAuth 令牌(例如,个人访问令牌)进行身份验证。这适用于除 GitHub 应用程序之外的大多数用例。
client := github.NewClient(nil).WithAuthToken("... 你的访问令牌 ...")
请注意,使用经过身份验证的客户端时,客户端发出的所有调用都将包含指定的 OAuth 令牌。因此,经过身份验证的客户端几乎不应该在不同用户之间共享。
对于需要 HTTP 基本身份验证的 API 方法,请使用 BasicAuthTransport
。
作为 GitHub 应用程序
GitHub 应用程序身份验证可以由不同的包提供,如 ghinstallation 或 go-githubauth。
注意:大多数端点(例如
GET /rate_limit
)需要访问令牌身份验证,而少数其他端点(例如GET /app/hook/deliveries
)需要 JWT 身份验证。
ghinstallation
提供了 Transport
,它实现了 http.RoundTripper
以提供 GitHub 应用程序安装的身份验证。
以下是使用 ghinstallation
包进行 GitHub 应用程序身份验证的示例:
import (
"net/http"
"github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v64/github"
)
func main() {
// 为集成 ID 1 和安装 ID 99 包装共享传输
itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem")
// 或者对于需要 JWT 身份验证的端点
// itr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem")
if err != nil {
// 处理错误
}
// 使用安装传输创建客户端
client := github.NewClient(&http.Client{Transport: itr})
// 使用客户端...
}
go-githubauth
实现了一组 oauth2.TokenSource
,用于 oauth2.Client
。可以将 oauth2.Client
注入到 github.Client
中以对请求进行身份验证。
使用 go-githubauth
的其他示例:
package main
import (
"context"
"fmt"
"os"
"strconv"
"github.com/google/go-github/v64/github"
"github.com/jferrl/go-githubauth"
"golang.org/x/oauth2"
)
func main() {
privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY"))
appTokenSource, err := githubauth.NewApplicationTokenSource(1112, privateKey)
if err != nil {
fmt.Println("创建应用程序令牌源时出错:", err)
return
}
installationTokenSource := githubauth.NewInstallationTokenSource(1113, appTokenSource)
// oauth2.NewClient 使用 oauth2.ReuseTokenSource 重用令牌直到过期。
// 令牌过期时会自动刷新。
// InstallationTokenSource 具有在令牌过期时刷新的机制。
httpClient := oauth2.NewClient(context.Background(), installationTokenSource)
client := github.NewClient(httpClient)
}
注意:为了与某些 API 交互,例如向仓库写入文件,必须使用 GitHub 应用程序的安装 ID 生成安装令牌,并使用上述 OAuth 方法进行身份验证。请参阅示例。
速率限制
GitHub 对所有 API 客户端施加速率限制。未经身份验证的客户端每小时限制 60 个请求,而经过身份验证的客户端每小时可以发出多达 5,000 个请求。搜索 API 有自定义的速率限制。未经身份验证的客户端每分钟限制 10 个请求,而经过身份验证的客户端每分钟可以发出多达 30 个请求。要在发出非代表用户的调用时获得更高的速率限制,请使用 UnauthenticatedRateLimitedTransport
。
返回的 Response.Rate
值包含最近一次 API 调用的速率限制信息。如果没有足够近期的响应可用,可以使用 RateLimits
获取客户端最新的速率限制数据。
要检测 API 速率限制错误,可以检查其类型是否为 *github.RateLimitError
:
repos, _, err := client.Repositories.List(ctx, "", nil)
if _, ok := err.(*github.RateLimitError); ok {
log.Println("达到速率限制")
}
了解更多关于 GitHub 速率限制的信息,请访问 https://docs.github.com/en/rest/rate-limit 。
除了这些速率限制外,GitHub 还对所有 API 客户端施加了二级速率限制。这个速率限制防止客户端发出过多的并发请求。
要检测 API 二级速率限制错误,可以检查其类型是否为 *github.AbuseRateLimitError
:
repos, _, err := client.Repositories.List(ctx, "", nil)
if _, ok := err.(*github.AbuseRateLimitError); ok {
log.Println("达到二级速率限制")
}
或者,你可以使用 context.WithValue
方法阻塞直到速率限制重置:
repos, _, err := client.Repositories.List(context.WithValue(ctx, github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "", nil)
你可以使用 go-github-ratelimit 来处理二级速率限制的休眠和重试。
了解更多关于 GitHub 二级速率限制的信息,请访问 https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits 。
接受状态
某些端点可能会返回 202 Accepted 状态码,这意味着所需信息尚未准备好,已安排在 GitHub 端收集。已知具有这种行为的方法都有相应的文档说明。
要检测这种错误情况,可以检查其类型是否为 *github.AcceptedError
:
stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo)
if _, ok := err.(*github.AcceptedError); ok {
log.Println("已在 GitHub 端安排")
}
条件请求
GitHub API 对条件请求有很好的支持,这有助于防止你耗尽速率限制,并加快应用程序的速度。go-github
不直接处理条件请求,而是设计为与缓存 http.Transport
一起工作。我们建议使用 https://github.com/gregjones/httpcache 。例如:
import "github.com/gregjones/httpcache"
client := github.NewClient(
httpcache.NewMemoryCacheTransport().Client()
).WithAuthToken(os.Getenv("GITHUB_TOKEN"))
了解更多关于 GitHub 条件请求的信息,请访问 https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#use-conditional-requests-if-appropriate
创建和更新资源
所有 GitHub 资源的结构体对所有非重复字段都使用指针值。这允许区分未设置的字段和设置为零值的字段。已提供辅助函数来轻松创建字符串、布尔和整数值的指针。例如:
// 创建一个名为 "foo" 的新私有仓库
repo := &github.Repository{
Name: github.String("foo"),
Private: github.Bool(true),
}
client.Repositories.Create(ctx, "", repo)
使用过 protocol buffers 的用户应该对这种模式很熟悉。
分页
所有资源集合(仓库、拉取请求、问题等)的请求都支持分页。分页选项在 github.ListOptions
结构中描述,并直接传递给列表方法,或作为更具体的列表选项结构的嵌入类型(例如 github.PullRequestListOptions
)。页面信息可通过 github.Response
结构获得。
client := github.NewClient(nil)
opt := &github.RepositoryListByOrgOptions{
ListOptions: github.ListOptions{PerPage: 10},
}
// 获取所有页面的结果
var allRepos []*github.Repository
for {
repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt)
if err != nil {
return err
}
allRepos = append(allRepos, repos...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
迭代器(实验性)
Go v1.23 引入了新的 iter
包。
使用 enrichman/gh-iter
包,可以为 go-github
创建迭代器。迭代器将为你处理分页,遍历所有可用的结果。
client := github.NewClient(nil)
var allRepos []*github.Repository
// 创建一个迭代器并开始遍历所有结果
repos := ghiter.NewFromFn1(client.Repositories.ListByOrg, "github")
for repo := range repos.All() {
allRepos = append(allRepos, repo)
}
有关 enrichman/gh-iter
的完整用法,请参阅完整的包文档。
Webhooks
go-github
为几乎所有 GitHub webhook 事件 提供了结构体,以及用于验证它们和从 http.Request
结构解析 JSON 负载的函数。
func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
payload, err := github.ValidatePayload(r, s.webhookSecretKey)
if err != nil { ... }
event, err := github.ParseWebHook(github.WebHookType(r), payload)
if err != nil { ... }
switch event := event.(type) {
case *github.CommitCommentEvent:
processCommitCommentEvent(event)
case *github.CreateEvent:
processCreateEvent(event)
...
}
}
此外,还有像 cbrgm/githubevents 这样的库,它们在上面的示例基础上构建,提供了订阅特定事件回调的函数。
有关 go-github 的完整用法,请参阅完整的包文档。
测试使用 go-github
的代码
migueleliasweb/go-github-mock 仓库提供了一种模拟响应的方法。查看该仓库以了解更多详情。
集成测试
你可以从 test
目录运行集成测试。请参阅集成测试的 README。
贡献
我希望涵盖整个 GitHub API,当然始终欢迎贡献。调用模式已经很好地确立了,所以添加新方法相对简单。有关详细信息,请参阅 CONTRIBUTING.md
。
版本控制
总的来说,go-github 尽可能遵循 semver 来标记包的发布版本。对于独立的库,语义版本控制的应用相对简单且普遍理解。但由于 go-github 是 GitHub API 的客户端库,而 GitHub API 本身会改变行为,并且我们通常会积极实现 GitHub API 的预览功能,我们采用了以下版本控制策略:
- 对非预览功能的任何不兼容更改,包括对导出的 Go API 表面或 API 行为的更改,我们都会增加主版本号。
- 对功能的任何向后兼容的更改,以及 GitHub API 中预览功能的任何更改,我们都会增加次版本号。GitHub 不保证预览功能的稳定性,因此我们也不将其视为 go-github API 的稳定部分。
- 对任何向后兼容的错误修复,我们都会增加补丁版本号。
预览功能可能表现为整个方法,或者仅仅是从非预览方法返回的额外数据。有关预览功能的详细信息,请参阅 GitHub API 文档。
日历版本控制
截至2022年11月28日,GitHub 已宣布他们开始基于"日历版本控制"对其v3 API进行版本管理。
实际上,我们的目标是使核心库中的每个方法的版本覆盖变得罕见且临时。
根据我们对GitHub文档的理解,即使只有少数方法有破坏性更改,他们也会将整个API更新到每个新的基于日期的版本。其他方法将接受新版本,保持现有功能。因此,当GitHub API发布新的基于日期的版本时,我们(仓库维护者)计划:
-
更新每个有破坏性更改的方法,覆盖它们的每个方法API版本头。这可能会在一个或多个提交和PR中完成,全部在主分支中进行。
-
一旦所有有破坏性更改的方法都已更新,进行最后一次提交,提升默认API版本,并移除所有每个方法的覆盖。这将在下一次go-github发布时获得主版本号的提升。
版本兼容性表
以下表格列出了本仓库(go-github)的当前和过去版本所支持的GitHub API版本。48.2.0之前的版本未列出。
go-github 版本 | GitHub v3 API 版本 |
---|---|
64.0.0 | 2022-11-28 |
63.0.0 | 2022-11-28 |
62.0.0 | 2022-11-28 |
61.0.0 | 2022-11-28 |
60.0.0 | 2022-11-28 |
59.0.0 | 2022-11-28 |
58.0.0 | 2022-11-28 |
57.0.0 | 2022-11-28 |
56.0.0 | 2022-11-28 |
55.0.0 | 2022-11-28 |
54.0.0 | 2022-11-28 |
53.2.0 | 2022-11-28 |
53.1.0 | 2022-11-28 |
53.0.0 | 2022-11-28 |
52.0.0 | 2022-11-28 |
51.0.0 | 2022-11-28 |
50.2.0 | 2022-11-28 |
50.1.0 | 2022-11-28 |
50.0.0 | 2022-11-28 |
49.1.0 | 2022-11-28 |
49.0.0 | 2022-11-28 |
48.2.0 | 2022-11-28 |
许可证
本库在LICENSE文件中的BSD风格许可证下分发。