From 4be4853fd3110a4097ad8b87dd611b07ed163ce9 Mon Sep 17 00:00:00 2001 From: ezl-keygraph Date: Tue, 23 Jun 2026 01:17:41 +0530 Subject: [PATCH] feat(preflight): support multi-repo targets by removing .git check (#371) --- apps/worker/src/services/preflight.ts | 47 ++++---------------------- apps/worker/src/temporal/activities.ts | 4 +-- apps/worker/src/temporal/shared.ts | 1 - apps/worker/src/temporal/workflows.ts | 1 - 4 files changed, 8 insertions(+), 45 deletions(-) diff --git a/apps/worker/src/services/preflight.ts b/apps/worker/src/services/preflight.ts index 29bc96b..6b3e65f 100644 --- a/apps/worker/src/services/preflight.ts +++ b/apps/worker/src/services/preflight.ts @@ -12,7 +12,7 @@ * time and API costs compared to failing mid-pipeline. * * Checks run sequentially, cheapest first: - * 1. Repository path exists and contains .git + * 1. Repository path exists and is a directory * 2. Config file parses and validates (if provided) * 3. code_path rules match real entries in the repo (filesystem only) * 4. Credentials validate via Claude Agent SDK query (API key, OAuth, Bedrock, or Vertex AI) @@ -78,14 +78,12 @@ function pinnedLookup(addresses: LookupAddress[]): LookupFunction { // === Repository Validation === -async function validateRepo( - repoPath: string, - logger: ActivityLogger, - skipGitCheck?: boolean, -): Promise> { +async function validateRepo(repoPath: string, logger: ActivityLogger): Promise> { logger.info('Checking repository path...', { repoPath }); - // 1. Check repo directory exists + // Check repo directory exists. The repo is not required to be a git repository: + // multi-repo targets (a parent directory containing several repos) have no top-level + // .git, and git-based checkpoint/rollback in git-manager already no-ops on non-git dirs. try { const stats = await fs.stat(repoPath); if (!stats.isDirectory()) { @@ -111,36 +109,6 @@ async function validateRepo( ); } - // 2. Check .git directory exists (skipped when consumer removes .git after clone) - if (!skipGitCheck) { - try { - const gitStats = await fs.stat(`${repoPath}/.git`); - if (!gitStats.isDirectory()) { - return err( - new PentestError( - `Not a git repository (no .git directory): ${repoPath}`, - 'config', - false, - { repoPath }, - ErrorCode.REPO_NOT_FOUND, - ), - ); - } - } catch { - return err( - new PentestError( - `Not a git repository (no .git directory): ${repoPath}`, - 'config', - false, - { repoPath }, - ErrorCode.REPO_NOT_FOUND, - ), - ); - } - } else { - logger.info('Skipping .git check (skipGitCheck enabled)'); - } - logger.info('Repository path OK'); return ok(undefined); } @@ -618,7 +586,7 @@ async function validateTargetUrl(targetUrl: string, logger: ActivityLogger): Pro /** * Run all preflight checks sequentially (cheapest first). * - * 1. Repository path exists and contains .git + * 1. Repository path exists and is a directory * 2. Config file parses and validates (if configPath provided) * 3. code_path rules match at least one entry in the repo (skipped without config) * 4. Credentials validate (API key, OAuth, Bedrock, or Vertex AI) @@ -631,12 +599,11 @@ export async function runPreflightChecks( repoPath: string, configPath: string | undefined, logger: ActivityLogger, - skipGitCheck?: boolean, apiKey?: string, providerConfig?: import('../types/config.js').ProviderConfig, ): Promise> { // 1. Repository check (free — filesystem only) - const repoResult = await validateRepo(repoPath, logger, skipGitCheck); + const repoResult = await validateRepo(repoPath, logger); if (!repoResult.ok) { return repoResult; } diff --git a/apps/worker/src/temporal/activities.ts b/apps/worker/src/temporal/activities.ts index abeec76..ac03515 100644 --- a/apps/worker/src/temporal/activities.ts +++ b/apps/worker/src/temporal/activities.ts @@ -76,7 +76,6 @@ export interface ActivityInput { auditDir?: string; promptDir?: string; sastSarifPath?: string; - skipGitCheck?: boolean; providerConfig?: ProviderConfig; } @@ -457,7 +456,7 @@ export async function runReportAgent(input: ActivityInput): Promise