diff --git a/acceptance/bundle/debug/output.txt b/acceptance/bundle/debug/output.txt index ce15a99bc9..6df8fdb6c7 100644 --- a/acceptance/bundle/debug/output.txt +++ b/acceptance/bundle/debug/output.txt @@ -12,9 +12,12 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts Use "databricks bundle debug [command] --help" for more information about a command. diff --git a/acceptance/bundle/deployment/bind/dashboard/recreation/output.txt b/acceptance/bundle/deployment/bind/dashboard/recreation/output.txt index 99c26a8ccc..1fb599fcac 100644 --- a/acceptance/bundle/deployment/bind/dashboard/recreation/output.txt +++ b/acceptance/bundle/deployment/bind/dashboard/recreation/output.txt @@ -18,7 +18,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle-[UNIQU This action will result in the deletion or recreation of the following dashboards. This will result in changed IDs and permanent URLs of the dashboards that will be recreated: recreate resources.dashboards.dashboard1 -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed Exit code: 1 diff --git a/acceptance/bundle/deployment/bind/job/job-abort-bind/output.txt b/acceptance/bundle/deployment/bind/job/job-abort-bind/output.txt index 544448efbe..28671f18f3 100644 --- a/acceptance/bundle/deployment/bind/job/job-abort-bind/output.txt +++ b/acceptance/bundle/deployment/bind/job/job-abort-bind/output.txt @@ -3,7 +3,7 @@ Created job with ID: [JOB_ID] === Expect binding to fail without an auto-approve flag: -Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed. +Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed. === Deploy bundle: >>> [CLI] bundle deploy --force-lock diff --git a/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.direct.txt b/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.direct.txt index c5e00b49cb..4cd88cfbd7 100644 --- a/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.direct.txt +++ b/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.direct.txt @@ -11,5 +11,5 @@ Changes detected: ~ root_path: "/Workspace/Users/someuser@databricks.com/lakeflow_pipeline" -> null ~ storage: "/Shared/old_storage" -> "/Shared/new_storage" -Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed. +Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed. diff --git a/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.terraform.txt b/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.terraform.txt index 1d300160a9..c976c5d6c5 100644 --- a/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.terraform.txt +++ b/acceptance/bundle/deployment/bind/pipelines/recreate/out.bind-fail.terraform.txt @@ -54,5 +54,5 @@ Terraform will perform the following actions: Plan: 1 to add, 0 to change, 1 to destroy. -Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed. +Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed. diff --git a/acceptance/bundle/deployment/bind/pipelines/recreate/output.txt b/acceptance/bundle/deployment/bind/pipelines/recreate/output.txt index af42322dee..91526326c3 100644 --- a/acceptance/bundle/deployment/bind/pipelines/recreate/output.txt +++ b/acceptance/bundle/deployment/bind/pipelines/recreate/output.txt @@ -16,7 +16,7 @@ Streaming Tables (STs) and Materialized Views (MVs) managed by them. Recreating restore the defined STs and MVs through full refresh. Note that recreation is necessary when pipeline properties such as the 'catalog' or 'storage' are changed: recreate resources.pipelines.foo -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed >>> [CLI] bundle deploy --auto-approve diff --git a/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.direct.txt b/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.direct.txt index 7b85803529..ceadc4de41 100644 --- a/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.direct.txt +++ b/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.direct.txt @@ -11,5 +11,5 @@ Changes detected: ~ name: "lakeflow-pipeline" -> "test-pipeline" ~ root_path: "/Workspace/Users/[USERNAME]/lakeflow_pipeline" -> null -Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed. +Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed. diff --git a/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.terraform.txt b/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.terraform.txt index 2ff60fce2b..8b4b0abe7e 100644 --- a/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.terraform.txt +++ b/acceptance/bundle/deployment/bind/pipelines/update/out.bind-fail.terraform.txt @@ -41,5 +41,5 @@ Terraform will perform the following actions: Plan: 0 to add, 1 to change, 0 to destroy. -Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed. +Error: This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed. diff --git a/acceptance/bundle/help/bundle-deploy/output.txt b/acceptance/bundle/help/bundle-deploy/output.txt index d9c5b1f14c..35d396a5dd 100644 --- a/acceptance/bundle/help/bundle-deploy/output.txt +++ b/acceptance/bundle/help/bundle-deploy/output.txt @@ -23,7 +23,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-deployment-migrate/output.txt b/acceptance/bundle/help/bundle-deployment-migrate/output.txt index a1bb7d951f..e3973217ca 100644 --- a/acceptance/bundle/help/bundle-deployment-migrate/output.txt +++ b/acceptance/bundle/help/bundle-deployment-migrate/output.txt @@ -19,7 +19,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-deployment/output.txt b/acceptance/bundle/help/bundle-deployment/output.txt index a135735cd4..cb525e9b0d 100644 --- a/acceptance/bundle/help/bundle-deployment/output.txt +++ b/acceptance/bundle/help/bundle-deployment/output.txt @@ -29,9 +29,12 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts Use "databricks bundle deployment [command] --help" for more information about a command. diff --git a/acceptance/bundle/help/bundle-destroy/output.txt b/acceptance/bundle/help/bundle-destroy/output.txt index 64aecf7ffe..3dc880e6fe 100644 --- a/acceptance/bundle/help/bundle-destroy/output.txt +++ b/acceptance/bundle/help/bundle-destroy/output.txt @@ -23,7 +23,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-generate-dashboard/output.txt b/acceptance/bundle/help/bundle-generate-dashboard/output.txt index 41ed2d6c02..6a1e10d97b 100644 --- a/acceptance/bundle/help/bundle-generate-dashboard/output.txt +++ b/acceptance/bundle/help/bundle-generate-dashboard/output.txt @@ -48,7 +48,10 @@ Flags: Global Flags: --debug enable debug logging --key string resource key to use for the generated configuration + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-generate-job/output.txt b/acceptance/bundle/help/bundle-generate-job/output.txt index f239691ab6..eb3bc2845f 100644 --- a/acceptance/bundle/help/bundle-generate-job/output.txt +++ b/acceptance/bundle/help/bundle-generate-job/output.txt @@ -37,7 +37,10 @@ Flags: Global Flags: --debug enable debug logging --key string resource key to use for the generated configuration + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-generate-pipeline/output.txt b/acceptance/bundle/help/bundle-generate-pipeline/output.txt index 7d0db9a098..514e5a9201 100644 --- a/acceptance/bundle/help/bundle-generate-pipeline/output.txt +++ b/acceptance/bundle/help/bundle-generate-pipeline/output.txt @@ -37,7 +37,10 @@ Flags: Global Flags: --debug enable debug logging --key string resource key to use for the generated configuration + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-generate/output.txt b/acceptance/bundle/help/bundle-generate/output.txt index 97e8667ac7..746f2e792c 100644 --- a/acceptance/bundle/help/bundle-generate/output.txt +++ b/acceptance/bundle/help/bundle-generate/output.txt @@ -39,9 +39,12 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts Use "databricks bundle generate [command] --help" for more information about a command. diff --git a/acceptance/bundle/help/bundle-init/output.txt b/acceptance/bundle/help/bundle-init/output.txt index dad5e198e4..07a7a7c5ce 100644 --- a/acceptance/bundle/help/bundle-init/output.txt +++ b/acceptance/bundle/help/bundle-init/output.txt @@ -37,7 +37,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-open/output.txt b/acceptance/bundle/help/bundle-open/output.txt index 568908f937..cdedb1620c 100644 --- a/acceptance/bundle/help/bundle-open/output.txt +++ b/acceptance/bundle/help/bundle-open/output.txt @@ -18,7 +18,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-run/output.txt b/acceptance/bundle/help/bundle-run/output.txt index 0c91c15ccd..a0644224b8 100644 --- a/acceptance/bundle/help/bundle-run/output.txt +++ b/acceptance/bundle/help/bundle-run/output.txt @@ -67,7 +67,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-schema/output.txt b/acceptance/bundle/help/bundle-schema/output.txt index 38d62a8949..ade6286df5 100644 --- a/acceptance/bundle/help/bundle-schema/output.txt +++ b/acceptance/bundle/help/bundle-schema/output.txt @@ -17,7 +17,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-summary/output.txt b/acceptance/bundle/help/bundle-summary/output.txt index d3c0c961ed..1fab8754ac 100644 --- a/acceptance/bundle/help/bundle-summary/output.txt +++ b/acceptance/bundle/help/bundle-summary/output.txt @@ -12,7 +12,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-sync/output.txt b/acceptance/bundle/help/bundle-sync/output.txt index a357882cbd..fc9b9e1dd1 100644 --- a/acceptance/bundle/help/bundle-sync/output.txt +++ b/acceptance/bundle/help/bundle-sync/output.txt @@ -24,6 +24,9 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle-validate/output.txt b/acceptance/bundle/help/bundle-validate/output.txt index dbb15761b3..3faa225acc 100644 --- a/acceptance/bundle/help/bundle-validate/output.txt +++ b/acceptance/bundle/help/bundle-validate/output.txt @@ -20,7 +20,10 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts diff --git a/acceptance/bundle/help/bundle/output.txt b/acceptance/bundle/help/bundle/output.txt index 379373b63a..64977aeecd 100644 --- a/acceptance/bundle/help/bundle/output.txt +++ b/acceptance/bundle/help/bundle/output.txt @@ -37,8 +37,11 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Use "databricks bundle [command] --help" for more information about a command. diff --git a/acceptance/bundle/refschema/output.txt b/acceptance/bundle/refschema/output.txt index eadb6bf3b3..85e226b7f9 100644 --- a/acceptance/bundle/refschema/output.txt +++ b/acceptance/bundle/refschema/output.txt @@ -14,9 +14,12 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="foo=bar" + -y, --yes Auto-approve all yes/no prompts >>> [CLI] bundle debug refschema diff --git a/acceptance/bundle/resources/pipelines/auto-approve/output.txt b/acceptance/bundle/resources/pipelines/auto-approve/output.txt index 5154a1206a..b60f4729db 100644 --- a/acceptance/bundle/resources/pipelines/auto-approve/output.txt +++ b/acceptance/bundle/resources/pipelines/auto-approve/output.txt @@ -50,7 +50,7 @@ Streaming Tables (STs) and Materialized Views (MVs) managed by them. Recreating restore the defined STs and MVs through full refresh. Note that recreation is necessary when pipeline properties such as the 'catalog' or 'storage' are changed: delete resources.pipelines.bar -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed Exit code: 1 diff --git a/acceptance/bundle/resources/pipelines/recreate/output.txt b/acceptance/bundle/resources/pipelines/recreate/output.txt index 8550395ff2..e39af47ac8 100644 --- a/acceptance/bundle/resources/pipelines/recreate/output.txt +++ b/acceptance/bundle/resources/pipelines/recreate/output.txt @@ -49,7 +49,7 @@ Streaming Tables (STs) and Materialized Views (MVs) managed by them. Recreating restore the defined STs and MVs through full refresh. Note that recreation is necessary when pipeline properties such as the 'catalog' or 'storage' are changed: recreate resources.pipelines.foo -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed Exit code: 1 diff --git a/acceptance/bundle/resources/schemas/auto-approve/output.txt b/acceptance/bundle/resources/schemas/auto-approve/output.txt index 6a773f60ca..08e025a7d0 100644 --- a/acceptance/bundle/resources/schemas/auto-approve/output.txt +++ b/acceptance/bundle/resources/schemas/auto-approve/output.txt @@ -57,7 +57,7 @@ Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/file This action will result in the deletion or recreation of the following UC schemas. Any underlying data may be lost: delete resources.schemas.bar -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed === Test cleanup diff --git a/acceptance/bundle/resources/volumes/recreate/output.txt b/acceptance/bundle/resources/volumes/recreate/output.txt index 494e9ff538..21064f30f1 100644 --- a/acceptance/bundle/resources/volumes/recreate/output.txt +++ b/acceptance/bundle/resources/volumes/recreate/output.txt @@ -45,7 +45,7 @@ For managed volumes, the files stored in the volume are also deleted from your cloud tenant within 30 days. For external volumes, the metadata about the volume is removed from the catalog, but the underlying files are not deleted: recreate resources.volumes.foo -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed Exit code: 1 diff --git a/acceptance/cmd/account/account-help/output.txt b/acceptance/cmd/account/account-help/output.txt index b39d439552..be56fb93b4 100644 --- a/acceptance/cmd/account/account-help/output.txt +++ b/acceptance/cmd/account/account-help/output.txt @@ -59,8 +59,11 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Use "databricks account [command] --help" for more information about a command. diff --git a/acceptance/cmd/sync-without-args/output.txt b/acceptance/cmd/sync-without-args/output.txt index 5d63d426c6..194a330037 100644 --- a/acceptance/cmd/sync-without-args/output.txt +++ b/acceptance/cmd/sync-without-args/output.txt @@ -19,8 +19,11 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Exit code: 1 diff --git a/acceptance/cmd/unknown-subcommand/output.txt b/acceptance/cmd/unknown-subcommand/output.txt index 14bd12a475..d8fdb60e04 100644 --- a/acceptance/cmd/unknown-subcommand/output.txt +++ b/acceptance/cmd/unknown-subcommand/output.txt @@ -27,9 +27,12 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Use "databricks secrets [command] --help" for more information about a command. diff --git a/acceptance/cmd/workspace/apps/output.txt b/acceptance/cmd/workspace/apps/output.txt index 618942df4c..fa79de54e4 100644 --- a/acceptance/cmd/workspace/apps/output.txt +++ b/acceptance/cmd/workspace/apps/output.txt @@ -79,10 +79,13 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) --var strings set values for variables defined in bundle config. Example: --var="key=value" + -y, --yes Auto-approve all yes/no prompts Exit code: 1 diff --git a/acceptance/cmd/workspace/database/update-database-instance/output.txt b/acceptance/cmd/workspace/database/update-database-instance/output.txt index c634d3fffe..1852039e66 100644 --- a/acceptance/cmd/workspace/database/update-database-instance/output.txt +++ b/acceptance/cmd/workspace/database/update-database-instance/output.txt @@ -18,9 +18,12 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Exit code: 1 diff --git a/acceptance/cmd/workspace/secrets/output.txt b/acceptance/cmd/workspace/secrets/output.txt index bfafcb3416..56f27a30bf 100644 --- a/acceptance/cmd/workspace/secrets/output.txt +++ b/acceptance/cmd/workspace/secrets/output.txt @@ -33,8 +33,11 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Use "databricks secrets [command] --help" for more information about a command. diff --git a/acceptance/help/output.txt b/acceptance/help/output.txt index 19b491dd57..e85c3ef92b 100644 --- a/acceptance/help/output.txt +++ b/acceptance/help/output.txt @@ -173,9 +173,12 @@ Additional Commands: Flags: --debug enable debug logging -h, --help help for databricks + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) -v, --version version for databricks + -y, --yes Auto-approve all yes/no prompts Use "databricks [command] --help" for more information about a command. diff --git a/acceptance/pipelines/databricks-cli-help/output.txt b/acceptance/pipelines/databricks-cli-help/output.txt index b5d7e4a483..75cbe6bec2 100644 --- a/acceptance/pipelines/databricks-cli-help/output.txt +++ b/acceptance/pipelines/databricks-cli-help/output.txt @@ -54,8 +54,11 @@ Flags: Global Flags: --debug enable debug logging + --no-input Disable interactive prompts -o, --output type output type: text or json (default text) -p, --profile string ~/.databrickscfg profile + -q, --quiet Suppress non-essential output -t, --target string bundle target to use (if applicable) + -y, --yes Auto-approve all yes/no prompts Use "databricks pipelines [command] --help" for more information about a command. diff --git a/acceptance/pipelines/deploy/auto-approve/output.txt b/acceptance/pipelines/deploy/auto-approve/output.txt index ff5ac099bf..84699d67d0 100644 --- a/acceptance/pipelines/deploy/auto-approve/output.txt +++ b/acceptance/pipelines/deploy/auto-approve/output.txt @@ -18,7 +18,7 @@ Streaming Tables (STs) and Materialized Views (MVs) managed by them. Recreating restore the defined STs and MVs through full refresh. Note that recreation is necessary when pipeline properties such as the 'catalog' or 'storage' are changed: delete resources.pipelines.foo -Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed +Error: the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed Exit code: 1 diff --git a/acceptance/pipelines/destroy/auto-approve/output.txt b/acceptance/pipelines/destroy/auto-approve/output.txt index 17aecc2059..cefb7ee695 100644 --- a/acceptance/pipelines/destroy/auto-approve/output.txt +++ b/acceptance/pipelines/destroy/auto-approve/output.txt @@ -8,7 +8,7 @@ Deployment complete! View your pipeline my_pipeline here: [DATABRICKS_URL]/pipelines/[UUID]?o=[NUMID] >>> errcode [CLI] pipelines destroy -Error: please specify --auto-approve since terminal does not support interactive prompts +Error: please specify --auto-approve or --yes since terminal does not support interactive prompts Exit code: 1 diff --git a/bundle/deploy/terraform/import.go b/bundle/deploy/terraform/import.go index 8fdfe1212d..5524ceee2f 100644 --- a/bundle/deploy/terraform/import.go +++ b/bundle/deploy/terraform/import.go @@ -71,8 +71,8 @@ func (m *importResource) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn } cmdio.LogString(ctx, output) - if !cmdio.IsPromptSupported(ctx) { - return diag.Errorf("This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed.") + if !cmdio.IsPromptSupported(ctx) && !cmdio.IsYes(ctx) { + return diag.Errorf("This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed.") } ans, err := cmdio.AskYesOrNo(ctx, "Confirm import changes? Changes will be remotely applied only after running 'bundle deploy'.") diff --git a/bundle/phases/bind.go b/bundle/phases/bind.go index 715f97f528..e9195e0ded 100644 --- a/bundle/phases/bind.go +++ b/bundle/phases/bind.go @@ -68,9 +68,9 @@ func Bind(ctx context.Context, b *bundle.Bundle, opts *terraform.BindOptions) { } } - if !cmdio.IsPromptSupported(ctx) { + if !cmdio.IsPromptSupported(ctx) && !cmdio.IsYes(ctx) { result.Cancel() - logdiag.LogError(ctx, errors.New("This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed.")) //nolint + logdiag.LogError(ctx, errors.New("This bind operation requires user confirmation, but the current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed.")) //nolint return } diff --git a/bundle/phases/deploy.go b/bundle/phases/deploy.go index 4613a7a211..de005804d0 100644 --- a/bundle/phases/deploy.go +++ b/bundle/phases/deploy.go @@ -84,8 +84,8 @@ func approvalForDeploy(ctx context.Context, b *bundle.Bundle, plan *deployplan.P return true, nil } - if !cmdio.IsPromptSupported(ctx) { - return false, errors.New("the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve if you would like to skip prompts and proceed") + if !cmdio.IsPromptSupported(ctx) && !cmdio.IsYes(ctx) { + return false, errors.New("the deployment requires destructive actions, but current console does not support prompting. Please specify --auto-approve or --yes if you would like to skip prompts and proceed") } cmdio.LogString(ctx, "") diff --git a/cmd/apps/import.go b/cmd/apps/import.go index b508335231..427a5568e9 100644 --- a/cmd/apps/import.go +++ b/cmd/apps/import.go @@ -39,7 +39,6 @@ func newImportCommand() *cobra.Command { var outputDir string var cleanup bool var forceImport bool - var quiet bool cmd := &cobra.Command{ Use: "import", @@ -82,6 +81,7 @@ Examples: PreRunE: root.MustWorkspaceClient, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + quiet := cmdio.IsQuiet(ctx) w := cmdctx.WorkspaceClient(ctx) // Get current user to filter apps @@ -217,7 +217,6 @@ Examples: cmd.Flags().StringVar(&outputDir, "output-dir", "", "Directory to output the bundle to (defaults to app name)") cmd.Flags().BoolVar(&cleanup, "cleanup", false, "Clean up the previous app folder and all its contents") cmd.Flags().BoolVar(&forceImport, "force-import", false, "Force re-import of an app that was already imported (only works for apps you own)") - cmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "Suppress informational messages (only show errors and prompts)") return cmd } diff --git a/cmd/auth/auth.go b/cmd/auth/auth.go index b06cf8945c..2cf958accc 100644 --- a/cmd/auth/auth.go +++ b/cmd/auth/auth.go @@ -38,7 +38,7 @@ GCP: https://docs.gcp.databricks.com/dev-tools/auth/index.html`, } func promptForHost(ctx context.Context) (string, error) { - if !cmdio.IsPromptSupported(ctx) { + if !cmdio.IsPromptSupported(ctx) || cmdio.IsNoInput(ctx) { return "", errors.New("the command is being run in a non-interactive environment, please specify a host using --host") } @@ -48,7 +48,7 @@ func promptForHost(ctx context.Context) (string, error) { } func promptForAccountID(ctx context.Context) (string, error) { - if !cmdio.IsPromptSupported(ctx) { + if !cmdio.IsPromptSupported(ctx) || cmdio.IsNoInput(ctx) { return "", errors.New("the command is being run in a non-interactive environment, please specify an account ID using --account-id") } @@ -60,7 +60,7 @@ func promptForAccountID(ctx context.Context) (string, error) { } func promptForWorkspaceID(ctx context.Context) (string, error) { - if !cmdio.IsPromptSupported(ctx) { + if !cmdio.IsPromptSupported(ctx) || cmdio.IsNoInput(ctx) { // Workspace ID is optional for unified hosts, so return empty string in non-interactive mode return "", nil } diff --git a/cmd/auth/logout.go b/cmd/auth/logout.go index c95be39c57..e7b2d2da24 100644 --- a/cmd/auth/logout.go +++ b/cmd/auth/logout.go @@ -125,7 +125,7 @@ func runLogout(ctx context.Context, args logoutArgs) error { } if !args.force { - if !cmdio.IsPromptSupported(ctx) { + if (!cmdio.IsPromptSupported(ctx) || cmdio.IsNoInput(ctx)) && !cmdio.IsYes(ctx) { return errors.New("please specify --force to skip confirmation in non-interactive mode") } diff --git a/cmd/bundle/destroy.go b/cmd/bundle/destroy.go index bd9b65f71c..86aa82676d 100644 --- a/cmd/bundle/destroy.go +++ b/cmd/bundle/destroy.go @@ -46,9 +46,9 @@ Typical use cases: } func CommandBundleDestroy(cmd *cobra.Command, args []string, autoApprove, forceDestroy bool) error { - // We require auto-approve for non-interactive terminals since prompts are not possible. - if !cmdio.IsPromptSupported(cmd.Context()) && !autoApprove { - return errors.New("please specify --auto-approve since terminal does not support interactive prompts") + // We require auto-approve or --yes for non-interactive terminals since prompts are not possible. + if !cmdio.IsPromptSupported(cmd.Context()) && !autoApprove && !cmdio.IsYes(cmd.Context()) { + return errors.New("please specify --auto-approve or --yes since terminal does not support interactive prompts") } // Check if context is already initialized (e.g., when called from apps delete override) diff --git a/cmd/completion/install.go b/cmd/completion/install.go index 2de0f656a4..513735688a 100644 --- a/cmd/completion/install.go +++ b/cmd/completion/install.go @@ -63,9 +63,9 @@ func newInstallCmd() *cobra.Command { } // Confirm before writing. - if !autoApprove { + if !autoApprove && !cmdio.IsYes(ctx) { if !cmdio.IsPromptSupported(ctx) { - return errors.New("use --auto-approve to skip the confirmation prompt, or run 'databricks completion status' to preview the detected shell and target file") + return errors.New("use --auto-approve or --yes to skip the confirmation prompt, or run 'databricks completion status' to preview the detected shell and target file") } cmdio.LogString(ctx, "Shell: "+shell.DisplayName()) cmdio.LogString(ctx, "File: "+displayPath) diff --git a/cmd/completion/install_test.go b/cmd/completion/install_test.go new file mode 100644 index 0000000000..b3ad04bae3 --- /dev/null +++ b/cmd/completion/install_test.go @@ -0,0 +1,62 @@ +package completion_test + +import ( + "bytes" + "os" + "strings" + "testing" + + "github.com/databricks/cli/cmd" + "github.com/databricks/cli/cmd/root" + libcompletion "github.com/databricks/cli/libs/completion" + "github.com/databricks/cli/libs/env" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInstallCommandHonorsRootYesFlag(t *testing.T) { + tests := []struct { + name string + args []string + wantErr string + }{ + { + name: "without yes prompts in non-interactive mode", + args: []string{"completion", "install", "--shell", "bash"}, + wantErr: "use --auto-approve or --yes to skip the confirmation prompt", + }, + { + name: "with yes auto-approves", + args: []string{"completion", "install", "--shell", "bash", "--yes"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + home := t.TempDir() + ctx := env.WithUserHomeDir(t.Context(), home) + + var stdout bytes.Buffer + var stderr bytes.Buffer + + rootCmd := cmd.New(ctx) + rootCmd.SetArgs(tt.args) + rootCmd.SetIn(strings.NewReader("")) + rootCmd.SetOut(&stdout) + rootCmd.SetErr(&stderr) + + err := root.Execute(ctx, rootCmd) + if tt.wantErr != "" { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErr) + return + } + + require.NoError(t, err) + + content, err := os.ReadFile(libcompletion.TargetFilePath(libcompletion.Bash, home)) + require.NoError(t, err) + assert.Contains(t, string(content), libcompletion.BeginMarker) + }) + } +} diff --git a/cmd/completion/uninstall.go b/cmd/completion/uninstall.go index 9398ad85fe..68866dc12b 100644 --- a/cmd/completion/uninstall.go +++ b/cmd/completion/uninstall.go @@ -64,9 +64,9 @@ func newUninstallCmd() *cobra.Command { } // Confirm before modifying. - if !autoApprove { + if !autoApprove && !cmdio.IsYes(ctx) { if !cmdio.IsPromptSupported(ctx) { - return errors.New("use --auto-approve to skip the confirmation prompt, or run 'databricks completion status' to preview the detected shell and target file") + return errors.New("use --auto-approve or --yes to skip the confirmation prompt, or run 'databricks completion status' to preview the detected shell and target file") } cmdio.LogString(ctx, "Shell: "+shell.DisplayName()) cmdio.LogString(ctx, "File: "+displayPath) diff --git a/cmd/configure/configure.go b/cmd/configure/configure.go index f134c91191..a96685af59 100644 --- a/cmd/configure/configure.go +++ b/cmd/configure/configure.go @@ -137,7 +137,7 @@ The host must be specified with the --host flag or the DATABRICKS_HOST environme } ctx := cmd.Context() - if cmdio.IsPromptSupported(ctx) { + if cmdio.IsPromptSupported(ctx) && !cmdio.IsNoInput(ctx) { err = configureInteractive(cmd, &flags, &cfg) } else { err = configureNonInteractive(cmd, &flags, &cfg) diff --git a/cmd/root/io.go b/cmd/root/io.go index 6393c62d66..e7418cfbbc 100644 --- a/cmd/root/io.go +++ b/cmd/root/io.go @@ -9,12 +9,23 @@ import ( "github.com/spf13/cobra" ) -const envOutputFormat = "DATABRICKS_OUTPUT_FORMAT" +const ( + envOutputFormat = "DATABRICKS_OUTPUT_FORMAT" + envQuiet = "DATABRICKS_QUIET" + envNoInput = "DATABRICKS_NO_INPUT" + envYes = "DATABRICKS_YES" +) type outputFlag struct { output flags.Output } +type interactionFlags struct { + quiet bool + noInput bool + yes bool +} + func initOutputFlag(cmd *cobra.Command) *outputFlag { f := outputFlag{ output: flags.OutputText, @@ -30,6 +41,28 @@ func initOutputFlag(cmd *cobra.Command) *outputFlag { return &f } +func initInteractionFlags(cmd *cobra.Command) *interactionFlags { + f := &interactionFlags{} + + ctx := cmd.Context() + + // Configure defaults from environment variables. + if enabled, ok := env.GetBool(ctx, envQuiet); ok && enabled { + f.quiet = true + } + if enabled, ok := env.GetBool(ctx, envNoInput); ok && enabled { + f.noInput = true + } + if enabled, ok := env.GetBool(ctx, envYes); ok && enabled { + f.yes = true + } + + cmd.PersistentFlags().BoolVarP(&f.quiet, "quiet", "q", f.quiet, "Suppress non-essential output") + cmd.PersistentFlags().BoolVar(&f.noInput, "no-input", f.noInput, "Disable interactive prompts") + cmd.PersistentFlags().BoolVarP(&f.yes, "yes", "y", f.yes, "Auto-approve all yes/no prompts") + return f +} + func OutputType(cmd *cobra.Command) flags.Output { f, ok := cmd.Flag("output").Value.(*flags.Output) if !ok { @@ -52,3 +85,16 @@ func (f *outputFlag) initializeIO(ctx context.Context, cmd *cobra.Command) (cont cmd.SetContext(ctx) return ctx, nil } + +func (f *interactionFlags) applyToContext(ctx context.Context) context.Context { + if f.quiet { + ctx = cmdio.SetQuiet(ctx) + } + if f.noInput { + ctx = cmdio.SetNoInput(ctx) + } + if f.yes { + ctx = cmdio.SetYes(ctx) + } + return ctx +} diff --git a/cmd/root/io_test.go b/cmd/root/io_test.go new file mode 100644 index 0000000000..bb6748f378 --- /dev/null +++ b/cmd/root/io_test.go @@ -0,0 +1,73 @@ +package root + +import ( + "testing" + + "github.com/databricks/cli/libs/cmdio" + "github.com/databricks/cli/libs/env" + "github.com/databricks/cli/libs/flags" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +func TestInitInteractionFlagsFromEnv(t *testing.T) { + ctx := t.Context() + + ctx = env.Set(ctx, envQuiet, "on") + ctx = env.Set(ctx, envNoInput, "T") + ctx = env.Set(ctx, envYes, "YES") + + cmd := &cobra.Command{Use: "test"} + cmd.SetContext(ctx) + + f := initInteractionFlags(cmd) + assert.True(t, f.quiet) + assert.True(t, f.noInput) + assert.True(t, f.yes) +} + +func TestInitInteractionFlagsFromFalseOrInvalidEnv(t *testing.T) { + ctx := t.Context() + + ctx = env.Set(ctx, envQuiet, "off") + ctx = env.Set(ctx, envNoInput, "") + ctx = env.Set(ctx, envYes, "maybe") + + cmd := &cobra.Command{Use: "test"} + cmd.SetContext(ctx) + + f := initInteractionFlags(cmd) + assert.False(t, f.quiet) + assert.False(t, f.noInput) + assert.False(t, f.yes) +} + +func TestInitInteractionFlagsDefaultFalse(t *testing.T) { + ctx := t.Context() + + cmd := &cobra.Command{Use: "test"} + cmd.SetContext(ctx) + + f := initInteractionFlags(cmd) + assert.False(t, f.quiet) + assert.False(t, f.noInput) + assert.False(t, f.yes) +} + +func TestApplyToContextSetsFlags(t *testing.T) { + ctx := t.Context() + + // Create a minimal cmdio context + cmdIO := cmdio.NewIO(ctx, flags.OutputText, nil, nil, nil, "", "") + ctx = cmdio.InContext(ctx, cmdIO) + + f := &interactionFlags{quiet: true, noInput: true, yes: true} + flagCtx := f.applyToContext(ctx) + + assert.False(t, cmdio.IsQuiet(ctx)) + assert.False(t, cmdio.IsNoInput(ctx)) + assert.False(t, cmdio.IsYes(ctx)) + assert.True(t, cmdio.IsQuiet(flagCtx)) + assert.True(t, cmdio.IsNoInput(flagCtx)) + assert.True(t, cmdio.IsYes(flagCtx)) +} diff --git a/cmd/root/root.go b/cmd/root/root.go index ca47a23d3a..862bf903c7 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -46,6 +46,7 @@ func New(ctx context.Context) *cobra.Command { // Initialize flags logFlags := initLogFlags(cmd) outputFlag := initOutputFlag(cmd) + interactionFlags := initInteractionFlags(cmd) initProfileFlag(cmd) initEnvironmentFlag(cmd) initTargetFlag(cmd) @@ -64,6 +65,10 @@ func New(ctx context.Context) *cobra.Command { return err } + // Apply interaction flags (--quiet, --no-input, --yes) to cmdio context. + ctx = interactionFlags.applyToContext(ctx) + cmd.SetContext(ctx) + // Configure default logger. ctx, err = logFlags.initializeContext(ctx) if err != nil { diff --git a/libs/cmdio/capabilities.go b/libs/cmdio/capabilities.go index 455acebc77..7629d4ae18 100644 --- a/libs/cmdio/capabilities.go +++ b/libs/cmdio/capabilities.go @@ -18,6 +18,11 @@ type Capabilities struct { // Environment flags color bool // Color output is enabled (NO_COLOR not set and TERM not dumb) isGitBash bool // Git Bash on Windows + + // User-controlled interaction flags (set via CLI flags or env vars) + quiet bool // Suppress non-essential output (spinners, logs, non-error diagnostics) + noInput bool // Disable all interactive prompts + yes bool // Auto-approve all yes/no confirmation prompts } // newCapabilities detects terminal capabilities from context and I/O streams. @@ -38,6 +43,8 @@ func (c Capabilities) SupportsInteractive() bool { // SupportsPrompt returns true if terminal supports user prompting. // Prompts write to stderr and read from stdin, so we only need those to be TTYs. +// Note: this only reflects terminal capability. The --no-input flag is checked +// directly in prompt functions (Ask, AskYesOrNo, etc.) rather than here. func (c Capabilities) SupportsPrompt() bool { return c.SupportsInteractive() && c.stdinIsTTY && !c.isGitBash } diff --git a/libs/cmdio/compat.go b/libs/cmdio/compat.go index 1c0b3b0615..b7dfa8ebc8 100644 --- a/libs/cmdio/compat.go +++ b/libs/cmdio/compat.go @@ -15,14 +15,21 @@ Temporary compatibility layer for the progress logger interfaces. // Log is a compatibility layer for the progress logger interfaces. // It writes the string representation of the stringer to the error writer. +// Suppressed in quiet mode. func Log(ctx context.Context, str fmt.Stringer) { LogString(ctx, str.String()) } // LogString is a compatibility layer for the progress logger interfaces. // It writes the string to the error writer. +// Suppressed in quiet mode. func LogString(ctx context.Context, str string) { c := fromContext(ctx) + // Quiet mode suppresses informational output (progress, status messages) + // while still allowing Render() to write structured data to stdout. + if c.capabilities.quiet { + return + } _, _ = io.WriteString(c.err, str+"\n") } @@ -55,9 +62,18 @@ func readLine(r io.Reader) (string, error) { // Ask is a compatibility layer for the progress logger interfaces. // It prompts the user with a question and returns the answer. +// When --no-input is set, returns the default value if one is provided, +// otherwise returns ErrNoInput. func Ask(ctx context.Context, question, defaultVal string) (string, error) { c := fromContext(ctx) + if c.capabilities.noInput { + if defaultVal != "" { + return defaultVal, nil + } + return "", ErrNoInput + } + // Add default value to question prompt. if defaultVal != "" { question += fmt.Sprintf(` [%s]`, defaultVal) @@ -86,7 +102,17 @@ func Ask(ctx context.Context, question, defaultVal string) (string, error) { // AskYesOrNo is a compatibility layer for the progress logger interfaces. // It prompts the user with a question and returns the answer. +// Precedence: --yes (returns true) > --no-input (returns ErrNoInput) > normal prompting. func AskYesOrNo(ctx context.Context, question string) (bool, error) { + c := fromContext(ctx) + + if c.capabilities.yes { + return true, nil + } + if c.capabilities.noInput { + return false, ErrNoInput + } + ans, err := Ask(ctx, question+" [y/n]", "") if err != nil { return false, err @@ -105,9 +131,14 @@ func splitAtLastNewLine(s string) (string, string) { // AskSelect is a compatibility layer for the progress logger interfaces. // It prompts the user with a question and returns the answer. +// Returns ErrNoInput when --no-input is set. func AskSelect(ctx context.Context, question string, choices []string) (string, error) { c := fromContext(ctx) + if c.capabilities.noInput { + return "", ErrNoInput + } + // Promptui does not support multiline prompts. So we split the question. first, last := splitAtLastNewLine(question) _, err := io.WriteString(c.err, first) diff --git a/libs/cmdio/interaction_flags_test.go b/libs/cmdio/interaction_flags_test.go new file mode 100644 index 0000000000..c7551d5c13 --- /dev/null +++ b/libs/cmdio/interaction_flags_test.go @@ -0,0 +1,405 @@ +package cmdio + +import ( + "bytes" + "io" + "strings" + "testing" + + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/flags" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// stringer is a simple fmt.Stringer implementation for tests. +type stringer string + +func (s stringer) String() string { return string(s) } + +// --- Quiet mode tests --- + +func TestLogStringSuppressedWhenQuiet(t *testing.T) { + ctx := t.Context() + stderr := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{quiet: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: stderr, + } + ctx = InContext(ctx, cmdIO) + + LogString(ctx, "should be suppressed") + assert.Empty(t, stderr.String()) +} + +func TestLogStringShownWhenNotQuiet(t *testing.T) { + ctx := t.Context() + stderr := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{quiet: false}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: stderr, + } + ctx = InContext(ctx, cmdIO) + + LogString(ctx, "hello") + assert.Equal(t, "hello\n", stderr.String()) +} + +func TestLogSuppressedWhenQuiet(t *testing.T) { + ctx := t.Context() + stderr := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{quiet: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: stderr, + } + ctx = InContext(ctx, cmdIO) + + Log(ctx, stringer("should be suppressed")) + assert.Empty(t, stderr.String()) +} + +func TestSpinnerNoOpWhenQuiet(t *testing.T) { + ctx := t.Context() + stderr := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{ + quiet: true, + stderrIsTTY: true, + color: true, + }, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: stderr, + } + ctx = InContext(ctx, cmdIO) + + sp := NewSpinner(ctx) + assert.Nil(t, sp.p) // Should be no-op (nil program) + sp.Update("test") + sp.Close() +} + +func TestRenderDiagnosticsQuietFiltersNonErrors(t *testing.T) { + ctx := t.Context() + stderr := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{quiet: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: stderr, + } + ctx = InContext(ctx, cmdIO) + + diags := diag.Diagnostics{ + {Severity: diag.Error, Summary: "error message"}, + {Severity: diag.Warning, Summary: "warning message"}, + {Severity: diag.Recommendation, Summary: "recommendation message"}, + } + + err := RenderDiagnostics(ctx, diags) + require.NoError(t, err) + + output := stderr.String() + assert.Contains(t, output, "error message") + assert.NotContains(t, output, "warning message") + assert.NotContains(t, output, "recommendation message") +} + +func TestRenderDiagnosticsNonQuietShowsAll(t *testing.T) { + ctx := t.Context() + stderr := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{quiet: false}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: stderr, + } + ctx = InContext(ctx, cmdIO) + + diags := diag.Diagnostics{ + {Severity: diag.Error, Summary: "error message"}, + {Severity: diag.Warning, Summary: "warning message"}, + } + + err := RenderDiagnostics(ctx, diags) + require.NoError(t, err) + + output := stderr.String() + assert.Contains(t, output, "error message") + assert.Contains(t, output, "warning message") +} + +func TestIsQuietAndSetQuiet(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + assert.False(t, IsQuiet(ctx)) + quietCtx := SetQuiet(ctx) + assert.False(t, IsQuiet(ctx)) + assert.True(t, IsQuiet(quietCtx)) +} + +// --- No-input mode tests --- + +func TestIsNoInputAndSetNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + assert.False(t, IsNoInput(ctx)) + noInputCtx := SetNoInput(ctx) + assert.False(t, IsNoInput(ctx)) + assert.True(t, IsNoInput(noInputCtx)) +} + +func TestSupportsPromptTrueEvenWhenNoInput(t *testing.T) { + // SupportsPrompt reports terminal capability only. + // The --no-input flag is checked directly in prompt functions. + caps := Capabilities{ + stdinIsTTY: true, + stdoutIsTTY: true, + stderrIsTTY: true, + color: true, + isGitBash: false, + noInput: true, + } + assert.True(t, caps.SupportsPrompt()) +} + +func TestAskReturnsDefaultWhenNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + result, err := Ask(ctx, "Enter value", "default_value") + require.NoError(t, err) + assert.Equal(t, "default_value", result) +} + +func TestAskReturnsErrNoInputWhenNoDefault(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + _, err := Ask(ctx, "Enter value", "") + assert.ErrorIs(t, err, ErrNoInput) +} + +func TestAskYesOrNoReturnsErrNoInputWhenNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + _, err := AskYesOrNo(ctx, "Continue?") + assert.ErrorIs(t, err, ErrNoInput) +} + +func TestAskSelectReturnsErrNoInputWhenNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + _, err := AskSelect(ctx, "Choose", []string{"a", "b"}) + assert.ErrorIs(t, err, ErrNoInput) +} + +func TestSelectReturnsErrNoInputWhenNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + items := map[string]string{"item1": "1", "item2": "2"} + _, err := Select(ctx, items, "Choose item") + assert.ErrorIs(t, err, ErrNoInput) +} + +func TestSecretReturnsErrNoInputWhenNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + _, err := Secret(ctx, "Enter secret") + assert.ErrorIs(t, err, ErrNoInput) +} + +func TestRunSelectReturnsErrNoInputWhenNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + _, _, err := RunSelect(ctx, nil) + assert.ErrorIs(t, err, ErrNoInput) +} + +// --- Yes mode tests --- + +func TestIsYesAndSetYes(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + assert.False(t, IsYes(ctx)) + yesCtx := SetYes(ctx) + assert.False(t, IsYes(ctx)) + assert.True(t, IsYes(yesCtx)) +} + +func TestAskYesOrNoReturnsTrueWhenYes(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{yes: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + result, err := AskYesOrNo(ctx, "Continue?") + require.NoError(t, err) + assert.True(t, result) +} + +// --- Precedence tests --- + +func TestAskYesOrNoYesTakesPrecedenceOverNoInput(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{yes: true, noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + // --yes should take precedence over --no-input for yes/no prompts + result, err := AskYesOrNo(ctx, "Continue?") + require.NoError(t, err) + assert.True(t, result) +} + +func TestAskStillReturnsErrNoInputEvenWithYes(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{yes: true, noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + // --yes does not affect Ask() (free-form input), --no-input still blocks it + _, err := Ask(ctx, "Enter value", "") + assert.ErrorIs(t, err, ErrNoInput) +} + +func TestAskSelectStillReturnsErrNoInputEvenWithYes(t *testing.T) { + ctx := t.Context() + cmdIO := &cmdIO{ + capabilities: Capabilities{yes: true, noInput: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: io.Discard, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + // --yes does not affect AskSelect(), --no-input still blocks it + _, err := AskSelect(ctx, "Choose", []string{"a", "b"}) + assert.ErrorIs(t, err, ErrNoInput) +} + +// --- Render output is not affected by quiet --- + +func TestRenderDataOutputNotAffectedByQuiet(t *testing.T) { + ctx := t.Context() + stdout := &bytes.Buffer{} + cmdIO := &cmdIO{ + capabilities: Capabilities{quiet: true}, + outputFormat: flags.OutputText, + in: io.NopCloser(strings.NewReader("")), + out: stdout, + err: io.Discard, + } + ctx = InContext(ctx, cmdIO) + + data := map[string]string{"key": "value"} + err := Render(ctx, data) + require.NoError(t, err) + assert.Contains(t, stdout.String(), "value") +} diff --git a/libs/cmdio/io.go b/libs/cmdio/io.go index 125196b58e..415373e596 100644 --- a/libs/cmdio/io.go +++ b/libs/cmdio/io.go @@ -2,6 +2,7 @@ package cmdio import ( "context" + "errors" "fmt" "io" "os" @@ -14,6 +15,9 @@ import ( "github.com/manifoldco/promptui" ) +// ErrNoInput is returned when an interactive prompt is attempted with --no-input set. +var ErrNoInput = errors.New("prompting is disabled with --no-input") + // cmdIO is the private instance, that is not supposed to be accessed // outside of `cmdio` package. Use the public package-level functions // to access the inner state. @@ -77,9 +81,71 @@ func GetInteractiveMode(ctx context.Context) InteractiveMode { return c.capabilities.InteractiveMode() } +func withCapabilities(ctx context.Context, update func(*Capabilities)) context.Context { + c := fromContext(ctx).clone() + update(&c.capabilities) + return InContext(ctx, c) +} + +func (c *cmdIO) clone() *cmdIO { + return &cmdIO{ + capabilities: c.capabilities, + outputFormat: c.outputFormat, + headerTemplate: c.headerTemplate, + template: c.template, + in: c.in, + out: c.out, + err: c.err, + teaProgram: c.teaProgram, + teaDone: c.teaDone, + } +} + +// SetQuiet returns a new context with quiet mode enabled. +func SetQuiet(ctx context.Context) context.Context { + return withCapabilities(ctx, func(c *Capabilities) { + c.quiet = true + }) +} + +// IsQuiet returns true if quiet mode is enabled. +func IsQuiet(ctx context.Context) bool { + c := fromContext(ctx) + return c.capabilities.quiet +} + +// SetNoInput returns a new context with prompting disabled. +func SetNoInput(ctx context.Context) context.Context { + return withCapabilities(ctx, func(c *Capabilities) { + c.noInput = true + }) +} + +// IsNoInput returns true if no-input mode is enabled. +func IsNoInput(ctx context.Context) bool { + c := fromContext(ctx) + return c.capabilities.noInput +} + +// SetYes returns a new context with yes/no prompts auto-approved. +func SetYes(ctx context.Context) context.Context { + return withCapabilities(ctx, func(c *Capabilities) { + c.yes = true + }) +} + +// IsYes returns true if yes mode is enabled. +func IsYes(ctx context.Context) bool { + c := fromContext(ctx) + return c.capabilities.yes +} + type Tuple struct{ Name, Id string } func (c *cmdIO) Select(items []Tuple, label string) (id string, err error) { + if c.capabilities.noInput { + return "", ErrNoInput + } if !c.capabilities.SupportsInteractive() { return "", fmt.Errorf("expected to have %s", label) } @@ -129,6 +195,9 @@ func SelectOrdered(ctx context.Context, items []Tuple, label string) (id string, } func (c *cmdIO) Secret(label string) (value string, err error) { + if c.capabilities.noInput { + return "", ErrNoInput + } prompt := (promptui.Prompt{ Label: label, Mask: '*', @@ -176,6 +245,9 @@ func Prompt(ctx context.Context) *promptui.Prompt { func RunSelect(ctx context.Context, prompt *promptui.Select) (int, string, error) { c := fromContext(ctx) + if c.capabilities.noInput { + return 0, "", ErrNoInput + } prompt.Stdin = c.promptStdin() prompt.Stdout = nopWriteCloser{c.err} return prompt.Run() diff --git a/libs/cmdio/render.go b/libs/cmdio/render.go index c344c3d028..c9ca9f2be3 100644 --- a/libs/cmdio/render.go +++ b/libs/cmdio/render.go @@ -449,8 +449,19 @@ const recommendationTemplate = `{{ "Recommendation" | blue }}: {{ .Summary }} ` +// RenderDiagnostics renders diagnostics to stderr. +// In quiet mode, only Error-severity diagnostics are rendered. func RenderDiagnostics(ctx context.Context, diags diag.Diagnostics) error { c := fromContext(ctx) + if c.capabilities.quiet { + var errorsOnly diag.Diagnostics + for _, d := range diags { + if d.Severity == diag.Error { + errorsOnly = append(errorsOnly, d) + } + } + return renderDiagnostics(c.err, errorsOnly) + } return renderDiagnostics(c.err, diags) } diff --git a/libs/cmdio/spinner.go b/libs/cmdio/spinner.go index e9ca438f9b..af7d228bba 100644 --- a/libs/cmdio/spinner.go +++ b/libs/cmdio/spinner.go @@ -122,8 +122,8 @@ func (sp *spinner) Close() { // defer sp.Close() // sp.Update("processing files") func (c *cmdIO) NewSpinner(ctx context.Context) *spinner { - // Don't show spinner if not interactive - if !c.capabilities.SupportsInteractive() { + // Don't show spinner if quiet or not interactive + if c.capabilities.quiet || !c.capabilities.SupportsInteractive() { return &spinner{p: nil, c: c, ctx: ctx} }