Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add confirmation prompts to unsafe cli commands #6878

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7240f02
feat: added logic handeling for the `env:set` command
wconrad265 Oct 9, 2024
8bc880a
feat: prompt before setting env variable across context and scope
Oct 9, 2024
e068e56
fix: prettier
Oct 9, 2024
5b5c050
fix: refactored prompts
Oct 9, 2024
f77233d
fix: refactor prompts
Oct 9, 2024
ba41978
feat: env:unset prompts user before unsetting env variable indiscrimi…
Oct 9, 2024
679dcc4
feat: created tests for env:set prompts
wconrad265 Oct 10, 2024
f19207c
build: refactored env:set promts and rewrote tests
wconrad265 Oct 11, 2024
a404382
feat: added prompt for env:clone and tests
wconrad265 Oct 11, 2024
1d6c8f6
fix: prettier fix
wconrad265 Oct 11, 2024
4ebe87f
build: added prompts and tests for blob command
wconrad265 Oct 14, 2024
d7c4065
Merge branch 'netlify:main' into prompts
wconrad265 Oct 14, 2024
98a1399
fix: updated tests in file to reflect new prompts
wconrad265 Oct 14, 2024
31cd93e
fix: updated documentation
wconrad265 Oct 15, 2024
cf6b27a
fix: updated error
wconrad265 Oct 15, 2024
6bfb620
fix: updated new lines in messages for consistence
wconrad265 Oct 15, 2024
4f21d60
fix: fixed prettier error
wconrad265 Oct 15, 2024
fc46eb3
Merge branch 'netlify:main' into prompts
wconrad265 Oct 21, 2024
7527f74
feat: env-set refactored
wconrad265 Oct 21, 2024
469691c
fix: reactored env:unset prompts
wconrad265 Oct 21, 2024
f6aa58d
fix: refactored prompts and tests messages
wconrad265 Oct 22, 2024
9ba5ada
fix: another pass of refactoring
wconrad265 Oct 22, 2024
a51fe6e
feat: added skip for non interactive shell and CI
wconrad265 Oct 22, 2024
fe4bc7f
feat: refactored code for tests realted to ci and prompts
wconrad265 Oct 22, 2024
fea9bb4
fix: prettier fix
wconrad265 Oct 22, 2024
8c65226
fix: removed console.log statements
wconrad265 Oct 22, 2024
0ef4b7b
fix: updated prompts based on pr feedback
wconrad265 Oct 24, 2024
2ad45c2
feat: added force flag option to all commands
Oct 25, 2024
4e42609
fix: started updating tests to work with higher level --force flag fo…
Oct 28, 2024
486d457
feat: refactored tests to use mockProgram
wconrad265 Oct 28, 2024
4f78ab7
Merge remote-tracking branch 'origin/main' into prompts
wconrad265 Oct 28, 2024
621c640
feat: refactor of run.js into components to add force flag
wconrad265 Oct 29, 2024
0d35797
Merge branch 'main' into prompts
wconrad265 Oct 29, 2024
26d70c8
fix: types.ts merge } deletion
wconrad265 Oct 29, 2024
f45f6d4
fix: fix default lint issue and typescript issue
wconrad265 Oct 29, 2024
637df20
fix: update blob to blobs
wconrad265 Oct 29, 2024
cbc3b7c
fix: updated prompt tests for ci/cd enviroment
wconrad265 Oct 29, 2024
c9592a0
fix: updated prompt tests to work correctly in ci/cd enviroments
wconrad265 Oct 29, 2024
b2b200d
fix: updated types and env variables not being restored after tests
wconrad265 Oct 29, 2024
c929fcc
fix: fixed tests
wconrad265 Oct 30, 2024
6707b33
fix: fixed flakey deploy test and added env cleanup to more tests
wconrad265 Oct 30, 2024
34d0b88
fix: removed a console.log() statement
wconrad265 Oct 30, 2024
ed3c6f3
fix: cleaned up unused functions and comments
wconrad265 Oct 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions bin/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { argv } from 'process'

import updateNotifier from 'update-notifier'

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

// 12 hours
const UPDATE_CHECK_INTERVAL = 432e5
Expand All @@ -24,7 +25,8 @@ try {
const program = createMainCommand()

try {
await program.parseAsync(argv)
await runProgram(program, argv)

program.onEnd()
} catch (error_) {
program.onEnd(error_)
Expand Down
2 changes: 2 additions & 0 deletions docs/commands/blobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ netlify blobs:delete
**Flags**

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

---
Expand Down Expand Up @@ -124,6 +125,7 @@ netlify blobs:set
**Flags**

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

Expand Down
3 changes: 3 additions & 0 deletions docs/commands/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ netlify env:clone
**Flags**

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

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

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

**Examples**
Expand Down
2 changes: 1 addition & 1 deletion docs/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ netlify init
**Flags**

- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
- `force` (*boolean*) - Reinitialize CI hooks if the linked site is already configured to use CI
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
- `git-remote-name` (*string*) - Name of Git remote to use. e.g. "origin"
- `manual` (*boolean*) - Manually configure a git remote for CI
- `debug` (*boolean*) - Print debugging information
Expand Down
2 changes: 1 addition & 1 deletion docs/commands/sites.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ netlify sites:delete
**Flags**

- `filter` (*string*) - For monorepos, specify the name of the application to run the command in
- `force` (*boolean*) - delete without prompting (useful for CI)
- `force` (*boolean*) - Bypasses prompts & Force the command to run.
- `debug` (*boolean*) - Print debugging information

**Examples**
Expand Down
33 changes: 24 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
"@netlify/functions": "2.8.2",
"@sindresorhus/slugify": "2.2.1",
"@types/fs-extra": "11.0.4",
"@types/inquirer": "9.0.7",
"@types/inquirer": "^9.0.7",
"@types/jsonwebtoken": "9.0.7",
"@types/lodash": "4.17.12",
"@types/node": "20.14.8",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/addons/addons.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the old --force option, the -f option no longer works. Do we want to add -f for just this option. Is this ok?

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
11 changes: 9 additions & 2 deletions src/commands/base-command.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { isCI } from 'ci-info'

import { existsSync } from 'fs'
import { join, relative, resolve } from 'path'
import process from 'process'
Expand All @@ -8,6 +6,7 @@ import { format } from 'util'
import { DefaultLogger, Project } from '@netlify/build-info'
import { NodeFS, NoopLogger } from '@netlify/build-info/node'
import { resolveConfig } from '@netlify/config'
import { isCI } from 'ci-info'
import { Command, Help, Option } from 'commander'
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'debu... Remove this comment to see the full error message
import debug from 'debug'
Expand Down Expand Up @@ -168,6 +167,13 @@ export default class BaseCommand extends Command {
// eslint-disable-next-line workspace/no-process-cwd
workingDir = process.cwd()

/**
* Determines if the command is scripted or not.
* If the command is scripted (SHLVL is greater than 1 or CI/CONTINUOUS_INTEGRATION is true) then some commands
* might behave differently.
*/
scriptedCommand = Boolean(!process.stdin.isTTY || isCI || process.env.CI)

/**
* The workspace root if inside a mono repository.
* Must not be the repository root!
Expand All @@ -187,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
13 changes: 12 additions & 1 deletion src/commands/blobs/blobs-delete.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import { getStore } from '@netlify/blobs'

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

/**
* The blobs:delete command
*/
export const blobsDelete = async (storeName: string, key: string, _options: Record<string, unknown>, command: any) => {
// Prevents prompts from blocking scripted commands

const { api, siteInfo } = command.netlify
const { force } = _options

const store = getStore({
apiURL: `${api.scheme}://${api.host}`,
name: storeName,
siteID: siteInfo.id ?? '',
token: api.accessToken ?? '',
})

if (force === undefined) {
await promptBlobDelete(key, storeName)
}

try {
await store.delete(key)

log(`${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} deleted from store ${chalk.yellow(storeName)}`)
} catch {
return printError(`Could not delete blob ${chalk.yellow(key)} from store ${chalk.yellow(storeName)}`)
}
Expand Down
19 changes: 15 additions & 4 deletions src/commands/blobs/blobs-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { resolve } from 'path'
import { getStore } from '@netlify/blobs'
import { OptionValues } from 'commander'

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

interface Options extends OptionValues {
input?: string
force?: string | boolean
}

export const blobsSet = async (
Expand All @@ -18,20 +20,20 @@ export const blobsSet = async (
options: Options,
command: BaseCommand,
) => {
// Prevents prompts from blocking scripted commands

const { api, siteInfo } = command.netlify
const { input } = options
const { force, input } = options
const store = getStore({
apiURL: `${api.scheme}://${api.host}`,
name: storeName,
siteID: siteInfo.id ?? '',
token: api.accessToken ?? '',
})

let value = valueParts.join(' ')

if (input) {
const inputPath = resolve(input)

try {
value = await fs.readFile(inputPath, 'utf8')
} catch (error) {
Expand All @@ -57,8 +59,17 @@ export const blobsSet = async (
)
}

if (force === undefined) {
const existingValue = await store.get(key)

if (existingValue) {
await promptBlobSetOverwrite(key, storeName)
}
}

try {
await store.set(key, value)
log(`${chalk.greenBright('Success')}: Blob ${chalk.yellow(key)} set in store ${chalk.yellow(storeName)}`)
} catch {
return printError(`Could not set blob ${chalk.yellow(key)} in store ${chalk.yellow(storeName)}`)
}
Expand Down
Loading
Loading