#!/usr/bin/env node // Cut a release: apply the pending changesets, commit, tag and push. Pushing // the tag to knotless runs the release workflows under .knotless/workflows/, // so this command is the whole human side of a release. // // npm run release:cut // // Refuses to run on a dirty tree, off main, or with nothing to release. import { execFileSync } from 'node:child_process'; import { readdirSync, readFileSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** @param {string} cmd @param {string[]} args */ function run(cmd, args) { return execFileSync(cmd, args, { cwd: repoRoot, encoding: 'utf8' }).trim(); } /** @param {string} cmd @param {string[]} args */ function show(cmd, args) { console.log(`> ${cmd} ${args.join(' ')}`); execFileSync(cmd, args, { cwd: repoRoot, stdio: 'inherit' }); } const branch = run('git', ['branch', '--show-current']); if (branch !== 'main') { console.error(`On ${branch || 'a detached HEAD'}; releases cut from main.`); process.exit(1); } if (run('git', ['status', '--porcelain']) !== '') { console.error('The tree is dirty; commit or stash first.'); process.exit(1); } show('git', ['pull', '--ff-only']); const pending = readdirSync(join(repoRoot, '.changeset')).filter( (f) => f.endsWith('.md') && f !== 'README.md', ); if (pending.length === 0) { console.error('No pending changesets; nothing to release.'); process.exit(1); } console.log(`${pending.length} changeset(s) pending.`); show('npm', ['run', 'version-packages']); const { version } = JSON.parse( readFileSync(join(repoRoot, 'packages/core/package.json'), 'utf8'), ); const tag = `v${version}`; if (run('git', ['tag', '--list', tag]) !== '') { console.error(`${tag} already exists; the group version did not move.`); process.exit(1); } show('git', ['add', '-A']); show('git', ['commit', '-m', `chore: version packages for ${version}`]); show('git', ['tag', tag]); show('git', ['push', 'knotless://chadtmiller.com/pds.js', 'main', tag]); console.log(`\n${tag} pushed. The runner takes it from here:`); console.log(' release-npm -> npm packages'); console.log(' release-image -> the PDS container image'); console.log(' release-runner-image -> the check runner container image'); console.log( ' release-worker -> the worker artifact on the artifacts site', );