feat: GHEC tenant support for proxy and guard URL parsing#2481
Conversation
Add auto-derivation of GitHub API URL from GITHUB_API_URL and GITHUB_SERVER_URL environment variables, matching the pattern used by GitHub Agentic Workflows. Priority order: 1. --github-api-url flag (explicit) 2. GITHUB_API_URL env var 3. GITHUB_SERVER_URL auto-derivation: - *.ghe.com → api.*.ghe.com (GHEC) - other hosts → <host>/api/v3 (GHES) - github.com → api.github.com 4. Default: https://api.github.com Also fix Rust guard extract_repo_from_github_url() to handle GHEC/GHES URLs by looking for /repos/<owner>/<repo> pattern generically instead of only matching api.github.com prefixes. Closes #2480 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot api..ghe.com is incorrect. it should be copilot-api..ghe.com |
There was a problem hiding this comment.
Pull request overview
Adds tenant-aware GitHub API URL derivation for the proxy (GHEC/GHES) and expands the Rust guard’s repo URL parsing to work with non-github.com hosts, aligning behavior with environment variables commonly present in GitHub Actions.
Changes:
- Proxy: derive upstream GitHub API base from
GITHUB_API_URL/GITHUB_SERVER_URLwhen--github-api-urlis unset. - Proxy CLI: update
--github-api-urlflag defaulting behavior to enable env-based auto-derivation with fallback tohttps://api.github.com. - Rust guard: make repo extraction work generically by locating
/repos/<owner>/<repo>in the URL path and add tests; update docs to describe env vars.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/proxy/proxy.go | Adds env-based API URL derivation helpers for GHEC/GHES/github.com. |
| internal/cmd/proxy.go | Switches --github-api-url to default empty and resolves flag→env→default at runtime. |
| internal/proxy/proxy_test.go | Adds Go unit tests for API URL derivation functions. |
| guards/github-guard/rust-guard/src/labels/helpers.rs | Updates repo extraction to support non-api.github.com URL shapes. |
| guards/github-guard/rust-guard/src/labels/mod.rs | Adds Rust unit tests covering GHEC/GHES/standard URL extraction. |
| AGENTS.md | Documents GITHUB_API_URL / GITHUB_SERVER_URL behavior for the proxy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // deriveAPIFromServerURL converts a GITHUB_SERVER_URL to the corresponding API endpoint. | ||
| // GHEC tenants (*.ghe.com): https://tenant.ghe.com → https://api.tenant.ghe.com | ||
| // GitHub.com: https://github.com → https://api.github.com | ||
| // GHES (all others): https://github.example.com → https://github.example.com/api/v3 | ||
| func deriveAPIFromServerURL(serverURL string) string { | ||
| parsed, err := url.Parse(strings.TrimRight(serverURL, "/")) | ||
| if err != nil || parsed.Host == "" { | ||
| return "" | ||
| } | ||
|
|
||
| host := strings.ToLower(parsed.Host) | ||
|
|
||
| switch { | ||
| case host == "github.com" || host == "www.github.com": | ||
| return DefaultGitHubAPIBase | ||
| case strings.HasSuffix(host, ".ghe.com"): | ||
| // GHEC tenant: api.<subdomain>.ghe.com | ||
| return fmt.Sprintf("%s://api.%s", parsed.Scheme, parsed.Host) | ||
| default: | ||
| // GHES: <host>/api/v3 | ||
| return fmt.Sprintf("%s://%s/api/v3", parsed.Scheme, parsed.Host) | ||
| } |
There was a problem hiding this comment.
For the GHES case this derives the upstream base as /api/v3. Elsewhere the proxy forwards GraphQL requests to a hard-coded "/graphql" path; with an /api/v3 base this becomes /api/v3/graphql, but GHES GraphQL is typically served under /api/graphql (and the proxy already recognizes "/api/graphql" as a GraphQL path). To avoid GHES GraphQL requests breaking when using the auto-derived URL, consider either (a) keeping the base at the host root and adding /api/v3 only for REST requests, or (b) making the forwarded GraphQL path configurable/derived separately (e.g., /graphql for github.com/GHEC vs /api/graphql for GHES).
internal/proxy/proxy.go
Outdated
| host := strings.ToLower(parsed.Host) | ||
|
|
||
| switch { | ||
| case host == "github.com" || host == "www.github.com": | ||
| return DefaultGitHubAPIBase | ||
| case strings.HasSuffix(host, ".ghe.com"): | ||
| // GHEC tenant: api.<subdomain>.ghe.com | ||
| return fmt.Sprintf("%s://api.%s", parsed.Scheme, parsed.Host) | ||
| default: | ||
| // GHES: <host>/api/v3 | ||
| return fmt.Sprintf("%s://%s/api/v3", parsed.Scheme, parsed.Host) |
There was a problem hiding this comment.
url.Parse stores the host including any port (e.g. "mycompany.ghe.com:8443") in parsed.Host. The GHEC detection uses strings.HasSuffix(host, ".ghe.com"), which will fail when a port is present and will incorrectly fall through to the GHES /api/v3 derivation. Consider using parsed.Hostname() for the suffix/equals checks (and then re-adding parsed.Port() when formatting the returned URL).
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/78885d5c-1a84-4220-82ee-1439dec81702
Fixed in 880c1d9. Changed GHEC tenant URL derivation from Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
…EC detection Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/9a1660e8-6d08-4073-af81-1733316890aa
`url.Parse` stores host+port together in `parsed.Host` (e.g. `mycompany.ghe.com:8443`), so `strings.HasSuffix(host, ".ghe.com")` silently fails when a non-standard port is present, causing GHEC URLs to fall through to GHES `/api/v3` derivation. ## Changes - **`deriveAPIFromServerURL`**: switch from `parsed.Host` to `parsed.Hostname()` for `.ghe.com`/`github.com` checks; re-append port via `parsed.Port()` when formatting the GHEC `copilot-api.*` URL - **Tests**: add `GHEC tenant with port` case (`https://mycompany.ghe.com:8443` → `https://copilot-api.mycompany.ghe.com:8443`) ```go // Before — port breaks suffix match, falls through to GHES host := strings.ToLower(parsed.Host) // "mycompany.ghe.com:8443" strings.HasSuffix(host, ".ghe.com") // false ❌ // After — hostname-only check, port re-added when formatting hostname := strings.ToLower(parsed.Hostname()) // "mycompany.ghe.com" strings.HasSuffix(hostname, ".ghe.com") // true ✅ ``` <!-- START COPILOT CODING AGENT TIPS --> --- 📍 Connect Copilot coding agent with [Jira](https://gh.io/cca-jira-docs), [Azure Boards](https://gh.io/cca-azure-boards-docs) or [Linear](https://gh.io/cca-linear-docs) to delegate work to Copilot in one click without leaving your project management tool.
Summary
Adds auto-derivation of GitHub API URL from environment variables for GHEC/GHES tenants, and fixes the Rust guard URL parser to handle non-github.com URLs.
Changes
Proxy: Environment-based API URL resolution
The proxy
--github-api-urlflag now auto-resolves from environment when not explicitly set:GITHUB_API_URL— explicit API endpointGITHUB_SERVER_URL— auto-derived:*.ghe.com→copilot-api.*.ghe.com(GHEC)<host>/api/v3(GHES)github.com→api.github.comhttps://api.github.comThis matches the derivation pattern used by GitHub Agentic Workflows'
deriveCopilotApiTarget().Rust Guard: Generic URL parsing
extract_repo_from_github_url()now handles GHEC/GHES URLs by looking for/repos/<owner>/<repo>generically in the URL path, instead of only matchingapi.github.comprefixes.Tests
DeriveGitHubAPIURL()andderiveAPIFromServerURL()💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.