mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-15 02:18:31 -05:00
feat: create a new field for allowing/disallowing builds (#10311)
ref #10235
This commit is contained in:
27
.changeset/cold-weeks-work.md
Normal file
27
.changeset/cold-weeks-work.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
"@pnpm/workspace.manifest-writer": minor
|
||||
"@pnpm/types": minor
|
||||
"@pnpm/config": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Added support for `allowBuilds`, which is a new field that can be used instead of `onlyBuiltDependencies` and `ignoredBuiltDependencies`. The new `allowBuilds` field in your `pnpm-workspace.yaml` uses a map of package matchers to explicitly allow (`true`) or disallow (`false`) script execution. This allows for a single, easy-to-manage source of truth for your build permissions.
|
||||
|
||||
**Example Usage.** To explicitly allow all versions of `esbuild` to run scripts and prevent `core-js` from running them:
|
||||
|
||||
```yaml
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
core-js: false
|
||||
```
|
||||
|
||||
The example above achieves the same result as the previous configuration:
|
||||
|
||||
```yaml
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
ignoredBuiltDependencies:
|
||||
- core-js
|
||||
```
|
||||
|
||||
Related PR: [#10311](https://github.com/pnpm/pnpm/pull/10311)
|
||||
@@ -185,6 +185,7 @@ export interface Config extends OptionsFromRootManifest {
|
||||
gitShallowHosts?: string[]
|
||||
legacyDirFiltering?: boolean
|
||||
onlyBuiltDependencies?: string[]
|
||||
allowBuilds?: Record<string, boolean | string>
|
||||
dedupePeerDependents?: boolean
|
||||
patchesDir?: string
|
||||
ignoreWorkspaceCycles?: boolean
|
||||
|
||||
@@ -5,6 +5,7 @@ export const DEPS_BUILD_CONFIG_KEYS = [
|
||||
'onlyBuiltDependencies',
|
||||
'onlyBuiltDependenciesFile',
|
||||
'neverBuiltDependencies',
|
||||
'allowBuilds',
|
||||
] as const satisfies Array<keyof Config>
|
||||
|
||||
export type DepsBuildConfigKey = typeof DEPS_BUILD_CONFIG_KEYS[number]
|
||||
|
||||
@@ -28,12 +28,14 @@ export type OptionsFromRootManifest = {
|
||||
patchedDependencies?: Record<string, string>
|
||||
peerDependencyRules?: PeerDependencyRules
|
||||
supportedArchitectures?: SupportedArchitectures
|
||||
allowBuilds?: Record<string, boolean | string>
|
||||
} & Pick<PnpmSettings, 'configDependencies' | 'auditConfig' | 'executionEnv' | 'updateConfig'>
|
||||
|
||||
export function getOptionsFromRootManifest (manifestDir: string, manifest: ProjectManifest): OptionsFromRootManifest {
|
||||
const settings: OptionsFromRootManifest = getOptionsFromPnpmSettings(manifestDir, {
|
||||
...pick([
|
||||
'allowNonAppliedPatches',
|
||||
'allowBuilds',
|
||||
'allowUnusedPatches',
|
||||
'allowedDeprecatedVersions',
|
||||
'auditConfig',
|
||||
@@ -67,7 +69,7 @@ export function getOptionsFromRootManifest (manifestDir: string, manifest: Proje
|
||||
}
|
||||
|
||||
export function getOptionsFromPnpmSettings (manifestDir: string, pnpmSettings: PnpmSettings, manifest?: ProjectManifest): OptionsFromRootManifest {
|
||||
const renamedKeys = ['allowNonAppliedPatches'] as const satisfies Array<keyof PnpmSettings>
|
||||
const renamedKeys = ['allowNonAppliedPatches', 'allowBuilds'] as const satisfies Array<keyof PnpmSettings>
|
||||
const settings: OptionsFromRootManifest = omit(renamedKeys, replaceEnvInSettings(pnpmSettings))
|
||||
if (settings.overrides) {
|
||||
if (Object.keys(settings.overrides).length === 0) {
|
||||
@@ -93,6 +95,22 @@ export function getOptionsFromPnpmSettings (manifestDir: string, pnpmSettings: P
|
||||
if (pnpmSettings.ignorePatchFailures != null) {
|
||||
settings.ignorePatchFailures = pnpmSettings.ignorePatchFailures
|
||||
}
|
||||
|
||||
if (pnpmSettings.allowBuilds) {
|
||||
settings.onlyBuiltDependencies ??= []
|
||||
settings.ignoredBuiltDependencies ??= []
|
||||
for (const [packagePattern, build] of Object.entries(pnpmSettings.allowBuilds)) {
|
||||
switch (build) {
|
||||
case true:
|
||||
settings.onlyBuiltDependencies.push(packagePattern)
|
||||
break
|
||||
case false:
|
||||
settings.ignoredBuiltDependencies.push(packagePattern)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
|
||||
@@ -174,3 +174,19 @@ test('getOptionsFromPnpmSettings() replaces env variables in settings', () => {
|
||||
} as any) as any // eslint-disable-line
|
||||
expect(options.foo).toEqual('bar')
|
||||
})
|
||||
|
||||
test('getOptionsFromRootManifest() converts allowBuilds', () => {
|
||||
const options = getOptionsFromRootManifest(process.cwd(), {
|
||||
pnpm: {
|
||||
allowBuilds: {
|
||||
foo: true,
|
||||
bar: false,
|
||||
qar: 'warn',
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(options).toStrictEqual({
|
||||
onlyBuiltDependencies: ['foo'],
|
||||
ignoredBuiltDependencies: ['bar'],
|
||||
})
|
||||
})
|
||||
|
||||
@@ -153,9 +153,10 @@ export interface AuditConfig {
|
||||
|
||||
export interface PnpmSettings {
|
||||
configDependencies?: ConfigDependencies
|
||||
neverBuiltDependencies?: string[]
|
||||
onlyBuiltDependencies?: string[]
|
||||
onlyBuiltDependenciesFile?: string
|
||||
neverBuiltDependencies?: string[] // deprecated
|
||||
onlyBuiltDependencies?: string[] // deprecated
|
||||
onlyBuiltDependenciesFile?: string // deprecated
|
||||
allowBuilds?: Record<string, boolean | string>
|
||||
ignoredBuiltDependencies?: string[]
|
||||
overrides?: Record<string, string>
|
||||
packageExtensions?: Record<string, PackageExtension>
|
||||
|
||||
@@ -37,7 +37,32 @@ export async function updateWorkspaceManifest (dir: string, opts: {
|
||||
shouldBeUpdated = removePackagesFromWorkspaceCatalog(manifest, opts.allProjects ?? []) || shouldBeUpdated
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(opts.updatedFields ?? {})) {
|
||||
// If the current manifest has allowBuilds, convert old fields to allowBuilds format
|
||||
const updatedFields = { ...opts.updatedFields }
|
||||
if (manifest.allowBuilds != null && (updatedFields.onlyBuiltDependencies != null || updatedFields.ignoredBuiltDependencies != null)) {
|
||||
const allowBuilds: Record<string, boolean | string> = { ...manifest.allowBuilds }
|
||||
|
||||
// Convert onlyBuiltDependencies to allowBuilds with true values
|
||||
if (updatedFields.onlyBuiltDependencies != null) {
|
||||
for (const pattern of updatedFields.onlyBuiltDependencies) {
|
||||
allowBuilds[pattern] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ignoredBuiltDependencies to allowBuilds with false values
|
||||
if (updatedFields.ignoredBuiltDependencies != null) {
|
||||
for (const pattern of updatedFields.ignoredBuiltDependencies) {
|
||||
allowBuilds[pattern] = false
|
||||
}
|
||||
}
|
||||
|
||||
// Update allowBuilds instead of the old fields
|
||||
updatedFields.allowBuilds = allowBuilds
|
||||
delete updatedFields.onlyBuiltDependencies
|
||||
delete updatedFields.ignoredBuiltDependencies
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(updatedFields)) {
|
||||
if (!equals(manifest[key as keyof WorkspaceManifest], value)) {
|
||||
shouldBeUpdated = true
|
||||
if (value == null) {
|
||||
|
||||
@@ -42,3 +42,20 @@ test('updateWorkspaceManifest updates an existing setting', async () => {
|
||||
overrides: { bar: '3' },
|
||||
})
|
||||
})
|
||||
|
||||
test('updateWorkspaceManifest updates allowBuilds', async () => {
|
||||
const dir = tempDir(false)
|
||||
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
|
||||
writeYamlFile(filePath, { packages: ['*'], allowBuilds: { qar: 'warn' } })
|
||||
await updateWorkspaceManifest(dir, {
|
||||
updatedFields: { onlyBuiltDependencies: ['foo'], ignoredBuiltDependencies: ['bar'] },
|
||||
})
|
||||
expect(readYamlFile(filePath)).toStrictEqual({
|
||||
packages: ['*'],
|
||||
allowBuilds: {
|
||||
bar: false,
|
||||
foo: true,
|
||||
qar: 'warn',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user