Skip to content

Commit fd77a12

Browse files
wconrad265tlane25tDanielSLew
authored
feat: add confirmation prompts to unsafe cli commands (#6878)
* feat: added logic handeling for the `env:set` command Will prompt the user if scope and/or context is not provided Co-authored-by: Thomas Lane <[email protected]> * feat: prompt before setting env variable across context and scope Co-authored-by: Will <[email protected]> * fix: prettier Co-authored-by: Will <[email protected]> * fix: refactored prompts Co-authored-by: Will <[email protected]> * fix: refactor prompts Co-authored-by: Will <[email protected]> * feat: env:unset prompts user before unsetting env variable indiscriminantly across contexts Co-authored-by: Will <[email protected]> * feat: created tests for env:set prompts Created several tests to check env:test prompts * build: refactored env:set promts and rewrote tests created a new directory in utils called prompts, to store all future prompts. rewrote the prompts to only check for destructive actions. added tests for each of the destructive prompts Co-authored-by: Thomas Lane <[email protected]> * feat: added prompt for env:clone and tests Co-authored-by: Thomas Lane <[email protected]> * fix: prettier fix Co-authored-by: Thomas Lane <[email protected]> * build: added prompts and tests for blob command for blobl:set and blob:delete Co-authored-by: Thomas Lane <[email protected]> * fix: updated tests in file to reflect new prompts * fix: updated documentation updated the documentation Co-authored-by: Thomas Lane <[email protected]> * fix: updated error updated error handeling Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: Thomas Lane <[email protected]> * fix: updated new lines in messages for consistence updated prompts spacing for consistencey Co-authored-by: Thomas Lane <[email protected]> * fix: fixed prettier error Co-authored-by: Thomas Lane <[email protected]> * feat: env-set refactored refactored messages in env-set to a function that exports an object to be reused Co-authored-by: Thomas Lane <[email protected]> * fix: reactored env:unset prompts Co-authored-by: Thomas Lane <[email protected]> * fix: refactored prompts and tests messages Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: Thomas Lane <[email protected]> * fix: another pass of refactoring env and blob commands Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: Thomas Lane <[email protected]> * feat: added skip for non interactive shell and CI Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: Thomas Lane <[email protected]> * feat: refactored code for tests realted to ci and prompts Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: Thomas Lane <[email protected]> * fix: prettier fix Co-authored-by: Thomas Lane <[email protected]> * fix: removed console.log statements Co-authored-by: Thomas Lane <[email protected]> * fix: updated prompts based on pr feedback Co-authored-by: Thomas Lane <[email protected]> * feat: added force flag option to all commands scripted commands automatically given the force flag Co-authored-by: Will <[email protected]> * fix: started updating tests to work with higher level --force flag for scritped commands Co-authored-by: Will <[email protected]> * feat: refactored tests to use mockProgram Co-authored-by: Thomas Lane <[email protected]> * feat: refactor of run.js into components to add force flag Co-authored-by: Thomas Lane <[email protected]> * fix: types.ts merge } deletion Co-authored-by: Thomas Lane <[email protected]> * fix: fix default lint issue and typescript issue fixed lint issue that was casuing test in ci enviroment to fail Co-authored-by: Thomas Lane <[email protected]> * fix: update blob to blobs Co-authored-by: Thomas Lane <[email protected]> * fix: updated prompt tests for ci/cd enviroment Co-authored-by: Thomas Lane <[email protected]> * fix: updated prompt tests to work correctly in ci/cd enviroments Co-authored-by: Thomas Lane <[email protected]> * fix: updated types and env variables not being restored after tests Co-authored-by: Thomas Lane <[email protected]> * fix: fixed tests Co-authored-by: Thomas Lane <[email protected]> * fix: fixed flakey deploy test and added env cleanup to more tests * fix: removed a console.log() statement Co-authored-by: Thomas Lane <[email protected]> * fix: cleaned up unused functions and comments Co-authored-by: Thomas Lane <[email protected]> * chore: cleanup comments minor bug fixes Co-authored-by: Will <[email protected]> * chore: prettier, needed to update docs Co-authored-by: Will <[email protected]> * chore: merged updates to main.ts Co-authored-by: Will <[email protected]> * fix: reset env variable and mocks type error * fix: fixed test type error part 2 * fix: removed restModules from test * fix: updated test to have inquirer mocked correctly add the missing methods to the inquirer mock to fix all type errors * fix: reverted the force flags on lm commands to original behavior as the force option was for credential helper installation rather than skipping prompts Co-authored-by: Will <[email protected]> --------- Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: t <[email protected]> Co-authored-by: Thomas Lane <[email protected]> Co-authored-by: Daniel Lew <[email protected]>
1 parent d132ddd commit fd77a12

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1639
-140
lines changed

bin/run.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { argv } from 'process'
33

44
import updateNotifier from 'update-notifier'
55

6-
import { createMainCommand } from '../dist/commands/index.js'
6+
import { runProgram } from '../dist/utils/run-program.js'
77
import { error } from '../dist/utils/command-helpers.js'
88
import getPackageJson from '../dist/utils/get-package-json.js'
9+
import { createMainCommand } from '../dist/commands/main.js'
910

1011
// 12 hours
1112
const UPDATE_CHECK_INTERVAL = 432e5
@@ -24,7 +25,8 @@ try {
2425
const program = createMainCommand()
2526

2627
try {
27-
await program.parseAsync(argv)
28+
await runProgram(program, argv)
29+
2830
program.onEnd()
2931
} catch (error_) {
3032
program.onEnd(error_)

docs/commands/blobs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ netlify blobs:delete
5757
**Flags**
5858

5959
- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
60+
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
6061
- `debug` (*boolean*) - Print debugging information
6162

6263
---
@@ -124,6 +125,7 @@ netlify blobs:set
124125
**Flags**
125126

126127
- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
128+
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
127129
- `input` (*string*) - Defines the filesystem path where the blob data should be read from
128130
- `debug` (*boolean*) - Print debugging information
129131

docs/commands/env.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ netlify env:clone
5454
**Flags**
5555

5656
- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
57+
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
5758
- `from` (*string*) - Site ID (From)
5859
- `to` (*string*) - Site ID (To)
5960
- `debug` (*boolean*) - Print debugging information
@@ -167,6 +168,7 @@ netlify env:set
167168

168169
- `context` (*string*) - Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)
169170
- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
171+
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
170172
- `scope` (*builds | functions | post-processing | runtime*) - Specify a scope (default: all scopes)
171173
- `secret` (*boolean*) - Indicate whether the environment variable value can be read again.
172174
- `debug` (*boolean*) - Print debugging information
@@ -202,6 +204,7 @@ netlify env:unset
202204

203205
- `context` (*string*) - Specify a deploy context or branch (contexts: "production", "deploy-preview", "branch-deploy", "dev") (default: all contexts)
204206
- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
207+
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
205208
- `debug` (*boolean*) - Print debugging information
206209

207210
**Examples**

docs/commands/sites.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ netlify sites:delete
109109
**Flags**
110110

111111
- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
112-
- `force` (*boolean*) - delete without prompting (useful for CI)
112+
- `force` (*boolean*) - Delete without prompting (useful for CI).
113113
- `debug` (*boolean*) - Print debugging information
114114

115115
**Examples**

package-lock.json

Lines changed: 24 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@
191191
"@netlify/functions": "2.8.2",
192192
"@sindresorhus/slugify": "2.2.1",
193193
"@types/fs-extra": "11.0.4",
194-
"@types/inquirer": "9.0.7",
194+
"@types/inquirer": "^9.0.7",
195195
"@types/jsonwebtoken": "9.0.7",
196196
"@types/lodash": "4.17.13",
197197
"@types/node": "20.14.8",

src/commands/addons/addons.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ Add-ons are a way to extend the functionality of your Netlify site`,
5151
.description(
5252
`Remove an add-on extension to your site\nAdd-ons are a way to extend the functionality of your Netlify site`,
5353
)
54-
.option('-f, --force', 'delete without prompting (useful for CI)')
5554
.action(async (addonName: string, options: OptionValues, command: BaseCommand) => {
5655
const { addonsDelete } = await import('./addons-delete.js')
5756
await addonsDelete(addonName, options, command)

src/commands/base-command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { isCI } from 'ci-info'
2-
31
import { existsSync } from 'fs'
42
import { join, relative, resolve } from 'path'
53
import process from 'process'
@@ -8,6 +6,7 @@ import { format } from 'util'
86
import { DefaultLogger, Project } from '@netlify/build-info'
97
import { NodeFS, NoopLogger } from '@netlify/build-info/node'
108
import { resolveConfig } from '@netlify/config'
9+
import { isCI } from 'ci-info'
1110
import { Command, Help, Option } from 'commander'
1211
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'debu... Remove this comment to see the full error message
1312
import debug from 'debug'
@@ -187,6 +186,7 @@ export default class BaseCommand extends Command {
187186
createCommand(name: string): BaseCommand {
188187
const base = new BaseCommand(name)
189188
// If --silent or --json flag passed disable logger
189+
// .addOption(new Option('--force', 'Force command to run. Bypasses prompts for certain destructive commands.'))
190190
.addOption(new Option('--json', 'Output return values as JSON').hideHelp(true))
191191
.addOption(new Option('--silent', 'Silence CLI output').hideHelp(true))
192192
.addOption(new Option('--cwd <cwd>').hideHelp(true))

src/commands/blobs/blobs-delete.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
import { getStore } from '@netlify/blobs'
22

3-
import { chalk, error as printError } from '../../utils/command-helpers.js'
3+
import { chalk, error as printError, log } from '../../utils/command-helpers.js'
4+
import { promptBlobDelete } from '../../utils/prompts/blob-delete-prompts.js'
45

56
/**
67
* The blobs:delete command
78
*/
89
export const blobsDelete = async (storeName: string, key: string, _options: Record<string, unknown>, command: any) => {
910
const { api, siteInfo } = command.netlify
11+
const { force } = _options
12+
1013
const store = getStore({
1114
apiURL: `${api.scheme}://${api.host}`,
1215
name: storeName,
1316
siteID: siteInfo.id ?? '',
1417
token: api.accessToken ?? '',
1518
})
1619

20+
if (force === undefined) {
21+
await promptBlobDelete(key, storeName)
22+
}
23+
1724
try {
1825
await store.delete(key)
26+
27+
log(`${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} deleted from store ${chalk.yellow(storeName)}`)
1928
} catch {
2029
return printError(`Could not delete blob ${chalk.yellow(key)} from store ${chalk.yellow(storeName)}`)
2130
}

src/commands/blobs/blobs-set.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { resolve } from 'path'
44
import { getStore } from '@netlify/blobs'
55
import { OptionValues } from 'commander'
66

7-
import { chalk, error as printError, isNodeError } from '../../utils/command-helpers.js'
7+
import { chalk, error as printError, isNodeError, log } from '../../utils/command-helpers.js'
8+
import { promptBlobSetOverwrite } from '../../utils/prompts/blob-set-prompt.js'
89
import BaseCommand from '../base-command.js'
910

1011
interface Options extends OptionValues {
1112
input?: string
13+
force?: string | boolean
1214
}
1315

1416
export const blobsSet = async (
@@ -19,19 +21,17 @@ export const blobsSet = async (
1921
command: BaseCommand,
2022
) => {
2123
const { api, siteInfo } = command.netlify
22-
const { input } = options
24+
const { force, input } = options
2325
const store = getStore({
2426
apiURL: `${api.scheme}://${api.host}`,
2527
name: storeName,
2628
siteID: siteInfo.id ?? '',
2729
token: api.accessToken ?? '',
2830
})
29-
3031
let value = valueParts.join(' ')
3132

3233
if (input) {
3334
const inputPath = resolve(input)
34-
3535
try {
3636
value = await fs.readFile(inputPath, 'utf8')
3737
} catch (error) {
@@ -57,8 +57,17 @@ export const blobsSet = async (
5757
)
5858
}
5959

60+
if (force === undefined) {
61+
const existingValue = await store.get(key)
62+
63+
if (existingValue) {
64+
await promptBlobSetOverwrite(key, storeName)
65+
}
66+
}
67+
6068
try {
6169
await store.set(key, value)
70+
log(`${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} set in store ${chalk.yellow(storeName)}`)
6271
} catch {
6372
return printError(`Could not set blob ${chalk.yellow(key)} in store ${chalk.yellow(storeName)}`)
6473
}

0 commit comments

Comments
 (0)