Release ForgeFlow 0.6.1

This commit is contained in:
NuklearRabbit
2026-07-25 06:18:11 +02:00
parent 9d3933c878
commit 971896a1d5
10 changed files with 229 additions and 49 deletions
+39 -12
View File
@@ -31,28 +31,52 @@ async function readJsonFile(filePath) {
catch { return null; }
}
async function readLogTail(filePath, maxLines = 12) {
if (!filePath) return '';
try {
const text = await fs.readFile(filePath, 'utf8');
return text.split(/\r?\n/).filter(Boolean).slice(-maxLines).join('\n');
} catch { return ''; }
}
async function updaterStartupError(message, code, { statusPath, logPath, expectedUpdateId } = {}) {
const status = statusPath ? await readJsonFile(statusPath) : null;
const logTail = await readLogTail(logPath);
const details = [];
if (status?.updateId && expectedUpdateId && status.updateId !== expectedUpdateId) details.push('The helper wrote a status for a different update request.');
if (status?.message) details.push(status.message);
if (logTail) details.push(`Update helper log:\n${logTail}`);
const error = new Error([message, ...details].filter(Boolean).join('\n\n'));
error.code = code;
error.status = status;
error.logPath = logPath || null;
return error;
}
async function waitForUpdaterStarted(statusPath, {
timeoutMs = 12000,
timeoutMs = 15000,
pollMs = 100,
childState = null
childState = null,
expectedUpdateId = null,
logPath = null
} = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const status = await readJsonFile(statusPath);
if (status && ['started', 'waiting-for-exit', 'backing-up', 'extracting', 'applying', 'validating'].includes(status.state)) {
const belongsToRequest = !expectedUpdateId || status?.updateId === expectedUpdateId;
if (status && belongsToRequest && ['started', 'waiting-for-exit', 'backing-up', 'extracting', 'applying', 'validating'].includes(status.state)) {
return status;
}
if (status && belongsToRequest && ['failed', 'rolled-back'].includes(status.state)) {
throw await updaterStartupError('The update helper reported a failure before ForgeFlow could close.', 'UPDATE_HELPER_START_FAILED', { statusPath, logPath, expectedUpdateId });
}
if (childState?.error) throw childState.error;
if (childState?.exited) {
const error = new Error(`The update helper exited before it confirmed startup (exit code ${childState.code ?? 'unknown'}).`);
error.code = 'UPDATE_HELPER_EXITED_EARLY';
throw error;
throw await updaterStartupError(`The update helper exited before it confirmed startup (exit code ${childState.code ?? 'unknown'}).`, 'UPDATE_HELPER_EXITED_EARLY', { statusPath, logPath, expectedUpdateId });
}
await delay(pollMs);
}
const error = new Error('The update helper did not confirm startup. ForgeFlow was left open and no source files were changed.');
error.code = 'UPDATE_HELPER_START_TIMEOUT';
throw error;
throw await updaterStartupError('The update helper did not confirm startup. ForgeFlow was left open and no source files were changed.', 'UPDATE_HELPER_START_TIMEOUT', { statusPath, logPath, expectedUpdateId });
}
class UpdateService {
@@ -227,12 +251,14 @@ class UpdateService {
if (!child.once) finish(resolve);
});
child.unref?.();
const started = await waitForUpdaterStarted(statusPath, {
timeoutMs: this.handshakeTimeoutMs,
pollMs: this.handshakePollMs,
childState
childState,
expectedUpdateId: updateId,
logPath
});
child.unref?.();
await this.diagnostics?.info('updates.apply-started', {
updateId,
@@ -281,5 +307,6 @@ module.exports = {
safeRepositoryPart,
resolveWindowsPowerShellPath,
waitForUpdaterStarted,
readJsonFile
readJsonFile,
readLogTail
};