diff --git a/scripts/npmRelease.js b/scripts/npmRelease.js index a6a1313283..a2976ffb09 100755 --- a/scripts/npmRelease.js +++ b/scripts/npmRelease.js @@ -5,29 +5,18 @@ * Usage: * node scripts/npmRelease.js --version 12.0.1 --tag next * node scripts/npmRelease.js --version 12.0.1 --tag latest --otp 123456 + * node scripts/npmRelease.js --version 12.0.1 --tag latest --token * * - Runs `npm dist-tag add` for every non-private workspace (same as CI publish) - * reusing the same OTP so you only get prompted once. + * reusing the same OTP (when provided) so you only get prompted once. + * - If no OTP is provided, npm uses the current auth flow (for example passkey). + * - Token auth can be passed with `--token` or via `NODE_AUTH_TOKEN`/`NPM_TOKEN`. * - Pass `--dry-run` to see the commands without executing them. */ import process from "node:process"; -import readline from "node:readline/promises"; import { parseArgs } from "node:util"; import { npm, yarn } from "../lib_dev/process.js"; -async function promptForOtp(existingOtp) { - if (existingOtp) { - return existingOtp; - } - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - const answer = await rl.question("npm one-time password: "); - rl.close(); - return answer.trim(); -} - async function getPublicWorkspaces() { const { stdout } = await yarn("workspaces", [ "list", @@ -41,15 +30,40 @@ async function getPublicWorkspaces() { .map(entry => entry.name); } -async function runDistTag(pkgName, version, tag, otp, dryRun) { +function resolveAuth(values) { + const otp = values.otp?.trim() || undefined; + const token = + values.token?.trim() || + process.env.NODE_AUTH_TOKEN?.trim() || + process.env.NPM_TOKEN?.trim() || + undefined; + + if (otp && token) { + throw new Error("Use either --otp or token auth, not both."); + } + + return { otp, token }; +} + +async function runDistTag(pkgName, version, tag, otp, token, dryRun) { const spec = `${pkgName}@${version}`; - const args = ["dist-tag", "add", spec, tag, "--otp", otp]; + const args = ["dist-tag", "add", spec, tag]; + if (otp) { + args.push("--otp", otp); + } if (dryRun) { console.log(`[dry-run] npm ${args.join(" ")}`); return; } console.log(`Tagging ${spec} as ${tag}...`); - await npm("dist-tag", ["add", spec, tag, "--otp", otp], { + const env = token + ? { + ...process.env, + NODE_AUTH_TOKEN: token, + } + : process.env; + await npm("dist-tag", args.slice(1), { + env, stdio: "inherit", throwOnFail: true, }); @@ -64,12 +78,13 @@ async function main() { version: { type: "string", short: "v" }, tag: { type: "string", short: "t" }, otp: { type: "string" }, + token: { type: "string" }, "dry-run": { type: "boolean" }, }, }); if (!values.version || !values.tag) { console.error( - "Usage: node scripts/npmRelease.js --version --tag [--otp ] [--dry-run]", + "Usage: node scripts/npmRelease.js --version --tag [--otp | --token ] [--dry-run]", ); process.exitCode = 1; return; @@ -79,16 +94,14 @@ async function main() { throw new Error("No public workspaces found."); } - const otp = await promptForOtp(values.otp); - if (!otp) { - throw new Error("OTP is required to publish dist-tags."); - } + const { otp, token } = resolveAuth(values); for (const workspace of workspaces) { await runDistTag( workspace, values.version, values.tag, otp, + token, Boolean(values["dry-run"]), ); }