backend: Add realtime output on building phase

The realtime output could be configured through
LUET_GENERAL__SHOW_BUILD_OUTPUT environment
variable or related config option or through
`--live-output` option.
This commit is contained in:
Daniele Rondina
2021-02-01 19:10:16 +01:00
parent 2854c68209
commit c1fe3278fa
5 changed files with 108 additions and 14 deletions

View File

@@ -83,6 +83,8 @@ Build packages specifying multiple definition trees:
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount")) LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate")) LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts")) 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) { Run: func(cmd *cobra.Command, args []string) {
@@ -155,6 +157,8 @@ Build packages specifying multiple definition trees:
LuetCfg.GetSolverOptions().Discount = float32(discount) LuetCfg.GetSolverOptions().Discount = float32(discount)
LuetCfg.GetSolverOptions().MaxAttempts = attempts LuetCfg.GetSolverOptions().MaxAttempts = attempts
LuetCfg.GetGeneral().ShowBuildOutput = LuetCfg.Viper.GetBool("general.show_build_output")
Debug("Solver", LuetCfg.GetSolverOptions().CompactString()) Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
opts := compiler.NewDefaultCompilerOptions() opts := compiler.NewDefaultCompilerOptions()
@@ -317,6 +321,7 @@ func init() {
buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate") buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts") buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
buildCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)") 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") buildCmd.Flags().Bool("pretend", false, "Just print what packages will be compiled")

View File

@@ -16,8 +16,15 @@
package backend package backend
import ( import (
"github.com/google/go-containerregistry/pkg/crane" "fmt"
"os/exec"
"github.com/mudler/luet/pkg/compiler" "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 ( const (
@@ -41,3 +48,45 @@ func NewBackend(s string) compiler.CompilerBackend {
} }
return 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
}

View File

@@ -56,10 +56,11 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
Info(":whale2: Building image " + name) Info(":whale2: Building image " + name)
cmd := exec.Command("docker", buildarg...) cmd := exec.Command("docker", buildarg...)
cmd.Dir = path cmd.Dir = path
out, err := cmd.CombinedOutput() _, err := runCommand(cmd)
if err != nil { if err != nil {
return errors.Wrap(err, "Failed building image: "+string(out)) return err
} }
Info(":whale: Building image " + name + " done") Info(":whale: Building image " + name + " done")
if os.Getenv("DOCKER_SQUASH") == "true" { if os.Getenv("DOCKER_SQUASH") == "true" {
@@ -77,12 +78,6 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
Info(":whale: Squashing image " + name + " done") Info(":whale: Squashing image " + name + " done")
} }
if config.LuetCfg.GetGeneral().ShowBuildOutput {
Info(string(out))
} else {
Debug(string(out))
}
return nil return nil
} }

View File

@@ -44,17 +44,17 @@ func (*SimpleImg) BuildImage(opts compiler.CompilerBackendOptions) error {
dockerfileName := opts.DockerFileName dockerfileName := opts.DockerFileName
buildarg := []string{"build", "-f", dockerfileName, "-t", name, context} buildarg := []string{"build", "-f", dockerfileName, "-t", name, context}
Spinner(22)
defer SpinnerStop()
Info(":tea: Building image " + name) Info(":tea: Building image " + name)
cmd := exec.Command("img", buildarg...) cmd := exec.Command("img", buildarg...)
cmd.Dir = path cmd.Dir = path
out, err := cmd.CombinedOutput() _, err := runCommand(cmd)
if err != nil { if err != nil {
return errors.Wrap(err, "Failed building image: "+string(out)) return err
} }
Info(":tea: Building image " + name + " done") Info(":tea: Building image " + name + " done")
return nil return nil
} }

View File

@@ -0,0 +1,45 @@
// Copyright © 2021 Daniele Rondina <geaaru@sabayonlinux.org>
//
// 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 <http://www.gnu.org/licenses/>.
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() }