release: a cancelled parent run is not a failure (a version-only child runs everything instead of stopping at the gate); a queued run gets 20 min before 'no ci run'; release.ts --resume continues a release that stopped after its push (the history rewrite re-pushed every old tag, their release.yml runs took the runners and cancelled 0.8.5's parent)
This commit is contained in:
parent
a64a7f198d
commit
77f141e223
3 changed files with 29 additions and 15 deletions
|
|
@ -7,6 +7,8 @@
|
|||
// rehearsal/<n>: a prerelease on git.crt.fyi and the rehearsal
|
||||
// channel of storebound.dev (no app reads it), never npm and never
|
||||
// /Applications; everything it made is deleted at the end
|
||||
// bun tools/release.ts <version> --resume --yes after a stop past the push: HEAD is origin/main, tagged v<version>,
|
||||
// with that version; skips the bump and continues from step 4
|
||||
// bun tools/release.ts mac-acceptance the macOS acceptance runs for this checkout's CLI key (cached)
|
||||
//
|
||||
// Without --yes: the checks and the plan only. With --yes, in order:
|
||||
|
|
@ -224,12 +226,15 @@ async function main(): Promise<void> {
|
|||
const yes = has('--yes');
|
||||
if (argv[0] === 'mac-acceptance') { const key = cliKey(ROOT); console.log(await macAcceptance(key, join(RELEASES, 'logs', `acceptance-${key}`))); return; }
|
||||
const rehearsal = has('--rehearsal');
|
||||
const resume = has('--resume');
|
||||
if (resume && rehearsal) fail('--resume is for a real version');
|
||||
const positional = argv.filter(a => !a.startsWith('--'));
|
||||
const current = COPIES[0]!.read(readFileSync(join(ROOT, 'package.json'), 'utf8'))!;
|
||||
const pkgVersion = COPIES[0]!.read(readFileSync(join(ROOT, 'package.json'), 'utf8'))!;
|
||||
const current = pkgVersion;
|
||||
const n = rehearsal ? Number(positional[0] ?? Math.floor(Date.now() / 1000)) : 0;
|
||||
const version = rehearsal ? `0.0.0-rehearsal.${n}` : positional[0] ?? fail('usage: bun tools/release.ts <version> [--yes] | --rehearsal [<n>] [--yes]');
|
||||
if (!SEMVER.test(version)) fail(`${version} is not a version`);
|
||||
if (!rehearsal && !newer(version, current)) fail(`${version} is not newer than ${current}`);
|
||||
if (!rehearsal && !resume && !newer(version, current)) fail(`${version} is not newer than ${current}`);
|
||||
const tag = `v${version}`;
|
||||
const branch = rehearsal ? `rehearsal/${n}` : 'main';
|
||||
const out = join(RELEASES, 'desktop', version);
|
||||
|
|
@ -240,14 +245,19 @@ async function main(): Promise<void> {
|
|||
const remote = remoteSessionReason(process.env, sh(['launchctl', 'managername'], { allowFail: true }).trim());
|
||||
if (remote) fail(`${remote}: signing and notarization need the owner's logged-in macOS session`);
|
||||
if (!existsSync(join(KEYS, 'updater.key'))) fail(`no updater key in ${KEYS.replace(HOME, '~')}`);
|
||||
if (sh(['git', 'status', '--porcelain']).trim()) fail('the checkout has uncommitted changes');
|
||||
// --resume may run with a fixed release tool (tools/release*.ts), which is not part of what ships
|
||||
const dirty = sh(['git', 'status', '--porcelain']).split('\n').filter(l => l.trim() && !(resume && /^ M tools\/release(-candidate)?\.ts$/.test(l)));
|
||||
if (dirty.length) fail('the checkout has uncommitted changes');
|
||||
sh(['git', 'fetch', '--quiet', 'origin', 'main', '--tags']);
|
||||
const head = sh(['git', 'rev-parse', 'HEAD']).trim();
|
||||
if (head !== sh(['git', 'rev-parse', 'origin/main']).trim()) fail(`HEAD ${head.slice(0, 10)} is not origin/main: release from main as it is on git.crt.fyi (pull / push first), so the bump's parent is a candidate`);
|
||||
if (!rehearsal && sh(['git', 'rev-parse', '-q', '--verify', `refs/tags/${tag}`], { allowFail: true }).trim()) fail(`tag ${tag} exists already`);
|
||||
const parent = await status(head);
|
||||
if (parent.state === 'red') fail(`origin/main ${head.slice(0, 10)} is red (${parent.failed.join(', ')}): ${parent.run}`);
|
||||
if (parent.state === 'none') fail(`origin/main ${head.slice(0, 10)} has no ci run: its candidate is needed (push to main runs ci.yml)`);
|
||||
const tagged = sh(['git', 'rev-parse', '-q', '--verify', `refs/tags/${tag}^{commit}`], { allowFail: true }).trim();
|
||||
if (resume) {
|
||||
if (tagged !== head || pkgVersion !== version) fail(`--resume needs HEAD = origin/main tagged ${tag} with version ${version} (HEAD ${head.slice(0, 10)}, ${tag} ${tagged.slice(0, 10) || 'missing'}, package.json ${pkgVersion})`);
|
||||
} else if (!rehearsal && tagged) fail(`tag ${tag} exists already (--resume continues a release that stopped after its push)`);
|
||||
const parent = await status(resume ? sh(['git', 'rev-parse', 'HEAD~1']).trim() : head);
|
||||
if (parent.state === 'red' && !resume) fail(`origin/main ${head.slice(0, 10)} is red (${parent.failed.join(', ')}): ${parent.run}`);
|
||||
if (parent.state === 'none' && !resume) fail(`origin/main ${head.slice(0, 10)} has no ci run: its candidate is needed (push to main runs ci.yml)`);
|
||||
say(`storebound ${current} -> ${version}${rehearsal ? ' (rehearsal: prerelease, rehearsal channel, deleted at the end)' : ''}; parent ${head.slice(0, 10)} is ${parent.state}${parent.state === 'pending' ? ` (${parent.run})` : ''}`);
|
||||
// the release commit's author is the one of the commits already on main (the project's identity)
|
||||
const identity = sh(['git', 'log', '-1', '--format=%an <%ae>', 'origin/main']).trim();
|
||||
|
|
@ -265,8 +275,8 @@ async function main(): Promise<void> {
|
|||
|
||||
// ---------------------------------------------------------------- 2. + 3. bump, gate, commit, push
|
||||
const startBranch = sh(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).trim();
|
||||
let sha = '';
|
||||
await phase('bump + fast gate + commit + push', async () => {
|
||||
let sha = resume ? head : '';
|
||||
if (!resume) await phase('bump + fast gate + commit + push', async () => {
|
||||
if (rehearsal) sh(['git', 'checkout', '-q', '-b', branch]);
|
||||
setVersion(version);
|
||||
if (!rehearsal) writeFileSync(join(ROOT, 'CHANGELOG.md'), cutChangelog(readFileSync(join(ROOT, 'CHANGELOG.md'), 'utf8'), version, new Date().toISOString().slice(0, 10)));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue