everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
153 lines (151 loc) • 5.65 kB
JavaScript
const require_services_registry_fastkv = require('../registry/fastkv.cjs');
const require_manifest = require('../platform/manifest.cjs');
const require_boot_check = require('../resolution/boot-check.cjs');
const require_rollback = require('./rollback.cjs');
const require_train = require('./train.cjs');
//#region src/services/deploy/rollback-flow.ts
async function fetchRollbackHistory(input) {
return require_services_registry_fastkv.fetchConfigHistory({
accountId: input.account,
gatewayId: input.domain,
registry: input.registry,
limit: input.limit
});
}
/** The live config, or undefined when the registry read fails (an unknown
* live state never blocks a rollback — the snapshot publish is idempotent). */
async function fetchLiveConfig(input) {
try {
return await require_services_registry_fastkv.fetchBosConfigFromFastKv(`bos://${input.account}/${input.domain}`, input.registry);
} catch {
return;
}
}
function isSnapshotLive(targetValue, liveValue) {
return JSON.stringify(liveValue) === JSON.stringify(targetValue);
}
/** Stamp + republish the target snapshot verbatim. Callers own verification:
* the smoke protocol refuses unverified targets via
* `rollbackToPreviousSnapshot`; the command runs its two-stage check and
* passes `skipVerify` once it has vetted (or --forced) the snapshot. */
async function publishRollbackSnapshot(targetValue, input, opts) {
if (!opts?.skipVerify) {
const verification = await require_rollback.verifyRollbackSnapshot(targetValue);
if (!verification.ok && verification.verifiable) return {
ok: false,
registryUrl: "",
error: "rollback refused — a pinned slot's bytes are gone or no longer match their SRI"
};
}
const bootCheck = await require_boot_check.verifyBootPayload(targetValue, {
account: input.account,
gateway: input.domain
}, { ...input.registry ? { registry: input.registry } : {} });
if (!bootCheck.ok) {
const detail = bootCheck.issues.map((issue) => `${issue.path ? `${issue.path}: ` : ""}${issue.message}`).join("; ");
return {
ok: false,
registryUrl: "",
error: `rollback refused — the target snapshot would not boot${bootCheck.chain.length > 0 ? ` (extends chain: ${bootCheck.chain.join(" -> ")})` : ""}: ${detail}`
};
}
const payload = require_rollback.buildRollbackPayload(targetValue, (/* @__PURE__ */ new Date()).toISOString());
const result = await require_train.publishToFastKv({
bosConfig: payload,
runtimeConfig: input.runtimeConfig,
configDir: input.configDir,
env: input.env,
build: false,
dryRun: input.dryRun ?? false,
verbose: input.verbose ?? false,
packages: "all",
network: input.network,
privateKey: input.privateKey,
wallet: input.wallet ?? false,
registry: input.registry,
exactPayload: true,
...opts?.restorePlatformPointer ? { restorePlatformPointer: opts.restorePlatformPointer } : {}
});
if (result.status === "dry-run") return {
ok: true,
registryUrl: result.registryUrl,
dryRun: true
};
if (result.status === "error") return {
ok: false,
registryUrl: result.registryUrl,
error: `rollback publish failed: ${result.error ?? result.status}`
};
return {
ok: true,
registryUrl: result.registryUrl,
txHash: result.txHash
};
}
/** The platform-pointer restore for a rollback (#573): the pointer's own
* history[1] is the pre-release manifest — the same "previous entry" logic
* the config rollback uses. Best-effort: a failed lookup returns undefined
* and the drift alarm (#571's cross-check) catches any residue loudly. */
async function resolvePointerRestore(account, domain, registry) {
try {
const previousPointer = (await require_services_registry_fastkv.fetchConfigHistory({
accountId: account,
gatewayId: domain,
registry,
limit: 5,
keyPath: "platform.json"
}))[1];
if (!previousPointer) return void 0;
return {
key: require_manifest.getPlatformManifestKey(account, domain),
value: JSON.stringify(previousPointer.value)
};
} catch {
console.warn(" platform pointer restore unavailable — the drift alarm will catch any residue");
return;
}
}
/** The post-publish smoke failure protocol (ADR 0021): roll the registry
* config back to the previous snapshot — a red boot gate must never leave a
* new config live against an old image. #573: the platform pointer rolls
* back in the same atomic write. */
async function rollbackToPreviousSnapshot(input) {
let history;
try {
history = await fetchRollbackHistory({
account: input.account,
domain: input.domain,
registry: input.registry,
limit: 20
});
} catch (error) {
return {
ok: false,
registryUrl: "",
error: `fetching publish history failed: ${error instanceof Error ? error.message : String(error)}`
};
}
const previous = history[1];
if (!previous) return {
ok: false,
registryUrl: "",
error: "no previous snapshot in the publish history — nothing to roll back to"
};
const restorePlatformPointer = await resolvePointerRestore(input.account, input.domain, input.registry);
const result = await publishRollbackSnapshot(previous.value, input, { restorePlatformPointer });
if (result.ok) return {
ok: true,
blockHeight: String(previous.blockHeight),
registryUrl: result.registryUrl,
txHash: result.txHash
};
return result;
}
//#endregion
exports.fetchLiveConfig = fetchLiveConfig;
exports.fetchRollbackHistory = fetchRollbackHistory;
exports.isSnapshotLive = isSnapshotLive;
exports.publishRollbackSnapshot = publishRollbackSnapshot;
exports.resolvePointerRestore = resolvePointerRestore;
exports.rollbackToPreviousSnapshot = rollbackToPreviousSnapshot;
//# sourceMappingURL=rollback-flow.cjs.map