mirror of
https://github.com/coleam00/Archon
synced 2026-04-21 13:37:41 +00:00
fix(core): parse non-GitHub SSH remote URLs correctly in registerRepository
When registering a repo whose remote uses a self-hosted or GitLab SSH URL (git@host:org/repo), the previous code only normalised git@github.com: URLs. All other SSH hosts fell through to a generic split-on-slash, leaving the full SSH host (e.g. git@gitlab.example.com:org) as the owner component. This caused two downstream failures: 1. The codebase name stored in the DB contained a colon (e.g. git@gitlab.example.com:org/repo), which made parseOwnerRepo() return null due to its SAFE_NAME regex. resolveProjectPaths() then fell back to writing artifacts inside the worktree directory instead of the canonical ~/.archon/workspaces/ tree, so the server could never serve them and the Web UI always showed "Artifact file not found". 2. The colon in the worktree path acts as a classpath separator on Unix, breaking Java/Maven/Gradle test compilation for any project run inside that worktree. Fix: match any git@host:owner/repo SSH URL with a single regex and extract just the owner/repo portion, discarding the host. HTTPS and other URL forms continue to use the existing path-split fallback unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bed36ca4ad
commit
4e4982e4fd
2 changed files with 40 additions and 10 deletions
|
|
@ -706,6 +706,32 @@ describe('registerRepository', () => {
|
|||
expect(createArg.name).toBe('acme/backend');
|
||||
});
|
||||
|
||||
test('builds owner/repo name from non-GitHub SSH remote URL without colon in name', async () => {
|
||||
// GitLab / self-hosted SSH URLs: git@host:org/repo — the host must NOT appear in
|
||||
// the codebase name or worktree path because colons break Java classpaths on Unix.
|
||||
spyExecFileAsync.mockImplementation((cmd: string, args: string[]) => {
|
||||
if (args.includes('rev-parse')) return Promise.resolve({ stdout: '.git', stderr: '' });
|
||||
if (args.includes('get-url'))
|
||||
return Promise.resolve({
|
||||
stdout: 'git@gitlab.example.com:myorg/myproject.git',
|
||||
stderr: '',
|
||||
});
|
||||
return Promise.resolve({ stdout: '', stderr: '' });
|
||||
});
|
||||
mockFindCodebaseByDefaultCwd.mockResolvedValueOnce(null);
|
||||
mockCreateCodebase.mockResolvedValueOnce(
|
||||
makeCodebase({ name: 'myorg/myproject' }) as ReturnType<typeof makeCodebase>
|
||||
);
|
||||
|
||||
await registerRepository('/home/user/myproject');
|
||||
|
||||
const createArg = mockCreateCodebase.mock.calls[0]?.[0] as { name: string };
|
||||
// Must be plain owner/repo — no SSH host, no colon
|
||||
expect(createArg.name).toBe('myorg/myproject');
|
||||
expect(createArg.name).not.toContain(':');
|
||||
expect(createArg.name).not.toContain('@');
|
||||
});
|
||||
|
||||
// ── Command auto-loading ───────────────────────────────────────────────
|
||||
test('auto-loads markdown commands found in .archon/commands', async () => {
|
||||
spyExecFileAsync.mockImplementation((cmd: string, args: string[]) => {
|
||||
|
|
|
|||
|
|
@ -328,16 +328,20 @@ export async function registerRepository(localPath: string): Promise<RegisterRes
|
|||
let ownerName = '_local';
|
||||
if (remoteUrl) {
|
||||
const cleaned = remoteUrl.replace(/\.git$/, '').replace(/\/+$/, '');
|
||||
let workingRemote = cleaned;
|
||||
if (cleaned.startsWith('git@github.com:')) {
|
||||
workingRemote = cleaned.replace('git@github.com:', 'https://github.com/');
|
||||
}
|
||||
const parts = workingRemote.split('/');
|
||||
const r = parts.pop();
|
||||
const o = parts.pop();
|
||||
if (o && r) {
|
||||
name = `${o}/${r}`;
|
||||
ownerName = o;
|
||||
// Handle any SSH git URL (git@host:owner/repo) by extracting just owner/repo.
|
||||
// This avoids colons in worktree paths, which break Java classpaths on Unix.
|
||||
const sshMatch = /^git@[^:]+:([^/]+)\/(.+)$/.exec(cleaned);
|
||||
if (sshMatch) {
|
||||
name = `${sshMatch[1]}/${sshMatch[2]}`;
|
||||
ownerName = sshMatch[1];
|
||||
} else {
|
||||
const parts = cleaned.split('/');
|
||||
const r = parts.pop();
|
||||
const o = parts.pop();
|
||||
if (o && r) {
|
||||
name = `${o}/${r}`;
|
||||
ownerName = o;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue