diff options
author | Ruy Adorno <ruyadorno@hotmail.com> | 2022-02-03 06:10:22 +0300 |
---|---|---|
committer | Ruy Adorno <ruyadorno@hotmail.com> | 2022-02-03 22:23:46 +0300 |
commit | 457e0ae61bbc55846f5af44afa4066921923490f (patch) | |
tree | 43a3bf35bc6a357c82b2c19874e022169e31fb41 /lib | |
parent | 0b0a7cc767947ea738da50caa832d8a922e20ac6 (diff) |
fix(ci): lock file validation
Make sure to validate any lock file (either package-lock.json or
npm-shrinkwrap.json) against the current install. This will properly
throw an error in case any of the dependencies being installed don't
match the dependencies that are currently listed in the lock file.
Fixes: https://github.com/npm/cli/issues/2701
Fixes: https://github.com/npm/cli/issues/3947
Diffstat (limited to 'lib')
-rw-r--r-- | lib/commands/ci.js | 23 | ||||
-rw-r--r-- | lib/utils/validate-lockfile.js | 29 |
2 files changed, 52 insertions, 0 deletions
diff --git a/lib/commands/ci.js b/lib/commands/ci.js index 2c2f8da86..376a85d60 100644 --- a/lib/commands/ci.js +++ b/lib/commands/ci.js @@ -6,6 +6,7 @@ const runScript = require('@npmcli/run-script') const fs = require('fs') const readdir = util.promisify(fs.readdir) const log = require('../utils/log-shim.js') +const validateLockfile = require('../utils/validate-lockfile.js') const removeNodeModules = async where => { const rimrafOpts = { glob: false } @@ -55,6 +56,28 @@ class CI extends ArboristWorkspaceCmd { }), removeNodeModules(where), ]) + + // retrieves inventory of packages from loaded virtual tree (lock file) + const virtualInventory = new Map(arb.virtualTree.inventory) + + // build ideal tree step needs to come right after retrieving the virtual + // inventory since it's going to erase the previous ref to virtualTree + await arb.buildIdealTree() + + // verifies that the packages from the ideal tree will match + // the same versions that are present in the virtual tree (lock file) + // throws a validation error in case of mismatches + const errors = validateLockfile(virtualInventory, arb.idealTree.inventory) + if (errors.length) { + throw new Error( + '`npm ci` can only install packages when your package.json and ' + + 'package-lock.json or npm-shrinkwrap.json are in sync. Please ' + + 'update your lock file with `npm install` ' + + 'before continuing.\n\n' + + errors.join('\n') + '\n' + ) + } + await arb.reify(opts) const ignoreScripts = this.npm.config.get('ignore-scripts') diff --git a/lib/utils/validate-lockfile.js b/lib/utils/validate-lockfile.js new file mode 100644 index 000000000..29161ec55 --- /dev/null +++ b/lib/utils/validate-lockfile.js @@ -0,0 +1,29 @@ +// compares the inventory of package items in the tree +// that is about to be installed (idealTree) with the inventory +// of items stored in the package-lock file (virtualTree) +// +// Returns empty array if no errors found or an array populated +// with an entry for each validation error found. +function validateLockfile (virtualTree, idealTree) { + const errors = [] + + // loops through the inventory of packages resulted by ideal tree, + // for each package compares the versions with the version stored in the + // package-lock and adds an error to the list in case of mismatches + for (const [key, entry] of idealTree.entries()) { + const lock = virtualTree.get(key) + + if (!lock) { + errors.push(`Missing: ${entry.name}@${entry.version} from lock file`) + continue + } + + if (entry.version !== lock.version) { + errors.push(`Invalid: lock file's ${lock.name}@${lock.version} does ` + + `not satisfy ${entry.name}@${entry.version}`) + } + } + return errors +} + +module.exports = validateLockfile |