Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRuy Adorno <ruyadorno@hotmail.com>2022-02-03 06:10:22 +0300
committerRuy Adorno <ruyadorno@hotmail.com>2022-02-03 22:23:46 +0300
commit457e0ae61bbc55846f5af44afa4066921923490f (patch)
tree43a3bf35bc6a357c82b2c19874e022169e31fb41 /lib
parent0b0a7cc767947ea738da50caa832d8a922e20ac6 (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.js23
-rw-r--r--lib/utils/validate-lockfile.js29
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