import { execSync } from "node:child_process"; import { existsSync, rmSync } from "node:fs"; import { log } from "./cli.ts"; import type { RepoContext } from "./github.ts"; export interface SetupOptions { tempDir: string; repoUrl?: string; forceClean?: boolean; } /** * Setup the test repository for running actions */ export function setupTestRepo(options: SetupOptions): void { const { tempDir, repoUrl = "git@github.com:pullfrogai/scratch.git", forceClean = false, } = options; if (existsSync(tempDir)) { if (forceClean) { log.info("🗑️ Removing existing .temp directory..."); rmSync(tempDir, { recursive: true, force: true }); log.info("📦 Cloning pullfrogai/scratch into .temp..."); execSync(`git clone ${repoUrl} ${tempDir}`, { stdio: "inherit" }); } else { log.info("📦 Resetting existing .temp repository..."); execSync("git reset --hard HEAD && git clean -fd", { cwd: tempDir, stdio: "inherit", }); } } else { log.info("📦 Cloning pullfrogai/scratch into .temp..."); execSync(`git clone ${repoUrl} ${tempDir}`, { stdio: "inherit" }); } } /** * Setup git configuration to avoid identity errors * Only runs in GitHub Actions environment to avoid overwriting local git config */ export function setupGitConfig(): void { // Only set up git config in GitHub Actions environment // In local development, use the user's existing git config if (!process.env.GITHUB_ACTIONS) { log.info("⚠️ Skipping git configuration setup (not in GitHub Actions)"); return; } // Debug logging for git repo detection log.debug(`setupGitConfig: Current working directory: ${process.cwd()}`); log.debug(`setupGitConfig: GITHUB_WORKSPACE: ${process.env.GITHUB_WORKSPACE}`); // Check if we're in a git repository before trying to set config log.debug(`setupGitConfig: Checking for git repository...`); try { const gitDir = execSync("git rev-parse --git-dir", { encoding: "utf-8", stdio: "pipe" }).trim(); log.debug(`setupGitConfig: Git directory found: ${gitDir}`); } catch (error) { log.warning( `⚠️ Skipping git configuration setup (not in a git repository): ${error instanceof Error ? error.message : String(error)}` ); try { const dirContents = execSync("ls -la", { encoding: "utf-8", stdio: "pipe" }).trim(); log.debug(`setupGitConfig: Current directory contents:\n${dirContents}`); } catch { // Ignore if ls fails } return; } log.info("🔧 Setting up git configuration..."); try { execSync('git config user.email "action@pullfrog.ai"', { stdio: "pipe" }); execSync('git config user.name "Pullfrog Action"', { stdio: "pipe" }); log.debug("setupGitConfig: ✓ Git configuration set successfully"); } catch (error) { // If git config fails, log warning but don't fail the action // This can happen if we're not in a git repo or git isn't available log.warning( `Failed to set git config: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Setup git authentication using GitHub token * Only runs in GitHub Actions environment to avoid breaking local git remotes */ export function setupGitAuth(githubToken: string, repoContext: RepoContext): void { // Only set up git auth in GitHub Actions environment // In local testing, this would overwrite the real git remote with fake credentials if (!process.env.GITHUB_ACTIONS) { log.info("⚠️ Skipping git authentication setup (not in GitHub Actions)"); return; } log.info("🔐 Setting up git authentication..."); // Remove existing git auth headers that actions/checkout might have set try { execSync("git config --unset-all http.https://github.com/.extraheader", { stdio: "inherit" }); log.info("✓ Removed existing authentication headers"); } catch { log.info("No existing authentication headers to remove"); } // Update remote URL to embed the token const remoteUrl = `https://x-access-token:${githubToken}@github.com/${repoContext.owner}/${repoContext.name}.git`; execSync(`git remote set-url origin "${remoteUrl}"`, { stdio: "inherit" }); log.info("✓ Updated remote URL with authentication token"); }