relay-companion 0.1.575 → 0.1.577
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -23,6 +23,32 @@ function inside(root, file) {
|
|
|
23
23
|
const relative = path.relative(root, file);
|
|
24
24
|
return relative && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
25
25
|
}
|
|
26
|
+
// Relay's own recovery check runs every minute. Setup stops the existing
|
|
27
|
+
// services during activation; a check that notices starts repairing them and
|
|
28
|
+
// collides with setup, which then fails with "Rollback failed" (CI, 2026-10-01).
|
|
29
|
+
// Pause the check (recovery honours intent.json) and wait out a run already in
|
|
30
|
+
// progress. The returned function restores the previous state, or leaves
|
|
31
|
+
// recovery running when setup succeeded.
|
|
32
|
+
async function pauseRecoveryChecks({ homeDir, timeoutMs = 3 * 60_000, pollMs = 2000,
|
|
33
|
+
sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) } = {}) {
|
|
34
|
+
const intent = require("./recovery-intent.cjs");
|
|
35
|
+
const wasStopped = intent.stopped(homeDir);
|
|
36
|
+
intent.setStopped(true, homeDir);
|
|
37
|
+
const runLock = path.join(homeDir, ".relay", "recovery", "run.lock");
|
|
38
|
+
for (const deadline = Date.now() + timeoutMs; ;) {
|
|
39
|
+
try { bootstrap.acquireCanonicalLock(runLock).release(); break; }
|
|
40
|
+
catch (error) {
|
|
41
|
+
// Past the deadline setup proceeds, as it did before this pause existed.
|
|
42
|
+
if (!/already in progress/.test(error.message) || Date.now() >= deadline) break;
|
|
43
|
+
await sleep(pollMs);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
repause: () => intent.setStopped(true, homeDir),
|
|
48
|
+
resume: succeeded => intent.setStopped(succeeded ? false : wasStopped, homeDir),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
26
52
|
function versionCompare(a, b) {
|
|
27
53
|
const aa = a.split(".").map(BigInt), bb = b.split(".").map(BigInt);
|
|
28
54
|
for (let i = 0; i < 3; i++) if (aa[i] !== bb[i]) return aa[i] > bb[i] ? 1 : -1;
|
|
@@ -80,7 +106,7 @@ async function installFromApplication({ resourcesDir, applicationRoot, executabl
|
|
|
80
106
|
allowRecovery = true,
|
|
81
107
|
homeDir = os.homedir(), verify = verifyBundle, extract = extractBundle, activate = bootstrap.activateRuntime,
|
|
82
108
|
acquireLock = bootstrap.acquireCanonicalLock, health = require("./runtime-health.cjs").exactRuntimeHealth,
|
|
83
|
-
recover = recovery.recoverWithCandidate,
|
|
109
|
+
recover = recovery.recoverWithCandidate, pauseRecovery = pauseRecoveryChecks,
|
|
84
110
|
drain = require("./update-activity.cjs").drainCalls, download = bootstrap.downloadVerifiedArtifact,
|
|
85
111
|
onProgress = () => {}, signal } = {}) {
|
|
86
112
|
onProgress({ phase: "verifying", canCancel: false });
|
|
@@ -102,6 +128,7 @@ async function installFromApplication({ resourcesDir, applicationRoot, executabl
|
|
|
102
128
|
const journalPath = path.join(homeDir, ".relay", "application-migration.json");
|
|
103
129
|
let releaseDrain;
|
|
104
130
|
let downloadDirectory;
|
|
131
|
+
let pausedRecovery, succeeded = false;
|
|
105
132
|
try {
|
|
106
133
|
const recordedCurrent = recovery.marker(path.join(runtimeRoot, "current.json"));
|
|
107
134
|
const removed = recovery.marker(path.join(homeDir, ".relay", "application-uninstall.json"));
|
|
@@ -171,6 +198,7 @@ async function installFromApplication({ resourcesDir, applicationRoot, executabl
|
|
|
171
198
|
onProgress({ phase: "ready", canCancel: false });
|
|
172
199
|
return { ok: true, applicationOnly: true, owner, runtime: current, updateOwner: "canonical-runtime" };
|
|
173
200
|
}
|
|
201
|
+
pausedRecovery = await pauseRecovery({ homeDir });
|
|
174
202
|
if (bundle.receipt.runtimeDelivery === "download") {
|
|
175
203
|
signal?.throwIfAborted();
|
|
176
204
|
// Stage beside releases: tar requires archive and destination on one volume.
|
|
@@ -200,7 +228,8 @@ async function installFromApplication({ resourcesDir, applicationRoot, executabl
|
|
|
200
228
|
atomic(recoveryFile, { ...cleaned, schema: 1, state: "installing", releaseRoot, version: bundle.receipt.version });
|
|
201
229
|
current = null; previousOwner = null; config = {};
|
|
202
230
|
}
|
|
203
|
-
|
|
231
|
+
// A repair may have reset the recovery folder; keep checks paused until setup ends.
|
|
232
|
+
pausedRecovery.repause();
|
|
204
233
|
const layout = { root: runtimeRoot, releaseId, releaseRoot, releasesDir: path.dirname(releaseRoot),
|
|
205
234
|
packageRoot: runtime.packageRoot, pointerPath: path.join(runtimeRoot, "current.json"), lockPath: path.join(runtimeRoot, "transaction.lock") };
|
|
206
235
|
const owner = { schema: 1, appId: APPLICATION_ID, platform: process.platform,
|
|
@@ -229,6 +258,7 @@ async function installFromApplication({ resourcesDir, applicationRoot, executabl
|
|
|
229
258
|
atomic(journalPath, { ...journal, state: "complete", completedAt: Date.now() });
|
|
230
259
|
if (needsRecovery) atomic(recoveryFile, { ...recovery.marker(recoveryFile), state: "complete", completedAt: Date.now() });
|
|
231
260
|
onProgress({ phase: "ready", canCancel: false });
|
|
261
|
+
succeeded = true;
|
|
232
262
|
return { ok: true, recovered: needsRecovery, owner, runtime: result.candidate, updateOwner: "canonical-runtime" };
|
|
233
263
|
} catch (error) {
|
|
234
264
|
// Bootstrap owns service rollback. Only restore our launcher marker once
|
|
@@ -244,6 +274,7 @@ async function installFromApplication({ resourcesDir, applicationRoot, executabl
|
|
|
244
274
|
} finally {
|
|
245
275
|
try { releaseDrain?.(); }
|
|
246
276
|
finally {
|
|
277
|
+
try { pausedRecovery?.resume(succeeded); } catch { /* recovery reads a missing intent as running */ }
|
|
247
278
|
lock.release();
|
|
248
279
|
if (downloadDirectory) fs.rmSync(downloadDirectory, { recursive: true, force: true });
|
|
249
280
|
}
|
|
@@ -276,4 +307,4 @@ async function reconcileApplication({ homeDir = os.homedir(), acquireLock = boot
|
|
|
276
307
|
} finally { lock.release(); }
|
|
277
308
|
}
|
|
278
309
|
|
|
279
|
-
module.exports = { verifyBundle, extractBundle, installFromApplication, reconcileApplication, versionCompare };
|
|
310
|
+
module.exports = { pauseRecoveryChecks, verifyBundle, extractBundle, installFromApplication, reconcileApplication, versionCompare };
|
package/package.json
CHANGED