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..8209bd22 100644 --- a/pkg/compiler/backend/common.go +++ b/pkg/compiler/backend/common.go @@ -16,8 +16,14 @@ 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/google/go-containerregistry/pkg/crane" + "github.com/pkg/errors" ) const ( @@ -41,3 +47,38 @@ func NewBackend(s string) compiler.CompilerBackend { } return compilerBackend } + +func runCommand(cmd *exec.Cmd) (string, error) { + ans := "" + writer := NewBackendWriter(!config.LuetCfg.GetGeneral().ShowBuildOutput) + + 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 { + ans = writer.GetCombinedOutput() + } + + return ans, nil +} diff --git a/pkg/compiler/backend/simpledocker.go b/pkg/compiler/backend/simpledocker.go index 9cec7136..98a42ca5 100644 --- a/pkg/compiler/backend/simpledocker.go +++ b/pkg/compiler/backend/simpledocker.go @@ -53,19 +53,31 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error { } buildarg := []string{"build", "-f", dockerfileName, "-t", name, context} + if !config.LuetCfg.GetGeneral().ShowBuildOutput { + Spinner(22) + defer SpinnerStop() + } + 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 } + + if !config.LuetCfg.GetGeneral().ShowBuildOutput { + SpinnerStop() + } + Info(":whale: Building image " + name + " done") if os.Getenv("DOCKER_SQUASH") == "true" { Info(":whale: Squashing image " + name) var client *docker.Client + Spinner(22) + client, err = docker.NewClientFromEnv() if err != nil { return errors.Wrap(err, "could not connect to the Docker daemon") @@ -74,13 +86,8 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error { if err != nil { return errors.Wrap(err, "Failed squashing image") } - Info(":whale: Squashing image " + name + " done") - } - if config.LuetCfg.GetGeneral().ShowBuildOutput { - Info(string(out)) - } else { - Debug(string(out)) + Info(":whale: Squashing image " + name + " done") } return nil @@ -101,11 +108,16 @@ func (*SimpleDocker) DownloadImage(opts compiler.CompilerBackendOptions) error { name := opts.ImageName buildarg := []string{"pull", name} Debug(":whale: Downloading image " + name) + + Spinner(22) + defer SpinnerStop() + cmd := exec.Command("docker", buildarg...) out, err := cmd.CombinedOutput() if err != nil { return errors.Wrap(err, "Failed pulling image: "+string(out)) } + Info(":whale: Downloaded image:", name) return nil } @@ -142,6 +154,10 @@ func (*SimpleDocker) RemoveImage(opts compiler.CompilerBackendOptions) error { func (*SimpleDocker) Push(opts compiler.CompilerBackendOptions) error { name := opts.ImageName pusharg := []string{"push", name} + + Spinner(22) + defer SpinnerStop() + out, err := exec.Command("docker", pusharg...).CombinedOutput() if err != nil { return errors.Wrap(err, "Failed pushing image: "+string(out)) @@ -170,6 +186,10 @@ func (*SimpleDocker) ExportImage(opts compiler.CompilerBackendOptions) error { buildarg := []string{"save", name, "-o", path} Debug(":whale: Saving image " + name) + + Spinner(22) + defer SpinnerStop() + out, err := exec.Command("docker", buildarg...).CombinedOutput() if err != nil { return errors.Wrap(err, "Failed exporting image: "+string(out)) @@ -194,10 +214,16 @@ func (b *SimpleDocker) ExtractRootfs(opts compiler.CompilerBackendOptions, keepP defer os.RemoveAll(tempexport) // clean up imageExport := filepath.Join(tempexport, "image.tar") + + Spinner(22) + defer SpinnerStop() + if err := b.ExportImage(compiler.CompilerBackendOptions{ImageName: name, Destination: imageExport}); err != nil { return errors.Wrap(err, "failed while extracting rootfs for "+name) } + SpinnerStop() + src := imageExport if src == "" && opts.ImageName != "" { diff --git a/pkg/compiler/backend/simpleimg.go b/pkg/compiler/backend/simpleimg.go index ae489939..a85757f8 100644 --- a/pkg/compiler/backend/simpleimg.go +++ b/pkg/compiler/backend/simpleimg.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/mudler/luet/pkg/compiler" + "github.com/mudler/luet/pkg/config" . "github.com/mudler/luet/pkg/logger" "github.com/pkg/errors" @@ -44,17 +45,23 @@ 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) + + if !config.LuetCfg.GetGeneral().ShowBuildOutput { + Spinner(22) + defer SpinnerStop() + } + 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 } @@ -73,11 +80,13 @@ func (*SimpleImg) RemoveImage(opts compiler.CompilerBackendOptions) error { } func (*SimpleImg) DownloadImage(opts compiler.CompilerBackendOptions) error { - name := opts.ImageName buildarg := []string{"pull", name} - Debug(":tea: Downloading image " + name) + + Spinner(22) + defer SpinnerStop() + cmd := exec.Command("img", buildarg...) out, err := cmd.CombinedOutput() if err != nil { @@ -138,6 +147,10 @@ func (*SimpleImg) ExportImage(opts compiler.CompilerBackendOptions) error { path := opts.Destination buildarg := []string{"save", "-o", path, name} Debug(":tea: Saving image " + name) + + Spinner(22) + defer SpinnerStop() + out, err := exec.Command("img", buildarg...).CombinedOutput() if err != nil { return errors.Wrap(err, "Failed exporting image: "+string(out)) @@ -158,8 +171,13 @@ func (s *SimpleImg) ExtractRootfs(opts compiler.CompilerBackendOptions, keepPerm } os.RemoveAll(path) + buildarg := []string{"unpack", "-o", path, name} Debug(":tea: Extracting image " + name) + + Spinner(22) + defer SpinnerStop() + out, err := exec.Command("img", buildarg...).CombinedOutput() if err != nil { return errors.Wrap(err, "Failed extracting image: "+string(out)) 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() } diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 4b0b197b..ba7bd903 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -148,8 +148,6 @@ func (cs *LuetCompiler) CompileWithReverseDeps(keepPermissions bool, ps Compilat } func (cs *LuetCompiler) CompileParallel(keepPermissions bool, ps CompilationSpecs) ([]Artifact, []error) { - Spinner(22) - defer SpinnerStop() all := make(chan CompilationSpec) artifacts := []Artifact{} mutex := &sync.Mutex{} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index ea800ecb..6b2028cf 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -173,7 +173,7 @@ func level2AtomicLevel(level string) zap.AtomicLevel { } } -func msg(level string, withoutColor bool, msg ...interface{}) { +func Msg(level string, withoutColor, ln bool, msg ...interface{}) { var message string var confLevel, msgLevel int @@ -219,11 +219,16 @@ func msg(level string, withoutColor bool, msg ...interface{}) { log2File(level, message) } - fmt.Println(levelMsg) + if ln { + fmt.Println(levelMsg) + } else { + fmt.Print(levelMsg) + } + } func Warning(mess ...interface{}) { - msg("warning", false, mess...) + Msg("warning", false, true, mess...) if LuetCfg.GetGeneral().FatalWarns { os.Exit(2) } @@ -235,23 +240,23 @@ func Debug(mess ...interface{}) { mess = append([]interface{}{fmt.Sprintf("DEBUG (%s:#%d:%v)", path.Base(file), line, runtime.FuncForPC(pc).Name())}, mess...) } - msg("debug", false, mess...) + Msg("debug", false, true, mess...) } func DebugC(mess ...interface{}) { - msg("debug", true, mess...) + Msg("debug", true, true, mess...) } func Info(mess ...interface{}) { - msg("info", false, mess...) + Msg("info", false, true, mess...) } func InfoC(mess ...interface{}) { - msg("info", true, mess...) + Msg("info", true, true, mess...) } func Error(mess ...interface{}) { - msg("error", false, mess...) + Msg("error", false, true, mess...) } func Fatal(mess ...interface{}) {