Skip to content

feat: Add support for Github Enterprise - #199

Open
darshanparajuli wants to merge 1 commit into
multimediallc:mainfrom
darshanparajuli:darshan/github-api-base-url-option
Open

feat: Add support for Github Enterprise#199
darshanparajuli wants to merge 1 commit into
multimediallc:mainfrom
darshanparajuli:darshan/github-api-base-url-option

Conversation

@darshanparajuli

Copy link
Copy Markdown

Summary / Background

My team is looking into using codeowners-plus in our github enterprise repo. I noticed that codeowners-plus internally uses public GH apis, and this change adds an option to set a different base api url for Github so that it can be used in github enterprise environment as well.

@darshanparajuli darshanparajuli changed the title Add an option for customizing base github api url in order to support GH enterprise api calls Add support for Github Enterprise Aug 26, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for GitHub Enterprise instances by adding a new github-api-url configuration option. This option allows users to specify a custom GitHub API base URL, which is then correctly parsed and applied to the GitHub client, with comprehensive unit tests added to verify various URL formats. There are no review comments, and I have no additional feedback to provide.

@darshanparajuli darshanparajuli changed the title Add support for Github Enterprise feat: Add support for Github Enterprise Aug 26, 2026
@darshanparajuli
darshanparajuli marked this pull request as ready for review August 26, 2026 16:57
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

This PR should not merge until host-only GitHub Enterprise Server endpoints beginning with api. are normalized to /api/v3/.

The new endpoint flow is internally consistent, but a realistic Enterprise hostname shape causes every REST request to target the host root, preventing pull-request initialization and all subsequent processing.

Files Needing Attention: internal/github/gh.go and internal/github/gh_test.go

Important Files Changed

Filename Overview
action.yml Adds the optional Enterprise API input and consistently exports it to the executable.
main.go Adds the CLI/environment endpoint flag and propagates it into application configuration.
internal/app/app.go Carries the configured endpoint into GitHub client construction.
internal/github/gh.go Configures Enterprise URLs, but host-only GHES names beginning with api. can resolve to the wrong REST root.
internal/github/gh_test.go Covers common GHES and data-residency URL forms but omits the failing api.-prefixed GHES hostname case.
README.md Documents host-only and suffixed Enterprise endpoints, including the input form affected by the normalization edge case.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[github-api-url action input] --> B[INPUT_GITHUB-API-URL]
  B --> C[api-url flag]
  C --> D[app.Config.ApiUrl]
  D --> E[gh.NewClient]
  E --> F[go-github BaseURL]
  F --> G[GitHub Enterprise API requests]
Loading

Reviews (1): Last reviewed commit: "Add an option for customizing base githu..." | Re-trigger Greptile

Comment thread internal/github/gh.go
// 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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Broken GHES URL normalization

When a host-only GitHub Enterprise Server URL begins with api., passing it directly to WithEnterpriseURLs prevents /api/v3/ from being appended, causing InitPR and subsequent REST operations to target the host root and fail.

Knowledge Base Used: GitHub API integration

@BakerNet BakerNet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for the contribution, @darshanparajuli — Enterprise support is a great addition, and the test table is much appreciated! 🎉

The implementation works well for the common cases; just a few changes requested, mostly around making the URL handling foolproof (details inline):

  1. Default github-api-url to ${{ github.api_url }} so GHES works out of the box — and a forgotten input can't silently send an enterprise token to public GitHub.
  2. With that default, NewClient can use the URL verbatim instead of relying on go-github's hostname heuristics.
  3. Align the README / action.yml guidance on the /api/v3 suffix (it's only optional for some hostnames).

One note that can't be inline since the file isn't in the diff: scripts/install-action.sh still downloads release binaries from public github.com, so tagged releases won't bootstrap on egress-restricted GHES. A sentence in the README's Enterprise section about that prerequisite would be great (or a follow-up to honor GITHUB_SERVER_URL).

Thanks again — happy to clarify any of these!

Comment thread action.yml
Comment on lines +19 to +21
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: ''

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 api.github.com with their enterprise token. (A getEnv fallback in main.go wouldn't catch it, since the run step always exports INPUT_GITHUB-API-URL, even when empty.)

Suggested change
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: ''
description: 'GitHub API base URL. Defaults to the API URL of the instance running the workflow. If overriding, use the exact value of the `github.api_url` context (GitHub Enterprise Server includes the /api/v3 suffix; GHE Cloud with data residency does not).'
required: false
default: '${{ github.api_url }}'

Comment thread internal/github/gh.go
Comment on lines +74 to +79
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))
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for the common cases, but WithEnterpriseURLs guesses the layout from the hostname (e.g. it skips appending /api/v3 for hosts starting with api.), and a doubled trailing slash slips past the single TrimSuffix and yields .../api/v3//api/v3/. If the input defaults to github.api_url (see action.yml comment), the value is already the exact API base URL on every platform, so we can skip the guessing entirely:

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 github.NewClient(github.WithAuthToken(token)) call)

Bonus: this trims stray whitespace and catches schemeless typos like ghe.example.com at startup instead of failing later with an opaque transport error. I'd drop the upload URL handling entirely — nothing in the repo calls upload endpoints.

Comment thread README.md
github-api-url: 'https://ghe.example.com'
```

The `/api/v3` suffix is optional. For GitHub Enterprise Cloud with data residency, use `https://api.<tenant>.ghe.com`. If the input is not set, the public GitHub API is used.

Copy link
Copy Markdown
Collaborator

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/v3 when the host starts with api., so e.g. a GHES instance at api.corp.example.com would break without it. Simplest guidance that's always right:

Suggested change
The `/api/v3` suffix is optional. For GitHub Enterprise Cloud with data residency, use `https://api.<tenant>.ghe.com`. If the input is not set, the public GitHub API is used.
The value should be the instance's exact API URL — the same value as the `github.api_url` context (`GITHUB_API_URL`). For GitHub Enterprise Server this includes the `/api/v3` suffix; for GitHub Enterprise Cloud with data residency use `https://api.<tenant>.ghe.com`. If the input is not set, it defaults to the API URL of the instance running the workflow.

name: "enterprise cloud with data residency",
apiUrl: "https://api.acme.ghe.com",
expectedBase: "https://api.acme.ghe.com/",
expectedUpload: "https://api.acme.ghe.com/",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny note: GitHub's actual upload host for data residency is uploads.<tenant>.ghe.com, so this asserts a value that wouldn't work if we ever add uploads. Since nothing in the repo calls upload endpoints, I'd just drop the expectedUpload column (it goes away naturally if NewClient sets BaseURL directly).

Comment on lines +348 to +350
if got := client.client.BaseURL(); got != "https://api.github.com/" {
t.Errorf("Expected base URL to be https://api.github.com/, got %s", got)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (optional): this duplicates the empty uses public GitHub row in the new table below — fine to let the table own default-URL coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants