Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: lockfile format v6 #5810

Merged
merged 5 commits into from Jan 9, 2023
Merged

feat!: lockfile format v6 #5810

merged 5 commits into from Jan 9, 2023

Conversation

zkochan
Copy link
Member

@zkochan zkochan commented Dec 19, 2022

This new lockfile format will be opt-in in pnpm@7. pnpm@8 will only be able to convert the old lockfile format to the new one. So we'll be able to refactor the code in v8.

The primary reason for the lockfile format change is to get rid of the hashes in package keys. When a package in the dependency graph has peer dependencies, the names and versions of these peer dependencies are added to its pass.

For example, in this case, babel-preset-jest has @babel/core in its peer dependencies and it got resolved from @babel/core@7.20.7

/babel-preset-jest/29.2.0_@babel+core@7.20.7:

However, when there are many peer dependencies and the key become long, we use a hashing algorithm to create a hash from the names and versions of the resolved peer dependencies. Like in this case:

/ts-node/10.9.1_xl7wyiapi7jo5c2pfz5vjm55na:

This is needed because we use this key as the directory name inside node_modules/.pnpm. The name of the directory shouldn't become too long or otherwise there will be issues on Windows. Unfortunately, these hashes confuse our users. They don't understand what they mean and why they exist. It is also unclear why the hashes change during code reviews.

Hence, in the new lockfile format we won't hash long keys in the lockfile. But we will still hash the directory names inside node_modules/.pnpm. Also, we will use a more readable format for the key. The above keys will look like this:

/babel-preset-jest@29.2.0(@babel/core@7.20.7): 

/ts-node@10.9.1(@types/node@14.18.36)(typescript@4.9.3):

@zkochan zkochan changed the title feat!: more changes to the new lockfile format feat!: lockfile format v6 Jan 7, 2023
@zkochan zkochan marked this pull request as ready for review January 8, 2023 13:05
@zkochan zkochan requested a review from gluxon January 8, 2023 13:16
@gluxon
Copy link
Member

gluxon commented Jan 9, 2023

I like this! 🎉

This is needed because we use this key as the directory name inside node_modules/.pnpm. The name of the directory shouldn't become too long or otherwise there will be issues on Windows. Unfortunately, these hashes confuse our users. They don't understand what they mean and why they exist. It is also unclear why the hashes change during code reviews.

The new format definitely looks more clear to me too. It's also nice to be able to see exactly what peer dependencies were resolved to make a package different. That's a great usability improvement, even for someone familiar with the hashes introduced in pnpm v7 like me.

One nice property of the existing format was that the hashed path could be searched in node_modules/.pnpm to look up the exact copy of a dependency. (As you mentioned in the quoted text above.) I found this useful when I needed to set debugging breakpoints, which may not hit if I set them in the wrong peers suffixed copy of a dependency.

I wonder if there's a way we can get the best of both worlds? What if we noted a way to identify where a package was hardlinked into node_modules/.pnpm? This would basically be extra serialized metadata, and not anything pnpm re-reads.

  /ts-node@10.9.1(@types/node@14.18.36)(typescript@4.9.3):
    installDirectory: ts-node@10.9.1_xl7wyiapi7jo5c2pfz5vjm55na

Or perhaps just the peers hash:

  /ts-node@10.9.1(@types/node@14.18.36)(typescript@4.9.3):
    peersHash: xl7wyiapi7jo5c2pfz5vjm55na

The extra field makes pnpm-lock.yaml larger, which does has its own issues. Wanted to throw the idea out there anyway to get thoughts. I personally think it's worthwhile since it makes it more clear that peer dependency resolution affects where a dependency gets hardlinked, which is not obvious for most users.

@zkochan zkochan merged commit 3ebce5d into main Jan 9, 2023
@zkochan zkochan deleted the newlockfile branch January 9, 2023 12:37
@zkochan zkochan added this to the v7.24 milestone Jan 10, 2023
Comment on lines +116 to +120
peerSepIndex = version.indexOf('(')
if (peerSepIndex !== -1) {
peersSuffix = version.substring(peerSepIndex)
version = version.substring(0, peerSepIndex)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zkochan This does not work for patched dependencies. _ is used to separate version and patch hash: /ts-node/10.9.1_k5brw22k7hadiw3hedogf4eiee(@swc/core@1.3.27)(@types/node@18.11.18)(typescript@4.9.4)

I believe this should fix it, but I am not familiar with the codebase to know if it might affect anything else:

Suggested change
peerSepIndex = version.indexOf('(')
if (peerSepIndex !== -1) {
peersSuffix = version.substring(peerSepIndex)
version = version.substring(0, peerSepIndex)
}
peerSepIndex = version.indexOf('(')
if (peerSepIndex !== -1) {
peersSuffix = version.substring(peerSepIndex)
version = version.substring(0, peerSepIndex)
}
let patchSepIndex = version.indexOf("_");
if(patchSepIndex !== -1) {
version = version.substring(0, patchSepIndex);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion didn't quite work either. Here is a PR that fixes the isssue: #5964

chris-olszewski added a commit to vercel/turbo that referenced this pull request Jan 24, 2023
Add support for pnpm lockfile v6
(pnpm/pnpm#5810)

Notable changes in the lockfile v6:
- Lockfile keys are encoded as `/name@version` as opposed to
`/name/version`
- Specifiers are inline with resolved versions as opposed to being in
their own field
- Packages with multiple peer dependencies no longer have the peer
dependencies hashed

Notes:
- We currently just attempt to parse the v5 structure and if that fails
we attempt v6. We do this since lockfile version can be decided by a
combination of `pnpm` version and `.npmrc` contents. In the future once
`pnpm@8` is widely used we can switch the ordering of this if it becomes
a performance bottle neck
- A lot of the implementation was copy/pasta for the *slightly*
different structure. I could have gone the route of making most of these
functions work on `map[string]interface{}`, but I don't love doing type
assertions all over the place.
chris-olszewski added a commit to vercel/turbo that referenced this pull request Mar 16, 2023
### Description

Fixes #4133 (huge thanks to @shawnmcknight for the reproduction)

The crux of the issue was that if a dependency is patched and peer
dependencies than both get encoded into the lockfile key. We can't
construct a full expected lockfile key from the patch entry anymore as
the patch entry doesn't contain information about peer dependencies. To
circumvent this we now scan through the entire package list, parse the
key, extract the patch hash (if present), and then check if it matches a
patch listed in `patchedDependencies`.

Notes for reviewers:
- A large portion of this PR is actually porting the lockfile key
parsing logic that pnpm itself uses so we can extract the individual
pieces that we care about. This logic is in a slightly messy state as
the pnpm codebase is currently in a state where it supports v5 and v6
lockfiles. As mentioned in pnpm/pnpm#5810 once
pnpm@8 lands this code will start to be refactored. I tried to keep the
Go port as 1:1 as possible in hopes to make future maintenance easier.
- This PR includes new logic for v6 as pnpm now encodes patches in
lockfile keys: pnpm/pnpm#5979

### Testing Instructions

Added new packages to the pnpm lockfile fixtures that are patched *and*
have peer dependencies.

Also verify that the changes work as expected with the provided repro:
 - `make turbo`
 - Clone https://github.com/shawnmcknight/turbo-pnpm-patch-repro
- Remove private dependency: `pushd packages/test && pnpm rm @sheet/core
&& popd`
 - `turbo_dev --skip-infer prune --scope=test`
- Verify that `out/pnpm-lock.yaml` contains a `patchedDependencies`
section with both `@types/jsonwebtoken@8.5.9` and `moleculer@0.14.28` as
entries
neutron0831 added a commit to neutron0831/nlp100-ts that referenced this pull request Mar 29, 2023
@lpalmes lpalmes mentioned this pull request Apr 14, 2023
3 tasks
lfades pushed a commit to vercel/examples that referenced this pull request Apr 14, 2023
### Description

#### Problem
When updating the internal pnpm lockfile to
[v6](pnpm/pnpm#5810) ci stopped working because
it couldn't parse the lockfile correctly via `pnpm i --frozen-lockfile
--ignore-scripts`.

#### Solution
By updating pnpm to 8.2.0 we can solve this issue, and turborepo needs
to be updated as well to understand the new lockfiles otherwise it
crashes while trying to parse them.


<!--
Provide a URL to a live deployment where we can test your PR. If a demo
isn't possible feel free to omit this section.
-->

### Type of Change

- [ ] New Example
- [ ] Example updates (Bug fixes, new features, etc.)
- [x] Other (changes to the codebase, but not to examples)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants