mirror of
https://github.com/stablyai/orca.git
synced 2026-10-05 08:02:33 +00:00
fix(gitlab): recognize self-hosted GitLab on non-default ports over SSH connections; stop one project failing the whole issues panel (#5400)
* fix(gitlab): port-aware self-hosted host recognition
Use the URL host (including a non-default web/API port) as the GitLab
host identity instead of the port-less hostname, and match known hosts
port-aware:
- A known-host entry without a port matches any port of the same
hostname (preserves legacy bare-host and gitlab.com recognition).
- A known-host entry WITH a port matches only that exact host:port, so
two services sharing a hostname on different ports (e.g. a GitLab and
a Gitea) are no longer conflated.
- For ssh/git remotes the port is a transport port (e.g. ssh :2222) and
is dropped; for http(s) remotes the port is the endpoint and kept.
- Also capture an optional :port in parseGlabAuthStatusHosts so a
self-hosted GitLab on a non-default port is discovered correctly.
* fix(gitlab): per-connection known-hosts cache + port-aware auth-status parsing
getGlabKnownHosts() was connection-blind and cached process-globally,
and on any failure it cached [gitlab.com] forever — so a repo on an SSH
connection never discovered its self-hosted host once a probe failed
before the tunnel was ready.
- getGlabKnownHosts(connectionId?) now caches per connection so a
connected repo's authenticated hosts don't leak into the local
context (or vice versa).
- The failure fallback (canonical default) is no longer cached, so a
later probe can re-discover the real host once auth/tunnel is ready.
- parseGlabAuthStatusHosts captures an optional :port on both the
'Logged in to <host>' and header-style lines, keeping two services on
the same hostname distinct by port.
* fix(gitlab): isolate unresolvable projects instead of cwd-fallback that hits exit 128
listIssues/getIssue fell back to an unscoped 'glab issue list' / 'glab
issue view' that infers the project from cwd. For a repo on an SSH
connection cwd is not the repo dir, so glab runs git resolution in a
non-repo dir and fails with 'git: exit status 128'. In an 'All projects'
aggregate one such failure could sink the whole issues panel.
When a projectRef cannot be resolved, return a structured, isolated
per-project result (listIssues: { items: [], error: not_found };
getIssue: null) and spawn no glab subprocess. Behavior is unchanged when
a projectRef IS resolved (the scoped '-R' / 'api projects/...' path).
* fix(gitlab): recognize modern /-/work_items/<iid> issue URLs
Modern GitLab emits issue URLs as /-/work_items/<iid> in addition to the
legacy /-/issues/<iid>. The URL classifiers only matched /-/issues/, so
work-item-form issue links went unrecognized.
Extend the gitlab-links parsers (parseGitLabIssueOrMRNumber /
parseGitLabIssueOrMRLink, which also backs isWorkItemLookupText) and
isGitLabIssueUrl to accept /-/work_items/<iid>, mapping it to an issue
work item with the same project-path + iid extraction.
* fix(gitlab): thread connectionId into getGlabKnownHosts call sites
Follow the existing connectionId-threading pattern: pass the repo's
connectionId into every getGlabKnownHosts() call (client.ts,
work-item-details.ts, orca-runtime.ts) so the per-connection known-hosts
cache is keyed correctly and self-hosted hosts are discovered against
the right glab context.
* docs(gitlab): use generic example hosts in comments
* fix(gitlab): pass self-hosted host:port via GITLAB_HOST (glab --hostname rejects ports)
* polish: satisfy oxlint curly + oxfmt on merged gitlab port-recognition code
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Ptah-CT <auctor@xinfty.space>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
co-authored by
Orca
Ptah-CT
Neil
parent
25896f2cdb
commit
a10e1d7584
@@ -12,10 +12,33 @@ export function normalizeGitLabHost(value: string): string {
|
||||
return value.trim().toLowerCase()
|
||||
}
|
||||
|
||||
// Why: host recognition is port-aware so two services on the same hostname
|
||||
// but different ports (e.g. a GitLab on :8080 and a Gitea on :3030) are not
|
||||
// conflated. The hostname (port-less) part is kept for legacy known-host
|
||||
// entries that were recorded without a port.
|
||||
function hostnameOf(host: string): string {
|
||||
// `host` may be `name` or `name:port`. Strip a trailing `:digits` port.
|
||||
return host.replace(/:\d+$/, '')
|
||||
}
|
||||
|
||||
function stripGitSuffix(path: string): string {
|
||||
return path.replace(/\/+$/, '').replace(/\.git$/i, '')
|
||||
}
|
||||
|
||||
// Why: the GitLab host identity is the web/API endpoint, which is what `glab
|
||||
// --hostname` and the known-hosts list speak in terms of. For http(s)
|
||||
// remotes the URL port IS that endpoint port (e.g. self-hosted on :8080),
|
||||
// so it must be kept. For ssh/git remotes the port is a transport port
|
||||
// (e.g. ssh on :2222) that does not identify the GitLab instance, so it is
|
||||
// dropped and only the hostname is used.
|
||||
function hostIdentityFromUrl(url: URL): string {
|
||||
const protocol = url.protocol.toLowerCase()
|
||||
if (protocol === 'http:' || protocol === 'https:') {
|
||||
return url.host
|
||||
}
|
||||
return url.hostname
|
||||
}
|
||||
|
||||
function makeProjectRefForTrustedHost(host: string, path: string): ProjectRef | null {
|
||||
const normalizedHost = normalizeGitLabHost(host)
|
||||
const normalizedPath = stripGitSuffix(path.replace(/^\/+/, '')).trim()
|
||||
@@ -27,6 +50,27 @@ function makeProjectRefForTrustedHost(host: string, path: string): ProjectRef |
|
||||
return { host: normalizedHost, path: normalizedPath }
|
||||
}
|
||||
|
||||
/**
|
||||
* Does `urlHost` (which may include a `:port`) match a known-host entry?
|
||||
* - An exact match (including any port) always counts.
|
||||
* - A known entry WITHOUT a port also matches a URL host on the same
|
||||
* hostname regardless of the URL's port — this preserves recognition for
|
||||
* legacy `gitlab.com` / bare-hostname known entries.
|
||||
* - A known entry WITH a port only matches a URL host with the exact same
|
||||
* port, so `gitlab.example.com:8443` does not accept a
|
||||
* `gitea.example.com:3000` (or same-host different-port) remote.
|
||||
*/
|
||||
function knownHostMatches(urlHost: string, knownHost: string): boolean {
|
||||
if (urlHost === knownHost) {
|
||||
return true
|
||||
}
|
||||
if (hostnameOf(knownHost) === knownHost) {
|
||||
// Known entry has no port — match on hostname alone.
|
||||
return hostnameOf(urlHost) === knownHost
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function makeProjectRef(
|
||||
host: string,
|
||||
path: string,
|
||||
@@ -34,7 +78,7 @@ function makeProjectRef(
|
||||
): ProjectRef | null {
|
||||
const normalizedHost = normalizeGitLabHost(host)
|
||||
const normalizedKnownHosts = knownHosts.map(normalizeGitLabHost)
|
||||
if (!normalizedKnownHosts.includes(normalizedHost)) {
|
||||
if (!normalizedKnownHosts.some((knownHost) => knownHostMatches(normalizedHost, knownHost))) {
|
||||
return null
|
||||
}
|
||||
return makeProjectRefForTrustedHost(normalizedHost, path)
|
||||
@@ -54,7 +98,7 @@ export function parseRemoteProjectRefCandidate(remoteUrl: string): ProjectRef |
|
||||
if (!['http:', 'https:', 'ssh:', 'git:', 'git+ssh:'].includes(url.protocol.toLowerCase())) {
|
||||
return null
|
||||
}
|
||||
return makeProjectRefForTrustedHost(url.hostname, url.pathname)
|
||||
return makeProjectRefForTrustedHost(hostIdentityFromUrl(url), url.pathname)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
@@ -77,7 +121,7 @@ export function parseGitLabProjectRef(
|
||||
if (!['http:', 'https:', 'ssh:', 'git:', 'git+ssh:'].includes(url.protocol.toLowerCase())) {
|
||||
return null
|
||||
}
|
||||
return makeProjectRef(url.hostname, url.pathname, knownHosts)
|
||||
return makeProjectRef(hostIdentityFromUrl(url), url.pathname, knownHosts)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user