From da6e4286500a49211bf1aaf444d30b44bb2703e0 Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Sun, 5 Apr 2026 05:30:26 +0900 Subject: [PATCH] Allow reviewer runtime with remote origins --- runners/agent-runner/src/reviewer-runtime.ts | 11 +++++++++ .../test/reviewer-runtime.test.ts | 24 +++++++++++++++++++ runners/codex-runner/src/reviewer-runtime.ts | 11 +++++++++ .../test/reviewer-runtime.test.ts | 24 +++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/runners/agent-runner/src/reviewer-runtime.ts b/runners/agent-runner/src/reviewer-runtime.ts index 1d0856d..d217e6f 100644 --- a/runners/agent-runner/src/reviewer-runtime.ts +++ b/runners/agent-runner/src/reviewer-runtime.ts @@ -203,6 +203,13 @@ function readGitOutput( }).trim(); } +function isRemoteGitOrigin(originUrl: string): boolean { + return ( + /^(?:https?|ssh|git):\/\//i.test(originUrl) || + /^[^/\\]+@[^:]+:.+/.test(originUrl) + ); +} + export function assertReadonlyWorkspaceRepoConnectivity( baseEnv: NodeJS.ProcessEnv, enabled: boolean, @@ -227,6 +234,10 @@ export function assertReadonlyWorkspaceRepoConnectivity( return; } + if (isRemoteGitOrigin(originUrl)) { + return; + } + if (!path.isAbsolute(originUrl) || !fs.existsSync(originUrl)) { throw new Error( `EJClaw readonly runtime cannot access local git origin path: ${originUrl || '(missing)'}`, diff --git a/runners/agent-runner/test/reviewer-runtime.test.ts b/runners/agent-runner/test/reviewer-runtime.test.ts index 65e3807..1dafd50 100644 --- a/runners/agent-runner/test/reviewer-runtime.test.ts +++ b/runners/agent-runner/test/reviewer-runtime.test.ts @@ -167,6 +167,30 @@ describe('claude reviewer runtime guard', () => { expect(() => assertReadonlyWorkspaceRepoConnectivity(env, true)).not.toThrow(); }); + it.each([ + 'https://github.com/EyeJoker-Internal/eyejoker-db.git', + 'git@github.com:EyeJoker-Internal/eyejoker-db.git', + ])( + 'accepts remote origin %s without requiring a mounted local canonical path', + (originUrl) => { + const cwd = createTempRepo('ejclaw-reviewer-remote-origin-'); + execFileSync('git', ['remote', 'add', 'origin', originUrl], { + cwd, + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'pipe'], + }); + const env = buildReviewerGitGuardEnv( + { + PATH: process.env.PATH, + EJCLAW_WORK_DIR: cwd, + }, + true, + ); + + expect(() => assertReadonlyWorkspaceRepoConnectivity(env, true)).not.toThrow(); + }, + ); + it('fails fast when the local origin path is not mounted as a git repo', () => { const cwd = createTempRepo('ejclaw-reviewer-workspace-'); const missingOriginDir = path.join( diff --git a/runners/codex-runner/src/reviewer-runtime.ts b/runners/codex-runner/src/reviewer-runtime.ts index a77cdcd..012c0b1 100644 --- a/runners/codex-runner/src/reviewer-runtime.ts +++ b/runners/codex-runner/src/reviewer-runtime.ts @@ -200,6 +200,13 @@ function readGitOutput( }).trim(); } +function isRemoteGitOrigin(originUrl: string): boolean { + return ( + /^(?:https?|ssh|git):\/\//i.test(originUrl) || + /^[^/\\]+@[^:]+:.+/.test(originUrl) + ); +} + export function assertReadonlyWorkspaceRepoConnectivity( baseEnv: NodeJS.ProcessEnv, enabled: boolean, @@ -224,6 +231,10 @@ export function assertReadonlyWorkspaceRepoConnectivity( return; } + if (isRemoteGitOrigin(originUrl)) { + return; + } + if (!path.isAbsolute(originUrl) || !fs.existsSync(originUrl)) { throw new Error( `EJClaw readonly runtime cannot access local git origin path: ${originUrl || '(missing)'}`, diff --git a/runners/codex-runner/test/reviewer-runtime.test.ts b/runners/codex-runner/test/reviewer-runtime.test.ts index b2a542c..27f6f40 100644 --- a/runners/codex-runner/test/reviewer-runtime.test.ts +++ b/runners/codex-runner/test/reviewer-runtime.test.ts @@ -154,6 +154,30 @@ describe('codex reviewer runtime guard', () => { expect(() => assertReadonlyWorkspaceRepoConnectivity(env, true)).not.toThrow(); }); + it.each([ + 'https://github.com/EyeJoker-Internal/eyejoker-db.git', + 'git@github.com:EyeJoker-Internal/eyejoker-db.git', + ])( + 'accepts remote origin %s without requiring a mounted local canonical path', + (originUrl) => { + const cwd = createTempRepo('ejclaw-reviewer-remote-origin-'); + execFileSync('git', ['remote', 'add', 'origin', originUrl], { + cwd, + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'pipe'], + }); + const env = buildReviewerGitGuardEnv( + { + PATH: process.env.PATH, + EJCLAW_WORK_DIR: cwd, + }, + true, + ); + + expect(() => assertReadonlyWorkspaceRepoConnectivity(env, true)).not.toThrow(); + }, + ); + it('fails fast when the local origin path is not mounted as a git repo', () => { const cwd = createTempRepo('ejclaw-reviewer-workspace-'); const missingOriginDir = path.join(