fix(update): mosaic update runs the install-ordering guard post-reseed (#882 --sync-only bypass) (#883)
Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
This commit was merged in pull request #883.
This commit is contained in:
@@ -32,10 +32,7 @@ import {
|
||||
formatAllPackagesTable,
|
||||
getInstallAllCommand,
|
||||
repairFleetCommsTools,
|
||||
runFrameworkReseed,
|
||||
refreshActiveFleetUnits,
|
||||
readRosterAgentNames,
|
||||
buildRelaunchCommands,
|
||||
runUpdateReseedFlow,
|
||||
checkFrameworkDrift,
|
||||
FRAMEWORK_RESEED_PACKAGE,
|
||||
} from './runtime/update-checker.js';
|
||||
@@ -445,12 +442,18 @@ program
|
||||
'--repair-tools',
|
||||
'Restore the supported current-version TOOLS contract and executable fleet helper',
|
||||
)
|
||||
.option(
|
||||
'--allow-inactive-enforcement',
|
||||
'Wire lease-enforcement hooks into settings.json even when activation cannot be confirmed ' +
|
||||
'(explicit, loud, non-default opt-out for the post-reseed install-ordering guard — see #869/#882)',
|
||||
)
|
||||
.action(
|
||||
async (opts: {
|
||||
check?: boolean;
|
||||
reseed?: boolean;
|
||||
relaunch?: boolean;
|
||||
repairTools?: boolean;
|
||||
allowInactiveEnforcement?: boolean;
|
||||
}) => {
|
||||
if (opts.repairTools) {
|
||||
const repair = repairFleetCommsTools();
|
||||
@@ -471,57 +474,24 @@ program
|
||||
// checkForAllUpdates imported statically above
|
||||
const { execSync } = await import('node:child_process');
|
||||
|
||||
// Re-seed the framework from the freshly-installed package, propagate shipped
|
||||
// systemd unit fixes to the active units, and (opt-in) relaunch durable
|
||||
// agents. Shared by the "packages updated" and the "framework drift" paths.
|
||||
// Re-seed the framework from the freshly-installed package, re-apply the
|
||||
// install-ordering guard to settings.json (#882 (b) — closes the
|
||||
// `--sync-only` bypass so `mosaic update` never leaves enforcement-hook
|
||||
// wiring stale/unguarded), propagate shipped systemd unit fixes to the
|
||||
// active units, and (opt-in) relaunch durable agents. Shared by the
|
||||
// "packages updated" and the "framework drift" paths. Extracted to
|
||||
// update-checker.ts (`runUpdateReseedFlow`) for direct unit testability.
|
||||
const reseedFramework = (reason: string): void => {
|
||||
console.log(reason);
|
||||
const reseed = runFrameworkReseed();
|
||||
if (!reseed.ok) {
|
||||
console.error(
|
||||
`\n⚠ Framework re-seed skipped: ${reseed.reason ?? 'unknown'}.\n` +
|
||||
' Activate manually: bash "$(npm root -g)/@mosaicstack/mosaic/framework/install.sh" ' +
|
||||
'(MOSAIC_SYNC_ONLY=1 MOSAIC_INSTALL_MODE=keep)',
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log('✔ Framework re-seeded.');
|
||||
if (reseed.skillSyncError) {
|
||||
console.error(` ⚠ Claude skill reconciliation skipped: ${reseed.skillSyncError}`);
|
||||
}
|
||||
const skillConflicts = reseed.skillSync?.conflicts ?? [];
|
||||
const skillChanges =
|
||||
(reseed.skillSync?.registered.length ?? 0) + (reseed.skillSync?.repaired.length ?? 0);
|
||||
if (skillChanges > 0) {
|
||||
console.log(`✔ Registered ${skillChanges.toString()} Mosaic skill(s) with Claude Code.`);
|
||||
}
|
||||
for (const conflict of skillConflicts) {
|
||||
console.error(` ⚠ Skill registration skipped for ${conflict.name}: ${conflict.reason}`);
|
||||
}
|
||||
// Propagate shipped systemd unit fixes to the ACTIVE units (re-seed only
|
||||
// touches ~/.config/mosaic/systemd/user; systemd runs ~/.config/systemd/user).
|
||||
const units = refreshActiveFleetUnits();
|
||||
if (units.refreshed.length > 0) {
|
||||
console.log(`✔ Refreshed ${units.refreshed.length} active systemd unit(s).`);
|
||||
}
|
||||
const agents = readRosterAgentNames();
|
||||
if (agents.length === 0) return;
|
||||
if (opts.relaunch) {
|
||||
console.log(`\nRelaunching ${agents.length} fleet agent(s) to pick up the new runtime…`);
|
||||
for (const restart of buildRelaunchCommands(agents)) {
|
||||
try {
|
||||
execSync(restart.join(' '), { stdio: 'inherit', timeout: 30_000 });
|
||||
} catch {
|
||||
console.error(` ⚠ failed to restart agent — run: ${restart.join(' ')}`);
|
||||
}
|
||||
}
|
||||
console.log('✔ Agents relaunched.');
|
||||
} else {
|
||||
console.log(
|
||||
`\nℹ ${agents.length} fleet agent(s) are still running the previous runtime. ` +
|
||||
'Restart them to activate the update:\n mosaic update --relaunch ' +
|
||||
'(or: mosaic fleet restart <agent>)',
|
||||
);
|
||||
const flow = runUpdateReseedFlow(reason, {
|
||||
reseed: opts.reseed,
|
||||
relaunch: opts.relaunch,
|
||||
allowInactiveEnforcement: opts.allowInactiveEnforcement === true,
|
||||
});
|
||||
if (flow.settingsGuard?.ran && flow.settingsGuard.result?.exitCode === 1) {
|
||||
// Fail-loud: enforcement hooks were refused/stripped. Surface this
|
||||
// in the command's own exit status without aborting the rest of
|
||||
// the update (mirrors mosaic-link-runtime-assets' guard_degraded).
|
||||
process.exitCode = 1;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -544,7 +514,7 @@ program
|
||||
// package is reported outdated. Detect that via the framework version and
|
||||
// re-seed so shipped launcher/runtime fixes still activate.
|
||||
const drift = checkFrameworkDrift();
|
||||
if (drift.drifted && opts.reseed !== false) {
|
||||
if (drift.drifted) {
|
||||
reseedFramework(
|
||||
`\nFramework drift detected (on-disk v${drift.installed} < bundled v${drift.bundled}) — ` +
|
||||
'the CLI was updated outside `mosaic update`. Re-seeding framework files into ' +
|
||||
@@ -582,7 +552,7 @@ program
|
||||
(r: { package: string }) => r.package === FRAMEWORK_RESEED_PACKAGE,
|
||||
);
|
||||
const drift = checkFrameworkDrift();
|
||||
if ((mosaicUpdated || drift.drifted) && opts.reseed !== false) {
|
||||
if (mosaicUpdated || drift.drifted) {
|
||||
reseedFramework(
|
||||
'\nRe-seeding framework files into ~/.config/mosaic (data-safe; keeps your edits)…',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user