lockfile-lint icon indicating copy to clipboard operation
lockfile-lint copied to clipboard

npm 7 and Yarn v2 support needed

Open jdanil opened this issue 4 years ago • 8 comments

Is your feature request related to a problem? Please describe.

Yarn v2 introduced a new lockfile format that now adheres to YAML. I believe ATM when trying to run lockfile-lint on a project with yarn v2, lockfile-lint tries to use the v1 parser and fails with an error.

ABORTING lockfile lint process due to error exceptions

Unable to parse lockfile "yarn.lock"

SyntaxError: Unknown token: {line: 3, col: 2, type: ''INVALID', value: undefined } 3:2 in lockfile

Describe the solution you'd like

As far as I can tell, yarn v2 does not have an equivalent lockfile parser. But since it adheres to YAML now, I'm wondering if it is possible to just use a parser like yaml and create a small function to transform it like parseNpmLockfile?

Describe alternatives you've considered

None.

jdanil avatar Dec 07 '20 06:12 jdanil

@jdanil yep, sounds like that would be a great way to support it and shouldn't need too much work besides some try/catch to figure out which yarn version it is when you start parsing. Would you be up to sending a PR to lockfile-lint to support it?

lirantal avatar Dec 12 '20 20:12 lirantal

@lirantal Hey, I would like to work on this issue, however, I see there's this lockfile parser https://github.com/snyk/nodejs-lockfile-parser/ which seems to be covering yarn lockfile v2 already. However, they don't provide the capability of getting the resolved url and the integrity hash.

Do you think I should take this up with the authors of that repo and if they are willing to accept that change? Once done, we can delegate all the parsing related logic to that package.

abdulhannanali avatar Apr 05 '21 02:04 abdulhannanali

In case, if you think they won't be able to accept that change, I can make a PR to this repo adding support for yarn v2 lockfile, I have already done some prior work

abdulhannanali avatar Apr 05 '21 02:04 abdulhannanali

@abdulhannanali

Do you think I should take this up with the authors of that repo and if they are willing to accept that change? Once done, we can delegate all the parsing related logic to that package.

Yep, sounds like a good idea to see if the folks maintaining that nodejs-lockfile-parser are interested to add those metadata items, so we can ultimately use that for the whole parsing we need.

Sounds good with me. Happy to have you collaborate on this pull request. Thanks ❤️

lirantal avatar Apr 05 '21 18:04 lirantal

@lirantal

Awesome, I'll open an issue there and mention you to get this going. Thank you likewise, happy to collaborate <3

abdulhannanali avatar Apr 05 '21 20:04 abdulhannanali

It looks like npm 7 support was implemented: https://github.com/snyk/nodejs-lockfile-parser/releases/tag/v1.34.0

jerone avatar May 13 '21 18:05 jerone

Nice. Thanks for the heads up @jerone @abdulhannanali would you want to go at it?

lirantal avatar May 13 '21 20:05 lirantal

@lirantal Thanks for the heads up, sorry I wasn't able to attend to it earlier. I will take a go at it.

abdulhannanali avatar May 14 '21 06:05 abdulhannanali

Same problem here when attempting to upgrade from Yarn 1 to Yarn 4 RC (Berry): https://github.com/blockprotocol/blockprotocol/actions/runs/3274094581/jobs/5387403013#step:8:17

 ℹ ABORTING lockfile lint process due to error exceptions 
 
Unable to parse yarn lockfile "yarn.lock" 

Error: Lockfile does not seem to contain a valid dependency list
    at yarnParseAndVerify (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint-api/src/ParseLockfile.js:42:11)
    at ParseLockfile.parseYarnLockfile (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint-api/src/ParseLockfile.js:141:20)
    at ParseLockfile.parseSync (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint-api/src/ParseLockfile.js:103:27)
    at ValidateHostManager (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint/src/validators/index.js:49:27)
    at /home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint/src/main.js:41:28
    at Array.forEach (<anonymous>)
    at Object.runValidators (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint/src/main.js:31:14)
    at Object.<anonymous> (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint/bin/lockfile-lint.js:80:17)
    at Module._compile (node:internal/modules/cjs/loader:1126:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10) 

/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint/bin/lockfile-lint.js:89
  error('Error: command failed with exit code 1')
  ^

TypeError: error is not a function
    at Object.<anonymous> (/home/runner/work/blockprotocol/blockprotocol/node_modules/lockfile-lint/bin/lockfile-lint.js:89:3)
    at Module._compile (node:internal/modules/cjs/loader:1126:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10)
    at Module.load (node:internal/modules/cjs/loader:1004:32)
    at Function.Module._load (node:internal/modules/cjs/loader:839:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

PR: https://github.com/blockprotocol/blockprotocol/pull/680

kachkaev avatar Oct 18 '22 14:10 kachkaev

So yarnpkg/parsers is now updated to work with the new lockfile format. I was able to get everything "working" with the following patch:

diff --git a/src/ParseLockfile.js b/src/ParseLockfile.js
index 0f0c951027ec83c61769bb6a48943420dff133b8..bad2d251cf376bf3ef4b444a0d49f03a602d7a6e 100644
--- a/src/ParseLockfile.js
+++ b/src/ParseLockfile.js
@@ -21,13 +21,13 @@ const {
  * @return boolean
  */
 function checkSampleContent (lockfile) {
-  const [sampleKey, sampleValue] = Object.entries(lockfile)[0]
+  const [sampleKey, sampleValue] = Object.entries(lockfile)[1]
   return (
     sampleKey.match(/.*@.*/) &&
     (sampleValue &&
       typeof sampleValue === 'object' &&
       sampleValue.hasOwnProperty('version') &&
-      sampleValue.hasOwnProperty('resolved'))
+      sampleValue.hasOwnProperty('resolution'))
   )
 }
 /**
@@ -41,7 +41,25 @@ function yarnParseAndVerify (lockfileBuffer) {
   if (!hasSensibleContent) {
     throw Error('Lockfile does not seem to contain a valid dependency list')
   }
-  return {type: 'success', object: lockfile}
+  const normalized = Object.fromEntries(Object.entries(lockfile).map(([packageName, packageDetails]) => {
+    const resolution = packageDetails.resolution;
+    if (!resolution) {
+      return [packageName, packageDetails];
+    }
+    const splitByAt = resolution.split('@');
+    let [resolvedPackageName, host] = splitByAt;
+    if (splitByAt.length > 2) {
+      resolvedPackageName = `${splitByAt[0]}${splitByAt[1]}`;
+      host = splitByAt[2];
+    }
+
+    if (splitByAt.length > 2 && resolution[0] !== '@') {
+      [resolvedPackageName, host] = splitByAt;
+    }
+
+    return [packageName, { ...packageDetails, resolved: host}]
+  }))
+  return {type: 'success', object: normalized}
 }
 class ParseLockfile {
   /**

Note, i think you will also have to get the latest version of yarnpkg/parsers resolved, and you'll have to add 'npm:', 'patch:' etc to schemes instead of hosts. The way I broke apart the resolution field is probably really naive but it met our use case. It would be much safer to use a regex or something to pull any urls out of the resolution and evaluate them for hosts, and then somehow leave the scheme (npm:, patch:) etc for evaluation.

brad-decker avatar Oct 20 '22 13:10 brad-decker

@brad-decker the above is just a Yarn2 compatible update then? I'm thinking, perhaps we could have specific Yarn versions logic spread out, and then have one main code that determines the lockfile version, and then uses the relevant parser. WDYT? If you wanted to take a shot at that, I'm happy to merge a PR.

lirantal avatar Oct 20 '22 19:10 lirantal

Im about to go on vacation but when i get back and up to speed ill work on it

brad-decker avatar Oct 20 '22 21:10 brad-decker

Sounds good!

lirantal avatar Oct 21 '22 06:10 lirantal

@brad-decker are you still interested in crafting a PR? No worries if not, just checking if the task is taken.

kachkaev avatar Nov 16 '22 19:11 kachkaev

I didn't read through the entire convo, but as yarn above 1 goes, I remember replacing the lib used for reading yarn.lock with the modern one. FYI

naugtur avatar Nov 16 '22 22:11 naugtur

Can the title of this issue please be updated to "Yarn v2 support needed" for accuracy since NPM v7 is already supported?

candrews avatar Nov 21 '22 19:11 candrews

@kachkaev if you or someone else wants to tackle please go for it. Otherwise i'll take a stab at it soon

brad-decker avatar Nov 22 '22 16:11 brad-decker

@brad-decker call me whenever you need a second pair of eyes

naugtur avatar Nov 22 '22 20:11 naugtur

Oh, and are you sure it's not working? I replaced the lockfile parser with the one used by yarn berry earlier this year https://github.com/lirantal/lockfile-lint/commit/18c6ae0e75b8064ec7ca8c8460e7a6f9d9546f9a

naugtur avatar Nov 25 '22 14:11 naugtur

@naugtur I'm on [email protected] (2022-10-08) and it does not seem to work with [email protected]:

  • https://github.com/lirantal/lockfile-lint/issues/101#issuecomment-1282502621
  • https://github.com/blockprotocol/blockprotocol/pull/680
  • Output for lockfile-lint --path yarn.lock --allowed-hosts registry.yarnpkg.com --allowed-schemes \"https:\":
    Unable to parse yarn lockfile "yarn.lock" (CI output)

kachkaev avatar Nov 25 '22 18:11 kachkaev

@lirantal i have authored https://github.com/lirantal/lockfile-lint/pull/147 which is a cleaned up version of my patch for our project. It doesn't do anything in the way of adding support to the command line interface and proper documentation of the way yarn berry appends hostnames to the package is needed to be effective. For example this is our usage:

lockfile-lint --path yarn.lock --allowed-hosts npm yarn github.com codeload.github.com --empty-hostname true --allowed-schemes "https:" "git+https:" "npm:" "patch:" "workspace:"

brad-decker avatar Dec 07 '22 18:12 brad-decker

Thanks, looking at it, Brad. Appreciate the PR.

lirantal avatar Dec 11 '22 18:12 lirantal