Write interactive prompts to stderr and handle echo-disable failures

Interactive prompts from 'helm registry login' (username/password/token)
and the signing-key passphrase prompts in 'helm package' and
'helm plugin package' are printed to stdout. Anyone capturing stdout
(scripts, pipelines) gets prompt text mixed into the output; the
docker/kubectl/oras convention is to keep prompts on stderr so stdout
stays machine-readable.

Also in registry login's readLine:

- the term.DisableEcho error was silently discarded, so a failure to
  disable echo would print the password to the terminal with no
  warning; it now returns an error instead
- echo handling is only attempted when stdin is actually a terminal,
  so piped input (e.g. 'echo pw | helm registry login ...' without
  --password-stdin) keeps working as before

Signed-off-by: Mukul <nmukul32@gmail.com>
pull/32393/head
Mukul 2 weeks ago
parent ccb8f595ab
commit c5defd0f6c

@ -206,11 +206,12 @@ func (p *Package) Clearsign(filename string) error {
// promptUser implements provenance.PassphraseFetcher
func promptUser(name string) ([]byte, error) {
fmt.Printf("Password for key %q > ", name)
// Prompts go to stderr so stdout stays clean for scripting.
fmt.Fprintf(os.Stderr, "Password for key %q > ", name)
// syscall.Stdin is not an int in all environments and needs to be coerced
// into one there (e.g., Windows)
pw, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
fmt.Fprintln(os.Stderr)
return pw, err
}

@ -172,9 +172,10 @@ func (o *pluginPackageOptions) run(out io.Writer) error {
}
func (o *pluginPackageOptions) promptUser(name string) ([]byte, error) {
fmt.Printf("Password for key %q > ", name)
// Prompts go to stderr so stdout stays clean for scripting.
fmt.Fprintf(os.Stderr, "Password for key %q > ", name)
pw, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
fmt.Fprintln(os.Stderr)
return pw, err
}

@ -136,14 +136,17 @@ func getUsernamePassword(usernameOpt string, passwordOpt string, passwordFromStd
// Copied/adapted from https://github.com/oras-project/oras
func readLine(prompt string, silent bool) (string, error) {
fmt.Print(prompt)
if silent {
// Prompts go to stderr so stdout stays clean for scripting.
fmt.Fprint(os.Stderr, prompt)
if silent && term.IsTerminal(os.Stdin.Fd()) {
fd := os.Stdin.Fd()
state, err := term.SaveState(fd)
if err != nil {
return "", err
}
term.DisableEcho(fd, state)
if err := term.DisableEcho(fd, state); err != nil {
return "", fmt.Errorf("failed to disable terminal echo for password input: %w", err)
}
defer term.RestoreTerminal(fd, state)
}
@ -153,7 +156,7 @@ func readLine(prompt string, silent bool) (string, error) {
return "", err
}
if silent {
fmt.Println()
fmt.Fprintln(os.Stderr)
}
return string(line), nil

Loading…
Cancel
Save