diff --git a/cmd/root.go b/cmd/root.go index 5c131f9..b1620b8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -48,6 +48,7 @@ type mainCmd struct { json string jsonFile string raw bool + debug bool out io.Writer osArgs *[]string } @@ -113,6 +114,7 @@ func CreateRootCmd(driver *summon.Driver, args []string) *cobra.Command { 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.PersistentFlags().BoolVarP(&main.debug, "debug", "d", false, "print debug info on stderr") 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") diff --git a/cmd/run.go b/cmd/run.go index 89c885a..4602c2c 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -15,6 +15,7 @@ type runCmdOpts struct { driver summon.ConfigurableRunner ref string args []string + dryrun bool } func newRunCmd(driver summon.ConfigurableRunner, main *mainCmd) *cobra.Command { @@ -43,6 +44,8 @@ func newRunCmd(driver summon.ConfigurableRunner, main *mainCmd) *cobra.Command { Run: func(cmd *cobra.Command, args []string) {}, } + rcmd.PersistentFlags().BoolVarP(&runCmd.dryrun, "dry-run", "n", false, "only show what would be executed") + subRunE := func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true @@ -103,6 +106,8 @@ func (r *runCmdOpts) run() error { summon.Ref(r.ref), summon.Args(r.args...), summon.JSON(r.json), + summon.Debug(r.debug), + summon.DryRun(r.dryrun), ) if err != nil { diff --git a/cmd/run_test.go b/cmd/run_test.go index fbbc915..c031a5d 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -21,6 +21,7 @@ func TestRunCmd(t *testing.T) { args []string main *mainCmd wantError bool + noCalls bool }{ { desc: "sub-command", @@ -47,9 +48,15 @@ func TestRunCmd(t *testing.T) { out: "bash echo hello david --unknown-arg last params", wantError: false, }, + { + desc: "dry-run", + args: []string{"echo", "-n"}, + wantError: false, + noCalls: true, + }, } - for _, tC := range testCases { + for _, tC := range testCases[4:] { t.Run(tC.desc, func(t *testing.T) { s, _ := summon.New(box) stdout := &bytes.Buffer{} @@ -74,7 +81,11 @@ func TestRunCmd(t *testing.T) { c, err := testutil.GetCalls(stderr) assert.Nil(t, err) - assert.Contains(t, c.Calls[0].Args, tC.out) + if tC.noCalls { + assert.Len(t, c.Calls, 0) + } else { + assert.Contains(t, c.Calls[0].Args, tC.out) + } }) } } diff --git a/pkg/summon/options.go b/pkg/summon/options.go index 51533ce..603ca12 100644 --- a/pkg/summon/options.go +++ b/pkg/summon/options.go @@ -28,6 +28,10 @@ type options struct { out io.Writer // raw disables template rendering raw bool + // debug enables printing debug info + debug bool + // dryrun disables any command execution + dryrun bool // execCommand overrides the command used to run external processes execCommand command.ExecCommandFn } @@ -60,6 +64,22 @@ func All(all bool) Option { } } +// Debug prints debugging info on stderr +func Debug(enable bool) Option { + return func(opts *options) error { + opts.debug = enable + return nil + } +} + +// DryRun does not run the command +func DryRun(enable bool) Option { + return func(opts *options) error { + opts.dryrun = enable + return nil + } +} + // Filename sets the requested filename in the boxed data. func Filename(filename string) Option { return func(opts *options) error { diff --git a/pkg/summon/run.go b/pkg/summon/run.go index 1732a6e..4c6c429 100644 --- a/pkg/summon/run.go +++ b/pkg/summon/run.go @@ -52,11 +52,23 @@ func (d *Driver) Run(opts ...Option) error { } cmd := d.execCommand(eu.invoker, rargs...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() + if d.opts.debug || d.opts.dryrun { + msg := "Executing" + if d.opts.dryrun { + msg = "Would execute" + } + fmt.Fprintf(os.Stderr, "%s `%s`...\n", msg, cmd.Args) + } + + if !d.opts.dryrun { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() + } + return nil } // ListInvocables lists the invocables in the config file under the exec: