fix: never skip resolution if the overrides were changed

Never skip resolution if the overrides were changed,
even if frozenLockfile is true.

PR #3079
This commit is contained in:
Zoltan Kochan
2021-01-18 03:09:27 +02:00
committed by GitHub
parent 6f12541662
commit af897c3246
11 changed files with 81 additions and 20 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/filter-lockfile": patch
---
Include all the properties of the filtered lockfile.

View File

@@ -0,0 +1,5 @@
---
"supi": patch
---
Resolution should never be skipped if the overrides were updated.

View File

@@ -0,0 +1,5 @@
---
"supi": patch
---
Installation should fail if the overrides in the lockfile don't match the ones in the package.json and the frozenLockfile option is on.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/lockfile-file": patch
---
An empty overrides field should be removed from the lockfile before saving.

View File

@@ -22,11 +22,11 @@ export default function filterLockfile (
pairs = pairs.filter(([depPath, pkg]) => !pkg.optional)
}
return {
...lockfile,
importers: Object.keys(lockfile.importers).reduce((acc, importerId) => {
acc[importerId] = filterImporter(lockfile.importers[importerId], opts.include)
return acc
}, {}),
lockfileVersion: lockfile.lockfileVersion,
packages: R.fromPairs(pairs),
}
}

View File

@@ -41,8 +41,8 @@ export default function filterByImporters (
}, { ...lockfile.importers })
return {
...lockfile,
importers,
lockfileVersion: lockfile.lockfileVersion,
packages,
}
}

View File

@@ -67,8 +67,8 @@ export default function filterByImportersAndEngine (
}, { ...lockfile.importers })
return {
...lockfile,
importers,
lockfileVersion: lockfile.lockfileVersion,
packages,
}
}

View File

@@ -73,8 +73,9 @@ function isEmptyLockfile (lockfile: Lockfile) {
type LockfileFile = Omit<Lockfile, 'importers'> & Partial<ProjectSnapshot> & Partial<Pick<Lockfile, 'importers'>>
function normalizeLockfile (lockfile: Lockfile, forceSharedFormat: boolean) {
let lockfileToSave!: LockfileFile
if (!forceSharedFormat && R.equals(R.keys(lockfile.importers), ['.'])) {
const lockfileToSave: LockfileFile = {
lockfileToSave = {
...lockfile,
...lockfile.importers['.'],
}
@@ -87,9 +88,8 @@ function normalizeLockfile (lockfile: Lockfile, forceSharedFormat: boolean) {
if (R.isEmpty(lockfileToSave.packages) || !lockfileToSave.packages) {
delete lockfileToSave.packages
}
return lockfileToSave
} else {
const lockfileToSave = {
lockfileToSave = {
...lockfile,
importers: R.keys(lockfile.importers).reduce((acc, alias) => {
const importer = lockfile.importers[alias]
@@ -108,8 +108,11 @@ function normalizeLockfile (lockfile: Lockfile, forceSharedFormat: boolean) {
if (R.isEmpty(lockfileToSave.packages) || !lockfileToSave.packages) {
delete lockfileToSave.packages
}
return lockfileToSave
}
if (lockfileToSave.overrides && R.isEmpty(lockfileToSave.overrides)) {
delete lockfileToSave.overrides
}
return lockfileToSave
}
export default async function writeLockfiles (

View File

@@ -19,7 +19,6 @@ import semver = require('semver')
export default function allProjectsAreUpToDate (
projects: Array<ProjectOptions & { id: string }>,
opts: {
overrides?: Record<string, string>
linkWorkspacePackages: boolean
wantedLockfile: Lockfile
workspacePackages: WorkspacePackages
@@ -32,7 +31,7 @@ export default function allProjectsAreUpToDate (
manifestsByDir,
workspacePackages: opts.workspacePackages,
})
return R.equals(opts.wantedLockfile.overrides ?? {}, opts.overrides ?? {}) && pEvery(projects, (project) => {
return pEvery(projects, (project) => {
const importer = opts.wantedLockfile.importers[project.id]
return !hasLocalTarballDepsInRoot(importer) &&
_satisfiesPackageManifest(project.manifest, project.id) &&

View File

@@ -175,6 +175,8 @@ export async function mutateModules (
return result
async function _install (): Promise<Array<{ rootDir: string, manifest: ProjectManifest }>> {
const needsFullResolution = !R.equals(ctx.wantedLockfile.overrides ?? {}, overrides ?? {})
ctx.wantedLockfile.overrides = overrides
const frozenLockfile = opts.frozenLockfile ||
opts.frozenLockfileIfExists && ctx.existsWantedLockfile
if (
@@ -184,18 +186,21 @@ export async function mutateModules (
installsOnly &&
(
frozenLockfile ||
!needsFullResolution &&
opts.preferFrozenLockfile &&
(!opts.pruneLockfileImporters || Object.keys(ctx.wantedLockfile.importers).length === ctx.projects.length) &&
ctx.existsWantedLockfile &&
ctx.wantedLockfile.lockfileVersion === LOCKFILE_VERSION &&
await allProjectsAreUpToDate(ctx.projects, {
linkWorkspacePackages: opts.linkWorkspacePackagesDepth >= 0,
overrides,
wantedLockfile: ctx.wantedLockfile,
workspacePackages: opts.workspacePackages,
})
)
) {
if (needsFullResolution) {
throw new PnpmError('FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE', 'Cannot perform a frozen installation because the lockfile needs updates')
}
if (!ctx.existsWantedLockfile) {
if (ctx.projects.some((project) => pkgHasDependencies(project.manifest))) {
throw new Error(`Headless installation requires a ${WANTED_LOCKFILE} file`)
@@ -428,6 +433,7 @@ export async function mutateModules (
...opts,
currentLockfileIsUpToDate: !ctx.existsWantedLockfile || ctx.currentLockfileIsUpToDate,
makePartialCurrentLockfile,
needsFullResolution,
overrides,
update: opts.update || !installsOnly,
updateLockfileMinorVersion: true,
@@ -573,6 +579,7 @@ async function installInContext (
ctx: PnpmContext<DependenciesMutation>,
opts: StrictInstallOptions & {
makePartialCurrentLockfile: boolean
needsFullResolution: boolean
overrides?: Record<string, string>
updateLockfileMinorVersion: boolean
preferredVersions?: PreferredVersions
@@ -600,15 +607,6 @@ async function installInContext (
}
}
}
const overridesChanged = !R.equals(opts.overrides ?? {}, ctx.wantedLockfile.overrides ?? {})
if (!R.isEmpty(opts.overrides ?? {})) {
ctx.wantedLockfile.overrides = opts.overrides
} else {
delete ctx.wantedLockfile.overrides
// We were only setting the resolutions field in pnpm v5.10.0, which was never latest,
// so we can probably remove this line safely in future pnpm versions.
delete ctx.wantedLockfile['resolutions']
}
await Promise.all(
projects
@@ -637,7 +635,7 @@ async function installInContext (
const forceFullResolution = ctx.wantedLockfile.lockfileVersion !== LOCKFILE_VERSION ||
!opts.currentLockfileIsUpToDate ||
opts.force ||
overridesChanged ||
opts.needsFullResolution ||
ctx.lockfileHadConflicts
const _toResolveImporter = toResolveImporter.bind(null, {
defaultUpdateDepth: (opts.update || opts.updateMatching) ? opts.depth : -1,

View File

@@ -1,3 +1,4 @@
import PnpmError from '@pnpm/error'
import { prepareEmpty } from '@pnpm/prepare'
import { addDistTag } from '@pnpm/registry-mock'
import { addDependenciesToPackage, mutateModules } from 'supi'
@@ -32,6 +33,8 @@ test('versions are replaced with versions specified through pnpm.overrides field
'bar@^100.0.0': '100.1.0',
'dep-of-pkg-with-1-dep': '101.0.0',
})
const currentLockfile = await project.readCurrentLockfile()
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
}
// The lockfile is updated if the overrides are changed
@@ -54,7 +57,45 @@ test('versions are replaced with versions specified through pnpm.overrides field
'bar@^100.0.0': '100.0.0',
'dep-of-pkg-with-1-dep': '101.0.0',
})
const currentLockfile = await project.readCurrentLockfile()
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
}
await mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true }))
{
const lockfile = await project.readLockfile()
expect(lockfile.overrides).toStrictEqual({
'foobarqar>foo': 'npm:qar@100.0.0',
'bar@^100.0.0': '100.0.0',
'dep-of-pkg-with-1-dep': '101.0.0',
})
const currentLockfile = await project.readCurrentLockfile()
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
}
manifest.pnpm!.overrides!['bar@^100.0.0'] = '100.0.1'
await expect(
mutateModules([
{
buildIndex: 0,
manifest,
mutation: 'install',
rootDir: process.cwd(),
},
], await testDefaults({ frozenLockfile: true }))
).rejects.toThrow(
new PnpmError('FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE',
'Cannot perform a frozen installation because the lockfile needs updates'
)
)
})
test('versions are replaced with versions specified through "resolutions" field (for Yarn compatibility)', async () => {