dive/runtime/run.go
Alex Goodman d5e8a92968
Rework CI validation workflow and makefile (#460)
* rework CI validation workflow and makefile

* enable push

* fix job names

* fix license check

* fix snapshot builds

* fix acceptance tests

* fix linting

* disable pull request event

* rework windows runner caching

* disable release pipeline and add issue templates
2023-07-06 22:01:46 -04:00

161 lines
4.3 KiB
Go

package runtime
import (
"fmt"
"os"
"time"
"github.com/dustin/go-humanize"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/wagoodman/dive/dive"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
"github.com/wagoodman/dive/runtime/ci"
"github.com/wagoodman/dive/runtime/export"
"github.com/wagoodman/dive/runtime/ui"
"github.com/wagoodman/dive/utils"
)
func run(enableUi bool, options Options, imageResolver image.Resolver, events eventChannel, filesystem afero.Fs) {
var img *image.Image
var err error
defer close(events)
doExport := options.ExportFile != ""
doBuild := len(options.BuildArgs) > 0
if doBuild {
events.message(utils.TitleFormat("Building image..."))
img, err = imageResolver.Build(options.BuildArgs)
if err != nil {
events.exitWithErrorMessage("cannot build image", err)
return
}
} else {
events.message(utils.TitleFormat("Image Source: ") + options.Source.String() + "://" + options.Image)
events.message(utils.TitleFormat("Fetching image...") + " (this can take a while for large images)")
img, err = imageResolver.Fetch(options.Image)
if err != nil {
events.exitWithErrorMessage("cannot fetch image", err)
return
}
}
events.message(utils.TitleFormat("Analyzing image..."))
analysis, err := img.Analyze()
if err != nil {
events.exitWithErrorMessage("cannot analyze image", err)
return
}
if doExport {
events.message(utils.TitleFormat(fmt.Sprintf("Exporting image to '%s'...", options.ExportFile)))
bytes, err := export.NewExport(analysis).Marshal()
if err != nil {
events.exitWithErrorMessage("cannot marshal export payload", err)
return
}
file, err := filesystem.OpenFile(options.ExportFile, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
events.exitWithErrorMessage("cannot open export file", err)
return
}
defer file.Close()
_, err = file.Write(bytes)
if err != nil {
events.exitWithErrorMessage("cannot write to export file", err)
}
return
}
if options.Ci {
events.message(fmt.Sprintf(" efficiency: %2.4f %%", analysis.Efficiency*100))
events.message(fmt.Sprintf(" wastedBytes: %d bytes (%s)", analysis.WastedBytes, humanize.Bytes(analysis.WastedBytes)))
events.message(fmt.Sprintf(" userWastedPercent: %2.4f %%", analysis.WastedUserPercent*100))
evaluator := ci.NewCiEvaluator(options.CiConfig)
pass := evaluator.Evaluate(analysis)
events.message(evaluator.Report())
if !pass {
events.exitWithError(nil)
}
return
} else {
events.message(utils.TitleFormat("Building cache..."))
treeStack := filetree.NewComparer(analysis.RefTrees)
errors := treeStack.BuildCache()
if errors != nil {
for _, err := range errors {
events.message(" " + err.Error())
}
if !options.IgnoreErrors {
events.exitWithError(fmt.Errorf("file tree has path errors (use '--ignore-errors' to attempt to continue)"))
return
}
}
if enableUi {
// it appears there is a race condition where termbox.Init() will
// block nearly indefinitely when running as the first process in
// a Docker container when started within ~25ms of container startup.
// I can't seem to determine the exact root cause, however, a large
// enough sleep will prevent this behavior (todo: remove this hack)
time.Sleep(100 * time.Millisecond)
err = ui.Run(options.Image, analysis, treeStack)
if err != nil {
events.exitWithError(err)
return
}
}
}
}
func Run(options Options) {
var exitCode int
var events = make(eventChannel)
imageResolver, err := dive.GetImageResolver(options.Source)
if err != nil {
message := "cannot determine image provider"
logrus.Error(message)
logrus.Error(err)
fmt.Fprintf(os.Stderr, "%s: %+v\n", message, err)
os.Exit(1)
}
go run(true, options, imageResolver, events, afero.NewOsFs())
for event := range events {
if event.stdout != "" {
fmt.Println(event.stdout)
}
if event.stderr != "" {
_, err := fmt.Fprintln(os.Stderr, event.stderr)
if err != nil {
fmt.Println("error: could not write to buffer:", err)
}
}
if event.err != nil {
logrus.Error(event.err)
_, err := fmt.Fprintln(os.Stderr, event.err.Error())
if err != nil {
fmt.Println("error: could not write to buffer:", err)
}
}
if event.errorOnExit {
exitCode = 1
}
}
os.Exit(exitCode)
}