Skip to content

Commit

Permalink
Merge pull request #56 from davidovich/fix-json-params-in-run
Browse files Browse the repository at this point in the history
fix json configuration on command-line
  • Loading branch information
davidovich authored Aug 21, 2020
2 parents 1547b36 + 85a884c commit 82a5afc
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 19 deletions.
7 changes: 4 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type mainCmd struct {
jsonFile string
raw bool
out io.Writer
osArgs *[]string
}

// CreateRootCmd creates the root command
Expand Down Expand Up @@ -110,15 +111,15 @@ func CreateRootCmd(driver *summon.Driver, args []string) *cobra.Command {
},
}

rootCmd.Flags().StringVar(&main.json, "json", "", "json to use to render template")
rootCmd.Flags().StringVar(&main.jsonFile, "json-file", "", "json file to use to render template, with '-' for stdin")
rootCmd.PersistentFlags().StringVar(&main.json, "json", "", "json to use to render template")
rootCmd.PersistentFlags().StringVar(&main.jsonFile, "json-file", "", "json file to use to render template, with '-' for stdin")
rootCmd.Flags().BoolVarP(&main.copyAll, "all", "a", false, "restitute all data")
rootCmd.Flags().BoolVar(&main.raw, "raw", false, "output without any template rendering")
rootCmd.Flags().StringVarP(&main.dest, "out", "o", config.OutputDir, "destination directory, or '-' for stdout")
rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "output data version info and exit")

rootCmd.AddCommand(newListCmd(driver))
rootCmd.AddCommand(newRunCmd(driver))
rootCmd.AddCommand(newRunCmd(driver, main))
rootCmd.AddCommand(newCompletionCmd(driver))

return rootCmd
Expand Down
57 changes: 51 additions & 6 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,29 @@ package cmd
import (
"fmt"
"os"
"strings"

"github.com/davidovich/summon/pkg/summon"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

type runCmdOpts struct {
*mainCmd
driver summon.ConfigurableRunner
ref string
args []string
}

func newRunCmd(driver summon.ConfigurableRunner) *cobra.Command {
func newRunCmd(driver summon.ConfigurableRunner, main *mainCmd) *cobra.Command {
runCmd := &runCmdOpts{
driver: driver,
mainCmd: main,
driver: driver,
}

osArgs := os.Args
if main.osArgs != nil {
osArgs = *main.osArgs
}

invocables := driver.ListInvocables()
Expand All @@ -38,12 +47,14 @@ func newRunCmd(driver summon.ConfigurableRunner) *cobra.Command {
cmd.SilenceUsage = true

runCmd.ref = cmd.Name()
// pass all Args down to the referenced executable
// calculate the extra args to pass to the referenced executable
// this is due to a limitation in spf13/cobra which eats
// all unknown args or flags making it hard to wrap other commands.
// We are lucky, we know the structure, just pass all args.
// We are lucky, we know the prefix order of params,
// extract args after the run command [summon run handle]
// see https://github.com/spf13/pflag/pull/160
runCmd.args = os.Args[3:] // 3 is [summon, run, handle]
// and https://github.com/spf13/cobra/issues/739
runCmd.args = extractUnknownArgs(cmd.Flags(), osArgs[3:])
return runCmd.run()
}
for _, i := range invocables {
Expand All @@ -58,11 +69,45 @@ func newRunCmd(driver summon.ConfigurableRunner) *cobra.Command {
return rcmd
}

func extractUnknownArgs(flags *pflag.FlagSet, args []string) []string {
unknownArgs := []string{}

for i := 0; i < len(args); i++ {
a := args[i]
var f *pflag.Flag
if a[0] == '-' {
if a[1] == '-' {
f = flags.Lookup(strings.SplitN(a[2:], "=", 2)[0])
} else {
for _, s := range a[1:] {
f = flags.ShorthandLookup(string(s))
if f == nil {
break
}
}
}
}
if f != nil {
if f.NoOptDefVal == "" && i+1 < len(args) && f.Value.String() == args[i+1] {
i++
}
continue
}
unknownArgs = append(unknownArgs, a)
}
return unknownArgs
}

func (r *runCmdOpts) run() error {
r.driver.Configure(
err := r.driver.Configure(
summon.Ref(r.ref),
summon.Args(r.args...),
summon.JSON(r.json),
)

if err != nil {
return err
}

return r.driver.Run()
}
47 changes: 39 additions & 8 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,45 @@ import (
"github.com/davidovich/summon/internal/testutil"
"github.com/davidovich/summon/pkg/summon"
"github.com/gobuffalo/packr/v2"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
)

func TestRunCmd(t *testing.T) {
box := packr.New("test box", "testdata")

testCases := []struct {
desc string
out string
args []string
desc string
out string
args []string
main *mainCmd
wantError bool
}{
{
desc: "sub-command",
args: []string{"echo"},
out: "bash echo hello",
out: "bash echo hello",
},
{
desc: "no-sub-command",
desc: "no-sub-command",
wantError: true,
},
{
desc: "invalid-sub-command",
args: []string{"ec"},
desc: "invalid-sub-command",
args: []string{"ec"},
wantError: true,
},
{
desc: "sub-param-passed",
args: []string{"echo", "--unknown-arg", "last", "params"},
main: &mainCmd{
json: "{\"Name\": \"david\"}",
},
// this relies on the v0.10.0 version of templated exec
// see the echo command in testdata/summon.config.yaml
out: "bash echo hello david --unknown-arg last params",
wantError: false,
},
}

for _, tC := range testCases {
Expand All @@ -45,7 +58,12 @@ func TestRunCmd(t *testing.T) {

s.Configure(summon.ExecCmd(execCommand))

cmd := newRunCmd(s)
if tC.main == nil {
tC.main = &mainCmd{}
}
injectOsArgs := append([]string{"summon", "run"}, tC.args...)
tC.main.osArgs = &injectOsArgs
cmd := newRunCmd(s, tC.main)
cmd.SetArgs(tC.args)

err := cmd.Execute()
Expand All @@ -70,3 +88,16 @@ func TestSummonRunHelper(t *testing.T) {
testutil.WriteCall(call)
}
}

func TestExtractUnknownArgs(t *testing.T) {
fset := pflag.NewFlagSet("test", pflag.ContinueOnError)

json := ""
fset.StringVarP(&json, "json", "j", "{}", "")

unknown := extractUnknownArgs(fset, []string{"--json", "{}", "--unknown"})
assert.Equal(t, []string{"--unknown"}, unknown)

unknownShort := extractUnknownArgs(fset, []string{"-j", "--unknown"})
assert.Equal(t, []string{"--unknown"}, unknownShort)
}
2 changes: 1 addition & 1 deletion cmd/testdata/summon.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ aliases:
outputdir: "overridden_dir"
exec:
bash:
echo: echo hello
echo: echo hello {{ .Name -}}
hello-bash: hello.sh
gobin -run:
gobin: github.com/myitcv/gobin@v0.0.8
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ module github.com/davidovich/summon
require (
github.com/DiSiqueira/GoTree v1.0.1-0.20190529205929-3e23dcd4532b
github.com/Masterminds/sprig/v3 v3.1.0
github.com/alessio/shellescape v1.2.2 // indirect
github.com/gobuffalo/packr/v2 v2.5.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/lithammer/dedent v1.1.0
github.com/spf13/afero v1.2.1
github.com/spf13/cobra v0.0.5
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.5.1
golang.org/x/sys v0.0.0-20200819091447-39769834ee22 // indirect
gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61
Expand Down
Loading

0 comments on commit 82a5afc

Please sign in to comment.