fix(deploy): inherit pnpm from the root manifest (#8991)

* fix(deploy): inherit `pnpm` from the root manifest

* fix: eslint

* refactor: share code

* test: add

* fix: eslint

* fix: remove duplicate dep from package.json
This commit is contained in:
Khải
2025-01-26 23:26:16 +07:00
committed by GitHub
parent cb33ff59d2
commit 9a44e6ce87
11 changed files with 134 additions and 6 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/constants": minor
"@pnpm/find-packages": patch
"@pnpm/plugin-commands-deploy": patch
"pnpm": patch
---
`pnpm deploy` should inherit the `pnpm` object from the root `package.json` [#8991](https://github.com/pnpm/pnpm/pull/8991).

View File

@@ -16,3 +16,5 @@ export const WORKSPACE_MANIFEST_FILENAME = 'pnpm-workspace.yaml'
export const ABBREVIATED_META_DIR = 'metadata-v1.3'
export const FULL_META_DIR = 'metadata-full-v1.3' // This is currently not used at all
export const FULL_FILTERED_META_DIR = 'metadata-v1.3'
export const USEFUL_NON_ROOT_PNPM_FIELDS = ['executionEnv'] as const

6
pnpm-lock.yaml generated
View File

@@ -6181,6 +6181,9 @@ importers:
'@pnpm/config':
specifier: workspace:*
version: link:../../config/config
'@pnpm/constants':
specifier: workspace:*
version: link:../../packages/constants
'@pnpm/dependency-path':
specifier: workspace:*
version: link:../../packages/dependency-path
@@ -7918,6 +7921,9 @@ importers:
'@pnpm/cli-utils':
specifier: workspace:*
version: link:../../cli/cli-utils
'@pnpm/constants':
specifier: workspace:*
version: link:../../packages/constants
'@pnpm/fs.find-packages':
specifier: workspace:*
version: link:../../fs/find-packages

View File

@@ -49,6 +49,7 @@
"@pnpm/cli-utils": "workspace:*",
"@pnpm/common-cli-options-help": "workspace:*",
"@pnpm/config": "workspace:*",
"@pnpm/constants": "workspace:*",
"@pnpm/dependency-path": "workspace:*",
"@pnpm/directory-fetcher": "workspace:*",
"@pnpm/error": "workspace:*",

View File

@@ -2,6 +2,7 @@ import path from 'path'
import url from 'url'
import normalizePath from 'normalize-path'
import pick from 'ramda/src/pick'
import { USEFUL_NON_ROOT_PNPM_FIELDS } from '@pnpm/constants'
import * as dp from '@pnpm/dependency-path'
import {
type DirectoryResolution,
@@ -42,7 +43,8 @@ export interface CreateDeployFilesOptions {
deployDir: string
lockfile: LockfileObject
lockfileDir: string
manifest: DeployManifest
rootProjectManifest?: Pick<ProjectManifest, 'pnpm'>
selectedProjectManifest: DeployManifest
projectId: ProjectId
rootProjectManifestDir: string
}
@@ -57,7 +59,8 @@ export function createDeployFiles ({
deployDir,
lockfile,
lockfileDir,
manifest,
rootProjectManifest,
selectedProjectManifest,
projectId,
rootProjectManifestDir,
}: CreateDeployFilesOptions): DeployFiles {
@@ -141,12 +144,13 @@ export function createDeployFiles ({
packages: targetPackageSnapshots,
},
manifest: {
...pick(INHERITED_MANIFEST_KEYS, manifest),
...pick(INHERITED_MANIFEST_KEYS, selectedProjectManifest),
dependencies: targetSnapshot.dependencies,
devDependencies: targetSnapshot.devDependencies,
optionalDependencies: targetSnapshot.optionalDependencies,
pnpm: {
...manifest.pnpm,
...rootProjectManifest?.pnpm,
...pick(USEFUL_NON_ROOT_PNPM_FIELDS, selectedProjectManifest.pnpm ?? {}),
overrides: undefined, // the effects of package overrides should already be part of the package snapshots
patchedDependencies: undefined,
packageExtensions: undefined, // the effects of the package extensions should already be part of the package snapshots

View File

@@ -181,6 +181,7 @@ async function deployFromSharedLockfile (
const {
allProjects,
lockfileDir,
rootProjectManifest,
rootProjectManifestDir,
workspaceDir,
} = opts
@@ -202,7 +203,8 @@ async function deployFromSharedLockfile (
deployDir,
lockfile,
lockfileDir,
manifest: selectedProject.manifest,
rootProjectManifest,
selectedProjectManifest: selectedProject.manifest,
projectId,
rootProjectManifestDir,
})

View File

@@ -255,6 +255,103 @@ test('deploy with a shared lockfile after full install', async () => {
}
})
test('the deploy manifest should inherit the pnpm object from the root manifest and the manifest of the selected project', async () => {
const preparedManifests: Record<'root' | 'project-0', ProjectManifest> = {
root: {
name: 'root',
version: '0.0.0',
private: true,
pnpm: {
onlyBuiltDependencies: ['from-root'],
overrides: {
'is-positive': '2.0.0',
},
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
'project-0': {
name: 'project-0',
version: '0.0.0',
private: true,
dependencies: {
'is-positive': '3.1.0',
},
pnpm: {
onlyBuiltDependencies: ['from-project-0'],
overrides: {
'is-positive': '=1.0.0',
},
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
}
preparePackages([
{
location: '.',
package: preparedManifests.root,
},
preparedManifests['project-0'],
])
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
rootProjectManifest: preparedManifests.root,
rootProjectManifestDir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('is-positive')
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest.pnpm).toStrictEqual({
onlyBuiltDependencies: preparedManifests.root.pnpm!.onlyBuiltDependencies,
executionEnv: preparedManifests['project-0'].pnpm!.executionEnv,
} as ProjectManifest['pnpm'])
expect(manifest.pnpm?.overrides).toBeUndefined() // by design
expect(readPackageJson('deploy/node_modules/is-positive/')).toHaveProperty(['version'], preparedManifests.root.pnpm!.overrides!['is-positive'])
expect(project.readLockfile().importers).toStrictEqual({
'.': {
dependencies: {
'is-positive': {
specifier: preparedManifests.root.pnpm!.overrides!['is-positive'],
version: preparedManifests.root.pnpm!.overrides!['is-positive'],
},
},
},
} as LockfileFile['importers'])
})
test('deploy with a shared lockfile and --prod filter should not fail even if dev workspace package does not exist (#8778)', async () => {
preparePackages([
{

View File

@@ -48,6 +48,9 @@
{
"path": "../../lockfile/types"
},
{
"path": "../../packages/constants"
},
{
"path": "../../packages/dependency-path"
},

View File

@@ -30,6 +30,7 @@
"homepage": "https://github.com/pnpm/pnpm/blob/main/workspace/find-packages#readme",
"dependencies": {
"@pnpm/cli-utils": "workspace:*",
"@pnpm/constants": "workspace:*",
"@pnpm/fs.find-packages": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/util.lex-comparator": "catalog:"

View File

@@ -1,4 +1,5 @@
import { packageIsInstallable } from '@pnpm/cli-utils'
import { USEFUL_NON_ROOT_PNPM_FIELDS } from '@pnpm/constants'
import { type ProjectManifest, type Project, type SupportedArchitectures } from '@pnpm/types'
import { lexCompare } from '@pnpm/util.lex-comparator'
import { findPackages } from '@pnpm/fs.find-packages'
@@ -64,7 +65,7 @@ export async function findWorkspacePackagesNoCheck (workspaceRoot: string, opts?
const uselessNonRootManifestFields: Array<keyof ProjectManifest> = ['resolutions']
type ProjectManifestPnpm = Required<ProjectManifest>['pnpm']
const usefulNonRootPnpmFields: Array<keyof ProjectManifestPnpm> = ['executionEnv']
const usefulNonRootPnpmFields: ReadonlyArray<keyof ProjectManifestPnpm> = USEFUL_NON_ROOT_PNPM_FIELDS
function checkNonRootProjectManifest ({ manifest, rootDir }: Project): void {
const warn = printNonRootFieldWarning.bind(null, rootDir)

View File

@@ -15,6 +15,9 @@
{
"path": "../../fs/find-packages"
},
{
"path": "../../packages/constants"
},
{
"path": "../../packages/logger"
},