From 3a0650269088abf829e53d690b7fb88c6e2cac6e Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 10 Oct 2025 17:07:59 +0200 Subject: [PATCH 1/2] chore: script --- package-lock.json | 3 +- package.json | 3 +- .../monorepo-tools/bin/request-npm-token.js | 3 + packages/monorepo-tools/package.json | 3 +- packages/monorepo-tools/src/index.ts | 1 + .../monorepo-tools/src/request-npm-token.ts | 69 +++++++++++++++++++ .../src/utils/get-npm-token-list.ts | 43 ++++++++++++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100755 packages/monorepo-tools/bin/request-npm-token.js create mode 100644 packages/monorepo-tools/src/request-npm-token.ts create mode 100644 packages/monorepo-tools/src/utils/get-npm-token-list.ts diff --git a/package-lock.json b/package-lock.json index a0b7f40f..957a74c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31074,7 +31074,8 @@ "bump-monorepo-packages": "bin/bump-packages.js", "depalign": "bin/depalign.js", "monorepo-where": "bin/where.js", - "precommit": "bin/precommit.js" + "precommit": "bin/precommit.js", + "request-npm-token": "bin/request-npm-token.js" }, "devDependencies": { "@mongodb-js/eslint-config-devtools": "0.9.12", diff --git a/package.json b/package.json index 182378a6..75f968e4 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "test-changed": "lerna run test --stream --concurrency 1 --since origin/HEAD", "test-ci": "lerna run test-ci --concurrency 1", "test": "lerna run test --concurrency 1 --stream", - "where": "node ./scripts/src/where.js" + "where": "node ./scripts/src/where.js", + "request-npm-token": "request-npm-token" }, "dependencies": { "@mongodb-js/monorepo-tools": "^1.1.18" diff --git a/packages/monorepo-tools/bin/request-npm-token.js b/packages/monorepo-tools/bin/request-npm-token.js new file mode 100755 index 00000000..0736f736 --- /dev/null +++ b/packages/monorepo-tools/bin/request-npm-token.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +'use strict'; +require('../dist/request-npm-token.js'); diff --git a/packages/monorepo-tools/package.json b/packages/monorepo-tools/package.json index 538a9925..751ceaad 100644 --- a/packages/monorepo-tools/package.json +++ b/packages/monorepo-tools/package.json @@ -26,7 +26,8 @@ "precommit": "./bin/precommit.js", "depalign": "./bin/depalign.js", "monorepo-where": "./bin/where.js", - "bump-monorepo-packages": "./bin/bump-packages.js" + "bump-monorepo-packages": "./bin/bump-packages.js", + "request-npm-token": "./bin/request-npm-token.js" }, "scripts": { "bootstrap": "npm run compile", diff --git a/packages/monorepo-tools/src/index.ts b/packages/monorepo-tools/src/index.ts index e07bbd69..64f8a2d3 100644 --- a/packages/monorepo-tools/src/index.ts +++ b/packages/monorepo-tools/src/index.ts @@ -7,3 +7,4 @@ export * from './utils/update-package-json'; export * from './utils/with-progress'; export * from './utils/workspace-dependencies'; export * from './utils/get-packages-in-topological-order'; +export * from './utils/get-npm-token-list'; diff --git a/packages/monorepo-tools/src/request-npm-token.ts b/packages/monorepo-tools/src/request-npm-token.ts new file mode 100644 index 00000000..d0a22a98 --- /dev/null +++ b/packages/monorepo-tools/src/request-npm-token.ts @@ -0,0 +1,69 @@ +#! /usr/bin/env node +/* eslint-disable no-console */ + +/** + * CLI command to get NPM token requirements for the current monorepo. + * + * Usage: + * npm run request-npm-token + * + * This will output the scopes and packages that need to be included in NPM token permissions. + */ + +import { getNpmTokenList } from './utils/get-npm-token-list'; + +async function main() { + try { + const requirements = await getNpmTokenList(); + + console.log( + 'Open an IAMSEC ticket with https://jira.mongodb.org/plugins/servlet/desk/portal/81/create/1380', + ); + + console.log('Use the following description for the ticket:'); + console.log('--------------------------------'); + console.log('Hello,'); + console.log( + 'We need to update the NPM token for publishing our packages. The token needs Read/Write/Publish access to:\n', + ); + + console.log('Following Scopes:'); + if (requirements.scopes.length > 0) { + requirements.scopes.forEach((scope) => { + console.log(scope); + }); + } else { + console.log('(none)'); + } + + console.log('\nFollowing Packages:'); + if (requirements.packages.length > 0) { + requirements.packages.forEach((pkg) => { + console.log(pkg); + }); + } else { + console.log('(none)'); + } + + console.log(''); + console.log('Please share it with our team lead: {TEAM LEADER NAME}'); + } catch (error) { + console.error( + 'Error:', + error instanceof Error ? error.message : String(error), + ); + process.exit(1); + } +} + +process.on('unhandledRejection', (err: Error) => { + console.error(); + console.error(err?.stack || err?.message || err); + process.exitCode = 1; +}); + +main().catch((err) => + process.nextTick(() => { + throw err; + }), +); diff --git a/packages/monorepo-tools/src/utils/get-npm-token-list.ts b/packages/monorepo-tools/src/utils/get-npm-token-list.ts new file mode 100644 index 00000000..1a4eaca5 --- /dev/null +++ b/packages/monorepo-tools/src/utils/get-npm-token-list.ts @@ -0,0 +1,43 @@ +import { listAllPackages } from './list-all-packages'; + +export interface NpmTokenRequirements { + scopes: string[]; + packages: string[]; +} + +/** + * Gets all package names and scopes from the current monorepo. + * Returns scoped packages as scopes and unscoped packages as individual packages. + */ +export async function getNpmTokenList(): Promise { + const allPackagesArr = []; + for await (const { packageJson } of listAllPackages()) { + // listAllPackages yields { name: string, ... } + if (packageJson && typeof packageJson?.name === 'string') { + allPackagesArr.push(packageJson.name); + } + } + + // Separate scoped and unscoped packages + const scopedPackages = allPackagesArr.filter( + (pkg) => typeof pkg === 'string' && pkg.startsWith('@'), + ); + const unscopedPackages = allPackagesArr.filter( + (pkg) => typeof pkg === 'string' && !pkg.startsWith('@'), + ); + + // Extract unique scopes from scoped packages + const scopes = [ + ...new Set( + scopedPackages.map((pkg) => { + const scope = pkg.split('/')[0]; + return `${scope}/*`; + }), + ), + ].sort(); + + // Sort unscoped packages + const packages = [...new Set(unscopedPackages)].sort(); + + return { scopes, packages }; +} From 9cf2f28e086ca38ed018d83c99302182f6cbeffc Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 10 Oct 2025 17:10:43 +0200 Subject: [PATCH 2/2] chore: deconstruct for cleanliness --- packages/monorepo-tools/src/request-npm-token.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/monorepo-tools/src/request-npm-token.ts b/packages/monorepo-tools/src/request-npm-token.ts index d0a22a98..ed76f544 100644 --- a/packages/monorepo-tools/src/request-npm-token.ts +++ b/packages/monorepo-tools/src/request-npm-token.ts @@ -14,7 +14,7 @@ import { getNpmTokenList } from './utils/get-npm-token-list'; async function main() { try { - const requirements = await getNpmTokenList(); + const { scopes, packages } = await getNpmTokenList(); console.log( 'Open an IAMSEC ticket with https://jira.mongodb.org/plugins/servlet/desk/portal/81/create/1380', @@ -28,8 +28,8 @@ async function main() { ); console.log('Following Scopes:'); - if (requirements.scopes.length > 0) { - requirements.scopes.forEach((scope) => { + if (scopes.length > 0) { + scopes.forEach((scope) => { console.log(scope); }); } else { @@ -37,8 +37,8 @@ async function main() { } console.log('\nFollowing Packages:'); - if (requirements.packages.length > 0) { - requirements.packages.forEach((pkg) => { + if (packages.length > 0) { + packages.forEach((pkg) => { console.log(pkg); }); } else {