Closed amonks closed 4 years ago
This needs a test and maybe some cleanup (see RISK above), but I'd be happy to do those things if you're comfortable with the spirit of this change. š
Big fan of your blogs btw!
Can we ignore all npm errors that lead to nonzero exit codes, or just peer dependency errors?
A step back: Has this been an issue in the wild?
TLDR
When I run npm ls --production --json
in the package I'm interested in, I get errors of three forms:
npm ERR! peer dep missing: peer-package@^1.0.0, required by old-package@1.0.0
npm ERR! missing: transient-package@latest, required by rude-package@1.0.0
npm ERR! invalid: my-forked-package@1.0.0 /home/my/project/my-forked-package
peer dep missing
errors are because the old-package has a peer dependency requirement that is too specific. old-package declares that it works with ^1.0.0
of peer-package, but it also works with 2.0.0
, which fixes a bug I care about. old-package has not been updated.missing
errors, I suspect, are because transient-package has been updated since I installed, so I am no longer using "latest".invalid
error I'm not sure about. My package.json specifies my-forked-package as "my-forked-package": "githubname/my-forked-package#branchname"
, but I don't know if that pattern always produces an "invalid" error or if there's something else going on.In all cases, I could take steps on my end to "fix" the error, like installing different versions of packages, or forking them to have different dependency specifications. I don't want to do this. I have good integration tests and my build works fine. Licensee.js also works fine, given this patch.
One check that could be added is "does every line in stderr match a pattern that we know still produces reasonable json output?"
Another check that could be added is "does every line in stderr match some explicit user-configured whitelist pattern?"
If it actually works with v2 but declares it needs v1, your dep graph is invalid with v2, so thatās a real error - i hear that you donāt want to correct your dep graph, and perhaps licensee could be made to still work in that case, but it seems to me like itās trying to cure the symptoms instead of the disease.
Have you tried forking old-package as well and updating its peer dep range, so that you donāt have to have those errors at all?
@ljharb Yes, I could make the error go away by forking old-package and rude-package. I probably even should do this.
But fundamentally, I think it is valid to ask the question "under what licenses am I using my dependencies" even if my dependency graph is invalid. These seem to me like orthogonal issues.
One check that could be added is "does every line in stderr match a pattern that we know still produces reasonable json output?"
Another check that could be added is "does every line in stderr match some explicit user-configured whitelist pattern?"
Do we need to look at stderr
? I believe licensee
currently only parses stdout
By the by, I think everyone here is "right". I agree with @amonks that if npm ls
gives a usable tree, licensee could say whether its license meet the rules or not. But I also agree with @ljharb that we shouldn't go out of our way too far to make project problems licensee
problems.
@kemitchell As far as I am aware, stderr is the only place npm ls
tells us which errors happened. The change I'm thinking about is something like,
diff --git a/index.js b/index.js
index 451ec71..9a5db0c 100644
--- a/index.js
+++ b/index.js
@@ -83,16 +83,26 @@ function licensee (configuration, path, callback) {
)
var outputError
var json
+ var errorIsUnworkable
simpleConcat(child.stdout, function (error, buffer) {
if (error) outputError = error
else json = buffer
})
+ simpleConcat(child.stderr, function (error, buffer) {
+ if (error) outputError = error
+ else {
+ var errorLines = buffer.toString().split('\n')
+ if (errorLines.some(l => !errorMatchesPatternWeAreOkWith(l))) errorIsUnworkable = true
+ }
+ })
child.once('error', function (error) {
outputError = error
})
child.once('close', function (code) {
if (code !== 0 && !configuration.ignoreNpmErrors) {
done(new Error('npm exited with status ' + code))
+ } else if (configuration.ignoreNpmErrors && code !== 0 && errorIsUnworkable) {
+ done(new Error('npm produced an unacceptable error and exited with status ' + code))
} else if (outputError) {
done(outputError)
} else {
[edited] [untested but you get the idea]
How about, with the new flag enabled:
Note the exit code of npm ls
. If it's non-zero, report that to stderr
, but don't bail.
Ignore npm ls
error output entirely.
If we can parse the JSON output of npm ls
, process it.
I'll make that change. Thanks!
Maybe a slightly different flag for that approach, like --with-npm-errors
.
I don't mean to nitpick or micromanage. I'm just thinking about the pressure SemVer puts to get the interface/API right the first time.
I learned while testing this that I was mistaken. stderr is not the only place that npm ls
reports errors. It also includes a key problems
in the json printed to stdout. Since we were not going to read stderr anyway, I will also not look at the problems value. Happy to do so if you'd like, though.
{
"name": "with-npm-errors",
"problems": [
"missing: with-invalid-dependency@^1.0.0, required by with-npm-errors@",
"missing: mit-licensed@^1.0.0, required by with-npm-errors@"
],
"dependencies": {
"with-invalid-dependency": {
"required": "^1.0.0",
"missing": true
},
"mit-licensed": {
"required": "^1.0.0",
"missing": true
}
}
}
I also learned that since licensee only invokes npm ls
if configuration.production is set, licensee always ignores these sorts of package integrity errors without that flag.
So, with the change I've submitted, the behavior would be,
--with-npm-errors | --with-npm-errors unset | |
---|---|---|
--production | ignore dependency graph errors | fail on dependency graph errors |
--production unset | ignore dependency graph errors | ignore dependency graph errors |
currently on master, the behavior is
always | |
---|---|
--production | fail on dependency graph errors |
--production unset | ignore dependency graph errors |
I think this is confusingāI don't know that users would expect that the production flag would have anything to do with dependency graph integrity checking. In my opinion, there are two good ways and one mediocre way to handle this:
npm ls
check even with --production disabled, so licensee exits 1 if there are dependency graph errors and production is disabledNice catch! Of the options you laid out, my favorite is:
change --production behavior so that licensee never checks dependency graph integrity
Simple. Simple. Simpler.
PS: @amonks, this is a lot of good work, on a Friday, after a US federal holiday. Do you have good support for all this work that you're doing? If not, let's make sure you do. E-mail me separately if you prefer to discuss privately. kyle@kemitchell.com
I updated the PR to the proposed change, and added a test that failed before this change.
In conclusion,
cannot interpret npm ls --json output
below to tell us if it is safe to proceed.While this work on licensee.js is my own and not conducted on behalf of my employer, I am plenty well supported.
You can consider this PR a thank you for /dev/lawyer, license zero, Blue Oak, jslicense, and all the rest š
@amonks, do you have written permission from your employer to contribute this PR under Apache 2.0 terms?
Perhaps the PR title should be updated as well?
@amonks, if the licensing situation is green, you should add yourself to package.json
, too.
[still here; working on getting written confirmation of open source licensing policy; will make suggested changes when that lands]
@amonks thanks!
š Hello! We're running into the behaviour this PR would fix, and would love to see it merged.
@amonks did you ever get confirmation from your employer?
Thank you all
Hi, @rowanoulton. Thanks for following up with @amonks.
@kemitchell I don't want to steal @amonks's thunder, but if we don't hear back from them is this something you or I could commit to the project in their stead?
@rowanoulton we don't have a clear license for @amonks' work yet. We can't merge without one.
Worse comes to worst, we have to drop this and clean room the functionality.
hi! still alive! still working on an outbound open source policy, which is takingā¦ more legal rigamarole than I expected.
in hindsight, that was too big a project to block this on, so I'll seek one-off permission for this change.
sorry for the wait!
Any update on this? I'd really like to have this in the mainline release instead of using a fork.
I got one-off legal permission. I'll make the suggested changes tonight, then merge.
Thanks for your patience, all!
@amonks thank you for making sure this code is safe to share!
I made the suggested error message change and rebased.
Pending licensing confirmation, this LGTM, with one nit: We should
git add --force tests/with-npm-errors-in-production/package-lock.json
.
It looks like the package-lock.json is never created, perhaps because cd tests/with-npm-errors-in-production && npm install
(intentionally) fails. I think this is probably fine.
> cd tests/with-npm-errors-in-production/; and npm install
npm ERR! code ETARGET
npm ERR! notarget No matching version found for mit-licensed@^2.0.0.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
npm ERR! notarget
npm ERR! notarget It was specified as a dependency of 'with-invalid-dependency'
npm ERR! notarget
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/ajm/.npm/_logs/2020-05-16T18_17_26_851Z-debug.log
One more note,
In hindsight, "mit-licensed": "^2.0.0"
might not be the most robust invalid dependency. I don't think you're likely to push a version 2.0 of mit-licensed
, but if you did, with-invalid-dependency would no longer have an invalid dependency. It may be safer to create a new package explicitly for this purpose.
I'm happy to merge as is or to make further changes. Since @kemitchell I'm not following your nit about package-lock.json, I will hold off on merging until you give me a š.
@amonks, add yourself to package.json and let me know? I will merge, deal with the testing thing, and release.
Forgive me if I've just forgotten: Did we decide whether this is SemVer-major, -minor, or -patch?
Since it errors in fewer cases, it seems like it could be a patch?
@amonks, add yourself to package.json and let me know? I will merge, deal with the testing thing, and release.
done.
Forgive me if I've just forgotten: Did we decide whether this is SemVer-major, -minor, or -patch?
I don't think we did.
I'm going to release as a patch bump. It might be right. It might be wrong. It's better than thinking about SemVer for an hour.
v8.0.2 out
Ya'll the best. Thanks ā¤ļø
In #48, @nzacca and @kemitchell discussed the possibility of ignoring peer dependency errors.
@kemitchell said,
When
npm ls
encounters a peer-dependency violation or other error, it does three things:WHAT WAS CHANGED:
This commit adds a flag,
--ignore-npm-errors
that makes licensee proceed normally with the logged result, even if npm ls exits with a non-zero code.RISK:
I'm suspect this could produce operational errors down the line, if there are cases where
npm ls
does not log a result to stdout.