diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index c303f23468..cf4edcb6ad 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v3 - name: golangci-lint ${{ matrix.working-directory }} - uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 #v3.3.1 + uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 #v3.4.0 with: version: v1.50.1 working-directory: ${{ matrix.working-directory}} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3a0366d42e..d4a197746b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,3 +1,7 @@ +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: push: branches: @@ -15,9 +19,6 @@ permissions: jobs: test: - permissions: - actions: write # for styfle/cancel-workflow-action to cancel/stop running workflows - contents: read # for actions/checkout to fetch code strategy: matrix: go-version: [1.x, 1.18.x] @@ -35,11 +36,6 @@ jobs: runs-on: ${{ matrix.platform }} steps: - - name: Cancel previous - uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 #0.11.0 - with: - access_token: ${{ github.token }} - - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} diff --git a/.golangci.yml b/.golangci.yml index db9a4d7117..4d3fc17813 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,7 +21,9 @@ linters: - staticcheck - ineffassign - unused -issues: +issues: + exclude: + - composites exclude-rules: - linters: - dogsled diff --git a/AUTHORS b/AUTHORS index 47e85bdb78..5e40cd1f38 100644 --- a/AUTHORS +++ b/AUTHORS @@ -152,6 +152,7 @@ Florian Wagner Francesc Gil Francis Francisco Guimarães +François de Metz Fredrik Jönsson Gabriel Garrett Squire @@ -261,6 +262,7 @@ Martins Sipenko Marwan Sulaiman Masayuki Izumi Mat Geist +Matija Horvat Matin Rahmanian Matt Matt Brender @@ -274,6 +276,7 @@ Michael Tiller Michał Glapa Michelangelo Morrillo Miguel Elias dos Santos +Mike Chen Mohammed AlDujaili Mukundan Senthil Munia Balayil @@ -304,6 +307,7 @@ parkhyukjun89 Pat Alwell Patrick DeVivo Patrick Marabeas +Pavel Dvoinos Pavel Shtanko Pete Wagner Petr Shevtsov @@ -359,6 +363,7 @@ Sasha Melentyev Sean Wang Sebastian Mandrean Sebastian Mæland Pedersen +Sergei Popinevskii Sergey Romanov Sergio Garcia Seth Vargo @@ -399,6 +404,7 @@ tkhandel Tobias Gesellchen Tom Payne Trey Tacon +tsbkw ttacon Vaibhav Singh Varadarajan Aravamudhan diff --git a/README.md b/README.md index 0f99698d17..461ae4d02d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # go-github # [![go-github release (latest SemVer)](https://img.shields.io/github/v/release/google/go-github?sort=semver)](https://github.com/google/go-github/releases) -[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/google/go-github/v48/github) +[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/google/go-github/v50/github) [![Test Status](https://github.com/google/go-github/workflows/tests/badge.svg)](https://github.com/google/go-github/actions?query=workflow%3Atests) [![Test Coverage](https://codecov.io/gh/google/go-github/branch/master/graph/badge.svg)](https://codecov.io/gh/google/go-github) [![Discuss at go-github@googlegroups.com](https://img.shields.io/badge/discuss-go--github%40googlegroups.com-blue.svg)](https://groups.google.com/group/go-github) @@ -24,7 +24,7 @@ If you're interested in using the [GraphQL API v4][], the recommended library is go-github is compatible with modern Go releases in module mode, with Go installed: ```bash -go get github.com/google/go-github/v48 +go get github.com/google/go-github/v50 ``` will resolve and add the package to the current development module, along with its dependencies. @@ -32,7 +32,7 @@ will resolve and add the package to the current development module, along with i Alternatively the same can be achieved if you use import in a package: ```go -import "github.com/google/go-github/v48/github" +import "github.com/google/go-github/v50/github" ``` and run `go get` without parameters. @@ -40,13 +40,13 @@ and run `go get` without parameters. Finally, to use the top-of-trunk version of this repo, use the following command: ```bash -go get github.com/google/go-github/v48@master +go get github.com/google/go-github/v50@master ``` ## Usage ## ```go -import "github.com/google/go-github/v48/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) +import "github.com/google/go-github/v50/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) import "github.com/google/go-github/github" // with go modules disabled ``` @@ -135,7 +135,7 @@ import ( "net/http" "github.com/bradleyfalzon/ghinstallation/v2" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) func main() { @@ -186,6 +186,24 @@ if _, ok := err.(*github.RateLimitError); ok { Learn more about GitHub rate limiting at https://docs.github.com/en/rest/rate-limit . +In addition to these rate limits, GitHub imposes a secondary rate limit on all API clients. +This rate limit prevents clients from making too many concurrent requests. + +To detect an API secondary rate limit error, you can check if its type is `*github.AbuseRateLimitError`: + +```go +repos, _, err := client.Repositories.List(ctx, "", nil) +if _, ok := err.(*github.AbuseRateLimitError); ok { + log.Println("hit secondary rate limit") +} +``` + +You can use [go-github-ratelimit](https://github.com/gofri/go-github-ratelimit) to handle +secondary rate limit sleep-and-retry for you. + +Learn more about GitHub secondary rate limiting at +https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits . + ### Accepted Status ### Some endpoints may return a 202 Accepted status code, meaning that the @@ -209,7 +227,22 @@ The GitHub API has good support for conditional requests which will help prevent you from burning through your rate limit, as well as help speed up your application. `go-github` does not handle conditional requests directly, but is instead designed to work with a caching `http.Transport`. We recommend using -https://github.com/gregjones/httpcache for that. +https://github.com/gregjones/httpcache for that. For example: + +```go +import "github.com/gregjones/httpcache" + + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}, + ) + tc := &http.Client{ + Transport: &oauth2.Transport{ + Base: httpcache.NewMemoryCacheTransport(), + Source: ts, + }, + } + client := github.NewClient(tc) +``` Learn more about GitHub conditional requests at https://docs.github.com/en/rest/overview/resources-in-the-rest-api#conditional-requests. @@ -290,7 +323,7 @@ For complete usage of go-github, see the full [package docs][]. [oauth2]: https://github.com/golang/oauth2 [oauth2 docs]: https://godoc.org/golang.org/x/oauth2 [personal API token]: https://github.com/blog/1509-personal-api-tokens -[package docs]: https://pkg.go.dev/github.com/google/go-github/v48/github +[package docs]: https://pkg.go.dev/github.com/google/go-github/v50/github [GraphQL API v4]: https://developer.github.com/v4/ [shurcooL/githubv4]: https://github.com/shurcooL/githubv4 [GitHub webhook events]: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads @@ -332,6 +365,42 @@ Preview functionality may take the form of entire methods or simply additional data returned from an otherwise non-preview method. Refer to the GitHub API documentation for details on preview functionality. +### Calendar Versioning ### + +As of 2022-11-28, GitHub [has announced](https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/) +that they are starting to version their v3 API based on "calendar-versioning". + +In practice, our goal is to make per-method version overrides (at +least in the core library) rare and temporary. + +Our understanding of the GitHub docs is that they will be revving the +entire API to each new date-based version, even if only a few methods +have breaking changes. Other methods will accept the new version with +their existing functionality. So when a new date-based version of the +GitHub API is released, we (the repo maintainers) plan to: + +* update each method that had breaking changes, overriding their + per-method API version header. This may happen in one or multiple + commits and PRs, and is all done in the main branch. + +* once all of the methods with breaking changes have been updated, + have a final commit that bumps the default API version, and remove + all of the per-method overrides. That would now get a major version + bump when the next go-github release is made. + +### Version Compatibility Table ### + +The following table identifies which version of the GitHub API is +supported by this (and past) versions of this repo (go-github). +Versions prior to 48.2.0 are not listed. + +| go-github Version | GitHub v3 API Version | +| ----------------- | --------------------- | +| 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 ## This library is distributed under the BSD-style license found in the [LICENSE](./LICENSE) diff --git a/example/actionpermissions/main.go b/example/actionpermissions/main.go index c568d57255..5227fd0205 100644 --- a/example/actionpermissions/main.go +++ b/example/actionpermissions/main.go @@ -14,8 +14,7 @@ import ( "log" "os" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) var ( @@ -36,9 +35,7 @@ func main() { log.Fatal("No owner: owner of repo must be given") } ctx := context.Background() - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, token) actionsPermissionsRepository, _, err := client.Repositories.GetActionsPermissions(ctx, *owner, *name) if err != nil { diff --git a/example/appengine/app.go b/example/appengine/app.go index e4ba5d9612..21260af3f3 100644 --- a/example/appengine/app.go +++ b/example/appengine/app.go @@ -12,8 +12,7 @@ import ( "net/http" "os" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" "google.golang.org/appengine" "google.golang.org/appengine/log" ) @@ -29,11 +28,7 @@ func handler(w http.ResponseWriter, r *http.Request) { } ctx := appengine.NewContext(r) - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: os.Getenv("GITHUB_AUTH_TOKEN")}, - ) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, os.Getenv("GITHUB_AUTH_TOKEN")) commits, _, err := client.Repositories.ListCommits(ctx, "google", "go-github", nil) if err != nil { diff --git a/example/basicauth/main.go b/example/basicauth/main.go index 43604434e2..3624b272fe 100644 --- a/example/basicauth/main.go +++ b/example/basicauth/main.go @@ -22,7 +22,7 @@ import ( "strings" "syscall" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" "golang.org/x/crypto/ssh/terminal" ) diff --git a/example/commitpr/main.go b/example/commitpr/main.go index 1d60625d1c..ad57d69a46 100644 --- a/example/commitpr/main.go +++ b/example/commitpr/main.go @@ -30,8 +30,7 @@ import ( "strings" "time" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) var ( @@ -133,7 +132,7 @@ func pushCommit(ref *github.Reference, tree *github.Tree) (err error) { // Create the commit using the tree. date := time.Now() - author := &github.CommitAuthor{Date: &date, Name: authorName, Email: authorEmail} + author := &github.CommitAuthor{Date: &github.Timestamp{date}, Name: authorName, Email: authorEmail} commit := &github.Commit{Author: author, Message: commitMessage, Tree: tree, Parents: []*github.Commit{parent.Commit}} newCommit, _, err := client.Git.CreateCommit(ctx, *sourceOwner, *sourceRepo, commit) if err != nil { @@ -188,9 +187,7 @@ func main() { if *sourceOwner == "" || *sourceRepo == "" || *commitBranch == "" || *sourceFiles == "" || *authorName == "" || *authorEmail == "" { log.Fatal("You need to specify a non-empty value for the flags `-source-owner`, `-source-repo`, `-commit-branch`, `-files`, `-author-name` and `-author-email`") } - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - tc := oauth2.NewClient(ctx, ts) - client = github.NewClient(tc) + client = github.NewTokenClient(ctx, token) ref, err := getRef() if err != nil { diff --git a/example/go.mod b/example/go.mod index 9d98950535..e268c7ad62 100644 --- a/example/go.mod +++ b/example/go.mod @@ -1,10 +1,11 @@ -module github.com/google/go-github/v48/example +module github.com/google/go-github/v50/example go 1.17 require ( github.com/bradleyfalzon/ghinstallation/v2 v2.0.4 - github.com/google/go-github/v48 v48.0.0 + github.com/gofri/go-github-ratelimit v1.0.1 + github.com/google/go-github/v50 v50.0.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be google.golang.org/appengine v1.6.7 @@ -21,4 +22,4 @@ require ( ) // Use version at HEAD, not the latest published. -replace github.com/google/go-github/v48 => ../ +replace github.com/google/go-github/v50 => ../ diff --git a/example/go.sum b/example/go.sum index a3c96e95f2..1043059ffa 100644 --- a/example/go.sum +++ b/example/go.sum @@ -1,5 +1,7 @@ github.com/bradleyfalzon/ghinstallation/v2 v2.0.4 h1:tXKVfhE7FcSkhkv0UwkLvPDeZ4kz6OXd0PKPlFqf81M= github.com/bradleyfalzon/ghinstallation/v2 v2.0.4/go.mod h1:B40qPqJxWE0jDZgOR1JmaMy+4AY1eBP+IByOvqyAKp0= +github.com/gofri/go-github-ratelimit v1.0.1 h1:sgefSzxhnvwZ+wR9uZ4l9TnjgLuNiwipJVzJL4YLj9A= +github.com/gofri/go-github-ratelimit v1.0.1/go.mod h1:OnCi5gV+hAG/LMR7llGhU7yHt44se9sYgKPnafoL7RY= github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -7,8 +9,8 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg= github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= diff --git a/example/listenvironments/main.go b/example/listenvironments/main.go index ddee688d80..655341e0ce 100644 --- a/example/listenvironments/main.go +++ b/example/listenvironments/main.go @@ -18,8 +18,7 @@ import ( "log" "os" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) func main() { @@ -31,11 +30,8 @@ func main() { log.Fatal("Unauthorized: No token present") } - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - ctx := context.Background() - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, token) expectedPageSize := 2 diff --git a/example/migrations/main.go b/example/migrations/main.go index 096085ce88..daa90d3c08 100644 --- a/example/migrations/main.go +++ b/example/migrations/main.go @@ -12,17 +12,12 @@ import ( "context" "fmt" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) func fetchAllUserMigrations() ([]*github.UserMigration, error) { ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: ""}, - ) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, "") migrations, _, err := client.Migrations.ListUserMigrations(ctx, &github.ListOptions{Page: 1}) return migrations, err diff --git a/example/newfilewithappauth/main.go b/example/newfilewithappauth/main.go index a448a056f5..f67bbe9a96 100644 --- a/example/newfilewithappauth/main.go +++ b/example/newfilewithappauth/main.go @@ -16,7 +16,7 @@ import ( "time" "github.com/bradleyfalzon/ghinstallation/v2" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" "golang.org/x/oauth2" ) diff --git a/example/newrepo/main.go b/example/newrepo/main.go index 46378f9853..65a3573924 100644 --- a/example/newrepo/main.go +++ b/example/newrepo/main.go @@ -16,8 +16,7 @@ import ( "log" "os" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) var ( @@ -37,9 +36,7 @@ func main() { log.Fatal("No name: New repos must be given a name") } ctx := context.Background() - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, token) r := &github.Repository{Name: name, Private: private, Description: description, AutoInit: autoInit} repo, _, err := client.Repositories.Create(ctx, "", r) diff --git a/example/newreposecretwithlibsodium/go.mod b/example/newreposecretwithlibsodium/go.mod index 4519c051e4..8f2bcfcdbc 100644 --- a/example/newreposecretwithlibsodium/go.mod +++ b/example/newreposecretwithlibsodium/go.mod @@ -4,9 +4,9 @@ go 1.15 require ( github.com/GoKillers/libsodium-go v0.0.0-20171022220152-dd733721c3cb - github.com/google/go-github/v48 v48.0.0 + github.com/google/go-github/v50 v50.0.0 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 ) // Use version at HEAD, not the latest published. -replace github.com/google/go-github/v48 => ../.. +replace github.com/google/go-github/v50 => ../.. diff --git a/example/newreposecretwithlibsodium/go.sum b/example/newreposecretwithlibsodium/go.sum index abc73624ad..dfbf7641b9 100644 --- a/example/newreposecretwithlibsodium/go.sum +++ b/example/newreposecretwithlibsodium/go.sum @@ -84,8 +84,8 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= diff --git a/example/newreposecretwithlibsodium/main.go b/example/newreposecretwithlibsodium/main.go index fb66df8368..8d3b17aafd 100644 --- a/example/newreposecretwithlibsodium/main.go +++ b/example/newreposecretwithlibsodium/main.go @@ -36,8 +36,7 @@ import ( "os" sodium "github.com/GoKillers/libsodium-go/cryptobox" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) var ( @@ -71,7 +70,8 @@ func main() { log.Fatal(err) } - ctx, client, err := githubAuth(token) + ctx := context.Background() + client := github.NewTokenClient(ctx, token) if err != nil { log.Fatalf("unable to authorize using env GITHUB_AUTH_TOKEN: %v", err) } @@ -99,18 +99,6 @@ func getSecretValue(secretName string) (string, error) { return secretValue, nil } -// githubAuth returns a GitHub client and context. -func githubAuth(token string) (context.Context, *github.Client, error) { - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) - return ctx, client, nil -} - // addRepoSecret will add a secret to a GitHub repo for use in GitHub Actions. // // Finally, the secretName and secretValue will determine the name of the secret added and it's corresponding value. diff --git a/example/newreposecretwithxcrypto/main.go b/example/newreposecretwithxcrypto/main.go index c4f38f34ec..a0b850b935 100644 --- a/example/newreposecretwithxcrypto/main.go +++ b/example/newreposecretwithxcrypto/main.go @@ -36,9 +36,8 @@ import ( "log" "os" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" "golang.org/x/crypto/nacl/box" - "golang.org/x/oauth2" ) var ( @@ -72,10 +71,8 @@ func main() { log.Fatal(err) } - ctx, client, err := githubAuth(token) - if err != nil { - log.Fatalf("unable to authorize using env GITHUB_AUTH_TOKEN: %v", err) - } + ctx := context.Background() + client := github.NewTokenClient(ctx, token) if err := addRepoSecret(ctx, client, *owner, *repo, secretName, secretValue); err != nil { log.Fatal(err) @@ -100,18 +97,6 @@ func getSecretValue(secretName string) (string, error) { return secretValue, nil } -// githubAuth returns a GitHub client and context. -func githubAuth(token string) (context.Context, *github.Client, error) { - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) - return ctx, client, nil -} - // addRepoSecret will add a secret to a GitHub repo for use in GitHub Actions. // // Finally, the secretName and secretValue will determine the name of the secret added and it's corresponding value. diff --git a/example/ratelimit/main.go b/example/ratelimit/main.go new file mode 100644 index 0000000000..228099faa9 --- /dev/null +++ b/example/ratelimit/main.go @@ -0,0 +1,42 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The ratelimit command demonstrates using the github_ratelimit.SecondaryRateLimitWaiter. +// By using the waiter, the client automatically sleeps and retry requests +// when it hits secondary rate limits. +package main + +import ( + "context" + "fmt" + + "github.com/gofri/go-github-ratelimit/github_ratelimit" + "github.com/google/go-github/v50/github" +) + +func main() { + var username string + fmt.Print("Enter GitHub username: ") + fmt.Scanf("%s", &username) + + rateLimiter, err := github_ratelimit.NewRateLimitWaiterClient(nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + client := github.NewClient(rateLimiter) + + // arbitrary usage of the client + organizations, _, err := client.Organizations.List(context.Background(), username, nil) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + for i, organization := range organizations { + fmt.Printf("%v. %v\n", i+1, organization.GetLogin()) + } +} diff --git a/example/simple/main.go b/example/simple/main.go index ed68b4eead..1940585950 100644 --- a/example/simple/main.go +++ b/example/simple/main.go @@ -12,7 +12,7 @@ import ( "context" "fmt" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) // Fetch all the public organizations' membership of a user. diff --git a/example/tagprotection/main.go b/example/tagprotection/main.go index 09ae4dcfb6..41b605e28f 100644 --- a/example/tagprotection/main.go +++ b/example/tagprotection/main.go @@ -19,9 +19,8 @@ import ( "strings" "syscall" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" "golang.org/x/crypto/ssh/terminal" - "golang.org/x/oauth2" ) func main() { @@ -45,12 +44,7 @@ func main() { token := string(byteToken) ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, token) // create new tag protection if pattern != "" { diff --git a/example/tokenauth/main.go b/example/tokenauth/main.go index c6ef3cd464..1cff2c7879 100644 --- a/example/tokenauth/main.go +++ b/example/tokenauth/main.go @@ -14,9 +14,8 @@ import ( "log" "syscall" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" "golang.org/x/crypto/ssh/terminal" - "golang.org/x/oauth2" ) func main() { @@ -26,12 +25,7 @@ func main() { token := string(byteToken) ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) + client := github.NewTokenClient(ctx, token) user, resp, err := client.Users.Get(ctx, "") if err != nil { diff --git a/example/topics/main.go b/example/topics/main.go index 1033c27648..d8237da53e 100644 --- a/example/topics/main.go +++ b/example/topics/main.go @@ -12,7 +12,7 @@ import ( "context" "fmt" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) // Fetch and lists all the public topics associated with the specified GitHub topic diff --git a/github/actions_cache.go b/github/actions_cache.go new file mode 100644 index 0000000000..9592d9ab62 --- /dev/null +++ b/github/actions_cache.go @@ -0,0 +1,235 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ActionsCache represents a GitHub action cache. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#about-the-cache-api +type ActionsCache struct { + ID *int64 `json:"id,omitempty" url:"-"` + Ref *string `json:"ref,omitempty" url:"ref"` + Key *string `json:"key,omitempty" url:"key"` + Version *string `json:"version,omitempty" url:"-"` + LastAccessedAt *Timestamp `json:"last_accessed_at,omitempty" url:"-"` + CreatedAt *Timestamp `json:"created_at,omitempty" url:"-"` + SizeInBytes *int64 `json:"size_in_bytes,omitempty" url:"-"` +} + +// ActionsCacheList represents a list of GitHub actions Cache. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository +type ActionsCacheList struct { + TotalCount int `json:"total_count"` + ActionsCaches []*ActionsCache `json:"actions_caches,omitempty"` +} + +// ActionsCacheUsage represents a GitHub Actions Cache Usage object. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-a-repository +type ActionsCacheUsage struct { + FullName string `json:"full_name"` + ActiveCachesSizeInBytes int64 `json:"active_caches_size_in_bytes"` + ActiveCachesCount int `json:"active_caches_count"` +} + +// ActionsCacheUsageList represents a list of repositories with GitHub Actions cache usage for an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-a-repository +type ActionsCacheUsageList struct { + TotalCount int `json:"total_count"` + RepoCacheUsage []*ActionsCacheUsage `json:"repository_cache_usages,omitempty"` +} + +// TotalCacheUsage represents total GitHub actions cache usage of an organization or enterprise. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-an-enterprise +type TotalCacheUsage struct { + TotalActiveCachesUsageSizeInBytes int64 `json:"total_active_caches_size_in_bytes"` + TotalActiveCachesCount int `json:"total_active_caches_count"` +} + +// ActionsCacheListOptions represents a list of all possible optional Query parameters for ListCaches method. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository +type ActionsCacheListOptions struct { + ListOptions + // The Git reference for the results you want to list. + // The ref for a branch can be formatted either as refs/heads/ + // or simply . To reference a pull request use refs/pull//merge + Ref *string `url:"ref,omitempty"` + Key *string `url:"key,omitempty"` + // Can be one of: "created_at", "last_accessed_at", "size_in_bytes". Default: "last_accessed_at" + Sort *string `url:"sort,omitempty"` + // Can be one of: "asc", "desc" Default: desc + Direction *string `url:"direction,omitempty"` +} + +// ListCaches lists the GitHub Actions caches for a repository. +// You must authenticate using an access token with the repo scope to use this endpoint. +// +// Permissions: must have the actions:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository +func (s *ActionsService) ListCaches(ctx context.Context, owner, repo string, opts *ActionsCacheListOptions) (*ActionsCacheList, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/caches", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + actionCacheList := new(ActionsCacheList) + resp, err := s.client.Do(ctx, req, actionCacheList) + if err != nil { + return nil, resp, err + } + + return actionCacheList, resp, nil +} + +// DeleteCachesByKey deletes one or more GitHub Actions caches for a repository, using a complete cache key. +// By default, all caches that match the provided key are deleted, but you can optionally provide +// a Git ref to restrict deletions to caches that match both the provided key and the Git ref. +// The ref for a branch can be formatted either as "refs/heads/" or simply "". +// To reference a pull request use "refs/pull//merge". If you don't want to use ref just pass nil in parameter. +// +// Permissions: You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have the actions:write permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-github-actions-caches-for-a-repository-using-a-cache-key +func (s *ActionsService) DeleteCachesByKey(ctx context.Context, owner, repo, key string, ref *string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/caches", owner, repo) + u, err := addOptions(u, ActionsCache{Key: &key, Ref: ref}) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// DeleteCachesByID deletes a GitHub Actions cache for a repository, using a cache ID. +// +// Permissions: You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have the actions:write permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id +func (s *ActionsService) DeleteCachesByID(ctx context.Context, owner, repo string, cacheID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/caches/%v", owner, repo, cacheID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// GetCacheUsageForRepo gets GitHub Actions cache usage for a repository. The data fetched using this API is refreshed approximately every 5 minutes, +// so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: Anyone with read access to the repository can use this endpoint. If the repository is private, you must use an +// access token with the repo scope. GitHub Apps must have the actions:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-a-repository +func (s *ActionsService) GetCacheUsageForRepo(ctx context.Context, owner, repo string) (*ActionsCacheUsage, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/cache/usage", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + cacheUsage := new(ActionsCacheUsage) + res, err := s.client.Do(ctx, req, cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} + +// ListCacheUsageByRepoForOrg lists repositories and their GitHub Actions cache usage for an organization. The data fetched using this API is +// refreshed approximately every 5 minutes, so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: You must authenticate using an access token with the read:org scope to use this endpoint. +// GitHub Apps must have the organization_admistration:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#list-repositories-with-github-actions-cache-usage-for-an-organization +func (s *ActionsService) ListCacheUsageByRepoForOrg(ctx context.Context, org string, opts *ListOptions) (*ActionsCacheUsageList, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/cache/usage-by-repository", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + cacheUsage := new(ActionsCacheUsageList) + res, err := s.client.Do(ctx, req, cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} + +// GetTotalCacheUsageForOrg gets the total GitHub Actions cache usage for an organization. The data fetched using this API is refreshed approximately every +// 5 minutes, so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: You must authenticate using an access token with the read:org scope to use this endpoint. +// GitHub Apps must have the organization_admistration:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-an-organization +func (s *ActionsService) GetTotalCacheUsageForOrg(ctx context.Context, org string) (*TotalCacheUsage, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/cache/usage", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + cacheUsage := new(TotalCacheUsage) + res, err := s.client.Do(ctx, req, cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} + +// GetTotalCacheUsageForEnterprise gets the total GitHub Actions cache usage for an enterprise. The data fetched using this API is refreshed approximately every 5 minutes, +// so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: You must authenticate using an access token with the "admin:enterprise" scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-an-enterprise +func (s *ActionsService) GetTotalCacheUsageForEnterprise(ctx context.Context, enterprise string) (*TotalCacheUsage, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/cache/usage", enterprise) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + cacheUsage := new(TotalCacheUsage) + res, err := s.client.Do(ctx, req, cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} diff --git a/github/actions_cache_test.go b/github/actions_cache_test.go new file mode 100644 index 0000000000..b72d5cb828 --- /dev/null +++ b/github/actions_cache_test.go @@ -0,0 +1,517 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListCaches(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/caches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, + `{ + "total_count":1, + "actions_caches":[{"id":1}] + }`, + ) + }) + + opts := &ActionsCacheListOptions{ListOptions: ListOptions{Page: 2}} + ctx := context.Background() + cacheList, _, err := client.Actions.ListCaches(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListCaches returned error: %v", err) + } + + want := &ActionsCacheList{TotalCount: 1, ActionsCaches: []*ActionsCache{{ID: Int64(1)}}} + if !cmp.Equal(cacheList, want) { + t.Errorf("Actions.ListCaches returned %+v, want %+v", cacheList, want) + } + + const methodName = "ListCaches" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListCaches(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListCaches(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListCaches_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.ListCaches(ctx, "%", "r", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListCaches_invalidRepo(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.ListCaches(ctx, "o", "%", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListCaches_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/caches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + caches, resp, err := client.Actions.ListCaches(ctx, "o", "r", nil) + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.ListCaches return status %d, want %d", got, want) + } + if caches != nil { + t.Errorf("Actions.ListCaches return %+v, want nil", caches) + } +} + +func TestActionsService_DeleteCachesByKey(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/caches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testFormValues(t, r, values{"key": "1", "ref": "main"}) + }) + + ctx := context.Background() + _, err := client.Actions.DeleteCachesByKey(ctx, "o", "r", "1", String("main")) + if err != nil { + t.Errorf("Actions.DeleteCachesByKey return error: %v", err) + } + + const methodName = "DeleteCachesByKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteCachesByKey(ctx, "\n", "\n", "\n", String("\n")) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteCachesByKey(ctx, "o", "r", "1", String("main")) + }) +} + +func TestActionsService_DeleteCachesByKey_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, err := client.Actions.DeleteCachesByKey(ctx, "%", "r", "1", String("main")) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByKey_invalidRepo(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, err := client.Actions.DeleteCachesByKey(ctx, "o", "%", "1", String("main")) + testURLParseError(t, err) +} +func TestActionsService_DeleteCachesByKey_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + resp, err := client.Actions.DeleteCachesByKey(ctx, "o", "r", "1", String("main")) + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.DeleteCachesByKey return status %d, want %d", got, want) + } +} + +func TestActionsService_DeleteCachesByID(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/caches/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Actions.DeleteCachesByID(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Actions.DeleteCachesByID return error: %v", err) + } + + const methodName = "DeleteCachesByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteCachesByID(ctx, "\n", "\n", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteCachesByID(ctx, "o", "r", 1) + }) +} + +func TestActionsService_DeleteCachesByID_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, err := client.Actions.DeleteCachesByID(ctx, "%", "r", 1) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByID_invalidRepo(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, err := client.Actions.DeleteCachesByID(ctx, "o", "%", 1) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByID_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("repos/o/r/actions/caches/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + resp, err := client.Actions.DeleteCachesByID(ctx, "o", "r", 1) + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.DeleteCachesByID return status %d, want %d", got, want) + } +} + +func TestActionsService_GetCacheUsageForRepo(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, + `{ + "full_name":"test-cache", + "active_caches_size_in_bytes":1000, + "active_caches_count":1 + }`, + ) + }) + + ctx := context.Background() + cacheUse, _, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetCacheUsageForRepo returned error: %v", err) + } + + want := &ActionsCacheUsage{FullName: "test-cache", ActiveCachesSizeInBytes: 1000, ActiveCachesCount: 1} + if !cmp.Equal(cacheUse, want) { + t.Errorf("Actions.GetCacheUsageForRepo returned %+v, want %+v", cacheUse, want) + } + + const methodName = "GetCacheUsageForRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetCacheUsageForRepo(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetCacheUsageForRepo_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.GetCacheUsageForRepo(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForRepo_invalidRepo(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "%") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForRepo_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + caches, resp, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "r") + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetCacheUsageForRepo return status %d, want %d", got, want) + } + if caches != nil { + t.Errorf("Actions.GetCacheUsageForRepo return %+v, want nil", caches) + } +} + +func TestActionsService_ListCacheUsageByRepoForOrg(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/cache/usage-by-repository", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "1"}) + fmt.Fprint(w, + `{ + "total_count":1, + "repository_cache_usages":[{"full_name":"test-cache","active_caches_size_in_bytes":1000,"active_caches_count":1}] + }`, + ) + }) + + opts := &ListOptions{PerPage: 1, Page: 2} + ctx := context.Background() + cacheList, _, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListCacheUsageByRepoForOrg returned error: %v", err) + } + + want := &ActionsCacheUsageList{TotalCount: 1, RepoCacheUsage: []*ActionsCacheUsage{{FullName: "test-cache", ActiveCachesSizeInBytes: 1000, ActiveCachesCount: 1}}} + if !cmp.Equal(cacheList, want) { + t.Errorf("Actions.ListCacheUsageByRepoForOrg returned %+v, want %+v", cacheList, want) + } + + const methodName = "ListCacheUsageByRepoForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListCacheUsageByRepoForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListCacheUsageByRepoForOrg_invalidOrganization(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListCacheUsageByRepoForOrg_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/cache/usage-by-repository", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + caches, resp, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "o", nil) + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.ListCacheUsageByRepoForOrg return status %d, want %d", got, want) + } + if caches != nil { + t.Errorf("Actions.ListCacheUsageByRepoForOrg return %+v, want nil", caches) + } +} + +func TestActionsService_GetCacheUsageForOrg(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, + `{ + "total_active_caches_size_in_bytes":1000, + "total_active_caches_count":1 + }`, + ) + }) + + ctx := context.Background() + cache, _, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "o") + if err != nil { + t.Errorf("Actions.GetTotalCacheUsageForOrg returned error: %v", err) + } + + want := &TotalCacheUsage{TotalActiveCachesUsageSizeInBytes: 1000, TotalActiveCachesCount: 1} + if !cmp.Equal(cache, want) { + t.Errorf("Actions.GetTotalCacheUsageForOrg returned %+v, want %+v", cache, want) + } + + const methodName = "GetTotalCacheUsageForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetTotalCacheUsageForOrg(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetCacheUsageForOrg_invalidOrganization(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "%") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForOrg_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + caches, resp, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "o") + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetTotalCacheUsageForOrg return status %d, want %d", got, want) + } + if caches != nil { + t.Errorf("Actions.GetTotalCacheUsageForOrg return %+v, want nil", caches) + } +} + +func TestActionsService_GetCacheUsageForEnterprise(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/enterprises/e/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, + `{ + "total_active_caches_size_in_bytes":1000, + "total_active_caches_count":1 + }`, + ) + }) + + ctx := context.Background() + cache, _, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise returned error: %v", err) + } + + want := &TotalCacheUsage{TotalActiveCachesUsageSizeInBytes: 1000, TotalActiveCachesCount: 1} + if !cmp.Equal(cache, want) { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise returned %+v, want %+v", cache, want) + } + + const methodName = "GetTotalCacheUsageForEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetTotalCacheUsageForEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetCacheUsageForEnterprise_invalidEnterprise(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "%") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForEnterprise_notFound(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/enterprises/e/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := context.Background() + caches, resp, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "o") + if err == nil { + t.Errorf("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise return status %d, want %d", got, want) + } + if caches != nil { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise return %+v, want nil", caches) + } +} diff --git a/github/actions_oidc.go b/github/actions_oidc.go new file mode 100644 index 0000000000..b7f2d26ae9 --- /dev/null +++ b/github/actions_oidc.go @@ -0,0 +1,73 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// OIDCSubjectClaimCustomTemplate represents an OIDC subject claim customization template. +type OIDCSubjectClaimCustomTemplate struct { + UseDefault *bool `json:"use_default,omitempty"` + IncludeClaimKeys []string `json:"include_claim_keys,omitempty"` +} + +// GetOrgOIDCSubjectClaimCustomTemplate gets the subject claim customization template for an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/oidc#get-the-customization-template-for-an-oidc-subject-claim-for-an-organization +func (s *ActionsService) GetOrgOIDCSubjectClaimCustomTemplate(ctx context.Context, org string) (*OIDCSubjectClaimCustomTemplate, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/sub", org) + return s.getOIDCSubjectClaimCustomTemplate(ctx, u) +} + +// GetRepoOIDCSubjectClaimCustomTemplate gets the subject claim customization template for a repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/oidc#get-the-customization-template-for-an-oidc-subject-claim-for-a-repository +func (s *ActionsService) GetRepoOIDCSubjectClaimCustomTemplate(ctx context.Context, owner, repo string) (*OIDCSubjectClaimCustomTemplate, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/oidc/customization/sub", owner, repo) + return s.getOIDCSubjectClaimCustomTemplate(ctx, u) +} + +func (s *ActionsService) getOIDCSubjectClaimCustomTemplate(ctx context.Context, url string) (*OIDCSubjectClaimCustomTemplate, *Response, error) { + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + tmpl := new(OIDCSubjectClaimCustomTemplate) + resp, err := s.client.Do(ctx, req, tmpl) + if err != nil { + return nil, resp, err + } + + return tmpl, resp, nil +} + +// SetOrgOIDCSubjectClaimCustomTemplate sets the subject claim customization for an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/oidc#set-the-customization-template-for-an-oidc-subject-claim-for-an-organization +func (s *ActionsService) SetOrgOIDCSubjectClaimCustomTemplate(ctx context.Context, org string, template *OIDCSubjectClaimCustomTemplate) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/sub", org) + return s.setOIDCSubjectClaimCustomTemplate(ctx, u, template) +} + +// SetRepoOIDCSubjectClaimCustomTemplate sets the subject claim customization for a repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/oidc#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository +func (s *ActionsService) SetRepoOIDCSubjectClaimCustomTemplate(ctx context.Context, owner, repo string, template *OIDCSubjectClaimCustomTemplate) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/oidc/customization/sub", owner, repo) + return s.setOIDCSubjectClaimCustomTemplate(ctx, u, template) +} + +func (s *ActionsService) setOIDCSubjectClaimCustomTemplate(ctx context.Context, url string, template *OIDCSubjectClaimCustomTemplate) (*Response, error) { + req, err := s.client.NewRequest("PUT", url, template) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/actions_oidc_test.go b/github/actions_oidc_test.go new file mode 100644 index 0000000000..c56dfef3d9 --- /dev/null +++ b/github/actions_oidc_test.go @@ -0,0 +1,181 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_GetOrgOIDCSubjectClaimCustomTemplate(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"include_claim_keys":["repo","context"]}`) + }) + + ctx := context.Background() + template, _, err := client.Actions.GetOrgOIDCSubjectClaimCustomTemplate(ctx, "o") + if err != nil { + t.Errorf("Actions.GetOrgOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + want := &OIDCSubjectClaimCustomTemplate{IncludeClaimKeys: []string{"repo", "context"}} + if !cmp.Equal(template, want) { + t.Errorf("Actions.GetOrgOIDCSubjectClaimCustomTemplate returned %+v, want %+v", template, want) + } + + const methodName = "GetOrgOIDCSubjectClaimCustomTemplate" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrgOIDCSubjectClaimCustomTemplate(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrgOIDCSubjectClaimCustomTemplate(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRepoOIDCSubjectClaimCustomTemplate(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"use_default":false,"include_claim_keys":["repo","context"]}`) + }) + + ctx := context.Background() + template, _, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetRepoOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + want := &OIDCSubjectClaimCustomTemplate{UseDefault: Bool(false), IncludeClaimKeys: []string{"repo", "context"}} + if !cmp.Equal(template, want) { + t.Errorf("Actions.GetOrgOIDCSubjectClaimCustomTemplate returned %+v, want %+v", template, want) + } + + const methodName = "GetRepoOIDCSubjectClaimCustomTemplate" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetOrgOIDCSubjectClaimCustomTemplate(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"include_claim_keys":["repo","context"]}`+"\n") + w.WriteHeader(http.StatusCreated) + }) + + input := &OIDCSubjectClaimCustomTemplate{ + IncludeClaimKeys: []string{"repo", "context"}, + } + ctx := context.Background() + _, err := client.Actions.SetOrgOIDCSubjectClaimCustomTemplate(ctx, "o", input) + if err != nil { + t.Errorf("Actions.SetOrgOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + const methodName = "SetOrgOIDCSubjectClaimCustomTemplate" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetOrgOIDCSubjectClaimCustomTemplate(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetOrgOIDCSubjectClaimCustomTemplate(ctx, "o", input) + }) +} + +func TestActionsService_SetRepoOIDCSubjectClaimCustomTemplate(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"use_default":false,"include_claim_keys":["repo","context"]}`+"\n") + w.WriteHeader(http.StatusCreated) + }) + + input := &OIDCSubjectClaimCustomTemplate{ + UseDefault: Bool(false), + IncludeClaimKeys: []string{"repo", "context"}, + } + ctx := context.Background() + _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.SetRepoOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + const methodName = "SetRepoOIDCSubjectClaimCustomTemplate" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + }) +} + +func TestActionService_SetRepoOIDCSubjectClaimCustomTemplateToDefault(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"use_default":true}`+"\n") + w.WriteHeader(http.StatusCreated) + }) + + input := &OIDCSubjectClaimCustomTemplate{ + UseDefault: Bool(true), + } + ctx := context.Background() + _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.SetRepoOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + const methodName = "SetRepoOIDCSubjectClaimCustomTemplate" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + }) +} diff --git a/github/actions_workflow_jobs.go b/github/actions_workflow_jobs.go index 1422fd471c..b55f9aa7f8 100644 --- a/github/actions_workflow_jobs.go +++ b/github/actions_workflow_jobs.go @@ -45,6 +45,7 @@ type WorkflowJob struct { RunnerGroupID *int64 `json:"runner_group_id,omitempty"` RunnerGroupName *string `json:"runner_group_name,omitempty"` RunAttempt *int64 `json:"run_attempt,omitempty"` + WorkflowName *string `json:"workflow_name,omitempty"` } // Jobs represents a slice of repository action workflow job. diff --git a/github/actions_workflow_jobs_test.go b/github/actions_workflow_jobs_test.go index 204f001409..da52619a19 100644 --- a/github/actions_workflow_jobs_test.go +++ b/github/actions_workflow_jobs_test.go @@ -264,7 +264,8 @@ func TestWorkflowJob_Marshal(t *testing.T) { CompletedAt: &Timestamp{referenceTime}, }, }, - CheckRunURL: String("c"), + CheckRunURL: String("c"), + WorkflowName: String("w"), } want := `{ @@ -288,7 +289,8 @@ func TestWorkflowJob_Marshal(t *testing.T) { "started_at": ` + referenceTimeStr + `, "completed_at": ` + referenceTimeStr + ` }], - "check_run_url": "c" + "check_run_url": "c", + "workflow_name": "w" }` testJSONMarshal(t, u, want) @@ -323,8 +325,9 @@ func TestJobs_Marshal(t *testing.T) { CompletedAt: &Timestamp{referenceTime}, }, }, - CheckRunURL: String("c"), - RunAttempt: Int64(2), + CheckRunURL: String("c"), + RunAttempt: Int64(2), + WorkflowName: String("w"), }, }, } @@ -353,7 +356,8 @@ func TestJobs_Marshal(t *testing.T) { "completed_at": ` + referenceTimeStr + ` }], "check_run_url": "c", - "run_attempt": 2 + "run_attempt": 2, + "workflow_name": "w" }] }` diff --git a/github/actions_workflow_runs.go b/github/actions_workflow_runs.go index 9fd01c4a56..6241dc629f 100644 --- a/github/actions_workflow_runs.go +++ b/github/actions_workflow_runs.go @@ -65,16 +65,13 @@ type ListWorkflowRunsOptions struct { // WorkflowRunUsage represents a usage of a specific workflow run. type WorkflowRunUsage struct { - Billable *WorkflowRunEnvironment `json:"billable,omitempty"` - RunDurationMS *int64 `json:"run_duration_ms,omitempty"` + Billable *WorkflowRunBillMap `json:"billable,omitempty"` + RunDurationMS *int64 `json:"run_duration_ms,omitempty"` } -// WorkflowRunEnvironment represents different runner environments available for a workflow run. -type WorkflowRunEnvironment struct { - Ubuntu *WorkflowRunBill `json:"UBUNTU,omitempty"` - MacOS *WorkflowRunBill `json:"MACOS,omitempty"` - Windows *WorkflowRunBill `json:"WINDOWS,omitempty"` -} +// WorkflowRunBillMap represents different runner environments available for a workflow run. +// Its key is the name of its environment, e.g. "UBUNTU", "MACOS", "WINDOWS", etc. +type WorkflowRunBillMap map[string]*WorkflowRunBill // WorkflowRunBill specifies billable time for a specific environment in a workflow run. type WorkflowRunBill struct { diff --git a/github/actions_workflow_runs_test.go b/github/actions_workflow_runs_test.go index 2705363ba6..641c4d739b 100644 --- a/github/actions_workflow_runs_test.go +++ b/github/actions_workflow_runs_test.go @@ -403,7 +403,6 @@ func TestActionService_ListRepositoryWorkflowRuns(t *testing.T) { opts := &ListWorkflowRunsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} ctx := context.Background() runs, _, err := client.Actions.ListRepositoryWorkflowRuns(ctx, "o", "r", opts) - if err != nil { t.Errorf("Actions.ListRepositoryWorkflowRuns returned error: %v", err) } @@ -505,8 +504,8 @@ func TestActionsService_GetWorkflowRunUsageByID(t *testing.T) { } want := &WorkflowRunUsage{ - Billable: &WorkflowRunEnvironment{ - Ubuntu: &WorkflowRunBill{ + Billable: &WorkflowRunBillMap{ + "UBUNTU": &WorkflowRunBill{ TotalMS: Int64(180000), Jobs: Int(1), JobRuns: []*WorkflowRunJobRun{ @@ -516,7 +515,7 @@ func TestActionsService_GetWorkflowRunUsageByID(t *testing.T) { }, }, }, - MacOS: &WorkflowRunBill{ + "MACOS": &WorkflowRunBill{ TotalMS: Int64(240000), Jobs: Int(2), JobRuns: []*WorkflowRunJobRun{ @@ -530,7 +529,7 @@ func TestActionsService_GetWorkflowRunUsageByID(t *testing.T) { }, }, }, - Windows: &WorkflowRunBill{ + "WINDOWS": &WorkflowRunBill{ TotalMS: Int64(300000), Jobs: Int(2), }, @@ -975,7 +974,7 @@ func TestWorkflowRuns_Marshal(t *testing.T) { "created_at": ` + referenceTimeStr + `, "suspended_at": ` + referenceTimeStr + `, "url": "u" - } + } } ] }` @@ -999,19 +998,19 @@ func TestWorkflowRunBill_Marshal(t *testing.T) { testJSONMarshal(t, u, want) } -func TestWorkflowRunEnvironment_Marshal(t *testing.T) { - testJSONMarshal(t, &WorkflowRunEnvironment{}, "{}") +func TestWorkflowRunBillMap_Marshal(t *testing.T) { + testJSONMarshal(t, &WorkflowRunBillMap{}, "{}") - u := &WorkflowRunEnvironment{ - Ubuntu: &WorkflowRunBill{ + u := &WorkflowRunBillMap{ + "UBUNTU": &WorkflowRunBill{ TotalMS: Int64(1), Jobs: Int(1), }, - MacOS: &WorkflowRunBill{ + "MACOS": &WorkflowRunBill{ TotalMS: Int64(1), Jobs: Int(1), }, - Windows: &WorkflowRunBill{ + "WINDOWS": &WorkflowRunBill{ TotalMS: Int64(1), Jobs: Int(1), }, @@ -1039,16 +1038,16 @@ func TestWorkflowRunUsage_Marshal(t *testing.T) { testJSONMarshal(t, &WorkflowRunUsage{}, "{}") u := &WorkflowRunUsage{ - Billable: &WorkflowRunEnvironment{ - Ubuntu: &WorkflowRunBill{ + Billable: &WorkflowRunBillMap{ + "UBUNTU": &WorkflowRunBill{ TotalMS: Int64(1), Jobs: Int(1), }, - MacOS: &WorkflowRunBill{ + "MACOS": &WorkflowRunBill{ TotalMS: Int64(1), Jobs: Int(1), }, - Windows: &WorkflowRunBill{ + "WINDOWS": &WorkflowRunBill{ TotalMS: Int64(1), Jobs: Int(1), }, diff --git a/github/actions_workflows.go b/github/actions_workflows.go index 9973a5d3f3..c9b47ed4be 100644 --- a/github/actions_workflows.go +++ b/github/actions_workflows.go @@ -32,15 +32,12 @@ type Workflows struct { // WorkflowUsage represents a usage of a specific workflow. type WorkflowUsage struct { - Billable *WorkflowEnvironment `json:"billable,omitempty"` + Billable *WorkflowBillMap `json:"billable,omitempty"` } -// WorkflowEnvironment represents different runner environments available for a workflow. -type WorkflowEnvironment struct { - Ubuntu *WorkflowBill `json:"UBUNTU,omitempty"` - MacOS *WorkflowBill `json:"MACOS,omitempty"` - Windows *WorkflowBill `json:"WINDOWS,omitempty"` -} +// WorkflowBillMap represents different runner environments available for a workflow. +// Its key is the name of its environment, e.g. "UBUNTU", "MACOS", "WINDOWS", etc. +type WorkflowBillMap map[string]*WorkflowBill // WorkflowBill specifies billable time for a specific environment in a workflow. type WorkflowBill struct { diff --git a/github/actions_workflows_test.go b/github/actions_workflows_test.go index 60a9d91b37..5e0dbbc9c1 100644 --- a/github/actions_workflows_test.go +++ b/github/actions_workflows_test.go @@ -153,14 +153,14 @@ func TestActionsService_GetWorkflowUsageByID(t *testing.T) { } want := &WorkflowUsage{ - Billable: &WorkflowEnvironment{ - Ubuntu: &WorkflowBill{ + Billable: &WorkflowBillMap{ + "UBUNTU": &WorkflowBill{ TotalMS: Int64(180000), }, - MacOS: &WorkflowBill{ + "MACOS": &WorkflowBill{ TotalMS: Int64(240000), }, - Windows: &WorkflowBill{ + "WINDOWS": &WorkflowBill{ TotalMS: Int64(300000), }, }, @@ -200,14 +200,14 @@ func TestActionsService_GetWorkflowUsageByFileName(t *testing.T) { } want := &WorkflowUsage{ - Billable: &WorkflowEnvironment{ - Ubuntu: &WorkflowBill{ + Billable: &WorkflowBillMap{ + "UBUNTU": &WorkflowBill{ TotalMS: Int64(180000), }, - MacOS: &WorkflowBill{ + "MACOS": &WorkflowBill{ TotalMS: Int64(240000), }, - Windows: &WorkflowBill{ + "WINDOWS": &WorkflowBill{ TotalMS: Int64(300000), }, }, @@ -545,17 +545,17 @@ func TestWorkflowBill_Marshal(t *testing.T) { testJSONMarshal(t, u, want) } -func TestWorkflowEnvironment_Marshal(t *testing.T) { - testJSONMarshal(t, &WorkflowEnvironment{}, "{}") +func TestWorkflowBillMap_Marshal(t *testing.T) { + testJSONMarshal(t, &WorkflowBillMap{}, "{}") - u := &WorkflowEnvironment{ - Ubuntu: &WorkflowBill{ + u := &WorkflowBillMap{ + "UBUNTU": &WorkflowBill{ TotalMS: Int64(1), }, - MacOS: &WorkflowBill{ + "MACOS": &WorkflowBill{ TotalMS: Int64(1), }, - Windows: &WorkflowBill{ + "WINDOWS": &WorkflowBill{ TotalMS: Int64(1), }, } @@ -579,14 +579,14 @@ func TestWorkflowUsage_Marshal(t *testing.T) { testJSONMarshal(t, &WorkflowUsage{}, "{}") u := &WorkflowUsage{ - Billable: &WorkflowEnvironment{ - Ubuntu: &WorkflowBill{ + Billable: &WorkflowBillMap{ + "UBUNTU": &WorkflowBill{ TotalMS: Int64(1), }, - MacOS: &WorkflowBill{ + "MACOS": &WorkflowBill{ TotalMS: Int64(1), }, - Windows: &WorkflowBill{ + "WINDOWS": &WorkflowBill{ TotalMS: Int64(1), }, }, diff --git a/github/activity_notifications.go b/github/activity_notifications.go index 38a3184536..03476c2e2c 100644 --- a/github/activity_notifications.go +++ b/github/activity_notifications.go @@ -23,8 +23,8 @@ type Notification struct { Reason *string `json:"reason,omitempty"` Unread *bool `json:"unread,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - LastReadAt *time.Time `json:"last_read_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + LastReadAt *Timestamp `json:"last_read_at,omitempty"` URL *string `json:"url,omitempty"` } @@ -97,13 +97,13 @@ func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner } type markReadOptions struct { - LastReadAt time.Time `json:"last_read_at,omitempty"` + LastReadAt Timestamp `json:"last_read_at,omitempty"` } // MarkNotificationsRead marks all notifications up to lastRead as read. // // GitHub API docs: https://docs.github.com/en/rest/activity#mark-as-read -func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) { +func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead Timestamp) (*Response, error) { opts := &markReadOptions{ LastReadAt: lastRead, } @@ -119,7 +119,7 @@ func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead ti // the specified repository as read. // // GitHub API docs: https://docs.github.com/en/rest/activity/notifications#mark-repository-notifications-as-read -func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) { +func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead Timestamp) (*Response, error) { opts := &markReadOptions{ LastReadAt: lastRead, } diff --git a/github/activity_notifications_test.go b/github/activity_notifications_test.go index fcd1cc3adc..830c8b10cf 100644 --- a/github/activity_notifications_test.go +++ b/github/activity_notifications_test.go @@ -107,14 +107,14 @@ func TestActivityService_MarkNotificationsRead(t *testing.T) { }) ctx := context.Background() - _, err := client.Activity.MarkNotificationsRead(ctx, time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + _, err := client.Activity.MarkNotificationsRead(ctx, Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}) if err != nil { t.Errorf("Activity.MarkNotificationsRead returned error: %v", err) } const methodName = "MarkNotificationsRead" testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - return client.Activity.MarkNotificationsRead(ctx, time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + return client.Activity.MarkNotificationsRead(ctx, Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}) }) } @@ -131,19 +131,19 @@ func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) { }) ctx := context.Background() - _, err := client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + _, err := client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}) if err != nil { t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err) } const methodName = "MarkRepositoryNotificationsRead" testBadOptions(t, methodName, func() (err error) { - _, err = client.Activity.MarkRepositoryNotificationsRead(ctx, "\n", "\n", time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + _, err = client.Activity.MarkRepositoryNotificationsRead(ctx, "\n", "\n", Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - return client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + return client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}) }) } @@ -331,8 +331,8 @@ func TestNotification_Marshal(t *testing.T) { }, Reason: String("r"), Unread: Bool(true), - UpdatedAt: &referenceTime, - LastReadAt: &referenceTime, + UpdatedAt: &Timestamp{referenceTime}, + LastReadAt: &Timestamp{referenceTime}, URL: String("u"), } @@ -383,7 +383,7 @@ func TestMarkReadOptions_Marshal(t *testing.T) { testJSONMarshal(t, &markReadOptions{}, "{}") u := &markReadOptions{ - LastReadAt: referenceTime, + LastReadAt: Timestamp{referenceTime}, } want := `{ diff --git a/github/apps.go b/github/apps.go index 98d8a6fda3..e1d9aadaf5 100644 --- a/github/apps.go +++ b/github/apps.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // AppsService provides access to the installation related functions @@ -36,7 +35,7 @@ type App struct { // InstallationToken represents an installation token. type InstallationToken struct { Token *string `json:"token,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` Permissions *InstallationPermissions `json:"permissions,omitempty"` Repositories []*Repository `json:"repositories,omitempty"` } diff --git a/github/apps_test.go b/github/apps_test.go index 494f1031bb..d3e7c43423 100644 --- a/github/apps_test.go +++ b/github/apps_test.go @@ -997,7 +997,7 @@ func TestInstallationToken_Marshal(t *testing.T) { u := &InstallationToken{ Token: String("t"), - ExpiresAt: &referenceTime, + ExpiresAt: &Timestamp{referenceTime}, Permissions: &InstallationPermissions{ Actions: String("a"), Administration: String("ad"), diff --git a/github/billing.go b/github/billing.go index d516cd0c29..2900a01670 100644 --- a/github/billing.go +++ b/github/billing.go @@ -18,30 +18,27 @@ type BillingService service // ActionBilling represents a GitHub Action billing. type ActionBilling struct { - TotalMinutesUsed int `json:"total_minutes_used"` + TotalMinutesUsed float64 `json:"total_minutes_used"` TotalPaidMinutesUsed float64 `json:"total_paid_minutes_used"` - IncludedMinutes int `json:"included_minutes"` + IncludedMinutes float64 `json:"included_minutes"` MinutesUsedBreakdown MinutesUsedBreakdown `json:"minutes_used_breakdown"` } -type MinutesUsedBreakdown struct { - Ubuntu int `json:"UBUNTU"` - MacOS int `json:"MACOS"` - Windows int `json:"WINDOWS"` -} +// MinutesUsedBreakdown counts the actions minutes used by machine type (e.g. UBUNTU, WINDOWS, MACOS). +type MinutesUsedBreakdown = map[string]int // PackageBilling represents a GitHub Package billing. type PackageBilling struct { - TotalGigabytesBandwidthUsed int `json:"total_gigabytes_bandwidth_used"` - TotalPaidGigabytesBandwidthUsed int `json:"total_paid_gigabytes_bandwidth_used"` - IncludedGigabytesBandwidth int `json:"included_gigabytes_bandwidth"` + TotalGigabytesBandwidthUsed int `json:"total_gigabytes_bandwidth_used"` + TotalPaidGigabytesBandwidthUsed int `json:"total_paid_gigabytes_bandwidth_used"` + IncludedGigabytesBandwidth float64 `json:"included_gigabytes_bandwidth"` } // StorageBilling represents a GitHub Storage billing. type StorageBilling struct { DaysLeftInBillingCycle int `json:"days_left_in_billing_cycle"` EstimatedPaidStorageForMonth float64 `json:"estimated_paid_storage_for_month"` - EstimatedStorageForMonth int `json:"estimated_storage_for_month"` + EstimatedStorageForMonth float64 `json:"estimated_storage_for_month"` } // ActiveCommitters represents the total active committers across all repositories in an Organization. diff --git a/github/billing_test.go b/github/billing_test.go index 2df9a0e97f..52b338b834 100644 --- a/github/billing_test.go +++ b/github/billing_test.go @@ -21,9 +21,9 @@ func TestBillingService_GetActionsBillingOrg(t *testing.T) { mux.HandleFunc("/orgs/o/settings/billing/actions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ - "total_minutes_used": 305, + "total_minutes_used": 305.0, "total_paid_minutes_used": 0.0, - "included_minutes": 3000, + "included_minutes": 3000.0, "minutes_used_breakdown": { "UBUNTU": 205, "MACOS": 10, @@ -39,13 +39,13 @@ func TestBillingService_GetActionsBillingOrg(t *testing.T) { } want := &ActionBilling{ - TotalMinutesUsed: 305, - TotalPaidMinutesUsed: 0, - IncludedMinutes: 3000, + TotalMinutesUsed: 305.0, + TotalPaidMinutesUsed: 0.0, + IncludedMinutes: 3000.0, MinutesUsedBreakdown: MinutesUsedBreakdown{ - Ubuntu: 205, - MacOS: 10, - Windows: 90, + "UBUNTU": 205, + "MACOS": 10, + "WINDOWS": 90, }, } if !cmp.Equal(hook, want) { @@ -209,9 +209,9 @@ func TestBillingService_GetActionsBillingUser(t *testing.T) { TotalPaidMinutesUsed: 0, IncludedMinutes: 3000, MinutesUsedBreakdown: MinutesUsedBreakdown{ - Ubuntu: 205, - MacOS: 10, - Windows: 90, + "UBUNTU": 205, + "MACOS": 10, + "WINDOWS": 90, }, } if !cmp.Equal(hook, want) { @@ -350,9 +350,9 @@ func TestMinutesUsedBreakdown_Marshal(t *testing.T) { testJSONMarshal(t, &MinutesUsedBreakdown{}, "{}") u := &MinutesUsedBreakdown{ - Ubuntu: 1, - MacOS: 1, - Windows: 1, + "UBUNTU": 1, + "MACOS": 1, + "WINDOWS": 1, } want := `{ @@ -372,9 +372,9 @@ func TestActionBilling_Marshal(t *testing.T) { TotalPaidMinutesUsed: 1, IncludedMinutes: 1, MinutesUsedBreakdown: MinutesUsedBreakdown{ - Ubuntu: 1, - MacOS: 1, - Windows: 1, + "UBUNTU": 1, + "MACOS": 1, + "WINDOWS": 1, }, } diff --git a/github/code-scanning.go b/github/code-scanning.go index 53200fa3ee..6717348ed7 100644 --- a/github/code-scanning.go +++ b/github/code-scanning.go @@ -173,6 +173,22 @@ type SarifAnalysis struct { ToolName *string `json:"tool_name,omitempty"` } +// CodeScanningAlertState specifies the state of a code scanning alert. +// +// GitHub API docs: https://docs.github.com/en/rest/code-scanning +type CodeScanningAlertState struct { + // State sets the state of the code scanning alert and is a required field. + // You must also provide DismissedReason when you set the state to "dismissed". + // State can be one of: "open", "dismissed". + State string `json:"state"` + // DismissedReason represents the reason for dismissing or closing the alert. + // It is required when the state is "dismissed". + // It can be one of: "false positive", "won't fix", "used in tests". + DismissedReason *string `json:"dismissed_reason,omitempty"` + // DismissedComment is associated with the dismissal of the alert. + DismissedComment *string `json:"dismissed_comment,omitempty"` +} + // SarifID identifies a sarif analysis upload. // // GitHub API docs: https://docs.github.com/en/rest/code-scanning @@ -261,6 +277,31 @@ func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string, return a, resp, nil } +// UpdateAlert updates the state of a single code scanning alert for a repository. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// The security alert_id is the number at the end of the security alert's URL. +// +// GitHub API docs: https://docs.github.com/en/rest/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-alert +func (s *CodeScanningService) UpdateAlert(ctx context.Context, owner, repo string, id int64, stateInfo *CodeScanningAlertState) (*Alert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id) + + req, err := s.client.NewRequest("PATCH", u, stateInfo) + if err != nil { + return nil, nil, err + } + + a := new(Alert) + resp, err := s.client.Do(ctx, req, a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + // UploadSarif uploads the result of code scanning job to GitHub. // // For the parameter sarif, you must first compress your SARIF file using gzip and then translate the contents of the file into a Base64 encoding string. diff --git a/github/code-scanning_test.go b/github/code-scanning_test.go index c8cf35cfba..7911fca636 100644 --- a/github/code-scanning_test.go +++ b/github/code-scanning_test.go @@ -83,7 +83,7 @@ func TestCodeScanningService_UploadSarif(t *testing.T) { return err }) - testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + testNewRequestAndDoFailureCategory(t, methodName, client, codeScanningUploadCategory, func() (*Response, error) { _, resp, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis) return resp, err }) @@ -613,6 +613,123 @@ func TestCodeScanningService_ListAlertsForRepo(t *testing.T) { }) } +func TestCodeScanningService_UpdateAlert(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"rule_id":"js/useless-expression", + "rule_severity":"warning", + "rule_description":"Expression has no effect", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "rule": { + "id": "useless expression", + "severity": "warning", + "description": "Expression has no effect", + "name": "useless expression", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "dismissed", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":"2019-01-02T15:04:05Z", + "state":"dismissed", + "dismissed_reason": "false positive", + "dismissed_comment": "This alert is not actually correct as sanitizer is used", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", + "html_url":"https://github.com/o/r/security/code-scanning/88"}`) + }) + + ctx := context.Background() + dismissedComment := String("This alert is not actually correct as sanitizer is used") + dismissedReason := String("false positive") + state := String("dismissed") + stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment} + alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo) + if err != nil { + t.Errorf("CodeScanning.UpdateAlert returned error: %v", err) + } + + date := Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)} + want := &Alert{ + RuleID: String("js/useless-expression"), + RuleSeverity: String("warning"), + RuleDescription: String("Expression has no effect"), + Tool: &Tool{Name: String("CodeQL"), GUID: nil, Version: String("1.4.0")}, + Rule: &Rule{ + ID: String("useless expression"), + Severity: String("warning"), + Description: String("Expression has no effect"), + Name: String("useless expression"), + FullDescription: String("Expression has no effect"), + Help: String("Expression has no effect"), + }, + CreatedAt: &date, + State: state, + DismissedReason: dismissedReason, + DismissedComment: dismissedComment, + ClosedBy: nil, + ClosedAt: nil, + URL: String("https://api.github.com/repos/o/r/code-scanning/alerts/88"), + HTMLURL: String("https://github.com/o/r/security/code-scanning/88"), + MostRecentInstance: &MostRecentInstance{ + Ref: String("refs/heads/main"), + State: String("dismissed"), + CommitSHA: String("abcdefg12345"), + Message: &Message{ + Text: String("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: String("spec-main/api-session-spec.ts"), + StartLine: Int(917), + EndLine: Int(917), + StartColumn: Int(7), + EndColumn: Int(18), + }, + Classifications: []string{"test"}, + }, + } + if !cmp.Equal(alert, want) { + t.Errorf("CodeScanning.UpdateAlert returned %+v, want %+v", alert, want) + } + + const methodName = "UpdateAlert" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.UpdateAlert(ctx, "\n", "\n", -88, stateInfo) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestCodeScanningService_GetAlert(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/github/doc.go b/github/doc.go index 0c4afaa3d6..9adfea8fe9 100644 --- a/github/doc.go +++ b/github/doc.go @@ -8,7 +8,7 @@ Package github provides a client for using the GitHub API. Usage: - import "github.com/google/go-github/v48/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) + import "github.com/google/go-github/v50/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) import "github.com/google/go-github/github" // with go modules disabled Construct a new GitHub client, then use the various services on the client to diff --git a/github/enterprise_code_security_and_analysis.go b/github/enterprise_code_security_and_analysis.go new file mode 100644 index 0000000000..3980a86aa4 --- /dev/null +++ b/github/enterprise_code_security_and_analysis.go @@ -0,0 +1,78 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnterpriseSecurityAnalysisSettings represents security analysis settings for an enterprise. +type EnterpriseSecurityAnalysisSettings struct { + AdvancedSecurityEnabledForNewRepositories *bool `json:"advanced_security_enabled_for_new_repositories,omitempty"` + SecretScanningEnabledForNewRepositories *bool `json:"secret_scanning_enabled_for_new_repositories,omitempty"` + SecretScanningPushProtectionEnabledForNewRepositories *bool `json:"secret_scanning_push_protection_enabled_for_new_repositories,omitempty"` + SecretScanningPushProtectionCustomLink *string `json:"secret_scanning_push_protection_custom_link,omitempty"` +} + +// GetCodeSecurityAndAnalysis gets code security and analysis features for an enterprise. +// +// GitHub API docs: https://docs.github.com/en/rest/enterprise-admin/code-security-and-analysis?apiVersion=2022-11-28#get-code-security-and-analysis-features-for-an-enterprise +func (s *EnterpriseService) GetCodeSecurityAndAnalysis(ctx context.Context, enterprise string) (*EnterpriseSecurityAnalysisSettings, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code_security_and_analysis", enterprise) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + settings := new(EnterpriseSecurityAnalysisSettings) + resp, err := s.client.Do(ctx, req, settings) + if err != nil { + return nil, resp, err + } + + return settings, resp, nil +} + +// UpdateCodeSecurityAndAnalysis updates code security and analysis features for new repositories in an enterprise. +// +// GitHub API docs: https://docs.github.com/en/rest/enterprise-admin/code-security-and-analysis?apiVersion=2022-11-28#update-code-security-and-analysis-features-for-an-enterprise +func (s *EnterpriseService) UpdateCodeSecurityAndAnalysis(ctx context.Context, enterprise string, settings *EnterpriseSecurityAnalysisSettings) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/code_security_and_analysis", enterprise) + req, err := s.client.NewRequest("PATCH", u, settings) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// EnableDisableSecurityFeature enables or disables a security feature for all repositories in an enterprise. +// +// Valid values for securityProduct: "advanced_security", "secret_scanning", "secret_scanning_push_protection". +// Valid values for enablement: "enable_all", "disable_all". +// +// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis?apiVersion=2022-11-28#enable-or-disable-a-security-feature +func (s *EnterpriseService) EnableDisableSecurityFeature(ctx context.Context, enterprise, securityProduct, enablement string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/%v/%v", enterprise, securityProduct, enablement) + req, err := s.client.NewRequest("POST", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/enterprise_code_security_and_analysis_test.go b/github/enterprise_code_security_and_analysis_test.go new file mode 100644 index 0000000000..24bf89c51e --- /dev/null +++ b/github/enterprise_code_security_and_analysis_test.go @@ -0,0 +1,132 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetCodeSecurityAndAnalysis(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/enterprises/e/code_security_and_analysis", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, ` + { + "advanced_security_enabled_for_new_repositories": true, + "secret_scanning_enabled_for_new_repositories": true, + "secret_scanning_push_protection_enabled_for_new_repositories": true, + "secret_scanning_push_protection_custom_link": "https://github.com/test-org/test-repo/blob/main/README.md" + }`) + }) + + ctx := context.Background() + + const methodName = "GetCodeSecurityAndAnalysis" + + settings, _, err := client.Enterprise.GetCodeSecurityAndAnalysis(ctx, "e") + if err != nil { + t.Errorf("Enterprise.%v returned error: %v", methodName, err) + } + want := &EnterpriseSecurityAnalysisSettings{ + AdvancedSecurityEnabledForNewRepositories: Bool(true), + SecretScanningEnabledForNewRepositories: Bool(true), + SecretScanningPushProtectionEnabledForNewRepositories: Bool(true), + SecretScanningPushProtectionCustomLink: String("https://github.com/test-org/test-repo/blob/main/README.md"), + } + + if !cmp.Equal(settings, want) { + t.Errorf("Enterprise.%v return \ngot: %+v,\nwant:%+v", methodName, settings, want) + } + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetCodeSecurityAndAnalysis(ctx, "o") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetCodeSecurityAndAnalysis(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateCodeSecurityAndAnalysis(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &EnterpriseSecurityAnalysisSettings{ + AdvancedSecurityEnabledForNewRepositories: Bool(true), + SecretScanningEnabledForNewRepositories: Bool(true), + SecretScanningPushProtectionEnabledForNewRepositories: Bool(true), + SecretScanningPushProtectionCustomLink: String("https://github.com/test-org/test-repo/blob/main/README.md"), + } + + mux.HandleFunc("/enterprises/e/code_security_and_analysis", func(w http.ResponseWriter, r *http.Request) { + v := new(EnterpriseSecurityAnalysisSettings) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PATCH") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + }) + + ctx := context.Background() + + const methodName = "UpdateCodeSecurityAndAnalysis" + + _, err := client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, "e", input) + if err != nil { + t.Errorf("Enterprise.%v returned error: %v", methodName, err) + } + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, "o", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, "e", input) + }) +} + +func TestEnterpriseService_EnableAdvancedSecurity(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/enterprises/e/advanced_security/enable_all", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := context.Background() + + const methodName = "EnableDisableSecurityFeature" + + _, err := client.Enterprise.EnableDisableSecurityFeature(ctx, "e", "advanced_security", "enable_all") + if err != nil { + t.Errorf("Enterprise.%v returned error: %v", methodName, err) + } + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.EnableDisableSecurityFeature(ctx, "o", "advanced_security", "enable_all") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.EnableDisableSecurityFeature(ctx, "e", "advanced_security", "enable_all") + }) +} diff --git a/github/event.go b/github/event.go index 5b2312fb35..44c5f46f9b 100644 --- a/github/event.go +++ b/github/event.go @@ -7,7 +7,6 @@ package github import ( "encoding/json" - "time" ) // Event represents a GitHub event. @@ -18,7 +17,7 @@ type Event struct { Repo *Repository `json:"repo,omitempty"` Actor *User `json:"actor,omitempty"` Org *Organization `json:"org,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` ID *string `json:"id,omitempty"` } diff --git a/github/event_test.go b/github/event_test.go index 713f3b1ed7..6d20582c2f 100644 --- a/github/event_test.go +++ b/github/event_test.go @@ -78,7 +78,7 @@ func TestEvent_Marshal(t *testing.T) { MembersCanCreatePublicPages: Bool(false), MembersCanCreatePrivatePages: Bool(true), }, - CreatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, ID: String("id"), } diff --git a/github/event_types.go b/github/event_types.go index 6c59b7b532..8330353a3c 100644 --- a/github/event_types.go +++ b/github/event_types.go @@ -1389,4 +1389,6 @@ type CodeScanningAlertEvent struct { Repo *Repository `json:"repository,omitempty"` Org *Organization `json:"organization,omitempty"` Sender *User `json:"sender,omitempty"` + + Installation *Installation `json:"installation,omitempty"` } diff --git a/github/event_types_test.go b/github/event_types_test.go index 8cd0868836..fdc0809e69 100644 --- a/github/event_types_test.go +++ b/github/event_types_test.go @@ -4855,8 +4855,8 @@ func TestCommitCommentEvent_Marshal(t *testing.T) { Eyes: Int(1), URL: String("url"), }, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, Body: String("b"), Path: String("path"), Position: Int(1), @@ -6224,8 +6224,8 @@ func TestPingEvent_Marshal(t *testing.T) { Zen: String("z"), HookID: Int64(1), Hook: &Hook{ - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, URL: String("url"), ID: Int64(1), Type: String("t"), @@ -10698,8 +10698,8 @@ func TestMetaEvent_Marshal(t *testing.T) { Action: String("a"), HookID: Int64(1), Hook: &Hook{ - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, URL: String("u"), ID: Int64(1), Type: String("t"), @@ -11745,7 +11745,7 @@ func TestHeadCommit_Marshal(t *testing.T) { u := &HeadCommit{ Message: String("m"), Author: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), @@ -11757,7 +11757,7 @@ func TestHeadCommit_Marshal(t *testing.T) { TreeID: String("tid"), Timestamp: &Timestamp{referenceTime}, Committer: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), diff --git a/github/examples_test.go b/github/examples_test.go index 79c226fc48..40a96b94f4 100644 --- a/github/examples_test.go +++ b/github/examples_test.go @@ -12,7 +12,7 @@ import ( "fmt" "log" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) func ExampleClient_Markdown() { diff --git a/github/gists.go b/github/gists.go index ecdc6f2726..80961fcb90 100644 --- a/github/gists.go +++ b/github/gists.go @@ -28,8 +28,8 @@ type Gist struct { HTMLURL *string `json:"html_url,omitempty"` GitPullURL *string `json:"git_pull_url,omitempty"` GitPushURL *string `json:"git_push_url,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` NodeID *string `json:"node_id,omitempty"` } diff --git a/github/gists_comments.go b/github/gists_comments.go index d551e9a11d..ee0fbfa45f 100644 --- a/github/gists_comments.go +++ b/github/gists_comments.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // GistComment represents a Gist comment. @@ -17,7 +16,7 @@ type GistComment struct { URL *string `json:"url,omitempty"` Body *string `json:"body,omitempty"` User *User `json:"user,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` } func (g GistComment) String() string { diff --git a/github/gists_comments_test.go b/github/gists_comments_test.go index 0b3cb73a85..c9c9706897 100644 --- a/github/gists_comments_test.go +++ b/github/gists_comments_test.go @@ -42,7 +42,7 @@ func TestGistComments_Marshal(t *testing.T) { CreatedAt: &Timestamp{referenceTime}, URL: String("u"), }, - CreatedAt: &createdAt, + CreatedAt: &Timestamp{createdAt}, } want := `{ diff --git a/github/gists_test.go b/github/gists_test.go index 06a1c43f72..3066d585a2 100644 --- a/github/gists_test.go +++ b/github/gists_test.go @@ -57,8 +57,8 @@ func TestGist_Marshal(t *testing.T) { HTMLURL: String("html-url"), GitPullURL: String("gitpull-url"), GitPushURL: String("gitpush-url"), - CreatedAt: &createdAt, - UpdatedAt: &updatedAt, + CreatedAt: &Timestamp{createdAt}, + UpdatedAt: &Timestamp{updatedAt}, NodeID: String("node"), } diff --git a/github/git_commits.go b/github/git_commits.go index baedb3d686..81994c6e50 100644 --- a/github/git_commits.go +++ b/github/git_commits.go @@ -11,7 +11,6 @@ import ( "errors" "fmt" "strings" - "time" "golang.org/x/crypto/openpgp" ) @@ -56,7 +55,7 @@ func (c Commit) String() string { // CommitAuthor represents the author or committer of a commit. The commit // author may not correspond to a GitHub User. type CommitAuthor struct { - Date *time.Time `json:"date,omitempty"` + Date *Timestamp `json:"date,omitempty"` Name *string `json:"name,omitempty"` Email *string `json:"email,omitempty"` diff --git a/github/git_commits_test.go b/github/git_commits_test.go index cc9cc3fcbd..1fbfa0d2e1 100644 --- a/github/git_commits_test.go +++ b/github/git_commits_test.go @@ -24,13 +24,13 @@ func TestCommit_Marshal(t *testing.T) { u := &Commit{ SHA: String("s"), Author: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), }, Committer: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), @@ -313,7 +313,7 @@ func TestGitService_CreateSignedCommitWithKey(t *testing.T) { author := CommitAuthor{ Name: String("go-github"), Email: String("go-github@github.com"), - Date: &date, + Date: &Timestamp{date}, } input := &Commit{ Message: String("Commit Message."), @@ -417,7 +417,7 @@ func TestGitService_createSignature_invalidKey(t *testing.T) { Author: &CommitAuthor{ Name: String("go-github"), Email: String("go-github@github.com"), - Date: &date, + Date: &Timestamp{date}, }, }) @@ -442,7 +442,7 @@ func TestGitService_createSignatureMessage_nilMessage(t *testing.T) { Author: &CommitAuthor{ Name: String("go-github"), Email: String("go-github@github.com"), - Date: &date, + Date: &Timestamp{date}, }, }) if err == nil { @@ -459,7 +459,7 @@ func TestGitService_createSignatureMessage_emptyMessage(t *testing.T) { Author: &CommitAuthor{ Name: String("go-github"), Email: String("go-github@github.com"), - Date: &date, + Date: &Timestamp{date}, }, }) if err == nil { @@ -487,7 +487,7 @@ func TestGitService_createSignatureMessage_withoutTree(t *testing.T) { Author: &CommitAuthor{ Name: String("go-github"), Email: String("go-github@github.com"), - Date: &date, + Date: &Timestamp{date}, }, }) expected := `parent p @@ -509,12 +509,12 @@ func TestGitService_createSignatureMessage_withoutCommitter(t *testing.T) { Author: &CommitAuthor{ Name: String("go-github"), Email: String("go-github@github.com"), - Date: &date, + Date: &Timestamp{date}, }, Committer: &CommitAuthor{ Name: String("foo"), Email: String("foo@bar.com"), - Date: &date, + Date: &Timestamp{date}, }, }) expected := `parent p @@ -619,7 +619,7 @@ func TestCommitAuthor_Marshal(t *testing.T) { testJSONMarshal(t, &CommitAuthor{}, "{}") u := &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), @@ -640,13 +640,13 @@ func TestCreateCommit_Marshal(t *testing.T) { u := &createCommit{ Author: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), }, Committer: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), diff --git a/github/git_tags_test.go b/github/git_tags_test.go index 2d13722aba..0d45ee2689 100644 --- a/github/git_tags_test.go +++ b/github/git_tags_test.go @@ -107,7 +107,7 @@ func TestTag_Marshal(t *testing.T) { URL: String("url"), Message: String("msg"), Tagger: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), @@ -163,7 +163,7 @@ func TestCreateTagRequest_Marshal(t *testing.T) { Object: String("obj"), Type: String("type"), Tagger: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), diff --git a/github/github-accessors.go b/github/github-accessors.go index c348e1a3b5..e2d0d7f835 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -38,6 +38,94 @@ func (a *ActionsAllowed) GetVerifiedAllowed() bool { return *a.VerifiedAllowed } +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} + } + return *a.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetID() int64 { + if a == nil || a.ID == nil { + return 0 + } + return *a.ID +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetKey() string { + if a == nil || a.Key == nil { + return "" + } + return *a.Key +} + +// GetLastAccessedAt returns the LastAccessedAt field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetLastAccessedAt() Timestamp { + if a == nil || a.LastAccessedAt == nil { + return Timestamp{} + } + return *a.LastAccessedAt +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetRef() string { + if a == nil || a.Ref == nil { + return "" + } + return *a.Ref +} + +// GetSizeInBytes returns the SizeInBytes field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetSizeInBytes() int64 { + if a == nil || a.SizeInBytes == nil { + return 0 + } + return *a.SizeInBytes +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetVersion() string { + if a == nil || a.Version == nil { + return "" + } + return *a.Version +} + +// GetDirection returns the Direction field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetDirection() string { + if a == nil || a.Direction == nil { + return "" + } + return *a.Direction +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetKey() string { + if a == nil || a.Key == nil { + return "" + } + return *a.Key +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetRef() string { + if a == nil || a.Ref == nil { + return "" + } + return *a.Ref +} + +// GetSort returns the Sort field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetSort() string { + if a == nil || a.Sort == nil { + return "" + } + return *a.Sort +} + // GetAllowedActions returns the AllowedActions field if it's non-nil, zero value otherwise. func (a *ActionsPermissions) GetAllowedActions() string { if a == nil || a.AllowedActions == nil { @@ -470,6 +558,14 @@ func (a *AllowDeletionsEnforcementLevelChanges) GetFrom() string { return *a.From } +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (a *AllowForkSyncing) GetEnabled() bool { + if a == nil || a.Enabled == nil { + return false + } + return *a.Enabled +} + // GetRef returns the Ref field if it's non-nil, zero value otherwise. func (a *AnalysesListOptions) GetRef() string { if a == nil || a.Ref == nil { @@ -2446,6 +2542,14 @@ func (c *CodeScanningAlertEvent) GetCommitOID() string { return *c.CommitOID } +// GetInstallation returns the Installation field. +func (c *CodeScanningAlertEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + // GetOrg returns the Org field. func (c *CodeScanningAlertEvent) GetOrg() *Organization { if c == nil { @@ -2478,6 +2582,22 @@ func (c *CodeScanningAlertEvent) GetSender() *User { return c.Sender } +// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertState) GetDismissedComment() string { + if c == nil || c.DismissedComment == nil { + return "" + } + return *c.DismissedComment +} + +// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertState) GetDismissedReason() string { + if c == nil || c.DismissedReason == nil { + return "" + } + return *c.DismissedReason +} + // GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. func (c *CodeSearchResult) GetIncompleteResults() bool { if c == nil || c.IncompleteResults == nil { @@ -2607,9 +2727,9 @@ func (c *CombinedStatus) GetTotalCount() int { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (c *Comment) GetCreatedAt() time.Time { +func (c *Comment) GetCreatedAt() Timestamp { if c == nil || c.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *c.CreatedAt } @@ -2735,9 +2855,9 @@ func (c *Commit) GetVerification() *SignatureVerification { } // GetDate returns the Date field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetDate() time.Time { +func (c *CommitAuthor) GetDate() Timestamp { if c == nil || c.Date == nil { - return time.Time{} + return Timestamp{} } return *c.Date } @@ -3191,9 +3311,9 @@ func (c *CommunityHealthMetrics) GetHealthPercentage() int { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (c *CommunityHealthMetrics) GetUpdatedAt() time.Time { +func (c *CommunityHealthMetrics) GetUpdatedAt() Timestamp { if c == nil || c.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *c.UpdatedAt } @@ -4134,6 +4254,46 @@ func (d *Deployment) GetURL() string { return *d.URL } +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicyRequest) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicyResponse) GetTotalCount() int { + if d == nil || d.TotalCount == nil { + return 0 + } + return *d.TotalCount +} + // GetDeployment returns the Deployment field. func (d *DeploymentEvent) GetDeployment() *Deployment { if d == nil { @@ -5110,6 +5270,38 @@ func (e *Enterprise) GetWebsiteURL() string { return *e.WebsiteURL } +// GetAdvancedSecurityEnabledForNewRepositories returns the AdvancedSecurityEnabledForNewRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetAdvancedSecurityEnabledForNewRepositories() bool { + if e == nil || e.AdvancedSecurityEnabledForNewRepositories == nil { + return false + } + return *e.AdvancedSecurityEnabledForNewRepositories +} + +// GetSecretScanningEnabledForNewRepositories returns the SecretScanningEnabledForNewRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningEnabledForNewRepositories() bool { + if e == nil || e.SecretScanningEnabledForNewRepositories == nil { + return false + } + return *e.SecretScanningEnabledForNewRepositories +} + +// GetSecretScanningPushProtectionCustomLink returns the SecretScanningPushProtectionCustomLink field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningPushProtectionCustomLink() string { + if e == nil || e.SecretScanningPushProtectionCustomLink == nil { + return "" + } + return *e.SecretScanningPushProtectionCustomLink +} + +// GetSecretScanningPushProtectionEnabledForNewRepositories returns the SecretScanningPushProtectionEnabledForNewRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningPushProtectionEnabledForNewRepositories() bool { + if e == nil || e.SecretScanningPushProtectionEnabledForNewRepositories == nil { + return false + } + return *e.SecretScanningPushProtectionEnabledForNewRepositories +} + // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. func (e *Environment) GetCreatedAt() Timestamp { if e == nil || e.CreatedAt == nil { @@ -5255,9 +5447,9 @@ func (e *Event) GetActor() *User { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (e *Event) GetCreatedAt() time.Time { +func (e *Event) GetCreatedAt() Timestamp { if e == nil || e.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *e.CreatedAt } @@ -5591,9 +5783,9 @@ func (g *Gist) GetComments() int { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *Gist) GetCreatedAt() time.Time { +func (g *Gist) GetCreatedAt() Timestamp { if g == nil || g.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *g.CreatedAt } @@ -5671,9 +5863,9 @@ func (g *Gist) GetPublic() bool { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *Gist) GetUpdatedAt() time.Time { +func (g *Gist) GetUpdatedAt() Timestamp { if g == nil || g.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *g.UpdatedAt } @@ -5687,9 +5879,9 @@ func (g *GistComment) GetBody() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GistComment) GetCreatedAt() time.Time { +func (g *GistComment) GetCreatedAt() Timestamp { if g == nil || g.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *g.CreatedAt } @@ -6023,17 +6215,17 @@ func (g *GPGKey) GetCanSign() bool { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCreatedAt() time.Time { +func (g *GPGKey) GetCreatedAt() Timestamp { if g == nil || g.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *g.CreatedAt } // GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetExpiresAt() time.Time { +func (g *GPGKey) GetExpiresAt() Timestamp { if g == nil || g.ExpiresAt == nil { - return time.Time{} + return Timestamp{} } return *g.ExpiresAt } @@ -6199,9 +6391,9 @@ func (h *Hook) GetActive() bool { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (h *Hook) GetCreatedAt() time.Time { +func (h *Hook) GetCreatedAt() Timestamp { if h == nil || h.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *h.CreatedAt } @@ -6247,9 +6439,9 @@ func (h *Hook) GetType() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (h *Hook) GetUpdatedAt() time.Time { +func (h *Hook) GetUpdatedAt() Timestamp { if h == nil || h.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *h.UpdatedAt } @@ -7151,9 +7343,9 @@ func (i *InstallationRepositoriesEvent) GetSender() *User { } // GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (i *InstallationToken) GetExpiresAt() time.Time { +func (i *InstallationToken) GetExpiresAt() Timestamp { if i == nil || i.ExpiresAt == nil { - return time.Time{} + return Timestamp{} } return *i.ExpiresAt } @@ -7207,9 +7399,9 @@ func (i *InteractionRestriction) GetOrigin() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Invitation) GetCreatedAt() time.Time { +func (i *Invitation) GetCreatedAt() Timestamp { if i == nil || i.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.CreatedAt } @@ -7327,9 +7519,9 @@ func (i *Issue) GetBody() string { } // GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetClosedAt() time.Time { +func (i *Issue) GetClosedAt() Timestamp { if i == nil || i.ClosedAt == nil { - return time.Time{} + return Timestamp{} } return *i.ClosedAt } @@ -7359,9 +7551,9 @@ func (i *Issue) GetCommentsURL() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetCreatedAt() time.Time { +func (i *Issue) GetCreatedAt() Timestamp { if i == nil || i.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.CreatedAt } @@ -7487,9 +7679,9 @@ func (i *Issue) GetTitle() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetUpdatedAt() time.Time { +func (i *Issue) GetUpdatedAt() Timestamp { if i == nil || i.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.UpdatedAt } @@ -7527,9 +7719,9 @@ func (i *IssueComment) GetBody() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetCreatedAt() time.Time { +func (i *IssueComment) GetCreatedAt() Timestamp { if i == nil || i.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.CreatedAt } @@ -7575,9 +7767,9 @@ func (i *IssueComment) GetReactions() *Reactions { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetUpdatedAt() time.Time { +func (i *IssueComment) GetUpdatedAt() Timestamp { if i == nil || i.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.UpdatedAt } @@ -7695,9 +7887,9 @@ func (i *IssueEvent) GetCommitID() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetCreatedAt() time.Time { +func (i *IssueEvent) GetCreatedAt() Timestamp { if i == nil || i.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.CreatedAt } @@ -7815,17 +8007,17 @@ func (i *IssueImport) GetClosed() bool { } // GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (i *IssueImport) GetClosedAt() time.Time { +func (i *IssueImport) GetClosedAt() Timestamp { if i == nil || i.ClosedAt == nil { - return time.Time{} + return Timestamp{} } return *i.ClosedAt } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueImport) GetCreatedAt() time.Time { +func (i *IssueImport) GetCreatedAt() Timestamp { if i == nil || i.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.CreatedAt } @@ -7839,9 +8031,9 @@ func (i *IssueImport) GetMilestone() int { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *IssueImport) GetUpdatedAt() time.Time { +func (i *IssueImport) GetUpdatedAt() Timestamp { if i == nil || i.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.UpdatedAt } @@ -7887,9 +8079,9 @@ func (i *IssueImportError) GetValue() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueImportResponse) GetCreatedAt() time.Time { +func (i *IssueImportResponse) GetCreatedAt() Timestamp { if i == nil || i.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.CreatedAt } @@ -7943,9 +8135,9 @@ func (i *IssueImportResponse) GetStatus() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *IssueImportResponse) GetUpdatedAt() time.Time { +func (i *IssueImportResponse) GetUpdatedAt() Timestamp { if i == nil || i.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *i.UpdatedAt } @@ -8742,6 +8934,14 @@ func (l *Location) GetStartLine() int { return *l.StartLine } +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (l *LockBranch) GetEnabled() bool { + if l == nil || l.Enabled == nil { + return false + } + return *l.Enabled +} + // GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. func (m *MarketplacePendingChange) GetEffectiveDate() Timestamp { if m == nil || m.EffectiveDate == nil { @@ -9455,9 +9655,9 @@ func (m *Migration) GetURL() string { } // GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetClosedAt() time.Time { +func (m *Milestone) GetClosedAt() Timestamp { if m == nil || m.ClosedAt == nil { - return time.Time{} + return Timestamp{} } return *m.ClosedAt } @@ -9471,9 +9671,9 @@ func (m *Milestone) GetClosedIssues() int { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetCreatedAt() time.Time { +func (m *Milestone) GetCreatedAt() Timestamp { if m == nil || m.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *m.CreatedAt } @@ -9495,9 +9695,9 @@ func (m *Milestone) GetDescription() string { } // GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. -func (m *Milestone) GetDueOn() time.Time { +func (m *Milestone) GetDueOn() Timestamp { if m == nil || m.DueOn == nil { - return time.Time{} + return Timestamp{} } return *m.DueOn } @@ -9567,9 +9767,9 @@ func (m *Milestone) GetTitle() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetUpdatedAt() time.Time { +func (m *Milestone) GetUpdatedAt() Timestamp { if m == nil || m.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *m.UpdatedAt } @@ -9823,9 +10023,9 @@ func (n *Notification) GetID() string { } // GetLastReadAt returns the LastReadAt field if it's non-nil, zero value otherwise. -func (n *Notification) GetLastReadAt() time.Time { +func (n *Notification) GetLastReadAt() Timestamp { if n == nil || n.LastReadAt == nil { - return time.Time{} + return Timestamp{} } return *n.LastReadAt } @@ -9863,9 +10063,9 @@ func (n *Notification) GetUnread() bool { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (n *Notification) GetUpdatedAt() time.Time { +func (n *Notification) GetUpdatedAt() Timestamp { if n == nil || n.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *n.UpdatedAt } @@ -9934,6 +10134,14 @@ func (o *OAuthAPP) GetURL() string { return *o.URL } +// GetUseDefault returns the UseDefault field if it's non-nil, zero value otherwise. +func (o *OIDCSubjectClaimCustomTemplate) GetUseDefault() bool { + if o == nil || o.UseDefault == nil { + return false + } + return *o.UseDefault +} + // GetAdvancedSecurityEnabledForNewRepos returns the AdvancedSecurityEnabledForNewRepos field if it's non-nil, zero value otherwise. func (o *Organization) GetAdvancedSecurityEnabledForNewRepos() bool { if o == nil || o.AdvancedSecurityEnabledForNewRepos == nil { @@ -9983,9 +10191,9 @@ func (o *Organization) GetCompany() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (o *Organization) GetCreatedAt() time.Time { +func (o *Organization) GetCreatedAt() Timestamp { if o == nil || o.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *o.CreatedAt } @@ -10351,9 +10559,9 @@ func (o *Organization) GetType() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (o *Organization) GetUpdatedAt() time.Time { +func (o *Organization) GetUpdatedAt() Timestamp { if o == nil || o.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *o.UpdatedAt } @@ -12262,12 +12470,12 @@ func (p *Protection) GetAllowForcePushes() *AllowForcePushes { return p.AllowForcePushes } -// GetAllowForkSyncing returns the AllowForkSyncing field if it's non-nil, zero value otherwise. -func (p *Protection) GetAllowForkSyncing() bool { - if p == nil || p.AllowForkSyncing == nil { - return false +// GetAllowForkSyncing returns the AllowForkSyncing field. +func (p *Protection) GetAllowForkSyncing() *AllowForkSyncing { + if p == nil { + return nil } - return *p.AllowForkSyncing + return p.AllowForkSyncing } // GetEnforceAdmins returns the EnforceAdmins field. @@ -12278,12 +12486,12 @@ func (p *Protection) GetEnforceAdmins() *AdminEnforcement { return p.EnforceAdmins } -// GetLockBranch returns the LockBranch field if it's non-nil, zero value otherwise. -func (p *Protection) GetLockBranch() bool { - if p == nil || p.LockBranch == nil { - return false +// GetLockBranch returns the LockBranch field. +func (p *Protection) GetLockBranch() *LockBranch { + if p == nil { + return nil } - return *p.LockBranch + return p.LockBranch } // GetRequiredConversationResolution returns the RequiredConversationResolution field. @@ -12639,9 +12847,9 @@ func (p *PullRequest) GetChangedFiles() int { } // GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetClosedAt() time.Time { +func (p *PullRequest) GetClosedAt() Timestamp { if p == nil || p.ClosedAt == nil { - return time.Time{} + return Timestamp{} } return *p.ClosedAt } @@ -12679,9 +12887,9 @@ func (p *PullRequest) GetCommitsURL() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCreatedAt() time.Time { +func (p *PullRequest) GetCreatedAt() Timestamp { if p == nil || p.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *p.CreatedAt } @@ -12799,9 +13007,9 @@ func (p *PullRequest) GetMerged() bool { } // GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergedAt() time.Time { +func (p *PullRequest) GetMergedAt() Timestamp { if p == nil || p.MergedAt == nil { - return time.Time{} + return Timestamp{} } return *p.MergedAt } @@ -12903,9 +13111,9 @@ func (p *PullRequest) GetTitle() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetUpdatedAt() time.Time { +func (p *PullRequest) GetUpdatedAt() Timestamp { if p == nil || p.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *p.UpdatedAt } @@ -13047,9 +13255,9 @@ func (p *PullRequestComment) GetCommitID() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetCreatedAt() time.Time { +func (p *PullRequestComment) GetCreatedAt() Timestamp { if p == nil || p.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *p.CreatedAt } @@ -13199,9 +13407,9 @@ func (p *PullRequestComment) GetStartSide() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetUpdatedAt() time.Time { +func (p *PullRequestComment) GetUpdatedAt() Timestamp { if p == nil || p.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *p.UpdatedAt } @@ -13455,9 +13663,9 @@ func (p *PullRequestReview) GetState() string { } // GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetSubmittedAt() time.Time { +func (p *PullRequestReview) GetSubmittedAt() Timestamp { if p == nil || p.SubmittedAt == nil { - return time.Time{} + return Timestamp{} } return *p.SubmittedAt } @@ -13662,6 +13870,14 @@ func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() return p.DismissalRestrictionsRequest } +// GetRequireLastPushApproval returns the RequireLastPushApproval field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementRequest) GetRequireLastPushApproval() bool { + if p == nil || p.RequireLastPushApproval == nil { + return false + } + return *p.RequireLastPushApproval +} + // GetBypassPullRequestAllowancesRequest returns the BypassPullRequestAllowancesRequest field. func (p *PullRequestReviewsEnforcementUpdate) GetBypassPullRequestAllowancesRequest() *BypassPullRequestAllowancesRequest { if p == nil { @@ -15158,6 +15374,14 @@ func (r *Repository) GetGitURL() string { return *r.GitURL } +// GetHasDiscussions returns the HasDiscussions field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasDiscussions() bool { + if r == nil || r.HasDiscussions == nil { + return false + } + return *r.HasDiscussions +} + // GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. func (r *Repository) GetHasDownloads() bool { if r == nil || r.HasDownloads == nil { @@ -15670,6 +15894,14 @@ func (r *Repository) GetWatchersCount() int { return *r.WatchersCount } +// GetAccessLevel returns the AccessLevel field if it's non-nil, zero value otherwise. +func (r *RepositoryActionsAccessLevel) GetAccessLevel() string { + if r == nil || r.AccessLevel == nil { + return "" + } + return *r.AccessLevel +} + // GetAdvancedSecurityCommitters returns the AdvancedSecurityCommitters field if it's non-nil, zero value otherwise. func (r *RepositoryActiveCommitters) GetAdvancedSecurityCommitters() int { if r == nil || r.AdvancedSecurityCommitters == nil { @@ -15703,9 +15935,9 @@ func (r *RepositoryComment) GetCommitID() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetCreatedAt() time.Time { +func (r *RepositoryComment) GetCreatedAt() Timestamp { if r == nil || r.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *r.CreatedAt } @@ -15759,9 +15991,9 @@ func (r *RepositoryComment) GetReactions() *Reactions { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetUpdatedAt() time.Time { +func (r *RepositoryComment) GetUpdatedAt() Timestamp { if r == nil || r.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *r.UpdatedAt } @@ -16390,6 +16622,14 @@ func (r *RepositoryRelease) GetID() int64 { return *r.ID } +// GetMakeLatest returns the MakeLatest field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetMakeLatest() string { + if r == nil || r.MakeLatest == nil { + return "" + } + return *r.MakeLatest +} + // GetName returns the Name field if it's non-nil, zero value otherwise. func (r *RepositoryRelease) GetName() string { if r == nil || r.Name == nil { @@ -16703,9 +16943,9 @@ func (r *RepoStatus) GetContext() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetCreatedAt() time.Time { +func (r *RepoStatus) GetCreatedAt() Timestamp { if r == nil || r.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *r.CreatedAt } @@ -16759,9 +16999,9 @@ func (r *RepoStatus) GetTargetURL() string { } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetUpdatedAt() time.Time { +func (r *RepoStatus) GetUpdatedAt() Timestamp { if r == nil || r.UpdatedAt == nil { - return time.Time{} + return Timestamp{} } return *r.UpdatedAt } @@ -19023,9 +19263,9 @@ func (t *Timeline) GetCommitURL() string { } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCreatedAt() time.Time { +func (t *Timeline) GetCreatedAt() Timestamp { if t == nil || t.CreatedAt == nil { - return time.Time{} + return Timestamp{} } return *t.CreatedAt } @@ -19127,9 +19367,9 @@ func (t *Timeline) GetState() string { } // GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. -func (t *Timeline) GetSubmittedAt() time.Time { +func (t *Timeline) GetSubmittedAt() Timestamp { if t == nil || t.SubmittedAt == nil { - return time.Time{} + return Timestamp{} } return *t.SubmittedAt } @@ -20542,30 +20782,6 @@ func (w *WorkflowDispatchEvent) GetWorkflow() string { return *w.Workflow } -// GetMacOS returns the MacOS field. -func (w *WorkflowEnvironment) GetMacOS() *WorkflowBill { - if w == nil { - return nil - } - return w.MacOS -} - -// GetUbuntu returns the Ubuntu field. -func (w *WorkflowEnvironment) GetUbuntu() *WorkflowBill { - if w == nil { - return nil - } - return w.Ubuntu -} - -// GetWindows returns the Windows field. -func (w *WorkflowEnvironment) GetWindows() *WorkflowBill { - if w == nil { - return nil - } - return w.Windows -} - // GetCheckRunURL returns the CheckRunURL field if it's non-nil, zero value otherwise. func (w *WorkflowJob) GetCheckRunURL() string { if w == nil || w.CheckRunURL == nil { @@ -20710,6 +20926,14 @@ func (w *WorkflowJob) GetURL() string { return *w.URL } +// GetWorkflowName returns the WorkflowName field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetWorkflowName() string { + if w == nil || w.WorkflowName == nil { + return "" + } + return *w.WorkflowName +} + // GetAction returns the Action field if it's non-nil, zero value otherwise. func (w *WorkflowJobEvent) GetAction() string { if w == nil || w.Action == nil { @@ -21022,30 +21246,6 @@ func (w *WorkflowRunBill) GetTotalMS() int64 { return *w.TotalMS } -// GetMacOS returns the MacOS field. -func (w *WorkflowRunEnvironment) GetMacOS() *WorkflowRunBill { - if w == nil { - return nil - } - return w.MacOS -} - -// GetUbuntu returns the Ubuntu field. -func (w *WorkflowRunEnvironment) GetUbuntu() *WorkflowRunBill { - if w == nil { - return nil - } - return w.Ubuntu -} - -// GetWindows returns the Windows field. -func (w *WorkflowRunEnvironment) GetWindows() *WorkflowRunBill { - if w == nil { - return nil - } - return w.Windows -} - // GetAction returns the Action field if it's non-nil, zero value otherwise. func (w *WorkflowRunEvent) GetAction() string { if w == nil || w.Action == nil { @@ -21127,7 +21327,7 @@ func (w *WorkflowRuns) GetTotalCount() int { } // GetBillable returns the Billable field. -func (w *WorkflowRunUsage) GetBillable() *WorkflowRunEnvironment { +func (w *WorkflowRunUsage) GetBillable() *WorkflowRunBillMap { if w == nil { return nil } @@ -21151,7 +21351,7 @@ func (w *Workflows) GetTotalCount() int { } // GetBillable returns the Billable field. -func (w *WorkflowUsage) GetBillable() *WorkflowEnvironment { +func (w *WorkflowUsage) GetBillable() *WorkflowBillMap { if w == nil { return nil } diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 05fa1961a1..24e0a75826 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -45,6 +45,116 @@ func TestActionsAllowed_GetVerifiedAllowed(tt *testing.T) { a.GetVerifiedAllowed() } +func TestActionsCache_GetCreatedAt(tt *testing.T) { + var zeroValue Timestamp + a := &ActionsCache{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &ActionsCache{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestActionsCache_GetID(tt *testing.T) { + var zeroValue int64 + a := &ActionsCache{ID: &zeroValue} + a.GetID() + a = &ActionsCache{} + a.GetID() + a = nil + a.GetID() +} + +func TestActionsCache_GetKey(tt *testing.T) { + var zeroValue string + a := &ActionsCache{Key: &zeroValue} + a.GetKey() + a = &ActionsCache{} + a.GetKey() + a = nil + a.GetKey() +} + +func TestActionsCache_GetLastAccessedAt(tt *testing.T) { + var zeroValue Timestamp + a := &ActionsCache{LastAccessedAt: &zeroValue} + a.GetLastAccessedAt() + a = &ActionsCache{} + a.GetLastAccessedAt() + a = nil + a.GetLastAccessedAt() +} + +func TestActionsCache_GetRef(tt *testing.T) { + var zeroValue string + a := &ActionsCache{Ref: &zeroValue} + a.GetRef() + a = &ActionsCache{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestActionsCache_GetSizeInBytes(tt *testing.T) { + var zeroValue int64 + a := &ActionsCache{SizeInBytes: &zeroValue} + a.GetSizeInBytes() + a = &ActionsCache{} + a.GetSizeInBytes() + a = nil + a.GetSizeInBytes() +} + +func TestActionsCache_GetVersion(tt *testing.T) { + var zeroValue string + a := &ActionsCache{Version: &zeroValue} + a.GetVersion() + a = &ActionsCache{} + a.GetVersion() + a = nil + a.GetVersion() +} + +func TestActionsCacheListOptions_GetDirection(tt *testing.T) { + var zeroValue string + a := &ActionsCacheListOptions{Direction: &zeroValue} + a.GetDirection() + a = &ActionsCacheListOptions{} + a.GetDirection() + a = nil + a.GetDirection() +} + +func TestActionsCacheListOptions_GetKey(tt *testing.T) { + var zeroValue string + a := &ActionsCacheListOptions{Key: &zeroValue} + a.GetKey() + a = &ActionsCacheListOptions{} + a.GetKey() + a = nil + a.GetKey() +} + +func TestActionsCacheListOptions_GetRef(tt *testing.T) { + var zeroValue string + a := &ActionsCacheListOptions{Ref: &zeroValue} + a.GetRef() + a = &ActionsCacheListOptions{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestActionsCacheListOptions_GetSort(tt *testing.T) { + var zeroValue string + a := &ActionsCacheListOptions{Sort: &zeroValue} + a.GetSort() + a = &ActionsCacheListOptions{} + a.GetSort() + a = nil + a.GetSort() +} + func TestActionsPermissions_GetAllowedActions(tt *testing.T) { var zeroValue string a := &ActionsPermissions{AllowedActions: &zeroValue} @@ -528,6 +638,16 @@ func TestAllowDeletionsEnforcementLevelChanges_GetFrom(tt *testing.T) { a.GetFrom() } +func TestAllowForkSyncing_GetEnabled(tt *testing.T) { + var zeroValue bool + a := &AllowForkSyncing{Enabled: &zeroValue} + a.GetEnabled() + a = &AllowForkSyncing{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + func TestAnalysesListOptions_GetRef(tt *testing.T) { var zeroValue string a := &AnalysesListOptions{Ref: &zeroValue} @@ -2890,6 +3010,13 @@ func TestCodeScanningAlertEvent_GetCommitOID(tt *testing.T) { c.GetCommitOID() } +func TestCodeScanningAlertEvent_GetInstallation(tt *testing.T) { + c := &CodeScanningAlertEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + func TestCodeScanningAlertEvent_GetOrg(tt *testing.T) { c := &CodeScanningAlertEvent{} c.GetOrg() @@ -2921,6 +3048,26 @@ func TestCodeScanningAlertEvent_GetSender(tt *testing.T) { c.GetSender() } +func TestCodeScanningAlertState_GetDismissedComment(tt *testing.T) { + var zeroValue string + c := &CodeScanningAlertState{DismissedComment: &zeroValue} + c.GetDismissedComment() + c = &CodeScanningAlertState{} + c.GetDismissedComment() + c = nil + c.GetDismissedComment() +} + +func TestCodeScanningAlertState_GetDismissedReason(tt *testing.T) { + var zeroValue string + c := &CodeScanningAlertState{DismissedReason: &zeroValue} + c.GetDismissedReason() + c = &CodeScanningAlertState{} + c.GetDismissedReason() + c = nil + c.GetDismissedReason() +} + func TestCodeSearchResult_GetIncompleteResults(tt *testing.T) { var zeroValue bool c := &CodeSearchResult{IncompleteResults: &zeroValue} @@ -3073,7 +3220,7 @@ func TestCombinedStatus_GetTotalCount(tt *testing.T) { } func TestComment_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp c := &Comment{CreatedAt: &zeroValue} c.GetCreatedAt() c = &Comment{} @@ -3218,7 +3365,7 @@ func TestCommit_GetVerification(tt *testing.T) { } func TestCommitAuthor_GetDate(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp c := &CommitAuthor{Date: &zeroValue} c.GetDate() c = &CommitAuthor{} @@ -3731,7 +3878,7 @@ func TestCommunityHealthMetrics_GetHealthPercentage(tt *testing.T) { } func TestCommunityHealthMetrics_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp c := &CommunityHealthMetrics{UpdatedAt: &zeroValue} c.GetUpdatedAt() c = &CommunityHealthMetrics{} @@ -4832,6 +4979,56 @@ func TestDeployment_GetURL(tt *testing.T) { d.GetURL() } +func TestDeploymentBranchPolicy_GetID(tt *testing.T) { + var zeroValue int64 + d := &DeploymentBranchPolicy{ID: &zeroValue} + d.GetID() + d = &DeploymentBranchPolicy{} + d.GetID() + d = nil + d.GetID() +} + +func TestDeploymentBranchPolicy_GetName(tt *testing.T) { + var zeroValue string + d := &DeploymentBranchPolicy{Name: &zeroValue} + d.GetName() + d = &DeploymentBranchPolicy{} + d.GetName() + d = nil + d.GetName() +} + +func TestDeploymentBranchPolicy_GetNodeID(tt *testing.T) { + var zeroValue string + d := &DeploymentBranchPolicy{NodeID: &zeroValue} + d.GetNodeID() + d = &DeploymentBranchPolicy{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDeploymentBranchPolicyRequest_GetName(tt *testing.T) { + var zeroValue string + d := &DeploymentBranchPolicyRequest{Name: &zeroValue} + d.GetName() + d = &DeploymentBranchPolicyRequest{} + d.GetName() + d = nil + d.GetName() +} + +func TestDeploymentBranchPolicyResponse_GetTotalCount(tt *testing.T) { + var zeroValue int + d := &DeploymentBranchPolicyResponse{TotalCount: &zeroValue} + d.GetTotalCount() + d = &DeploymentBranchPolicyResponse{} + d.GetTotalCount() + d = nil + d.GetTotalCount() +} + func TestDeploymentEvent_GetDeployment(tt *testing.T) { d := &DeploymentEvent{} d.GetDeployment() @@ -5974,6 +6171,46 @@ func TestEnterprise_GetWebsiteURL(tt *testing.T) { e.GetWebsiteURL() } +func TestEnterpriseSecurityAnalysisSettings_GetAdvancedSecurityEnabledForNewRepositories(tt *testing.T) { + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{AdvancedSecurityEnabledForNewRepositories: &zeroValue} + e.GetAdvancedSecurityEnabledForNewRepositories() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetAdvancedSecurityEnabledForNewRepositories() + e = nil + e.GetAdvancedSecurityEnabledForNewRepositories() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningEnabledForNewRepositories(tt *testing.T) { + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{SecretScanningEnabledForNewRepositories: &zeroValue} + e.GetSecretScanningEnabledForNewRepositories() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningEnabledForNewRepositories() + e = nil + e.GetSecretScanningEnabledForNewRepositories() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningPushProtectionCustomLink(tt *testing.T) { + var zeroValue string + e := &EnterpriseSecurityAnalysisSettings{SecretScanningPushProtectionCustomLink: &zeroValue} + e.GetSecretScanningPushProtectionCustomLink() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningPushProtectionCustomLink() + e = nil + e.GetSecretScanningPushProtectionCustomLink() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningPushProtectionEnabledForNewRepositories(tt *testing.T) { + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{SecretScanningPushProtectionEnabledForNewRepositories: &zeroValue} + e.GetSecretScanningPushProtectionEnabledForNewRepositories() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningPushProtectionEnabledForNewRepositories() + e = nil + e.GetSecretScanningPushProtectionEnabledForNewRepositories() +} + func TestEnvironment_GetCreatedAt(tt *testing.T) { var zeroValue Timestamp e := &Environment{CreatedAt: &zeroValue} @@ -6146,7 +6383,7 @@ func TestEvent_GetActor(tt *testing.T) { } func TestEvent_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp e := &Event{CreatedAt: &zeroValue} e.GetCreatedAt() e = &Event{} @@ -6527,7 +6764,7 @@ func TestGist_GetComments(tt *testing.T) { } func TestGist_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp g := &Gist{CreatedAt: &zeroValue} g.GetCreatedAt() g = &Gist{} @@ -6624,7 +6861,7 @@ func TestGist_GetPublic(tt *testing.T) { } func TestGist_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp g := &Gist{UpdatedAt: &zeroValue} g.GetUpdatedAt() g = &Gist{} @@ -6644,7 +6881,7 @@ func TestGistComment_GetBody(tt *testing.T) { } func TestGistComment_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp g := &GistComment{CreatedAt: &zeroValue} g.GetCreatedAt() g = &GistComment{} @@ -7037,7 +7274,7 @@ func TestGPGKey_GetCanSign(tt *testing.T) { } func TestGPGKey_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp g := &GPGKey{CreatedAt: &zeroValue} g.GetCreatedAt() g = &GPGKey{} @@ -7047,7 +7284,7 @@ func TestGPGKey_GetCreatedAt(tt *testing.T) { } func TestGPGKey_GetExpiresAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp g := &GPGKey{ExpiresAt: &zeroValue} g.GetExpiresAt() g = &GPGKey{} @@ -7248,7 +7485,7 @@ func TestHook_GetActive(tt *testing.T) { } func TestHook_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp h := &Hook{CreatedAt: &zeroValue} h.GetCreatedAt() h = &Hook{} @@ -7308,7 +7545,7 @@ func TestHook_GetType(tt *testing.T) { } func TestHook_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp h := &Hook{UpdatedAt: &zeroValue} h.GetUpdatedAt() h = &Hook{} @@ -8408,7 +8645,7 @@ func TestInstallationRepositoriesEvent_GetSender(tt *testing.T) { } func TestInstallationToken_GetExpiresAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &InstallationToken{ExpiresAt: &zeroValue} i.GetExpiresAt() i = &InstallationToken{} @@ -8472,7 +8709,7 @@ func TestInteractionRestriction_GetOrigin(tt *testing.T) { } func TestInvitation_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &Invitation{CreatedAt: &zeroValue} i.GetCreatedAt() i = &Invitation{} @@ -8616,7 +8853,7 @@ func TestIssue_GetBody(tt *testing.T) { } func TestIssue_GetClosedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &Issue{ClosedAt: &zeroValue} i.GetClosedAt() i = &Issue{} @@ -8653,7 +8890,7 @@ func TestIssue_GetCommentsURL(tt *testing.T) { } func TestIssue_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &Issue{CreatedAt: &zeroValue} i.GetCreatedAt() i = &Issue{} @@ -8801,7 +9038,7 @@ func TestIssue_GetTitle(tt *testing.T) { } func TestIssue_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &Issue{UpdatedAt: &zeroValue} i.GetUpdatedAt() i = &Issue{} @@ -8848,7 +9085,7 @@ func TestIssueComment_GetBody(tt *testing.T) { } func TestIssueComment_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueComment{CreatedAt: &zeroValue} i.GetCreatedAt() i = &IssueComment{} @@ -8905,7 +9142,7 @@ func TestIssueComment_GetReactions(tt *testing.T) { } func TestIssueComment_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueComment{UpdatedAt: &zeroValue} i.GetUpdatedAt() i = &IssueComment{} @@ -9022,7 +9259,7 @@ func TestIssueEvent_GetCommitID(tt *testing.T) { } func TestIssueEvent_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueEvent{CreatedAt: &zeroValue} i.GetCreatedAt() i = &IssueEvent{} @@ -9148,7 +9385,7 @@ func TestIssueImport_GetClosed(tt *testing.T) { } func TestIssueImport_GetClosedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueImport{ClosedAt: &zeroValue} i.GetClosedAt() i = &IssueImport{} @@ -9158,7 +9395,7 @@ func TestIssueImport_GetClosedAt(tt *testing.T) { } func TestIssueImport_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueImport{CreatedAt: &zeroValue} i.GetCreatedAt() i = &IssueImport{} @@ -9178,7 +9415,7 @@ func TestIssueImport_GetMilestone(tt *testing.T) { } func TestIssueImport_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueImport{UpdatedAt: &zeroValue} i.GetUpdatedAt() i = &IssueImport{} @@ -9238,7 +9475,7 @@ func TestIssueImportError_GetValue(tt *testing.T) { } func TestIssueImportResponse_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueImportResponse{CreatedAt: &zeroValue} i.GetCreatedAt() i = &IssueImportResponse{} @@ -9308,7 +9545,7 @@ func TestIssueImportResponse_GetStatus(tt *testing.T) { } func TestIssueImportResponse_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp i := &IssueImportResponse{UpdatedAt: &zeroValue} i.GetUpdatedAt() i = &IssueImportResponse{} @@ -10265,6 +10502,16 @@ func TestLocation_GetStartLine(tt *testing.T) { l.GetStartLine() } +func TestLockBranch_GetEnabled(tt *testing.T) { + var zeroValue bool + l := &LockBranch{Enabled: &zeroValue} + l.GetEnabled() + l = &LockBranch{} + l.GetEnabled() + l = nil + l.GetEnabled() +} + func TestMarketplacePendingChange_GetEffectiveDate(tt *testing.T) { var zeroValue Timestamp m := &MarketplacePendingChange{EffectiveDate: &zeroValue} @@ -11066,7 +11313,7 @@ func TestMigration_GetURL(tt *testing.T) { } func TestMilestone_GetClosedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp m := &Milestone{ClosedAt: &zeroValue} m.GetClosedAt() m = &Milestone{} @@ -11086,7 +11333,7 @@ func TestMilestone_GetClosedIssues(tt *testing.T) { } func TestMilestone_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp m := &Milestone{CreatedAt: &zeroValue} m.GetCreatedAt() m = &Milestone{} @@ -11113,7 +11360,7 @@ func TestMilestone_GetDescription(tt *testing.T) { } func TestMilestone_GetDueOn(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp m := &Milestone{DueOn: &zeroValue} m.GetDueOn() m = &Milestone{} @@ -11203,7 +11450,7 @@ func TestMilestone_GetTitle(tt *testing.T) { } func TestMilestone_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp m := &Milestone{UpdatedAt: &zeroValue} m.GetUpdatedAt() m = &Milestone{} @@ -11499,7 +11746,7 @@ func TestNotification_GetID(tt *testing.T) { } func TestNotification_GetLastReadAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp n := &Notification{LastReadAt: &zeroValue} n.GetLastReadAt() n = &Notification{} @@ -11543,7 +11790,7 @@ func TestNotification_GetUnread(tt *testing.T) { } func TestNotification_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp n := &Notification{UpdatedAt: &zeroValue} n.GetUpdatedAt() n = &Notification{} @@ -11632,6 +11879,16 @@ func TestOAuthAPP_GetURL(tt *testing.T) { o.GetURL() } +func TestOIDCSubjectClaimCustomTemplate_GetUseDefault(tt *testing.T) { + var zeroValue bool + o := &OIDCSubjectClaimCustomTemplate{UseDefault: &zeroValue} + o.GetUseDefault() + o = &OIDCSubjectClaimCustomTemplate{} + o.GetUseDefault() + o = nil + o.GetUseDefault() +} + func TestOrganization_GetAdvancedSecurityEnabledForNewRepos(tt *testing.T) { var zeroValue bool o := &Organization{AdvancedSecurityEnabledForNewRepos: &zeroValue} @@ -11693,7 +11950,7 @@ func TestOrganization_GetCompany(tt *testing.T) { } func TestOrganization_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp o := &Organization{CreatedAt: &zeroValue} o.GetCreatedAt() o = &Organization{} @@ -12150,7 +12407,7 @@ func TestOrganization_GetType(tt *testing.T) { } func TestOrganization_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp o := &Organization{UpdatedAt: &zeroValue} o.GetUpdatedAt() o = &Organization{} @@ -14321,10 +14578,7 @@ func TestProtection_GetAllowForcePushes(tt *testing.T) { } func TestProtection_GetAllowForkSyncing(tt *testing.T) { - var zeroValue bool - p := &Protection{AllowForkSyncing: &zeroValue} - p.GetAllowForkSyncing() - p = &Protection{} + p := &Protection{} p.GetAllowForkSyncing() p = nil p.GetAllowForkSyncing() @@ -14338,10 +14592,7 @@ func TestProtection_GetEnforceAdmins(tt *testing.T) { } func TestProtection_GetLockBranch(tt *testing.T) { - var zeroValue bool - p := &Protection{LockBranch: &zeroValue} - p.GetLockBranch() - p = &Protection{} + p := &Protection{} p.GetLockBranch() p = nil p.GetLockBranch() @@ -14701,7 +14952,7 @@ func TestPullRequest_GetChangedFiles(tt *testing.T) { } func TestPullRequest_GetClosedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequest{ClosedAt: &zeroValue} p.GetClosedAt() p = &PullRequest{} @@ -14751,7 +15002,7 @@ func TestPullRequest_GetCommitsURL(tt *testing.T) { } func TestPullRequest_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequest{CreatedAt: &zeroValue} p.GetCreatedAt() p = &PullRequest{} @@ -14895,7 +15146,7 @@ func TestPullRequest_GetMerged(tt *testing.T) { } func TestPullRequest_GetMergedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequest{MergedAt: &zeroValue} p.GetMergedAt() p = &PullRequest{} @@ -15019,7 +15270,7 @@ func TestPullRequest_GetTitle(tt *testing.T) { } func TestPullRequest_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequest{UpdatedAt: &zeroValue} p.GetUpdatedAt() p = &PullRequest{} @@ -15187,7 +15438,7 @@ func TestPullRequestComment_GetCommitID(tt *testing.T) { } func TestPullRequestComment_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequestComment{CreatedAt: &zeroValue} p.GetCreatedAt() p = &PullRequestComment{} @@ -15374,7 +15625,7 @@ func TestPullRequestComment_GetStartSide(tt *testing.T) { } func TestPullRequestComment_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequestComment{UpdatedAt: &zeroValue} p.GetUpdatedAt() p = &PullRequestComment{} @@ -15661,7 +15912,7 @@ func TestPullRequestReview_GetState(tt *testing.T) { } func TestPullRequestReview_GetSubmittedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp p := &PullRequestReview{SubmittedAt: &zeroValue} p.GetSubmittedAt() p = &PullRequestReview{} @@ -15869,6 +16120,16 @@ func TestPullRequestReviewsEnforcementRequest_GetDismissalRestrictionsRequest(tt p.GetDismissalRestrictionsRequest() } +func TestPullRequestReviewsEnforcementRequest_GetRequireLastPushApproval(tt *testing.T) { + var zeroValue bool + p := &PullRequestReviewsEnforcementRequest{RequireLastPushApproval: &zeroValue} + p.GetRequireLastPushApproval() + p = &PullRequestReviewsEnforcementRequest{} + p.GetRequireLastPushApproval() + p = nil + p.GetRequireLastPushApproval() +} + func TestPullRequestReviewsEnforcementUpdate_GetBypassPullRequestAllowancesRequest(tt *testing.T) { p := &PullRequestReviewsEnforcementUpdate{} p.GetBypassPullRequestAllowancesRequest() @@ -17619,6 +17880,16 @@ func TestRepository_GetGitURL(tt *testing.T) { r.GetGitURL() } +func TestRepository_GetHasDiscussions(tt *testing.T) { + var zeroValue bool + r := &Repository{HasDiscussions: &zeroValue} + r.GetHasDiscussions() + r = &Repository{} + r.GetHasDiscussions() + r = nil + r.GetHasDiscussions() +} + func TestRepository_GetHasDownloads(tt *testing.T) { var zeroValue bool r := &Repository{HasDownloads: &zeroValue} @@ -18238,6 +18509,16 @@ func TestRepository_GetWatchersCount(tt *testing.T) { r.GetWatchersCount() } +func TestRepositoryActionsAccessLevel_GetAccessLevel(tt *testing.T) { + var zeroValue string + r := &RepositoryActionsAccessLevel{AccessLevel: &zeroValue} + r.GetAccessLevel() + r = &RepositoryActionsAccessLevel{} + r.GetAccessLevel() + r = nil + r.GetAccessLevel() +} + func TestRepositoryActiveCommitters_GetAdvancedSecurityCommitters(tt *testing.T) { var zeroValue int r := &RepositoryActiveCommitters{AdvancedSecurityCommitters: &zeroValue} @@ -18279,7 +18560,7 @@ func TestRepositoryComment_GetCommitID(tt *testing.T) { } func TestRepositoryComment_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp r := &RepositoryComment{CreatedAt: &zeroValue} r.GetCreatedAt() r = &RepositoryComment{} @@ -18346,7 +18627,7 @@ func TestRepositoryComment_GetReactions(tt *testing.T) { } func TestRepositoryComment_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp r := &RepositoryComment{UpdatedAt: &zeroValue} r.GetUpdatedAt() r = &RepositoryComment{} @@ -19057,6 +19338,16 @@ func TestRepositoryRelease_GetID(tt *testing.T) { r.GetID() } +func TestRepositoryRelease_GetMakeLatest(tt *testing.T) { + var zeroValue string + r := &RepositoryRelease{MakeLatest: &zeroValue} + r.GetMakeLatest() + r = &RepositoryRelease{} + r.GetMakeLatest() + r = nil + r.GetMakeLatest() +} + func TestRepositoryRelease_GetName(tt *testing.T) { var zeroValue string r := &RepositoryRelease{Name: &zeroValue} @@ -19430,7 +19721,7 @@ func TestRepoStatus_GetContext(tt *testing.T) { } func TestRepoStatus_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp r := &RepoStatus{CreatedAt: &zeroValue} r.GetCreatedAt() r = &RepoStatus{} @@ -19497,7 +19788,7 @@ func TestRepoStatus_GetTargetURL(tt *testing.T) { } func TestRepoStatus_GetUpdatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp r := &RepoStatus{UpdatedAt: &zeroValue} r.GetUpdatedAt() r = &RepoStatus{} @@ -22162,7 +22453,7 @@ func TestTimeline_GetCommitURL(tt *testing.T) { } func TestTimeline_GetCreatedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp t := &Timeline{CreatedAt: &zeroValue} t.GetCreatedAt() t = &Timeline{} @@ -22271,7 +22562,7 @@ func TestTimeline_GetState(tt *testing.T) { } func TestTimeline_GetSubmittedAt(tt *testing.T) { - var zeroValue time.Time + var zeroValue Timestamp t := &Timeline{SubmittedAt: &zeroValue} t.GetSubmittedAt() t = &Timeline{} @@ -23992,27 +24283,6 @@ func TestWorkflowDispatchEvent_GetWorkflow(tt *testing.T) { w.GetWorkflow() } -func TestWorkflowEnvironment_GetMacOS(tt *testing.T) { - w := &WorkflowEnvironment{} - w.GetMacOS() - w = nil - w.GetMacOS() -} - -func TestWorkflowEnvironment_GetUbuntu(tt *testing.T) { - w := &WorkflowEnvironment{} - w.GetUbuntu() - w = nil - w.GetUbuntu() -} - -func TestWorkflowEnvironment_GetWindows(tt *testing.T) { - w := &WorkflowEnvironment{} - w.GetWindows() - w = nil - w.GetWindows() -} - func TestWorkflowJob_GetCheckRunURL(tt *testing.T) { var zeroValue string w := &WorkflowJob{CheckRunURL: &zeroValue} @@ -24193,6 +24463,16 @@ func TestWorkflowJob_GetURL(tt *testing.T) { w.GetURL() } +func TestWorkflowJob_GetWorkflowName(tt *testing.T) { + var zeroValue string + w := &WorkflowJob{WorkflowName: &zeroValue} + w.GetWorkflowName() + w = &WorkflowJob{} + w.GetWorkflowName() + w = nil + w.GetWorkflowName() +} + func TestWorkflowJobEvent_GetAction(tt *testing.T) { var zeroValue string w := &WorkflowJobEvent{Action: &zeroValue} @@ -24556,27 +24836,6 @@ func TestWorkflowRunBill_GetTotalMS(tt *testing.T) { w.GetTotalMS() } -func TestWorkflowRunEnvironment_GetMacOS(tt *testing.T) { - w := &WorkflowRunEnvironment{} - w.GetMacOS() - w = nil - w.GetMacOS() -} - -func TestWorkflowRunEnvironment_GetUbuntu(tt *testing.T) { - w := &WorkflowRunEnvironment{} - w.GetUbuntu() - w = nil - w.GetUbuntu() -} - -func TestWorkflowRunEnvironment_GetWindows(tt *testing.T) { - w := &WorkflowRunEnvironment{} - w.GetWindows() - w = nil - w.GetWindows() -} - func TestWorkflowRunEvent_GetAction(tt *testing.T) { var zeroValue string w := &WorkflowRunEvent{Action: &zeroValue} diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go index d4093a0921..e2ec62b283 100644 --- a/github/github-stringify_test.go +++ b/github/github-stringify_test.go @@ -268,11 +268,12 @@ func TestCommit_String(t *testing.T) { func TestCommitAuthor_String(t *testing.T) { v := CommitAuthor{ + Date: &Timestamp{}, Name: String(""), Email: String(""), Login: String(""), } - want := `github.CommitAuthor{Name:"", Email:"", Login:""}` + want := `github.CommitAuthor{Date:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Name:"", Email:"", Login:""}` if got := v.String(); got != want { t.Errorf("CommitAuthor.String = %v, want %v", got, want) } @@ -400,14 +401,15 @@ func TestEnterprise_String(t *testing.T) { func TestEvent_String(t *testing.T) { v := Event{ - Type: String(""), - Public: Bool(false), - Repo: &Repository{}, - Actor: &User{}, - Org: &Organization{}, - ID: String(""), + Type: String(""), + Public: Bool(false), + Repo: &Repository{}, + Actor: &User{}, + Org: &Organization{}, + CreatedAt: &Timestamp{}, + ID: String(""), } - want := `github.Event{Type:"", Public:false, Repo:github.Repository{}, Actor:github.User{}, Org:github.Organization{}, ID:""}` + want := `github.Event{Type:"", Public:false, Repo:github.Repository{}, Actor:github.User{}, Org:github.Organization{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ID:""}` if got := v.String(); got != want { t.Errorf("Event.String = %v, want %v", got, want) } @@ -424,8 +426,10 @@ func TestGPGKey_String(t *testing.T) { CanEncryptComms: Bool(false), CanEncryptStorage: Bool(false), CanCertify: Bool(false), + CreatedAt: &Timestamp{}, + ExpiresAt: &Timestamp{}, } - want := `github.GPGKey{ID:0, PrimaryKeyID:0, KeyID:"", RawKey:"", PublicKey:"", CanSign:false, CanEncryptComms:false, CanEncryptStorage:false, CanCertify:false}` + want := `github.GPGKey{ID:0, PrimaryKeyID:0, KeyID:"", RawKey:"", PublicKey:"", CanSign:false, CanEncryptComms:false, CanEncryptStorage:false, CanCertify:false, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ExpiresAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("GPGKey.String = %v, want %v", got, want) } @@ -441,9 +445,11 @@ func TestGist_String(t *testing.T) { HTMLURL: String(""), GitPullURL: String(""), GitPushURL: String(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, NodeID: String(""), } - want := `github.Gist{ID:"", Description:"", Public:false, Owner:github.User{}, Comments:0, HTMLURL:"", GitPullURL:"", GitPushURL:"", NodeID:""}` + want := `github.Gist{ID:"", Description:"", Public:false, Owner:github.User{}, Comments:0, HTMLURL:"", GitPullURL:"", GitPushURL:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:""}` if got := v.String(); got != want { t.Errorf("Gist.String = %v, want %v", got, want) } @@ -451,12 +457,13 @@ func TestGist_String(t *testing.T) { func TestGistComment_String(t *testing.T) { v := GistComment{ - ID: Int64(0), - URL: String(""), - Body: String(""), - User: &User{}, + ID: Int64(0), + URL: String(""), + Body: String(""), + User: &User{}, + CreatedAt: &Timestamp{}, } - want := `github.GistComment{ID:0, URL:"", Body:"", User:github.User{}}` + want := `github.GistComment{ID:0, URL:"", Body:"", User:github.User{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("GistComment.String = %v, want %v", got, want) } @@ -580,16 +587,18 @@ func TestHeadCommit_String(t *testing.T) { func TestHook_String(t *testing.T) { v := Hook{ - URL: String(""), - ID: Int64(0), - Type: String(""), - Name: String(""), - TestURL: String(""), - PingURL: String(""), - Events: []string{""}, - Active: Bool(false), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + URL: String(""), + ID: Int64(0), + Type: String(""), + Name: String(""), + TestURL: String(""), + PingURL: String(""), + Events: []string{""}, + Active: Bool(false), } - want := `github.Hook{URL:"", ID:0, Type:"", Name:"", TestURL:"", PingURL:"", Events:[""], Active:false}` + want := `github.Hook{CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", ID:0, Type:"", Name:"", TestURL:"", PingURL:"", Events:[""], Active:false}` if got := v.String(); got != want { t.Errorf("Hook.String = %v, want %v", got, want) } @@ -696,13 +705,14 @@ func TestInvitation_String(t *testing.T) { Login: String(""), Email: String(""), Role: String(""), + CreatedAt: &Timestamp{}, Inviter: &User{}, TeamCount: Int(0), InvitationTeamURL: String(""), FailedAt: &Timestamp{}, FailedReason: String(""), } - want := `github.Invitation{ID:0, NodeID:"", Login:"", Email:"", Role:"", Inviter:github.User{}, TeamCount:0, InvitationTeamURL:"", FailedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, FailedReason:""}` + want := `github.Invitation{ID:0, NodeID:"", Login:"", Email:"", Role:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Inviter:github.User{}, TeamCount:0, InvitationTeamURL:"", FailedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, FailedReason:""}` if got := v.String(); got != want { t.Errorf("Invitation.String = %v, want %v", got, want) } @@ -721,6 +731,9 @@ func TestIssue_String(t *testing.T) { User: &User{}, Assignee: &User{}, Comments: Int(0), + ClosedAt: &Timestamp{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, ClosedBy: &User{}, URL: String(""), HTMLURL: String(""), @@ -735,7 +748,7 @@ func TestIssue_String(t *testing.T) { NodeID: String(""), ActiveLockReason: String(""), } - want := `github.Issue{ID:0, Number:0, State:"", StateReason:"", Locked:false, Title:"", Body:"", AuthorAssociation:"", User:github.User{}, Assignee:github.User{}, Comments:0, ClosedBy:github.User{}, URL:"", HTMLURL:"", CommentsURL:"", EventsURL:"", LabelsURL:"", RepositoryURL:"", Milestone:github.Milestone{}, PullRequestLinks:github.PullRequestLinks{}, Repository:github.Repository{}, Reactions:github.Reactions{}, NodeID:"", ActiveLockReason:""}` + want := `github.Issue{ID:0, Number:0, State:"", StateReason:"", Locked:false, Title:"", Body:"", AuthorAssociation:"", User:github.User{}, Assignee:github.User{}, Comments:0, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ClosedBy:github.User{}, URL:"", HTMLURL:"", CommentsURL:"", EventsURL:"", LabelsURL:"", RepositoryURL:"", Milestone:github.Milestone{}, PullRequestLinks:github.PullRequestLinks{}, Repository:github.Repository{}, Reactions:github.Reactions{}, NodeID:"", ActiveLockReason:""}` if got := v.String(); got != want { t.Errorf("Issue.String = %v, want %v", got, want) } @@ -748,12 +761,14 @@ func TestIssueComment_String(t *testing.T) { Body: String(""), User: &User{}, Reactions: &Reactions{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, AuthorAssociation: String(""), URL: String(""), HTMLURL: String(""), IssueURL: String(""), } - want := `github.IssueComment{ID:0, NodeID:"", Body:"", User:github.User{}, Reactions:github.Reactions{}, AuthorAssociation:"", URL:"", HTMLURL:"", IssueURL:""}` + want := `github.IssueComment{ID:0, NodeID:"", Body:"", User:github.User{}, Reactions:github.Reactions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, AuthorAssociation:"", URL:"", HTMLURL:"", IssueURL:""}` if got := v.String(); got != want { t.Errorf("IssueComment.String = %v, want %v", got, want) } @@ -895,9 +910,13 @@ func TestMilestone_String(t *testing.T) { Creator: &User{}, OpenIssues: Int(0), ClosedIssues: Int(0), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + ClosedAt: &Timestamp{}, + DueOn: &Timestamp{}, NodeID: String(""), } - want := `github.Milestone{URL:"", HTMLURL:"", LabelsURL:"", ID:0, Number:0, State:"", Title:"", Description:"", Creator:github.User{}, OpenIssues:0, ClosedIssues:0, NodeID:""}` + want := `github.Milestone{URL:"", HTMLURL:"", LabelsURL:"", ID:0, Number:0, State:"", Title:"", Description:"", Creator:github.User{}, OpenIssues:0, ClosedIssues:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, DueOn:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:""}` if got := v.String(); got != want { t.Errorf("Milestone.String = %v, want %v", got, want) } @@ -975,6 +994,8 @@ func TestOrganization_String(t *testing.T) { PublicGists: Int(0), Followers: Int(0), Following: Int(0), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, TotalPrivateRepos: Int(0), OwnedPrivateRepos: Int(0), PrivateGists: Int(0), @@ -1013,7 +1034,7 @@ func TestOrganization_String(t *testing.T) { PublicMembersURL: String(""), ReposURL: String(""), } - want := `github.Organization{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", Name:"", Company:"", Blog:"", Location:"", Email:"", TwitterUsername:"", Description:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, BillingEmail:"", Type:"", Plan:github.Plan{}, TwoFactorRequirementEnabled:false, IsVerified:false, HasOrganizationProjects:false, HasRepositoryProjects:false, DefaultRepoPermission:"", DefaultRepoSettings:"", MembersCanCreateRepos:false, MembersCanCreatePublicRepos:false, MembersCanCreatePrivateRepos:false, MembersCanCreateInternalRepos:false, MembersCanForkPrivateRepos:false, MembersAllowedRepositoryCreationType:"", MembersCanCreatePages:false, MembersCanCreatePublicPages:false, MembersCanCreatePrivatePages:false, WebCommitSignoffRequired:false, AdvancedSecurityEnabledForNewRepos:false, DependabotAlertsEnabledForNewRepos:false, DependabotSecurityUpdatesEnabledForNewRepos:false, DependencyGraphEnabledForNewRepos:false, SecretScanningEnabledForNewRepos:false, SecretScanningPushProtectionEnabledForNewRepos:false, URL:"", EventsURL:"", HooksURL:"", IssuesURL:"", MembersURL:"", PublicMembersURL:"", ReposURL:""}` + want := `github.Organization{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", Name:"", Company:"", Blog:"", Location:"", Email:"", TwitterUsername:"", Description:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, BillingEmail:"", Type:"", Plan:github.Plan{}, TwoFactorRequirementEnabled:false, IsVerified:false, HasOrganizationProjects:false, HasRepositoryProjects:false, DefaultRepoPermission:"", DefaultRepoSettings:"", MembersCanCreateRepos:false, MembersCanCreatePublicRepos:false, MembersCanCreatePrivateRepos:false, MembersCanCreateInternalRepos:false, MembersCanForkPrivateRepos:false, MembersAllowedRepositoryCreationType:"", MembersCanCreatePages:false, MembersCanCreatePublicPages:false, MembersCanCreatePrivatePages:false, WebCommitSignoffRequired:false, AdvancedSecurityEnabledForNewRepos:false, DependabotAlertsEnabledForNewRepos:false, DependabotSecurityUpdatesEnabledForNewRepos:false, DependencyGraphEnabledForNewRepos:false, SecretScanningEnabledForNewRepos:false, SecretScanningPushProtectionEnabledForNewRepos:false, URL:"", EventsURL:"", HooksURL:"", IssuesURL:"", MembersURL:"", PublicMembersURL:"", ReposURL:""}` if got := v.String(); got != want { t.Errorf("Organization.String = %v, want %v", got, want) } @@ -1217,6 +1238,10 @@ func TestPullRequest_String(t *testing.T) { Locked: Bool(false), Title: String(""), Body: String(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + ClosedAt: &Timestamp{}, + MergedAt: &Timestamp{}, User: &User{}, Draft: Bool(false), Merged: Bool(false), @@ -1252,7 +1277,7 @@ func TestPullRequest_String(t *testing.T) { Base: &PullRequestBranch{}, ActiveLockReason: String(""), } - want := `github.PullRequest{ID:0, Number:0, State:"", Locked:false, Title:"", Body:"", User:github.User{}, Draft:false, Merged:false, Mergeable:false, MergeableState:"", MergedBy:github.User{}, MergeCommitSHA:"", Rebaseable:false, Comments:0, Commits:0, Additions:0, Deletions:0, ChangedFiles:0, URL:"", HTMLURL:"", IssueURL:"", StatusesURL:"", DiffURL:"", PatchURL:"", CommitsURL:"", CommentsURL:"", ReviewCommentsURL:"", ReviewCommentURL:"", ReviewComments:0, Assignee:github.User{}, Milestone:github.Milestone{}, MaintainerCanModify:false, AuthorAssociation:"", NodeID:"", AutoMerge:github.PullRequestAutoMerge{}, Links:github.PRLinks{}, Head:github.PullRequestBranch{}, Base:github.PullRequestBranch{}, ActiveLockReason:""}` + want := `github.PullRequest{ID:0, Number:0, State:"", Locked:false, Title:"", Body:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, MergedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, User:github.User{}, Draft:false, Merged:false, Mergeable:false, MergeableState:"", MergedBy:github.User{}, MergeCommitSHA:"", Rebaseable:false, Comments:0, Commits:0, Additions:0, Deletions:0, ChangedFiles:0, URL:"", HTMLURL:"", IssueURL:"", StatusesURL:"", DiffURL:"", PatchURL:"", CommitsURL:"", CommentsURL:"", ReviewCommentsURL:"", ReviewCommentURL:"", ReviewComments:0, Assignee:github.User{}, Milestone:github.Milestone{}, MaintainerCanModify:false, AuthorAssociation:"", NodeID:"", AutoMerge:github.PullRequestAutoMerge{}, Links:github.PRLinks{}, Head:github.PullRequestBranch{}, Base:github.PullRequestBranch{}, ActiveLockReason:""}` if got := v.String(); got != want { t.Errorf("PullRequest.String = %v, want %v", got, want) } @@ -1279,12 +1304,14 @@ func TestPullRequestComment_String(t *testing.T) { OriginalCommitID: String(""), User: &User{}, Reactions: &Reactions{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, AuthorAssociation: String(""), URL: String(""), HTMLURL: String(""), PullRequestURL: String(""), } - want := `github.PullRequestComment{ID:0, NodeID:"", InReplyTo:0, Body:"", Path:"", DiffHunk:"", PullRequestReviewID:0, Position:0, OriginalPosition:0, StartLine:0, Line:0, OriginalLine:0, OriginalStartLine:0, Side:"", StartSide:"", CommitID:"", OriginalCommitID:"", User:github.User{}, Reactions:github.Reactions{}, AuthorAssociation:"", URL:"", HTMLURL:"", PullRequestURL:""}` + want := `github.PullRequestComment{ID:0, NodeID:"", InReplyTo:0, Body:"", Path:"", DiffHunk:"", PullRequestReviewID:0, Position:0, OriginalPosition:0, StartLine:0, Line:0, OriginalLine:0, OriginalStartLine:0, Side:"", StartSide:"", CommitID:"", OriginalCommitID:"", User:github.User{}, Reactions:github.Reactions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, AuthorAssociation:"", URL:"", HTMLURL:"", PullRequestURL:""}` if got := v.String(); got != want { t.Errorf("PullRequestComment.String = %v, want %v", got, want) } @@ -1296,13 +1323,14 @@ func TestPullRequestReview_String(t *testing.T) { NodeID: String(""), User: &User{}, Body: String(""), + SubmittedAt: &Timestamp{}, CommitID: String(""), HTMLURL: String(""), PullRequestURL: String(""), State: String(""), AuthorAssociation: String(""), } - want := `github.PullRequestReview{ID:0, NodeID:"", User:github.User{}, Body:"", CommitID:"", HTMLURL:"", PullRequestURL:"", State:"", AuthorAssociation:""}` + want := `github.PullRequestReview{ID:0, NodeID:"", User:github.User{}, Body:"", SubmittedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CommitID:"", HTMLURL:"", PullRequestURL:"", State:"", AuthorAssociation:""}` if got := v.String(); got != want { t.Errorf("PullRequestReview.String = %v, want %v", got, want) } @@ -1480,8 +1508,10 @@ func TestRepoStatus_String(t *testing.T) { Context: String(""), AvatarURL: String(""), Creator: &User{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, } - want := `github.RepoStatus{ID:0, NodeID:"", URL:"", State:"", TargetURL:"", Description:"", Context:"", AvatarURL:"", Creator:github.User{}}` + want := `github.RepoStatus{ID:0, NodeID:"", URL:"", State:"", TargetURL:"", Description:"", Context:"", AvatarURL:"", Creator:github.User{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("RepoStatus.String = %v, want %v", got, want) } @@ -1546,6 +1576,7 @@ func TestRepository_String(t *testing.T) { HasPages: Bool(false), HasProjects: Bool(false), HasDownloads: Bool(false), + HasDiscussions: Bool(false), IsTemplate: Bool(false), LicenseTemplate: String(""), GitignoreTemplate: String(""), @@ -1591,7 +1622,7 @@ func TestRepository_String(t *testing.T) { Visibility: String(""), RoleName: String(""), } - want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowUpdateBranch:false, AllowSquashMerge:false, AllowMergeCommit:false, AllowAutoMerge:false, AllowForking:false, DeleteBranchOnMerge:false, UseSquashPRTitleAsDefault:false, SquashMergeCommitTitle:"", SquashMergeCommitMessage:"", MergeCommitTitle:"", MergeCommitMessage:"", Topics:[""], Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", SecurityAndAnalysis:github.SecurityAndAnalysis{}, TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:"", RoleName:""}` + want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowUpdateBranch:false, AllowSquashMerge:false, AllowMergeCommit:false, AllowAutoMerge:false, AllowForking:false, DeleteBranchOnMerge:false, UseSquashPRTitleAsDefault:false, SquashMergeCommitTitle:"", SquashMergeCommitMessage:"", MergeCommitTitle:"", MergeCommitMessage:"", Topics:[""], Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, HasDiscussions:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", SecurityAndAnalysis:github.SecurityAndAnalysis{}, TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:"", RoleName:""}` if got := v.String(); got != want { t.Errorf("Repository.String = %v, want %v", got, want) } @@ -1606,11 +1637,13 @@ func TestRepositoryComment_String(t *testing.T) { CommitID: String(""), User: &User{}, Reactions: &Reactions{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, Body: String(""), Path: String(""), Position: Int(0), } - want := `github.RepositoryComment{HTMLURL:"", URL:"", ID:0, NodeID:"", CommitID:"", User:github.User{}, Reactions:github.Reactions{}, Body:"", Path:"", Position:0}` + want := `github.RepositoryComment{HTMLURL:"", URL:"", ID:0, NodeID:"", CommitID:"", User:github.User{}, Reactions:github.Reactions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Body:"", Path:"", Position:0}` if got := v.String(); got != want { t.Errorf("RepositoryComment.String = %v, want %v", got, want) } @@ -1695,6 +1728,7 @@ func TestRepositoryRelease_String(t *testing.T) { Body: String(""), Draft: Bool(false), Prerelease: Bool(false), + MakeLatest: String(""), DiscussionCategoryName: String(""), GenerateReleaseNotes: Bool(false), ID: Int64(0), @@ -1709,7 +1743,7 @@ func TestRepositoryRelease_String(t *testing.T) { Author: &User{}, NodeID: String(""), } - want := `github.RepositoryRelease{TagName:"", TargetCommitish:"", Name:"", Body:"", Draft:false, Prerelease:false, DiscussionCategoryName:"", GenerateReleaseNotes:false, ID:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PublishedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", HTMLURL:"", AssetsURL:"", UploadURL:"", ZipballURL:"", TarballURL:"", Author:github.User{}, NodeID:""}` + want := `github.RepositoryRelease{TagName:"", TargetCommitish:"", Name:"", Body:"", Draft:false, Prerelease:false, MakeLatest:"", DiscussionCategoryName:"", GenerateReleaseNotes:false, ID:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PublishedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", HTMLURL:"", AssetsURL:"", UploadURL:"", ZipballURL:"", TarballURL:"", Author:github.User{}, NodeID:""}` if got := v.String(); got != want { t.Errorf("RepositoryRelease.String = %v, want %v", got, want) } diff --git a/github/github.go b/github/github.go index 38a5bcd29b..6b37f082cf 100644 --- a/github/github.go +++ b/github/github.go @@ -24,15 +24,18 @@ import ( "time" "github.com/google/go-querystring/query" + "golang.org/x/oauth2" ) const ( - Version = "v48.0.0" + Version = "v50.0.0" - defaultBaseURL = "https://api.github.com/" - defaultUserAgent = "go-github" + "/" + Version - uploadBaseURL = "https://uploads.github.com/" + defaultAPIVersion = "2022-11-28" + defaultBaseURL = "https://api.github.com/" + defaultUserAgent = "go-github" + "/" + Version + uploadBaseURL = "https://uploads.github.com/" + headerAPIVersion = "X-GitHub-Api-Version" headerRateLimit = "X-RateLimit-Limit" headerRateRemaining = "X-RateLimit-Remaining" headerRateReset = "X-RateLimit-Reset" @@ -168,8 +171,9 @@ type Client struct { // User agent used when communicating with the GitHub API. UserAgent string - rateMu sync.Mutex - rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls. + rateMu sync.Mutex + rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls. + secondaryRateLimitReset time.Time // Secondary rate limit reset for the client as determined by the most recent API calls. common service // Reuse a single struct instead of allocating one for each service on the heap. @@ -344,6 +348,11 @@ func NewClient(httpClient *http.Client) *Client { return c } +// NewTokenClient returns a new GitHub API client authenticated with the provided token. +func NewTokenClient(ctx context.Context, token string) *Client { + return NewClient(oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}))) +} + // NewEnterpriseClient returns a new GitHub API client with provided // base URL and upload URL (often is your GitHub Enterprise hostname). // If the base URL does not have the suffix "/api/v3/", it will be added automatically. @@ -392,12 +401,24 @@ func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*C return c, nil } +// RequestOption represents an option that can modify an http.Request. +type RequestOption func(req *http.Request) + +// WithVersion overrides the GitHub v3 API version for this individual request. +// For more information, see: +// https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/ +func WithVersion(version string) RequestOption { + return func(req *http.Request) { + req.Header.Set(headerAPIVersion, version) + } +} + // NewRequest creates an API request. A relative URL can be provided in urlStr, // in which case it is resolved relative to the BaseURL of the Client. // Relative URLs should always be specified without a preceding slash. If // specified, the value pointed to by body is JSON encoded and included as the // request body. -func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { +func (c *Client) NewRequest(method, urlStr string, body interface{}, opts ...RequestOption) (*http.Request, error) { if !strings.HasSuffix(c.BaseURL.Path, "/") { return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) } @@ -430,6 +451,12 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ if c.UserAgent != "" { req.Header.Set("User-Agent", c.UserAgent) } + req.Header.Set(headerAPIVersion, defaultAPIVersion) + + for _, opt := range opts { + opt(req) + } + return req, nil } @@ -437,7 +464,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ // in which case it is resolved relative to the BaseURL of the Client. // Relative URLs should always be specified without a preceding slash. // Body is sent with Content-Type: application/x-www-form-urlencoded. -func (c *Client) NewFormRequest(urlStr string, body io.Reader) (*http.Request, error) { +func (c *Client) NewFormRequest(urlStr string, body io.Reader, opts ...RequestOption) (*http.Request, error) { if !strings.HasSuffix(c.BaseURL.Path, "/") { return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) } @@ -457,13 +484,19 @@ func (c *Client) NewFormRequest(urlStr string, body io.Reader) (*http.Request, e if c.UserAgent != "" { req.Header.Set("User-Agent", c.UserAgent) } + req.Header.Set(headerAPIVersion, defaultAPIVersion) + + for _, opt := range opts { + opt(req) + } + return req, nil } // NewUploadRequest creates an upload request. A relative URL can be provided in // urlStr, in which case it is resolved relative to the UploadURL of the Client. // Relative URLs should always be specified without a preceding slash. -func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { +func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption) (*http.Request, error) { if !strings.HasSuffix(c.UploadURL.Path, "/") { return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL) } @@ -485,6 +518,12 @@ func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, m req.Header.Set("Content-Type", mediaType) req.Header.Set("Accept", mediaTypeV3) req.Header.Set("User-Agent", c.UserAgent) + req.Header.Set(headerAPIVersion, defaultAPIVersion) + + for _, opt := range opts { + opt(req) + } + return req, nil } @@ -664,7 +703,7 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro req = withContext(ctx, req) - rateLimitCategory := category(req.URL.Path) + rateLimitCategory := category(req.Method, req.URL.Path) if bypass := ctx.Value(bypassRateLimitCheck); bypass == nil { // If we've hit rate limit, don't make further requests before Reset time. @@ -674,6 +713,12 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro Rate: err.Rate, }, err } + // If we've hit a secondary rate limit, don't make further requests before Retry After. + if err := c.checkSecondaryRateLimitBeforeDo(ctx, req); err != nil { + return &Response{ + Response: err.Response, + }, err + } } resp, err := c.client.Do(req) @@ -725,6 +770,14 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro aerr.Raw = b err = aerr } + + // Update the secondary rate limit if we hit it. + rerr, ok := err.(*AbuseRateLimitError) + if ok && rerr.RetryAfter != nil { + c.rateMu.Lock() + c.secondaryRateLimitReset = time.Now().Add(*rerr.RetryAfter) + c.rateMu.Unlock() + } } return response, err } @@ -789,6 +842,35 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat return nil } +// checkSecondaryRateLimitBeforeDo does not make any network calls, but uses existing knowledge from +// current client state in order to quickly check if *AbuseRateLimitError can be immediately returned +// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily. +// Otherwise it returns nil, and Client.Do should proceed normally. +func (c *Client) checkSecondaryRateLimitBeforeDo(ctx context.Context, req *http.Request) *AbuseRateLimitError { + c.rateMu.Lock() + secondary := c.secondaryRateLimitReset + c.rateMu.Unlock() + if !secondary.IsZero() && time.Now().Before(secondary) { + // Create a fake response. + resp := &http.Response{ + Status: http.StatusText(http.StatusForbidden), + StatusCode: http.StatusForbidden, + Request: req, + Header: make(http.Header), + Body: io.NopCloser(strings.NewReader("")), + } + + retryAfter := time.Until(secondary) + return &AbuseRateLimitError{ + Response: resp, + Message: fmt.Sprintf("API secondary rate limit exceeded until %v, not making remote request.", secondary), + RetryAfter: &retryAfter, + } + } + + return nil +} + // compareHTTPResponse returns whether two http.Response objects are equal or not. // Currently, only StatusCode is checked. This function is used when implementing the // Is(error) bool interface for the custom error types in this package. @@ -809,7 +891,7 @@ An ErrorResponse reports one or more errors caused by an API request. GitHub API docs: https://docs.github.com/en/rest/#client-errors */ type ErrorResponse struct { - Response *http.Response // HTTP response that caused this error + Response *http.Response `json:"-"` // HTTP response that caused this error Message string `json:"message"` // error message Errors []Error `json:"errors"` // more detail on individual errors // Block is only populated on certain types of errors such as code 451. @@ -1159,13 +1241,36 @@ const ( categories // An array of this length will be able to contain all rate limit categories. ) -// category returns the rate limit category of the endpoint, determined by Request.URL.Path. -func category(path string) rateLimitCategory { +// category returns the rate limit category of the endpoint, determined by HTTP method and Request.URL.Path. +func category(method, path string) rateLimitCategory { switch { + // https://docs.github.com/en/rest/rate-limit#about-rate-limits default: + // NOTE: coreCategory is returned for actionsRunnerRegistrationCategory too, + // because no API found for this category. return coreCategory case strings.HasPrefix(path, "/search/"): return searchCategory + case path == "/graphql": + return graphqlCategory + case strings.HasPrefix(path, "/app-manifests/") && + strings.HasSuffix(path, "/conversions") && + method == http.MethodPost: + return integrationManifestCategory + + // https://docs.github.com/en/rest/migrations/source-imports#start-an-import + case strings.HasPrefix(path, "/repos/") && + strings.HasSuffix(path, "/import") && + method == http.MethodPut: + return sourceImportCategory + + // https://docs.github.com/en/rest/code-scanning#upload-an-analysis-as-sarif-data + case strings.HasSuffix(path, "/code-scanning/sarifs"): + return codeScanningUploadCategory + + // https://docs.github.com/en/enterprise-cloud@latest/rest/scim + case strings.HasPrefix(path, "/scim/"): + return scimCategory } } @@ -1358,8 +1463,8 @@ func formatRateReset(d time.Duration) string { // When using roundTripWithOptionalFollowRedirect, note that it // is the responsibility of the caller to close the response body. -func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u string, followRedirects bool) (*http.Response, error) { - req, err := c.NewRequest("GET", u, nil) +func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u string, followRedirects bool, opts ...RequestOption) (*http.Response, error) { + req, err := c.NewRequest("GET", u, nil, opts...) if err != nil { return nil, err } @@ -1380,7 +1485,7 @@ func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u stri if followRedirects && resp.StatusCode == http.StatusMovedPermanently { resp.Body.Close() u = resp.Header.Get("Location") - resp, err = c.roundTripWithOptionalFollowRedirect(ctx, u, false) + resp, err = c.roundTripWithOptionalFollowRedirect(ctx, u, false, opts...) } return resp, err } diff --git a/github/github_test.go b/github/github_test.go index c4ceb89bb5..7342e2dfd7 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "golang.org/x/oauth2" ) const ( @@ -184,6 +185,11 @@ func testBadOptions(t *testing.T, methodName string, f func() error) { // Method f should be a regular call that would normally succeed, but // should return an error when NewRequest or s.client.Do fails. func testNewRequestAndDoFailure(t *testing.T, methodName string, client *Client, f func() (*Response, error)) { + testNewRequestAndDoFailureCategory(t, methodName, client, coreCategory, f) +} + +// testNewRequestAndDoFailureCategory works Like testNewRequestAndDoFailure, but allows setting the category +func testNewRequestAndDoFailureCategory(t *testing.T, methodName string, client *Client, category rateLimitCategory, f func() (*Response, error)) { t.Helper() if methodName == "" { t.Error("testNewRequestAndDoFailure: must supply method methodName") @@ -199,7 +205,7 @@ func testNewRequestAndDoFailure(t *testing.T, methodName string, client *Client, } client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) + client.rateLimits[category].Reset.Time = time.Now().Add(10 * time.Minute) resp, err = f() if bypass := resp.Request.Context().Value(bypassRateLimitCheck); bypass != nil { return @@ -266,6 +272,19 @@ func TestClient(t *testing.T) { } } +func TestNewTokenClient(t *testing.T) { + token := "gh_test_token" + ctx := context.Background() + c := NewTokenClient(ctx, token) + tr, ok := c.Client().Transport.(*oauth2.Transport) + if !ok { + t.Error("Client transport is not oauth2.Transport") + } + if tok, err := tr.Source.Token(); err != nil || tok.AccessToken != token { + t.Errorf("Client not using correct token") + } +} + func TestNewEnterpriseClient(t *testing.T) { baseURL := "https://custom-url/api/v3/" uploadURL := "https://custom-upload-url/api/uploads/" @@ -517,6 +536,17 @@ func TestNewRequest(t *testing.T) { if !strings.Contains(userAgent, Version) { t.Errorf("NewRequest() User-Agent should contain %v, found %v", Version, userAgent) } + + apiVersion := req.Header.Get(headerAPIVersion) + if got, want := apiVersion, defaultAPIVersion; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } + + req, _ = c.NewRequest("GET", inURL, inBody, WithVersion("2022-11-29")) + apiVersion = req.Header.Get(headerAPIVersion) + if got, want := apiVersion, "2022-11-29"; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } } func TestNewRequest_invalidJSON(t *testing.T) { @@ -626,6 +656,17 @@ func TestNewFormRequest(t *testing.T) { if got, want := req.Header.Get("User-Agent"), c.UserAgent; got != want { t.Errorf("NewFormRequest() User-Agent is %v, want %v", got, want) } + + apiVersion := req.Header.Get(headerAPIVersion) + if got, want := apiVersion, defaultAPIVersion; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } + + req, _ = c.NewFormRequest(inURL, inBody, WithVersion("2022-11-29")) + apiVersion = req.Header.Get(headerAPIVersion) + if got, want := apiVersion, "2022-11-29"; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } } func TestNewFormRequest_badURL(t *testing.T) { @@ -680,6 +721,22 @@ func TestNewFormRequest_errorForNoTrailingSlash(t *testing.T) { } } +func TestNewUploadRequest_WithVersion(t *testing.T) { + c := NewClient(nil) + req, _ := c.NewUploadRequest("https://example.com/", nil, 0, "") + + apiVersion := req.Header.Get(headerAPIVersion) + if got, want := apiVersion, defaultAPIVersion; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } + + req, _ = c.NewUploadRequest("https://example.com/", nil, 0, "", WithVersion("2022-11-29")) + apiVersion = req.Header.Get(headerAPIVersion) + if got, want := apiVersion, "2022-11-29"; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } +} + func TestNewUploadRequest_badURL(t *testing.T) { c := NewClient(nil) _, err := c.NewUploadRequest(":", nil, 0, "") @@ -1084,6 +1141,67 @@ func TestDo_rateLimit(t *testing.T) { } } +func TestDo_rateLimitCategory(t *testing.T) { + tests := []struct { + method string + url string + category rateLimitCategory + }{ + { + method: http.MethodGet, + url: "/", + category: coreCategory, + }, + { + method: http.MethodGet, + url: "/search/issues?q=rate", + category: searchCategory, + }, + { + method: http.MethodGet, + url: "/graphql", + category: graphqlCategory, + }, + { + method: http.MethodPost, + url: "/app-manifests/code/conversions", + category: integrationManifestCategory, + }, + { + method: http.MethodGet, + url: "/app-manifests/code/conversions", + category: coreCategory, // only POST requests are in the integration manifest category + }, + { + method: http.MethodPut, + url: "/repos/google/go-github/import", + category: sourceImportCategory, + }, + { + method: http.MethodGet, + url: "/repos/google/go-github/import", + category: coreCategory, // only PUT requests are in the source import category + }, + { + method: http.MethodPost, + url: "/repos/google/go-github/code-scanning/sarifs", + category: codeScanningUploadCategory, + }, + { + method: http.MethodGet, + url: "/scim/v2/organizations/ORG/Users", + category: scimCategory, + }, + // missing a check for actionsRunnerRegistrationCategory: API not found + } + + for _, tt := range tests { + if got, want := category(tt.method, tt.url), tt.category; got != want { + t.Errorf("expecting category %v, found %v", got, want) + } + } +} + // ensure rate limit is still parsed, even for error responses func TestDo_rateLimit_errorResponse(t *testing.T) { client, mux, _, teardown := setup() @@ -1355,6 +1473,25 @@ func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) { if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; got != want { t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) } + + // expect prevention of a following request + if _, err = client.Do(ctx, req, nil); err == nil { + t.Error("Expected error to be returned.") + } + abuseRateLimitErr, ok = err.(*AbuseRateLimitError) + if !ok { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatalf("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + // the saved duration might be a bit smaller than Retry-After because the duration is calculated from the expected end-of-cooldown time + if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; want-got > 1*time.Second { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } + if got, wantSuffix := abuseRateLimitErr.Message, "not making remote request."; !strings.HasSuffix(got, wantSuffix) { + t.Errorf("Expected request to be prevented because of secondary rate limit, got: %v.", got) + } } func TestDo_noContent(t *testing.T) { @@ -2013,30 +2150,48 @@ func TestRateLimits(t *testing.T) { if !cmp.Equal(rate, want) { t.Errorf("RateLimits returned %+v, want %+v", rate, want) } - - if got, want := client.rateLimits[coreCategory], *want.Core; got != want { - t.Errorf("client.rateLimits[coreCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[searchCategory], *want.Search; got != want { - t.Errorf("client.rateLimits[searchCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[graphqlCategory], *want.GraphQL; got != want { - t.Errorf("client.rateLimits[graphqlCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[integrationManifestCategory], *want.IntegrationManifest; got != want { - t.Errorf("client.rateLimits[integrationManifestCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[sourceImportCategory], *want.SourceImport; got != want { - t.Errorf("client.rateLimits[sourceImportCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[codeScanningUploadCategory], *want.CodeScanningUpload; got != want { - t.Errorf("client.rateLimits[codeScanningUploadCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[actionsRunnerRegistrationCategory], *want.ActionsRunnerRegistration; got != want { - t.Errorf("client.rateLimits[actionsRunnerRegistrationCategory] is %+v, want %+v", got, want) + tests := []struct { + category rateLimitCategory + rate *Rate + }{ + { + category: coreCategory, + rate: want.Core, + }, + { + category: searchCategory, + rate: want.Search, + }, + { + category: graphqlCategory, + rate: want.GraphQL, + }, + { + category: integrationManifestCategory, + rate: want.IntegrationManifest, + }, + { + category: sourceImportCategory, + rate: want.SourceImport, + }, + { + category: codeScanningUploadCategory, + rate: want.CodeScanningUpload, + }, + { + category: actionsRunnerRegistrationCategory, + rate: want.ActionsRunnerRegistration, + }, + { + category: scimCategory, + rate: want.SCIM, + }, } - if got, want := client.rateLimits[scimCategory], *want.SCIM; got != want { - t.Errorf("client.rateLimits[scimCategory] is %+v, want %+v", got, want) + + for _, tt := range tests { + if got, want := client.rateLimits[tt.category], *tt.rate; got != want { + t.Errorf("client.rateLimits[%v] is %+v, want %+v", tt.category, got, want) + } } } @@ -2065,7 +2220,13 @@ func TestRateLimits_overQuota(t *testing.T) { mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, `{"resources":{ "core": {"limit":2,"remaining":1,"reset":1372700873}, - "search": {"limit":3,"remaining":2,"reset":1372700874} + "search": {"limit":3,"remaining":2,"reset":1372700874}, + "graphql": {"limit":4,"remaining":3,"reset":1372700875}, + "integration_manifest": {"limit":5,"remaining":4,"reset":1372700876}, + "source_import": {"limit":6,"remaining":5,"reset":1372700877}, + "code_scanning_upload": {"limit":7,"remaining":6,"reset":1372700878}, + "actions_runner_registration": {"limit":8,"remaining":7,"reset":1372700879}, + "scim": {"limit":9,"remaining":8,"reset":1372700880} }}`) }) @@ -2086,16 +2247,82 @@ func TestRateLimits_overQuota(t *testing.T) { Remaining: 2, Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 54, 0, time.UTC).Local()}, }, + GraphQL: &Rate{ + Limit: 4, + Remaining: 3, + Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 55, 0, time.UTC).Local()}, + }, + IntegrationManifest: &Rate{ + Limit: 5, + Remaining: 4, + Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 56, 0, time.UTC).Local()}, + }, + SourceImport: &Rate{ + Limit: 6, + Remaining: 5, + Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 57, 0, time.UTC).Local()}, + }, + CodeScanningUpload: &Rate{ + Limit: 7, + Remaining: 6, + Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 58, 0, time.UTC).Local()}, + }, + ActionsRunnerRegistration: &Rate{ + Limit: 8, + Remaining: 7, + Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 59, 0, time.UTC).Local()}, + }, + SCIM: &Rate{ + Limit: 9, + Remaining: 8, + Reset: Timestamp{time.Date(2013, time.July, 1, 17, 48, 00, 0, time.UTC).Local()}, + }, } if !cmp.Equal(rate, want) { t.Errorf("RateLimits returned %+v, want %+v", rate, want) } - if got, want := client.rateLimits[coreCategory], *want.Core; got != want { - t.Errorf("client.rateLimits[coreCategory] is %+v, want %+v", got, want) + tests := []struct { + category rateLimitCategory + rate *Rate + }{ + { + category: coreCategory, + rate: want.Core, + }, + { + category: searchCategory, + rate: want.Search, + }, + { + category: graphqlCategory, + rate: want.GraphQL, + }, + { + category: integrationManifestCategory, + rate: want.IntegrationManifest, + }, + { + category: sourceImportCategory, + rate: want.SourceImport, + }, + { + category: codeScanningUploadCategory, + rate: want.CodeScanningUpload, + }, + { + category: actionsRunnerRegistrationCategory, + rate: want.ActionsRunnerRegistration, + }, + { + category: scimCategory, + rate: want.SCIM, + }, } - if got, want := client.rateLimits[searchCategory], *want.Search; got != want { - t.Errorf("client.rateLimits[searchCategory] is %+v, want %+v", got, want) + for _, tt := range tests { + if got, want := client.rateLimits[tt.category], *tt.rate; got != want { + t.Errorf("client.rateLimits[%v] is %+v, want %+v", tt.category, got, want) + } } } diff --git a/github/issue_import.go b/github/issue_import.go index a9810407cc..4bc8d5f1d2 100644 --- a/github/issue_import.go +++ b/github/issue_import.go @@ -10,7 +10,6 @@ import ( "context" "encoding/json" "fmt" - "time" ) // IssueImportService handles communication with the issue import related @@ -29,9 +28,9 @@ type IssueImportRequest struct { type IssueImport struct { Title string `json:"title"` Body string `json:"body"` - CreatedAt *time.Time `json:"created_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` Assignee *string `json:"assignee,omitempty"` Milestone *int `json:"milestone,omitempty"` Closed *bool `json:"closed,omitempty"` @@ -40,7 +39,7 @@ type IssueImport struct { // Comment represents comments of issue to import. type Comment struct { - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` Body string `json:"body"` } @@ -53,8 +52,8 @@ type IssueImportResponse struct { URL *string `json:"url,omitempty"` ImportIssuesURL *string `json:"import_issues_url,omitempty"` RepositoryURL *string `json:"repository_url,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` Message *string `json:"message,omitempty"` DocumentationURL *string `json:"documentation_url,omitempty"` Errors []*IssueImportError `json:"errors,omitempty"` @@ -126,7 +125,7 @@ func (s *IssueImportService) CheckStatus(ctx context.Context, owner, repo string // CheckStatusSince checks the status of multiple imported issues since a given date. // // https://gist.github.com/jonmagic/5282384165e0f86ef105#check-status-of-multiple-issues -func (s *IssueImportService) CheckStatusSince(ctx context.Context, owner, repo string, since time.Time) ([]*IssueImportResponse, *Response, error) { +func (s *IssueImportService) CheckStatusSince(ctx context.Context, owner, repo string, since Timestamp) ([]*IssueImportResponse, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import/issues?since=%v", owner, repo, since.Format("2006-01-02")) req, err := s.client.NewRequest("GET", u, nil) if err != nil { diff --git a/github/issue_import_test.go b/github/issue_import_test.go index 3533b3ab4b..a86ead898b 100644 --- a/github/issue_import_test.go +++ b/github/issue_import_test.go @@ -25,13 +25,13 @@ func TestIssueImportService_Create(t *testing.T) { IssueImport: IssueImport{ Assignee: String("developer"), Body: "Dummy description", - CreatedAt: &createdAt, + CreatedAt: &Timestamp{createdAt}, Labels: []string{"l1", "l2"}, Milestone: Int(1), Title: "Dummy Issue", }, Comments: []*Comment{{ - CreatedAt: &createdAt, + CreatedAt: &Timestamp{createdAt}, Body: "Comment body", }}, } @@ -142,7 +142,7 @@ func TestIssueImportService_CheckStatusSince(t *testing.T) { }) ctx := context.Background() - got, _, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", time.Now()) + got, _, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", Timestamp{time.Now()}) if err != nil { t.Errorf("CheckStatusSince returned error: %v", err) } @@ -154,12 +154,12 @@ func TestIssueImportService_CheckStatusSince(t *testing.T) { const methodName = "CheckStatusSince" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.IssueImport.CheckStatusSince(ctx, "\n", "\n", time.Now()) + _, _, err = client.IssueImport.CheckStatusSince(ctx, "\n", "\n", Timestamp{time.Now()}) return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", time.Now()) + got, resp, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", Timestamp{time.Now()}) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } @@ -179,7 +179,7 @@ func TestIssueImportService_CheckStatusSince_badResponse(t *testing.T) { }) ctx := context.Background() - if _, _, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", time.Now()); err == nil { + if _, _, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", Timestamp{time.Now()}); err == nil { t.Errorf("CheckStatusSince returned no error, want JSON err") } } @@ -189,7 +189,7 @@ func TestIssueImportService_CheckStatusSince_invalidOwner(t *testing.T) { defer teardown() ctx := context.Background() - _, _, err := client.IssueImport.CheckStatusSince(ctx, "%", "r", time.Now()) + _, _, err := client.IssueImport.CheckStatusSince(ctx, "%", "r", Timestamp{time.Now()}) testURLParseError(t, err) } @@ -240,8 +240,8 @@ func TestIssueImportResponse_Marshal(t *testing.T) { URL: String("url"), ImportIssuesURL: String("iiu"), RepositoryURL: String("ru"), - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, Message: String("msg"), DocumentationURL: String("durl"), Errors: []*IssueImportError{ @@ -283,7 +283,7 @@ func TestComment_Marshal(t *testing.T) { testJSONMarshal(t, &Comment{}, "{}") u := &Comment{ - CreatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, Body: "body", } @@ -301,9 +301,9 @@ func TestIssueImport_Marshal(t *testing.T) { u := &IssueImport{ Title: "title", Body: "body", - CreatedAt: &referenceTime, - ClosedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + ClosedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, Assignee: String("a"), Milestone: Int(1), Closed: Bool(false), @@ -334,9 +334,9 @@ func TestIssueImportRequest_Marshal(t *testing.T) { IssueImport: IssueImport{ Title: "title", Body: "body", - CreatedAt: &referenceTime, - ClosedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + ClosedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, Assignee: String("a"), Milestone: Int(1), Closed: Bool(false), @@ -344,7 +344,7 @@ func TestIssueImportRequest_Marshal(t *testing.T) { }, Comments: []*Comment{ { - CreatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, Body: "body", }, }, diff --git a/github/issues.go b/github/issues.go index 571a721170..42e58a17a8 100644 --- a/github/issues.go +++ b/github/issues.go @@ -38,9 +38,9 @@ type Issue struct { Labels []*Label `json:"labels,omitempty"` Assignee *User `json:"assignee,omitempty"` Comments *int `json:"comments,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` ClosedBy *User `json:"closed_by,omitempty"` URL *string `json:"url,omitempty"` HTMLURL *string `json:"html_url,omitempty"` diff --git a/github/issues_comments.go b/github/issues_comments.go index 361ee49a69..17881c093d 100644 --- a/github/issues_comments.go +++ b/github/issues_comments.go @@ -18,8 +18,8 @@ type IssueComment struct { Body *string `json:"body,omitempty"` User *User `json:"user,omitempty"` Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` // AuthorAssociation is the comment author's relationship to the issue's repository. // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". AuthorAssociation *string `json:"author_association,omitempty"` diff --git a/github/issues_comments_test.go b/github/issues_comments_test.go index 7780676f08..d35deedaf1 100644 --- a/github/issues_comments_test.go +++ b/github/issues_comments_test.go @@ -323,8 +323,8 @@ func TestIssueComment_Marshal(t *testing.T) { SuspendedAt: &Timestamp{referenceTime}, }, Reactions: &Reactions{TotalCount: Int(1)}, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, AuthorAssociation: String("aa"), URL: String("url"), HTMLURL: String("hurl"), diff --git a/github/issues_events.go b/github/issues_events.go index d8ffc0b542..ed07659170 100644 --- a/github/issues_events.go +++ b/github/issues_events.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // IssueEvent represents an event that occurred around an Issue or Pull Request. @@ -71,7 +70,7 @@ type IssueEvent struct { // Event *string `json:"event,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` Issue *Issue `json:"issue,omitempty"` // Only present on certain events; see above. diff --git a/github/issues_events_test.go b/github/issues_events_test.go index ba21565f2e..1d8d3233d1 100644 --- a/github/issues_events_test.go +++ b/github/issues_events_test.go @@ -193,7 +193,7 @@ func TestIssueEvent_Marshal(t *testing.T) { SuspendedAt: &Timestamp{referenceTime}, }, Event: String("event"), - CreatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, Issue: &Issue{ID: Int64(1)}, Assignee: &User{ Login: String("l"), diff --git a/github/issues_milestones.go b/github/issues_milestones.go index 3c9be2407e..897c7c0b6d 100644 --- a/github/issues_milestones.go +++ b/github/issues_milestones.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // Milestone represents a GitHub repository milestone. @@ -24,10 +23,10 @@ type Milestone struct { Creator *User `json:"creator,omitempty"` OpenIssues *int `json:"open_issues,omitempty"` ClosedIssues *int `json:"closed_issues,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - DueOn *time.Time `json:"due_on,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + DueOn *Timestamp `json:"due_on,omitempty"` NodeID *string `json:"node_id,omitempty"` } diff --git a/github/issues_milestones_test.go b/github/issues_milestones_test.go index 86d1bddcef..b6472f3468 100644 --- a/github/issues_milestones_test.go +++ b/github/issues_milestones_test.go @@ -284,10 +284,10 @@ func TestMilestone_Marshal(t *testing.T) { }, OpenIssues: Int(1), ClosedIssues: Int(1), - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, - ClosedAt: &referenceTime, - DueOn: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + ClosedAt: &Timestamp{referenceTime}, + DueOn: &Timestamp{referenceTime}, NodeID: String("nid"), } diff --git a/github/issues_test.go b/github/issues_test.go index e2cf063914..37b2c2ad0f 100644 --- a/github/issues_test.go +++ b/github/issues_test.go @@ -556,9 +556,9 @@ func TestIssue_Marshal(t *testing.T) { Labels: []*Label{{ID: Int64(1)}}, Assignee: &User{ID: Int64(1)}, Comments: Int(1), - ClosedAt: &referenceTime, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + ClosedAt: &Timestamp{referenceTime}, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, ClosedBy: &User{ID: Int64(1)}, URL: String("url"), HTMLURL: String("hurl"), diff --git a/github/issues_timeline.go b/github/issues_timeline.go index 7b9068acc5..2f049aff56 100644 --- a/github/issues_timeline.go +++ b/github/issues_timeline.go @@ -9,7 +9,6 @@ import ( "context" "fmt" "strings" - "time" ) // Timeline represents an event that occurred around an Issue or Pull Request. @@ -118,7 +117,7 @@ type Timeline struct { // The string SHA of a commit that referenced this Issue or Pull Request. CommitID *string `json:"commit_id,omitempty"` // The timestamp indicating when the event occurred. - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` // The Label object including `name` and `color` attributes. Only provided for // 'labeled' and 'unlabeled' events. Label *Label `json:"label,omitempty"` @@ -149,7 +148,7 @@ type Timeline struct { // The review summary text. Body *string `json:"body,omitempty"` - SubmittedAt *time.Time `json:"submitted_at,omitempty"` + SubmittedAt *Timestamp `json:"submitted_at,omitempty"` } // Source represents a reference's source. diff --git a/github/issues_timeline_test.go b/github/issues_timeline_test.go index 17ae6187be..27c7c115e7 100644 --- a/github/issues_timeline_test.go +++ b/github/issues_timeline_test.go @@ -126,7 +126,7 @@ func TestTimeline_Marshal(t *testing.T) { }, Event: String("event"), CommitID: String("cid"), - CreatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, Label: &Label{ID: Int64(1)}, Assignee: &User{ Login: String("l"), diff --git a/github/orgs.go b/github/orgs.go index 8105afad38..487405778f 100644 --- a/github/orgs.go +++ b/github/orgs.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // OrganizationsService provides access to the organization related functions @@ -35,8 +34,8 @@ type Organization struct { PublicGists *int `json:"public_gists,omitempty"` Followers *int `json:"followers,omitempty"` Following *int `json:"following,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` TotalPrivateRepos *int `json:"total_private_repos,omitempty"` OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` PrivateGists *int `json:"private_gists,omitempty"` diff --git a/github/orgs_hooks_test.go b/github/orgs_hooks_test.go index d4d4c54ebb..0fe45565b9 100644 --- a/github/orgs_hooks_test.go +++ b/github/orgs_hooks_test.go @@ -66,7 +66,7 @@ func TestOrganizationsService_CreateHook(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - input := &Hook{CreatedAt: &referenceTime} + input := &Hook{CreatedAt: &Timestamp{referenceTime}} mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) { v := new(createHookRequest) diff --git a/github/orgs_members_test.go b/github/orgs_members_test.go index adc14f5b00..0b5068e55e 100644 --- a/github/orgs_members_test.go +++ b/github/orgs_members_test.go @@ -590,7 +590,7 @@ func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) { Login: String("monalisa"), Email: String("octocat@github.com"), Role: String("direct_member"), - CreatedAt: &createdAt, + CreatedAt: &Timestamp{createdAt}, Inviter: &User{ Login: String("other_user"), ID: Int64(1), @@ -805,7 +805,7 @@ func TestOrganizationsService_ListFailedOrgInvitations(t *testing.T) { Role: String("direct_member"), FailedAt: &Timestamp{time.Date(2017, time.January, 2, 1, 10, 0, 0, time.UTC)}, FailedReason: String("the reason"), - CreatedAt: &createdAt, + CreatedAt: &Timestamp{createdAt}, Inviter: &User{ Login: String("other_user"), ID: Int64(1), diff --git a/github/pulls.go b/github/pulls.go index 120a1d6f18..6e49eba2f9 100644 --- a/github/pulls.go +++ b/github/pulls.go @@ -9,7 +9,6 @@ import ( "bytes" "context" "fmt" - "time" ) // PullRequestsService handles communication with the pull request related @@ -34,10 +33,10 @@ type PullRequest struct { Locked *bool `json:"locked,omitempty"` Title *string `json:"title,omitempty"` Body *string `json:"body,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - MergedAt *time.Time `json:"merged_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + MergedAt *Timestamp `json:"merged_at,omitempty"` Labels []*Label `json:"labels,omitempty"` User *User `json:"user,omitempty"` Draft *bool `json:"draft,omitempty"` diff --git a/github/pulls_comments.go b/github/pulls_comments.go index 83e7881e51..1f6b726d85 100644 --- a/github/pulls_comments.go +++ b/github/pulls_comments.go @@ -33,8 +33,8 @@ type PullRequestComment struct { OriginalCommitID *string `json:"original_commit_id,omitempty"` User *User `json:"user,omitempty"` Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` // AuthorAssociation is the comment author's relationship to the pull request's repository. // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". AuthorAssociation *string `json:"author_association,omitempty"` diff --git a/github/pulls_comments_test.go b/github/pulls_comments_test.go index cd6f19c12c..47d08d2b77 100644 --- a/github/pulls_comments_test.go +++ b/github/pulls_comments_test.go @@ -20,8 +20,8 @@ import ( func TestPullComments_Marshal(t *testing.T) { testJSONMarshal(t, &PullRequestComment{}, "{}") - createdAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) - updatedAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) + createdAt := Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)} + updatedAt := Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)} reactions := &Reactions{ TotalCount: Int(1), PlusOne: Int(1), diff --git a/github/pulls_reviews.go b/github/pulls_reviews.go index 77bf60ce2d..addcce4683 100644 --- a/github/pulls_reviews.go +++ b/github/pulls_reviews.go @@ -9,7 +9,6 @@ import ( "context" "errors" "fmt" - "time" ) var ErrMixedCommentStyles = errors.New("cannot use both position and side/line form comments") @@ -20,7 +19,7 @@ type PullRequestReview struct { NodeID *string `json:"node_id,omitempty"` User *User `json:"user,omitempty"` Body *string `json:"body,omitempty"` - SubmittedAt *time.Time `json:"submitted_at,omitempty"` + SubmittedAt *Timestamp `json:"submitted_at,omitempty"` CommitID *string `json:"commit_id,omitempty"` HTMLURL *string `json:"html_url,omitempty"` PullRequestURL *string `json:"pull_request_url,omitempty"` diff --git a/github/pulls_reviews_test.go b/github/pulls_reviews_test.go index aacc036888..7931ba0514 100644 --- a/github/pulls_reviews_test.go +++ b/github/pulls_reviews_test.go @@ -739,7 +739,7 @@ func TestPullRequestReview_Marshal(t *testing.T) { SuspendedAt: &Timestamp{referenceTime}, }, Body: String("body"), - SubmittedAt: &referenceTime, + SubmittedAt: &Timestamp{referenceTime}, CommitID: String("cid"), HTMLURL: String("hurl"), PullRequestURL: String("prurl"), diff --git a/github/pulls_test.go b/github/pulls_test.go index b498193817..590483fadb 100644 --- a/github/pulls_test.go +++ b/github/pulls_test.go @@ -1162,10 +1162,10 @@ func TestPullRequest_Marshal(t *testing.T) { Locked: Bool(false), Title: String("title"), Body: String("body"), - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, - ClosedAt: &referenceTime, - MergedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + ClosedAt: &Timestamp{referenceTime}, + MergedAt: &Timestamp{referenceTime}, Labels: []*Label{{ID: Int64(1)}}, User: &User{ Login: String("l"), diff --git a/github/pulls_threads_test.go b/github/pulls_threads_test.go index 6df2840403..7c3c861996 100644 --- a/github/pulls_threads_test.go +++ b/github/pulls_threads_test.go @@ -13,8 +13,8 @@ import ( func TestPullRequestThread_Marshal(t *testing.T) { testJSONMarshal(t, &PullRequestThread{}, "{}") - createdAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) - updatedAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) + createdAt := Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)} + updatedAt := Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)} reactions := &Reactions{ TotalCount: Int(1), PlusOne: Int(1), diff --git a/github/repos.go b/github/repos.go index 2b1a263f0f..1f6a3181ea 100644 --- a/github/repos.go +++ b/github/repos.go @@ -88,6 +88,7 @@ type Repository struct { HasPages *bool `json:"has_pages,omitempty"` HasProjects *bool `json:"has_projects,omitempty"` HasDownloads *bool `json:"has_downloads,omitempty"` + HasDiscussions *bool `json:"has_discussions,omitempty"` IsTemplate *bool `json:"is_template,omitempty"` LicenseTemplate *string `json:"license_template,omitempty"` GitignoreTemplate *string `json:"gitignore_template,omitempty"` @@ -365,12 +366,13 @@ type createRepoRequest struct { Description *string `json:"description,omitempty"` Homepage *string `json:"homepage,omitempty"` - Private *bool `json:"private,omitempty"` - Visibility *string `json:"visibility,omitempty"` - HasIssues *bool `json:"has_issues,omitempty"` - HasProjects *bool `json:"has_projects,omitempty"` - HasWiki *bool `json:"has_wiki,omitempty"` - IsTemplate *bool `json:"is_template,omitempty"` + Private *bool `json:"private,omitempty"` + Visibility *string `json:"visibility,omitempty"` + HasIssues *bool `json:"has_issues,omitempty"` + HasProjects *bool `json:"has_projects,omitempty"` + HasWiki *bool `json:"has_wiki,omitempty"` + HasDiscussions *bool `json:"has_discussions,omitempty"` + IsTemplate *bool `json:"is_template,omitempty"` // Creating an organization repository. Required for non-owners. TeamID *int64 `json:"team_id,omitempty"` @@ -423,6 +425,7 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo HasIssues: repo.HasIssues, HasProjects: repo.HasProjects, HasWiki: repo.HasWiki, + HasDiscussions: repo.HasDiscussions, IsTemplate: repo.IsTemplate, TeamID: repo.TeamID, AutoInit: repo.AutoInit, @@ -492,7 +495,7 @@ func (s *RepositoriesService) CreateFromTemplate(ctx context.Context, templateOw // Get fetches a repository. // -// GitHub API docs: https://docs.github.com/en/rest/repos/repos#update-a-repository +// GitHub API docs: https://docs.github.com/en/rest/repos/repos#get-a-repository func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v", owner, repo) req, err := s.client.NewRequest("GET", u, nil) @@ -840,10 +843,18 @@ type Protection struct { AllowForcePushes *AllowForcePushes `json:"allow_force_pushes"` AllowDeletions *AllowDeletions `json:"allow_deletions"` RequiredConversationResolution *RequiredConversationResolution `json:"required_conversation_resolution"` - // LockBranch represents if the branch is marked as read-only. If this is true, users will not be able to push to the branch. - LockBranch *bool `json:"lock_branch,omitempty"` - // AllowForkSyncing represents whether users can pull changes from upstream when the branch is locked. - AllowForkSyncing *bool `json:"allow_fork_syncing,omitempty"` + LockBranch *LockBranch `json:"lock_branch,omitempty"` + AllowForkSyncing *AllowForkSyncing `json:"allow_fork_syncing,omitempty"` +} + +// LockBranch represents if the branch is marked as read-only. If this is true, users will not be able to push to the branch. +type LockBranch struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// AllowForkSyncing represents whether users can pull changes from upstream when the branch is locked. +type AllowForkSyncing struct { + Enabled *bool `json:"enabled,omitempty"` } // BranchProtectionRule represents the rule applied to a repositories branch. @@ -1053,6 +1064,8 @@ type PullRequestReviewsEnforcementRequest struct { // RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged. // Valid values are 1-6. RequiredApprovingReviewCount int `json:"required_approving_review_count"` + // RequireLastPushApproval specifies whether the last pusher to a pull request branch can approve it. + RequireLastPushApproval *bool `json:"require_last_push_approval,omitempty"` } // PullRequestReviewsEnforcementUpdate represents request to patch the pull request review @@ -1167,7 +1180,7 @@ type DismissalRestrictionsRequest struct { Users *[]string `json:"users,omitempty"` // The list of team slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.) Teams *[]string `json:"teams,omitempty"` - // The list of apps which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.) + // The list of app slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.) Apps *[]string `json:"apps,omitempty"` } @@ -1663,6 +1676,8 @@ func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo // It requires the GitHub apps to have `write` access to the `content` permission. // // GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#get-apps-with-access-to-the-protected-branch +// +// Deprecated: Please use ListAppRestrictions instead. func (s *RepositoriesService) ListApps(ctx context.Context, owner, repo, branch string) ([]*App, *Response, error) { u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch) req, err := s.client.NewRequest("GET", u, nil) @@ -1679,6 +1694,16 @@ func (s *RepositoriesService) ListApps(ctx context.Context, owner, repo, branch return apps, resp, nil } +// ListAppRestrictions lists the GitHub apps that have push access to a given protected branch. +// It requires the GitHub apps to have `write` access to the `content` permission. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#get-apps-with-access-to-the-protected-branch +// +// Note: This is a wrapper around ListApps so a naming convention with ListUserRestrictions and ListTeamRestrictions is preserved. +func (s *RepositoriesService) ListAppRestrictions(ctx context.Context, owner, repo, branch string) ([]*App, *Response, error) { + return s.ListApps(ctx, owner, repo, branch) +} + // ReplaceAppRestrictions replaces the apps that have push access to a given protected branch. // It removes all apps that previously had push access and grants push access to the new list of apps. // It requires the GitHub apps to have `write` access to the `content` permission. @@ -1686,20 +1711,20 @@ func (s *RepositoriesService) ListApps(ctx context.Context, owner, repo, branch // Note: The list of users, apps, and teams in total is limited to 100 items. // // GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#set-app-access-restrictions -func (s *RepositoriesService) ReplaceAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) { +func (s *RepositoriesService) ReplaceAppRestrictions(ctx context.Context, owner, repo, branch string, apps []string) ([]*App, *Response, error) { u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch) - req, err := s.client.NewRequest("PUT", u, slug) + req, err := s.client.NewRequest("PUT", u, apps) if err != nil { return nil, nil, err } - var apps []*App - resp, err := s.client.Do(ctx, req, &apps) + var newApps []*App + resp, err := s.client.Do(ctx, req, &newApps) if err != nil { return nil, resp, err } - return apps, resp, nil + return newApps, resp, nil } // AddAppRestrictions grants the specified apps push access to a given protected branch. @@ -1708,42 +1733,216 @@ func (s *RepositoriesService) ReplaceAppRestrictions(ctx context.Context, owner, // Note: The list of users, apps, and teams in total is limited to 100 items. // // GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#add-app-access-restrictions -func (s *RepositoriesService) AddAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) { +func (s *RepositoriesService) AddAppRestrictions(ctx context.Context, owner, repo, branch string, apps []string) ([]*App, *Response, error) { u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch) - req, err := s.client.NewRequest("POST", u, slug) + req, err := s.client.NewRequest("POST", u, apps) if err != nil { return nil, nil, err } - var apps []*App - resp, err := s.client.Do(ctx, req, &apps) + var newApps []*App + resp, err := s.client.Do(ctx, req, &newApps) if err != nil { return nil, resp, err } - return apps, resp, nil + return newApps, resp, nil } -// RemoveAppRestrictions removes the ability of an app to push to this branch. +// RemoveAppRestrictions removes the restrictions of an app from pushing to this branch. // It requires the GitHub apps to have `write` access to the `content` permission. // // Note: The list of users, apps, and teams in total is limited to 100 items. // // GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#remove-app-access-restrictions -func (s *RepositoriesService) RemoveAppRestrictions(ctx context.Context, owner, repo, branch string, slug []string) ([]*App, *Response, error) { +func (s *RepositoriesService) RemoveAppRestrictions(ctx context.Context, owner, repo, branch string, apps []string) ([]*App, *Response, error) { u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, slug) + req, err := s.client.NewRequest("DELETE", u, apps) if err != nil { return nil, nil, err } - var apps []*App - resp, err := s.client.Do(ctx, req, &apps) + var newApps []*App + resp, err := s.client.Do(ctx, req, &newApps) if err != nil { return nil, resp, err } - return apps, resp, nil + return newApps, resp, nil +} + +// ListTeamRestrictions lists the GitHub teams that have push access to a given protected branch. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#get-teams-with-access-to-the-protected-branch +func (s *RepositoriesService) ListTeamRestrictions(ctx context.Context, owner, repo, branch string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var teams []*Team + resp, err := s.client.Do(ctx, req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// ReplaceTeamRestrictions replaces the team that have push access to a given protected branch. +// This removes all teams that previously had push access and grants push access to the new list of teams. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#set-team-access-restrictions +func (s *RepositoriesService) ReplaceTeamRestrictions(ctx context.Context, owner, repo, branch string, teams []string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, branch) + req, err := s.client.NewRequest("PUT", u, teams) + if err != nil { + return nil, nil, err + } + + var newTeams []*Team + resp, err := s.client.Do(ctx, req, &newTeams) + if err != nil { + return nil, resp, err + } + + return newTeams, resp, nil +} + +// AddTeamRestrictions grants the specified teams push access to a given protected branch. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#add-team-access-restrictions +func (s *RepositoriesService) AddTeamRestrictions(ctx context.Context, owner, repo, branch string, teams []string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, branch) + req, err := s.client.NewRequest("POST", u, teams) + if err != nil { + return nil, nil, err + } + + var newTeams []*Team + resp, err := s.client.Do(ctx, req, &newTeams) + if err != nil { + return nil, resp, err + } + + return newTeams, resp, nil +} + +// RemoveTeamRestrictions removes the restrictions of a team from pushing to this branch. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#remove-team-access-restrictions +func (s *RepositoriesService) RemoveTeamRestrictions(ctx context.Context, owner, repo, branch string, teams []string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, branch) + req, err := s.client.NewRequest("DELETE", u, teams) + if err != nil { + return nil, nil, err + } + + var newTeams []*Team + resp, err := s.client.Do(ctx, req, &newTeams) + if err != nil { + return nil, resp, err + } + + return newTeams, resp, nil +} + +// ListUserRestrictions lists the GitHub users that have push access to a given protected branch. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#get-users-with-access-to-the-protected-branch +func (s *RepositoriesService) ListUserRestrictions(ctx context.Context, owner, repo, branch string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, branch) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var users []*User + resp, err := s.client.Do(ctx, req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// ReplaceUserRestrictions replaces the user that have push access to a given protected branch. +// It removes all users that previously had push access and grants push access to the new list of users. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#set-team-access-restrictions +func (s *RepositoriesService) ReplaceUserRestrictions(ctx context.Context, owner, repo, branch string, users []string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, branch) + req, err := s.client.NewRequest("PUT", u, users) + if err != nil { + return nil, nil, err + } + + var newUsers []*User + resp, err := s.client.Do(ctx, req, &newUsers) + if err != nil { + return nil, resp, err + } + + return newUsers, resp, nil +} + +// AddUserRestrictions grants the specified users push access to a given protected branch. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#add-team-access-restrictions +func (s *RepositoriesService) AddUserRestrictions(ctx context.Context, owner, repo, branch string, users []string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, branch) + req, err := s.client.NewRequest("POST", u, users) + if err != nil { + return nil, nil, err + } + + var newUsers []*User + resp, err := s.client.Do(ctx, req, &newUsers) + if err != nil { + return nil, resp, err + } + + return newUsers, resp, nil +} + +// RemoveUserRestrictions removes the restrictions of a user from pushing to this branch. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// GitHub API docs: https://docs.github.com/en/rest/branches/branch-protection#remove-team-access-restrictions +func (s *RepositoriesService) RemoveUserRestrictions(ctx context.Context, owner, repo, branch string, users []string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, branch) + req, err := s.client.NewRequest("DELETE", u, users) + if err != nil { + return nil, nil, err + } + + var newUsers []*User + resp, err := s.client.Do(ctx, req, &newUsers) + if err != nil { + return nil, resp, err + } + + return newUsers, resp, nil } // TransferRequest represents a request to transfer a repository. diff --git a/github/repos_actions_access.go b/github/repos_actions_access.go new file mode 100644 index 0000000000..55761eeb7e --- /dev/null +++ b/github/repos_actions_access.go @@ -0,0 +1,55 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepositoryActionsAccessLevel represents the repository actions access level. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/permissions#set-the-level-of-access-for-workflows-outside-of-the-repository +type RepositoryActionsAccessLevel struct { + // AccessLevel specifies the level of access that workflows outside of the repository have + // to actions and reusable workflows within the repository. + // Possible values are: "none", "organization" "enterprise". + AccessLevel *string `json:"access_level,omitempty"` +} + +// GetActionsAccessLevel gets the level of access that workflows outside of the repository have +// to actions and reusable workflows in the repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/permissions#get-the-level-of-access-for-workflows-outside-of-the-repository +func (s *RepositoriesService) GetActionsAccessLevel(ctx context.Context, owner, repo string) (*RepositoryActionsAccessLevel, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/access", owner, repo) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + raal := new(RepositoryActionsAccessLevel) + resp, err := s.client.Do(ctx, req, raal) + if err != nil { + return nil, resp, err + } + + return raal, resp, nil +} + +// EditActionsAccessLevel sets the level of access that workflows outside of the repository have +// to actions and reusable workflows in the repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/permissions#set-the-level-of-access-for-workflows-outside-of-the-repository +func (s *RepositoriesService) EditActionsAccessLevel(ctx context.Context, owner, repo string, repositoryActionsAccessLevel RepositoryActionsAccessLevel) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/access", owner, repo) + req, err := s.client.NewRequest("PUT", u, repositoryActionsAccessLevel) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/repos_actions_access_test.go b/github/repos_actions_access_test.go new file mode 100644 index 0000000000..7085ac7d5c --- /dev/null +++ b/github/repos_actions_access_test.go @@ -0,0 +1,98 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetActionsAccessLevel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/permissions/access", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{"access_level": "none"}`) + }) + + ctx := context.Background() + org, _, err := client.Repositories.GetActionsAccessLevel(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetActionsAccessLevel returned error: %v", err) + } + want := &RepositoryActionsAccessLevel{AccessLevel: String("none")} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.GetActionsAccessLevel returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsAccessLevel" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetActionsAccessLevel(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetActionsAccessLevel(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_EditActionsAccessLevel(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &RepositoryActionsAccessLevel{AccessLevel: String("organization")} + + mux.HandleFunc("/repos/o/r/actions/permissions/access", func(w http.ResponseWriter, r *http.Request) { + v := new(RepositoryActionsAccessLevel) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + }) + + ctx := context.Background() + _, err := client.Repositories.EditActionsAccessLevel(ctx, "o", "r", *input) + if err != nil { + t.Errorf("Repositories.EditActionsAccessLevel returned error: %v", err) + } + + const methodName = "EditActionsAccessLevel" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EditActionsAccessLevel(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Repositories.EditActionsAccessLevel(ctx, "o", "r", *input) + return resp, err + }) +} + +func TestRepositoryActionsAccessLevel_Marshal(t *testing.T) { + testJSONMarshal(t, &ActionsPermissions{}, "{}") + + u := &RepositoryActionsAccessLevel{ + AccessLevel: String("enterprise"), + } + + want := `{ + "access_level": "enterprise" + }` + + testJSONMarshal(t, u, want) +} diff --git a/github/repos_comments.go b/github/repos_comments.go index 55a88d1f5e..e282374e9e 100644 --- a/github/repos_comments.go +++ b/github/repos_comments.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // RepositoryComment represents a comment for a commit, file, or line in a repository. @@ -20,8 +19,8 @@ type RepositoryComment struct { CommitID *string `json:"commit_id,omitempty"` User *User `json:"user,omitempty"` Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` // User-mutable fields Body *string `json:"body"` diff --git a/github/repos_comments_test.go b/github/repos_comments_test.go index 3dbc10cb47..b3579bf5a8 100644 --- a/github/repos_comments_test.go +++ b/github/repos_comments_test.go @@ -335,8 +335,8 @@ func TestRepositoryComment_Marshal(t *testing.T) { Eyes: Int(1), URL: String("u"), }, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, Body: String("body"), Path: String("path"), Position: Int(1), diff --git a/github/repos_commits_test.go b/github/repos_commits_test.go index 1ce068c659..c5c132786c 100644 --- a/github/repos_commits_test.go +++ b/github/repos_commits_test.go @@ -672,13 +672,13 @@ func TestBranchCommit_Marshal(t *testing.T) { Commit: &Commit{ SHA: String("s"), Author: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), }, Committer: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), diff --git a/github/repos_community_health.go b/github/repos_community_health.go index 9de438b625..750ee15827 100644 --- a/github/repos_community_health.go +++ b/github/repos_community_health.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // Metric represents the different fields for one file in community health files. @@ -38,7 +37,7 @@ type CommunityHealthMetrics struct { Description *string `json:"description"` Documentation *string `json:"documentation"` Files *CommunityHealthFiles `json:"files"` - UpdatedAt *time.Time `json:"updated_at"` + UpdatedAt *Timestamp `json:"updated_at"` ContentReportsEnabled *bool `json:"content_reports_enabled"` } diff --git a/github/repos_community_health_test.go b/github/repos_community_health_test.go index 0031cdb7d8..6ff88bda5a 100644 --- a/github/repos_community_health_test.go +++ b/github/repos_community_health_test.go @@ -76,7 +76,7 @@ func TestRepositoriesService_GetCommunityHealthMetrics(t *testing.T) { want := &CommunityHealthMetrics{ HealthPercentage: Int(100), Description: String("My first repository on GitHub!"), - UpdatedAt: &updatedAt, + UpdatedAt: &Timestamp{updatedAt}, ContentReportsEnabled: Bool(true), Files: &CommunityHealthFiles{ CodeOfConduct: &Metric{ @@ -310,7 +310,7 @@ func TestCommunityHealthMetrics_Marshal(t *testing.T) { HTMLURL: String("hurl"), }, }, - UpdatedAt: &referenceTime, + UpdatedAt: &Timestamp{referenceTime}, ContentReportsEnabled: Bool(true), } diff --git a/github/repos_contents_test.go b/github/repos_contents_test.go index f5de5570af..db3efb91c6 100644 --- a/github/repos_contents_test.go +++ b/github/repos_contents_test.go @@ -838,13 +838,13 @@ func TestRepositoryContentResponse_Marshal(t *testing.T) { Commit: Commit{ SHA: String("s"), Author: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), }, Committer: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("n"), Email: String("e"), Login: String("u"), @@ -958,13 +958,13 @@ func TestRepositoryContentFileOptions_Marshal(t *testing.T) { SHA: String("type"), Branch: String("type"), Author: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), }, Committer: &CommitAuthor{ - Date: &referenceTime, + Date: &Timestamp{referenceTime}, Name: String("name"), Email: String("email"), Login: String("login"), diff --git a/github/repos_deployment_branch_policies.go b/github/repos_deployment_branch_policies.go new file mode 100644 index 0000000000..8c4628b39b --- /dev/null +++ b/github/repos_deployment_branch_policies.go @@ -0,0 +1,123 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// DeploymentBranchPolicy represents a single deployment branch policy for an environment. +type DeploymentBranchPolicy struct { + Name *string `json:"name,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +// DeploymentBranchPolicyResponse represents the slightly different format of response that comes back when you list deployment branch policies. +type DeploymentBranchPolicyResponse struct { + TotalCount *int `json:"total_count,omitempty"` + BranchPolicies []*DeploymentBranchPolicy `json:"branch_policies,omitempty"` +} + +// DeploymentBranchPolicyRequest represents a deployment branch policy request. +type DeploymentBranchPolicyRequest struct { + Name *string `json:"name,omitempty"` +} + +// ListDeploymentBranchPolicies lists the deployment branch policies for an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/deployments/branch-policies#list-deployment-branch-policies +func (s *RepositoriesService) ListDeploymentBranchPolicies(ctx context.Context, owner, repo, environment string) (*DeploymentBranchPolicyResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies", owner, repo, environment) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var list *DeploymentBranchPolicyResponse + resp, err := s.client.Do(ctx, req, &list) + if err != nil { + return nil, resp, err + } + + return list, resp, nil +} + +// GetDeploymentBranchPolicy gets a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/deployments/branch-policies#get-a-deployment-branch-policy +func (s *RepositoriesService) GetDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, branchPolicyID int64) (*DeploymentBranchPolicy, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies/%v", owner, repo, environment, branchPolicyID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var policy *DeploymentBranchPolicy + resp, err := s.client.Do(ctx, req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// CreateDeploymentBranchPolicy creates a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/deployments/branch-policies#create-a-deployment-branch-policy +func (s *RepositoriesService) CreateDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, request *DeploymentBranchPolicyRequest) (*DeploymentBranchPolicy, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies", owner, repo, environment) + + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + var policy *DeploymentBranchPolicy + resp, err := s.client.Do(ctx, req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// UpdateDeploymentBranchPolicy updates a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/deployments/branch-policies#update-a-deployment-branch-policy +func (s *RepositoriesService) UpdateDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, branchPolicyID int64, request *DeploymentBranchPolicyRequest) (*DeploymentBranchPolicy, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies/%v", owner, repo, environment, branchPolicyID) + + req, err := s.client.NewRequest("PUT", u, request) + if err != nil { + return nil, nil, err + } + + var policy *DeploymentBranchPolicy + resp, err := s.client.Do(ctx, req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// DeleteDeploymentBranchPolicy deletes a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/deployments/branch-policies#delete-a-deployment-branch-policy +func (s *RepositoriesService) DeleteDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, branchPolicyID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies/%v", owner, repo, environment, branchPolicyID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/repos_deployment_branch_policies_test.go b/github/repos_deployment_branch_policies_test.go new file mode 100644 index 0000000000..69bffac86e --- /dev/null +++ b/github/repos_deployment_branch_policies_test.go @@ -0,0 +1,158 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "reflect" + "testing" +) + +func TestRepositoriesService_ListDeploymentBranchPolicies(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"total_count":2, "branch_policies":[{"id":1}, {"id": 2}]}`) + }) + + ctx := context.Background() + got, _, err := client.Repositories.ListDeploymentBranchPolicies(ctx, "o", "r", "e") + if err != nil { + t.Errorf("Repositories.ListDeploymentBranchPolicies returned error: %v", err) + } + + want := &DeploymentBranchPolicyResponse{ + BranchPolicies: []*DeploymentBranchPolicy{ + {ID: Int64(1)}, + {ID: Int64(2)}, + }, + TotalCount: Int(2), + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.ListDeploymentBranchPolicies = %+v, want %+v", got, want) + } + + const methodName = "ListDeploymentBranchPolicies" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListDeploymentBranchPolicies(ctx, "o", "r", "e") + if got != nil { + t.Errorf("got non-nil Repositories.ListDeploymentBranchPolicies response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetDeploymentBranchPolicy(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies/1", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := context.Background() + got, _, err := client.Repositories.GetDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + if err != nil { + t.Errorf("Repositories.GetDeploymentBranchPolicy returned error: %v", err) + } + + want := &DeploymentBranchPolicy{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.GetDeploymentBranchPolicy = %+v, want %+v", got, want) + } + + const methodName = "GetDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + if got != nil { + t.Errorf("got non-nil Repositories.GetDeploymentBranchPolicy response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateDeploymentBranchPolicy(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := context.Background() + got, _, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, "o", "r", "e", &DeploymentBranchPolicyRequest{Name: String("n")}) + if err != nil { + t.Errorf("Repositories.CreateDeploymentBranchPolicy returned error: %v", err) + } + + want := &DeploymentBranchPolicy{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.CreateDeploymentBranchPolicy = %+v, want %+v", got, want) + } + + const methodName = "CreateDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, "o", "r", "e", &DeploymentBranchPolicyRequest{Name: String("n")}) + if got != nil { + t.Errorf("got non-nil Repositories.CreateDeploymentBranchPolicy response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateDeploymentBranchPolicy(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := context.Background() + got, _, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, "o", "r", "e", 1, &DeploymentBranchPolicyRequest{Name: String("n")}) + if err != nil { + t.Errorf("Repositories.UpdateDeploymentBranchPolicy returned error: %v", err) + } + + want := &DeploymentBranchPolicy{ID: Int64(1)} + if !reflect.DeepEqual(got, want) { + t.Errorf("Repositories.UpdateDeploymentBranchPolicy = %+v, want %+v", got, want) + } + + const methodName = "UpdateDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, "o", "r", "e", 1, &DeploymentBranchPolicyRequest{Name: String("n")}) + if got != nil { + t.Errorf("got non-nil Repositories.UpdateDeploymentBranchPolicy response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteDeploymentBranchPolicy(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Repositories.DeleteDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + if err != nil { + t.Errorf("Repositories.DeleteDeploymentBranchPolicy returned error: %v", err) + } + + const methodName = "DeleteDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + }) +} diff --git a/github/repos_environments.go b/github/repos_environments.go index 365f8d9202..2e85fdf99c 100644 --- a/github/repos_environments.go +++ b/github/repos_environments.go @@ -9,6 +9,7 @@ import ( "context" "encoding/json" "fmt" + "net/http" ) // Environment represents a single environment in a repository. @@ -168,6 +169,13 @@ type CreateUpdateEnvironment struct { DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"` } +// createUpdateEnvironmentNoEnterprise represents the fields accepted for Pro/Teams private repos. +// Ref: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment +// See https://github.com/google/go-github/issues/2602 for more information. +type createUpdateEnvironmentNoEnterprise struct { + DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"` +} + // CreateUpdateEnvironment create or update a new environment for a repository. // // GitHub API docs: https://docs.github.com/en/rest/deployments/environments#create-or-update-an-environment @@ -179,6 +187,33 @@ func (s *RepositoriesService) CreateUpdateEnvironment(ctx context.Context, owner return nil, nil, err } + e := new(Environment) + resp, err := s.client.Do(ctx, req, e) + if err != nil { + // The API returns 422 when the pricing plan doesn't support all the fields sent. + // This path will be executed for Pro/Teams private repos. + // For public repos, regardless of the pricing plan, all fields supported. + // For Free plan private repos the returned error code is 404. + // We are checking that the user didn't try to send a value for unsupported fields, + // and return an error if they did. + if resp != nil && resp.StatusCode == http.StatusUnprocessableEntity && environment != nil && len(environment.Reviewers) == 0 && environment.GetWaitTimer() == 0 { + return s.createNewEnvNoEnterprise(ctx, u, environment) + } + return nil, resp, err + } + return e, resp, nil +} + +// createNewEnvNoEnterprise is an internal function for cases where the original call returned 422. +// Currently only the `deployment_branch_policy` parameter is supported for Pro/Team private repos. +func (s *RepositoriesService) createNewEnvNoEnterprise(ctx context.Context, u string, environment *CreateUpdateEnvironment) (*Environment, *Response, error) { + req, err := s.client.NewRequest("PUT", u, &createUpdateEnvironmentNoEnterprise{ + DeploymentBranchPolicy: environment.DeploymentBranchPolicy, + }) + if err != nil { + return nil, nil, err + } + e := new(Environment) resp, err := s.client.Do(ctx, req, e) if err != nil { diff --git a/github/repos_environments_test.go b/github/repos_environments_test.go index 93d0fc25eb..0b27da933d 100644 --- a/github/repos_environments_test.go +++ b/github/repos_environments_test.go @@ -220,6 +220,110 @@ func TestRepositoriesService_CreateEnvironment(t *testing.T) { }) } +func TestRepositoriesService_CreateEnvironment_noEnterprise(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &CreateUpdateEnvironment{} + callCount := 0 + + mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) { + v := new(CreateUpdateEnvironment) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if callCount == 0 { + w.WriteHeader(http.StatusUnprocessableEntity) + callCount++ + } else { + want := &CreateUpdateEnvironment{} + if !cmp.Equal(v, want) { + t.Errorf("Request body = %+v, want %+v", v, want) + } + fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": []}`) + } + }) + + ctx := context.Background() + release, _, err := client.Repositories.CreateUpdateEnvironment(ctx, "o", "r", "e", input) + if err != nil { + t.Errorf("Repositories.CreateUpdateEnvironment returned error: %v", err) + } + + want := &Environment{ID: Int64(1), Name: String("staging"), ProtectionRules: []*ProtectionRule{}} + if !cmp.Equal(release, want) { + t.Errorf("Repositories.CreateUpdateEnvironment returned %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_createNewEnvNoEnterprise(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &CreateUpdateEnvironment{ + DeploymentBranchPolicy: &BranchPolicy{ + ProtectedBranches: Bool(true), + CustomBranchPolicies: Bool(false), + }, + } + + mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) { + v := new(createUpdateEnvironmentNoEnterprise) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + want := &createUpdateEnvironmentNoEnterprise{ + DeploymentBranchPolicy: &BranchPolicy{ + ProtectedBranches: Bool(true), + CustomBranchPolicies: Bool(false), + }, + } + if !cmp.Equal(v, want) { + t.Errorf("Request body = %+v, want %+v", v, want) + } + fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": [{"id": 1, "node_id": "id", "type": "branch_policy"}], "deployment_branch_policy": {"protected_branches": true, "custom_branch_policies": false}}`) + }) + + ctx := context.Background() + release, _, err := client.Repositories.createNewEnvNoEnterprise(ctx, "repos/o/r/environments/e", input) + if err != nil { + t.Errorf("Repositories.createNewEnvNoEnterprise returned error: %v", err) + } + + want := &Environment{ + ID: Int64(1), + Name: String("staging"), + ProtectionRules: []*ProtectionRule{ + { + ID: Int64(1), + NodeID: String("id"), + Type: String("branch_policy"), + }, + }, + DeploymentBranchPolicy: &BranchPolicy{ + ProtectedBranches: Bool(true), + CustomBranchPolicies: Bool(false), + }, + } + if !cmp.Equal(release, want) { + t.Errorf("Repositories.createNewEnvNoEnterprise returned %+v, want %+v", release, want) + } + + const methodName = "createNewEnvNoEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.createNewEnvNoEnterprise(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.createNewEnvNoEnterprise(ctx, "repos/o/r/environments/e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestRepositoriesService_DeleteEnvironment(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/github/repos_forks.go b/github/repos_forks.go index 5c7d5dbd78..f175dfe3b0 100644 --- a/github/repos_forks.go +++ b/github/repos_forks.go @@ -7,9 +7,8 @@ package github import ( "context" - "fmt" - "encoding/json" + "fmt" ) // RepositoryListForksOptions specifies the optional parameters to the @@ -53,9 +52,9 @@ func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, // RepositoriesService.CreateFork method. type RepositoryCreateForkOptions struct { // The organization to fork the repository into. - Organization string `url:"organization,omitempty"` - Name string `url:"name,omitempty"` - DefaultBranchOnly bool `url:"default_branch_only,omitempty"` + Organization string `json:"organization,omitempty"` + Name string `json:"name,omitempty"` + DefaultBranchOnly bool `json:"default_branch_only,omitempty"` } // CreateFork creates a fork of the specified repository. @@ -70,12 +69,8 @@ type RepositoryCreateForkOptions struct { // GitHub API docs: https://docs.github.com/en/rest/repos/forks#create-a-fork func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opts *RepositoryCreateForkOptions) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) - u, err := addOptions(u, opts) - if err != nil { - return nil, nil, err - } - req, err := s.client.NewRequest("POST", u, nil) + req, err := s.client.NewRequest("POST", u, opts) if err != nil { return nil, nil, err } diff --git a/github/repos_forks_test.go b/github/repos_forks_test.go index 07be423bc3..d1a17f62d1 100644 --- a/github/repos_forks_test.go +++ b/github/repos_forks_test.go @@ -73,7 +73,7 @@ func TestRepositoriesService_CreateFork(t *testing.T) { mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testFormValues(t, r, values{"organization": "o", "name": "n", "default_branch_only": "true"}) + testBody(t, r, `{"organization":"o","name":"n","default_branch_only":true}`+"\n") fmt.Fprint(w, `{"id":1}`) }) @@ -110,7 +110,7 @@ func TestRepositoriesService_CreateFork_deferred(t *testing.T) { mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testFormValues(t, r, values{"organization": "o", "name": "n", "default_branch_only": "true"}) + testBody(t, r, `{"organization":"o","name":"n","default_branch_only":true}`+"\n") // This response indicates the fork will happen asynchronously. w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, `{"id":1}`) diff --git a/github/repos_hooks.go b/github/repos_hooks.go index 44eac8a401..ba229e7bca 100644 --- a/github/repos_hooks.go +++ b/github/repos_hooks.go @@ -11,7 +11,6 @@ import ( "net/http" "net/url" "strings" - "time" ) // WebHookPayload represents the data that is received from GitHub when a push @@ -40,8 +39,8 @@ type WebHookAuthor = CommitAuthor // Hook represents a GitHub (web and service) hook for a repository. type Hook struct { - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` URL *string `json:"url,omitempty"` ID *int64 `json:"id,omitempty"` Type *string `json:"type,omitempty"` diff --git a/github/repos_hooks_test.go b/github/repos_hooks_test.go index 3c6ee6fa16..82e8b554d6 100644 --- a/github/repos_hooks_test.go +++ b/github/repos_hooks_test.go @@ -19,7 +19,7 @@ func TestRepositoriesService_CreateHook(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - input := &Hook{CreatedAt: &referenceTime} + input := &Hook{CreatedAt: &Timestamp{referenceTime}} mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { v := new(createHookRequest) @@ -525,8 +525,8 @@ func TestBranchHook_Marshal(t *testing.T) { testJSONMarshal(t, &Hook{}, "{}") v := &Hook{ - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, URL: String("url"), ID: Int64(1), Type: String("type"), diff --git a/github/repos_releases.go b/github/repos_releases.go index c030f04d9a..464c2ee1e2 100644 --- a/github/repos_releases.go +++ b/github/repos_releases.go @@ -19,12 +19,14 @@ import ( // RepositoryRelease represents a GitHub release in a repository. type RepositoryRelease struct { - TagName *string `json:"tag_name,omitempty"` - TargetCommitish *string `json:"target_commitish,omitempty"` - Name *string `json:"name,omitempty"` - Body *string `json:"body,omitempty"` - Draft *bool `json:"draft,omitempty"` - Prerelease *bool `json:"prerelease,omitempty"` + TagName *string `json:"tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + Name *string `json:"name,omitempty"` + Body *string `json:"body,omitempty"` + Draft *bool `json:"draft,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + // MakeLatest can be one of: "true", "false", or "legacy". + MakeLatest *string `json:"make_latest,omitempty"` DiscussionCategoryName *string `json:"discussion_category_name,omitempty"` // The following fields are not used in EditRelease: @@ -176,6 +178,7 @@ type repositoryReleaseRequest struct { Body *string `json:"body,omitempty"` Draft *bool `json:"draft,omitempty"` Prerelease *bool `json:"prerelease,omitempty"` + MakeLatest *string `json:"make_latest,omitempty"` GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"` DiscussionCategoryName *string `json:"discussion_category_name,omitempty"` } @@ -196,6 +199,7 @@ func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo str Body: release.Body, Draft: release.Draft, Prerelease: release.Prerelease, + MakeLatest: release.MakeLatest, DiscussionCategoryName: release.DiscussionCategoryName, GenerateReleaseNotes: release.GenerateReleaseNotes, } @@ -229,6 +233,7 @@ func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo strin Body: release.Body, Draft: release.Draft, Prerelease: release.Prerelease, + MakeLatest: release.MakeLatest, DiscussionCategoryName: release.DiscussionCategoryName, } diff --git a/github/repos_releases_test.go b/github/repos_releases_test.go index 77a20ffe95..73a9f3a530 100644 --- a/github/repos_releases_test.go +++ b/github/repos_releases_test.go @@ -708,6 +708,7 @@ func TestRepositoryReleaseRequest_Marshal(t *testing.T) { Body: String("body"), Draft: Bool(false), Prerelease: Bool(false), + MakeLatest: String("legacy"), DiscussionCategoryName: String("dcn"), } @@ -718,6 +719,7 @@ func TestRepositoryReleaseRequest_Marshal(t *testing.T) { "body": "body", "draft": false, "prerelease": false, + "make_latest": "legacy", "discussion_category_name": "dcn" }` @@ -774,6 +776,7 @@ func TestRepositoryRelease_Marshal(t *testing.T) { Body: String("body"), Draft: Bool(false), Prerelease: Bool(false), + MakeLatest: String("legacy"), DiscussionCategoryName: String("dcn"), ID: Int64(1), CreatedAt: &Timestamp{referenceTime}, @@ -796,6 +799,7 @@ func TestRepositoryRelease_Marshal(t *testing.T) { "body": "body", "draft": false, "prerelease": false, + "make_latest": "legacy", "discussion_category_name": "dcn", "id": 1, "created_at": ` + referenceTimeStr + `, diff --git a/github/repos_statuses.go b/github/repos_statuses.go index 42238f3c9d..ea3d166c75 100644 --- a/github/repos_statuses.go +++ b/github/repos_statuses.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // RepoStatus represents the status of a repository at a particular reference. @@ -35,8 +34,8 @@ type RepoStatus struct { AvatarURL *string `json:"avatar_url,omitempty"` Creator *User `json:"creator,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` } func (r RepoStatus) String() string { diff --git a/github/repos_statuses_test.go b/github/repos_statuses_test.go index 62fe3336f3..c9e03acb8d 100644 --- a/github/repos_statuses_test.go +++ b/github/repos_statuses_test.go @@ -163,8 +163,8 @@ func TestRepoStatus_Marshal(t *testing.T) { Context: String("ctx"), AvatarURL: String("aurl"), Creator: &User{ID: Int64(1)}, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, } want := `{ @@ -205,8 +205,8 @@ func TestCombinedStatus_Marshal(t *testing.T) { Context: String("ctx"), AvatarURL: String("aurl"), Creator: &User{ID: Int64(1)}, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, }, }, CommitURL: String("curl"), diff --git a/github/repos_test.go b/github/repos_test.go index bcaa7d3d61..5f59216b2b 100644 --- a/github/repos_test.go +++ b/github/repos_test.go @@ -1082,6 +1082,7 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) { }, "dismiss_stale_reviews":true, "require_code_owner_reviews":true, + "require_last_push_approval":false, "required_approving_review_count":1 }, "enforce_admins":{ @@ -1130,6 +1131,7 @@ func TestRepositoriesService_GetBranchProtection(t *testing.T) { }, RequireCodeOwnerReviews: true, RequiredApprovingReviewCount: 1, + RequireLastPushApproval: false, }, EnforceAdmins: &AdminEnforcement{ URL: String("/repos/o/r/branches/b/protection/enforce_admins"), @@ -1633,6 +1635,7 @@ func TestRepositoriesService_UpdateBranchProtection_StrictNoChecks(t *testing.T) }, "dismiss_stale_reviews":true, "require_code_owner_reviews":true, + "require_last_push_approval":false, "bypass_pull_request_allowances": { "users":[{"id":10,"login":"uuu"}], "teams":[{"id":20,"slug":"ttt"}], @@ -1702,6 +1705,48 @@ func TestRepositoriesService_UpdateBranchProtection_StrictNoChecks(t *testing.T) } } +func TestRepositoriesService_UpdateBranchProtection_RequireLastPushApproval(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + input := &ProtectionRequest{ + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + RequireLastPushApproval: Bool(true), + }, + } + + mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { + v := new(ProtectionRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "PUT") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprintf(w, `{ + "required_pull_request_reviews":{ + "require_last_push_approval":true + } + }`) + }) + + ctx := context.Background() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + RequireLastPushApproval: true, + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } +} + func TestRepositoriesService_RemoveBranchProtection(t *testing.T) { client, mux, _, teardown := setup() defer teardown() @@ -2561,6 +2606,7 @@ func TestPullRequestReviewsEnforcementRequest_MarshalJSON_nilDismissalRestirctio Teams: &[]string{}, Apps: &[]string{}, }, + RequireLastPushApproval: Bool(true), } got, err = json.Marshal(req) @@ -2568,7 +2614,7 @@ func TestPullRequestReviewsEnforcementRequest_MarshalJSON_nilDismissalRestirctio t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned error: %v", err) } - want = `{"dismissal_restrictions":{"users":[],"teams":[],"apps":[]},"dismiss_stale_reviews":false,"require_code_owner_reviews":false,"required_approving_review_count":0}` + want = `{"dismissal_restrictions":{"users":[],"teams":[],"apps":[]},"dismiss_stale_reviews":false,"require_code_owner_reviews":false,"required_approving_review_count":0,"require_last_push_approval":true}` if want != string(got) { t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned %+v, want %+v", string(got), want) } @@ -2714,7 +2760,7 @@ func TestRepositoriesService_ReplaceAllTopics_emptySlice(t *testing.T) { } } -func TestRepositoriesService_ListApps(t *testing.T) { +func TestRepositoriesService_ListAppRestrictions(t *testing.T) { client, mux, _, teardown := setup() defer teardown() @@ -2723,19 +2769,19 @@ func TestRepositoriesService_ListApps(t *testing.T) { }) ctx := context.Background() - _, _, err := client.Repositories.ListApps(ctx, "o", "r", "b") + _, _, err := client.Repositories.ListAppRestrictions(ctx, "o", "r", "b") if err != nil { - t.Errorf("Repositories.ListApps returned error: %v", err) + t.Errorf("Repositories.ListAppRestrictions returned error: %v", err) } - const methodName = "ListApps" + const methodName = "ListAppRestrictions" testBadOptions(t, methodName, func() (err error) { - _, _, err = client.Repositories.ListApps(ctx, "\n", "\n", "\n") + _, _, err = client.Repositories.ListAppRestrictions(ctx, "\n", "\n", "\n") return err }) testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.Repositories.ListApps(ctx, "o", "r", "b") + got, resp, err := client.Repositories.ListAppRestrictions(ctx, "o", "r", "b") if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } @@ -2853,6 +2899,284 @@ func TestRepositoriesService_RemoveAppRestrictions(t *testing.T) { }) } +func TestRepositoriesService_ListTeamRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + ctx := context.Background() + _, _, err := client.Repositories.ListTeamRestrictions(ctx, "o", "r", "b") + if err != nil { + t.Errorf("Repositories.ListTeamRestrictions returned error: %v", err) + } + + const methodName = "ListTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTeamRestrictions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTeamRestrictions(ctx, "o", "r", "b") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ReplaceTeamRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := context.Background() + got, _, err := client.Repositories.ReplaceTeamRestrictions(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.ReplaceTeamRestrictions returned error: %v", err) + } + want := []*Team{ + {Name: String("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceTeamRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "ReplaceTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ReplaceTeamRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ReplaceTeamRestrictions(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_AddTeamRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := context.Background() + got, _, err := client.Repositories.AddTeamRestrictions(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.AddTeamRestrictions returned error: %v", err) + } + want := []*Team{ + {Name: String("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.AddTeamRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "AddTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddTeamRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddTeamRestrictions(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_RemoveTeamRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `[]`) + }) + input := []string{"octocat"} + ctx := context.Background() + got, _, err := client.Repositories.RemoveTeamRestrictions(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.RemoveTeamRestrictions returned error: %v", err) + } + want := []*Team{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.RemoveTeamRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "RemoveTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RemoveTeamRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RemoveTeamRestrictions(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListUserRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + ctx := context.Background() + _, _, err := client.Repositories.ListUserRestrictions(ctx, "o", "r", "b") + if err != nil { + t.Errorf("Repositories.ListUserRestrictions returned error: %v", err) + } + + const methodName = "ListUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListUserRestrictions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListUserRestrictions(ctx, "o", "r", "b") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ReplaceUserRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := context.Background() + got, _, err := client.Repositories.ReplaceUserRestrictions(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.ReplaceUserRestrictions returned error: %v", err) + } + want := []*User{ + {Name: String("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceUserRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "ReplaceUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ReplaceUserRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ReplaceUserRestrictions(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_AddUserRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := context.Background() + got, _, err := client.Repositories.AddUserRestrictions(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.AddUserRestrictions returned error: %v", err) + } + want := []*User{ + {Name: String("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.AddUserRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "AddUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddUserRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddUserRestrictions(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_RemoveUserRestrictions(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/branches/b/protection/restrictions/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `[]`) + }) + input := []string{"octocat"} + ctx := context.Background() + got, _, err := client.Repositories.RemoveUserRestrictions(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Repositories.RemoveUserRestrictions returned error: %v", err) + } + want := []*User{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.RemoveUserRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "RemoveUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RemoveUserRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RemoveUserRestrictions(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestRepositoriesService_Transfer(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/github/search_test.go b/github/search_test.go index 8998681eaa..b746175360 100644 --- a/github/search_test.go +++ b/github/search_test.go @@ -788,9 +788,9 @@ func TestIssuesSearchResult_Marshal(t *testing.T) { Labels: []*Label{{ID: Int64(1)}}, Assignee: &User{ID: Int64(1)}, Comments: Int(1), - ClosedAt: &referenceTime, - CreatedAt: &referenceTime, - UpdatedAt: &referenceTime, + ClosedAt: &Timestamp{referenceTime}, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, ClosedBy: &User{ID: Int64(1)}, URL: String("url"), HTMLURL: String("hurl"), diff --git a/github/teams.go b/github/teams.go index 38845e0953..963a69b3d9 100644 --- a/github/teams.go +++ b/github/teams.go @@ -10,7 +10,6 @@ import ( "fmt" "net/http" "strings" - "time" ) // TeamsService provides access to the team-related functions @@ -68,7 +67,7 @@ type Invitation struct { Email *string `json:"email,omitempty"` // Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'. Role *string `json:"role,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` Inviter *User `json:"inviter,omitempty"` TeamCount *int `json:"team_count,omitempty"` InvitationTeamURL *string `json:"invitation_team_url,omitempty"` diff --git a/github/teams_test.go b/github/teams_test.go index 6085eac13e..93e658af09 100644 --- a/github/teams_test.go +++ b/github/teams_test.go @@ -1709,7 +1709,7 @@ func TestInvitation_Marshal(t *testing.T) { Login: String("login123"), Email: String("go@github.com"), Role: String("developer"), - CreatedAt: &referenceTime, + CreatedAt: &Timestamp{referenceTime}, TeamCount: Int(99), InvitationTeamURL: String("url"), } diff --git a/github/users_gpg_keys.go b/github/users_gpg_keys.go index e9ce62221c..54189b8307 100644 --- a/github/users_gpg_keys.go +++ b/github/users_gpg_keys.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // GPGKey represents a GitHub user's public GPG key used to verify GPG signed commits and tags. @@ -26,8 +25,8 @@ type GPGKey struct { CanEncryptComms *bool `json:"can_encrypt_comms,omitempty"` CanEncryptStorage *bool `json:"can_encrypt_storage,omitempty"` CanCertify *bool `json:"can_certify,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` } // String stringifies a GPGKey. diff --git a/github/users_gpg_keys_test.go b/github/users_gpg_keys_test.go index 2cef75228b..79ebd2d6f4 100644 --- a/github/users_gpg_keys_test.go +++ b/github/users_gpg_keys_test.go @@ -11,7 +11,6 @@ import ( "fmt" "net/http" "testing" - "time" "github.com/google/go-cmp/cmp" ) @@ -210,7 +209,7 @@ func TestGPGEmail_Marshal(t *testing.T) { func TestGPGKey_Marshal(t *testing.T) { testJSONMarshal(t, &GPGKey{}, "{}") - ti := &time.Time{} + ti := &Timestamp{} g := &GPGKey{ ID: Int64(1), diff --git a/go.mod b/go.mod index 656bb7543e..ceb9aff090 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/google/go-github/v48 +module github.com/google/go-github/v50 require ( github.com/google/go-cmp v0.5.9 diff --git a/scrape/apps.go b/scrape/apps.go index 55afeb6cc8..48480a3d80 100644 --- a/scrape/apps.go +++ b/scrape/apps.go @@ -17,7 +17,7 @@ import ( "strings" "github.com/PuerkitoBio/goquery" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) // AppRestrictionsEnabled returns whether the specified organization has diff --git a/scrape/apps_test.go b/scrape/apps_test.go index 972656290a..779ffdbbd3 100644 --- a/scrape/apps_test.go +++ b/scrape/apps_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) func Test_AppRestrictionsEnabled(t *testing.T) { diff --git a/scrape/go.mod b/scrape/go.mod index 0b4c3e55e8..67b1fa9f7a 100644 --- a/scrape/go.mod +++ b/scrape/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/PuerkitoBio/goquery v1.8.0 github.com/google/go-cmp v0.5.9 - github.com/google/go-github/v48 v48.1.0 + github.com/google/go-github/v50 v50.0.0 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 ) diff --git a/scrape/go.sum b/scrape/go.sum index 3aebb2384d..824ec0544b 100644 --- a/scrape/go.sum +++ b/scrape/go.sum @@ -3,12 +3,13 @@ github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejn github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v48 v48.1.0 h1:nqPqq+0oRY2AMR/SRskGrrP4nnewPB7e/m2+kbT/UvM= -github.com/google/go-github/v48 v48.1.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y= +github.com/google/go-github/v50 v50.0.0 h1:gdO1AeuSZZK4iYWwVbjni7zg8PIQhp7QfmPunr016Jk= +github.com/google/go-github/v50 v50.0.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4= @@ -20,6 +21,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -32,4 +34,5 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= diff --git a/test/fields/fields.go b/test/fields/fields.go index 3d42b7c43b..dd21cc8ccc 100644 --- a/test/fields/fields.go +++ b/test/fields/fields.go @@ -25,8 +25,7 @@ import ( "reflect" "strings" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) var ( @@ -47,10 +46,7 @@ func main() { print("!!! No OAuth token. Some tests won't run. !!!\n\n") client = github.NewClient(nil) } else { - tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - )) - client = github.NewClient(tc) + client = github.NewTokenClient(context.Background(), token) auth = true } diff --git a/test/integration/activity_test.go b/test/integration/activity_test.go index 5d2f231bd7..669d775d31 100644 --- a/test/integration/activity_test.go +++ b/test/integration/activity_test.go @@ -12,7 +12,7 @@ import ( "context" "testing" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) const ( diff --git a/test/integration/authorizations_test.go b/test/integration/authorizations_test.go index 50d38e0cc0..d94a750667 100644 --- a/test/integration/authorizations_test.go +++ b/test/integration/authorizations_test.go @@ -17,7 +17,7 @@ import ( "testing" "time" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) const msgEnvMissing = "Skipping test because the required environment variable (%v) is not present." diff --git a/test/integration/github_test.go b/test/integration/github_test.go index aabada850c..892a7178c4 100644 --- a/test/integration/github_test.go +++ b/test/integration/github_test.go @@ -15,8 +15,7 @@ import ( "net/http" "os" - "github.com/google/go-github/v48/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v50/github" ) var ( @@ -33,10 +32,7 @@ func init() { print("!!! No OAuth token. Some tests won't run. !!!\n\n") client = github.NewClient(nil) } else { - tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - )) - client = github.NewClient(tc) + client = github.NewTokenClient(context.Background(), token) auth = true } } diff --git a/test/integration/repos_test.go b/test/integration/repos_test.go index 49ff98f0da..8efed472da 100644 --- a/test/integration/repos_test.go +++ b/test/integration/repos_test.go @@ -15,7 +15,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) func TestRepositories_CRUD(t *testing.T) { diff --git a/test/integration/users_test.go b/test/integration/users_test.go index cd7797b696..d844896580 100644 --- a/test/integration/users_test.go +++ b/test/integration/users_test.go @@ -14,7 +14,7 @@ import ( "math/rand" "testing" - "github.com/google/go-github/v48/github" + "github.com/google/go-github/v50/github" ) func TestUsers_Get(t *testing.T) { diff --git a/update-urls/go.mod b/update-urls/go.mod index 2fed223a35..d7419e2812 100644 --- a/update-urls/go.mod +++ b/update-urls/go.mod @@ -1,4 +1,4 @@ -module github.com/google/go-github/v48/update-urls +module github.com/google/go-github/v50/update-urls go 1.16