Repository: aws-cloudformation/rain
Pull request #24 (2020-06-23 10:44:46)

Cfn commands
*Issue #, if available:*

*Description of changes:*


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.



Patch:
diff --git a/cmd/build.go b/cmd/build.go
index 00ef2515..c8e726bd 100644
--- a/cmd/build.go
+++ b/cmd/build.go
@@ -15,10 +15,16 @@ import (
 var buildListFlag = false
 
 var buildCmd = &cobra.Command{
-	Use:                   "build [<resource type>...]",
-	Short:                 "Create CloudFormation templates",
-	Long:                  "Outputs a CloudFormation template containing the named resource types.",
-	Aliases:               []string{"docs"},
+	Use:   "build [<resource type>...]",
+	Short: "Create CloudFormation templates",
+	Long:  "Outputs a CloudFormation template containing the named resource types.",
+	Args: func(cmd *cobra.Command, args []string) error {
+		if buildListFlag {
+			return nil
+		}
+
+		return cobra.MinimumNArgs(1)(cmd, args)
+	},
 	Annotations:           templateAnnotation,
 	DisableFlagsInUseLine: true,
 	Run: func(cmd *cobra.Command, args []string) {
@@ -54,5 +60,5 @@ var buildCmd = &cobra.Command{
 
 func init() {
 	buildCmd.Flags().BoolVarP(&buildListFlag, "list", "l", false, "List all CloudFormation resource types")
-	Root.AddCommand(buildCmd)
+	Rain.AddCommand(buildCmd)
 }
diff --git a/cmd/cat.go b/cmd/cat.go
index 0aac4758..905eddce 100644
--- a/cmd/cat.go
+++ b/cmd/cat.go
@@ -46,5 +46,5 @@ var catCmd = &cobra.Command{
 func init() {
 	catCmd.Flags().BoolVarP(&transformed, "transformed", "t", false, "Get the template with transformations applied by CloudFormation.")
 	catCmd.Flags().BoolVarP(&unformatted, "unformatted", "u", false, "Output the template in its raw form and do not attempt to format it.")
-	Root.AddCommand(catCmd)
+	Rain.AddCommand(catCmd)
 }
diff --git a/cmd/cfn-format/main.go b/cmd/cfn-format/main.go
new file mode 100644
index 00000000..aa00cc72
--- /dev/null
+++ b/cmd/cfn-format/main.go
@@ -0,0 +1,25 @@
+// Copyright © 2019 NAME HERE <EMAIL ADDRESS>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"github.com/aws-cloudformation/rain/cmd"
+)
+
+func main() {
+	root := cmd.Wrap("cfn-format", cmd.Fmt)
+
+	cmd.Execute(root)
+}
diff --git a/cmd/cfn-skeleton/main.go b/cmd/cfn-skeleton/main.go
new file mode 100644
index 00000000..f4115695
--- /dev/null
+++ b/cmd/cfn-skeleton/main.go
@@ -0,0 +1,25 @@
+// Copyright © 2019 NAME HERE <EMAIL ADDRESS>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"github.com/aws-cloudformation/rain/cmd"
+)
+
+func main() {
+	root := cmd.Wrap("cfn-skeleton", cmd.Build)
+
+	cmd.Execute(root)
+}
diff --git a/cmd/check.go b/cmd/check.go
index 9e393c40..bba9fd86 100644
--- a/cmd/check.go
+++ b/cmd/check.go
@@ -74,5 +74,5 @@ var checkCmd = &cobra.Command{
 }
 
 func init() {
-	Root.AddCommand(checkCmd)
+	Rain.AddCommand(checkCmd)
 }
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 2149c998..b2108cef 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -1,16 +1,6 @@
 package cmd
 
-import (
-	"fmt"
-	"os"
-	"runtime"
-	"strings"
-
-	"github.com/aws-cloudformation/rain/config"
-	"github.com/aws-cloudformation/rain/version"
-	"github.com/spf13/cobra"
-)
-
+// Labels used as annotations on commands to provide grouping
 const (
 	groupAnnotationLabel = "Group"
 	stackGroup           = "Stack commands"
@@ -26,72 +16,8 @@ var (
 	templateAnnotation = map[string]string{groupAnnotationLabel: templateGroup}
 )
 
-// Command is a local type that simply wraps cobra.Command
-type Command struct {
-	cobra.Command
-}
-
-// Root represents the base command when called without any subcommands
-var Root = &cobra.Command{
-	Use:     "rain",
-	Long:    "Rain is what happens when you have a lot of CloudFormation ;)",
-	Version: version.VERSION,
-}
-
-const usageTemplate = `Usage:{{if .Runnable}}
-  {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
-  {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
-
-Aliases:
-  {{.NameAndAliases}}{{end}}{{if .HasExample}}
-
-Examples:
-{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
-
-{{range $group := groups}}{{ $group }}:{{range $c := $.Commands}}{{if $c.IsAvailableCommand}}{{if eq $c.Annotations.Group $group}}
-  {{rpad $c.Name $c.NamePadding }} {{$c.Short}}{{end}}{{end}}{{end}}
-
-{{end}}Other Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}{{if .Annotations.Group}}{{else}}
-  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
-
-Flags:
-{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
+// Fmt is exposed to be available to the cfn-format command
+var Fmt = fmtCmd
 
-Global Flags:
-{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
-
-Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
-  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
-
-Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
-`
-
-func init() {
-	Root.PersistentFlags().BoolVarP(&config.Debug, "debug", "", false, "Output debugging information")
-	Root.PersistentFlags().StringVarP(&config.Profile, "profile", "p", "", "AWS profile name; read from the AWS CLI configuration file")
-	Root.PersistentFlags().StringVarP(&config.Region, "region", "r", "", "AWS region to use")
-
-	// Customise usage
-	Root.Annotations = map[string]string{"Groups": strings.Join(groups, "|")}
-
-	cobra.AddTemplateFunc("groups", func() []string {
-		return groups
-	})
-
-	Root.SetUsageTemplate(usageTemplate)
-
-	// Customise version string
-	Root.SetVersionTemplate(fmt.Sprintf("%s {{.Version}} %s/%s\n",
-		version.NAME,
-		runtime.GOOS,
-		runtime.GOARCH,
-	))
-}
-
-// Execute adds all child commands to the root command and sets flags appropriately.
-// This is called by main.main(). It only needs to happen once to the Root.
-func Execute() {
-	if err := Root.Execute(); err != nil {
-		os.Exit(1)
-	}
-}
+// Build is exposed to be available to the cfn-skeleton command
+var Build = buildCmd
diff --git a/cmd/deploy.go b/cmd/deploy.go
index f6e9ff27..0d052c40 100644
--- a/cmd/deploy.go
+++ b/cmd/deploy.go
@@ -336,7 +336,7 @@ If you don't specify a stack name, rain will use the template filename minus its
 			} else if status == "UPDATE_COMPLETE" {
 				fmt.Println(text.Green("Successfully updated " + stackName))
 			} else {
-				logsCmd.Run(Root, []string{stackName})
+				logsCmd.Run(Rain, []string{stackName})
 				panic(errors.New("Failed deployment: " + stackName))
 			}
 		}
@@ -352,5 +352,5 @@ func init() {
 	deployCmd.Flags().BoolVarP(&forceDeploy, "force", "f", false, "Don't ask questions; just deploy.")
 	deployCmd.Flags().StringSliceVar(&tags, "tags", []string{}, "Add tags to the stack. Use the format key1=value1,key2=value2.")
 	deployCmd.Flags().StringSliceVar(&params, "params", []string{}, "Set parameter values. Use the format key1=value1,key2=value2.")
-	Root.AddCommand(deployCmd)
+	Rain.AddCommand(deployCmd)
 }
diff --git a/cmd/diff.go b/cmd/diff.go
index 84d8864b..4303ed00 100644
--- a/cmd/diff.go
+++ b/cmd/diff.go
@@ -35,5 +35,5 @@ var diffCmd = &cobra.Command{
 
 func init() {
 	diffCmd.Flags().BoolVarP(&longDiff, "long", "l", false, "Include unchanged elements in diff output")
-	Root.AddCommand(diffCmd)
+	Rain.AddCommand(diffCmd)
 }
diff --git a/cmd/fmt.go b/cmd/fmt.go
index b6d8e467..e181513a 100644
--- a/cmd/fmt.go
+++ b/cmd/fmt.go
@@ -76,5 +76,5 @@ func init() {
 	fmtCmd.Flags().BoolVarP(&jsonFlag, "json", "j", false, "Output the template as JSON (default format: YAML).")
 	fmtCmd.Flags().BoolVarP(&verifyFlag, "verify", "v", false, "Check if the input is already correctly formatted and exit.\nThe exit status will be 0 if so and 1 if not.")
 	fmtCmd.Flags().BoolVarP(&writeFlag, "write", "w", false, "Write the output back to the file rather than to stdout.")
-	Root.AddCommand(fmtCmd)
+	Rain.AddCommand(fmtCmd)
 }
diff --git a/cmd/info.go b/cmd/info.go
index 221ff1a1..dc4e52de 100644
--- a/cmd/info.go
+++ b/cmd/info.go
@@ -58,5 +58,5 @@ var infoCmd = &cobra.Command{
 
 func init() {
 	infoCmd.Flags().BoolVarP(&checkCreds, "creds", "c", false, "Include current AWS credentials")
-	Root.AddCommand(infoCmd)
+	Rain.AddCommand(infoCmd)
 }
diff --git a/cmd/logs.go b/cmd/logs.go
index fd326462..ad4780b8 100644
--- a/cmd/logs.go
+++ b/cmd/logs.go
@@ -135,5 +135,5 @@ func init() {
 	logsCmd.Flags().BoolVarP(&timeOrder, "time", "t", false, "Show results in order of time instead of grouped by resource")
 	logsCmd.Flags().BoolVarP(&longFormat, "long", "l", false, "Display full details")
 	logsCmd.Flags().BoolVarP(&allLogs, "all", "a", false, "Include uninteresting logs")
-	Root.AddCommand(logsCmd)
+	Rain.AddCommand(logsCmd)
 }
diff --git a/cmd/ls.go b/cmd/ls.go
index 2d836e80..a295d6bf 100644
--- a/cmd/ls.go
+++ b/cmd/ls.go
@@ -125,5 +125,5 @@ var lsCmd = &cobra.Command{
 func init() {
 	lsCmd.Flags().BoolVarP(&allRegions, "all", "a", false, "List stacks across all regions")
 	lsCmd.Flags().BoolVarP(&showNested, "nested", "n", false, "Show nested stacks (hidden by default)")
-	Root.AddCommand(lsCmd)
+	Rain.AddCommand(lsCmd)
 }
diff --git a/cmd/plugins.go b/cmd/plugins.go
index a7626c62..59dfaf07 100644
--- a/cmd/plugins.go
+++ b/cmd/plugins.go
@@ -29,7 +29,7 @@ func init() {
 				util.RunAttached(bin, "--help")
 			})
 
-			Root.AddCommand(cmd)
+			Rain.AddCommand(cmd)
 		}
 	}
 }
diff --git a/cmd/rain.go b/cmd/rain.go
new file mode 100644
index 00000000..13056c38
--- /dev/null
+++ b/cmd/rain.go
@@ -0,0 +1,60 @@
+package cmd
+
+import (
+	"strings"
+
+	"github.com/aws-cloudformation/rain/config"
+	"github.com/aws-cloudformation/rain/version"
+	"github.com/spf13/cobra"
+)
+
+// Rain represents the base command when called without any subcommands
+var Rain = &cobra.Command{
+	Use:     "rain",
+	Long:    "Rain is what happens when you have a lot of CloudFormation ;)",
+	Version: version.VERSION,
+}
+
+const usageTemplate = `Usage:{{if .Runnable}}
+  {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+  {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
+
+Aliases:
+  {{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Examples:
+{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
+
+{{range $group := groups}}{{ $group }}:{{range $c := $.Commands}}{{if $c.IsAvailableCommand}}{{if eq $c.Annotations.Group $group}}
+  {{rpad $c.Name $c.NamePadding }} {{$c.Short}}{{end}}{{end}}{{end}}
+
+{{end}}Other Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}{{if .Annotations.Group}}{{else}}
+  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
+
+Flags:
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
+
+Global Flags:
+{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
+
+Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
+  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
+
+Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
+`
+
+func init() {
+	Rain.PersistentFlags().StringVarP(&config.Profile, "profile", "p", "", "AWS profile name; read from the AWS CLI configuration file")
+	Rain.PersistentFlags().StringVarP(&config.Region, "region", "r", "", "AWS region to use")
+
+	// Customise usage
+	Rain.Annotations = map[string]string{"Groups": strings.Join(groups, "|")}
+
+	cobra.AddTemplateFunc("groups", func() []string {
+		return groups
+	})
+
+	Rain.SetUsageTemplate(usageTemplate)
+
+	addDefaults(Rain)
+}
diff --git a/cmd/rm.go b/cmd/rm.go
index d6512c17..5dd5b790 100644
--- a/cmd/rm.go
+++ b/cmd/rm.go
@@ -81,5 +81,5 @@ var rmCmd = &cobra.Command{
 func init() {
 	rmCmd.Flags().BoolVarP(&detachRm, "detach", "d", false, "Once removal has started, don't wait around for it to finish.")
 	rmCmd.Flags().BoolVarP(&forceRm, "force", "f", false, "Do not ask; just delete")
-	Root.AddCommand(rmCmd)
+	Rain.AddCommand(rmCmd)
 }
diff --git a/cmd/tree.go b/cmd/tree.go
index 7666dc26..a4132b8c 100644
--- a/cmd/tree.go
+++ b/cmd/tree.go
@@ -124,5 +124,5 @@ var graphCmd = &cobra.Command{
 func init() {
 	graphCmd.Flags().BoolVarP(&allLinks, "all", "a", false, "Display all elements, even those without any dependencies")
 	graphCmd.Flags().BoolVarP(&twoWayTree, "both", "b", false, "For each element, display both its dependencies and its dependents")
-	Root.AddCommand(graphCmd)
+	Rain.AddCommand(graphCmd)
 }
diff --git a/cmd/watch.go b/cmd/watch.go
index d8d2e0bf..4ebb14d1 100644
--- a/cmd/watch.go
+++ b/cmd/watch.go
@@ -49,5 +49,5 @@ var watchCmd = &cobra.Command{
 
 func init() {
 	watchCmd.Flags().BoolVarP(&waitThenWatch, "wait", "w", false, "Wait for changes to begin rather than refusing to watch an unchanging stack")
-	Root.AddCommand(watchCmd)
+	Rain.AddCommand(watchCmd)
 }
diff --git a/cmd/wrap.go b/cmd/wrap.go
new file mode 100644
index 00000000..8f41c2bb
--- /dev/null
+++ b/cmd/wrap.go
@@ -0,0 +1,87 @@
+package cmd
+
+import (
+	"fmt"
+	"os"
+	"runtime"
+	"strings"
+
+	"github.com/aws-cloudformation/rain/config"
+	"github.com/aws-cloudformation/rain/console/spinner"
+	"github.com/aws-cloudformation/rain/console/text"
+	"github.com/aws-cloudformation/rain/version"
+	"github.com/spf13/cobra"
+	"github.com/spf13/pflag"
+)
+
+func addDefaults(c *cobra.Command) {
+	// Don't add "[flags]" to the usage line
+	c.DisableFlagsInUseLine = true
+
+	// Set version string
+	c.Version = version.VERSION
+
+	// Add the debug flag
+	c.PersistentFlags().BoolVarP(&config.Debug, "debug", "", false, "Output debugging information")
+
+	// Customise version string
+	if c.Name() == "rain" {
+		c.SetVersionTemplate(fmt.Sprintf("%s {{.Version}} %s/%s\n",
+			version.NAME,
+			runtime.GOOS,
+			runtime.GOARCH,
+		))
+	} else {
+		c.SetVersionTemplate(fmt.Sprintf("{{.Name}} (%s {{.Version}} %s/%s)\n",
+			version.NAME,
+			runtime.GOOS,
+			runtime.GOARCH,
+		))
+	}
+}
+
+// Wrap creates a new command with the same functionality as src
+// but with a new name and default options added for executables
+// e.g. the --debug flag
+func Wrap(name string, src *cobra.Command) *cobra.Command {
+	use := strings.Split(src.Use, " ")
+	use[0] = name
+
+	// Create the new command
+	out := &cobra.Command{
+		Use:  strings.Join(use, " "),
+		Long: src.Long,
+		Args: src.Args,
+		Run:  src.Run,
+	}
+
+	// Set default options
+	addDefaults(out)
+
+	// Add the flags
+	src.Flags().VisitAll(func(f *pflag.Flag) {
+		out.Flags().AddFlag(f)
+	})
+
+	return out
+}
+
+// Execute wraps a command with error trapping that deals with the debug flag
+func Execute(cmd *cobra.Command) {
+	defer func() {
+		spinner.Stop()
+
+		if r := recover(); r != nil {
+			if config.Debug {
+				panic(r)
+			}
+
+			fmt.Println(text.Red(fmt.Sprint(r)))
+			os.Exit(1)
+		}
+	}()
+
+	if err := cmd.Execute(); err != nil {
+		os.Exit(1)
+	}
+}
diff --git a/docs/generate.go b/docs/generate.go
index 0330397e..4ab6aeb4 100644
--- a/docs/generate.go
+++ b/docs/generate.go
@@ -48,7 +48,7 @@ func identity(s string) string {
 }
 
 func main() {
-	err := doc.GenMarkdownTreeCustom(cmd.Root, "./", emptyStr, identity)
+	err := doc.GenMarkdownTreeCustom(cmd.Rain, "./", emptyStr, identity)
 	if err != nil {
 		panic(err)
 	}
@@ -60,8 +60,8 @@ func main() {
 
 	// Generate usage
 	usage := bytes.Buffer{}
-	cmd.Root.SetOutput(&usage)
-	cmd.Root.Usage()
+	cmd.Rain.SetOutput(&usage)
+	cmd.Rain.Usage()
 
 	// Generate README
 	buf := bytes.Buffer{}
@@ -74,6 +74,6 @@ func main() {
 
 	ioutil.WriteFile("../README.md", buf.Bytes(), 0644)
 
-	cmd.Root.GenBashCompletionFile("bash_completion.sh")
-	cmd.Root.GenZshCompletionFile("zsh_completion.sh")
+	cmd.Rain.GenBashCompletionFile("bash_completion.sh")
+	cmd.Rain.GenZshCompletionFile("zsh_completion.sh")
 }
diff --git a/go.mod b/go.mod
index 214b3dd5..e349ef49 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
 	github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b
 	github.com/sanathkr/yaml v1.0.1-0.20170819201035-0056894fa522
 	github.com/spf13/cobra v0.0.4
+	github.com/spf13/pflag v1.0.3
 	github.com/spf13/viper v1.3.2
 	github.com/stretchr/testify v1.3.0 // indirect
 	golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
diff --git a/main.go b/main.go
index a649dc22..7677fbfb 100644
--- a/main.go
+++ b/main.go
@@ -15,30 +15,9 @@
 package main
 
 import (
-	"fmt"
-	"os"
-
 	"github.com/aws-cloudformation/rain/cmd"
-	"github.com/aws-cloudformation/rain/config"
-	"github.com/aws-cloudformation/rain/console/spinner"
-	"github.com/aws-cloudformation/rain/console/text"
 )
 
 func main() {
-	defer func() {
-		spinner.Stop()
-
-		if r := recover(); r != nil {
-			if config.Debug {
-				panic(r)
-			}
-
-			fmt.Println(text.Red(fmt.Sprint(r)))
-			os.Exit(1)
-		}
-
-		os.Exit(0)
-	}()
-
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 }


Test patch:
diff --git a/cmd/build_test.go b/cmd/build_test.go
index aec0ff79..6fe7e336 100644
--- a/cmd/build_test.go
+++ b/cmd/build_test.go
@@ -13,7 +13,7 @@ func Example_build_bucket() {
 		"AWS::S3::Bucket",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// AWSTemplateFormatVersion: "2010-09-09"
 	//
diff --git a/cmd/cat_test.go b/cmd/cat_test.go
index 1f675c46..9f5e1521 100644
--- a/cmd/cat_test.go
+++ b/cmd/cat_test.go
@@ -13,7 +13,7 @@ func Example_cat_help() {
 		"cat",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Downloads the template used to deploy <stack> and prints it to stdout.
 	//
diff --git a/cmd/check_test.go b/cmd/check_test.go
index d19de6d9..ce9a7c59 100644
--- a/cmd/check_test.go
+++ b/cmd/check_test.go
@@ -13,7 +13,7 @@ func Example_check_help() {
 		"check",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Reads the specified CloudFormation template and validates it against the current CloudFormation specification.
 	//
diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go
index 6bd89b5d..1013c982 100644
--- a/cmd/cmd_test.go
+++ b/cmd/cmd_test.go
@@ -18,7 +18,7 @@ func Example_rainhelp() {
 		os.Args[0],
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Rain is what happens when you have a lot of CloudFormation ;)
 	//
diff --git a/cmd/deploy_test.go b/cmd/deploy_test.go
index da24a87b..e69366a6 100644
--- a/cmd/deploy_test.go
+++ b/cmd/deploy_test.go
@@ -13,7 +13,7 @@ func Example_deploy_help() {
 		"deploy",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Creates or updates a CloudFormation stack named <stack> from the template file <template>.
 	// If you don't specify a stack name, rain will use the template filename minus its extension.
diff --git a/cmd/diff_test.go b/cmd/diff_test.go
index dbd0734c..2d98ba96 100644
--- a/cmd/diff_test.go
+++ b/cmd/diff_test.go
@@ -14,7 +14,7 @@ func Example_diff() {
 		"../examples/failure.template",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// (>) Description: This template fails
 	// (-) Parameters: {...}
diff --git a/cmd/fmt_test.go b/cmd/fmt_test.go
index 238b5bda..d6a9913a 100644
--- a/cmd/fmt_test.go
+++ b/cmd/fmt_test.go
@@ -13,7 +13,7 @@ func Example_fmt_help() {
 		"fmt",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Reads the named template and outputs a nicely formatted copy.
 	//
diff --git a/cmd/info_test.go b/cmd/info_test.go
index 67136e85..0edd2ad4 100644
--- a/cmd/info_test.go
+++ b/cmd/info_test.go
@@ -13,7 +13,7 @@ func Example_info_help() {
 		"info",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Display the AWS account and region that you're configured to use.
 	//
diff --git a/cmd/logs_test.go b/cmd/logs_test.go
index 315d00ab..d0fae64b 100644
--- a/cmd/logs_test.go
+++ b/cmd/logs_test.go
@@ -13,7 +13,7 @@ func Example_logs_help() {
 		"logs",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Shows a nicely-formatted list of the event log for the named stack, optionally limiting the results to a single resource.
 	//
diff --git a/cmd/ls_test.go b/cmd/ls_test.go
index f259a8cb..6f5812be 100644
--- a/cmd/ls_test.go
+++ b/cmd/ls_test.go
@@ -13,7 +13,7 @@ func Example_ls_help() {
 		"ls",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Displays a list of all running stacks or the contents of <stack> if provided.
 	//
diff --git a/cmd/rm_test.go b/cmd/rm_test.go
index a617b888..fac1c8d9 100644
--- a/cmd/rm_test.go
+++ b/cmd/rm_test.go
@@ -13,7 +13,7 @@ func Example_rm_help() {
 		"rm",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Deletes the CloudFormation stack named <stack> and waits for the action to complete.
 	//
diff --git a/cmd/tree_test.go b/cmd/tree_test.go
index 03ea3ceb..5df8dd53 100644
--- a/cmd/tree_test.go
+++ b/cmd/tree_test.go
@@ -13,7 +13,7 @@ func Example_tree() {
 		"../examples/success.template",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Resources:
 	//   Bucket1:
diff --git a/cmd/watch_test.go b/cmd/watch_test.go
index 901e53a1..5ec37499 100644
--- a/cmd/watch_test.go
+++ b/cmd/watch_test.go
@@ -13,7 +13,7 @@ func Example_watch_help() {
 		"watch",
 	}
 
-	cmd.Execute()
+	cmd.Execute(cmd.Rain)
 	// Output:
 	// Repeatedly displays the status of a CloudFormation stack. Useful for watching the progress of a deployment started from outside of Rain.
 	//