From 06f403481fe1f30d6952ad60ecd759b410962926 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 8 Dec 2021 02:09:00 -0500 Subject: [PATCH] Add CI job to check npm dependencies (#22881) Checks that if one React package depends on another, the current version satisfies the given dependency range. That way we don't forget to bump dependencies when we release a new version. --- .circleci/config.yml | 18 +++++- package.json | 3 +- scripts/release/check-release-dependencies.js | 59 +++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 scripts/release/check-release-dependencies.js diff --git a/.circleci/config.yml b/.circleci/config.yml index a21dc532e69db..216a481f0cfb8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -205,11 +205,24 @@ jobs: environment: *environment steps: - checkout - - attach_workspace: *attach_workspace + - attach_workspace: + at: . - run: yarn workspaces info | head -n -1 > workspace_info.txt - *restore_node_modules - run: yarn lint-build + yarn_check_release_dependencies: + docker: *docker + environment: *environment + steps: + - checkout + - attach_workspace: + at: . + - run: yarn workspaces info | head -n -1 > workspace_info.txt + - *restore_node_modules + - run: yarn check-release-dependencies + + check_error_codes: docker: *docker environment: *environment @@ -427,6 +440,9 @@ workflows: - yarn_lint_build: requires: - yarn_build_combined + - yarn_check_release_dependencies: + requires: + - yarn_build_combined - check_error_codes: requires: - yarn_build_combined diff --git a/package.json b/package.json index c57c892b15730..7c2651474352a 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,8 @@ "publish-prereleases": "node ./scripts/release/publish-using-ci-workflow.js", "download-build": "node ./scripts/release/download-experimental-build.js", "download-build-for-head": "node ./scripts/release/download-experimental-build.js --commit=$(git rev-parse HEAD)", - "download-build-in-codesandbox-ci": "cd scripts/release && yarn install && cd ../../ && yarn download-build-for-head || yarn build-combined --type=node react/index react-dom scheduler" + "download-build-in-codesandbox-ci": "cd scripts/release && yarn install && cd ../../ && yarn download-build-for-head || yarn build-combined --type=node react/index react-dom scheduler", + "check-release-dependencies": "node ./scripts/release/check-release-dependencies" }, "resolutions": { "react-is": "npm:react-is" diff --git a/scripts/release/check-release-dependencies.js b/scripts/release/check-release-dependencies.js new file mode 100644 index 0000000000000..b009a53e1b024 --- /dev/null +++ b/scripts/release/check-release-dependencies.js @@ -0,0 +1,59 @@ +'use strict'; + +/* eslint-disable no-for-of-loops/no-for-of-loops */ + +const fs = require('fs'); +const semver = require('semver'); + +const {stablePackages} = require('../../ReactVersions'); + +function main() { + if (!fs.existsSync('./build/oss-stable-semver')) { + throw new Error('No build artifacts found'); + } + + const packages = new Map(); + for (const packageName in stablePackages) { + if (!fs.existsSync(`build/oss-stable-semver/${packageName}/package.json`)) { + throw new Error(`${packageName}`); + } else { + const info = JSON.parse( + fs.readFileSync(`build/oss-stable-semver/${packageName}/package.json`) + ); + packages.set(info.name, info); + } + } + + for (const [packageName, info] of packages) { + if (info.dependencies) { + for (const [depName, depRange] of Object.entries(info.dependencies)) { + if (packages.has(depName)) { + const releaseVersion = packages.get(depName).version; + checkDependency(packageName, depName, releaseVersion, depRange); + } + } + } + + if (info.peerDependencies) { + for (const [depName, depRange] of Object.entries(info.peerDependencies)) { + if (packages.has(depName)) { + const releaseVersion = packages.get(depName).version; + checkDependency(packageName, depName, releaseVersion, depRange); + } + } + } + } +} + +function checkDependency(packageName, depName, version, range) { + if (!semver.satisfies(version, range)) { + throw new Error( + `${packageName} has an invalid dependency on ${depName}: ${range}` + + '\n\n' + + 'Actual version is: ' + + version + ); + } +} + +main();