github.com/wuhuizuo/gomplate@v3.5.0+incompatible/cmd/gomplate/main.go (about) 1 /* 2 The gomplate command 3 4 */ 5 package main 6 7 import ( 8 "errors" 9 "fmt" 10 "os" 11 "os/exec" 12 "os/signal" 13 14 "github.com/hairyhenderson/gomplate" 15 "github.com/hairyhenderson/gomplate/env" 16 "github.com/hairyhenderson/gomplate/version" 17 "github.com/spf13/cobra" 18 ) 19 20 var ( 21 printVer bool 22 verbose bool 23 opts gomplate.Config 24 ) 25 26 // nolint: gocyclo 27 func validateOpts(cmd *cobra.Command, args []string) error { 28 if cmd.Flag("in").Changed && cmd.Flag("file").Changed { 29 return errors.New("--in and --file may not be used together") 30 } 31 32 if len(opts.InputFiles) != len(opts.OutputFiles) { 33 return fmt.Errorf("must provide same number of --out (%d) as --file (%d) options", len(opts.OutputFiles), len(opts.InputFiles)) 34 } 35 36 if cmd.Flag("input-dir").Changed && (cmd.Flag("in").Changed || cmd.Flag("file").Changed) { 37 return errors.New("--input-dir can not be used together with --in or --file") 38 } 39 40 if cmd.Flag("output-dir").Changed { 41 if cmd.Flag("out").Changed { 42 return errors.New("--output-dir can not be used together with --out") 43 } 44 if !cmd.Flag("input-dir").Changed { 45 return errors.New("--input-dir must be set when --output-dir is set") 46 } 47 } 48 49 if cmd.Flag("output-map").Changed { 50 if cmd.Flag("out").Changed || cmd.Flag("output-dir").Changed { 51 return errors.New("--output-map can not be used together with --out or --output-dir") 52 } 53 if !cmd.Flag("input-dir").Changed { 54 return errors.New("--input-dir must be set when --output-map is set") 55 } 56 } 57 return nil 58 } 59 60 func printVersion(name string) { 61 fmt.Printf("%s version %s\n", name, version.Version) 62 } 63 64 // postRunExec - if templating succeeds, the command following a '--' will be executed 65 func postRunExec(cmd *cobra.Command, args []string) error { 66 if len(args) > 0 { 67 name := args[0] 68 args = args[1:] 69 // nolint: gosec 70 c := exec.Command(name, args...) 71 c.Stdin = os.Stdin 72 c.Stderr = os.Stderr 73 c.Stdout = os.Stdout 74 75 // make sure all signals are propagated 76 sigs := make(chan os.Signal, 1) 77 signal.Notify(sigs) 78 go func() { 79 // Pass signals to the sub-process 80 sig := <-sigs 81 if c.Process != nil { 82 // nolint: gosec 83 _ = c.Process.Signal(sig) 84 } 85 }() 86 87 return c.Run() 88 } 89 return nil 90 } 91 92 // optionalExecArgs - implements cobra.PositionalArgs. Allows extra args following 93 // a '--', but not otherwise. 94 func optionalExecArgs(cmd *cobra.Command, args []string) error { 95 if cmd.ArgsLenAtDash() == 0 { 96 return nil 97 } 98 return cobra.NoArgs(cmd, args) 99 } 100 101 func newGomplateCmd() *cobra.Command { 102 rootCmd := &cobra.Command{ 103 Use: "gomplate", 104 Short: "Process text files with Go templates", 105 PreRunE: validateOpts, 106 RunE: func(cmd *cobra.Command, args []string) error { 107 if printVer { 108 printVersion(cmd.Name()) 109 return nil 110 } 111 if verbose { 112 // nolint: errcheck 113 fmt.Fprintf(os.Stderr, "%s version %s, build %s (%v)\nconfig is:\n%s\n\n", 114 cmd.Name(), version.Version, version.GitCommit, version.BuildDate, 115 &opts) 116 } 117 118 err := gomplate.RunTemplates(&opts) 119 cmd.SilenceErrors = true 120 cmd.SilenceUsage = true 121 if verbose { 122 // nolint: errcheck 123 fmt.Fprintf(os.Stderr, "rendered %d template(s) with %d error(s) in %v\n", 124 gomplate.Metrics.TemplatesProcessed, gomplate.Metrics.Errors, gomplate.Metrics.TotalRenderDuration) 125 } 126 return err 127 }, 128 PostRunE: postRunExec, 129 Args: optionalExecArgs, 130 } 131 return rootCmd 132 } 133 134 func initFlags(command *cobra.Command) { 135 command.Flags().SortFlags = false 136 137 command.Flags().StringArrayVarP(&opts.DataSources, "datasource", "d", nil, "`datasource` in alias=URL form. Specify multiple times to add multiple sources.") 138 command.Flags().StringArrayVarP(&opts.DataSourceHeaders, "datasource-header", "H", nil, "HTTP `header` field in 'alias=Name: value' form to be provided on HTTP-based data sources. Multiples can be set.") 139 140 command.Flags().StringArrayVarP(&opts.Contexts, "context", "c", nil, "pre-load a `datasource` into the context, in alias=URL form. Use the special alias `.` to set the root context.") 141 142 command.Flags().StringArrayVarP(&opts.InputFiles, "file", "f", []string{"-"}, "Template `file` to process. Omit to use standard input, or use --in or --input-dir") 143 command.Flags().StringVarP(&opts.Input, "in", "i", "", "Template `string` to process (alternative to --file and --input-dir)") 144 command.Flags().StringVar(&opts.InputDir, "input-dir", "", "`directory` which is examined recursively for templates (alternative to --file and --in)") 145 146 command.Flags().StringArrayVar(&opts.ExcludeGlob, "exclude", []string{}, "glob of files to not parse") 147 148 command.Flags().StringArrayVarP(&opts.OutputFiles, "out", "o", []string{"-"}, "output `file` name. Omit to use standard output.") 149 command.Flags().StringArrayVarP(&opts.Templates, "template", "t", []string{}, "Additional template file(s)") 150 command.Flags().StringVar(&opts.OutputDir, "output-dir", ".", "`directory` to store the processed templates. Only used for --input-dir") 151 command.Flags().StringVar(&opts.OutputMap, "output-map", "", "Template `string` to map the input file to an output path") 152 command.Flags().StringVar(&opts.OutMode, "chmod", "", "set the mode for output file(s). Omit to inherit from input file(s)") 153 154 ldDefault := env.Getenv("GOMPLATE_LEFT_DELIM", "{{") 155 rdDefault := env.Getenv("GOMPLATE_RIGHT_DELIM", "}}") 156 command.Flags().StringVar(&opts.LDelim, "left-delim", ldDefault, "override the default left-`delimiter` [$GOMPLATE_LEFT_DELIM]") 157 command.Flags().StringVar(&opts.RDelim, "right-delim", rdDefault, "override the default right-`delimiter` [$GOMPLATE_RIGHT_DELIM]") 158 159 command.Flags().BoolVarP(&verbose, "verbose", "V", false, "output extra information about what gomplate is doing") 160 161 command.Flags().BoolVarP(&printVer, "version", "v", false, "print the version") 162 } 163 164 func main() { 165 command := newGomplateCmd() 166 initFlags(command) 167 if err := command.Execute(); err != nil { 168 // nolint: errcheck 169 fmt.Fprintln(os.Stderr, err) 170 os.Exit(1) 171 } 172 }