Skip to content

Commit d9efe69

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

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-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: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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(
76+
"Usage: node scripts/npmRelease.js --version <version> --tag <tag> [--otp <code>] [--dry-run]",
77+
);
78+
process.exitCode = 1;
79+
return;
80+
}
81+
const otp = await promptForOtp(values.otp);
82+
if (!otp) {
83+
throw new Error("OTP is required to publish dist-tags.");
84+
}
85+
for (const pkg of packages) {
86+
await runDistTag(
87+
pkg,
88+
values.version,
89+
values.tag,
90+
otp,
91+
Boolean(values["dry-run"]),
92+
);
93+
}
94+
if (values["dry-run"]) {
95+
console.log("Dry run complete.");
96+
} else {
97+
console.log("All packages tagged successfully.");
98+
}
99+
} catch (error) {
100+
console.error(error.message || error);
101+
process.exitCode = 1;
102+
}
103+
}
104+
105+
await main();

0 commit comments

Comments
 (0)