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: add git-branch-lockfile config to generate lockfile in each branch #4475

Merged
merged 30 commits into from Jun 14, 2022

Conversation

chengcyber
Copy link
Member

@chengcyber chengcyber commented Mar 24, 2022

Summary

Ref #4324

Detail

  1. Add a new setting called git-branch-lockfile specified .npmrc to turn on lockfile assembly feature.
  2. When this feature is on, pnpm install will update pnpm-lock.<branch_name>.yaml instead of pnpm-lock.yaml, this helps avoiding PR conflicts on a single pnpm-lock.yaml file.
  3. Add a new arg called --merge-git-branch-lockfiles for pnpm install. After PR merged into the main branch, run pnpm install --merge-git-branch-lockfiles to update the main lockfile and remove the others.

How to test it

Unit tests are added to test this feature, including

  • lockfile-file: read, write, lockfileName
  • core/install: gitBranchLockfile & mergeGitBranchLockfiles

@chengcyber chengcyber marked this pull request as ready for review March 25, 2022 06:30
@zkochan
Copy link
Member

zkochan commented Mar 25, 2022

Thanks for working on this!

packages/lockfile-file/src/gitChecks.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/lockfileName.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/read.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/read.ts Outdated Show resolved Hide resolved
@zkochan
Copy link
Member

zkochan commented Mar 26, 2022

Add a new arg called --clean-git-branch-lockfiles for pnpm install. After PR merged into the main branch, run pnpm install --clean-git-branch-lockfiles to update the main lockfile and remove the others.

As far as I understand from your changes, the branch lockfiles are completely ignored. They are just removed. What we need instead is something like a --merge-lockfiles option, which would merge the branch lockfiles with the main lockfile. I think for the merge to work, it is enough to read both the branch and the main lockfile and extend the packages field of the branch lockfile with the packages field of the main lockfile. pnpm should then do everything automaically.

I mean this field:

packages:

@chengcyber
Copy link
Member Author

chengcyber commented Mar 26, 2022

Updates:

  1. Syntax/logic tweaks
  2. extract gitChecks to @pnpm/git-utils

As far as I understand from your changes, the branch lockfiles are completely ignored. They are just removed.

Yes

What we need instead is something like a --merge-lockfiles option, which would merge the branch lockfiles with the main lockfile. I think for the merge to work, it is enough to read both the branch and the main lockfile and extend the packages field of the branch lockfile with the packages field of the main lockfile.

After a second thought, i think it's essential to merge something into the main lockfile(packages field as you said). therefore i got some questions:

Assuming there are multiple lockfiles, pnpm-lock.yaml, pnpm-lock.a.yaml, pnpm-lock.b.yaml, pnpm-lock.c.yaml.

Q1: Does merge order matter?

If so, Q2: How to define order?

From git, it might be main <- a <- b <- c. Is it the real order? One might be run `pnpm install` on branch c before a and b, but the MR for c is merged later than a and b.

Q3: Can i use timestamp in fs?

Not sure. `pnpm install` might be run in different machines, I wonder this would makes `timestamp` unreliable.

For packages field, it seems order doesn't matter.

Q4: Conflicts among those lockfiles.

Like the conflicts on `pnpm-lock.yaml`. Those branch lockfiles conflicts as well. Is there any merge algorithm in codebase?

It seems conflicts doesn't a big issue when merging

What do you think? @zkochan

I will refactor cleanGitBranchLockfiles to mergeGitBranchLockfiles

@zkochan
Copy link
Member

zkochan commented Mar 26, 2022

I think order doesn't matter (sort the branches alphabetically). The only thing that matters is that you merge the branch lockfiles into the main lockfile. So the entries in the branch lockfiles should have bigger priority than the ones in the main lockfile.

@chengcyber
Copy link
Member Author

Updates:

packages in lockfiles on branch are assigned into the main lockifile when --merge-git-branch-lockfiles


I think order doesn't matter (sort the branches alphabetically).

Q: I wonder why sort the branches when the order doesn't matter?

@chengcyber chengcyber requested a review from zkochan March 26, 2022 14:36
@zkochan
Copy link
Member

zkochan commented Mar 26, 2022

Q: I wonder why sort the branches when the order doesn't matter?

To make it deterministic.

@chengcyber
Copy link
Member Author

To make it deterministic.

Got it. For now, i don't sort everything. but i think eventually the keys of the packages field will be sorted by pnpm automatically.

packages/headless/src/index.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/read.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/read.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/read.ts Outdated Show resolved Hide resolved
packages/lockfile-file/src/lockfileName.ts Show resolved Hide resolved
@zkochan
Copy link
Member

zkochan commented Mar 26, 2022

What will happen if I rebase my branch from the main branch? Should I regenerate my branch lockfile?

@chengcyber
Copy link
Member Author

chengcyber commented Mar 26, 2022

I would like to write down the whole workflow from a user perspective.

When git-branch-lockfiles=true in .npmrc

  1. User A checkout a branch names feat-a, and run pnpm install
  2. The first time pnpm-lock.feat-a.yaml is missing. So the readWantedLockfile returns the content of pnpm-lock.yaml
  3. pnpm logic...
  4. The new lockfile is written to pnpm-lock.feat-a.yaml (pnpm-lock.yaml no changes)
  5. Dependencies change, rerun pnpm install, every lockfile changes are applied into pnpm-lock.feat-a.yaml
  6. ...
  7. User A rebases the main branch to feat-a branch. and run pnpm install
  8. readWantedLockfile returns the content of pnpm-lock.feat-a.yaml

Note:

When useGitBranchLockfile = true, readWantedLockfile first try to return the content of pnpm-lock.<branch>.yaml, if it doesn't exists, returns the content of pnpm-lock.yaml. Expect the case when useGitBranchLockfile = true && mergeGitBranchLockfiles = true, readWantedLockfile returns the content pnpm-lock.yaml, ignore other branch lock files.

In a nutshell, every lockfile changes are applied to the branch lockfile.


  1. User A,B,C creates MR A,B,C merged into the main branch.
  2. There are three lockfiles pnpm-lock.feat-a.yaml, pnpm-lock.feat-b.yaml, pnpm-lock.feat-c.yaml in the main branch
  3. If i run pnpm install w/o args, pnpm-lock.main.yaml will be created
  4. If i run pnpm install --merge-git-branch-lockfiles, readWantedLockfile returns the content of pnpm-lock.yaml, and merge the packages field of the all other lockfiles into the main Lockfile.
  5. pnpm logic...
  6. writeWantedLockfile writes the new lockfile into pnpm-lock.yaml
  7. cleanGitBranchLockfiles is called to remove all other branch lock files.

Note:

For now, there is no differences between the default branch and the feature branches. pnpm install --merge-git-branch-lockfiles is a explicit way to merge branch lockfiles.

In a nutshell, no magic in the git default branch.


I thought about other designs:

  1. Treat git default branch specially, if run pnpm install under the main branch, automatically set --merge-git-branch-lockfiles

Pros:

  • Convenience

Cons:

  • Implicit might be confusing.
  • Lack of flexibility, what if user want to do merge-git-branch-lockfiles on other branch instead of the main branch. (Like some repo set main as default but develops mainly on next branch)
  1. A new setting maybe called merge-git-branch-lockfiles-branch-pattern (Bad naming :\ ). User can specify
merge-git-branch-lockfiles-branch-pattern[]=main
merge-git-branch-lockfiles-branch-pattern[]=release-*
merge-git-branch-lockfiles-branch-pattern[]=next

When pnpm install runs in those branches, set --merge-git-branch-lockfiles automatically.

Pros:

  • Convenience
  • Flexibility

Cons:

  • Implicit might be confusing

@wbern
Copy link

wbern commented Mar 27, 2022

I think you nailed the design, but that's just me! Being dependent on git for this feature is perhaps unfortunate to some but I think we can afford to be opinionated here as the original problem is with regards to git merge conflicts.

@chengcyber
Copy link
Member Author

Hey @wbern , thank you to push this issue move forward. Since we are targeting git merge conflicts, the solution depends on git seems reasonable :)

@chengcyber chengcyber requested a review from zkochan March 27, 2022 09:34
@zkochan
Copy link
Member

zkochan commented Mar 27, 2022

The new lockfile is written to pnpm-lock.feat-a.yaml (pnpm-lock.yaml no changes)

This should happen only if there were changes to the lockfile.

If i run pnpm install w/o args, pnpm-lock.main.yaml will be created

I don't get it, so you'll have to remember to always run pnpm install --merge-git-branch-lockfiles on the main branch? I guess it might be fine if a user will never directly push to the main branch but only PRs will be used.

Comment on lines +265 to +261
pnpmConfig.useGitBranchLockfile = (() => {
if (typeof pnpmConfig['gitBranchLockfile'] === 'boolean') return pnpmConfig['gitBranchLockfile']
return false
})()
Copy link
Member

Choose a reason for hiding this comment

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

Why is this needed? The default value of use-git-branch-lockfile is set to false at line 184

Copy link
Member Author

Choose a reason for hiding this comment

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

The idea is to keep the same code style with useLockfile

image

packages/core/test/install/gitBranchLockfile.test.ts Outdated Show resolved Hide resolved
Comment on lines +185 to +187
if (opts.mergeGitBranchLockfiles) {
result.lockfile = await _mergeGitBranchLockfiles(result.lockfile, pkgPath, pkgPath, opts)
}
Copy link
Member

Choose a reason for hiding this comment

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

should this be inside the loop?

})
})

test('readWantedLockfile() when useGitBranchLockfile and mergeGitBranchLockfiles', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a scenario in which useGitBranchLockfile is false and mergeGitBranchLockfiles is true?

Copy link
Member Author

Choose a reason for hiding this comment

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

It could happen. The behavior is same as useGitBranchLockfile = true and mergeGitBranchLockfiles = true. That means useGitBranchLockfile is of no use when mergeGitBranchLockfiles = true

pnpm-lock.yaml Outdated
@@ -8278,7 +8295,7 @@ packages:
resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==}
engines: {node: '>=8.10.0'}
peerDependencies:
eslint: '*'
eslint: '>=5.16.0 || *'
Copy link
Member

Choose a reason for hiding this comment

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

Update your pnpm version. You are not using the latest v6, that is why the lockfile gets changed like this.

@zkochan
Copy link
Member

zkochan commented Mar 29, 2022

This is a big change, so I recommend you to publish a version from this branch and try it in your team. Gather feedback to see if it works for you.

@chengcyber
Copy link
Member Author

This is a big change, so I recommend you to publish a version from this branch and try it in your team. Gather feedback to see if it works for you.

This make sense. Is it possible to publish an alpha version officially?

@zkochan
Copy link
Member

zkochan commented Mar 29, 2022

🚢 pnpm@7.0.0-pr4475.0

@nikoladev
Copy link
Contributor

🚢 pnpm@7.0.0-pr4475.0

@zkochan FYI, running pnpm add -g pnpm@^7.0.0-beta.2 installs the version from this PR. I assume because the p in -pr4475.0 has a higher value than the b in -beta.2, so by using ^ users will inadvertently get the pnpm version from this PR.

It probably also means that a future 7.0.0-beta.3 version will be ignored for 7.0.0-pr4475.0 if using ^ when installing, though I can't say that for sure.

I don't know what a good solution is, though.

@zkochan
Copy link
Member

zkochan commented Mar 30, 2022

This is confusing indeed. Maybe we need to change how canary versions are installed and disallow installing them by range. So pnpm@^7.0.0-beta.2 would install 7.0.0-beta.2 even if 7.0.0-beta.3 is out.

@nikoladev
Copy link
Contributor

Hmmm, that seems like it would break expectations. Because I would want to get newer pre-release versions of a package if I use ^7.0.0-beta.2.

Maybe we could change the naming of test releases like this one? So we could name it something like 0.0.0-pr4475.0. This would still allow users to use ^ for pre-releases but remove the confusion (because I don't think anyone will be installing a pre-release of version 0.0.0).

@chengcyber
Copy link
Member Author

Hi @zkochan ,

I am adapting our monorepo to pnpm@7, since this alpha version is based on pnpm@7. I run into the NODE_PATH issue in isolated node link mode, which should be fixed by #4513

Latest main branch rebased, could you kindly publish an alpha version for this PR?

@zkochan
Copy link
Member

zkochan commented Sep 15, 2023

Hey @chengcyber

I just realized that we don't have any information about this in the docs. Even the settings are not documented. We need to add a new page about this feature and describe how to use it.

@chengcyber
Copy link
Member Author

Hi @zkochan, you are right. I created a PR to add documents for this feature at pnpm/pnpm.io#455

@Crayon-ShinChan
Copy link

Crayon-ShinChan commented Dec 9, 2023

vercel can't detect the file pnpm-lock.branch.yaml, is there any solution for this?

Now specific issue is:

  1. create a new branch, which has pnpm-lock.yaml originally
  2. When I add a new package into package.json, then I got a pnpm-lock.branch.yaml
  3. push code to vercel, it will deploy my code and vercel use pnpm install --frozen-lockfile

So I ended up got the error in vercel:

ERR_PNPM_OUTDATED_LOCKFILE  Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with package.json

One way to solve this is to delete pnpm-lock.yaml file in my branch, but according to https://pnpm.io/git. This is not an encouraged way due to pnpm-lock.yaml or pnpm-lock.branch.yaml help us:

  • it enables faster installation for CI and production environments, due to being able to skip package resolution
  • it enforces consistent installations and resolution between development, testing, and production environments, meaning the packages used in testing and production will be exactly the same as when you developed your project

Is there any better solution for this?

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

9 participants