Do not allow input prompts in Git Bash terminal (#1069)

## Changes

Likely due to fact that Git Bash does not correctly support ANSI escape
sequences, we cannot use `promptui` package there. See known issues:

- https://github.com/manifoldco/promptui/issues/208
- https://github.com/chzyer/readline/issues/191
This commit is contained in:
Andrew Nester 2023-12-18 16:01:59 +01:00 committed by GitHub
parent 4765493f18
commit 6dd6899b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 56 additions and 10 deletions

View File

@ -131,7 +131,7 @@ See https://docs.databricks.com/en/dev-tools/bundles/templates.html for more inf
templatePath = args[0] templatePath = args[0]
} else { } else {
var err error var err error
if !cmdio.IsOutTTY(ctx) || !cmdio.IsInTTY(ctx) { if !cmdio.IsPromptSupported(ctx) {
return errors.New("please specify a template") return errors.New("please specify a template")
} }
templatePath, err = cmdio.AskSelect(ctx, "Template to use", nativeTemplateOptions()) templatePath, err = cmdio.AskSelect(ctx, "Template to use", nativeTemplateOptions())

View File

@ -45,7 +45,7 @@ func newRunCommand() *cobra.Command {
} }
// If no arguments are specified, prompt the user to select something to run. // If no arguments are specified, prompt the user to select something to run.
if len(args) == 0 && cmdio.IsInteractive(ctx) { if len(args) == 0 && cmdio.IsPromptSupported(ctx) {
// Invert completions from KEY -> NAME, to NAME -> KEY. // Invert completions from KEY -> NAME, to NAME -> KEY.
inv := make(map[string]string) inv := make(map[string]string)
for k, v := range run.ResourceCompletionMap(b) { for k, v := range run.ResourceCompletionMap(b) {

View File

@ -157,7 +157,7 @@ func (i *installer) recordVersion(ctx context.Context) error {
} }
func (i *installer) login(ctx context.Context) (*databricks.WorkspaceClient, error) { func (i *installer) login(ctx context.Context) (*databricks.WorkspaceClient, error) {
if !cmdio.IsInteractive(ctx) { if !cmdio.IsPromptSupported(ctx) {
log.Debugf(ctx, "Skipping workspace profile prompts in non-interactive mode") log.Debugf(ctx, "Skipping workspace profile prompts in non-interactive mode")
return nil, nil return nil, nil
} }

View File

@ -50,7 +50,7 @@ func (lc *loginConfig) askWorkspaceProfile(ctx context.Context, cfg *config.Conf
lc.WorkspaceProfile = cfg.Profile lc.WorkspaceProfile = cfg.Profile
return return
} }
if !cmdio.IsInteractive(ctx) { if !cmdio.IsPromptSupported(ctx) {
return ErrNotInTTY return ErrNotInTTY
} }
lc.WorkspaceProfile, err = root.AskForWorkspaceProfile(ctx) lc.WorkspaceProfile, err = root.AskForWorkspaceProfile(ctx)
@ -66,7 +66,7 @@ func (lc *loginConfig) askCluster(ctx context.Context, w *databricks.WorkspaceCl
lc.ClusterID = w.Config.ClusterID lc.ClusterID = w.Config.ClusterID
return return
} }
if !cmdio.IsInteractive(ctx) { if !cmdio.IsPromptSupported(ctx) {
return ErrNotInTTY return ErrNotInTTY
} }
clusterID, err := cfgpickers.AskForCluster(ctx, w, clusterID, err := cfgpickers.AskForCluster(ctx, w,
@ -87,7 +87,7 @@ func (lc *loginConfig) askWarehouse(ctx context.Context, w *databricks.Workspace
lc.WarehouseID = w.Config.WarehouseID lc.WarehouseID = w.Config.WarehouseID
return return
} }
if !cmdio.IsInteractive(ctx) { if !cmdio.IsPromptSupported(ctx) {
return ErrNotInTTY return ErrNotInTTY
} }
lc.WarehouseID, err = cfgpickers.AskForWarehouse(ctx, w, lc.WarehouseID, err = cfgpickers.AskForWarehouse(ctx, w,
@ -99,7 +99,7 @@ func (lc *loginConfig) askAccountProfile(ctx context.Context, cfg *config.Config
if !lc.HasAccountLevelCommands() { if !lc.HasAccountLevelCommands() {
return nil return nil
} }
if !cmdio.IsInteractive(ctx) { if !cmdio.IsPromptSupported(ctx) {
return ErrNotInTTY return ErrNotInTTY
} }
lc.AccountProfile, err = root.AskForAccountProfile(ctx) lc.AccountProfile, err = root.AskForAccountProfile(ctx)

View File

@ -41,7 +41,7 @@ func accountClientOrPrompt(ctx context.Context, cfg *config.Config, allowPrompt
} }
prompt := false prompt := false
if allowPrompt && err != nil && cmdio.IsInteractive(ctx) { if allowPrompt && err != nil && cmdio.IsPromptSupported(ctx) {
// Prompt to select a profile if the current configuration is not an account client. // Prompt to select a profile if the current configuration is not an account client.
prompt = prompt || errors.Is(err, databricks.ErrNotAccountClient) prompt = prompt || errors.Is(err, databricks.ErrNotAccountClient)
// Prompt to select a profile if the current configuration doesn't resolve to a credential provider. // Prompt to select a profile if the current configuration doesn't resolve to a credential provider.
@ -109,7 +109,7 @@ func workspaceClientOrPrompt(ctx context.Context, cfg *config.Config, allowPromp
} }
prompt := false prompt := false
if allowPrompt && err != nil && cmdio.IsInteractive(ctx) { if allowPrompt && err != nil && cmdio.IsPromptSupported(ctx) {
// Prompt to select a profile if the current configuration is not a workspace client. // Prompt to select a profile if the current configuration is not a workspace client.
prompt = prompt || errors.Is(err, databricks.ErrNotWorkspaceClient) prompt = prompt || errors.Is(err, databricks.ErrNotWorkspaceClient)
// Prompt to select a profile if the current configuration doesn't resolve to a credential provider. // Prompt to select a profile if the current configuration doesn't resolve to a credential provider.

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/briandowns/spinner" "github.com/briandowns/spinner"
"github.com/databricks/cli/libs/env"
"github.com/databricks/cli/libs/flags" "github.com/databricks/cli/libs/flags"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
@ -88,6 +89,30 @@ func (c *cmdIO) IsTTY() bool {
return isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd) return isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd)
} }
func IsPromptSupported(ctx context.Context) bool {
// We do not allow prompting in non-interactive mode and in Git Bash on Windows.
// Likely due to fact that Git Bash does not (correctly support ANSI escape sequences,
// we cannot use promptui package there.
// See known issues:
// - https://github.com/manifoldco/promptui/issues/208
// - https://github.com/chzyer/readline/issues/191
// We also do not allow prompting in non-interactive mode,
// because it's not possible to read from stdin in non-interactive mode.
return (IsInteractive(ctx) || (IsOutTTY(ctx) && IsInTTY(ctx))) && !IsGitBash(ctx)
}
func IsGitBash(ctx context.Context) bool {
// Check if the MSYSTEM environment variable is set to "MINGW64"
msystem := env.Get(ctx, "MSYSTEM")
if strings.EqualFold(msystem, "MINGW64") {
// Check for typical Git Bash env variable for prompts
ps1 := env.Get(ctx, "PS1")
return strings.Contains(ps1, "MINGW") || strings.Contains(ps1, "MSYSTEM")
}
return false
}
func Render(ctx context.Context, v any) error { func Render(ctx context.Context, v any) error {
c := fromContext(ctx) c := fromContext(ctx)
return RenderWithTemplate(ctx, v, c.template) return RenderWithTemplate(ctx, v, c.template)

21
libs/cmdio/io_test.go Normal file
View File

@ -0,0 +1,21 @@
package cmdio
import (
"context"
"testing"
"github.com/databricks/cli/libs/env"
"github.com/stretchr/testify/assert"
)
func TestIsPromptSupportedFalseForGitBash(t *testing.T) {
ctx := context.Background()
ctx, _ = SetupTest(ctx)
assert.True(t, IsPromptSupported(ctx))
ctx = env.Set(ctx, "MSYSTEM", "MINGW64")
ctx = env.Set(ctx, "TERM", "xterm")
ctx = env.Set(ctx, "PS1", "\\[\033]0;$TITLEPREFIX:$PWD\007\\]\n\\[\033[32m\\]\\u@\\h \\[\033[35m\\]$MSYSTEM \\[\033[33m\\]\\w\\[\033[36m\\]`__git_ps1`\\[\033[0m\\]\n$")
assert.False(t, IsPromptSupported(ctx))
}

View File

@ -212,7 +212,7 @@ func (c *config) promptForValues(r *renderer) error {
// Prompt user for any missing config values. Assign default values if // Prompt user for any missing config values. Assign default values if
// terminal is not TTY // terminal is not TTY
func (c *config) promptOrAssignDefaultValues(r *renderer) error { func (c *config) promptOrAssignDefaultValues(r *renderer) error {
if cmdio.IsOutTTY(c.ctx) && cmdio.IsInTTY(c.ctx) { if cmdio.IsPromptSupported(c.ctx) {
return c.promptForValues(r) return c.promptForValues(r)
} }
return c.assignDefaultValues(r) return c.assignDefaultValues(r)