Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@ const (
HeaderRequestID = "X-Github-Request-Id"

// https://docs.github.com/en/rest/about-the-rest-api/api-versions#about-api-versioning
defaultAPIVersion = api20221128
latestAPIVersion = api20260310
api20221128 = "2022-11-28"
api20260310 = "2026-03-10"
api20221128 = "2022-11-28"
api20260310 = "2026-03-10"

defaultBaseURL = "https://api.github.com/"
defaultUserAgent = "go-github" + "/" + Version
Expand Down Expand Up @@ -178,6 +176,13 @@ type Client struct {
// Base URL for uploading files.
uploadURL *url.URL

// Default API version to set in the X-Github-Api-Version header.
apiVersionDefault string
// Minimum API version that the client can use.
apiVersionMin string
// Maximum API version that the client can use.
apiVersionMax string

// User agent used when communicating with the GitHub API.
userAgent string

Expand Down Expand Up @@ -347,6 +352,8 @@ type clientOptions struct {
httpClient *http.Client
transport http.RoundTripper
timeout *time.Duration
apiVersionMin *string
apiVersionMax *string
userAgent *string
envProxy bool
token *string
Expand Down Expand Up @@ -558,7 +565,11 @@ func NewClient(opts ...ClientOptionsFunc) (*Client, error) {
// newClient creates a new Client with the provided options. This is an internal
// helper function that is called by [NewClient] and [Client.Clone].
func newClient(opts clientOptions) (*Client, error) {
c := &Client{}
c := &Client{
apiVersionDefault: api20221128,
apiVersionMin: api20221128,
apiVersionMax: api20260310,
}

if opts.httpClient != nil {
c.client = opts.httpClient
Expand Down Expand Up @@ -609,6 +620,14 @@ func newClient(opts clientOptions) (*Client, error) {
CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse },
}

if opts.apiVersionMin != nil {
c.apiVersionMin = *opts.apiVersionMin
}

if opts.apiVersionMax != nil {
c.apiVersionMax = *opts.apiVersionMax
}

if opts.userAgent != nil {
c.userAgent = *opts.userAgent
} else {
Expand Down Expand Up @@ -718,6 +737,8 @@ func (c *Client) Clone(opts ...ClientOptionsFunc) (*Client, error) {
}

o := clientOptions{
apiVersionMin: &c.apiVersionMin,
apiVersionMax: &c.apiVersionMax,
userAgent: &c.userAgent,
baseURL: Ptr(*c.baseURL),
uploadURL: Ptr(*c.uploadURL),
Expand Down Expand Up @@ -814,7 +835,7 @@ func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body any
if c.userAgent != "" {
req.Header.Set("User-Agent", c.userAgent)
}
req.Header.Set(headerAPIVersion, defaultAPIVersion)
req.Header.Set(headerAPIVersion, c.apiVersionDefault)

for _, opt := range opts {
opt(req)
Expand Down Expand Up @@ -851,7 +872,7 @@ func (c *Client) NewFormRequest(ctx context.Context, urlStr string, body io.Read
if c.userAgent != "" {
req.Header.Set("User-Agent", c.userAgent)
}
req.Header.Set(headerAPIVersion, defaultAPIVersion)
req.Header.Set(headerAPIVersion, c.apiVersionDefault)

for _, opt := range opts {
opt(req)
Expand Down Expand Up @@ -922,7 +943,7 @@ func (c *Client) NewUploadRequest(ctx context.Context, urlStr string, reader io.
req.Header.Set("Content-Type", mediaType)
req.Header.Set("Accept", mediaTypeV3)
req.Header.Set("User-Agent", c.userAgent)
req.Header.Set(headerAPIVersion, defaultAPIVersion)
req.Header.Set(headerAPIVersion, c.apiVersionDefault)

for _, opt := range opts {
opt(req)
Expand Down Expand Up @@ -1143,6 +1164,28 @@ const (
// unexpectedly large error body.
const maxErrorBodySize = 1 * 1024 * 1024 // 1 MiB

// ErrUnsupportedAPIVersion is returned when the API version specified in the
// request is not supported by the client.
var ErrUnsupportedAPIVersion = errors.New("unsupported api version")
Comment thread
stevehipwell marked this conversation as resolved.

// checkRequestAPIVersionBeforeDo checks if the API version specified in the
// request is supported by the client before making the API call. If the
// version is not supported, it returns [ErrUnsupportedAPIVersion]. If the
// version is empty it returns nil.
func (c *Client) checkRequestAPIVersionBeforeDo(req *http.Request) error {
reqAPIVersion := req.Header.Get(headerAPIVersion)

if reqAPIVersion == "" {
return nil
}

if reqAPIVersion < c.apiVersionMin || reqAPIVersion > c.apiVersionMax {
return ErrUnsupportedAPIVersion
}

return nil
}

// bareDo sends an API request using `caller` http.Client passed in the parameters
// and lets you handle the api response. If an error or API Error occurs, the error
// will contain more information. Otherwise, you are supposed to read and close the
Expand All @@ -1151,6 +1194,10 @@ const maxErrorBodySize = 1 * 1024 * 1024 // 1 MiB
func (c *Client) bareDo(caller *http.Client, req *http.Request) (*Response, error) {
ctx := req.Context()

if err := c.checkRequestAPIVersionBeforeDo(req); err != nil {
return nil, err
}

rateLimitCategory := CoreCategory

if !c.disableRateLimitCheck {
Expand Down
Loading
Loading