in app/lib/server/processors/DockerJobExecutor.ts [374:524]
async createBranchAndPush(options: {
repositoryUrl: string;
branch: string;
baseBranch: string;
title: string;
description: string;
files: any[];
credentials?: any;
}): Promise<{ branch: string; commitHash: string }> {
// For Docker executor, we'll use the same Git operations as ApiJobExecutor
// since Git operations don't need to run inside the container
const { promises: fs } = await import("fs");
const { exec } = await import("child_process");
const { promisify } = await import("util");
const path = await import("path");
const os = await import("os");
const execAsync = promisify(exec);
let tempDir: string | null = null;
try {
console.log(
`🌿 Creating branch '${options.branch}' and pushing changes...`
);
// Create temporary directory
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "hugex-git-"));
console.log(`📁 Created temp directory: ${tempDir}`);
// Get authenticated repository URL (if needed)
const repoUrl = await this.getAuthenticatedRepoUrl(options.repositoryUrl);
// 1. Shallow clone the repository
console.log(`📌 Cloning repository: ${options.repositoryUrl}`);
await execAsync(
`git clone --depth=1 --branch ${options.baseBranch} "${repoUrl}" repo`,
{
cwd: tempDir,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
}
);
const repoPath = path.join(tempDir, "repo");
// 2. Create and checkout new branch
console.log(`🌱 Creating branch: ${options.branch}`);
await execAsync(`git checkout -b "${options.branch}"`, {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
// 3. Apply file changes from diff
console.log(`🗏 Applying ${options.files.length} file changes...`);
await this.applyFileChanges(repoPath, options.files);
// 4. Configure git user (required for commits)
await execAsync('git config user.name "HugeX Bot"', {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
await execAsync(
'git config user.email "hugex@users.noreply.github.com"',
{
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
}
);
// 5. Stage all changes
console.log(`📚 Staging changes...`);
await execAsync("git add .", {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
// Check if there are any changes to commit
const { stdout: statusOutput } = await execAsync(
"git status --porcelain",
{
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
}
);
console.log(`Git status output:`, statusOutput);
if (!statusOutput.trim()) {
// No changes detected - let's create a minimal change to ensure we have something to commit
console.log("No changes detected, creating minimal change...");
// Create a simple marker file to indicate this branch was created by HugeX
const markerPath = path.join(repoPath, ".hugex-branch-marker");
const markerContent = `Branch created by Hugex\nTimestamp: ${new Date().toISOString()}\nBranch: ${
options.branch
}\nRepository: ${options.repositoryUrl}\n`;
await fs.writeFile(markerPath, markerContent, "utf8");
// Stage the marker file
await execAsync("git add .hugex-branch-marker", {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
console.log("Created marker file to ensure non-empty commit");
}
// 6. Commit changes
const commitMessage = `${options.title}\n\n${options.description}`;
console.log(`📝 Committing changes...`);
await execAsync(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
// 7. Get commit hash
const { stdout: commitHash } = await execAsync("git rev-parse HEAD", {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
// 8. Push branch to origin
console.log(`🚀 Pushing branch to origin...`);
await execAsync(`git push origin "${options.branch}"`, {
cwd: repoPath,
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
});
const finalCommitHash = commitHash.trim();
console.log(
`✅ Successfully created branch '${options.branch}' with commit: ${finalCommitHash}`
);
return {
branch: options.branch,
commitHash: finalCommitHash,
};
} catch (error) {
console.error("❌ Failed to create branch and push:", error);
throw new Error(`Git operation failed: ${error.message}`);
} finally {
// Clean up temporary directory
if (tempDir) {
try {
await fs.rm(tempDir, { recursive: true, force: true });
console.log(`🧽 Cleaned up temp directory: ${tempDir}`);
} catch (cleanupError) {
console.warn(`⚠️ Failed to clean up temp directory: ${cleanupError}`);
}
}
}