Skip to content

Hostname-substring forge detection drops self-hosted instances on custom domains #32

Description

@noyobo

The problem we hit

A repository whose remote is a self-hosted GitLab on a domain that does not contain gitlabssh://git@code.example.com:2222/group/project.git — has no forge at all in v0.15.0:

  • No Create pull request in the status bar (the whole button, not a disabled one)
  • View on remote, commit links, and #N issue chips produce nothing
  • The GitLab (self-hosted) account connected for that exact host in Settings → Accounts is never used for the repository

Environment: macOS 15 (darwin 24.6), v0.15.0, Tauri v2 / WKWebView.

Steps: connect a GitLab (self-hosted) account for code.example.com, open a repository whose remote is the URL above, check out a branch other than main, look at the status bar.

Where it comes from — packages/core/src/forge/remote.ts:

if (hostname.includes('github')) {  }
if (hostname === 'bitbucket.org') {  }
if (hostname.includes('bitbucket')) {  }
if (hostname.includes('gitlab')) {  }
return null;

That null then propagates quietly: webUrl() returns null for every kind, so currentPullRequestUrl is null and StatusBar renders nothing behind {prUrl && …}; buildBrowseUrl / buildFileUrl / buildCommitUrl / issueHref are null too. Nothing logs, so it reads as "the feature does not exist" rather than "your host was not recognized".

What it is not:

  • Not auth. The account verifies fine — account_check.rs already probes self-hosted GitLab by host (https://<host>/api/v4/personal_access_tokens/self, falling back to http and to /api/v4/user). The token works; the remote just never maps to that host's forge.
  • Not the default-branch gate in currentPullRequestUrl, and not the max-[950px]:hidden on the status bar action. It reproduces on a feature branch in a wide window.
  • Not specific to the ssh:// + port shape. parseRemote handles that correctly; https://code.example.com/group/project.git behaves the same.

Options we considered

Option Why it might work Cost
Widen the substring list (git., code., scm., …) Zero config Guesses, and cannot tell GitLab from Gitea or GitHub Enterprise on a same-shaped domain
Fall back to one kind when nothing matches One line; self-hosted GitLab is the common case, and GitLab's URL shapes (/-/blob/, /-/merge_requests/new) are what most unrecognized hosts want Gitea / Gogs / sr.ht get GitLab-shaped URLs; a GitHub Enterprise custom domain is read wrong
Take the kind from the connected account's provider (gitlab-self, github, …) Accounts are already keyed by host, so the user has effectively declared it parseForgeRemote is pure and runs inside render paths, so the map has to reach it — a module-level registry seeded from settings, or a parameter threaded through every caller. Does nothing until an account is connected
Explicit host → kind map in Settings, plus a configurable default Deterministic, covers GitHub Enterprise and Gitea, no guessing New settings surface, and the same "how does a pure function see it" plumbing
Probe the host (/api/v4/version vs the GitHub API) and cache the verdict No user input at all Turns a pure sync call into a network round-trip; VPN and captive portals make it flaky

What we verified works

  • On a real self-hosted instance (macOS, Tauri build). With the host resolved to GitLab instead of falling through to null, the status bar action comes back on a feature branch and opens the correct prefilled page, https://<host>/<group>/<project>/-/merge_requests/new?merge_request%5Bsource_branch%5D=…. That is the direct confirmation that the null out of parseForgeRemote — not auth, not the branch gate — is what removes the action.
  • Confirmed on the account side. The GitLab self-hosted token verifies against the same host the remote parser rejects, which is what makes the symptom confusing: the app reports the account as connected and still offers nothing on the repository.
  • Traced, not bisected. That the same null reaches buildBrowseUrl, buildFileUrl, buildCommitUrl, and issueHref comes from reading those paths, not from clicking each one.
  • Not verified. Creating a merge request in-app through the API (the dialog path) on a self-hosted instance. We also have a local host → kind mapping in Settings with unit tests, but we are not putting it forward as a proven design — we are asking about direction, not asking you to take a patch.

Questions for the maintainer

  1. Is hostname-substring matching intended as the whole detection story, or was a host → kind mapping already on your list? Settings → Accounts already distinguishes gitlab from gitlab-self per host, so the information exists one layer away.
  2. Would you take the connected account's provider as the source of truth for an unrecognized host? If so, how should it reach parseForgeRemote without breaking its purity — a registry the settings layer seeds, or an explicit argument at the call sites?
  3. When nothing is configured and nothing matches, what should the default be: keep returning null as today, assume GitLab, or assume GitHub? The silence is the part that reads as a missing feature rather than an unrecognized host.
  4. Is there a constraint that keeps parseForgeRemote pure and synchronous that we should design around — the boot path, graph row rendering, or something else?

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions