From d53d8926bca6c831de5d6599547913b8a580d4f2 Mon Sep 17 00:00:00 2001 From: Alex Goodman <wagoodman@gmail.com> Date: Mon, 7 Oct 2019 11:44:41 -0400 Subject: [PATCH] stop swallowing errors & cleanup exit logic --- cmd/analyze.go | 11 ++-- cmd/build.go | 3 - cmd/root.go | 6 +- dive/image/docker/image_archive.go | 7 +-- dive/image/podman/image_directory.go | 2 +- runtime/run.go | 24 ++++---- runtime/ui/details_controller.go | 30 ++++++---- runtime/ui/filetree_controller.go | 31 ++++++----- runtime/ui/filetree_viewmodel.go | 22 +++++--- runtime/ui/filter_controller.go | 6 +- runtime/ui/layer_controller.go | 29 ++++++---- runtime/ui/status_controller.go | 9 ++- runtime/ui/ui.go | 82 ++++++++++++++++++---------- utils/exit.go | 34 ------------ 14 files changed, 153 insertions(+), 143 deletions(-) delete mode 100644 utils/exit.go diff --git a/cmd/analyze.go b/cmd/analyze.go index 1b181cf..4e6c27e 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -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{ diff --git a/cmd/build.go b/cmd/build.go index cc6e09a..0cff973 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -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 diff --git a/cmd/root.go b/cmd/root.go index b09b5aa..41abe52 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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") diff --git a/dive/image/docker/image_archive.go b/dive/image/docker/image_archive.go index 69b1772..5c4b001 100644 --- a/dive/image/docker/image_archive.go +++ b/dive/image/docker/image_archive.go @@ -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 diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 4b03dae..8e551a8 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -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) diff --git a/runtime/run.go b/runtime/run.go index b8185e8..65b3670 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -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) } } diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index 92d7a6a..a9ec51c 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -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 } diff --git a/runtime/ui/filetree_controller.go b/runtime/ui/filetree_controller.go index 90bcba6..16a72b2 100644 --- a/runtime/ui/filetree_controller.go +++ b/runtime/ui/filetree_controller.go @@ -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 } diff --git a/runtime/ui/filetree_viewmodel.go b/runtime/ui/filetree_viewmodel.go index 30009fd..18ebfdd 100644 --- a/runtime/ui/filetree_viewmodel.go +++ b/runtime/ui/filetree_viewmodel.go @@ -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 diff --git a/runtime/ui/filter_controller.go b/runtime/ui/filter_controller.go index 7902052..bd740b3 100644 --- a/runtime/ui/filter_controller.go +++ b/runtime/ui/filter_controller.go @@ -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 diff --git a/runtime/ui/layer_controller.go b/runtime/ui/layer_controller.go index ed7f259..b7c3740 100644 --- a/runtime/ui/layer_controller.go +++ b/runtime/ui/layer_controller.go @@ -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 } } diff --git a/runtime/ui/status_controller.go b/runtime/ui/status_controller.go index 2b5e933..8e590df 100644 --- a/runtime/ui/status_controller.go +++ b/runtime/ui/status_controller.go @@ -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 } diff --git a/runtime/ui/ui.go b/runtime/ui/ui.go index 6f4e5ed..0e6c3d2 100644 --- a/runtime/ui/ui.go +++ b/runtime/ui/ui.go @@ -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 } diff --git a/utils/exit.go b/utils/exit.go deleted file mode 100644 index 512d37c..0000000 --- a/utils/exit.go +++ /dev/null @@ -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() - } -}