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

fix(install): delay fetching directory until symlink #7049

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

JacobLey
Copy link
Contributor

@JacobLey JacobLey commented Sep 4, 2023

Ensure that fetches from directory are delayed until the package is synlinked. That will allow the created exportableManifest to correctly find it's dependencies.

Resolves: #7040

@@ -134,6 +135,53 @@ export async function linkPackages (
failOnMissingDependencies: true,
skipped: new Set(),
})
let linkedToRoot = 0
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This section is just moving the sym link logic up before the linkNewPackages (which invokes the fetcher internally)

It is otherwise copy-pasted.

Now the directory fetcher will have locally linked packages setup as necsessary

)
linkedToRoot = await linkDirectDeps(projectsToLink, { dedupe: opts.dedupeDirectDeps })
}
await opts.finishLockfileUpdates()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

finishLockfileUpdates is required to be called because it resolves the requiresBuild promise https://github.com/pnpm/pnpm/blob/main/pkg-manager/resolve-dependencies/src/index.ts#L349, which is used inside linkNewPackages

It can't be invoked before sym linking, because it triggers the fetch internally

let blocker: Promise<void>

if (opts.pkg.resolution.type === 'directory') {
const blocked: pDefer.DeferredPromise<void> = pDefer()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not in love with this implementation, but basically we want to block the execution of doFetchToStore until actually invoked

only apply this to directory fetchers though, others like tarball should still get kicked off.

Alternative approach is instead of passing the blocker promise, invoke doFetchToStore(filesIndexFile, fetching, target, blocker) in the promiseSharer callback... Would need to be careful about deduping

return {
dependenciesByProjectId,
dependenciesGraph,
finishLockfileUpdates: promiseShare(finishLockfileUpdates(dependenciesGraph, pendingRequiresBuilds, newLockfile)),
finishLockfileUpdates: () => {
finishLockfileUpdatesPromise ??= finishLockfileUpdates(dependenciesGraph, pendingRequiresBuilds, newLockfile)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

finishLockfileUpdates calls fetching internally, so we should defer this logic until necessary https://github.com/pnpm/pnpm/blob/main/pkg-manager/resolve-dependencies/src/index.ts#L337

Another alternative approach is to have it depend on some "lazy" version of fetching that does not trigger the invocation, but will subscribe to the underlying promise...

Ensure that fetches from directory are delayed until the package is synlinked.
That will allow the created `exportableManifest` to correctly find it's dependencies.

Resolves: pnpm#7040
@@ -52,3 +53,124 @@ test('install with no store integrity validation', async () => {

expect(fs.readFileSync('node_modules/is-positive/readme.md', 'utf8')).toBe('modified')
})

describe('install injected dependencies', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Woops didn't mean to include with this PR (#7048) but hopefully serves as proof of passing tests...

@zkochan
Copy link
Member

zkochan commented Sep 6, 2023

I would rather look into other solutions as this will probably make installation speed worse.

@JacobLey
Copy link
Contributor Author

JacobLey commented Sep 6, 2023

probably make installation speed worse

Depending on desired install behavior (i.e. should injected dependencies have an exportable manifest) (see #7048 (comment)) we can optionally pull all this new logic out the critical install path

And only apply it to deploy (as that is where feature originally was supposed to only impact).

Even if we want injected packages to be exportable... perhaps we could perform that retroactively after all the sym linking (and leave the directory fetcher logic alone)

@JacobLey
Copy link
Contributor Author

JacobLey commented Sep 6, 2023

Note with revert of original deploy PR, this is not really in a mergeable state.

But would like to keep it open for point of discussion about how to best implement deploy functionality, while either:

  1. Also applying logic to install, but not breaking anything (see test chore(install): add test for injected installs #7048)
  2. Avoiding impacting install altogether

@zkochan
Copy link
Member

zkochan commented Feb 18, 2024

What if we just upgrade export-manifest to not fail and keep the workspace: versions?

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.

8.7.0 regression: Install of monorepo that contains locally injected packages fails
2 participants