Skip to content

Commit c99f24e

Browse files
Add support for GitHub Enterprise Cloud Data residency (#2547)
* Add support for GitHub Enterprise Cloud with Data residency * Fix broken build * Add test for GHECDataResidencyMatch --------- Co-authored-by: Keegan Campbell <[email protected]>
1 parent e5f0c4c commit c99f24e

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

github/apps.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func GenerateOAuthTokenFromApp(baseURL, appID, appInstallationID, pemData string
3131
}
3232

3333
func getInstallationAccessToken(baseURL string, jwt string, installationID string) (string, error) {
34-
if baseURL != "https://api.github.com/" {
34+
if baseURL != "https://api.github.com/" && !GHECDataResidencyMatch.MatchString(baseURL) {
3535
baseURL += "api/v3/"
3636
}
3737

github/config.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66
"net/url"
77
"path"
8+
"regexp"
89
"strings"
910
"time"
1011

@@ -36,6 +37,10 @@ type Owner struct {
3637
IsOrganization bool
3738
}
3839

40+
// GHECDataResidencyMatch is a regex to match a GitHub Enterprise Cloud data residency URL:
41+
// https://[hostname].ghe.com instances expect paths that behave similar to GitHub.com, not GitHub Enterprise Server.
42+
var GHECDataResidencyMatch = regexp.MustCompile(`^https:\/\/[a-zA-Z0-9.\-]*\.ghe\.com$`)
43+
3944
func RateLimitedHTTPClient(client *http.Client, writeDelay time.Duration, readDelay time.Duration, retryDelay time.Duration, parallelRequests bool, retryableErrors map[int]bool, maxRetries int) *http.Client {
4045

4146
client.Transport = NewEtagTransport(client.Transport)
@@ -80,7 +85,7 @@ func (c *Config) NewGraphQLClient(client *http.Client) (*githubv4.Client, error)
8085
return nil, err
8186
}
8287

83-
if uv4.String() != "https://api.github.com/" {
88+
if uv4.String() != "https://api.github.com/" && !GHECDataResidencyMatch.MatchString(uv4.String()) {
8489
uv4.Path = path.Join(uv4.Path, "api/graphql/")
8590
} else {
8691
uv4.Path = path.Join(uv4.Path, "graphql")
@@ -96,7 +101,7 @@ func (c *Config) NewRESTClient(client *http.Client) (*github.Client, error) {
96101
return nil, err
97102
}
98103

99-
if uv3.String() != "https://api.github.com/" {
104+
if uv3.String() != "https://api.github.com/" && !GHECDataResidencyMatch.MatchString(uv3.String()) {
100105
uv3.Path = uv3.Path + "api/v3/"
101106
}
102107

github/config_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,64 @@ import (
77
"github.com/shurcooL/githubv4"
88
)
99

10+
func TestGHECDataResidencyMatch(t *testing.T) {
11+
testCases := []struct {
12+
url string
13+
matches bool
14+
description string
15+
}{
16+
{
17+
url: "https://customer.ghe.com",
18+
matches: true,
19+
description: "GHEC data residency URL with customer name",
20+
},
21+
{
22+
url: "https://customer-name.ghe.com",
23+
matches: true,
24+
description: "GHEC data residency URL with hyphenated name",
25+
},
26+
{
27+
url: "https://api.github.com",
28+
matches: false,
29+
description: "GitHub.com API URL",
30+
},
31+
{
32+
url: "https://github.com",
33+
matches: false,
34+
description: "GitHub.com URL",
35+
},
36+
{
37+
url: "https://example.com",
38+
matches: false,
39+
description: "Generic URL",
40+
},
41+
{
42+
url: "http://customer.ghe.com",
43+
matches: false,
44+
description: "Non-HTTPS GHEC URL",
45+
},
46+
{
47+
url: "https://customer.ghe.com/api/v3",
48+
matches: false,
49+
description: "GHEC URL with path",
50+
},
51+
{
52+
url: "https://ghe.com",
53+
matches: false,
54+
description: "GHEC domain without subdomain",
55+
},
56+
}
57+
58+
for _, tc := range testCases {
59+
t.Run(tc.description, func(t *testing.T) {
60+
matches := GHECDataResidencyMatch.MatchString(tc.url)
61+
if matches != tc.matches {
62+
t.Errorf("URL %q: expected match=%v, got %v", tc.url, tc.matches, matches)
63+
}
64+
})
65+
}
66+
}
67+
1068
func TestAccConfigMeta(t *testing.T) {
1169

1270
// FIXME: Skip test runs during travis lint checking

0 commit comments

Comments
 (0)