Skip to content

Commit 5bd217d

Browse files
committed
Add script for tagging all npm packages for a release
1 parent 3027d26 commit 5bd217d

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ To build a new version and release it on NPM, follow these steps:
447447
1. Verify that the playground bundle for the new version is now present on the settings tab in https://rescript-lang.org/try.
448448
1. Run `npm info rescript` to verify that the new version is now present with tag "ci".
449449
1. Test the new version.
450-
1. Tag the new version as appropriate (`latest` or `next`): `npm dist-tag add rescript@<version> <tag>`
450+
1. Tag all packages for the new version as appropriate (`latest` or `next`): `./scripts/npmRelease.js --version <version> --tag <tag>`
451451
1. Create a release entry for the version tag on the [Github Releases page](https://github.com/rescript-lang/rescript-compiler/releases), copying the changes from `CHANGELOG.md`.
452452
1. Create a PR with the following changes to prepare for development of the next version:
453453
- Increment the `EXPECTED_VERSION` number in `yarn.config.cjs` for the next version.

scripts/npmRelease.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Tag a published version of the main ReScript packages with a given dist-tag.
4+
*
5+
* Usage:
6+
* node scripts/npmRelease.js --version 12.0.1 --tag next
7+
* node scripts/npmRelease.js --version 12.0.1 --tag latest --otp 123456
8+
*
9+
* - Runs `npm dist-tag add` for: rescript, @rescript/runtime, and all platform
10+
* optional packages, reusing the same OTP so you only get prompted once.
11+
* - Pass `--dry-run` to see the commands without executing them.
12+
*/
13+
import { spawn } from 'node:child_process';
14+
import process from 'node:process';
15+
import readline from 'node:readline/promises';
16+
import { parseArgs } from 'node:util';
17+
18+
const packages = [
19+
'rescript',
20+
'@rescript/runtime',
21+
'@rescript/darwin-arm64',
22+
'@rescript/darwin-x64',
23+
'@rescript/linux-arm64',
24+
'@rescript/linux-x64',
25+
'@rescript/win32-x64',
26+
];
27+
28+
async function promptForOtp(existingOtp) {
29+
if (existingOtp) {
30+
return existingOtp;
31+
}
32+
const rl = readline.createInterface({
33+
input: process.stdin,
34+
output: process.stdout,
35+
});
36+
const answer = await rl.question('npm one-time password: ');
37+
rl.close();
38+
return answer.trim();
39+
}
40+
41+
async function runDistTag(pkg, version, tag, otp, dryRun) {
42+
const spec = `${pkg}@${version}`;
43+
const args = ['dist-tag', 'add', spec, tag, '--otp', otp];
44+
if (dryRun) {
45+
console.log(`[dry-run] npm ${args.join(' ')}`);
46+
return;
47+
}
48+
console.log(`Tagging ${spec} as ${tag}...`);
49+
await new Promise((resolve, reject) => {
50+
const child = spawn('npm', args, { stdio: 'inherit' });
51+
child.on('exit', (code) => {
52+
if (code === 0) {
53+
resolve();
54+
} else {
55+
reject(new Error(`npm dist-tag failed for ${spec} (exit ${code})`));
56+
}
57+
});
58+
child.on('error', reject);
59+
});
60+
}
61+
62+
async function main() {
63+
try {
64+
const { values } = parseArgs({
65+
args: process.argv.slice(2),
66+
strict: true,
67+
options: {
68+
version: { type: 'string', short: 'v' },
69+
tag: { type: 'string', short: 't' },
70+
otp: { type: 'string' },
71+
'dry-run': { type: 'boolean' },
72+
},
73+
});
74+
if (!values.version || !values.tag) {
75+
console.error('Usage: node scripts/npmRelease.js --version <version> --tag <tag> [--otp <code>] [--dry-run]');
76+
process.exitCode = 1;
77+
return;
78+
}
79+
const otp = await promptForOtp(values.otp);
80+
if (!otp) {
81+
throw new Error('OTP is required to publish dist-tags.');
82+
}
83+
for (const pkg of packages) {
84+
await runDistTag(pkg, values.version, values.tag, otp, Boolean(values['dry-run']));
85+
}
86+
if (values['dry-run']) {
87+
console.log('Dry run complete.');
88+
} else {
89+
console.log('All packages tagged successfully.');
90+
}
91+
} catch (error) {
92+
console.error(error.message || error);
93+
process.exitCode = 1;
94+
}
95+
}
96+
97+
await main();

0 commit comments

Comments
 (0)