55 "fmt"
66 "net/http"
77 "net/url"
8- "path"
98 "regexp"
109 "strings"
1110 "time"
@@ -19,7 +18,8 @@ import (
1918type Config struct {
2019 Token string
2120 Owner string
22- BaseURL string
21+ BaseURL * url.URL
22+ IsGHES bool
2323 Insecure bool
2424 WriteDelay time.Duration
2525 ReadDelay time.Duration
@@ -38,12 +38,23 @@ type Owner struct {
3838 IsOrganization bool
3939}
4040
41- // DotComHost is the hostname for GitHub.com API.
42- const DotComHost = "api.github.com"
41+ const (
42+ // DotComAPIURL is the base API URL for github.com.
43+ DotComAPIURL = "https://api.github.com/"
44+ // DotComHost is the hostname for github.com.
45+ DotComHost = "github.com"
46+ // DotComAPIHost is the API hostname for github.com.
47+ DotComAPIHost = "api.github.com"
48+ // GHESRESTAPISuffix is the rest api suffix for GitHub Enterprise Server.
49+ GHESRESTAPIPath = "api/v3/"
50+ )
4351
44- // GHECDataResidencyHostMatch is a regex to match a GitHub Enterprise Cloud data residency host:
45- // https://[hostname].ghe.com/ instances expect paths that behave similar to GitHub.com, not GitHub Enterprise Server.
46- var GHECDataResidencyHostMatch = regexp .MustCompile (`^[a-zA-Z0-9.\-]+\.ghe\.com\/?$` )
52+ var (
53+ // GHECHostMatch is a regex to match GitHub Enterprise Cloud hosts.
54+ GHECHostMatch = regexp .MustCompile (`\.ghe\.com$` )
55+ // GHECAPIHostMatch is a regex to match GitHub Enterprise Cloud API hosts.
56+ GHECAPIHostMatch = regexp .MustCompile (`^api\.[a-zA-Z0-9-]+\.ghe\.com$` )
57+ )
4758
4859func RateLimitedHTTPClient (client * http.Client , writeDelay , readDelay , retryDelay time.Duration , parallelRequests bool , retryableErrors map [int ]bool , maxRetries int ) * http.Client {
4960 client .Transport = NewEtagTransport (client .Transport )
@@ -81,38 +92,24 @@ func (c *Config) AnonymousHTTPClient() *http.Client {
8192}
8293
8394func (c * Config ) NewGraphQLClient (client * http.Client ) (* githubv4.Client , error ) {
84- uv4 , err := url .Parse (c .BaseURL )
85- if err != nil {
86- return nil , err
87- }
88-
89- hostname := uv4 .Hostname ()
90- if hostname != DotComHost && ! GHECDataResidencyHostMatch .MatchString (hostname ) {
91- uv4 .Path = path .Join (uv4 .Path , "api/graphql/" )
95+ var path string
96+ if c .IsGHES {
97+ path = "api/graphql"
9298 } else {
93- uv4 . Path = path . Join ( uv4 . Path , "graphql" )
99+ path = "graphql"
94100 }
95101
96- return githubv4 .NewEnterpriseClient (uv4 .String (), client ), nil
102+ return githubv4 .NewEnterpriseClient (c . BaseURL . JoinPath ( path ) .String (), client ), nil
97103}
98104
99105func (c * Config ) NewRESTClient (client * http.Client ) (* github.Client , error ) {
100- uv3 , err := url . Parse ( c . BaseURL )
101- if err != nil {
102- return nil , err
106+ path := ""
107+ if c . IsGHES {
108+ path = GHESRESTAPIPath
103109 }
104110
105- hostname := uv3 .Hostname ()
106- if hostname != DotComHost && ! GHECDataResidencyHostMatch .MatchString (hostname ) {
107- uv3 .Path = fmt .Sprintf ("%s/" , path .Join (uv3 .Path , "api/v3" ))
108- }
109-
110- v3client , err := github .NewClient (client ).WithEnterpriseURLs (uv3 .String (), "" )
111- if err != nil {
112- return nil , err
113- }
114-
115- v3client .BaseURL = uv3
111+ v3client := github .NewClient (client )
112+ v3client .BaseURL = c .BaseURL .JoinPath (path )
116113
117114 return v3client , nil
118115}
@@ -199,3 +196,45 @@ func (injector *previewHeaderInjectorTransport) RoundTrip(req *http.Request) (*h
199196 }
200197 return injector .rt .RoundTrip (req )
201198}
199+
200+ // getBaseURL returns a correctly configured base URL and a bool as to if this is GitHub Enterprise Server.
201+ func getBaseURL (s string ) (* url.URL , bool , error ) {
202+ if len (s ) == 0 {
203+ s = DotComAPIURL
204+ }
205+
206+ u , err := url .Parse (s )
207+ if err != nil {
208+ return nil , false , err
209+ }
210+
211+ if ! u .IsAbs () {
212+ return nil , false , fmt .Errorf ("base url must be absolute" )
213+ }
214+
215+ u = u .JoinPath ("/" )
216+
217+ switch {
218+ case u .Host == DotComAPIHost :
219+ case u .Host == DotComHost :
220+ u .Host = DotComAPIHost
221+ case GHECAPIHostMatch .MatchString (u .Host ):
222+ case GHECHostMatch .MatchString (u .Host ):
223+ u .Host = fmt .Sprintf ("api.%s" , u .Host )
224+ default :
225+ u .Path = strings .TrimSuffix (u .Path , GHESRESTAPIPath )
226+ return u , true , nil
227+ }
228+
229+ if u .Scheme != "https" {
230+ return nil , false , fmt .Errorf ("base url for github.com or ghe.com must use the https scheme" )
231+ }
232+
233+ if len (u .Path ) > 1 {
234+ return nil , false , fmt .Errorf ("base url for github.com or ghe.com must not contain a path, got %s" , u .Path )
235+ }
236+
237+ u .Path = "/"
238+
239+ return u , false , nil
240+ }
0 commit comments