summaryrefslogtreecommitdiff
path: root/admin-panel/src/server/services/git.service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'admin-panel/src/server/services/git.service.ts')
-rw-r--r--admin-panel/src/server/services/git.service.ts142
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 '';
+ }
+}