Adds GitHub Enterprise Server (GHES) support by deriving the REST/GraphQL base URLs from the repository origin instead of hard-coding `api.github.com`. The change introduces `RepoUrl::github_api_url()` / `github_graphql_url()` plus a `scheme_ssh_as_https()` helper, a new `GitHub::from_repo_url` constructor that stores the derived REST base URL on `Remote`, and rewrites the GraphQL endpoint helper to switch between `/graphql` (dotcom) and `/api/graphql` (GHES). Tests cover dotcom, HTTPS GHES, GHES with a port, and SSH GHES origins, and the docs are updated. Overall the direction is sound, but a few concerns are worth addressing before merge: `RepoUrl::github_graphql_url` is dead code, the GraphQL helper relies on a brittle `host_str() == "api.github.com"` check that is implicitly coupled to `github_api_url`'s rewrite, and SSH origins with an explicit port silently carry the SSH port over to HTTPS. `github_graphql_url` looks unused: the only production caller for the GraphQL endpoint is `graphql_endpoint_from_rest_base` in `github_graphql.rs`, which derives the URL from `Remote::base_url` rather than from a `RepoUrl`. Having two independent code paths that must agree on the dotcom-vs-GHES rule is a maintenance hazard — if one is ever tweaked (e.g. changing the API host or adding a path prefix), the other will silently diverge.
Two cleaner options:
1. Drop `github_graphql_url` (and its tests) and keep the single derivation in `graphql_endpoint_from_rest_base`.
2. Or, better, compute both URLs from `RepoUrl` once in `GitHub::from_repo_url` and store them both on `Remote` (e.g. `rest_base_url` + `graphql_url`), so `github_graphql.rs` doesn't need to re-derive anything.
Option 2 also lets you delete the `host_str() == "api.github.com"` heuristic entirely. This dotcom check is implicitly coupled to `RepoUrl::github_api_url()`: it only works because that method rewrites `github.com` → `api.github.com` when building the REST base URL. If anyone ever changes `github_api_url` (for example to keep the original host, or to support a custom API host override), this branch will silently produce the wrong GraphQL endpoint (`/api/graphql` against `api.github.com`).
A couple of safer alternatives:
- Plumb the GraphQL URL through alongside the REST base URL (compute both in `from_repo_url`, store both on `Remote`), so this function disappears.
- Or detect dotcom from the path shape rather than the host, e.g. treat any base URL ending in `/api/v3/` as GHES and everything else as dotcom — that matches the actual REST URL conventions and is independent of the host rewrite.
At minimum, please add a comment here pointing at `github_api_url` so the invariant is documented. When the origin is an SSH URL with an explicit port (e.g. `ssh://git@github.example.com:22/owner/repo.git`), `scheme_ssh_as_https` rewrites the scheme to `https` but `self.port` still contains the SSH port. The result is `https://github.example.com:22/api/v3/`, which won't reach the API server.
The new `github_api_url_enterprise_ssh_origin` test only covers SCP-style origins (`git@host:owner/repo.git`) which have no port, so this path is untested. Consider either:
- Dropping `self.port` when promoting `ssh` → `https` (SSH ports are essentially never valid for HTTPS).
- Or only honoring the port for `http`/`https` schemes.
And add a regression test for `ssh://git@host:22/owner/repo.git`. `set_path("/api/graphql")` (and `"/graphql"`) replaces the entire path of the cloned URL. That's fine for typical GHES deployments, but it would break a GHES instance reverse-proxied behind a path prefix (e.g. REST base URL `https://example.com/github/api/v3/` would yield a GraphQL URL of `https://example.com/api/graphql`, dropping the `/github` prefix).
This is a niche setup and probably out of scope for this PR, but it's worth a short comment here so the assumption is explicit, or a follow-up issue. `GitHub::new(owner, repo, token)` is still public and unconditionally builds a `Remote` with the dotcom default base URL. With `from_repo_url` now being the correct constructor for any caller that has a `RepoUrl`, `new` is effectively a footgun for GHES users — any future caller who reaches for `GitHub::new` will silently target `api.github.com` regardless of the origin.
If there are no remaining external callers in this crate, consider making `new` private (or `pub(crate)`) and routing it through `from_repo_url`. If it must stay public, a doc comment warning that it assumes github.com would help.