google/go-github
GitHub: google/go-github
这是一个由 Google 维护的 Go 语言客户端库,旨在为开发者提供完整且高效的 GitHub REST API v3 交互接口。
Stars: 11189 | Forks: 2224
# go-github
[](https://github.com/google/go-github/releases)
[](https://pkg.go.dev/github.com/google/go-github/v84/github)
[](https://github.com/google/go-github/actions/workflows/tests.yml)
[](https://codecov.io/gh/google/go-github)
[](https://groups.google.com/group/go-github)
[](https://bestpractices.coreinfrastructure.org/projects/796)
go-github 是一个用于访问 [GitHub API v3][] 的 Go 客户端库。
go-github 遵循 [Go 的版本支持政策][support-policy],支持 Go 最新两个主要版本的任意次版本,go.mod 中的 go 指令反映了这一点。
如果不必要,我们会尽力不破坏旧版本的 Go,但我们不会显式测试旧版本。从 Go 1.23 开始,go.mod 中的 go 指令声明了使用此模块所需的硬性最低 Go 版本,且该版本必须大于或等于所有依赖的 go 行版本,因此 go-github 默认将需要 Go 的 N-1 主要版本。
## 开发
如果您有兴趣使用 [GraphQL API v4][],推荐的库是 [shurcooL/githubv4][]。
## 安装
go-github 与现代 Go 版本的模块模式兼容,安装 Go 后:
```
go get github.com/google/go-github/v84
```
将解析该软件包及其依赖项,并将其添加到当前开发模块中。
或者,如果您在软件包中使用 import,也可以实现相同的效果:
```
import "github.com/google/go-github/v84/github"
```
然后运行不带参数的 `go get`。
最后,要使用此仓库的最新 trunk 版本,请使用以下命令:
```
go get github.com/google/go-github/v84@master
```
要发现自上一个版本以来发生的所有更改,您可以先克隆该仓库,然后运行(例如):
```
go run tools/gen-release-notes/main.go --tag v84.0.0
```
## 用法
```
import "github.com/google/go-github/v84/github"
```
构造一个新的 GitHub 客户端,然后使用客户端上的各种服务来访问 GitHub API 的不同部分。例如:
```
client := github.NewClient(nil)
// list all organizations for user "willnorris"
orgs, _, err := client.Organizations.List(context.Background(), "willnorris", nil)
```
某些 API 方法具有可以传递的可选参数。例如:
```
client := github.NewClient(nil)
// list public repositories for org "github"
opt := &github.RepositoryListByOrgOptions{Type: "public"}
repos, _, err := client.Repositories.ListByOrg(context.Background(), "github", opt)
```
客户端的服务将 API 划分为逻辑块,并对应于 [GitHub API 文档](https://docs.github.com/en/rest) 的结构。
注意:使用 [context](https://pkg.go.dev/context) 包,可以轻松地将取消信号和截止日期传递给客户端的各个服务以处理请求。如果没有可用的 context,则可以使用 `context.Background()` 作为起点。
有关更多示例代码片段,请前往 [example](https://github.com/google/go-github/tree/master/example) 目录。
### 身份验证
使用 `WithAuthToken` 方法配置您的客户端以使用 OAuth 令牌(例如 [personal access token][])进行身份验证。除了 GitHub Apps 之外,这是大多数用例所必需的。
```
client := github.NewClient(nil).WithAuthToken("... your access token ...")
```
请注意,当使用经过身份验证的 Client 时,客户端发出的所有调用都将包含指定的 OAuth 令牌。因此,经过身份验证的客户端几乎绝不应在不同用户之间共享。
对于需要 HTTP Basic Authentication 的 API 方法,请使用 [`BasicAuthTransport`](https://pkg.go.dev/github.com/google/go-github/v84/github#BasicAuthTransport)。
#### 作为 GitHub App
GitHub Apps 身份验证可以由不同的包提供,例如 [bradleyfalzon/ghinstallation](https://github.com/bradleyfalzon/ghinstallation)
或 [jferrl/go-githubauth](https://github.com/jferrl/go-githubauth)。
`ghinstallation` 提供了 `Transport`,它实现了 `http.RoundTripper` 以作为 GitHub Apps 的 installation 提供身份验证。
以下是使用 `ghinstallation` 包作为 GitHub App 进行身份验证的示例:
```
import (
"net/http"
"github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v84/github"
)
func main() {
// Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99.
itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem")
// Or for endpoints that require JWT authentication
// itr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem")
if err != nil {
// Handle error.
}
// Use installation transport with client.
client := github.NewClient(&http.Client{Transport: itr})
// Use client...
}
```
`go-githubauth` 实现了一组用于与 `oauth2.Client` 一起使用的 `oauth2.TokenSource`。可以将 `oauth2.Client` 注入到 `github.Client` 中以对请求进行身份验证。
另一个使用 `go-githubauth` 的示例:
```
package main
import (
"context"
"fmt"
"os"
"strconv"
"github.com/google/go-github/v84/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("Error creating application token source:", err)
return
}
installationTokenSource := githubauth.NewInstallationTokenSource(1113, appTokenSource)
// oauth2.NewClient uses oauth2.ReuseTokenSource to reuse the token until it expires.
// The token will be automatically refreshed when it expires.
// InstallationTokenSource has the mechanism to refresh the token when it expires.
httpClient := oauth2.NewClient(context.Background(), installationTokenSource)
client := github.NewClient(httpClient)
}
```
*注意*:为了与某些 API 交互,例如将文件写入仓库,必须使用 GitHub app 的 installation ID 生成 installation 令牌,并使用上述提到的 OAuth 方法进行身份验证。请参阅示例。
### 速率限制
GitHub 对所有 API 客户端实施速率限制。[主要速率限制](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#about-primary-rate-limits)
是指客户端在特定时间内可以发出的 REST API 请求数量的限制。此限制有助于防止滥用和拒绝服务攻击,并确保 API 对所有用户保持可用。某些端点(例如搜索端点)具有限制更严格的限制。
未经身份验证的客户端可以请求公共数据,但速率限制较低,而经过身份验证的客户端的速率限制则基于客户端身份。
除了主要速率限制外,GitHub 还实施 [次要速率限制](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#about-secondary-rate-limits)
以防止滥用并确保 API 对所有用户可用。次要速率限制通常限制客户端可以发出的并发请求数量。
客户端返回的 `Response.Rate` 值包含来自最近一次 API 调用的速率限制信息。如果没有足够新的响应,您可以使用客户端 `RateLimits` 服务来获取客户端的最新速率限制数据。
要检测主要 API 速率限制错误,您可以检查错误是否为 `RateLimitError`。
```
repos, _, err := client.Repositories.List(ctx, "", nil)
var rateErr *github.RateLimitError
if errors.As(err, &rateErr) {
log.Printf("hit primary rate limit, used %v of %v\n", rateErr.Rate.Used, rateErr.Rate.Limit)
}
```
要检测 API 次要速率限制错误,您可以检查错误是否为 `AbuseRateLimitError`。
```
repos, _, err := client.Repositories.List(ctx, "", nil)
var rateErr *github.AbuseRateLimitError
if errors.As(err, &rateErr) {
log.Printf("hit secondary rate limit, retry after %v\n", rateErr.RetryAfter)
}
```
如果遇到主要速率限制,您可以使用 `SleepUntilPrimaryRateLimitResetWhenRateLimited`
方法进行阻塞,直到速率限制重置。
```
repos, _, err := client.Repositories.List(context.WithValue(ctx, github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "", nil)
```
如果即使达到速率限制也需要发出请求,您可以使用
`BypassRateLimitCheck` 方法绕过速率限制检查并强制发出请求。
```
repos, _, err := client.Repositories.List(context.WithValue(ctx, github.BypassRateLimitCheck, true), "", nil)
```
对于更高级的用例,您可以使用 [gofri/go-github-ratelimit](https://github.com/gofri/go-github-ratelimit)
它提供了一个处理 GitHub API 的主要速率限制和次要速率限制的中间件 (`http.RoundTripper`)。在这种情况下,您可以将客户端 `DisableRateLimitCheck` 设置为 `true`,以便客户端不跟踪速率限制的使用情况。
如果客户端是 [OAuth app](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#primary-rate-limit-for-oauth-apps)
您可以使用应用的更高速率限制,通过使用
`UnauthenticatedRateLimitedTransport` 以应用身份而不是用户身份进行调用来请求公共数据。
### Accepted 状态
某些端点可能返回 202 Accepted 状态代码,这意味着所需的信息尚未准备好,并已被安排在 GitHub 端收集。已知具有这种行为的方法在文档中说明了这种行为。
要检测这种错误情况,您可以检查其类型是否为 `*github.AcceptedError`:
```
stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo)
if errors.As(err, new(*github.AcceptedError)) {
log.Println("scheduled on GitHub side")
}
```
### 条件请求
GitHub REST API 通过 `ETag` 标头对[条件 HTTP 请求](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)
提供了良好的支持,这将有助于防止您耗尽速率限制,并有助于加快应用程序的速度。`go-github` 不直接处理条件请求,而是设计为与缓存 `http.Transport` 一起使用。
通常,建议使用符合 [RFC 9111](https://datatracker.ietf.org/doc/html/rfc9111)
的 HTTP 缓存,例如 [bartventer/httpcache](https://github.com/bartventer/httpcache)
,例如:
```
import (
"github.com/bartventer/httpcache"
_ "github.com/bartventer/httpcache/store/memcache" // Register the in-memory backend
)
client := github.NewClient(
httpcache.NewClient("memcache://"),
).WithAuthToken(os.Getenv("GITHUB_TOKEN"))
```
或者,[bored-engineer/github-conditional-http-transport](https://github.com/bored-engineer/github-conditional-http-transport)
包依赖于(未记录的)GitHub 特定缓存逻辑,在使用短期凭据(例如
[GitHub App installation token](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation))发出请求时推荐使用该包。
### 创建和更新资源
GitHub 资源的所有结构体都对所有非重复字段使用指针值。这允许区分未设置的字段和设置为零值的字段。提供了辅助函数以便轻松地为 string、bool 和 int 值创建这些指针。例如:
```
// create a new private repository named "foo"
repo := &github.Repository{
Name: github.Ptr("foo"),
Private: github.Ptr(true),
}
client.Repositories.Create(ctx, "", repo)
```
使用过 protocol buffers 的用户应该会发现这种模式很熟悉。
### 分页
对资源集合(repos、pull requests、issues 等)的所有请求都支持分页。使用页码的分页选项在 `github.ListOptions` 结构体中描述,并直接传递给 list 方法,或者作为更特定的 list options 结构体的嵌入类型(例如 `github.PullRequestListOptions`)。页面信息可通过 `github.Response` 结构体获取。
```
client := github.NewClient(nil)
opt := &github.RepositoryListByOrgOptions{
ListOptions: github.ListOptions{PerPage: 10},
}
// get all pages of results
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
}
```
使用字符串游标的分页选项在 `github.ListCursorOptions`
结构体中描述,并直接传递给 list 方法,或者作为
更特定的 list cursor options 结构体的嵌入类型(例如
`github.ListGlobalSecurityAdvisoriesOptions`)。同样,游标和页面信息可通过 `github.Response` 结构体获取。
#### 迭代器
Go v1.23 引入了新的 `iter` 包。
新的 `github/gen-iterators.go` 文件会自动在 `github/github-iterators.go` 中为所有支持页码迭代(使用每个响应中的 `NextPage` 字段)或字符串游标迭代(使用每个响应中的 `After` 字段)的方法生成 "*Iter" 方法。
要处理速率限制问题,请确保使用支持速率限制的 transport。
(有关更多详细信息,请参阅上面的 [速率限制](/#rate-limiting)。)
要使用这些方法,只需创建一个迭代器,然后对其进行 range,例如:
```
client := github.NewClient(nil)
var allRepos []*github.Repository
// create an iterator and start looping through all the results
iter := client.Repositories.ListIter(ctx, "github", nil)
for repo, err := range iter {
if err != nil {
log.Fatal(err)
}
allRepos = append(allRepos, repo)
}
```
或者,如果您希望使用外部包,可以使用 `enrichman/gh-iter`。
它的迭代器将为您处理分页,循环遍历所有可用结果。
```
client := github.NewClient(nil)
var allRepos []*github.Repository
// create an iterator and start looping through all the results
repos := ghiter.NewFromFn1(client.Repositories.ListByOrg, "github")
for repo := range repos.All() {
allRepos = append(allRepos, repo)
}
```
有关 `enrichman/gh-iter` 的完整用法,请参阅完整的 [package docs](https://github.com/enrichman/gh-iter)。
#### 中间件
您可以使用 [gofri/go-github-pagination](https://github.com/gofri/go-github-pagination) 来为您处理
分页。它支持同步和异步模式以及自定义。
默认情况下,中间件会自动对所有页面进行分页,聚合结果,并将它们作为数组返回。
用法请参阅 `example/ratelimit/main.go`。
### Webhooks
`go-github` 为几乎所有 [GitHub webhook events][] 提供了结构体,以及用于验证它们和从 `http.Request` 结构体解组 JSON payloads 的函数。
```
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][] 这样的库建立在上述示例之上,并提供函数来将 callbacks 订阅到特定事件。
有关 go-github 的完整用法,请参阅完整的 [package docs][]。
### 测试使用 `go-github` 的代码
仓库 [migueleliasweb/go-github-mock](https://github.com/migueleliasweb/go-github-mock) 提供了一种模拟响应的方法。查看该仓库以了解更多详细信息。
### 集成测试
您可以从 `test` 目录运行集成测试。请参阅集成测试 [README](test/README.md)。
## 版本控制
通常,go-github 在标记包的发布时尽可能遵循 [semver](https://semver.org/)。对于自包含的库,语义版本控制的应用相对简单且通常被理解。但由于 go-github 是 GitHub API 的客户端库,而 GitHub API 本身会更改行为,并且因为我们通常相当激进地实现 GitHub API 的预览功能,我们采用了以下版本控制策略:
* 当对非预览功能进行任何不兼容的更改(包括对导出的 Go API surface 或 API 行为的更改)时,我们会增加 **major version**。
* 当对功能进行任何向后兼容的更改以及对 GitHub API 中预览功能的任何更改时,我们会增加 **minor version**。GitHub 不保证预览功能的稳定性,因此我们也不认为它是 go-github API 的稳定部分。
* 当进行任何向后兼容的错误修复时,我们会增加 **patch version**。
预览功能可能采用整个方法的形式,或者仅仅是从非预览方法返回的额外数据。有关预览功能的详细信息,请参阅 GitHub API 文档。
### 日历版本控制
截至 2022-11-28,GitHub [已宣布](https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/)
他们开始基于“日历版本控制”对其 v3 API 进行版本控制。
实际上,我们的目标是使每个方法的版本覆盖(至少在核心库中)变得罕见且暂时。
我们对 GitHub 文档的理解是,他们将把整个 API 更新到每个新的基于日期的版本,即使只有少数方法有重大更改。其他方法将在其现有功能下接受新版本。因此,当发布新的基于日期版本的 GitHub API 时,我们(仓库维护者)计划:
* 更新每个有重大更改的方法,覆盖其每个方法的 API version 标头。这可能发生在一个或多个 commits 和 PRs 中,并且都在 main 分支中完成。
* 一旦所有有重大更改的方法都已更新,
进行最后一次提交以提升默认 API 版本,并删除
所有每个方法的覆盖。当制作下一个 go-github 版本时,这将获得 major version 提升。
### 版本兼容性表
下表标识了此(和过去)版本的仓库 (go-github) 支持
哪个版本的 GitHub API。
48.2.0 之前的版本未列出。
| go-github Version | GitHub v3 API Version |
| ----------------- | --------------------- |
| 84.0.0 | 2022-11-28 |
| ... | 2022-11-28 |
| 48.2.0 | 2022-11-28 |
## 许可证
此库根据 [LICENSE](./LICENSE)
文件中找到的 BSD 风格许可证进行分发。
标签:API客户端, DNS解析, Google, Go模块, Go语言, Nuclei, REST API, SOC Prime, v3 API, 代码托管, 安全可观测性, 开发工具, 开源项目, 日志审计, 漏洞探测, 版本控制, 程序破解, 第三方库