mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-27 18:46:18 -04:00
feat(approve-builds): add --all flag to skip interactive prompts (#10619)
Allow approving all pending build dependencies at once without interactive selection, useful for CI/CD pipelines and project bootstrapping scenarios where interactive prompts are not feasible. close #10136
This commit is contained in:
6
.changeset/approve-builds-all-flag.md
Normal file
6
.changeset/approve-builds-all-flag.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/exec.build-commands": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Added `--all` flag to `pnpm approve-builds` that approves all pending builds without interactive prompts [#10136](https://github.com/pnpm/pnpm/issues/10136).
|
||||
@@ -9,7 +9,7 @@ import { rebuild, type RebuildCommandOpts } from '@pnpm/plugin-commands-rebuild'
|
||||
import { writeSettings } from '@pnpm/config.config-writer'
|
||||
import { getAutomaticallyIgnoredBuilds } from './getAutomaticallyIgnoredBuilds.js'
|
||||
|
||||
export type ApproveBuildsCommandOpts = Pick<Config, 'modulesDir' | 'dir' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'allowBuilds'>
|
||||
export type ApproveBuildsCommandOpts = Pick<Config, 'modulesDir' | 'dir' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'allowBuilds'> & { all?: boolean }
|
||||
|
||||
export const commandNames = ['approve-builds']
|
||||
|
||||
@@ -22,6 +22,10 @@ export function help (): string {
|
||||
title: 'Options',
|
||||
|
||||
list: [
|
||||
{
|
||||
description: 'Approve all pending dependencies without interactive prompts',
|
||||
name: '--all',
|
||||
},
|
||||
{
|
||||
description: 'Approve dependencies of global packages',
|
||||
name: '--global',
|
||||
@@ -35,6 +39,7 @@ export function help (): string {
|
||||
|
||||
export function cliOptionsTypes (): Record<string, unknown> {
|
||||
return {
|
||||
all: Boolean,
|
||||
global: Boolean,
|
||||
}
|
||||
}
|
||||
@@ -53,43 +58,48 @@ export async function handler (opts: ApproveBuildsCommandOpts & RebuildCommandOp
|
||||
globalInfo('There are no packages awaiting approval')
|
||||
return
|
||||
}
|
||||
const { result } = await enquirer.prompt({
|
||||
choices: sortUniqueStrings([...automaticallyIgnoredBuilds]),
|
||||
indicator (state: any, choice: any) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
return ` ${choice.enabled ? '●' : '○'}`
|
||||
},
|
||||
message: 'Choose which packages to build ' +
|
||||
`(Press ${chalk.cyan('<space>')} to select, ` +
|
||||
`${chalk.cyan('<a>')} to toggle all, ` +
|
||||
`${chalk.cyan('<i>')} to invert selection)`,
|
||||
name: 'result',
|
||||
pointer: '❯',
|
||||
result () {
|
||||
return this.selected
|
||||
},
|
||||
styles: {
|
||||
dark: chalk.reset,
|
||||
em: chalk.bgBlack.whiteBright,
|
||||
success: chalk.reset,
|
||||
},
|
||||
type: 'multiselect',
|
||||
let buildPackages: string[] = []
|
||||
if (opts.all) {
|
||||
buildPackages = sortUniqueStrings([...automaticallyIgnoredBuilds])
|
||||
} else {
|
||||
const { result } = await enquirer.prompt({
|
||||
choices: sortUniqueStrings([...automaticallyIgnoredBuilds]),
|
||||
indicator (state: any, choice: any) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
return ` ${choice.enabled ? '●' : '○'}`
|
||||
},
|
||||
message: 'Choose which packages to build ' +
|
||||
`(Press ${chalk.cyan('<space>')} to select, ` +
|
||||
`${chalk.cyan('<a>')} to toggle all, ` +
|
||||
`${chalk.cyan('<i>')} to invert selection)`,
|
||||
name: 'result',
|
||||
pointer: '❯',
|
||||
result () {
|
||||
return this.selected
|
||||
},
|
||||
styles: {
|
||||
dark: chalk.reset,
|
||||
em: chalk.bgBlack.whiteBright,
|
||||
success: chalk.reset,
|
||||
},
|
||||
type: 'multiselect',
|
||||
|
||||
// For Vim users (related: https://github.com/enquirer/enquirer/pull/163)
|
||||
j () {
|
||||
return this.down()
|
||||
},
|
||||
k () {
|
||||
return this.up()
|
||||
},
|
||||
cancel () {
|
||||
// By default, canceling the prompt via Ctrl+c throws an empty string.
|
||||
// The custom cancel function prevents that behavior.
|
||||
// Otherwise, pnpm CLI would print an error and confuse users.
|
||||
// See related issue: https://github.com/enquirer/enquirer/issues/225
|
||||
process.exit(0)
|
||||
},
|
||||
} as any) as any // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const buildPackages = result.map(({ value }: { value: string }) => value)
|
||||
// For Vim users (related: https://github.com/enquirer/enquirer/pull/163)
|
||||
j () {
|
||||
return this.down()
|
||||
},
|
||||
k () {
|
||||
return this.up()
|
||||
},
|
||||
cancel () {
|
||||
// By default, canceling the prompt via Ctrl+c throws an empty string.
|
||||
// The custom cancel function prevents that behavior.
|
||||
// Otherwise, pnpm CLI would print an error and confuse users.
|
||||
// See related issue: https://github.com/enquirer/enquirer/issues/225
|
||||
process.exit(0)
|
||||
},
|
||||
} as any) as any // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
buildPackages = result.map(({ value }: { value: string }) => value)
|
||||
}
|
||||
const ignoredPackages = automaticallyIgnoredBuilds.filter((automaticallyIgnoredBuild) => !buildPackages.includes(automaticallyIgnoredBuild))
|
||||
const allowBuilds: Record<string, boolean | string> = { ...opts.allowBuilds }
|
||||
if (ignoredPackages.length) {
|
||||
@@ -102,19 +112,21 @@ export async function handler (opts: ApproveBuildsCommandOpts & RebuildCommandOp
|
||||
allowBuilds[pkg] = true
|
||||
}
|
||||
}
|
||||
if (buildPackages.length) {
|
||||
const confirmed = await enquirer.prompt<{ build: boolean }>({
|
||||
type: 'confirm',
|
||||
name: 'build',
|
||||
message: `The next packages will now be built: ${buildPackages.join(', ')}.
|
||||
if (!opts.all) {
|
||||
if (buildPackages.length) {
|
||||
const confirmed = await enquirer.prompt<{ build: boolean }>({
|
||||
type: 'confirm',
|
||||
name: 'build',
|
||||
message: `The next packages will now be built: ${buildPackages.join(', ')}.
|
||||
Do you approve?`,
|
||||
initial: false,
|
||||
})
|
||||
if (!confirmed.build) {
|
||||
return
|
||||
initial: false,
|
||||
})
|
||||
if (!confirmed.build) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
globalInfo('All packages were added to allowBuilds with value false.')
|
||||
}
|
||||
} else {
|
||||
globalInfo('All packages were added to allowBuilds with value false.')
|
||||
}
|
||||
await writeSettings({
|
||||
...opts,
|
||||
|
||||
@@ -176,6 +176,48 @@ test('should approve builds with package.json that has no allowBuilds field defi
|
||||
})
|
||||
})
|
||||
|
||||
test('approve all builds with --all flag', async () => {
|
||||
prepare({
|
||||
dependencies: {
|
||||
'@pnpm.e2e/pre-and-postinstall-scripts-example': '1.0.0',
|
||||
'@pnpm.e2e/install-script-example': '*',
|
||||
},
|
||||
})
|
||||
|
||||
const cliOptions = {
|
||||
argv: [],
|
||||
dir: process.cwd(),
|
||||
registry: `http://localhost:${REGISTRY_MOCK_PORT}`,
|
||||
}
|
||||
const config = {
|
||||
...omit(['reporter'], (await getConfig({
|
||||
cliOptions,
|
||||
packageManager: { name: 'pnpm', version: '' },
|
||||
})).config),
|
||||
storeDir: path.resolve('store'),
|
||||
cacheDir: path.resolve('cache'),
|
||||
pnpmfile: [],
|
||||
enableGlobalVirtualStore: false,
|
||||
strictDepBuilds: false,
|
||||
}
|
||||
await install.handler({ ...config, argv: { original: [] } })
|
||||
|
||||
prompt.mockClear()
|
||||
await approveBuilds.handler({ ...config, all: true })
|
||||
|
||||
expect(prompt).not.toHaveBeenCalled()
|
||||
|
||||
const workspaceManifest = readYamlFile<any>(path.resolve('pnpm-workspace.yaml')) // eslint-disable-line
|
||||
expect(workspaceManifest.allowBuilds).toStrictEqual({
|
||||
'@pnpm.e2e/install-script-example': true,
|
||||
'@pnpm.e2e/pre-and-postinstall-scripts-example': true,
|
||||
})
|
||||
|
||||
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeTruthy()
|
||||
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeTruthy()
|
||||
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
|
||||
})
|
||||
|
||||
test('should retain existing allowBuilds entries when approving builds', async () => {
|
||||
const temp = tempDir()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user