6.2
This commit is contained in:
1 parent
528104fff0
commit
beb498d38b
60 files changed
+3184
-2129
No files matched your search
@@ -0,0 +1,137 @@
|
||||
import { execFile } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { BLOG_ROOT } from "./config";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
type GitExecError = Error & {
|
||||
stdout?: string;
|
||||
stderr?: string;
|
||||
code?: number;
|
||||
};
|
||||
|
||||
export interface DeployResult {
|
||||
success: boolean;
|
||||
output?: string;
|
||||
error?: string;
|
||||
conflict?: boolean;
|
||||
}
|
||||
|
||||
interface QueueItem {
|
||||
message: string;
|
||||
resolve: (result: DeployResult) => void;
|
||||
reject: (err: Error) => void;
|
||||
}
|
||||
|
||||
class GitDeployQueue {
|
||||
private queue: QueueItem[] = [];
|
||||
private running = false;
|
||||
|
||||
async enqueue(commitMessage: string): Promise<DeployResult> {
|
||||
return new Promise<DeployResult>((resolve, reject) => {
|
||||
this.queue.push({ message: commitMessage, resolve, reject });
|
||||
if (!this.running) this.next();
|
||||
});
|
||||
}
|
||||
|
||||
async sync(): Promise<DeployResult> {
|
||||
try {
|
||||
const result = await this.runGit(["pull", "--rebase", "--autostash", "origin", "main"]);
|
||||
return { success: true, output: result.stdout + result.stderr };
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
if (this.isRebaseConflict(msg)) {
|
||||
await this.runGit(["rebase", "--abort"], true);
|
||||
return { success: false, conflict: true, error: "远程冲突,已放弃合并" };
|
||||
}
|
||||
return { success: false, error: msg };
|
||||
}
|
||||
}
|
||||
|
||||
private async next(): Promise<void> {
|
||||
const item = this.queue.shift();
|
||||
if (!item) {
|
||||
this.running = false;
|
||||
return;
|
||||
}
|
||||
this.running = true;
|
||||
try {
|
||||
const result = await this.execute(item.message);
|
||||
item.resolve(result);
|
||||
} catch (e) {
|
||||
item.reject(e instanceof Error ? e : new Error(String(e)));
|
||||
}
|
||||
this.next();
|
||||
}
|
||||
|
||||
private async runGit(args: string[], allowFailure = false) {
|
||||
try {
|
||||
const { stdout, stderr } = await execFileAsync("git", args, {
|
||||
cwd: BLOG_ROOT,
|
||||
windowsHide: true,
|
||||
maxBuffer: 1024 * 1024,
|
||||
});
|
||||
return { stdout, stderr, code: 0 };
|
||||
} catch (error) {
|
||||
const err = error as GitExecError;
|
||||
const result = {
|
||||
stdout: err.stdout || "",
|
||||
stderr: err.stderr || err.message,
|
||||
code: typeof err.code === "number" ? err.code : 1,
|
||||
};
|
||||
if (allowFailure) return result;
|
||||
throw new Error((result.stderr || result.stdout || err.message).trim());
|
||||
}
|
||||
}
|
||||
|
||||
private isRebaseConflict(message: string): boolean {
|
||||
return /conflict|CONFLICT|Resolve all conflicts|Automatic merge failed|could not apply|rebase/i.test(message);
|
||||
}
|
||||
|
||||
private async execute(message: string): Promise<DeployResult> {
|
||||
let output = "";
|
||||
|
||||
// git add
|
||||
const addResult = await this.runGit(["add", "content"]);
|
||||
output += addResult.stdout + addResult.stderr;
|
||||
|
||||
// git diff --cached --quiet (exit 1 = staged changes exist)
|
||||
const diffResult = await this.runGit(["diff", "--cached", "--quiet"], true);
|
||||
if (diffResult.code === 1) {
|
||||
const commitResult = await this.runGit(["commit", "-m", message]);
|
||||
output += commitResult.stdout + commitResult.stderr;
|
||||
} else if (diffResult.code !== 0) {
|
||||
return { success: false, error: (diffResult.stderr || diffResult.stdout || "检查暂存区失败").trim() };
|
||||
}
|
||||
|
||||
// git pull --rebase --autostash
|
||||
try {
|
||||
const pullResult = await this.runGit(["pull", "--rebase", "--autostash", "origin", "main"]);
|
||||
output += pullResult.stdout + pullResult.stderr;
|
||||
} catch (error) {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
if (this.isRebaseConflict(msg)) {
|
||||
await this.runGit(["rebase", "--abort"], true);
|
||||
return {
|
||||
success: false,
|
||||
conflict: true,
|
||||
error: "远程有冲突,已自动放弃合并。请先 /sync 同步,手动解决后再 deploy。",
|
||||
output: msg.slice(0, 1000),
|
||||
};
|
||||
}
|
||||
return { success: false, error: msg };
|
||||
}
|
||||
|
||||
// git push
|
||||
try {
|
||||
const pushResult = await this.runGit(["push"]);
|
||||
output += pushResult.stdout + pushResult.stderr;
|
||||
} catch (error) {
|
||||
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
||||
}
|
||||
|
||||
return { success: true, output: output.slice(0, 1000) };
|
||||
}
|
||||
}
|
||||
|
||||
export const deployQueue = new GitDeployQueue();
|
||||
Reference in new issue
Block a user