add experimental powershell support for shell aliases

This commit is contained in:
nate smith 2020-07-02 20:12:46 -05:00 committed by vilmibm
parent 3a9167cfe4
commit c3a5384895
3 changed files with 121 additions and 15 deletions

View file

@ -40,9 +40,10 @@ var aliasSetCmd = &cobra.Command{
includes positional placeholders such as '$1', '$2', etc., any extra arguments
that follow the invocation of an alias will be inserted appropriately.
If '--shell' is specified, the alias will be run through a shell interpreter (sh). This allows you
to compose commands with "|" or redirect output with ">". Note that extra arguments are not passed
to shell-interpreted aliases; only placeholders ("$1", "$2", etc) are supported.
If '--shell' is specified, the alias will be run through a shell interpreter (sh or pwsh). This allows you
to compose commands with "|" or redirect output. Note that extra arguments are not passed to
shell-interpreted aliases; only placeholders ("$1", "$2" on *nix, "$args" in powershell) are
supported.
Quotes must always be used when defining a command as in the examples.`),
Example: heredoc.Doc(`
@ -56,9 +57,15 @@ var aliasSetCmd = &cobra.Command{
$ gh epicsBy vilmibm
#=> gh issue list --author="vilmibm" --label="epic"
# On macOS and Linux:
$ gh alias set --shell igrep 'gh issue list --label="$1" | grep $2'
$ gh igrep epic foo
#=> gh issue list --label="epic" | grep "foo"`),
#=> gh issue list --label="epic" | grep "foo"
# On Windows (Powershell):
$ gh alias set --shell igrep 'gh issue list --label=$args[0] | Select-String -Pattern $args[1]
$ gh igrep epic foo
#=> gh issue list --label=epic | Select-String -Pattern foo`),
Args: cobra.ExactArgs(2),
RunE: aliasSet,
}

View file

@ -2,7 +2,7 @@ package command
import (
"bytes"
"fmt"
"runtime"
"strings"
"testing"
@ -177,12 +177,39 @@ aliases:
}
func TestExpandAlias_external(t *testing.T) {
func TestExpandAlias_shell_nix(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on windows")
}
cfg := `---
aliases:
ig: '!gh issue list | grep cool'
`
initBlankContext(cfg, "OWNER/REPO", "trunk")
cs, teardown := test.InitCmdStubber()
defer teardown()
cs.Stub("")
_, err := ExpandAlias([]string{"gh", "ig"})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
assert.Equal(t, 1, len(cs.Calls))
expected := []string{"sh", "-c", "gh issue list | grep cool"}
assert.Equal(t, expected, cs.Calls[0].Args)
}
func TestExpandAlias_shell_nix_extra_args(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on windows")
}
cfg := `---
aliases:
co: pr checkout
il: issue list --author="$1" --label="$2"
ia: issue list --author="$1" --assignee="$1"
ig: '!gh issue list --label=$1 | grep'
`
initBlankContext(cfg, "OWNER/REPO", "trunk")
@ -199,13 +226,66 @@ aliases:
assert.Equal(t, 1, len(cs.Calls))
fmt.Printf("DEBUG %#v\n", cs.Calls[0])
expected := []string{"sh", "-c", "gh issue list --label=$1 | grep", "--", "bug", "foo"}
assert.Equal(t, expected, cs.Calls[0].Args)
}
func TestExpandAlias_shell_windows(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("skipping test on non-windows")
}
cfg := `---
aliases:
ig: '!gh issue list | select-string -Pattern cool'
`
initBlankContext(cfg, "OWNER/REPO", "trunk")
cs, teardown := test.InitCmdStubber()
defer teardown()
cs.Stub("")
_, err := ExpandAlias([]string{"gh", "ig"})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
assert.Equal(t, 1, len(cs.Calls))
expected := []string{"pwsh", "-Command", "Invoke-Command -ScriptBlock { gh issue list | select-string -Pattern cool } "}
assert.Equal(t, expected, cs.Calls[0].Args)
}
func TestExpandAlias_shell_windows_extra_args(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("skipping test on non-windows")
}
cfg := `---
aliases:
co: pr checkout
ig: '!gh issue list --label=$args[0] | select-string -Pattern $args[1]'
`
initBlankContext(cfg, "OWNER/REPO", "trunk")
cs, teardown := test.InitCmdStubber()
defer teardown()
cs.Stub("")
_, err := ExpandAlias([]string{"gh", "ig", "bug", "foo"})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
assert.Equal(t, 1, len(cs.Calls))
expected := []string{"pwsh", "-Command", "Invoke-Command -ScriptBlock { gh issue list --label=$args[0] | select-string -Pattern $args[1] } -ArgumentList @('bug','foo')"}
assert.Equal(t, expected, cs.Calls[0].Args)
}
func TestExpandAlias(t *testing.T) {
cfg := `---
aliases:

View file

@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"regexp"
"runtime"
"runtime/debug"
"strings"
@ -385,11 +386,29 @@ func ExpandAlias(args []string) ([]string, error) {
if ok {
if strings.HasPrefix(expansion, "!") {
shellArgs := []string{"-c", expansion[1:]}
if len(args[2:]) > 0 {
shellArgs = append(shellArgs, "--")
shellArgs = append(shellArgs, args[2:]...)
shellCmd := "sh"
if runtime.GOOS == "windows" {
shellCmd = "pwsh"
argList := ""
if len(args[2:]) > 0 {
argList = " -ArgumentList @("
for i, arg := range args[2:] {
argList += fmt.Sprintf("'%s'", arg)
if i < len(args[2:])-1 {
argList += ","
}
}
argList += ")"
}
invoke := fmt.Sprintf("Invoke-Command -ScriptBlock { %s } %s", expansion[1:], argList)
shellArgs = []string{"-Command", invoke}
} else {
if len(args[2:]) > 0 {
shellArgs = append(shellArgs, "--")
shellArgs = append(shellArgs, args[2:]...)
}
}
externalCmd := exec.Command("sh", shellArgs...)
externalCmd := exec.Command(shellCmd, shellArgs...)
externalCmd.Stderr = os.Stderr
externalCmd.Stdout = os.Stdout
externalCmd.Stdin = os.Stdin