X Tutup
Skip to content

Commit bd27383

Browse files
committed
Optionally read stdin for gh alias set
Resolves cli#3487
1 parent 5821065 commit bd27383

File tree

2 files changed

+124
-17
lines changed

2 files changed

+124
-17
lines changed

pkg/cmd/alias/set/set.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package set
22

33
import (
44
"fmt"
5+
"io/ioutil"
56
"strings"
67

78
"github.com/MakeNowJust/heredoc"
@@ -37,6 +38,7 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
3738
The expansion may specify additional arguments and flags. If the expansion
3839
includes positional placeholders such as '$1', '$2', etc., any extra arguments
3940
that follow the invocation of an alias will be inserted appropriately.
41+
Reads from STDIN if not specified.
4042
4143
If '--shell' is specified, the alias will be run through a shell interpreter (sh). This allows you
4244
to compose commands with "|" or redirect with ">". Note that extra arguments following the alias
@@ -66,13 +68,21 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
6668
$ gh alias set --shell igrep 'gh issue list --label="$1" | grep $2'
6769
$ gh igrep epic foo
6870
#=> gh issue list --label="epic" | grep "foo"
71+
72+
# users.txt contains multiline 'api graphql -F name="$1" ...' with mixed quotes
73+
$ gh alias set users < users.txt
74+
$ gh users octocat
75+
#=> gh api graphql -F name="octocat" ...
6976
`),
70-
Args: cobra.ExactArgs(2),
77+
Args: cobra.RangeArgs(1, 2),
7178
RunE: func(cmd *cobra.Command, args []string) error {
7279
opts.RootCmd = cmd.Root()
7380

7481
opts.Name = args[0]
75-
opts.Expansion = args[1]
82+
83+
if len(args) > 1 {
84+
opts.Expansion = args[1]
85+
}
7686

7787
if runF != nil {
7888
return runF(opts)
@@ -98,12 +108,16 @@ func setRun(opts *SetOptions) error {
98108
return err
99109
}
100110

111+
expansion, err := getExpansion(opts)
112+
if err != nil {
113+
return fmt.Errorf("did not understand expansion: %w", err)
114+
}
115+
101116
isTerminal := opts.IO.IsStdoutTTY()
102117
if isTerminal {
103-
fmt.Fprintf(opts.IO.ErrOut, "- Adding alias for %s: %s\n", cs.Bold(opts.Name), cs.Bold(opts.Expansion))
118+
fmt.Fprintf(opts.IO.ErrOut, "- Adding alias for %s: %s\n", cs.Bold(opts.Name), cs.Bold(expansion))
104119
}
105120

106-
expansion := opts.Expansion
107121
isShell := opts.IsShell
108122
if isShell && !strings.HasPrefix(expansion, "!") {
109123
expansion = "!" + expansion
@@ -149,3 +163,16 @@ func validCommand(rootCmd *cobra.Command, expansion string) bool {
149163
cmd, _, err := rootCmd.Traverse(split)
150164
return err == nil && cmd != rootCmd
151165
}
166+
167+
func getExpansion(opts *SetOptions) (string, error) {
168+
if opts.Expansion == "" {
169+
stdin, err := ioutil.ReadAll(opts.IO.In)
170+
if err != nil {
171+
return "", fmt.Errorf("failed to read from STDIN: %w", err)
172+
}
173+
174+
return string(stdin), nil
175+
}
176+
177+
return opts.Expansion, nil
178+
}

pkg/cmd/alias/set/set_test.go

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ import (
1616
"github.com/stretchr/testify/require"
1717
)
1818

19-
func runCommand(cfg config.Config, isTTY bool, cli string) (*test.CmdOut, error) {
20-
io, _, stdout, stderr := iostreams.Test()
19+
func runCommand(cfg config.Config, isTTY bool, cli string, in string) (*test.CmdOut, error) {
20+
io, stdin, stdout, stderr := iostreams.Test()
2121
io.SetStdoutTTY(isTTY)
2222
io.SetStdinTTY(isTTY)
2323
io.SetStderrTTY(isTTY)
24+
stdin.WriteString(in)
2425

2526
factory := &cmdutil.Factory{
2627
IOStreams: io,
@@ -41,14 +42,17 @@ func runCommand(cfg config.Config, isTTY bool, cli string) (*test.CmdOut, error)
4142
issueCmd := &cobra.Command{Use: "issue"}
4243
issueCmd.AddCommand(&cobra.Command{Use: "list"})
4344
rootCmd.AddCommand(issueCmd)
45+
apiCmd := &cobra.Command{Use: "api"}
46+
apiCmd.AddCommand(&cobra.Command{Use: "graphql"})
47+
rootCmd.AddCommand(apiCmd)
4448

4549
argv, err := shlex.Split("set " + cli)
4650
if err != nil {
4751
return nil, err
4852
}
4953
rootCmd.SetArgs(argv)
5054

51-
rootCmd.SetIn(&bytes.Buffer{})
55+
rootCmd.SetIn(stdin)
5256
rootCmd.SetOut(ioutil.Discard)
5357
rootCmd.SetErr(ioutil.Discard)
5458

@@ -64,7 +68,7 @@ func TestAliasSet_gh_command(t *testing.T) {
6468

6569
cfg := config.NewFromString(``)
6670

67-
_, err := runCommand(cfg, true, "pr 'pr status'")
71+
_, err := runCommand(cfg, true, "pr 'pr status'", "")
6872
assert.EqualError(t, err, `could not create alias: "pr" is already a gh command`)
6973
}
7074

@@ -77,7 +81,7 @@ func TestAliasSet_empty_aliases(t *testing.T) {
7781
editor: vim
7882
`))
7983

80-
output, err := runCommand(cfg, true, "co 'pr checkout'")
84+
output, err := runCommand(cfg, true, "co 'pr checkout'", "")
8185

8286
if err != nil {
8387
t.Fatalf("unexpected error: %s", err)
@@ -104,7 +108,7 @@ func TestAliasSet_existing_alias(t *testing.T) {
104108
co: pr checkout
105109
`))
106110

107-
output, err := runCommand(cfg, true, "co 'pr checkout -Rcool/repo'")
111+
output, err := runCommand(cfg, true, "co 'pr checkout -Rcool/repo'", "")
108112
require.NoError(t, err)
109113

110114
//nolint:staticcheck // prefer exact matchers over ExpectLines
@@ -117,7 +121,7 @@ func TestAliasSet_space_args(t *testing.T) {
117121

118122
cfg := config.NewFromString(``)
119123

120-
output, err := runCommand(cfg, true, `il 'issue list -l "cool story"'`)
124+
output, err := runCommand(cfg, true, `il 'issue list -l "cool story"'`, "")
121125
require.NoError(t, err)
122126

123127
//nolint:staticcheck // prefer exact matchers over ExpectLines
@@ -153,7 +157,7 @@ func TestAliasSet_arg_processing(t *testing.T) {
153157

154158
cfg := config.NewFromString(``)
155159

156-
output, err := runCommand(cfg, true, c.Cmd)
160+
output, err := runCommand(cfg, true, c.Cmd, "")
157161
if err != nil {
158162
t.Fatalf("got unexpected error running %s: %s", c.Cmd, err)
159163
}
@@ -174,7 +178,7 @@ func TestAliasSet_init_alias_cfg(t *testing.T) {
174178
editor: vim
175179
`))
176180

177-
output, err := runCommand(cfg, true, "diff 'pr diff'")
181+
output, err := runCommand(cfg, true, "diff 'pr diff'", "")
178182
require.NoError(t, err)
179183

180184
expected := `editor: vim
@@ -196,7 +200,7 @@ func TestAliasSet_existing_aliases(t *testing.T) {
196200
foo: bar
197201
`))
198202

199-
output, err := runCommand(cfg, true, "view 'pr view'")
203+
output, err := runCommand(cfg, true, "view 'pr view'", "")
200204
require.NoError(t, err)
201205

202206
expected := `aliases:
@@ -215,7 +219,7 @@ func TestAliasSet_invalid_command(t *testing.T) {
215219

216220
cfg := config.NewFromString(``)
217221

218-
_, err := runCommand(cfg, true, "co 'pe checkout'")
222+
_, err := runCommand(cfg, true, "co 'pe checkout'", "")
219223
assert.EqualError(t, err, "could not create alias: pe checkout does not correspond to a gh command")
220224
}
221225

@@ -225,7 +229,7 @@ func TestShellAlias_flag(t *testing.T) {
225229

226230
cfg := config.NewFromString(``)
227231

228-
output, err := runCommand(cfg, true, "--shell igrep 'gh issue list | grep'")
232+
output, err := runCommand(cfg, true, "--shell igrep 'gh issue list | grep'", "")
229233
if err != nil {
230234
t.Fatalf("unexpected error: %s", err)
231235
}
@@ -245,7 +249,7 @@ func TestShellAlias_bang(t *testing.T) {
245249

246250
cfg := config.NewFromString(``)
247251

248-
output, err := runCommand(cfg, true, "igrep '!gh issue list | grep'")
252+
output, err := runCommand(cfg, true, "igrep '!gh issue list | grep'", "")
249253
require.NoError(t, err)
250254

251255
//nolint:staticcheck // prefer exact matchers over ExpectLines
@@ -256,3 +260,79 @@ func TestShellAlias_bang(t *testing.T) {
256260
`
257261
assert.Equal(t, expected, mainBuf.String())
258262
}
263+
264+
func TestShellAlias_from_stdin(t *testing.T) {
265+
mainBuf := bytes.Buffer{}
266+
defer config.StubWriteConfig(&mainBuf, ioutil.Discard)()
267+
268+
cfg := config.NewFromString(``)
269+
270+
output, err := runCommand(cfg, true, "users", `api graphql -F name="$1" -f query='
271+
query ($name: String!) {
272+
user(login: $name) {
273+
name
274+
}
275+
}'`)
276+
277+
require.NoError(t, err)
278+
279+
//nolint:staticcheck // prefer exact matchers over ExpectLines
280+
test.ExpectLines(t, output.Stderr(), "Adding alias for.*users")
281+
282+
expected := `aliases:
283+
users: |-
284+
api graphql -F name="$1" -f query='
285+
query ($name: String!) {
286+
user(login: $name) {
287+
name
288+
}
289+
}'
290+
`
291+
292+
assert.Equal(t, expected, mainBuf.String())
293+
}
294+
295+
func TestShellAlias_getExpansion(t *testing.T) {
296+
tests := []struct {
297+
name string
298+
want string
299+
expansionArg string
300+
stdin string
301+
}{
302+
{
303+
name: "co",
304+
want: "pr checkout",
305+
expansionArg: "pr checkout",
306+
},
307+
{
308+
name: "co",
309+
want: "pr checkout",
310+
expansionArg: "pr checkout",
311+
stdin: "api graphql -F name=\"$1\"",
312+
},
313+
{
314+
name: "stdin",
315+
want: "api graphql -F name=\"$1\"",
316+
stdin: "api graphql -F name=\"$1\"",
317+
},
318+
}
319+
320+
for _, tt := range tests {
321+
t.Run(tt.name, func(t *testing.T) {
322+
io, stdin, _, _ := iostreams.Test()
323+
324+
io.SetStdinTTY(false)
325+
326+
_, err := stdin.WriteString(tt.stdin)
327+
assert.NoError(t, err)
328+
329+
expansion, err := getExpansion(&SetOptions{
330+
Expansion: tt.expansionArg,
331+
IO: io,
332+
})
333+
assert.NoError(t, err)
334+
335+
assert.Equal(t, expansion, tt.want)
336+
})
337+
}
338+
}

0 commit comments

Comments
 (0)
X Tutup