Skip to content

Latest commit

 

History

History
1868 lines (1619 loc) · 65.1 KB

File metadata and controls

1868 lines (1619 loc) · 65.1 KB
 
Jun 27, 2013
Jun 27, 2013
1
// Copyright 2013 The go-github AUTHORS. All rights reserved.
May 24, 2013
May 24, 2013
2
//
3
// Use of this source code is governed by a BSD-style
Jun 27, 2013
Jun 27, 2013
4
// license that can be found in the LICENSE file.
May 24, 2013
May 24, 2013
5
Feb 24, 2017
Feb 24, 2017
6
//go:generate go run gen-accessors.go
Feb 3, 2026
Feb 3, 2026
7
//go:generate go run gen-iterators.go
Jun 21, 2019
Jun 21, 2019
8
//go:generate go run gen-stringify-test.go
Jan 30, 2026
Jan 30, 2026
9
//go:generate sh ../script/metadata.sh update-go
Feb 24, 2017
Feb 24, 2017
10
May 24, 2013
May 24, 2013
11
package github
12
13
import (
14
"bytes"
Feb 20, 2017
Feb 20, 2017
15
"context"
May 24, 2013
May 24, 2013
16
"encoding/json"
Jun 16, 2013
Jun 16, 2013
17
"errors"
May 24, 2013
May 24, 2013
18
"fmt"
Oct 21, 2013
Oct 21, 2013
19
"io"
May 24, 2013
May 24, 2013
20
"net/http"
21
"net/url"
Feb 13, 2025
Feb 13, 2025
22
"regexp"
Jun 27, 2013
Jun 27, 2013
23
"strconv"
Aug 1, 2013
Aug 1, 2013
24
"strings"
Dec 29, 2015
Dec 29, 2015
25
"sync"
Jul 3, 2013
Jul 3, 2013
26
"time"
Sep 10, 2013
Sep 10, 2013
27
28
"github.com/google/go-querystring/query"
May 24, 2013
May 24, 2013
29
)
30
31
const (
Feb 27, 2026
Feb 27, 2026
32
Version = "v84.0.0"
Aug 13, 2022
Aug 13, 2022
33
Feb 4, 2026
Feb 4, 2026
34
HeaderRateLimit = "X-Ratelimit-Limit"
35
HeaderRateRemaining = "X-Ratelimit-Remaining"
36
HeaderRateReset = "X-Ratelimit-Reset"
37
HeaderRateResource = "X-Ratelimit-Resource"
38
HeaderRateUsed = "X-Ratelimit-Used"
39
HeaderRequestID = "X-Github-Request-Id"
40
Dec 9, 2022
Dec 9, 2022
41
defaultAPIVersion = "2022-11-28"
42
defaultBaseURL = "https://api.github.com/"
43
defaultUserAgent = "go-github" + "/" + Version
44
uploadBaseURL = "https://uploads.github.com/"
Jun 27, 2013
Jun 27, 2013
45
Feb 4, 2026
Feb 4, 2026
46
headerAPIVersion = "X-Github-Api-Version"
47
headerOTP = "X-Github-Otp"
48
headerRetryAfter = "Retry-After"
Aug 5, 2013
Aug 5, 2013
49
Dec 16, 2024
Dec 16, 2024
50
headerTokenExpiration = "Github-Authentication-Token-Expiration"
Aug 6, 2021
Aug 6, 2021
51
Jun 10, 2016
Jun 10, 2016
52
mediaTypeV3 = "application/vnd.github.v3+json"
53
defaultMediaType = "application/octet-stream"
54
mediaTypeV3SHA = "application/vnd.github.v3.sha"
Dec 19, 2016
Dec 19, 2016
55
mediaTypeV3Diff = "application/vnd.github.v3.diff"
56
mediaTypeV3Patch = "application/vnd.github.v3.patch"
Jun 10, 2016
Jun 10, 2016
57
mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json"
Aug 15, 2020
Aug 15, 2020
58
mediaTypeIssueImportAPI = "application/vnd.github.golden-comet-preview+json"
Jun 9, 2025
Jun 9, 2025
59
mediaTypeStarring = "application/vnd.github.star+json"
Nov 25, 2025
Nov 25, 2025
60
mediaTypeSCIM = "application/scim+json"
Aug 25, 2014
Aug 25, 2014
61
Nov 13, 2025
Nov 13, 2025
62
// Media Type values to access preview APIs.
Oct 31, 2021
Oct 31, 2021
63
// These media types will be added to the API request as headers
64
// and used to enable particular features on GitHub API that are still in preview.
65
// After some time, specific media types will be promoted (to a "stable" state).
66
// From then on, the preview headers are not required anymore to activate the additional
67
// feature on GitHub.com's API. However, this API header might still be needed for users
68
// to run a GitHub Enterprise Server on-premise.
69
// It's not uncommon for GitHub Enterprise Server customers to run older versions which
70
// would probably rely on the preview headers for some time.
71
// While the header promotion is going out for GitHub.com, it may be some time before it
72
// even arrives in GitHub Enterprise Server.
73
// We keep those preview headers around to avoid breaking older GitHub Enterprise Server
74
// versions. Additionally, non-functional (preview) headers don't create any side effects
75
// on GitHub Cloud version.
76
//
Nov 13, 2025
Nov 13, 2025
77
// See https://github.com/google/go-github/pull/2125 and https://github.com/google/go-github/pull/2188 for full context.
Aug 25, 2014
Aug 25, 2014
78
Mar 23, 2016
Mar 23, 2016
79
// https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/
80
mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json"
Apr 8, 2016
Apr 8, 2016
81
82
// https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/
83
mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-preview+json"
May 6, 2016
May 6, 2016
84
Oct 30, 2018
Oct 30, 2018
85
// https://developer.github.com/changes/2018-10-16-deployments-environments-states-and-auto-inactive-updates/
86
mediaTypeExpandDeploymentStatusPreview = "application/vnd.github.flash-preview+json"
87
May 14, 2016
May 14, 2016
88
// https://developer.github.com/changes/2016-05-12-reactions-api-preview/
89
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
Jun 10, 2016
Jun 10, 2016
90
Jun 17, 2016
Jun 17, 2016
91
// https://developer.github.com/changes/2016-05-23-timeline-preview-api/
92
mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json"
Jun 20, 2016
Jun 20, 2016
93
Sep 30, 2016
Sep 30, 2016
94
// https://developer.github.com/changes/2016-09-14-projects-api/
95
mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json"
Oct 12, 2016
Oct 12, 2016
96
Nov 13, 2025
Nov 13, 2025
97
// https://developer.github.com/changes/2017-01-05-commit-search-api/
98
mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json"
99
Apr 8, 2017
Apr 8, 2017
100
// https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/
101
mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json"
May 15, 2017
May 15, 2017
102
Jun 3, 2017
Jun 3, 2017
103
// https://developer.github.com/changes/2017-05-23-coc-api/
104
mediaTypeCodesOfConductPreview = "application/vnd.github.scarlet-witch-preview+json"
Jul 19, 2017
Jul 19, 2017
105
106
// https://developer.github.com/changes/2017-07-17-update-topics-on-repositories/
107
mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json"
Aug 9, 2017
Aug 9, 2017
108
Aug 10, 2018
Aug 10, 2018
109
// https://developer.github.com/changes/2018-03-16-protected-branches-required-approving-reviews/
110
mediaTypeRequiredApprovingReviewsPreview = "application/vnd.github.luke-cage-preview+json"
111
Feb 22, 2022
Feb 22, 2022
112
// https://developer.github.com/changes/2018-05-07-new-checks-api-public-beta/
113
mediaTypeCheckRunsPreview = "application/vnd.github.antiope-preview+json"
114
Aug 2, 2018
Aug 2, 2018
115
// https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/
116
mediaTypePreReceiveHooksPreview = "application/vnd.github.eye-scream-preview"
Nov 9, 2018
Nov 9, 2018
117
118
// https://developer.github.com/changes/2018-02-22-protected-branches-required-signatures/
119
mediaTypeSignaturePreview = "application/vnd.github.zzzax-preview+json"
Dec 8, 2018
Dec 8, 2018
120
121
// https://developer.github.com/changes/2018-09-05-project-card-events/
122
mediaTypeProjectCardDetailsPreview = "application/vnd.github.starfox-preview+json"
Dec 28, 2018
Dec 28, 2018
123
124
// https://developer.github.com/changes/2018-12-18-interactions-preview/
Jan 3, 2019
Jan 3, 2019
125
mediaTypeInteractionRestrictionsPreview = "application/vnd.github.sombra-preview+json"
Feb 15, 2019
Feb 15, 2019
126
Apr 14, 2019
Apr 14, 2019
127
// https://developer.github.com/changes/2019-03-14-enabling-disabling-pages/
128
mediaTypeEnablePagesAPIPreview = "application/vnd.github.switcheroo-preview+json"
May 13, 2019
May 13, 2019
129
130
// https://developer.github.com/changes/2019-04-24-vulnerability-alerts/
131
mediaTypeRequiredVulnerabilityAlertsPreview = "application/vnd.github.dorian-preview+json"
Jun 9, 2019
Jun 9, 2019
132
133
// https://developer.github.com/changes/2019-05-29-update-branch-api/
134
mediaTypeUpdatePullRequestBranchPreview = "application/vnd.github.lydian-preview+json"
Jun 10, 2019
Jun 10, 2019
135
136
// https://developer.github.com/changes/2019-04-11-pulls-branches-for-commit/
137
mediaTypeListPullsOrBranchesForCommitPreview = "application/vnd.github.groot-preview+json"
Jul 8, 2019
Jul 8, 2019
138
Nov 3, 2023
Nov 3, 2023
139
// https://docs.github.com/rest/previews/#repository-creation-permissions
Jul 8, 2019
Jul 8, 2019
140
mediaTypeMemberAllowedRepoCreationTypePreview = "application/vnd.github.surtur-preview+json"
Jul 25, 2019
Jul 25, 2019
141
Nov 3, 2023
Nov 3, 2023
142
// https://docs.github.com/rest/previews/#create-and-use-repository-templates
Jul 25, 2019
Jul 25, 2019
143
mediaTypeRepositoryTemplatePreview = "application/vnd.github.baptiste-preview+json"
Dec 23, 2019
Dec 23, 2019
144
145
// https://developer.github.com/changes/2019-10-03-multi-line-comments/
146
mediaTypeMultiLineCommentsPreview = "application/vnd.github.comfort-fade-preview+json"
Mar 17, 2020
Mar 17, 2020
147
148
// https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/
149
mediaTypeOAuthAppPreview = "application/vnd.github.doctor-strange-preview+json"
Mar 25, 2020
Mar 25, 2020
150
151
// https://developer.github.com/changes/2019-12-03-internal-visibility-changes/
152
mediaTypeRepositoryVisibilityPreview = "application/vnd.github.nebula-preview+json"
Aug 17, 2020
Aug 17, 2020
153
154
// https://developer.github.com/changes/2018-12-10-content-attachments-api/
155
mediaTypeContentAttachmentsPreview = "application/vnd.github.corsair-preview+json"
May 24, 2013
May 24, 2013
156
)
157
Apr 14, 2021
Apr 14, 2021
158
var errNonNilContext = errors.New("context must be non-nil")
159
May 24, 2013
May 24, 2013
160
// A Client manages communication with the GitHub API.
161
type Client struct {
Jan 2, 2025
Jan 2, 2025
162
clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func.
163
client *http.Client // HTTP client used to communicate with the API.
164
clientIgnoreRedirects *http.Client // HTTP client used to communicate with the API on endpoints where we don't want to follow redirects.
May 24, 2013
May 24, 2013
165
Feb 15, 2017
Feb 15, 2017
166
// Base URL for API requests. Defaults to the public GitHub API, but can be
167
// set to a domain endpoint to use with GitHub Enterprise. BaseURL should
May 24, 2013
May 24, 2013
168
// always be specified with a trailing slash.
169
BaseURL *url.URL
170
Oct 21, 2013
Oct 21, 2013
171
// Base URL for uploading files.
172
UploadURL *url.URL
173
May 24, 2013
May 24, 2013
174
// User agent used when communicating with the GitHub API.
175
UserAgent string
176
Jun 30, 2025
Jun 30, 2025
177
// DisableRateLimitCheck stops the client checking for rate limits or tracking
178
// them. This is different to setting BypassRateLimitCheck in the context,
179
// as that still tracks the rate limits.
180
DisableRateLimitCheck bool
181
Jan 30, 2023
Jan 30, 2023
182
rateMu sync.Mutex
Mar 20, 2024
Mar 20, 2024
183
rateLimits [Categories]Rate // Rate limits for the client as determined by the most recent API calls.
Jan 30, 2023
Jan 30, 2023
184
secondaryRateLimitReset time.Time // Secondary rate limit reset for the client as determined by the most recent API calls.
Jun 27, 2013
Jun 27, 2013
185
Feb 5, 2025
Feb 5, 2025
186
// If specified, Client will block requests for at most this duration in case of reaching a secondary
187
// rate limit
188
MaxSecondaryRateLimitRetryAfterDuration time.Duration
189
Jan 2, 2025
Jan 2, 2025
190
// Whether to respect rate limit headers on endpoints that return 302 redirections to artifacts
191
RateLimitRedirectionalEndpoints bool
192
Jun 29, 2016
Jun 29, 2016
193
common service // Reuse a single struct instead of allocating one for each service on the heap.
194
Sep 4, 2013
Sep 4, 2013
195
// Services used for talking to different parts of the GitHub API.
Aug 13, 2023
Aug 13, 2023
196
Actions *ActionsService
197
Activity *ActivityService
198
Admin *AdminService
199
Apps *AppsService
200
Authorizations *AuthorizationsService
201
Billing *BillingService
202
Checks *ChecksService
Aug 18, 2025
Aug 18, 2025
203
Classroom *ClassroomService
Aug 13, 2023
Aug 13, 2023
204
CodeScanning *CodeScanningService
Oct 9, 2023
Oct 9, 2023
205
CodesOfConduct *CodesOfConductService
Aug 13, 2023
Aug 13, 2023
206
Codespaces *CodespacesService
Dec 19, 2023
Dec 19, 2023
207
Copilot *CopilotService
Dec 1, 2025
Dec 1, 2025
208
Credentials *CredentialsService
Aug 13, 2023
Aug 13, 2023
209
Dependabot *DependabotService
Aug 16, 2023
Aug 16, 2023
210
DependencyGraph *DependencyGraphService
Oct 9, 2023
Oct 9, 2023
211
Emojis *EmojisService
Aug 13, 2023
Aug 13, 2023
212
Enterprise *EnterpriseService
213
Gists *GistsService
214
Git *GitService
215
Gitignores *GitignoresService
216
Interactions *InteractionsService
217
IssueImport *IssueImportService
218
Issues *IssuesService
219
Licenses *LicensesService
Oct 9, 2023
Oct 9, 2023
220
Markdown *MarkdownService
Aug 13, 2023
Aug 13, 2023
221
Marketplace *MarketplaceService
Oct 9, 2023
Oct 9, 2023
222
Meta *MetaService
Aug 13, 2023
Aug 13, 2023
223
Migrations *MigrationService
224
Organizations *OrganizationsService
Oct 23, 2025
Oct 23, 2025
225
PrivateRegistries *PrivateRegistriesService
Oct 14, 2025
Oct 14, 2025
226
Projects *ProjectsService
Aug 13, 2023
Aug 13, 2023
227
PullRequests *PullRequestsService
Oct 20, 2023
Oct 20, 2023
228
RateLimit *RateLimitService
Aug 13, 2023
Aug 13, 2023
229
Reactions *ReactionsService
230
Repositories *RepositoriesService
231
SCIM *SCIMService
232
Search *SearchService
233
SecretScanning *SecretScanningService
234
SecurityAdvisories *SecurityAdvisoriesService
May 26, 2025
May 26, 2025
235
SubIssue *SubIssueService
Aug 13, 2023
Aug 13, 2023
236
Teams *TeamsService
237
Users *UsersService
May 24, 2013
May 24, 2013
238
}
239
Jun 29, 2016
Jun 29, 2016
240
type service struct {
241
client *Client
242
}
243
Jul 21, 2021
Jul 21, 2021
244
// Client returns the http.Client used by this GitHub client.
Dec 16, 2023
Dec 16, 2023
245
// This should only be used for requests to the GitHub API because
246
// request headers will contain an authorization token.
Jul 21, 2021
Jul 21, 2021
247
func (c *Client) Client() *http.Client {
248
c.clientMu.Lock()
249
defer c.clientMu.Unlock()
250
clientCopy := *c.client
251
return &clientCopy
252
}
253
May 24, 2013
May 24, 2013
254
// ListOptions specifies the optional parameters to various List methods that
Jan 20, 2020
Jan 20, 2020
255
// support offset pagination.
May 24, 2013
May 24, 2013
256
type ListOptions struct {
257
// For paginated result sets, page of results to retrieve.
Sep 10, 2013
Sep 10, 2013
258
Page int `url:"page,omitempty"`
Sep 9, 2013
Sep 9, 2013
259
260
// For paginated result sets, the number of results to include per page.
Sep 10, 2013
Sep 10, 2013
261
PerPage int `url:"per_page,omitempty"`
262
}
263
Jan 20, 2020
Jan 20, 2020
264
// ListCursorOptions specifies the optional parameters to various List methods that
265
// support cursor pagination.
266
type ListCursorOptions struct {
267
// For paginated result sets, page of results to retrieve.
268
Page string `url:"page,omitempty"`
269
270
// For paginated result sets, the number of results to include per page.
271
PerPage int `url:"per_page,omitempty"`
Mar 20, 2021
Mar 20, 2021
272
Nov 7, 2022
Nov 7, 2022
273
// For paginated result sets, the number of results per page (max 100), starting from the first matching result.
274
// This parameter must not be used in combination with last.
275
First int `url:"first,omitempty"`
276
277
// For paginated result sets, the number of results per page (max 100), starting from the last matching result.
278
// This parameter must not be used in combination with first.
279
Last int `url:"last,omitempty"`
280
Mar 20, 2021
Mar 20, 2021
281
// A cursor, as given in the Link header. If specified, the query only searches for events after this cursor.
282
After string `url:"after,omitempty"`
283
284
// A cursor, as given in the Link header. If specified, the query only searches for events before this cursor.
285
Before string `url:"before,omitempty"`
Jul 13, 2021
Jul 13, 2021
286
287
// A cursor, as given in the Link header. If specified, the query continues the search using this cursor.
288
Cursor string `url:"cursor,omitempty"`
Jan 20, 2020
Jan 20, 2020
289
}
290
Oct 21, 2013
Oct 21, 2013
291
// UploadOptions specifies the parameters to methods that support uploads.
292
type UploadOptions struct {
Jan 25, 2019
Jan 25, 2019
293
Name string `url:"name,omitempty"`
Jan 28, 2019
Jan 28, 2019
294
Label string `url:"label,omitempty"`
Jan 25, 2019
Jan 25, 2019
295
MediaType string `url:"-"`
Oct 21, 2013
Oct 21, 2013
296
}
297
Dec 19, 2016
Dec 19, 2016
298
// RawType represents type of raw format of a request instead of JSON.
299
type RawType uint8
300
301
const (
302
// Diff format.
303
Diff RawType = 1 + iota
304
// Patch format.
305
Patch
306
)
307
308
// RawOptions specifies parameters when user wants to get raw format of
309
// a response instead of JSON.
310
type RawOptions struct {
311
Type RawType
312
}
313
Feb 13, 2026
Feb 13, 2026
314
type structPtr[T any] interface{ *T }
315
Jun 24, 2020
Jun 24, 2020
316
// addOptions adds the parameters in opts as URL query parameters to s. opts
Sep 10, 2013
Sep 10, 2013
317
// must be a struct whose fields may contain "url" tags.
Feb 13, 2026
Feb 13, 2026
318
func addOptions[P structPtr[T], T any](s string, opts P) (string, error) {
319
if opts == nil {
Sep 10, 2013
Sep 10, 2013
320
return s, nil
321
}
322
323
u, err := url.Parse(s)
324
if err != nil {
325
return s, err
326
}
327
Feb 10, 2020
Feb 10, 2020
328
qs, err := query.Values(opts)
Sep 10, 2013
Sep 10, 2013
329
if err != nil {
330
return s, err
331
}
332
333
u.RawQuery = qs.Encode()
334
return u.String(), nil
May 24, 2013
May 24, 2013
335
}
336
Feb 15, 2017
Feb 15, 2017
337
// NewClient returns a new GitHub API client. If a nil httpClient is
May 30, 2019
May 30, 2019
338
// provided, a new http.Client will be used. To use API methods which require
Aug 31, 2023
Aug 31, 2023
339
// authentication, either use Client.WithAuthToken or provide NewClient with
Aug 27, 2023
Aug 27, 2023
340
// an http.Client that will perform the authentication for you (such as that
341
// provided by the golang.org/x/oauth2 library).
Jan 12, 2026
Jan 12, 2026
342
//
343
// Note: When using a nil httpClient, the default client has no timeout set.
344
// This may not be suitable for production environments. It is recommended to
345
// provide a custom http.Client with an appropriate timeout.
May 24, 2013
May 24, 2013
346
func NewClient(httpClient *http.Client) *Client {
Dec 16, 2023
Dec 16, 2023
347
if httpClient == nil {
348
httpClient = &http.Client{}
349
}
350
httpClient2 := *httpClient
351
c := &Client{client: &httpClient2}
Aug 31, 2023
Aug 31, 2023
352
c.initialize()
353
return c
354
}
355
356
// WithAuthToken returns a copy of the client configured to use the provided token for the Authorization header.
357
func (c *Client) WithAuthToken(token string) *Client {
358
c2 := c.copy()
359
defer c2.initialize()
360
transport := c2.client.Transport
361
if transport == nil {
362
transport = http.DefaultTransport
363
}
364
c2.client.Transport = roundTripperFunc(
365
func(req *http.Request) (*http.Response, error) {
366
req = req.Clone(req.Context())
Oct 23, 2025
Oct 23, 2025
367
if token != "" {
368
req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", token))
369
}
Aug 31, 2023
Aug 31, 2023
370
return transport.RoundTrip(req)
371
},
372
)
373
return c2
374
}
375
376
// WithEnterpriseURLs returns a copy of the client configured to use the provided base and
377
// upload URLs. If the base URL does not have the suffix "/api/v3/", it will be added
378
// automatically. If the upload URL does not have the suffix "/api/uploads", it will be
379
// added automatically.
380
//
381
// Note that WithEnterpriseURLs is a convenience helper only;
382
// its behavior is equivalent to setting the BaseURL and UploadURL fields.
383
//
384
// Another important thing is that by default, the GitHub Enterprise URL format
385
// should be http(s)://[hostname]/api/v3/ or you will always receive the 406 status code.
386
// The upload URL format should be http(s)://[hostname]/api/uploads/.
387
func (c *Client) WithEnterpriseURLs(baseURL, uploadURL string) (*Client, error) {
388
c2 := c.copy()
389
defer c2.initialize()
390
var err error
391
c2.BaseURL, err = url.Parse(baseURL)
392
if err != nil {
393
return nil, err
394
}
395
396
if !strings.HasSuffix(c2.BaseURL.Path, "/") {
397
c2.BaseURL.Path += "/"
398
}
399
if !strings.HasSuffix(c2.BaseURL.Path, "/api/v3/") &&
400
!strings.HasPrefix(c2.BaseURL.Host, "api.") &&
401
!strings.Contains(c2.BaseURL.Host, ".api.") {
402
c2.BaseURL.Path += "api/v3/"
403
}
404
405
c2.UploadURL, err = url.Parse(uploadURL)
406
if err != nil {
407
return nil, err
May 24, 2013
May 24, 2013
408
}
409
Aug 31, 2023
Aug 31, 2023
410
if !strings.HasSuffix(c2.UploadURL.Path, "/") {
411
c2.UploadURL.Path += "/"
412
}
413
if !strings.HasSuffix(c2.UploadURL.Path, "/api/uploads/") &&
414
!strings.HasPrefix(c2.UploadURL.Host, "api.") &&
Feb 11, 2026
Feb 11, 2026
415
!strings.Contains(c2.UploadURL.Host, ".api.") &&
416
!strings.HasPrefix(c2.UploadURL.Host, "uploads.") {
Aug 31, 2023
Aug 31, 2023
417
c2.UploadURL.Path += "api/uploads/"
418
}
419
return c2, nil
420
}
421
422
// initialize sets default values and initializes services.
423
func (c *Client) initialize() {
424
if c.client == nil {
425
c.client = &http.Client{}
426
}
Jan 2, 2025
Jan 2, 2025
427
// Copy the main http client into the IgnoreRedirects one, overriding the `CheckRedirect` func
428
c.clientIgnoreRedirects = &http.Client{}
429
c.clientIgnoreRedirects.Transport = c.client.Transport
430
c.clientIgnoreRedirects.Timeout = c.client.Timeout
431
c.clientIgnoreRedirects.Jar = c.client.Jar
Jun 25, 2025
Jun 25, 2025
432
c.clientIgnoreRedirects.CheckRedirect = func(*http.Request, []*http.Request) error {
Jan 2, 2025
Jan 2, 2025
433
return http.ErrUseLastResponse
434
}
Aug 31, 2023
Aug 31, 2023
435
if c.BaseURL == nil {
436
c.BaseURL, _ = url.Parse(defaultBaseURL)
437
}
438
if c.UploadURL == nil {
439
c.UploadURL, _ = url.Parse(uploadBaseURL)
440
}
441
if c.UserAgent == "" {
442
c.UserAgent = defaultUserAgent
443
}
Jun 29, 2016
Jun 29, 2016
444
c.common.client = c
Feb 10, 2020
Feb 10, 2020
445
c.Actions = (*ActionsService)(&c.common)
Jun 29, 2016
Jun 29, 2016
446
c.Activity = (*ActivityService)(&c.common)
Nov 30, 2016
Nov 30, 2016
447
c.Admin = (*AdminService)(&c.common)
Jun 4, 2017
Jun 4, 2017
448
c.Apps = (*AppsService)(&c.common)
Jun 29, 2016
Jun 29, 2016
449
c.Authorizations = (*AuthorizationsService)(&c.common)
Feb 9, 2021
Feb 9, 2021
450
c.Billing = (*BillingService)(&c.common)
Jul 25, 2018
Jul 25, 2018
451
c.Checks = (*ChecksService)(&c.common)
Aug 18, 2025
Aug 18, 2025
452
c.Classroom = (*ClassroomService)(&c.common)
Jun 17, 2020
Jun 17, 2020
453
c.CodeScanning = (*CodeScanningService)(&c.common)
Jun 19, 2023
Jun 19, 2023
454
c.Codespaces = (*CodespacesService)(&c.common)
Oct 9, 2023
Oct 9, 2023
455
c.CodesOfConduct = (*CodesOfConductService)(&c.common)
Dec 19, 2023
Dec 19, 2023
456
c.Copilot = (*CopilotService)(&c.common)
Dec 1, 2025
Dec 1, 2025
457
c.Credentials = (*CredentialsService)(&c.common)
Jan 13, 2022
Jan 13, 2022
458
c.Dependabot = (*DependabotService)(&c.common)
Aug 16, 2023
Aug 16, 2023
459
c.DependencyGraph = (*DependencyGraphService)(&c.common)
Oct 9, 2023
Oct 9, 2023
460
c.Emojis = (*EmojisService)(&c.common)
Dec 2, 2020
Dec 2, 2020
461
c.Enterprise = (*EnterpriseService)(&c.common)
Jun 29, 2016
Jun 29, 2016
462
c.Gists = (*GistsService)(&c.common)
463
c.Git = (*GitService)(&c.common)
464
c.Gitignores = (*GitignoresService)(&c.common)
Dec 28, 2018
Dec 28, 2018
465
c.Interactions = (*InteractionsService)(&c.common)
Aug 15, 2020
Aug 15, 2020
466
c.IssueImport = (*IssueImportService)(&c.common)
Jun 29, 2016
Jun 29, 2016
467
c.Issues = (*IssuesService)(&c.common)
468
c.Licenses = (*LicensesService)(&c.common)
Oct 9, 2023
Oct 9, 2023
469
c.Markdown = (*MarkdownService)(&c.common)
Nov 14, 2017
Nov 14, 2017
470
c.Marketplace = &MarketplaceService{client: c}
Oct 9, 2023
Oct 9, 2023
471
c.Meta = (*MetaService)(&c.common)
Jun 29, 2016
Jun 29, 2016
472
c.Migrations = (*MigrationService)(&c.common)
473
c.Organizations = (*OrganizationsService)(&c.common)
Oct 23, 2025
Oct 23, 2025
474
c.PrivateRegistries = (*PrivateRegistriesService)(&c.common)
Oct 14, 2025
Oct 14, 2025
475
c.Projects = (*ProjectsService)(&c.common)
Jun 29, 2016
Jun 29, 2016
476
c.PullRequests = (*PullRequestsService)(&c.common)
Oct 20, 2023
Oct 20, 2023
477
c.RateLimit = (*RateLimitService)(&c.common)
Jun 29, 2016
Jun 29, 2016
478
c.Reactions = (*ReactionsService)(&c.common)
479
c.Repositories = (*RepositoriesService)(&c.common)
Sep 3, 2021
Sep 3, 2021
480
c.SCIM = (*SCIMService)(&c.common)
Jun 29, 2016
Jun 29, 2016
481
c.Search = (*SearchService)(&c.common)
Feb 4, 2022
Feb 4, 2022
482
c.SecretScanning = (*SecretScanningService)(&c.common)
Aug 13, 2023
Aug 13, 2023
483
c.SecurityAdvisories = (*SecurityAdvisoriesService)(&c.common)
May 26, 2025
May 26, 2025
484
c.SubIssue = (*SubIssueService)(&c.common)
Apr 27, 2018
Apr 27, 2018
485
c.Teams = (*TeamsService)(&c.common)
Jun 29, 2016
Jun 29, 2016
486
c.Users = (*UsersService)(&c.common)
Aug 31, 2023
Aug 31, 2023
487
}
488
489
// copy returns a copy of the current client. It must be initialized before use.
490
func (c *Client) copy() *Client {
491
c.clientMu.Lock()
492
// can't use *c here because that would copy mutexes by value.
493
clone := Client{
Jan 2, 2025
Jan 2, 2025
494
client: &http.Client{},
495
UserAgent: c.UserAgent,
496
BaseURL: c.BaseURL,
497
UploadURL: c.UploadURL,
498
RateLimitRedirectionalEndpoints: c.RateLimitRedirectionalEndpoints,
499
secondaryRateLimitReset: c.secondaryRateLimitReset,
Aug 31, 2023
Aug 31, 2023
500
}
501
c.clientMu.Unlock()
Jan 28, 2024
Jan 28, 2024
502
if c.client != nil {
503
clone.client.Transport = c.client.Transport
504
clone.client.CheckRedirect = c.client.CheckRedirect
505
clone.client.Jar = c.client.Jar
506
clone.client.Timeout = c.client.Timeout
Aug 31, 2023
Aug 31, 2023
507
}
508
c.rateMu.Lock()
509
copy(clone.rateLimits[:], c.rateLimits[:])
510
c.rateMu.Unlock()
511
return &clone
May 24, 2013
May 24, 2013
512
}
513
Mar 8, 2023
Mar 8, 2023
514
// NewClientWithEnvProxy enhances NewClient with the HttpProxy env.
515
func NewClientWithEnvProxy() *Client {
516
return NewClient(&http.Client{Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}})
517
}
518
Jan 25, 2023
Jan 25, 2023
519
// NewTokenClient returns a new GitHub API client authenticated with the provided token.
Nov 8, 2025
Nov 8, 2025
520
//
Aug 31, 2023
Aug 31, 2023
521
// Deprecated: Use NewClient(nil).WithAuthToken(token) instead.
Aug 27, 2023
Aug 27, 2023
522
func NewTokenClient(_ context.Context, token string) *Client {
Aug 31, 2023
Aug 31, 2023
523
// This always returns a nil error.
524
return NewClient(nil).WithAuthToken(token)
Jan 25, 2023
Jan 25, 2023
525
}
526
Oct 30, 2017
Oct 30, 2017
527
// NewEnterpriseClient returns a new GitHub API client with provided
Apr 28, 2020
Apr 28, 2020
528
// base URL and upload URL (often is your GitHub Enterprise hostname).
Sep 18, 2019
Sep 18, 2019
529
//
Sep 18, 2023
Sep 18, 2023
530
// Deprecated: Use NewClient(httpClient).WithEnterpriseURLs(baseURL, uploadURL) instead.
Oct 30, 2017
Oct 30, 2017
531
func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) {
Aug 31, 2023
Aug 31, 2023
532
return NewClient(httpClient).WithEnterpriseURLs(baseURL, uploadURL)
Oct 30, 2017
Oct 30, 2017
533
}
534
Dec 9, 2022
Dec 9, 2022
535
// RequestOption represents an option that can modify an http.Request.
536
type RequestOption func(req *http.Request)
537
538
// WithVersion overrides the GitHub v3 API version for this individual request.
539
// For more information, see:
540
// https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/
541
func WithVersion(version string) RequestOption {
542
return func(req *http.Request) {
543
req.Header.Set(headerAPIVersion, version)
544
}
545
}
546
Jun 16, 2013
Jun 16, 2013
547
// NewRequest creates an API request. A relative URL can be provided in urlStr,
May 24, 2013
May 24, 2013
548
// in which case it is resolved relative to the BaseURL of the Client.
Feb 15, 2017
Feb 15, 2017
549
// Relative URLs should always be specified without a preceding slash. If
May 24, 2013
May 24, 2013
550
// specified, the value pointed to by body is JSON encoded and included as the
551
// request body.
Jun 3, 2025
Jun 3, 2025
552
func (c *Client) NewRequest(method, urlStr string, body any, opts ...RequestOption) (*http.Request, error) {
Aug 15, 2017
Aug 15, 2017
553
if !strings.HasSuffix(c.BaseURL.Path, "/") {
Jan 21, 2025
Jan 21, 2025
554
return nil, fmt.Errorf("baseURL must have a trailing slash, but %q does not", c.BaseURL)
Aug 15, 2017
Aug 15, 2017
555
}
Mar 25, 2022
Mar 25, 2022
556
Aug 15, 2017
Aug 15, 2017
557
u, err := c.BaseURL.Parse(urlStr)
May 24, 2013
May 24, 2013
558
if err != nil {
559
return nil, err
560
}
561
Oct 15, 2014
Oct 15, 2014
562
var buf io.ReadWriter
May 24, 2013
May 24, 2013
563
if body != nil {
Jan 25, 2020
Jan 25, 2020
564
buf = &bytes.Buffer{}
Oct 26, 2017
Oct 26, 2017
565
enc := json.NewEncoder(buf)
566
enc.SetEscapeHTML(false)
567
err := enc.Encode(body)
May 24, 2013
May 24, 2013
568
if err != nil {
569
return nil, err
570
}
571
}
572
Jun 16, 2013
Jun 16, 2013
573
req, err := http.NewRequest(method, u.String(), buf)
May 24, 2013
May 24, 2013
574
if err != nil {
575
return nil, err
576
}
577
Jul 19, 2016
Jul 19, 2016
578
if body != nil {
579
req.Header.Set("Content-Type", "application/json")
580
}
Jul 18, 2016
Jul 18, 2016
581
req.Header.Set("Accept", mediaTypeV3)
Oct 14, 2014
Oct 14, 2014
582
if c.UserAgent != "" {
Jul 18, 2016
Jul 18, 2016
583
req.Header.Set("User-Agent", c.UserAgent)
Oct 14, 2014
Oct 14, 2014
584
}
Dec 9, 2022
Dec 9, 2022
585
req.Header.Set(headerAPIVersion, defaultAPIVersion)
586
587
for _, opt := range opts {
588
opt(req)
589
}
590
May 24, 2013
May 24, 2013
591
return req, nil
592
}
593
Aug 21, 2022
Aug 21, 2022
594
// NewFormRequest creates an API request. A relative URL can be provided in urlStr,
595
// in which case it is resolved relative to the BaseURL of the Client.
596
// Relative URLs should always be specified without a preceding slash.
597
// Body is sent with Content-Type: application/x-www-form-urlencoded.
Dec 9, 2022
Dec 9, 2022
598
func (c *Client) NewFormRequest(urlStr string, body io.Reader, opts ...RequestOption) (*http.Request, error) {
Aug 21, 2022
Aug 21, 2022
599
if !strings.HasSuffix(c.BaseURL.Path, "/") {
Jan 21, 2025
Jan 21, 2025
600
return nil, fmt.Errorf("baseURL must have a trailing slash, but %q does not", c.BaseURL)
Aug 21, 2022
Aug 21, 2022
601
}
602
603
u, err := c.BaseURL.Parse(urlStr)
604
if err != nil {
605
return nil, err
606
}
607
Sep 23, 2025
Sep 23, 2025
608
req, err := http.NewRequest("POST", u.String(), body)
Aug 21, 2022
Aug 21, 2022
609
if err != nil {
610
return nil, err
611
}
612
613
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
614
req.Header.Set("Accept", mediaTypeV3)
615
if c.UserAgent != "" {
616
req.Header.Set("User-Agent", c.UserAgent)
617
}
Dec 9, 2022
Dec 9, 2022
618
req.Header.Set(headerAPIVersion, defaultAPIVersion)
619
620
for _, opt := range opts {
621
opt(req)
622
}
623
Aug 21, 2022
Aug 21, 2022
624
return req, nil
625
}
626
Nov 21, 2013
Nov 21, 2013
627
// NewUploadRequest creates an upload request. A relative URL can be provided in
Oct 21, 2013
Oct 21, 2013
628
// urlStr, in which case it is resolved relative to the UploadURL of the Client.
629
// Relative URLs should always be specified without a preceding slash.
Dec 9, 2022
Dec 9, 2022
630
func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption) (*http.Request, error) {
Aug 15, 2017
Aug 15, 2017
631
if !strings.HasSuffix(c.UploadURL.Path, "/") {
Jan 21, 2025
Jan 21, 2025
632
return nil, fmt.Errorf("uploadURL must have a trailing slash, but %q does not", c.UploadURL)
Aug 15, 2017
Aug 15, 2017
633
}
634
u, err := c.UploadURL.Parse(urlStr)
Oct 21, 2013
Oct 21, 2013
635
if err != nil {
636
return nil, err
637
}
638
Feb 28, 2026
Feb 28, 2026
639
requestBody := reader
640
if reader != nil {
641
// Wrap the provided reader so transport code does not observe concrete body types
642
// (for example *os.File) and switch to platform-specific sendfile fast paths.
643
//
644
// Why this exists:
645
// race-enabled test runs on Windows have surfaced data races in the sendfile path
646
// while request read/write loops run concurrently. Hiding concrete type information
647
// keeps uploads on the generic io.Reader copy path, which is race-stable and preserves
648
// request semantics (same bytes, same headers, same content length).
649
requestBody = uploadRequestBodyReader{Reader: reader}
650
}
651
652
req, err := http.NewRequest("POST", u.String(), requestBody)
Oct 21, 2013
Oct 21, 2013
653
if err != nil {
654
return nil, err
655
}
Mar 25, 2022
Mar 25, 2022
656
Dec 2, 2013
Dec 2, 2013
657
req.ContentLength = size
Oct 21, 2013
Oct 21, 2013
658
Jul 19, 2016
Jul 19, 2016
659
if mediaType == "" {
Dec 2, 2013
Dec 2, 2013
660
mediaType = defaultMediaType
661
}
Jul 18, 2016
Jul 18, 2016
662
req.Header.Set("Content-Type", mediaType)
663
req.Header.Set("Accept", mediaTypeV3)
664
req.Header.Set("User-Agent", c.UserAgent)
Dec 9, 2022
Dec 9, 2022
665
req.Header.Set(headerAPIVersion, defaultAPIVersion)
666
667
for _, opt := range opts {
668
opt(req)
669
}
670
Oct 21, 2013
Oct 21, 2013
671
return req, nil
672
}
673
Feb 28, 2026
Feb 28, 2026
674
// uploadRequestBodyReader intentionally wraps an io.Reader to hide concrete reader types.
675
// See NewUploadRequest for why this prevents race-prone transport optimizations.
676
type uploadRequestBodyReader struct {
677
io.Reader
678
}
679
Feb 15, 2017
Feb 15, 2017
680
// Response is a GitHub API response. This wraps the standard http.Response
Aug 1, 2013
Aug 1, 2013
681
// returned from GitHub and provides convenient access to things like
682
// pagination links.
683
type Response struct {
684
*http.Response
685
686
// These fields provide the page values for paginating through a set of
Feb 15, 2017
Feb 15, 2017
687
// results. Any or all of these may be set to the zero value for
Aug 1, 2013
Aug 1, 2013
688
// responses that are not part of a paginated set, or for which there
689
// are no additional pages.
Jan 20, 2020
Jan 20, 2020
690
//
691
// These fields support what is called "offset pagination" and should
692
// be used with the ListOptions struct.
Aug 1, 2013
Aug 1, 2013
693
NextPage int
694
PrevPage int
695
FirstPage int
696
LastPage int
697
Jan 20, 2020
Jan 20, 2020
698
// Additionally, some APIs support "cursor pagination" instead of offset.
699
// This means that a token points directly to the next record which
700
// can lead to O(1) performance compared to O(n) performance provided
701
// by offset pagination.
702
//
703
// For APIs that support cursor pagination (such as
704
// TeamsService.ListIDPGroupsInOrganization), the following field
705
// will be populated to point to the next page.
706
//
707
// To use this token, set ListCursorOptions.Page to this value before
708
// calling the endpoint again.
709
NextPageToken string
710
Aug 16, 2021
Aug 16, 2021
711
// For APIs that support cursor pagination, such as RepositoriesService.ListHookDeliveries,
Jul 13, 2021
Jul 13, 2021
712
// the following field will be populated to point to the next page.
713
// Set ListCursorOptions.Cursor to this value when calling the endpoint again.
714
Cursor string
715
Nov 15, 2021
Nov 15, 2021
716
// For APIs that support before/after pagination, such as OrganizationsService.AuditLog.
717
Before string
718
After string
719
Sep 7, 2018
Sep 7, 2018
720
// Explicitly specify the Rate type so Rate's String() receiver doesn't
721
// propagate to Response.
722
Rate Rate
Aug 6, 2021
Aug 6, 2021
723
Feb 1, 2023
Feb 1, 2023
724
// token's expiration date. Timestamp is 0001-01-01 when token doesn't expire.
725
// So it is valid for TokenExpiration.Equal(Timestamp{}) or TokenExpiration.Time.After(time.Now())
Aug 6, 2021
Aug 6, 2021
726
TokenExpiration Timestamp
Aug 1, 2013
Aug 1, 2013
727
}
728
Jun 5, 2015
Jun 5, 2015
729
// newResponse creates a new Response for the provided http.Response.
Jul 5, 2017
Jul 5, 2017
730
// r must not be nil.
Aug 1, 2013
Aug 1, 2013
731
func newResponse(r *http.Response) *Response {
732
response := &Response{Response: r}
733
response.populatePageValues()
Feb 11, 2016
Feb 11, 2016
734
response.Rate = parseRate(r)
Aug 6, 2021
Aug 6, 2021
735
response.TokenExpiration = parseTokenExpiration(r)
Aug 1, 2013
Aug 1, 2013
736
return response
737
}
738
739
// populatePageValues parses the HTTP Link response headers and populates the
Feb 4, 2016
Feb 4, 2016
740
// various pagination link values in the Response.
Aug 1, 2013
Aug 1, 2013
741
func (r *Response) populatePageValues() {
742
if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
Nov 8, 2025
Nov 8, 2025
743
for link := range strings.SplitSeq(links[0], ",") {
Aug 5, 2013
Aug 5, 2013
744
segments := strings.Split(strings.TrimSpace(link), ";")
Aug 1, 2013
Aug 1, 2013
745
746
// link must at least have href and rel
747
if len(segments) < 2 {
748
continue
749
}
750
751
// ensure href is properly formatted
752
if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
753
continue
754
}
755
756
// try to pull out page parameter
757
url, err := url.Parse(segments[0][1 : len(segments[0])-1])
758
if err != nil {
759
continue
760
}
Jul 13, 2021
Jul 13, 2021
761
762
q := url.Query()
763
764
if cursor := q.Get("cursor"); cursor != "" {
765
for _, segment := range segments[1:] {
Feb 20, 2026
Feb 20, 2026
766
if strings.TrimSpace(segment) == `rel="next"` {
Jul 13, 2021
Jul 13, 2021
767
r.Cursor = cursor
768
}
769
}
770
771
continue
772
}
773
774
page := q.Get("page")
Nov 27, 2021
Nov 27, 2021
775
since := q.Get("since")
Nov 15, 2021
Nov 15, 2021
776
before := q.Get("before")
777
after := q.Get("after")
Nov 27, 2021
Nov 27, 2021
778
779
if page == "" && before == "" && after == "" && since == "" {
Aug 1, 2013
Aug 1, 2013
780
continue
781
}
782
Dec 6, 2021
Dec 6, 2021
783
if since != "" && page == "" {
Nov 27, 2021
Nov 27, 2021
784
page = since
785
}
786
Aug 1, 2013
Aug 1, 2013
787
for _, segment := range segments[1:] {
788
switch strings.TrimSpace(segment) {
789
case `rel="next"`:
Jan 20, 2020
Jan 20, 2020
790
if r.NextPage, err = strconv.Atoi(page); err != nil {
791
r.NextPageToken = page
792
}
Nov 15, 2021
Nov 15, 2021
793
r.After = after
Aug 1, 2013
Aug 1, 2013
794
case `rel="prev"`:
795
r.PrevPage, _ = strconv.Atoi(page)
Nov 15, 2021
Nov 15, 2021
796
r.Before = before
Aug 1, 2013
Aug 1, 2013
797
case `rel="first"`:
798
r.FirstPage, _ = strconv.Atoi(page)
799
case `rel="last"`:
800
r.LastPage, _ = strconv.Atoi(page)
801
}
802
}
803
}
804
}
805
}
806
Feb 11, 2016
Feb 11, 2016
807
// parseRate parses the rate related headers.
808
func parseRate(r *http.Response) Rate {
809
var rate Rate
Feb 4, 2026
Feb 4, 2026
810
if limit := r.Header.Get(HeaderRateLimit); limit != "" {
Feb 11, 2016
Feb 11, 2016
811
rate.Limit, _ = strconv.Atoi(limit)
Aug 1, 2013
Aug 1, 2013
812
}
Feb 4, 2026
Feb 4, 2026
813
if remaining := r.Header.Get(HeaderRateRemaining); remaining != "" {
Feb 11, 2016
Feb 11, 2016
814
rate.Remaining, _ = strconv.Atoi(remaining)
Aug 1, 2013
Aug 1, 2013
815
}
Feb 4, 2026
Feb 4, 2026
816
if used := r.Header.Get(HeaderRateUsed); used != "" {
Jan 27, 2025
Jan 27, 2025
817
rate.Used, _ = strconv.Atoi(used)
818
}
Feb 4, 2026
Feb 4, 2026
819
if reset := r.Header.Get(HeaderRateReset); reset != "" {
Aug 1, 2013
Aug 1, 2013
820
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
Feb 11, 2016
Feb 11, 2016
821
rate.Reset = Timestamp{time.Unix(v, 0)}
Aug 1, 2013
Aug 1, 2013
822
}
823
}
Feb 4, 2026
Feb 4, 2026
824
if resource := r.Header.Get(HeaderRateResource); resource != "" {
Jan 27, 2025
Jan 27, 2025
825
rate.Resource = resource
826
}
Feb 11, 2016
Feb 11, 2016
827
return rate
Aug 1, 2013
Aug 1, 2013
828
}
829
May 14, 2023
May 14, 2023
830
// parseSecondaryRate parses the secondary rate related headers,
831
// and returns the time to retry after.
832
func parseSecondaryRate(r *http.Response) *time.Duration {
833
// According to GitHub support, the "Retry-After" header value will be
834
// an integer which represents the number of seconds that one should
835
// wait before resuming making requests.
836
if v := r.Header.Get(headerRetryAfter); v != "" {
837
retryAfterSeconds, _ := strconv.ParseInt(v, 10, 64) // Error handling is noop.
838
retryAfter := time.Duration(retryAfterSeconds) * time.Second
839
return &retryAfter
840
}
841
842
// According to GitHub support, endpoints might return x-ratelimit-reset instead,
843
// as an integer which represents the number of seconds since epoch UTC,
Sep 24, 2024
Sep 24, 2024
844
// representing the time to resume making requests.
Feb 4, 2026
Feb 4, 2026
845
if v := r.Header.Get(HeaderRateReset); v != "" {
May 14, 2023
May 14, 2023
846
secondsSinceEpoch, _ := strconv.ParseInt(v, 10, 64) // Error handling is noop.
847
retryAfter := time.Until(time.Unix(secondsSinceEpoch, 0))
848
return &retryAfter
849
}
850
851
return nil
852
}
853
Aug 6, 2021
Aug 6, 2021
854
// parseTokenExpiration parses the TokenExpiration related headers.
Feb 1, 2023
Feb 1, 2023
855
// Returns 0001-01-01 if the header is not defined or could not be parsed.
Aug 6, 2021
Aug 6, 2021
856
func parseTokenExpiration(r *http.Response) Timestamp {
857
if v := r.Header.Get(headerTokenExpiration); v != "" {
Feb 1, 2023
Feb 1, 2023
858
if t, err := time.Parse("2006-01-02 15:04:05 MST", v); err == nil {
859
return Timestamp{t.Local()}
860
}
861
// Some tokens include the timezone offset instead of the timezone.
862
// https://github.com/google/go-github/issues/2649
863
if t, err := time.Parse("2006-01-02 15:04:05 -0700", v); err == nil {
864
return Timestamp{t.Local()}
Aug 6, 2021
Aug 6, 2021
865
}
866
}
Feb 1, 2023
Feb 1, 2023
867
return Timestamp{} // 0001-01-01 00:00:00
Aug 6, 2021
Aug 6, 2021
868
}
869
Jul 8, 2021
Jul 8, 2021
870
type requestContext uint8
871
872
const (
Jan 7, 2025
Jan 7, 2025
873
// BypassRateLimitCheck prevents a pre-emptive check for exceeded primary rate limits
874
// Specify this by providing a context with this key, e.g.
875
// context.WithValue(context.Background(), github.BypassRateLimitCheck, true)
876
BypassRateLimitCheck requestContext = iota
877
May 1, 2024
May 1, 2024
878
SleepUntilPrimaryRateLimitResetWhenRateLimited
Jul 8, 2021
Jul 8, 2021
879
)
880
Jan 2, 2025
Jan 2, 2025
881
// bareDo sends an API request using `caller` http.Client passed in the parameters
882
// and lets you handle the api response. If an error or API Error occurs, the error
Oct 27, 2025
Oct 27, 2025
883
// will contain more information. Otherwise, you are supposed to read and close the
Jan 2, 2025
Jan 2, 2025
884
// response's Body. If rate limit is exceeded and reset time is in the future,
885
// bareDo returns *RateLimitError immediately without making a network API call.
Feb 20, 2017
Feb 20, 2017
886
//
Jan 13, 2021
Jan 13, 2021
887
// The provided ctx must be non-nil, if it is nil an error is returned. If it is
888
// canceled or times out, ctx.Err() will be returned.
Jan 2, 2025
Jan 2, 2025
889
func (c *Client) bareDo(ctx context.Context, caller *http.Client, req *http.Request) (*Response, error) {
Dec 9, 2019
Dec 9, 2019
890
if ctx == nil {
Apr 14, 2021
Apr 14, 2021
891
return nil, errNonNilContext
Dec 9, 2019
Dec 9, 2019
892
}
Mar 25, 2022
Mar 25, 2022
893
Aug 16, 2017
Aug 16, 2017
894
req = withContext(ctx, req)
Feb 20, 2017
Feb 20, 2017
895
Jun 30, 2025
Jun 30, 2025
896
rateLimitCategory := CoreCategory
May 3, 2016
May 3, 2016
897
Jun 30, 2025
Jun 30, 2025
898
if !c.DisableRateLimitCheck {
899
rateLimitCategory = GetRateLimitCategory(req.Method, req.URL.Path)
900
901
if bypass := ctx.Value(BypassRateLimitCheck); bypass == nil {
902
// If we've hit rate limit, don't make further requests before Reset time.
903
if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil {
904
return &Response{
905
Response: err.Response,
906
Rate: err.Rate,
907
}, err
908
}
909
910
// If we've hit a secondary rate limit, don't make further requests before Retry After.
911
if err := c.checkSecondaryRateLimitBeforeDo(req); err != nil {
912
return &Response{
913
Response: err.Response,
914
}, err
915
}
Jan 30, 2023
Jan 30, 2023
916
}
May 3, 2016
May 3, 2016
917
}
918
Jan 2, 2025
Jan 2, 2025
919
resp, err := caller.Do(req)
Dec 8, 2024
Dec 8, 2024
920
var response *Response
921
if resp != nil {
922
response = newResponse(resp)
923
}
924
May 24, 2013
May 24, 2013
925
if err != nil {
Feb 20, 2017
Feb 20, 2017
926
// If we got an error, and the context has been canceled,
927
// the context's error is probably more useful.
928
select {
929
case <-ctx.Done():
Dec 8, 2024
Dec 8, 2024
930
return response, ctx.Err()
Feb 20, 2017
Feb 20, 2017
931
default:
932
}
933
934
// If the error type is *url.Error, sanitize its URL before returning.
Sep 30, 2025
Sep 30, 2025
935
var e *url.Error
936
if errors.As(err, &e) {
Jan 19, 2017
Jan 19, 2017
937
if url, err := url.Parse(e.URL); err == nil {
938
e.URL = sanitizeURL(url).String()
Dec 8, 2024
Dec 8, 2024
939
return response, e
Jan 19, 2017
Jan 19, 2017
940
}
941
}
Feb 20, 2017
Feb 20, 2017
942
Dec 8, 2024
Dec 8, 2024
943
return response, err
May 24, 2013
May 24, 2013
944
}
Aug 11, 2020
Aug 11, 2020
945
Jun 30, 2025
Jun 30, 2025
946
// Don't update the rate limits if the client has rate limits disabled or if
947
// this was a cached response. The X-From-Cache is set by
948
// https://github.com/bartventer/httpcache if it's enabled.
949
if !c.DisableRateLimitCheck && response.Header.Get("X-From-Cache") == "" {
Feb 17, 2022
Feb 17, 2022
950
c.rateMu.Lock()
951
c.rateLimits[rateLimitCategory] = response.Rate
952
c.rateMu.Unlock()
953
}
Jun 27, 2013
Jun 27, 2013
954
May 24, 2013
May 24, 2013
955
err = CheckResponse(resp)
956
if err != nil {
Jan 13, 2021
Jan 13, 2021
957
defer resp.Body.Close()
Oct 3, 2018
Oct 3, 2018
958
// Special case for AcceptedErrors. If an AcceptedError
959
// has been encountered, the response's payload will be
960
// added to the AcceptedError and returned.
961
//
962
// Issue #1022
Sep 30, 2025
Sep 30, 2025
963
var aerr *AcceptedError
964
if errors.As(err, &aerr) {
Nov 7, 2022
Nov 7, 2022
965
b, readErr := io.ReadAll(resp.Body)
Oct 3, 2018
Oct 3, 2018
966
if readErr != nil {
967
return response, readErr
968
}
969
970
aerr.Raw = b
Jan 13, 2021
Jan 13, 2021
971
err = aerr
Apr 3, 2018
Apr 3, 2018
972
}
Jan 30, 2023
Jan 30, 2023
973
Sep 30, 2025
Sep 30, 2025
974
var rateLimitError *RateLimitError
975
if errors.As(err, &rateLimitError) &&
976
req.Context().Value(SleepUntilPrimaryRateLimitResetWhenRateLimited) != nil {
May 1, 2024
May 1, 2024
977
if err := sleepUntilResetWithBuffer(req.Context(), rateLimitError.Rate.Reset.Time); err != nil {
978
return response, err
979
}
980
// retry the request once when the rate limit has reset
Jan 2, 2025
Jan 2, 2025
981
return c.bareDo(context.WithValue(req.Context(), SleepUntilPrimaryRateLimitResetWhenRateLimited, nil), caller, req)
May 1, 2024
May 1, 2024
982
}
983
Jan 30, 2023
Jan 30, 2023
984
// Update the secondary rate limit if we hit it.
Sep 30, 2025
Sep 30, 2025
985
var rerr *AbuseRateLimitError
986
if errors.As(err, &rerr) && rerr.RetryAfter != nil {
Feb 5, 2025
Feb 5, 2025
987
// if a max duration is specified, make sure that we are waiting at most this duration
988
if c.MaxSecondaryRateLimitRetryAfterDuration > 0 && rerr.GetRetryAfter() > c.MaxSecondaryRateLimitRetryAfterDuration {
989
rerr.RetryAfter = &c.MaxSecondaryRateLimitRetryAfterDuration
990
}
Jan 30, 2023
Jan 30, 2023
991
c.rateMu.Lock()
992
c.secondaryRateLimitReset = time.Now().Add(*rerr.RetryAfter)
993
c.rateMu.Unlock()
994
}
Jan 13, 2021
Jan 13, 2021
995
}
996
return response, err
997
}
Oct 3, 2018
Oct 3, 2018
998
Jan 2, 2025
Jan 2, 2025
999
// BareDo sends an API request and lets you handle the api response. If an error
Oct 27, 2025
Oct 27, 2025
1000
// or API Error occurs, the error will contain more information. Otherwise, you