import { execSync } from 'child_process'; import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { join, dirname } from 'path'; const REPO_BASE = process.env.GIT_REPO_PATH || '/repos'; export interface RepoInfo { name: string; path: string; exists: boolean; } export function createBareRepo(repoName: string, defaultBranch = 'main'): RepoInfo { const repoPath = join(REPO_BASE, repoName); if (existsSync(repoPath)) { return { name: repoName, path: repoPath, exists: true }; } execSync(`git init --bare "${repoPath}"`, { stdio: 'pipe' }); execSync(`git -C "${repoPath}" symbolic-ref HEAD refs/heads/${defaultBranch}`, { stdio: 'pipe', }); // Set description for cgit const descPath = join(repoPath, 'description'); execSync(`echo "Repository managed by admin-panel" > "${descPath}"`, { stdio: 'pipe' }); // Enable post-update hook for git daemon const hookPath = join(repoPath, 'hooks', 'post-update'); if (!existsSync(hookPath)) { execSync(`cp "${repoPath}/hooks/post-update.sample" "${hookPath}" 2>/dev/null || true`, { stdio: 'pipe', }); } // Run git update-server-info for HTTP access execSync(`git -C "${repoPath}" update-server-info`, { stdio: 'pipe' }); return { name: repoName, path: repoPath, exists: false }; } export function setRepoDescription(repoName: string, description: string): void { const descPath = join(REPO_BASE, repoName, 'description'); execSync(`echo "${description.replace(/"/g, '\\"')}" > "${descPath}"`, { stdio: 'pipe' }); } export function repoExists(repoName: string): boolean { return existsSync(join(REPO_BASE, repoName)); } export function deleteRepo(repoName: string): void { const repoPath = join(REPO_BASE, repoName); if (existsSync(repoPath)) { execSync(`rm -rf "${repoPath}"`, { stdio: 'pipe' }); } } // --- Workspace operations for Builder agent --- export async function cloneRepo(bareRepoPath: string, workspacePath: string): Promise { if (existsSync(workspacePath)) { // Pull latest instead of re-cloning execSync(`git -C "${workspacePath}" fetch origin`, { stdio: 'pipe' }); execSync(`git -C "${workspacePath}" reset --hard origin/main 2>/dev/null || true`, { stdio: 'pipe' }); return; } mkdirSync(workspacePath, { recursive: true }); execSync(`git clone "${bareRepoPath}" "${workspacePath}"`, { stdio: 'pipe' }); // Configure git user for commits execSync(`git -C "${workspacePath}" config user.email "admin-panel@swave.lol"`, { stdio: 'pipe' }); execSync(`git -C "${workspacePath}" config user.name "Admin Panel"`, { stdio: 'pipe' }); } export async function createBranch(workspacePath: string, branchName: string): Promise { // Create or switch to branch try { execSync(`git -C "${workspacePath}" checkout -b "${branchName}"`, { stdio: 'pipe' }); } catch { // Branch might already exist execSync(`git -C "${workspacePath}" checkout "${branchName}"`, { stdio: 'pipe' }); } } export async function writeFiles( workspacePath: string, files: Array<{ path: string; content: string }> ): Promise { for (const file of files) { const fullPath = join(workspacePath, file.path); mkdirSync(dirname(fullPath), { recursive: true }); writeFileSync(fullPath, file.content, 'utf-8'); } } export async function commitAndDiff( workspacePath: string, message: string, branchName: string ): Promise { execSync(`git -C "${workspacePath}" add -A`, { stdio: 'pipe' }); // Check if there are changes to commit try { execSync(`git -C "${workspacePath}" diff --cached --quiet`, { stdio: 'pipe' }); // No changes return ''; } catch { // There are changes — commit them } execSync(`git -C "${workspacePath}" commit -m "${message.replace(/"/g, '\\"')}"`, { stdio: 'pipe' }); // Get diff against main const diff = execSync( `git -C "${workspacePath}" diff main..${branchName} 2>/dev/null || git -C "${workspacePath}" diff HEAD~1..HEAD`, { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 } ); return diff; } export async function pushToGerrit(workspacePath: string): Promise { const output = execSync( `git -C "${workspacePath}" push origin HEAD:refs/for/main 2>&1`, { encoding: 'utf-8' } ); return output; } export function getFileList(workspacePath: string): string { if (!existsSync(workspacePath)) return ''; try { return execSync(`find "${workspacePath}" -type f -not -path "*/.git/*" | sort`, { encoding: 'utf-8', maxBuffer: 1024 * 1024, }); } catch { return ''; } }