fix: 修复任务生命周期与调度就绪,优化执行和构建开销 (#3069)

* fix: harden task lifecycle and scheduler readiness

* fix: confine log writes to the configured log directory

* fix: verify complete build artifacts and untracked inputs

* fix: reconcile scheduler state and make stop win startup races

* fix: isolate cron generations and serialize scheduler recovery
This commit is contained in:
whyour
2026-09-13 00:32:41 +08:00
committed by GitHub
parent d62d8f3025
commit a94e665054
61 changed files with 4214 additions and 608 deletions
+44 -12
View File
@@ -12,6 +12,7 @@ import { DependenceTypes } from '../data/dependence';
import { FormData } from 'undici';
import os from 'os';
import { maybeSudo, isInContainer } from './container';
import { resolveFileAccess } from '../shared/fileAccess';
export * from './share';
@@ -144,7 +145,8 @@ export async function handleLogPath(
logPath: string,
data: string = '',
): Promise<string> {
const absolutePath = path.resolve(config.logPath, logPath);
const absolutePath = resolveFileAccess(config.logPath, [logPath]);
if (!absolutePath) throw new Error('Log path is outside the log directory');
const logFileExist = await fileExist(absolutePath);
if (!logFileExist) {
await createFile(absolutePath, data);
@@ -487,18 +489,48 @@ export function psTree(pid: number): Promise<number[]> {
});
}
export async function killTask(pid: number) {
const pids = await psTree(pid);
if (pids.length) {
try {
[pid, ...pids].reverse().forEach((x) => {
process.kill(x, 15);
});
} catch (error) { }
} else {
process.kill(pid, 2);
export async function killTask(pid: number, waitForExit = false) {
const descendants = await psTree(pid);
if (!waitForExit) {
if (descendants.length) {
try {
[pid, ...descendants]
.reverse()
.forEach((target) => process.kill(target, 15));
} catch {}
} else process.kill(pid, 2);
return;
}
const pids = [...descendants.reverse(), pid];
const signal = (target: number, sig: NodeJS.Signals) => {
try {
process.kill(target, sig);
} catch (error: any) {
if (error.code !== 'ESRCH') throw error;
}
};
for (const target of pids) signal(target, 'SIGTERM');
const alive = (target: number) => {
try {
process.kill(target, 0);
return true;
} catch (error: any) {
if (error.code === 'ESRCH') return false;
throw error;
}
};
const wait = async () => {
const deadline = Date.now() + 1000;
while (pids.some(alive) && Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 20));
}
return pids.filter(alive);
};
let remaining = await wait();
for (const target of remaining) signal(target, 'SIGKILL');
remaining = await wait();
if (remaining.length)
throw new Error(`Task processes did not exit: ${remaining.join(', ')}`);
}
export async function getPid(cmd: string) {