-
Notifications
You must be signed in to change notification settings - Fork 3k
Fix prune, add tests to demonstrate brokenness in npm 4.1.0+ #16637
Conversation
var isChildDev = function (parent) { return isDev(parent, childName) }
return child.requiredBy.every(isChildDev) This appears to be the logic at fault. If I understand correctly, it only prunes if |
I came up with a fix and updated the PR description. There are now two test cases; CI shows that they both demonstrate the failure until this fix is applied. 😄 |
Some testing shows that this does indeed need to take cycles into account. IMO it would be nice if this behavior was baked into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With and without your changes I'm seeing this test failure:
EDIT: never mind this, I misread my own edited output =D
t.is(code, 0, 'prune finished successfully') | ||
t.equal(stderr, | ||
'- [email protected] node_modules/a\n' + | ||
'- [email protected] node_modules/b\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't expect this to ever pass because that output is printed to stdout
not stderr
.
'--loglevel', 'silent', | ||
'--production', 'false' | ||
], EXEC_OPTS, function (err, code, stdout, stderr) { | ||
t.ifErr(err, 'install finished successfully') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you likely just copied this from another test, but it was wrong there too. =D
The test label says 'install finished successfully' but that's not what not getting an err
value means. err
will ONLY be set if something catastrophic happened like we couldn't open a new process. This would be better written as:
if (err) throw err
(The story of how any tests started doing the ifError
thing is an interesting bit of code archaeology, but the long and short of it is that this is never an appropriate check in this context. It was introduced due to either errors when converting between two types of test, or by someone who cargo culted it from another similar but quite different context.)
], EXEC_OPTS, function (err, code, stderr) { | ||
t.ifErr(err, 'prune finished successfully') | ||
t.notOk(code, 'exit ok') | ||
t.notOk(stderr, 'Should not get data on stderr: ' + stderr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not stderr
that is being checked here, this is stdout
.
I'm honestly not sure what the expected behavior for this one is? In my testing with the bleeding edge, it removes this:
# {
# "action": "remove",
# "name": "test-package",
# "version": "0.0.0",
# "path": "node_modules/test-package"
# }
Is that correct, or wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, that is wrong: test-package
should remain, because the result of npm prune --production
should be "anything NOT required by production dependencies
is removed", but test-package
is in dependencies
, so removing it would break the install.
], EXEC_OPTS, function (err, code, stderr) { | ||
t.ifErr(err, 'prune finished successfully') | ||
t.notOk(code, 'exit ok') | ||
t.equal(stderr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable holds STDOUT, not STDERR.
'--loglevel', 'silent', | ||
'--production', 'false' | ||
], EXEC_OPTS, function (err, code, stdout, stderr) { | ||
t.ifErr(err, 'prune finished successfully') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comments about ifErr apply
Ok, so don't worry about making any changes to this, I wanted to give you the feedback, but I've gone and made them myself when I was making this merge clean w/ the branch that will be release-next tomorrow. (or released tomorrow, not sure =D) This is what'll be merged: lock-missing-dep-fixes...iarna/pr/16637 |
@iarna Thanks for the feedback! I was indeed confused about the stderr/stdout discrepancies myself when making these changes – the code is copied from existing tests that had the same mistake. And yes, I left this with new test cases that were failing, never got around to solving it completely. |
This adds two test cases to demonstrate how npm 4.1.0+ (including the latest version) fails to prune in certain cases (likely introduced in #15090). A fix is included, but will need to be reviewed carefully (I'm not sure whether cycle detection or checking
userRequired
is necessary, or if that's already taken care of byisExtraneous
).prune
misbehaving is likely the cause of some of the funny business on display in issues #15727, #15669, #15646.My interest in this stems from the fact that broken pruning causes postinstall-build to break on the latest versions of npm ever since 4.1.0.
The Tests
Call
npm prune --production
on a module with ONLY these dependencies:...which looks like this before pruning:
npm prune --production
SHOULD remove both packages, resulting in an emptynode_modules
tree, but it fails to removetest-package
(because it's a prod dep oftest-package-with-one-dep
, but that shouldn't matter becausetest-package-with-one-dep
itself is a dev dep).Call
prune --production
with the same package in bothdependencies
anddevDependencies
. The package SHOULD NOT be removed.The Fix
In
--production
mode, packages are now pruned if they are only reachable from dev dependencies (the actual logic is: NOT reachable from prod or opt dependencies)./cc @iarna