~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
78 lines · 3.0 KB Raw History
// 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' });