1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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 '';
}
}
|