diff --git a/cmd/skaffold/app/cmd/build.go b/cmd/skaffold/app/cmd/build.go index 73d5630d780..d6204fa3f26 100644 --- a/cmd/skaffold/app/cmd/build.go +++ b/cmd/skaffold/app/cmd/build.go @@ -18,14 +18,21 @@ package cmd import ( "context" - "fmt" "io" + "io/ioutil" + "github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/flags" + "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build" "github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner" "github.com/pkg/errors" "github.com/spf13/cobra" ) +var ( + quietFlag bool + buildFormatFlag = flags.NewTemplateFlag("{{range .Builds}}{{.ImageName}} -> {{.Tag}}\n{{end}}", BuildOutput{}) +) + // NewCmdBuild describes the CLI command to build artifacts. func NewCmdBuild(out io.Writer) *cobra.Command { cmd := &cobra.Command{ @@ -33,14 +40,21 @@ func NewCmdBuild(out io.Writer) *cobra.Command { Short: "Builds the artifacts", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return build(out, filename) + return runBuild(out, filename) }, } AddRunDevFlags(cmd) + cmd.Flags().BoolVarP(&quietFlag, "quiet", "q", false, "Suppress the build output and print image built on success") + cmd.Flags().VarP(buildFormatFlag, "output", "o", buildFormatFlag.Usage()) return cmd } -func build(out io.Writer, filename string) error { +// BuildOutput is the output of `skaffold build`. +type BuildOutput struct { + Builds []build.Build +} + +func runBuild(out io.Writer, filename string) error { ctx := context.Background() config, err := readConfiguration(filename) @@ -53,14 +67,19 @@ func build(out io.Writer, filename string) error { return errors.Wrap(err, "creating runner") } - bRes, err := runner.Build(ctx, out, runner.Tagger, config.Build.Artifacts) + buildOut := out + if quietFlag { + buildOut = ioutil.Discard + } + + bRes, err := runner.Build(ctx, buildOut, runner.Tagger, config.Build.Artifacts) if err != nil { return errors.Wrap(err, "build step") } - for _, build := range bRes { - fmt.Fprintln(out, build.ImageName, "->", build.Tag) + cmdOut := BuildOutput{Builds: bRes} + if err := buildFormatFlag.Template().Execute(out, cmdOut); err != nil { + return errors.Wrap(err, "executing template") } - - return err + return nil } diff --git a/cmd/skaffold/app/flags/template.go b/cmd/skaffold/app/flags/template.go index 2708752f9b3..f26329a447a 100644 --- a/cmd/skaffold/app/flags/template.go +++ b/cmd/skaffold/app/flags/template.go @@ -17,8 +17,11 @@ limitations under the License. package flags import ( + "bytes" + "encoding/json" "fmt" "reflect" + "strings" "text/template" "github.com/pkg/errors" @@ -45,7 +48,7 @@ func (t *TemplateFlag) Usage() string { } func (t *TemplateFlag) Set(value string) error { - tmpl, err := template.New("flagtemplate").Parse(value) + tmpl, err := parseTemplate(value) if err != nil { return errors.Wrap(err, "setting template flag") } @@ -64,8 +67,26 @@ func (t *TemplateFlag) Template() *template.Template { func NewTemplateFlag(value string, context interface{}) *TemplateFlag { return &TemplateFlag{ - template: template.Must(template.New("flagtemplate").Parse(value)), + template: template.Must(parseTemplate(value)), rawTemplate: value, context: context, } } + +func parseTemplate(value string) (*template.Template, error) { + var funcs = template.FuncMap{ + "json": func(v interface{}) string { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + enc.Encode(v) + return strings.TrimSpace(buf.String()) + }, + "join": strings.Join, + "title": strings.Title, + "lower": strings.ToLower, + "upper": strings.ToUpper, + } + + return template.New("flagtemplate").Funcs(funcs).Parse(value) +}