mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 16:02:38 +00:00
* 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.
114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { normalizeGitHubRemoteHost } from './git-remote-host-alias'
|
|
import {
|
|
deriveGitRemoteIdentity,
|
|
matchGitRemoteKeyParts,
|
|
normalizeGitRemoteUrl,
|
|
splitGitRemoteKey
|
|
} from './git-remote-identity'
|
|
|
|
describe('normalizeGitRemoteUrl', () => {
|
|
it('normalizes HTTPS and SSH GitHub remotes to the same canonical key', () => {
|
|
expect(normalizeGitRemoteUrl('https://github.com/example/sample-app.git')).toBe(
|
|
'github.com/example/sample-app'
|
|
)
|
|
expect(normalizeGitRemoteUrl('git@github.com:example/sample-app.git')).toBe(
|
|
'github.com/example/sample-app'
|
|
)
|
|
expect(normalizeGitRemoteUrl('ssh://git@github.com/example/sample-app.git')).toBe(
|
|
'github.com/example/sample-app'
|
|
)
|
|
expect(normalizeGitRemoteUrl('https://GitHub.com/example/sample-app.git')).toBe(
|
|
'github.com/example/sample-app'
|
|
)
|
|
})
|
|
|
|
it('preserves nested GitLab/self-hosted paths', () => {
|
|
expect(normalizeGitRemoteUrl('git@gitlab.company.test:platform/tools/sample-app.git')).toBe(
|
|
'gitlab.company.test/platform/tools/sample-app'
|
|
)
|
|
})
|
|
|
|
it('ignores explicit URL ports in canonical keys', () => {
|
|
expect(normalizeGitRemoteUrl('ssh://git@git.company.test:2222/team/sample-app.git')).toBe(
|
|
'git.company.test/team/sample-app'
|
|
)
|
|
})
|
|
|
|
it('preserves path case for case-sensitive hosted remotes', () => {
|
|
expect(normalizeGitRemoteUrl('git@Git.Company.Test:Team/Sample-App.git')).toBe(
|
|
'git.company.test/Team/Sample-App'
|
|
)
|
|
expect(normalizeGitRemoteUrl('https://git.company.test/Team/Sample-App.git')).toBe(
|
|
'git.company.test/Team/Sample-App'
|
|
)
|
|
})
|
|
|
|
it('rejects Windows local filesystem remotes', () => {
|
|
expect(normalizeGitRemoteUrl('C:\\Repos\\sample-app.git')).toBeNull()
|
|
expect(normalizeGitRemoteUrl('C:/Repos/sample-app.git')).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('deriveGitRemoteIdentity', () => {
|
|
it('prefers upstream, then origin, then the first named remote', () => {
|
|
expect(
|
|
deriveGitRemoteIdentity(
|
|
[
|
|
'origin\tgit@git.company.test:forks/sample-app.git (fetch)',
|
|
'origin\tgit@git.company.test:forks/sample-app.git (push)',
|
|
'upstream\thttps://git.company.test/team/sample-app.git (fetch)',
|
|
'upstream\thttps://git.company.test/team/sample-app.git (push)'
|
|
].join('\n')
|
|
)
|
|
).toEqual({
|
|
canonicalKey: 'git.company.test/team/sample-app',
|
|
remoteName: 'upstream',
|
|
remoteUrl: 'https://git.company.test/team/sample-app.git'
|
|
})
|
|
|
|
expect(
|
|
deriveGitRemoteIdentity('origin\tgit@git.company.test:team/sample-app.git (fetch)')
|
|
).toMatchObject({
|
|
canonicalKey: 'git.company.test/team/sample-app',
|
|
remoteName: 'origin'
|
|
})
|
|
|
|
expect(
|
|
deriveGitRemoteIdentity('mirror\tgit@git.company.test:team/sample-app.git (fetch)')
|
|
).toMatchObject({
|
|
canonicalKey: 'git.company.test/team/sample-app',
|
|
remoteName: 'mirror'
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('splitGitRemoteKey / matchGitRemoteKeyParts', () => {
|
|
const github = (key: string | undefined) => splitGitRemoteKey(key, normalizeGitHubRemoteHost)
|
|
|
|
it('rejects keys without both a host and a path', () => {
|
|
expect(github('github.com')).toBeNull()
|
|
expect(github('github.com/')).toBeNull()
|
|
expect(github('/example/sample-app')).toBeNull()
|
|
expect(github(undefined)).toBeNull()
|
|
})
|
|
|
|
it('normalizes the host and lowercases the path tail', () => {
|
|
expect(github('SSH.GitHub.com:443/Example/Sample-App')).toEqual({
|
|
host: 'github.com',
|
|
tail: 'example/sample-app'
|
|
})
|
|
})
|
|
|
|
it('treats a dotless probed host as unknown but a real host as a mismatch', () => {
|
|
const target = { host: 'github.com', tail: 'example/sample-app' }
|
|
expect(matchGitRemoteKeyParts({ host: 'github-work', tail: target.tail }, target)).toBe(
|
|
'unknown'
|
|
)
|
|
expect(matchGitRemoteKeyParts({ host: 'ghe.example.com', tail: target.tail }, target)).toBe(
|
|
false
|
|
)
|
|
expect(matchGitRemoteKeyParts({ host: 'github-work', tail: 'other/repo' }, target)).toBe(false)
|
|
})
|
|
})
|