Skip to content
This repository was archived by the owner on Feb 16, 2023. It is now read-only.

Commit 9f6242f

Browse files
authored
Merge pull request #212 from secrethub/feature/multiline-line
Add --multiline flag to write command
2 parents dcbe81f + 12d8857 commit 9f6242f

5 files changed

Lines changed: 52 additions & 3 deletions

File tree

internals/cli/ui/ask.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ui
33
import (
44
"fmt"
55
"io"
6+
"io/ioutil"
67
"os"
78
"strconv"
89
"strings"
@@ -57,6 +58,27 @@ func AskSecret(io IO, question string) (string, error) {
5758
return string(raw), nil
5859
}
5960

61+
// AskMultiline prints out the question and reads back the input until an EOF is reached.
62+
// The input is displayed to the user.
63+
func AskMultiline(io IO, question string) ([]byte, error) {
64+
promptIn, promptOut, err := io.Prompts()
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
_, err = fmt.Fprintf(promptOut, "%s\n", question)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
raw, err := ioutil.ReadAll(promptIn)
75+
if err != nil {
76+
return nil, err
77+
}
78+
fmt.Fprintln(promptOut)
79+
return raw, nil
80+
}
81+
6082
// AskAndValidate asks the user a question and re-prompts the configured amount of times
6183
// when the users answer does not validate.
6284
func AskAndValidate(io IO, question string, n int, validationFunc func(string) error) (string, error) {

internals/cli/ui/io.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,9 @@ func (f file) IsPiped() bool {
115115

116116
return (stat.Mode() & os.ModeCharDevice) == 0
117117
}
118+
119+
// EOFKey returns the key that should be pressed to enter an EOF.
120+
// This can be used to end multiline input.
121+
func EOFKey() string {
122+
return eofKey()
123+
}

internals/cli/ui/io_unix.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ func NewUserIO() UserIO {
1818

1919
return NewStdUserIO()
2020
}
21+
22+
func eofKey() string {
23+
return "CTRL-D"
24+
}

internals/cli/ui/io_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,7 @@ func (c colorStdout) IsPiped() bool {
3030
return os.Getenv("TERM") == "dumb" ||
3131
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
3232
}
33+
34+
func eofKey() string {
35+
return "CTRL-Z + ENTER"
36+
}

internals/secrethub/write.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@ import (
1313
)
1414

1515
var (
16-
errCannotWriteToVersion = errMain.Code("cannot_write_version").Error("cannot (over)write a specific secret version, they are append only")
17-
errEmptySecret = errMain.Code("cannot_write_empty_secret").Error("secret is empty or contains only whitespace")
18-
errClipAndInFile = errMain.Code("clip_and_in_file").Error("clip and in-file cannot be used together")
16+
errCannotWriteToVersion = errMain.Code("cannot_write_version").Error("cannot (over)write a specific secret version, they are append only")
17+
errEmptySecret = errMain.Code("cannot_write_empty_secret").Error("secret is empty or contains only whitespace")
18+
errClipAndInFile = errMain.Code("clip_and_in_file").Error("clip and in-file cannot be used together")
19+
errMultilineWithNonInteractiveFlag = errMain.Code("multiline_flag_conflict").Error("multiline cannot be used together with clip or in-file")
1920
)
2021

2122
// WriteCommand is a command to write content to a secret.
2223
type WriteCommand struct {
2324
io ui.IO
2425
path api.SecretPath
2526
inFile string
27+
multiline bool
2628
useClipboard bool
2729
noTrim bool
2830
clipper clip.Clipper
@@ -43,6 +45,7 @@ func (cmd *WriteCommand) Register(r command.Registerer) {
4345
clause := r.Command("write", "Write a secret.")
4446
clause.Arg("secret-path", "The path to the secret").Required().PlaceHolder(secretPathPlaceHolder).SetValue(&cmd.path)
4547
clause.Flag("clip", "Use clipboard content as input.").Short('c').BoolVar(&cmd.useClipboard)
48+
clause.Flag("multiline", "Prompt for multiple lines of input, until an EOF is reached. On Linux/Mac, press CTRL-D to end input. On Windows, press CTRL-Z and then ENTER to end input.").Short('m').BoolVar(&cmd.multiline)
4649
clause.Flag("no-trim", "Do not trim leading and trailing whitespace in the secret.").BoolVar(&cmd.noTrim)
4750
clause.Flag("in-file", "Use the contents of this file as the value of the secret.").Short('i').StringVar(&cmd.inFile)
4851

@@ -60,6 +63,10 @@ func (cmd *WriteCommand) Run() error {
6063
return errCannotWriteToVersion
6164
}
6265

66+
if cmd.multiline && (cmd.useClipboard || cmd.inFile != "") {
67+
return errMultilineWithNonInteractiveFlag
68+
}
69+
6370
if cmd.useClipboard && cmd.inFile != "" {
6471
return errClipAndInFile
6572
}
@@ -80,6 +87,12 @@ func (cmd *WriteCommand) Run() error {
8087
if err != nil {
8188
return ui.ErrReadInput(err)
8289
}
90+
} else if cmd.multiline {
91+
var err error
92+
data, err = ui.AskMultiline(cmd.io, "Please type in the value of the secret, followed by ["+ui.EOFKey()+"]:")
93+
if err != nil {
94+
return err
95+
}
8396
} else {
8497
str, err := ui.AskSecret(cmd.io, "Please type in the value of the secret, followed by an [ENTER]:")
8598
if err != nil {

0 commit comments

Comments
 (0)