chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// Applies pending changesets, then checks that the version actually moved the
// way the changesets asked.
//
// This exists because it did not. A release carrying one minor and one patch
// changeset — no `major` anywhere, and no "### Major Changes" in any changelog —
// published as 2.0.0. The cause was `@pdsjs/spaces` sitting in the fixed group:
// with it there, every bump escalated to major, and the only visible sign was
// the version number in the success output, after publishing.
//
// Config alone would not have caught that, because the failure was silent. This
// makes it loud and puts it before `npm run release` rather than after.
import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
const RANK = { patch: 1, minor: 2, major: 3 };
const CORE = new URL('../packages/core/package.json', import.meta.url);
/** @param {URL} url */
const versionOf = (url) => JSON.parse(readFileSync(url, 'utf8')).version;
/**
* Highest bump any pending changeset asks for.
* @returns {'patch'|'minor'|'major'|null} Null when nothing is pending.
*/
function requestedBump() {
const dir = new URL('../.changeset/', import.meta.url);
let highest = null;
for (const file of readdirSync(dir)) {
if (!file.endsWith('.md') || file === 'README.md') continue;
const text = readFileSync(new URL(file, dir), 'utf8');
const frontmatter = text.split('---')[1] ?? '';
for (const match of frontmatter.matchAll(/:\s*(patch|minor|major)\s*$/gm)) {
const bump = /** @type {'patch'|'minor'|'major'} */ (match[1]);
if (!highest || RANK[bump] > RANK[highest]) highest = bump;
}
}
return highest;
}
/**
* How a version actually moved.
* @param {string} before
* @param {string} after
*/
function actualBump(before, after) {
const [bMaj, bMin] = before.split('.').map(Number);
const [aMaj, aMin] = after.split('.').map(Number);
if (aMaj > bMaj) return 'major';
if (aMin > bMin) return 'minor';
return 'patch';
}
const wanted = requestedBump();
if (!wanted) {
console.error('No pending changesets — nothing to version.');
process.exit(1);
}
const before = versionOf(CORE);
execFileSync('pnpm', ['exec', 'changeset', 'version'], { stdio: 'inherit' });
const after = versionOf(CORE);
const got = actualBump(before, after);
if (RANK[got] > RANK[wanted]) {
console.error(
`\nRefusing to continue: @pdsjs/core went ${before} -> ${after}, a ${got} ` +
`bump, but the highest any changeset asked for was ${wanted}.\n\n` +
'Something in .changeset/config.json is escalating the bump — a package ' +
'in the fixed group that drags the rest is the usual cause. The working ' +
'tree now holds the wrong versions: run `git checkout -- . && git clean ' +
'-fd packages` before trying again.\n',
);
process.exit(1);
}
console.log(`\n@pdsjs/core ${before} -> ${after} (${got}, as requested)`);
execFileSync('node', ['scripts/sync-version.js'], { stdio: 'inherit' });
execFileSync('pnpm', ['install', '--lockfile-only'], { stdio: 'inherit' });