Skip to content

Commit

Permalink
Move prompts and progress bars to stderr rather than stdout (gopasspw…
Browse files Browse the repository at this point in the history
…#2004)

Having password prompts/etc on stdout is inconvenient in any context where the output is being piped to something other than a terminal.  This change makes gopass more scripting-friendly, especially when using the age backend.  It also removes a redundant password prompt message when the age backend asks for a password, since pinentry will display its own password prompt immediately afterward.

RELEASE_NOTES=[ENHANCEMENT] Move password prompts to stderr

Signed-off-by: Faye Duxovni <duxovni@duxovni.org>
  • Loading branch information
duxovni committed Oct 8, 2021
1 parent e4ec15a commit e90cd9a
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 56 deletions.
24 changes: 12 additions & 12 deletions internal/action/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ func TestCreateWebsite(t *testing.T) {
act.cfg.ClipTimeout = 1

buf := &bytes.Buffer{}
out.Stdout = buf
termio.Stdout = buf
out.Stderr = buf
termio.Stderr = buf
defer func() {
out.Stdout = os.Stdout
termio.Stdout = os.Stdout
out.Stderr = os.Stderr
termio.Stderr = os.Stderr
}()

// provide values on redirected stdin
Expand Down Expand Up @@ -143,11 +143,11 @@ func TestCreatePIN(t *testing.T) {
act.cfg.ClipTimeout = 1

buf := &bytes.Buffer{}
out.Stdout = buf
termio.Stdout = buf
out.Stderr = buf
termio.Stderr = buf
defer func() {
out.Stdout = os.Stdout
termio.Stdout = os.Stdout
out.Stderr = os.Stderr
termio.Stderr = os.Stderr
}()

ctx = ctxutil.WithAlwaysYes(ctx, true)
Expand Down Expand Up @@ -197,11 +197,11 @@ func TestCreateGeneric(t *testing.T) {
act.cfg.ClipTimeout = 1

buf := &bytes.Buffer{}
out.Stdout = buf
termio.Stdout = buf
out.Stderr = buf
termio.Stderr = buf
defer func() {
out.Stdout = os.Stdout
termio.Stdout = os.Stdout
out.Stderr = os.Stderr
termio.Stderr = os.Stderr
}()

// provide values on redirected stdin
Expand Down
3 changes: 0 additions & 3 deletions internal/backend/crypto/age/askpass.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"time"

"github.com/gopasspw/gopass/internal/cache"
"github.com/gopasspw/gopass/internal/out"
"github.com/gopasspw/gopass/pkg/debug"
"github.com/gopasspw/gopass/pkg/pinentry/cli"
"github.com/gopasspw/pinentry"
Expand Down Expand Up @@ -62,8 +61,6 @@ func (a *askPass) Passphrase(key string, reason string, repeat bool) (string, er
}
debug.Log("Value for %s not found in cache", key)

// TODO we shouldn't print here like this ...
out.Printf(context.TODO(), "🔑 Please enter your passphrase to unlock the age keyring")
pi, err := a.pinentry()
if err != nil {
return "", fmt.Errorf("pinentry (%s) error: %w", pinentry.GetBinary(), err)
Expand Down
6 changes: 3 additions & 3 deletions pkg/termio/ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
)

var (
// Stdout is exported for tests
Stdout io.Writer = os.Stdout
// Stderr is exported for tests
Stderr io.Writer = os.Stderr
// Stdin is exported for tests
Stdin io.Reader = os.Stdin
// ErrAborted is returned if the user aborts an action
Expand All @@ -39,7 +39,7 @@ func AskForString(ctx context.Context, text, def string) (string, error) {
default:
}

fmt.Fprintf(Stdout, "%s [%s]: ", text, def)
fmt.Fprintf(Stderr, "%s [%s]: ", text, def)
input, err := NewReader(ctx, Stdin).ReadLine()
if err != nil {
return "", fmt.Errorf("failed to read user input: %w", err)
Expand Down
60 changes: 30 additions & 30 deletions pkg/termio/ask_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import (

func TestAskForString(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand All @@ -29,7 +29,7 @@ func TestAskForString(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "foobar", sv)

t.Logf("Out: %s", buf.String())
t.Logf("Stderr: %s", buf.String())
buf.Reset()

// provide value on redirected stdin
Expand All @@ -53,17 +53,17 @@ bar
assert.Equal(t, "foobar", sv)
Stdin = os.Stdin

t.Logf("Out: %s", buf.String())
t.Logf("Stderr: %s", buf.String())
buf.Reset()
}

func TestAskForBool(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand Down Expand Up @@ -115,11 +115,11 @@ z

func TestAskForInt(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand Down Expand Up @@ -147,11 +147,11 @@ func TestAskForInt(t *testing.T) {

func TestAskForConfirmation(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand All @@ -176,11 +176,11 @@ n

func TestAskForKeyImport(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand All @@ -204,11 +204,11 @@ z

func TestAskForPasswordNonInteractive(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand Down Expand Up @@ -244,11 +244,11 @@ foobat

func TestAskForPasswordInteractive(t *testing.T) {
buf := &bytes.Buffer{}
out.Stdout = buf
Stdout = buf
out.Stderr = buf
Stderr = buf
defer func() {
out.Stdout = os.Stdout
Stdout = os.Stdout
out.Stderr = os.Stderr
Stderr = os.Stderr
}()

ctx := context.Background()
Expand Down
8 changes: 4 additions & 4 deletions pkg/termio/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (p *ProgressBar) Done() {
if p.Hidden {
return
}
fmt.Fprintln(Stdout, "")
fmt.Fprintln(Stderr, "")
}

// Clear removes the progress bar
Expand Down Expand Up @@ -140,7 +140,7 @@ func (p *ProgressBar) doPrint() {
size := int(barWidth) - len(text) - len(pctStr) - 5
fill := int(math.Max(2, math.Floor((float64(size)*pct)+.5)))

fmt.Fprint(Stdout, text)
fmt.Fprint(Stderr, text)

// not enough space
if size < 11 {
Expand All @@ -155,7 +155,7 @@ func (p *ProgressBar) doPrint() {
ta := strings.Repeat(color.MagentaString("a"), boundedMin(1, fill-3))
ts := strings.Repeat(color.CyanString("s"), boundedMin(2, fill-1))
spc := strings.Repeat(" ", gteZero(size-fill))
fmt.Fprintf(Stdout, "[%s%s%s%s%s%s] %s ",
fmt.Fprintf(Stderr, "[%s%s%s%s%s%s] %s ",
tg,
to,
tp,
Expand Down Expand Up @@ -201,5 +201,5 @@ func (p *ProgressBar) percent() (int64, int64, float64) {
}

func clearLine() {
fmt.Fprintf(Stdout, "\033[2K\r]")
fmt.Fprintf(Stderr, "\033[2K\r]")
}
4 changes: 2 additions & 2 deletions pkg/termio/promptpass_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func promptPass(ctx context.Context, prompt string) (string, error) {
os.Exit(1)
}()

fmt.Fprintf(Stdout, "%s: ", prompt)
fmt.Fprintf(Stderr, "%s: ", prompt)
passBytes, err := term.ReadPassword(fd)
fmt.Fprintln(Stdout, "")
fmt.Fprintln(Stderr, "")
return string(passBytes), err
}
4 changes: 2 additions & 2 deletions pkg/termio/promptpass_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func promptPass(ctx context.Context, prompt string) (string, error) {
return AskForString(ctx, prompt, "")
}

fmt.Fprintf(Stdout, "%s: ", prompt)
fmt.Fprintf(Stderr, "%s: ", prompt)
passBytes, err := terminal.ReadPassword(int(os.Stdin.Fd()))
fmt.Fprintln(Stdout, "")
fmt.Fprintln(Stderr, "")
return string(passBytes), err
}

0 comments on commit e90cd9a

Please sign in to comment.