diff --git a/cmd/build.go b/cmd/build.go index 6b6fdc01..0a429f30 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -83,6 +83,8 @@ Build packages specifying multiple definition trees: LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) + LuetCfg.Viper.BindPFlag("general.show_build_output", cmd.Flags().Lookup("live-output")) + }, Run: func(cmd *cobra.Command, args []string) { @@ -155,6 +157,8 @@ Build packages specifying multiple definition trees: LuetCfg.GetSolverOptions().Discount = float32(discount) LuetCfg.GetSolverOptions().MaxAttempts = attempts + LuetCfg.GetGeneral().ShowBuildOutput = LuetCfg.Viper.GetBool("general.show_build_output") + Debug("Solver", LuetCfg.GetSolverOptions().CompactString()) opts := compiler.NewDefaultCompilerOptions() @@ -317,6 +321,7 @@ func init() { buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") buildCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)") + buildCmd.Flags().Bool("live-output", LuetCfg.GetGeneral().ShowBuildOutput, "Enable live output of the build phase.") buildCmd.Flags().Bool("pretend", false, "Just print what packages will be compiled") diff --git a/pkg/compiler/backend/common.go b/pkg/compiler/backend/common.go index d68de4d9..c1d107c8 100644 --- a/pkg/compiler/backend/common.go +++ b/pkg/compiler/backend/common.go @@ -16,8 +16,15 @@ package backend import ( - "github.com/google/go-containerregistry/pkg/crane" + "fmt" + "os/exec" + "github.com/mudler/luet/pkg/compiler" + "github.com/mudler/luet/pkg/config" + . "github.com/mudler/luet/pkg/logger" + + "github.com/google/go-containerregistry/pkg/crane" + "github.com/pkg/errors" ) const ( @@ -41,3 +48,45 @@ func NewBackend(s string) compiler.CompilerBackend { } return compilerBackend } + +func runCommand(cmd *exec.Cmd) (string, error) { + ans := "" + writer := NewBackendWriter(!config.LuetCfg.GetGeneral().ShowBuildOutput) + + if config.LuetCfg.GetGeneral().ShowBuildOutput { + // We have realtime output from command. Quiet spinner. + SpinnerStop() + } + + cmd.Stdout = writer + cmd.Stderr = writer + + err := cmd.Start() + if err != nil { + return "", errors.Wrap(err, "Failed starting build") + } + + err = cmd.Wait() + if err != nil { + return "", errors.Wrap(err, "Failed waiting for building command") + } + + res := cmd.ProcessState.ExitCode() + if res != 0 { + errMsg := fmt.Sprintf("Failed building image (exiting with %d)", res) + if !config.LuetCfg.GetGeneral().ShowBuildOutput { + errMsg = fmt.Sprintf("Failed building image (exiting with %d): %s", + res, writer.GetCombinedOutput()) + } + + return "", errors.Wrap(err, errMsg) + } + + if config.LuetCfg.GetGeneral().ShowBuildOutput { + Spinner(22) + } else { + ans = writer.GetCombinedOutput() + } + + return ans, nil +} diff --git a/pkg/compiler/backend/simpledocker.go b/pkg/compiler/backend/simpledocker.go index 9cec7136..dfd3cd3c 100644 --- a/pkg/compiler/backend/simpledocker.go +++ b/pkg/compiler/backend/simpledocker.go @@ -56,10 +56,11 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error { Info(":whale2: Building image " + name) cmd := exec.Command("docker", buildarg...) cmd.Dir = path - out, err := cmd.CombinedOutput() + _, err := runCommand(cmd) if err != nil { - return errors.Wrap(err, "Failed building image: "+string(out)) + return err } + Info(":whale: Building image " + name + " done") if os.Getenv("DOCKER_SQUASH") == "true" { @@ -77,12 +78,6 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error { Info(":whale: Squashing image " + name + " done") } - if config.LuetCfg.GetGeneral().ShowBuildOutput { - Info(string(out)) - } else { - Debug(string(out)) - } - return nil } diff --git a/pkg/compiler/backend/simpleimg.go b/pkg/compiler/backend/simpleimg.go index ae489939..54aae668 100644 --- a/pkg/compiler/backend/simpleimg.go +++ b/pkg/compiler/backend/simpleimg.go @@ -44,17 +44,17 @@ func (*SimpleImg) BuildImage(opts compiler.CompilerBackendOptions) error { dockerfileName := opts.DockerFileName buildarg := []string{"build", "-f", dockerfileName, "-t", name, context} - Spinner(22) - defer SpinnerStop() + Info(":tea: Building image " + name) cmd := exec.Command("img", buildarg...) cmd.Dir = path - out, err := cmd.CombinedOutput() - + _, err := runCommand(cmd) if err != nil { - return errors.Wrap(err, "Failed building image: "+string(out)) + return err } + Info(":tea: Building image " + name + " done") + return nil } diff --git a/pkg/compiler/backend/writer.go b/pkg/compiler/backend/writer.go new file mode 100644 index 00000000..ec2ff8d2 --- /dev/null +++ b/pkg/compiler/backend/writer.go @@ -0,0 +1,45 @@ +// Copyright © 2021 Daniele Rondina +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, see . + +package backend + +import ( + "bytes" + + . "github.com/mudler/luet/pkg/logger" +) + +type BackendWriter struct { + BufferedOutput bool + Buffer bytes.Buffer +} + +func NewBackendWriter(buffered bool) *BackendWriter { + return &BackendWriter{ + BufferedOutput: buffered, + } +} + +func (b *BackendWriter) Write(p []byte) (int, error) { + if b.BufferedOutput { + return b.Buffer.Write(p) + } else { + Msg("info", false, false, (string(p))) + } + return len(p), nil +} + +func (b *BackendWriter) Close() error { return nil } +func (b *BackendWriter) GetCombinedOutput() string { return b.Buffer.String() }