fix: honor never/only-built-dependencies in workspace setup (#7141)

close #7135
close #5407

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Alvis Tang
2023-10-04 00:42:13 +08:00
committed by GitHub
parent 832e288263
commit 12f45a83d2
13 changed files with 269 additions and 20 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/plugin-commands-installation": patch
"@pnpm/plugin-commands-rebuild": patch
"@pnpm/core": patch
"pnpm": patch
---
Use `neverBuiltDependencies` and `onlyBuiltDependencies` from the root `package.json` of the workspace, when `shared-workspace-lockfile` is set to `false` [#7141](https://github.com/pnpm/pnpm/pull/7141).

View File

@@ -45,6 +45,7 @@
"write-yaml-file": "^5.0.0"
},
"dependencies": {
"@pnpm/builder.policy": "1.0.0",
"@pnpm/calc-dep-state": "workspace:*",
"@pnpm/cli-utils": "workspace:*",
"@pnpm/common-cli-options-help": "workspace:*",

View File

@@ -45,6 +45,8 @@ export interface StrictRebuildOptions {
pending: boolean
shamefullyHoist: boolean
deployAllFiles: boolean
neverBuiltDependencies?: string[]
onlyBuiltDependencies?: string[]
}
export type RebuildOptions = Partial<StrictRebuildOptions> &

View File

@@ -25,6 +25,7 @@ import { logger, streamParser } from '@pnpm/logger'
import { writeModulesManifest } from '@pnpm/modules-yaml'
import { createOrConnectStoreController } from '@pnpm/store-connection-manager'
import { type ProjectManifest } from '@pnpm/types'
import { createAllowBuildFunction } from '@pnpm/builder.policy'
import * as dp from '@pnpm/dependency-path'
import { hardLinkDir } from '@pnpm/fs.hard-link-dir'
import loadJsonFile from 'load-json-file'
@@ -282,6 +283,9 @@ async function _rebuild (
const warn = (message: string) => {
logger.info({ message, prefix: opts.dir })
}
const allowBuild = createAllowBuildFunction(opts) ?? (() => true)
const groups = chunks.map((chunk) => chunk.filter((depPath) => ctx.pkgsToRebuild.has(depPath) && !ctx.skipped.has(depPath)).map((depPath) =>
async () => {
const pkgSnapshot = pkgSnapshots[depPath]
@@ -318,7 +322,8 @@ async function _rebuild (
return
}
}
const hasSideEffects = await runPostinstallHooks({
const hasSideEffects = allowBuild(pkgInfo.name) && await runPostinstallHooks({
depPath,
extraBinPaths,
extraEnv: opts.extraEnv,

View File

@@ -94,6 +94,8 @@ export async function handler (
reporter?: (logObj: LogBase) => void
pending: boolean
skipIfHasSideEffectsCache?: boolean
neverBuiltDependencies?: string[]
onlyBuiltDependencies?: string[]
},
params: string[]
) {

View File

@@ -375,3 +375,44 @@ test(`rebuild should not fail on incomplete ${WANTED_LOCKFILE}`, async () => {
storeDir,
}, [])
})
test('never build neverBuiltDependencies', async () => {
const project = prepare()
const cacheDir = path.resolve('cache')
const storeDir = path.resolve('store')
await execa('node', [
pnpmBin,
'add',
'@pnpm.e2e/install-script-example@1.0.0',
'@pnpm.e2e/pre-and-postinstall-scripts-example@1.0.0',
`--registry=${REGISTRY}`,
`--store-dir=${storeDir}`,
`--cache-dir=${cacheDir}`,
])
const modulesManifest = await project.readModulesManifest()
await rebuild.handler(
{
...DEFAULT_OPTS,
neverBuiltDependencies: ['@pnpm.e2e/pre-and-postinstall-scripts-example'],
cacheDir,
dir: process.cwd(),
pending: false,
registries: modulesManifest!.registries!,
storeDir,
},
[]
)
expect(
await exists(
'node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-prepare.js'
)
).toBeFalsy()
expect(
await exists(
'node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
).toBeTruthy()
})

View File

@@ -222,3 +222,191 @@ test.skip('rebuild multiple packages in correct order', async () => {
expect(outputs1).toStrictEqual(['project-1', 'project-2'])
expect(outputs2).toStrictEqual(['project-1', 'project-3'])
})
test('never build neverBuiltDependencies', async () => {
const projects = preparePackages([
{
name: 'project-1',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/install-script-example': '*',
'@pnpm.e2e/pre-and-postinstall-scripts-example': '*',
},
},
{
name: 'project-2',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/install-script-example': '*',
'@pnpm.e2e/pre-and-postinstall-scripts-example': '*',
},
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(
process.cwd(),
[]
)
await execa(
'node',
[
pnpmBin,
'install',
'-r',
`--registry=${REGISTRY}`,
`--store-dir=${path.resolve(DEFAULT_OPTS.storeDir)}`,
`--cache-dir=${path.resolve(DEFAULT_OPTS.cacheDir)}`,
'--ignore-scripts',
'--reporter=append-only',
],
{ stdout: 'inherit' }
)
await projects['project-1'].hasNot(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-1'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-1'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
const modulesManifest = await projects['project-1'].readModulesManifest()
await rebuild.handler(
{
...DEFAULT_OPTS,
neverBuiltDependencies: ['@pnpm.e2e/pre-and-postinstall-scripts-example'],
allProjects,
dir: process.cwd(),
recursive: true,
registries: modulesManifest!.registries!,
selectedProjectsGraph,
workspaceDir: process.cwd(),
},
[]
)
await projects['project-1'].has(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-2'].has(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-1'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-1'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
})
test('only build onlyBuiltDependencies', async () => {
const projects = preparePackages([
{
name: 'project-1',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/install-script-example': '*',
'@pnpm.e2e/pre-and-postinstall-scripts-example': '*',
},
},
{
name: 'project-2',
version: '1.0.0',
dependencies: {
'@pnpm.e2e/install-script-example': '*',
'@pnpm.e2e/pre-and-postinstall-scripts-example': '*',
},
},
])
const { allProjects, selectedProjectsGraph } = await readProjects(
process.cwd(),
[]
)
await execa(
'node',
[
pnpmBin,
'install',
'-r',
`--registry=${REGISTRY}`,
`--store-dir=${path.resolve(DEFAULT_OPTS.storeDir)}`,
`--cache-dir=${path.resolve(DEFAULT_OPTS.cacheDir)}`,
'--ignore-scripts',
'--reporter=append-only',
],
{ stdout: 'inherit' }
)
await projects['project-1'].hasNot(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-1'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-1'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
const modulesManifest = await projects['project-1'].readModulesManifest()
await rebuild.handler(
{
...DEFAULT_OPTS,
onlyBuiltDependencies: ['@pnpm.e2e/pre-and-postinstall-scripts-example'],
allProjects,
dir: process.cwd(),
recursive: true,
registries: modulesManifest!.registries!,
selectedProjectsGraph,
workspaceDir: process.cwd(),
},
[]
)
await projects['project-1'].hasNot(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-2'].hasNot(
'@pnpm.e2e/install-script-example/generated-by-install.js'
)
await projects['project-1'].has(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-1'].has(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
await projects['project-2'].has(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js'
)
await projects['project-2'].has(
'@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js'
)
})

View File

@@ -17,6 +17,7 @@
},
"dependencies": {
"@pnpm/build-modules": "workspace:*",
"@pnpm/builder.policy": "1.0.0",
"@pnpm/calc-dep-state": "workspace:*",
"@pnpm/constants": "workspace:*",
"@pnpm/core-loggers": "workspace:*",

View File

@@ -1,6 +1,7 @@
import crypto from 'crypto'
import path from 'path'
import { buildModules, type DepsStateCache, linkBinsOfDependencies } from '@pnpm/build-modules'
import { createAllowBuildFunction } from '@pnpm/builder.policy'
import {
LAYOUT_VERSION,
LOCKFILE_VERSION,
@@ -1364,22 +1365,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
}
}
function createAllowBuildFunction (
opts: {
neverBuiltDependencies?: string[]
onlyBuiltDependencies?: string[]
}
): undefined | ((pkgName: string) => boolean) {
if (opts.neverBuiltDependencies != null && opts.neverBuiltDependencies.length > 0) {
const neverBuiltDependencies = new Set(opts.neverBuiltDependencies)
return (pkgName) => !neverBuiltDependencies.has(pkgName)
} else if (opts.onlyBuiltDependencies != null) {
const onlyBuiltDependencies = new Set(opts.onlyBuiltDependencies)
return (pkgName) => onlyBuiltDependencies.has(pkgName)
}
return undefined
}
const installInContext: InstallFunction = async (projects, ctx, opts) => {
try {
if (opts.nodeLinker === 'hoisted' && !opts.lockfileOnly) {

View File

@@ -100,6 +100,7 @@ export type ImportCommandOptions = Pick<Config,
| 'ignoreWorkspaceCycles'
| 'disallowWorkspaceCycles'
| 'sharedWorkspaceLockfile'
| 'rootProjectManifest'
> & CreateStoreControllerOptions & Omit<InstallOptions, 'storeController' | 'lockfileOnly' | 'preferredVersions'>
export async function handler (
@@ -170,7 +171,7 @@ export async function handler (
const manifest = await readProjectManifestOnly(opts.dir)
const installOpts = {
...opts,
...getOptionsFromRootManifest(manifest),
...getOptionsFromRootManifest({ ...opts.rootProjectManifest, ...manifest }),
lockfileOnly: true,
preferredVersions,
storeController: store.ctrl,

View File

@@ -58,6 +58,7 @@ export type InstallDepsOptions = Pick<Config,
| 'production'
| 'rawLocalConfig'
| 'registries'
| 'rootProjectManifest'
| 'save'
| 'saveDev'
| 'saveExact'
@@ -187,6 +188,7 @@ when running add/update with the --workspace option')
params,
{
...opts,
...getOptionsFromRootManifest(opts.rootProjectManifest ?? {}),
forceHoistPattern,
forcePublicHoistPattern,
allProjectsGraph,
@@ -219,7 +221,7 @@ when running add/update with the --workspace option')
const store = await createOrConnectStoreController(opts)
const installOpts: Omit<MutateModulesOptions, 'allProjects'> = {
...opts,
...getOptionsFromRootManifest(manifest),
...getOptionsFromRootManifest({ ...opts.rootProjectManifest, ...manifest }),
forceHoistPattern,
forcePublicHoistPattern,
// In case installation is done in a multi-package repository

View File

@@ -58,6 +58,7 @@ type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
| 'pnpmfile'
| 'rawLocalConfig'
| 'registries'
| 'rootProjectManifest'
| 'save'
| 'saveDev'
| 'saveExact'
@@ -363,7 +364,7 @@ export async function recursive (
{
...installOpts,
...localConfig,
...getOptionsFromRootManifest(manifest),
...getOptionsFromRootManifest({ ...opts.rootProjectManifest, ...manifest }),
bin: path.join(rootDir, 'node_modules', '.bin'),
dir: rootDir,
hooks,
@@ -412,6 +413,7 @@ export async function recursive (
) {
await rebuild.handler({
...opts,
...getOptionsFromRootManifest(opts.rootProjectManifest ?? {}),
pending: opts.pending === true,
skipIfHasSideEffectsCache: true,
}, [])

11
pnpm-lock.yaml generated
View File

@@ -1158,6 +1158,9 @@ importers:
exec/plugin-commands-rebuild:
dependencies:
'@pnpm/builder.policy':
specifier: 1.0.0
version: 1.0.0
'@pnpm/calc-dep-state':
specifier: workspace:*
version: link:../../packages/calc-dep-state
@@ -2862,6 +2865,9 @@ importers:
'@pnpm/build-modules':
specifier: workspace:*
version: link:../../exec/build-modules
'@pnpm/builder.policy':
specifier: 1.0.0
version: 1.0.0
'@pnpm/calc-dep-state':
specifier: workspace:*
version: link:../../packages/calc-dep-state
@@ -7733,6 +7739,11 @@ packages:
dev: false
optional: true
/@pnpm/builder.policy@1.0.0:
resolution: {integrity: sha512-0BhTu6nZ5DzIjJzuTJWOVbd1cN+0wu8k1aXXSawT+/i9Biq/3vuzqnxiTSt5e7IStZ01n59GTMvCoZ/VRAYQiA==}
engines: {node: '>=12.22.0'}
dev: false
/@pnpm/byline@1.0.0:
resolution: {integrity: sha512-61tmh+k7hnKK6b2XbF4GvxmiaF3l2a+xQlZyeoOGBs7mXU3Ie8iCAeAnM0+r70KiqTrgWvBCjMeM+W3JarJqaQ==}
engines: {node: '>=12.17'}