Files
orca/src/shared/git-remote-host-alias.ts
T
Neil 400321edcd fix(workspaces): gate the GitHub palette number match on repo remote identity (#14413)
* fix(workspaces): gate the GitHub palette number match on repo identity

`repoMatchesGitHubSlug` returned the permissive `'unknown'` whenever the repo
displayName was not in `owner/repo` form and no upstream metadata existed — the
common basename-named non-fork case. The caller only rejects on `false`, so a
pasted issue/PR URL could activate a workspace in a different repo that happened
to share the number, since issue/PR numbers are per-repo.

Mirror the GitLab gate from #14381: fall back to the probed
`gitRemoteIdentity.canonicalKey` before giving up, comparing host and owner/repo
after normalizing port, `www.`, and case. An `upstream`-derived identity stays
`'unknown'` because `deriveGitRemoteIdentity` ranks `upstream` above `origin`, so
a fork's own origin is invisible and rejecting would drop URLs from the fork the
user actually checked out.

The canonicalKey compare runs after the displayName branch: displayName is
compared host-agnostically, so mirrors and host aliases of the same owner/repo
keep matching as they do today, and the probed remote only fills in where no
name evidence exists.

Refs STA-4237

* fix(workspaces): keep SSH host aliases matching in the palette identity gate

`git remote -v` reports ssh.github.com, www., and ~/.ssh/config `Host` aliases
verbatim, so comparing a probed canonicalKey against a pasted URL host rejected
legitimate GitHub/GitLab remotes. Normalize the alias hosts both sides can fold
offline, and downgrade a host-only mismatch to 'unknown' when the probed host is
dotless (an unexpandable OpenSSH alias); dotted hosts like ghe.example.com still
lose. Lifts the GitHub host normalizer into shared instead of a third copy.

* fix(repos): keep the www host fold out of the derived project identity

getProjectIdentityKey feeds the persisted Project id, so folding www. there
re-keyed existing projects on upgrade and dropped localWindowsRuntimePreference.
Restrict the fold to the palette's URL-vs-remote comparison, and pin the derived
id for a www. remote so it cannot drift silently again.
2026-08-13 20:28:32 -07:00

45 lines
1.8 KiB
TypeScript

// Why: a remote URL can name a forge through an alias host (SSH-over-443, `www.`), and every
// comparison of a probed remote against a pasted URL must fold those to the same identity.
function normalizeRemoteHost(host: string): string {
return host.trim().toLowerCase()
}
/**
* Fold a `www.` prefix. Deliberately NOT part of the forge normalizers: those feed
* `getProjectIdentityKey`, so folding there would re-key already-persisted projects on upgrade.
* Only URL-vs-remote comparison may use it.
*/
export function foldWwwHostAlias(host: string): string {
return normalizeRemoteHost(host).replace(/^www\./, '')
}
export function normalizeGitHubRemoteHost(host: string): string {
const normalizedHost = normalizeRemoteHost(host)
// Why: GitHub documents ssh.github.com as SSH-over-HTTPS for github.com repos.
return normalizedHost === 'ssh.github.com' ? 'github.com' : normalizedHost
}
export function normalizeGitLabRemoteHost(host: string): string {
const normalizedHost = normalizeRemoteHost(host)
// Why: GitLab documents altssh.gitlab.com as SSH-over-443 for gitlab.com projects.
return normalizedHost === 'altssh.gitlab.com' ? 'gitlab.com' : normalizedHost
}
/** Comparison-only host fold: the forge alias plus `www.`, for matching a remote to a pasted URL. */
export function foldComparableGitHubHost(host: string): string {
return normalizeGitHubRemoteHost(foldWwwHostAlias(host))
}
export function foldComparableGitLabHost(host: string): string {
return normalizeGitLabRemoteHost(foldWwwHostAlias(host))
}
/**
* A host with no dot is an OpenSSH `Host` alias (`git@github-work:owner/repo`) that only
* ~/.ssh/config can expand; `git remote -v` never resolves it, so it is unknown, not wrong.
*/
export function isUnresolvedSshHostAlias(host: string): boolean {
return !normalizeRemoteHost(host).includes('.')
}