-
Notifications
You must be signed in to change notification settings - Fork 12
feat: Add support for Github Enterprise #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,10 @@ inputs: | |||||||||||||
| description: 'The owner and repository name. For example `octocat/Hello-World`' | ||||||||||||||
| required: true | ||||||||||||||
| default: '${{ github.repository }}' | ||||||||||||||
| github-api-url: | ||||||||||||||
| description: 'GitHub API base URL. Set for GitHub Enterprise, e.g. https://ghe.example.com/api/v3. Defaults to the public GitHub API.' | ||||||||||||||
| required: false | ||||||||||||||
| default: '' | ||||||||||||||
|
Comment on lines
+19
to
+21
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The runner always exports the instance's API URL, so defaulting to it makes GHES work with zero config — and avoids the footgun where someone forgets this input and the action silently talks to public
Suggested change
|
||||||||||||||
| verbose: | ||||||||||||||
| description: 'Print debug info' | ||||||||||||||
| required: false | ||||||||||||||
|
|
@@ -111,6 +115,7 @@ runs: | |||||||||||||
| INPUT_GITHUB-TOKEN: ${{ inputs.github-token }} | ||||||||||||||
| INPUT_PR: ${{ inputs.pr }} | ||||||||||||||
| INPUT_REPOSITORY: ${{ inputs.repository }} | ||||||||||||||
| INPUT_GITHUB-API-URL: ${{ inputs.github-api-url }} | ||||||||||||||
| INPUT_VERBOSE: ${{ inputs.verbose }} | ||||||||||||||
| INPUT_QUIET: ${{ inputs.quiet }} | ||||||||||||||
| BIN: ${{ steps.resolve.outputs.bin }} | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,8 +69,15 @@ type GHClient struct { | |
| infoBuffer io.Writer | ||
| } | ||
|
|
||
| func NewClient(owner, repo, token string) (Client, error) { | ||
| client, err := github.NewClient(github.WithAuthToken(token)) | ||
| func NewClient(owner, repo, token, apiUrl string) (Client, error) { | ||
| opts := []github.ClientOptionsFunc{github.WithAuthToken(token)} | ||
| if apiUrl != "" { | ||
| // WithEnterpriseURLs appends api/uploads/ to the upload URL, so strip | ||
| // any api/v3 suffix to avoid ending up with api/v3/api/uploads/. | ||
| uploadUrl := strings.TrimSuffix(strings.TrimSuffix(apiUrl, "/"), "/api/v3") | ||
| opts = append(opts, github.WithEnterpriseURLs(apiUrl, uploadUrl)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When a host-only GitHub Enterprise Server URL begins with Knowledge Base Used: GitHub API integration |
||
| } | ||
|
Comment on lines
+74
to
+79
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This works for the common cases, but apiUrl = strings.TrimSpace(apiUrl)
if apiUrl != "" {
base, err := url.Parse(strings.TrimRight(apiUrl, "/") + "/")
if err != nil {
return nil, fmt.Errorf("invalid api url %q: %w", apiUrl, err)
}
if (base.Scheme != "http" && base.Scheme != "https") || base.Host == "" {
return nil, fmt.Errorf("api url %q must be an absolute http(s) URL", apiUrl)
}
client.BaseURL = base
}(after the plain Bonus: this trims stray whitespace and catches schemeless typos like |
||
| client, err := github.NewClient(opts...) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -322,7 +322,7 @@ func TestIsSubstringInComments(t *testing.T) { | |
| } | ||
|
|
||
| func TestNewGithubClient(t *testing.T) { | ||
| c, err := NewClient("owner", "repo", "token") | ||
| c, err := NewClient("owner", "repo", "token", "") | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
|
|
@@ -345,6 +345,77 @@ func TestNewGithubClient(t *testing.T) { | |
| if client.userReviewerMap != nil { | ||
| t.Error("Expected userReviewerMap to be nil") | ||
| } | ||
| if got := client.client.BaseURL(); got != "https://api.github.com/" { | ||
| t.Errorf("Expected base URL to be https://api.github.com/, got %s", got) | ||
| } | ||
|
Comment on lines
+348
to
+350
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit (optional): this duplicates the |
||
| } | ||
|
|
||
| func TestNewGithubClientApiUrl(t *testing.T) { | ||
| tt := []struct { | ||
| name string | ||
| apiUrl string | ||
| expectedBase string | ||
| expectedUpload string | ||
| expectError bool | ||
| }{ | ||
| { | ||
| name: "empty uses public GitHub", | ||
| apiUrl: "", | ||
| expectedBase: "https://api.github.com/", | ||
| expectedUpload: "https://uploads.github.com/", | ||
| }, | ||
| { | ||
| name: "enterprise host", | ||
| apiUrl: "https://ghe.example.com", | ||
| expectedBase: "https://ghe.example.com/api/v3/", | ||
| expectedUpload: "https://ghe.example.com/api/uploads/", | ||
| }, | ||
| { | ||
| name: "enterprise host with api/v3", | ||
| apiUrl: "https://ghe.example.com/api/v3", | ||
| expectedBase: "https://ghe.example.com/api/v3/", | ||
| expectedUpload: "https://ghe.example.com/api/uploads/", | ||
| }, | ||
| { | ||
| name: "enterprise host with api/v3 and trailing slash", | ||
| apiUrl: "https://ghe.example.com/api/v3/", | ||
| expectedBase: "https://ghe.example.com/api/v3/", | ||
| expectedUpload: "https://ghe.example.com/api/uploads/", | ||
| }, | ||
| { | ||
| name: "enterprise cloud with data residency", | ||
| apiUrl: "https://api.acme.ghe.com", | ||
| expectedBase: "https://api.acme.ghe.com/", | ||
| expectedUpload: "https://api.acme.ghe.com/", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tiny note: GitHub's actual upload host for data residency is |
||
| }, | ||
| { | ||
| name: "invalid URL", | ||
| apiUrl: "://bad", | ||
| expectError: true, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range tt { | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| c, err := NewClient("owner", "repo", "token", tc.apiUrl) | ||
| if tc.expectError { | ||
| if err == nil { | ||
| t.Fatal("expected error but got none") | ||
| } | ||
| return | ||
| } | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
| gh := c.(*GHClient) | ||
| if got := gh.client.BaseURL(); got != tc.expectedBase { | ||
| t.Errorf("Expected base URL to be %s, got %s", tc.expectedBase, got) | ||
| } | ||
| if got := gh.client.UploadURL(); got != tc.expectedUpload { | ||
| t.Errorf("Expected upload URL to be %s, got %s", tc.expectedUpload, got) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestNilPRErr(t *testing.T) { | ||
|
|
@@ -1464,7 +1535,7 @@ func TestContainsValidBypassApproval(t *testing.T) { | |
| } | ||
|
|
||
| func TestContainsValidBypassApprovalNoPR(t *testing.T) { | ||
| c, err := NewClient("test-owner", "test-repo", "test-token") | ||
| c, err := NewClient("test-owner", "test-repo", "test-token", "") | ||
| if err != nil { | ||
| t.Fatalf("unexpected error: %v", err) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small correction: the suffix is only optional for some hostnames — go-github skips appending
/api/v3when the host starts withapi., so e.g. a GHES instance atapi.corp.example.comwould break without it. Simplest guidance that's always right: