bump-release.mjs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #!/usr/bin/env node
  2. /**
  3. * Bump every package (workspace root + packages/*) to one version, refresh
  4. * the lockfile, and verify. Usage: `pnpm release:bump <major|minor|patch|x.y.z>`.
  5. */
  6. import fs from 'node:fs';
  7. import path from 'node:path';
  8. import { spawnSync } from 'node:child_process';
  9. import { packageDirs, readJson, root } from './repo.mjs';
  10. const bump = process.argv[2];
  11. const releaseTypes = new Set(['major', 'minor', 'patch']);
  12. function writeJson(file, value) {
  13. fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
  14. }
  15. function run(command, args) {
  16. const result = spawnSync(command, args, {
  17. cwd: root,
  18. stdio: 'inherit',
  19. env: { ...process.env, CI: 'true' },
  20. });
  21. if (result.error) throw result.error;
  22. if (result.status !== 0) {
  23. process.exit(result.status ?? 1);
  24. }
  25. }
  26. function packageFiles() {
  27. return ['package.json', ...packageDirs().map((dir) => path.join(dir, 'package.json'))];
  28. }
  29. function parseVersion(version) {
  30. const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
  31. if (!match) {
  32. throw new Error(`increment types need a plain x.y.z current version (current: ${version}) — pass an explicit target version instead`);
  33. }
  34. return match.slice(1).map((part) => Number(part));
  35. }
  36. /** Explicit target versions accept full semver, prereleases included (test publishes). */
  37. const EXPLICIT_VERSION = /^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/;
  38. function nextVersion(current, release) {
  39. if (EXPLICIT_VERSION.test(release)) return release;
  40. if (!releaseTypes.has(release)) {
  41. throw new Error('Usage: pnpm release:bump <major|minor|patch|x.y.z>');
  42. }
  43. const [major, minor, patch] = parseVersion(current);
  44. if (release === 'major') return `${major + 1}.0.0`;
  45. if (release === 'minor') return `${major}.${minor + 1}.0`;
  46. return `${major}.${minor}.${patch + 1}`;
  47. }
  48. function currentPublishedVersion(files) {
  49. const versions = new Set(
  50. files
  51. .filter((file) => file.startsWith('packages/'))
  52. .map((file) => readJson(path.join(root, file)).version),
  53. );
  54. if (versions.size !== 1) {
  55. throw new Error(`published package versions differ: ${[...versions].join(', ')}`);
  56. }
  57. return [...versions][0];
  58. }
  59. if (!bump) {
  60. console.error('Usage: pnpm release:bump <major|minor|patch|x.y.z>');
  61. process.exit(1);
  62. }
  63. const files = packageFiles();
  64. const targetVersion = nextVersion(currentPublishedVersion(files), bump);
  65. for (const file of files) {
  66. const fullPath = path.join(root, file);
  67. const json = readJson(fullPath);
  68. json.version = targetVersion;
  69. writeJson(fullPath, json);
  70. console.log(`${file}: ${targetVersion}`);
  71. }
  72. run('pnpm', ['install', '--ignore-scripts', '--lockfile-only']);
  73. run('node', ['./scripts/verify-release.mjs']);
  74. console.log(`Release version bumped to ${targetVersion}`);