diff options
Diffstat (limited to 'admin-panel/src/server/services/git.service.ts')
| -rw-r--r-- | admin-panel/src/server/services/git.service.ts | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/admin-panel/src/server/services/git.service.ts b/admin-panel/src/server/services/git.service.ts new file mode 100644 index 0000000..990f5df --- /dev/null +++ b/admin-panel/src/server/services/git.service.ts @@ -0,0 +1,142 @@ +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<void> { + 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<void> { + // 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<void> { + 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<string> { + 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<string> { + 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 ''; + } +} |
