From 0d2c50568f3d1cca634ea2e19283b0c65576a442 Mon Sep 17 00:00:00 2001 From: Luna Wei Date: Tue, 2 Jan 2024 11:46:03 -0800 Subject: [PATCH] Add print-packages as a command (#41959) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Working on releases, I'm often looking for the name of our monorepo packages (as sometimes the name doesn't align with the directory) and also getting a list of the versions of everything, as well as if its private/public -- which I've interpreted to mean that we publish it or we don't. I thought this might be convenient to add. [Internal] - Add `print-packages` as a command to print our monorepo packages (including react-native) Pull Request resolved: https://github.com/facebook/react-native/pull/41959 Test Plan: ``` ❯ yarn print-packages yarn run v1.22.19 $ node ./scripts/monorepo/print ┌─────────┬─────────┬─────────────────────────────────────────┬────────────────┐ │ (index) │ Public? │ Name │ Version (main) │ ├─────────┼─────────┼─────────────────────────────────────────┼────────────────┤ │ 0 │ '✅' │ 'react-native/assets-registry' │ '0.74.0' │ │ 1 │ '✅' │ 'react-native/babel-plugin-codegen' │ '0.74.0' │ │ 2 │ '✅' │ 'react-native/community-cli-plugin' │ '0.74.0' │ │ 3 │ '✅' │ 'react-native/debugger-frontend' │ '0.74.0' │ │ 4 │ '✅' │ 'react-native/dev-middleware' │ '0.74.0' │ │ 5 │ '✅' │ 'react-native/eslint-config' │ '0.74.0' │ │ 6 │ '✅' │ 'react-native/eslint-plugin' │ '0.74.0' │ │ 7 │ '✅' │ 'react-native/eslint-plugin-specs' │ '0.74.0' │ │ 8 │ '❌' │ 'react-native/hermes-inspector-msggen' │ '0.72.0' │ │ 9 │ '✅' │ 'react-native/metro-config' │ '0.74.0' │ │ 10 │ '✅' │ 'react-native/normalize-colors' │ '0.74.1' │ │ 11 │ '✅' │ 'react-native/js-polyfills' │ '0.74.0' │ │ 12 │ '✅' │ 'react-native' │ '1000.0.0' │ │ 13 │ '✅' │ 'react-native/babel-preset' │ '0.74.0' │ │ 14 │ '✅' │ 'react-native/metro-babel-transformer' │ '0.74.0' │ │ 15 │ '❌' │ 'react-native/bots' │ '0.0.0' │ │ 16 │ '✅' │ 'react-native/codegen' │ '0.74.0' │ │ 17 │ '❌' │ 'react-native/codegen-typescript-test' │ '0.0.1' │ │ 18 │ '✅' │ 'react-native/gradle-plugin' │ '0.74.0' │ │ 19 │ '❌' │ 'react-native/tester' │ '0.0.1' │ │ 20 │ '❌' │ 'react-native/tester-e2e' │ '0.0.1' │ │ 21 │ '✅' │ 'react-native/typescript-config' │ '0.74.0' │ │ 22 │ '✅' │ 'react-native/virtualized-lists' │ '0.74.0' │ └─────────┴─────────┴─────────────────────────────────────────┴────────────────┘ ✨ Done in 0.55s. ``` Also added filter flag for private/public ``` ❯ yarn print-packages --type private yarn run v1.22.19 $ node ./scripts/monorepo/print --type private ┌─────────┬─────────┬─────────────────────────────────────────┬────────────────┐ │ (index) │ Public? │ Name │ Version (main) │ ├─────────┼─────────┼─────────────────────────────────────────┼────────────────┤ │ 0 │ '❌' │ 'react-native/hermes-inspector-msggen' │ '0.72.0' │ │ 1 │ '❌' │ 'react-native/bots' │ '0.0.0' │ │ 2 │ '❌' │ 'react-native/codegen-typescript-test' │ '0.0.1' │ │ 3 │ '❌' │ 'react-native/tester' │ '0.0.1' │ │ 4 │ '❌' │ 'react-native/tester-e2e' │ '0.0.1' │ └─────────┴─────────┴─────────────────────────────────────────┴────────────────┘ ✨ Done in 0.16s. ``` Also added a npm query where you can see the latest published version of a minor ``` ❯ yarn print-packages --type public --minor 72 yarn run v1.22.19 $ node ./scripts/monorepo/print --type public --minor 72 ┌─────────┬─────────┬─────────────────────────────────────────┬────────────────┬──────────────────────────────────────┐ │ (index) │ Public? │ Name │ Version (main) │ Version (72) │ ├─────────┼─────────┼─────────────────────────────────────────┼────────────────┼──────────────────────────────────────┤ │ 0 │ '✅' │ 'react-native/assets-registry' │ '0.74.0' │ '0.72.0' │ │ 1 │ '✅' │ 'react-native/babel-plugin-codegen' │ '0.74.0' │ '0.72.3' │ │ 2 │ '✅' │ 'react-native/community-cli-plugin' │ '0.74.0' │ 'No match found for version ^0.72.0' │ │ 3 │ '✅' │ 'react-native/debugger-frontend' │ '0.74.0' │ 'No match found for version ^0.72.0' │ │ 4 │ '✅' │ 'react-native/dev-middleware' │ '0.74.0' │ 'No match found for version ^0.72.0' │ │ 5 │ '✅' │ 'react-native/eslint-config' │ '0.74.0' │ '0.72.2' │ │ 6 │ '✅' │ 'react-native/eslint-plugin' │ '0.74.0' │ '0.72.0' │ │ 7 │ '✅' │ 'react-native/eslint-plugin-specs' │ '0.74.0' │ '0.72.4' │ │ 8 │ '✅' │ 'react-native/metro-config' │ '0.74.0' │ '0.72.11' │ │ 9 │ '✅' │ 'react-native/normalize-colors' │ '0.74.1' │ '0.72.0' │ │ 10 │ '✅' │ 'react-native/js-polyfills' │ '0.74.0' │ '0.72.1' │ │ 11 │ '✅' │ 'react-native' │ '1000.0.0' │ '0.72.8' │ │ 12 │ '✅' │ 'react-native/babel-preset' │ '0.74.0' │ 'No match found for version ^0.72.0' │ │ 13 │ '✅' │ 'react-native/metro-babel-transformer' │ '0.74.0' │ 'No match found for version ^0.72.0' │ │ 14 │ '✅' │ 'react-native/codegen' │ '0.74.0' │ '0.72.8' │ │ 15 │ '✅' │ 'react-native/gradle-plugin' │ '0.74.0' │ '0.72.11' │ │ 16 │ '✅' │ 'react-native/typescript-config' │ '0.74.0' │ 'No match found for version ^0.72.0' │ │ 17 │ '✅' │ 'react-native/virtualized-lists' │ '0.74.0' │ '0.72.8' │ └─────────┴─────────┴─────────────────────────────────────────┴────────────────┴──────────────────────────────────────┘ ``` Reviewed By: cortinico Differential Revision: D52347140 Pulled By: lunaleaps fbshipit-source-id: 75811730e1afd5aae2d9fba4e437cd0d3d424a90 --- package.json | 1 + scripts/__tests__/npm-utils-test.js | 46 +++++++++++++++++ scripts/monorepo/print/index.js | 80 +++++++++++++++++++++++++++++ scripts/npm-utils.js | 75 ++++++++++++++++++++++----- 4 files changed, 190 insertions(+), 12 deletions(-) create mode 100644 scripts/monorepo/print/index.js diff --git a/package.json b/package.json index 78f3c44f2c7563..15290490c7fc9e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "lint-java": "node ./scripts/lint-java.js", "lint": "eslint .", "prettier": "prettier --write \"./**/*.{js,md,yml,ts,tsx}\"", + "print-packages": "node ./scripts/monorepo/print", "shellcheck": "./scripts/circleci/analyze_scripts.sh", "start": "cd packages/rn-tester && npm run start", "test-android": "./gradlew :packages:react-native:ReactAndroid:test", diff --git a/scripts/__tests__/npm-utils-test.js b/scripts/__tests__/npm-utils-test.js index b8013fcebd3e74..237cd29aa5b5c0 100644 --- a/scripts/__tests__/npm-utils-test.js +++ b/scripts/__tests__/npm-utils-test.js @@ -10,6 +10,7 @@ const { applyPackageVersions, getPackageVersionStrByTag, + getVersionsBySpec, publishPackage, } = require('../npm-utils'); @@ -104,4 +105,49 @@ describe('npm-utils', () => { ); }); }); + + describe('getVersionsBySpec', () => { + it('should return array when single version returned', () => { + execMock.mockImplementationOnce(() => ({code: 0, stdout: '"0.72.0" \n'})); + + const versions = getVersionsBySpec('mypackage', '^0.72.0'); + expect(versions).toEqual(['0.72.0']); + }); + + it('should return array of versions', () => { + execMock.mockImplementationOnce(() => ({ + code: 0, + stdout: '[\n"0.73.0",\n"0.73.1"\n]\n', + })); + + const versions = getVersionsBySpec('mypackage', '^0.73.0'); + expect(versions).toEqual(['0.73.0', '0.73.1']); + }); + + it('should return error summary if E404', () => { + const error = + `npm ERR! code E404\n` + + `npm ERR! 404 No match found for version ^0.72.0\n` + + `npm ERR! 404\n` + + `npm ERR! 404 '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n` + + `npm ERR! 404\n` + + `npm ERR! 404 Note that you can also install from a\n` + + `npm ERR! 404 tarball, folder, http url, or git url.\n` + + `{\n` + + ` "error": {\n` + + ` "code": "E404",\n` + + ` "summary": "No match found for version ^0.72.0",\n` + + ` "detail": "\n '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n\nNote that you can also install from a\ntarball, folder, http url, or git url."\n` + + ` }\n` + + `}\n`; + execMock.mockImplementationOnce(() => ({ + code: 1, + stderr: error, + })); + + expect(() => { + getVersionsBySpec('mypackage', '^0.72.0'); + }).toThrow('No match found for version ^0.72.0'); + }); + }); }); diff --git a/scripts/monorepo/print/index.js b/scripts/monorepo/print/index.js new file mode 100644 index 00000000000000..5846837ddfaf02 --- /dev/null +++ b/scripts/monorepo/print/index.js @@ -0,0 +1,80 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +const {getVersionsBySpec} = require('../../npm-utils'); +const forEachPackage = require('../for-each-package'); +const {exit} = require('shelljs'); +const yargs = require('yargs'); + +const { + argv: {type, minor}, +} = yargs + .option('type', { + type: 'string', + describe: 'Choose which packages to list, default is all', + choices: ['all', 'public', 'private'], + default: 'all', + }) + .option('minor', { + type: 'number', + describe: + 'List latest version for specified minor. Ex. 72, 73. Note this will make a network request to npm', + default: 0, + }) + .check(argv => { + if (argv.minor > 0 && argv.minor < 70) { + throw new Error('Invalid minor. No monorepo packages before 70'); + } + return true; + }) + .strict(); + +function reversePatchComp(semverA, semverB) { + const patchA = parseInt(semverA.split('.')[2], 10); + const patchB = parseInt(semverB.split('.')[2], 10); + return patchB - patchA; +} + +const main = () => { + const data = []; + forEachPackage( + (_packageAbsolutePath, _packageRelativePathFromRoot, packageManifest) => { + const isPublic = !packageManifest.private; + if ( + type === 'all' || + (type === 'private' && !isPublic) || + (type === 'public' && isPublic) + ) { + const packageInfo = { + 'Public?': isPublic ? '\u{2705}' : '\u{274C}', + Name: packageManifest.name, + 'Version (main)': packageManifest.version, + }; + + if (isPublic && minor !== 0) { + try { + const versions = getVersionsBySpec( + packageManifest.name, + `^0.${minor}.0`, + ).sort(reversePatchComp); + packageInfo[`Version (${minor})`] = versions[0]; + } catch (e) { + packageInfo[`Version (${minor})`] = e.message; + } + } + data.push(packageInfo); + } + }, + {includeReactNative: true}, + ); + console.table(data); + exit(0); +}; + +main(); diff --git a/scripts/npm-utils.js b/scripts/npm-utils.js index b3890f4d3025c0..efb7cc2784bc70 100644 --- a/scripts/npm-utils.js +++ b/scripts/npm-utils.js @@ -72,18 +72,6 @@ function getNpmInfo(buildType) { }; } -function getPackageVersionStrByTag(packageName, tag) { - const npmString = tag - ? `npm view ${packageName}@${tag} version` - : `npm view ${packageName} version`; - const result = exec(npmString, {silent: true}); - - if (result.code) { - throw new Error(`Failed to get ${tag} version from npm\n${result.stderr}`); - } - return result.stdout.trim(); -} - function publishPackage(packagePath, packageOptions, execOptions) { const {tag, otp} = packageOptions; const tagFlag = tag ? ` --tag ${tag}` : ''; @@ -147,10 +135,73 @@ function applyPackageVersions(originalPackageJson, packageVersions) { return packageJson; } +/** + * `packageName`: name of npm package + * `tag`: npm tag like `latest` or `next` + * + * This will fetch version of `packageName` with npm tag specified + */ +function getPackageVersionStrByTag(packageName, tag) { + const npmString = tag + ? `npm view ${packageName}@${tag} version` + : `npm view ${packageName} version`; + const result = exec(npmString, {silent: true}); + + if (result.code) { + throw new Error(`Failed to get ${tag} version from npm\n${result.stderr}`); + } + return result.stdout.trim(); +} + +/** + * `packageName`: name of npm package + * `spec`: spec range ex. '^0.72.0' + * + * Return an array of versions of the specified spec range or throw an error + */ +function getVersionsBySpec(packageName, spec) { + const npmString = `npm view ${packageName}@'${spec}' version --json`; + const result = exec(npmString, {silent: true}); + + if (result.code) { + // Special handling if no such package spec exists + if (result.stderr.includes('npm ERR! code E404')) { + /** + * npm ERR! code E404 + * npm ERR! 404 No match found for version ^0.72.0 + * npm ERR! 404 + * npm ERR! 404 '@react-native/community-cli-plugin@^0.72.0' is not in this registry. + * npm ERR! 404 + * npm ERR! 404 Note that you can also install from a + * npm ERR! 404 tarball, folder, http url, or git url. + * { + * "error": { + * "code": "E404", + * "summary": "No match found for version ^0.72.0", + * "detail": "\n '@react-native/community-cli-plugin@^0.72.0' is not in this registry.\n\nNote that you can also install from a\ntarball, folder, http url, or git url." + * } + * } + */ + const error = JSON.parse( + result.stderr + .split('\n') + .filter(line => !line.includes('npm ERR')) + .join(''), + ).error; + throw new Error(error.summary); + } else { + throw new Error(`Failed: ${npmString}`); + } + } + const versions = JSON.parse(result.stdout.trim()); + return !Array.isArray(versions) ? [versions] : versions; +} + module.exports = { applyPackageVersions, getNpmInfo, getPackageVersionStrByTag, + getVersionsBySpec, publishPackage, diffPackages, pack,