Skip to content

Commit

Permalink
feat: added force flag option to all commands
Browse files Browse the repository at this point in the history
scripted commands automatically given the force flag
Co-authored-by: Will <wconrad265@users.noreply.github.com>
  • Loading branch information
t authored and t committed Oct 25, 2024
1 parent 0ef4b7b commit 2ad45c2
Show file tree
Hide file tree
Showing 17 changed files with 44 additions and 33 deletions.
29 changes: 29 additions & 0 deletions bin/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,37 @@ try {

const program = createMainCommand()


try {

// Is the command run in a non-interactive shell or CI/CD environment?
const scriptedCommand = program.scriptedCommand

// Is not the base `netlify command w/o any flags
const notNetlifyCommand = argv.length > 2

// Is not the base `netlify` command w/ flags
const notNetlifyCommandWithFlags = argv[2] && !(argv[2].startsWith('-'))

// is not the `netlify help` command
const notNetlifyHelpCommand = argv[2] && !(argv[2] === 'help')

// Is the `--force` flag not already present?
const noForceFlag = !argv.includes('--force')

// Prevents prompts from blocking scripted commands
if (
scriptedCommand &&
notNetlifyCommand &&
notNetlifyCommandWithFlags &&
notNetlifyHelpCommand &&
noForceFlag
) {
argv.push("--force")
}

await program.parseAsync(argv)

program.onEnd()
} catch (error_) {
program.onEnd(error_)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/addons/addons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Add-ons are a way to extend the functionality of your Netlify site`,
.description(
`Remove an add-on extension to your site\nAdd-ons are a way to extend the functionality of your Netlify site`,
)
.option('-f, --force', 'delete without prompting (useful for CI)')
// .option('-f, --force', 'delete without prompting (useful for CI)')
.action(async (addonName: string, options: OptionValues, command: BaseCommand) => {
const { addonsDelete } = await import('./addons-delete.js')
await addonsDelete(addonName, options, command)
Expand Down
1 change: 1 addition & 0 deletions src/commands/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export default class BaseCommand extends Command {
createCommand(name: string): BaseCommand {
const base = new BaseCommand(name)
// If --silent or --json flag passed disable logger
.addOption(new Option('--force', 'Force command to run. Bypasses prompts for certain destructive commands.'))
.addOption(new Option('--json', 'Output return values as JSON').hideHelp(true))
.addOption(new Option('--silent', 'Silence CLI output').hideHelp(true))
.addOption(new Option('--cwd <cwd>').hideHelp(true))
Expand Down
3 changes: 0 additions & 3 deletions src/commands/blobs/blobs-delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import { promptBlobDelete } from '../../utils/prompts/blob-delete-prompts.js'
*/
export const blobsDelete = async (storeName: string, key: string, _options: Record<string, unknown>, command: any) => {
// Prevents prompts from blocking scripted commands
if (command.scriptedCommand) {
_options.force = true
}

const { api, siteInfo } = command.netlify
const { force } = _options
Expand Down
3 changes: 0 additions & 3 deletions src/commands/blobs/blobs-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const blobsSet = async (
command: BaseCommand,
) => {
// Prevents prompts from blocking scripted commands
if (command.scriptedCommand) {
options.force = true
}

const { api, siteInfo } = command.netlify
const { force, input } = options
Expand Down
2 changes: 0 additions & 2 deletions src/commands/blobs/blobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const createBlobsCommand = (program: BaseCommand) => {
.description(`Deletes an object with a given key, if it exists, from a Netlify Blobs store`)
.argument('<store>', 'Name of the store')
.argument('<key>', 'Object key')
.option('-f, --force', 'Force the operation to proceed without confirmation or warnings')
.alias('blob:delete')
.hook('preAction', requiresSiteInfo)
.action(async (storeName: string, key: string, _options: OptionValues, command: BaseCommand) => {
Expand Down Expand Up @@ -71,7 +70,6 @@ export const createBlobsCommand = (program: BaseCommand) => {
.argument('<key>', 'Object key')
.argument('[value...]', 'Object value')
.option('-i, --input <path>', 'Defines the filesystem path where the blob data should be read from')
.option('-f, --force', 'Force the operation to proceed without confirmation or warnings')
.alias('blob:set')
.hook('preAction', requiresSiteInfo)

Expand Down
3 changes: 0 additions & 3 deletions src/commands/env/env-clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ const cloneEnvVars = async ({ api, force, siteFrom, siteTo }): Promise<boolean>

export const envClone = async (options: OptionValues, command: BaseCommand) => {
// Prevents prompts from blocking scripted commands
if (command.scriptedCommand) {
options.force = true
}

const { api, site } = command.netlify
const { force } = options
Expand Down
5 changes: 1 addition & 4 deletions src/commands/env/env-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const setInEnvelope = async ({ api, context, force, key, scope, secret, siteInfo

// @ts-expect-error TS(7006) FIXME: Parameter 'envVar' implicitly has an 'any' type.
const existing = envelopeVariables.find((envVar) => envVar.key === key)

console.log('force w/n set', force)
// Checks if --force is passed and if it is an existing variaible, then we need to prompt the user
if (Boolean(force) === false && existing) {
await promptOverwriteEnvVariable(key)
Expand Down Expand Up @@ -115,9 +115,6 @@ const setInEnvelope = async ({ api, context, force, key, scope, secret, siteInfo

export const envSet = async (key: string, value: string, options: OptionValues, command: BaseCommand) => {
// Prevents prompts from ci and non ineractive shells
if (command.scriptedCommand) {
options.force = true
}

const { context, force, scope, secret } = options
const { api, cachedConfig, site } = command.netlify
Expand Down
4 changes: 1 addition & 3 deletions src/commands/env/env-unset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import BaseCommand from '../base-command.js'
*/
// @ts-expect-error TS(7031) FIXME: Binding element 'api' implicitly has an 'any' type... Remove this comment to see the full error message
const unsetInEnvelope = async ({ api, context, force, key, siteInfo }) => {
console.log('force w/n unset', force)
const accountId = siteInfo.account_slug
const siteId = siteInfo.id
// fetch envelope env vars
Expand Down Expand Up @@ -69,9 +70,6 @@ const unsetInEnvelope = async ({ api, context, force, key, siteInfo }) => {

export const envUnset = async (key: string, options: OptionValues, command: BaseCommand) => {
// Prevents prompts from blocking scripted commands
if (command.scriptedCommand) {
options.force = true
}

const { context, force } = options
const { api, cachedConfig, site } = command.netlify
Expand Down
3 changes: 0 additions & 3 deletions src/commands/env/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ export const createEnvCommand = (program: BaseCommand) => {
'runtime',
]),
)
.option('-f, --force', 'Force the operation to proceed without confirmation or warnings')
.option('--secret', 'Indicate whether the environment variable value can be read again.')
.description('Set value of environment variable')
.addExamples([
Expand Down Expand Up @@ -131,7 +130,6 @@ export const createEnvCommand = (program: BaseCommand) => {
// @ts-expect-error TS(7006) FIXME: Parameter 'context' implicitly has an 'any' type.
(context, previous = []) => [...previous, normalizeContext(context)],
)
.option('-f, --force', 'Force the operation to proceed without confirmation or warnings')
.addExamples([
'netlify env:unset VAR_NAME # unset in all contexts',
'netlify env:unset VAR_NAME --context production',
Expand All @@ -147,7 +145,6 @@ export const createEnvCommand = (program: BaseCommand) => {
.command('env:clone')
.alias('env:migrate')
.option('-f, --from <from>', 'Site ID (From)')
.option('--force', 'Force the operation to proceed without confirmation or warnings')
.requiredOption('-t, --to <to>', 'Site ID (To)')
.description(`Clone environment variables from one site to another`)
.addExamples(['netlify env:clone --to <to-site-id>', 'netlify env:clone --to <to-site-id> --from <from-site-id>'])
Expand Down
2 changes: 1 addition & 1 deletion src/commands/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const createInitCommand = (program: BaseCommand) =>
'Configure continuous deployment for a new or existing site. To create a new site without continuous deployment, use `netlify sites:create`',
)
.option('-m, --manual', 'Manually configure a git remote for CI')
.option('--force', 'Reinitialize CI hooks if the linked site is already configured to use CI')
// .option('--force', 'Reinitialize CI hooks if the linked site is already configured to use CI')
.addOption(
new Option(
'--gitRemoteName <name>',
Expand Down
4 changes: 2 additions & 2 deletions src/commands/lm/lm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const createLmCommand = (program: BaseCommand) => {
It installs the required credentials helper for Git,
and configures your Git environment with the right credentials.`,
)
.option('-f, --force', 'Force the credentials helper installation')
// .option('-f, --force', 'Force the credentials helper installation')
.action(async (options: OptionValues) => {
const { lmInstall } = await import('./lm-install.js')
await lmInstall(options)
Expand All @@ -33,7 +33,7 @@ and configures your Git environment with the right credentials.`,
.command('lm:setup', { hidden: true })
.description('Configures your site to use Netlify Large Media')
.option('-s, --skip-install', 'Skip the credentials helper installation check')
.option('-f, --force-install', 'Force the credentials helper installation')
// .option('-f, --force-install', 'Force the credentials helper installation')
.addHelpText('after', 'It runs the install command if you have not installed the dependencies yet.')
.action(async (options: OptionValues, command: BaseCommand) => {
const { lmSetup } = await import('./lm-setup.js')
Expand Down
2 changes: 1 addition & 1 deletion src/commands/sites/sites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const createSitesCommand = (program: BaseCommand) => {
.command('sites:delete')
.description('Delete a site\nThis command will permanently delete the site on Netlify. Use with caution.')
.argument('<siteId>', 'Site ID to delete.')
.option('-f, --force', 'delete without prompting (useful for CI)')
// .option('-f, --force', 'delete without prompting (useful for CI)')
.addExamples(['netlify sites:delete 1234-3262-1211'])
.action(async (siteId: string, options: OptionValues, command: BaseCommand) => {
const { sitesDelete } = await import('./sites-delete.js')
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/commands/addons/addons.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe.concurrent('command-addons', () => {
]

await withMockApi(deleteRoutes, async ({ apiUrl }) => {
const cliResponse = await callCli(['addons:delete', 'demo', '-f'], getCLIOptions({ builder, apiUrl }))
const cliResponse = await callCli(['addons:delete', 'demo', '--force'], getCLIOptions({ builder, apiUrl }))
t.expect(cliResponse.includes('Addon "demo" deleted')).toBe(true)
})
})
Expand Down
7 changes: 3 additions & 4 deletions tests/integration/commands/env/env-set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ describe('env:set command', () => {
})
})

test('should skip warnings and prompts if -f flag is passed', async () => {
test('should skip warnings and prompts if --force flag is passed', async () => {
await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))

Expand All @@ -344,7 +344,7 @@ describe('env:set command', () => {

const promptSpy = vi.spyOn(inquirer, 'prompt')

await program.parseAsync(['', '', 'env:set', existingVar, newEnvValue, '-f'])
await program.parseAsync(['', '', 'env:set', existingVar, newEnvValue, '--force'])

expect(promptSpy).not.toHaveBeenCalled()

Expand Down Expand Up @@ -410,7 +410,7 @@ describe('env:set command', () => {
})

test('should not show prompt in a ci/cd enviroment', async () => {
setCI(true)
setCI('true')

await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))
Expand All @@ -419,7 +419,6 @@ describe('env:set command', () => {
createEnvCommand(program)

const promptSpy = vi.spyOn(inquirer, 'prompt')

await program.parseAsync(['', '', 'env:set', existingVar, newEnvValue])

expect(promptSpy).not.toHaveBeenCalled()
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/commands/env/env-unset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe('env:unset command', () => {
})
})

test('should skip warnings and prompts if -f flag is passed', async () => {
test('should skip warnings and prompts if --force flag is passed', async () => {
await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))

Expand All @@ -125,7 +125,7 @@ describe('env:unset command', () => {

const promptSpy = vi.spyOn(inquirer, 'prompt')

await program.parseAsync(['', '', 'env:unset', existingVar, '-f'])
await program.parseAsync(['', '', 'env:unset', existingVar, '--force'])

expect(promptSpy).not.toHaveBeenCalled()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ USAGE
OPTIONS
-h, --help display help for command
--debug Print debugging information
--force Force command to run. Bypasses prompts for certain destructive commands.
DESCRIPTION
Run this command to see instructions for your shell.
Expand Down

0 comments on commit 2ad45c2

Please sign in to comment.