feat(desktop): add time-based staged rollout for stable updates
Linear ramp from 0% at publish to 100% at releaseDate + rolloutHours (default 24h, configurable per release). Beta channel and rolloutHours=0 short-circuit to admit everyone. Per-machine bucket derives from a persistent UUID at <userData>/.updaterId and feeds electron-updater's isUserWithinRollout hook. Adds desktop-rollout.yml workflow for in-place rolloutHours edits on already-published releases (hotfix to 0 or extend the ramp), serialized against finalize-rollout in desktop-release.yml via a shared concurrency group keyed on the tag. Replaces the hand-rolled mac manifest merge with a js-yaml round-trip that preserves unknown fields.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import fs from "node:fs";
|
||||
import { dump, load } from "js-yaml";
|
||||
|
||||
export function stampRollout({ releaseDate, rolloutHours }, paths) {
|
||||
if (releaseDate === undefined && rolloutHours === undefined) {
|
||||
throw new Error("stampRollout requires at least one of releaseDate or rolloutHours");
|
||||
}
|
||||
for (const filePath of paths) {
|
||||
const manifest = load(fs.readFileSync(filePath, "utf8")) ?? {};
|
||||
const next = { ...manifest };
|
||||
if (releaseDate !== undefined) next.releaseDate = releaseDate;
|
||||
if (rolloutHours !== undefined) next.rolloutHours = rolloutHours;
|
||||
fs.writeFileSync(filePath, dump(next, { lineWidth: -1, noRefs: true }));
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const opts = {};
|
||||
const paths = [];
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const arg = argv[i];
|
||||
if (arg === "--release-date") {
|
||||
opts.releaseDate = argv[++i];
|
||||
} else if (arg === "--rollout-hours") {
|
||||
const raw = argv[++i];
|
||||
const parsed = Number(raw);
|
||||
if (!Number.isFinite(parsed) || parsed < 0) {
|
||||
throw new Error(`--rollout-hours must be a non-negative number, got ${raw}`);
|
||||
}
|
||||
opts.rolloutHours = parsed;
|
||||
} else {
|
||||
paths.push(arg);
|
||||
}
|
||||
}
|
||||
return { opts, paths };
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const { opts, paths } = parseArgs(process.argv.slice(2));
|
||||
if (paths.length === 0 || (opts.releaseDate === undefined && opts.rolloutHours === undefined)) {
|
||||
throw new Error(
|
||||
"Usage: node scripts/stamp-rollout.mjs [--release-date <iso>] [--rollout-hours <n>] <yaml-path>...",
|
||||
);
|
||||
}
|
||||
stampRollout(opts, paths);
|
||||
}
|
||||
Reference in New Issue
Block a user