stop swallowing errors & cleanup exit logic

This commit is contained in:
Alex Goodman 2019-10-07 11:44:41 -04:00
parent 26d37baabd
commit d53d8926bc
No known key found for this signature in database
GPG Key ID: 98AF011C5C78EB7E
14 changed files with 153 additions and 143 deletions

View File

@ -3,16 +3,15 @@ package cmd
import (
"fmt"
"github.com/wagoodman/dive/dive"
"os"
"github.com/spf13/cobra"
"github.com/wagoodman/dive/runtime"
"github.com/wagoodman/dive/utils"
)
// doAnalyzeCmd takes a docker image tag, digest, or id and displays the
// image analysis to the screen
func doAnalyzeCmd(cmd *cobra.Command, args []string) {
defer utils.Cleanup()
if len(args) == 0 {
printVersionFlag, err := cmd.PersistentFlags().GetBool("version")
@ -22,13 +21,13 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) {
}
fmt.Println("No image argument given")
utils.Exit(1)
os.Exit(1)
}
userImage := args[0]
if userImage == "" {
fmt.Println("No image argument given")
utils.Exit(1)
os.Exit(1)
}
initLogging()
@ -37,13 +36,13 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) {
if err != nil {
fmt.Printf("ci configuration error: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
engine, err := cmd.PersistentFlags().GetString("engine")
if err != nil {
fmt.Printf("unable to determine engine: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
runtime.Run(runtime.Options{

View File

@ -5,7 +5,6 @@ import (
"github.com/spf13/viper"
"github.com/wagoodman/dive/dive"
"github.com/wagoodman/dive/runtime"
"github.com/wagoodman/dive/utils"
)
// buildCmd represents the build command
@ -22,8 +21,6 @@ func init() {
// doBuildCmd implements the steps taken for the build command
func doBuildCmd(cmd *cobra.Command, args []string) {
defer utils.Cleanup()
initLogging()
// there is no cli options allowed, only config can be supplied

View File

@ -13,7 +13,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/wagoodman/dive/utils"
)
var cfgFile string
@ -36,9 +35,8 @@ the amount of wasted space and identifies the offending files from the image.`,
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
utils.Exit(1)
os.Exit(1)
}
utils.Cleanup()
}
func init() {
@ -160,7 +158,7 @@ func getCfgFile(fromFlag string) string {
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
utils.Exit(0)
os.Exit(0)
}
xdgHome := os.Getenv("XDG_CONFIG_HOME")

View File

@ -5,9 +5,9 @@ import (
"fmt"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
"github.com/wagoodman/dive/utils"
"io"
"io/ioutil"
"os"
"strings"
)
@ -37,7 +37,7 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) {
if err != nil {
fmt.Println(err)
utils.Exit(1)
os.Exit(1)
}
name := header.Name
@ -116,8 +116,7 @@ func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) {
if err == io.EOF {
break
} else if err != nil {
fmt.Println(err)
utils.Exit(1)
return nil, err
}
name := header.Name

View File

@ -58,7 +58,7 @@ func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) {
// record the tree and layer info
imgDirRef.treeMap[curImg.ID()] = tree
imgDirRef.layerMap[curImg.ID()] = curImg
imgDirRef.layerOrder = append(imgDirRef.layerOrder, curImg.ID())
imgDirRef.layerOrder = append([]string{curImg.ID()}, imgDirRef.layerOrder...)
// continue to the next image
curImg, err = curImg.GetParent(ctx)

View File

@ -6,6 +6,7 @@ import (
"github.com/wagoodman/dive/dive"
"github.com/wagoodman/dive/runtime/ci"
"github.com/wagoodman/dive/runtime/export"
"os"
"time"
"github.com/dustin/go-humanize"
@ -26,9 +27,9 @@ func runCi(analysis *image.AnalysisResult, options Options) {
evaluator.Report()
if pass {
utils.Exit(0)
os.Exit(0)
}
utils.Exit(1)
os.Exit(1)
}
func Run(options Options) {
@ -44,7 +45,7 @@ func Run(options Options) {
imageResolver, err := dive.GetImageHandler(options.Engine)
if err != nil {
fmt.Printf("cannot determine image provider: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
var img *image.Image
@ -54,13 +55,13 @@ func Run(options Options) {
img, err = imageResolver.Build(options.BuildArgs)
if err != nil {
fmt.Printf("cannot build image: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
} else {
img, err = imageResolver.Fetch(options.ImageId)
if err != nil {
fmt.Printf("cannot fetch image: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
}
@ -76,14 +77,14 @@ func Run(options Options) {
result, err := img.Analyze()
if err != nil {
fmt.Printf("cannot analyze image: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
if doExport {
err = export.NewExport(result).ToFile(options.ExportFile)
if err != nil {
fmt.Printf("cannot write export file: %v\n", err)
utils.Exit(1)
os.Exit(1)
}
}
@ -91,7 +92,7 @@ func Run(options Options) {
runCi(result, options)
} else {
if doExport {
utils.Exit(0)
os.Exit(0)
}
fmt.Println(utils.TitleFormat("Building cache..."))
@ -99,7 +100,7 @@ func Run(options Options) {
err := cache.Build()
if err != nil {
logrus.Error(err)
utils.Exit(1)
os.Exit(1)
}
// it appears there is a race condition where termbox.Init() will
@ -110,11 +111,10 @@ func Run(options Options) {
time.Sleep(100 * time.Millisecond)
err = ui.Run(result, cache)
if err != nil {
logrus.Error(err)
utils.Exit(1)
os.Exit(1)
}
utils.Exit(0)
os.Exit(0)
}
}

View File

@ -2,6 +2,7 @@ package ui
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/wagoodman/dive/dive/filetree"
"strconv"
"strings"
@ -121,22 +122,29 @@ func (controller *DetailsController) Render() error {
layerHeaderStr := fmt.Sprintf("[Layer Details]%s", strings.Repeat("─", width-15))
imageHeaderStr := fmt.Sprintf("[Image Details]%s", strings.Repeat("─", width-15))
_, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(layerHeaderStr, false)))
_, err := fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(layerHeaderStr, false)))
if err != nil {
return err
}
// update contents
controller.view.Clear()
_, _ = fmt.Fprintln(controller.view, Formatting.Header("Digest: ")+currentLayer.Id())
_, _ = fmt.Fprintln(controller.view, Formatting.Header("Command:"))
_, _ = fmt.Fprintln(controller.view, currentLayer.Command())
_, _ = fmt.Fprintln(controller.view, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false)))
var lines = make([]string, 0)
lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id())
lines = append(lines, Formatting.Header("Command:"))
lines = append(lines, currentLayer.Command())
lines = append(lines, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false)))
lines = append(lines, imageSizeStr)
lines = append(lines, wastedSpaceStr)
lines = append(lines, effStr+"\n")
lines = append(lines, inefficiencyReport)
_, _ = fmt.Fprintln(controller.view, imageSizeStr)
_, _ = fmt.Fprintln(controller.view, wastedSpaceStr)
_, _ = fmt.Fprintln(controller.view, effStr+"\n")
_, _ = fmt.Fprintln(controller.view, inefficiencyReport)
return nil
_, err = fmt.Fprintln(controller.view, strings.Join(lines, "\n"))
if err != nil {
logrus.Debug("unable to write to buffer: ", err)
}
return err
})
return nil
}

View File

@ -42,15 +42,17 @@ type FileTreeController struct {
}
// NewFileTreeController creates a new view object attached the the global [gocui] screen object.
func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (controller *FileTreeController) {
func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (controller *FileTreeController, err error) {
controller = new(FileTreeController)
// populate main fields
controller.Name = name
controller.gui = gui
controller.vm = NewFileTreeViewModel(tree, refTrees, cache)
controller.vm, err = NewFileTreeViewModel(tree, refTrees, cache)
if err != nil {
return nil, err
}
var err error
controller.keybindingToggleCollapse, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-collapse-dir"))
if err != nil {
logrus.Error(err)
@ -100,7 +102,7 @@ func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree,
logrus.Error(err)
}
return controller
return controller, err
}
// Setup initializes the UI concerns within the context of a global [gocui] view object.
@ -302,19 +304,15 @@ func (controller *FileTreeController) toggleAttributes() error {
if err != nil {
return err
}
// we need to render the changes to the status pane as well
Update()
Render()
return nil
// we need to render the changes to the status pane as well (not just this contoller/view)
return UpdateAndRender()
}
// toggleShowDiffType will show/hide the selected DiffType in the filetree pane.
func (controller *FileTreeController) toggleShowDiffType(diffType filetree.DiffType) error {
controller.vm.toggleShowDiffType(diffType)
// we need to render the changes to the status pane as well
Update()
Render()
return nil
// we need to render the changes to the status pane as well (not just this contoller/view)
return UpdateAndRender()
}
// filterRegex will return a regular expression object to match the user's filter input.
@ -383,10 +381,13 @@ func (controller *FileTreeController) Render() error {
// update the contents
controller.view.Clear()
_ = controller.vm.Render()
_, _ = fmt.Fprint(controller.view, controller.vm.mainBuf.String())
err := controller.vm.Render()
if err != nil {
return err
}
_, err = fmt.Fprint(controller.view, controller.vm.mainBuf.String())
return nil
return err
})
return nil
}

View File

@ -6,11 +6,9 @@ import (
"regexp"
"strings"
"github.com/lunixbochs/vtclean"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/wagoodman/dive/utils"
"github.com/lunixbochs/vtclean"
"github.com/wagoodman/dive/dive/filetree"
)
@ -36,7 +34,7 @@ type FileTreeViewModel struct {
}
// NewFileTreeController creates a new view object attached the the global [gocui] screen object.
func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (treeViewModel *FileTreeViewModel) {
func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (treeViewModel *FileTreeViewModel, err error) {
treeViewModel = new(FileTreeViewModel)
// populate main fields
@ -59,11 +57,11 @@ func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree
case "unmodified":
treeViewModel.HiddenDiffTypes[filetree.Unmodified] = true
default:
utils.PrintAndExit(fmt.Sprintf("unknown diff.hide value: %s", t))
return nil, fmt.Errorf("unknown diff.hide value: %s", t)
}
}
return treeViewModel
return treeViewModel, nil
}
// Setup initializes the UI concerns within the context of a global [gocui] view object.
@ -420,9 +418,17 @@ func (vm *FileTreeViewModel) Render() error {
vm.mainBuf.Reset()
for idx, line := range lines {
if idx == vm.bufferIndex {
fmt.Fprintln(&vm.mainBuf, Formatting.Selected(vtclean.Clean(line, false)))
_, err := fmt.Fprintln(&vm.mainBuf, Formatting.Selected(vtclean.Clean(line, false)))
if err != nil {
logrus.Debug("unable to write to buffer: ", err)
return err
}
} else {
fmt.Fprintln(&vm.mainBuf, line)
_, err := fmt.Fprintln(&vm.mainBuf, line)
if err != nil {
logrus.Debug("unable to write to buffer: ", err)
return err
}
}
}
return nil

View File

@ -3,6 +3,7 @@ package ui
import (
"fmt"
"github.com/jroimartin/gocui"
"github.com/sirupsen/logrus"
)
// FilterController holds the UI objects and data models for populating the bottom row. Specifically the pane that
@ -99,9 +100,10 @@ func (controller *FilterController) Update() error {
// Render flushes the state objects to the screen. Currently this is the users path filter input.
func (controller *FilterController) Render() error {
controller.gui.Update(func(g *gocui.Gui) error {
// render the header
_, err := fmt.Fprintln(controller.header, Formatting.Header(controller.headerStr))
if err != nil {
logrus.Error("unable to write to buffer: ", err)
}
return err
})
return nil

View File

@ -9,7 +9,6 @@ import (
"github.com/lunixbochs/vtclean"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/wagoodman/dive/utils"
"github.com/wagoodman/keybinding"
)
@ -33,7 +32,7 @@ type LayerController struct {
}
// NewLayerController creates a new view object attached the the global [gocui] screen object.
func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (controller *LayerController) {
func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (controller *LayerController, err error) {
controller = new(LayerController)
// populate main fields
@ -47,10 +46,9 @@ func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (cont
case false:
controller.CompareMode = CompareLayer
default:
utils.PrintAndExit(fmt.Sprintf("unknown layer.show-aggregated-changes value: %v", mode))
return nil, fmt.Errorf("unknown layer.show-aggregated-changes value: %v", mode)
}
var err error
controller.keybindingCompareAll, err = keybinding.ParseAll(viper.GetString("keybinding.compare-all"))
if err != nil {
logrus.Error(err)
@ -71,7 +69,7 @@ func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (cont
logrus.Error(err)
}
return controller
return controller, err
}
// Setup initializes the UI concerns within the context of a global [gocui] view object.
@ -218,8 +216,11 @@ func (controller *LayerController) currentLayer() image.Layer {
// setCompareMode switches the layer comparison between a single-layer comparison to an aggregated comparison.
func (controller *LayerController) setCompareMode(compareMode CompareType) error {
controller.CompareMode = compareMode
Update()
Render()
err := UpdateAndRender()
if err != nil {
logrus.Errorf("unable to set compare mode: %+v", err)
return err
}
return Controllers.Tree.setTreeByLayer(controller.getCompareIndexes())
}
@ -283,7 +284,10 @@ func (controller *LayerController) Render() error {
width, _ := g.Size()
headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2))
headerStr += fmt.Sprintf("Cmp"+image.LayerFormat, "Size", "Command")
_, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false)))
_, err := fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false)))
if err != nil {
return err
}
// update contents
controller.view.Clear()
@ -295,9 +299,14 @@ func (controller *LayerController) Render() error {
compareBar := controller.renderCompareBar(idx)
if idx == controller.LayerIndex {
_, _ = fmt.Fprintln(controller.view, compareBar+" "+Formatting.Selected(layerStr))
_, err = fmt.Fprintln(controller.view, compareBar+" "+Formatting.Selected(layerStr))
} else {
_, _ = fmt.Fprintln(controller.view, compareBar+" "+layerStr)
_, err = fmt.Fprintln(controller.view, compareBar+" "+layerStr)
}
if err != nil {
logrus.Debug("unable to write to buffer: ", err)
return err
}
}

View File

@ -2,6 +2,7 @@ package ui
import (
"fmt"
"github.com/sirupsen/logrus"
"strings"
@ -61,11 +62,13 @@ func (controller *StatusController) Update() error {
func (controller *StatusController) Render() error {
controller.gui.Update(func(g *gocui.Gui) error {
controller.view.Clear()
_, _ = fmt.Fprintln(controller.view, controller.KeyHelp()+Controllers.lookup[controller.gui.CurrentView().Name()].KeyHelp()+Formatting.StatusNormal("▏"+strings.Repeat(" ", 1000)))
_, err := fmt.Fprintln(controller.view, controller.KeyHelp()+Controllers.lookup[controller.gui.CurrentView().Name()].KeyHelp()+Formatting.StatusNormal("▏"+strings.Repeat(" ", 1000)))
if err != nil {
logrus.Debug("unable to write to buffer: ", err)
}
return nil
return err
})
// todo: blerg
return nil
}

View File

@ -9,7 +9,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/utils"
"github.com/wagoodman/keybinding"
)
@ -72,6 +71,22 @@ type View interface {
IsVisible() bool
}
func UpdateAndRender() error {
err := Update()
if err != nil {
logrus.Debug("failed update: ", err)
return err
}
err = Render()
if err != nil {
logrus.Debug("failed render: ", err)
return err
}
return nil
}
// toggleView switches between the file view and the layer view and re-renders the screen.
func toggleView(g *gocui.Gui, v *gocui.View) (err error) {
if v == nil || v.Name() == Controllers.Layer.Name {
@ -79,9 +94,13 @@ func toggleView(g *gocui.Gui, v *gocui.View) (err error) {
} else {
_, err = g.SetCurrentView(Controllers.Layer.Name)
}
Update()
Render()
return err
if err != nil {
logrus.Error("unable to toggle view: ", err)
return err
}
return UpdateAndRender()
}
// toggleFilterView shows/hides the file tree filter pane.
@ -95,22 +114,24 @@ func toggleFilterView(g *gocui.Gui, v *gocui.View) error {
if !Controllers.Filter.hidden {
_, err := g.SetCurrentView(Controllers.Filter.Name)
if err != nil {
logrus.Error("unable to toggle filter view: ", err)
return err
}
Update()
Render()
} else {
err := toggleView(g, v)
if err != nil {
return err
}
err = Controllers.Filter.view.SetCursor(0, 0)
if err != nil {
return err
}
return UpdateAndRender()
}
err := toggleView(g, v)
if err != nil {
logrus.Error("unable to toggle filter view (back): ", err)
return err
}
err = Controllers.Filter.view.SetCursor(0, 0)
if err != nil {
return err
}
return nil
}
@ -325,9 +346,10 @@ func layout(g *gocui.Gui) error {
// Update refreshes the state objects for future rendering.
func Update() error {
for _, view := range Controllers.lookup {
err := view.Update()
for _, controller := range Controllers.lookup {
err := controller.Update()
if err != nil {
logrus.Debug("unable to update controller: ")
return err
}
}
@ -336,9 +358,9 @@ func Update() error {
// Render flushes the state objects to the screen.
func Render() error {
for _, view := range Controllers.lookup {
if view.IsVisible() {
err := view.Render()
for _, controller := range Controllers.lookup {
if controller.IsVisible() {
err := controller.Render()
if err != nil {
return err
}
@ -386,19 +408,24 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) error {
if err != nil {
return err
}
utils.SetUi(g)
defer g.Close()
Controllers.lookup = make(map[string]View)
Controllers.Layer = NewLayerController("side", g, analysis.Layers)
Controllers.Layer, err = NewLayerController("side", g, analysis.Layers)
if err != nil {
return err
}
Controllers.lookup[Controllers.Layer.Name] = Controllers.Layer
treeStack, err := filetree.StackTreeRange(analysis.RefTrees, 0, 0)
if err != nil {
return err
}
Controllers.Tree = NewFileTreeController("main", g, treeStack, analysis.RefTrees, cache)
Controllers.Tree, err = NewFileTreeController("main", g, treeStack, analysis.RefTrees, cache)
if err != nil {
return err
}
Controllers.lookup[Controllers.Tree.Name] = Controllers.Tree
Controllers.Status = NewStatusController("status", g)
@ -421,12 +448,7 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) error {
// }
// perform the first update and render now that all resources have been loaded
err = Update()
if err != nil {
return err
}
err = Render()
err = UpdateAndRender()
if err != nil {
return err
}

View File

@ -1,34 +0,0 @@
package utils
import (
"fmt"
"os"
"github.com/jroimartin/gocui"
"github.com/sirupsen/logrus"
)
var ui *gocui.Gui
func SetUi(g *gocui.Gui) {
ui = g
}
func PrintAndExit(args ...interface{}) {
logrus.Println(args...)
Cleanup()
fmt.Println(args...)
os.Exit(1)
}
// Note: this should only be used when exiting from non-gocui code
func Exit(rc int) {
Cleanup()
os.Exit(rc)
}
func Cleanup() {
if ui != nil {
ui.Close()
}
}