app/lib/server/processors/JobExecutor.ts (94 lines of code) (raw):
import type { JobDiff } from "~/types/job";
// Base interface for job execution strategies
export abstract class JobExecutor {
abstract execute(jobId: string, jobData: any, credentials: any): Promise<any>;
// Method for creating branch and pushing changes
abstract createBranchAndPush(options: {
repositoryUrl: string;
branch: string;
baseBranch: string;
title: string;
description: string;
files: any[];
credentials?: any;
}): Promise<{ branch: string; commitHash: string }>;
protected parseDiff(diffOutput: string, jobId: string): JobDiff {
const files: any[] = [];
const lines = diffOutput.split("\n");
let currentFile: any = null;
let currentPatch: string[] = [];
let inFileHeader = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
// Start of a new file diff
if (line.startsWith("diff --git ")) {
// Save previous file if it exists
if (currentFile) {
const patchContent = currentPatch.join("\n");
currentFile.patch = patchContent;
currentFile.diff = patchContent; // Add diff field as alias to patch
files.push(currentFile);
}
// Extract filename from "diff --git a/filename b/filename"
const match = line.match(/diff --git a\/(.+) b\/(.+)/);
if (match) {
const filename = match[2]; // Use the 'b/' version (destination)
currentFile = {
filename: filename,
status: "modified", // Default, will be updated if we see new/deleted file
additions: 0,
deletions: 0,
patch: "",
diff: "", // Add diff field
};
currentPatch = [line];
inFileHeader = true;
}
}
// File mode/index information
else if (
line.startsWith("index ") ||
line.startsWith("new file mode") ||
line.startsWith("deleted file mode")
) {
if (currentPatch) currentPatch.push(line);
// Detect new files
if (line.startsWith("new file mode")) {
if (currentFile) currentFile.status = "added";
}
// Detect deleted files
else if (line.startsWith("deleted file mode")) {
if (currentFile) currentFile.status = "deleted";
}
}
// File headers (--- and +++)
else if (line.startsWith("--- ") || line.startsWith("+++ ")) {
if (currentPatch) currentPatch.push(line);
}
// Hunk headers (@@ lines)
else if (line.startsWith("@@")) {
if (currentPatch) currentPatch.push(line);
inFileHeader = false;
}
// Content lines
else if (line.startsWith("+") && !inFileHeader) {
if (currentFile) currentFile.additions++;
if (currentPatch) currentPatch.push(line);
} else if (line.startsWith("-") && !inFileHeader) {
if (currentFile) currentFile.deletions++;
if (currentPatch) currentPatch.push(line);
}
// Context lines (unchanged lines in diff)
else if (line.startsWith(" ") || line === "") {
if (currentPatch && !inFileHeader) currentPatch.push(line);
}
// Handle "\ No newline at end of file"
else if (line.startsWith("\\ No newline at end of file")) {
if (currentPatch) currentPatch.push(line);
}
}
// Don't forget the last file
if (currentFile) {
const patchContent = currentPatch.join("\n");
currentFile.patch = patchContent;
currentFile.diff = patchContent; // Add diff field as alias to patch
files.push(currentFile);
}
// Calculate summary
const summary = {
totalAdditions: files.reduce((sum, file) => sum + file.additions, 0),
totalDeletions: files.reduce((sum, file) => sum + file.deletions, 0),
totalFiles: files.length,
};
return {
jobId,
files,
summary,
};
}
}