better error propagation on startup
This commit is contained in:
parent
56e8530dea
commit
26d37baabd
@ -13,29 +13,36 @@ type TreeCache struct {
|
||||
cache map[TreeCacheKey]*FileTree
|
||||
}
|
||||
|
||||
func (cache *TreeCache) Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) *FileTree {
|
||||
func (cache *TreeCache) Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) (*FileTree, error) {
|
||||
key := TreeCacheKey{bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop}
|
||||
if value, exists := cache.cache[key]; exists {
|
||||
return value
|
||||
return value, nil
|
||||
}
|
||||
|
||||
value := cache.buildTree(key)
|
||||
value, err := cache.buildTree(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cache.cache[key] = value
|
||||
return value
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (cache *TreeCache) buildTree(key TreeCacheKey) *FileTree {
|
||||
newTree := StackTreeRange(cache.refTrees, key.bottomTreeStart, key.bottomTreeStop)
|
||||
func (cache *TreeCache) buildTree(key TreeCacheKey) (*FileTree, error) {
|
||||
newTree, err := StackTreeRange(cache.refTrees, key.bottomTreeStart, key.bottomTreeStop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for idx := key.topTreeStart; idx <= key.topTreeStop; idx++ {
|
||||
err := newTree.CompareAndMark(cache.refTrees[idx])
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to build tree: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return newTree
|
||||
return newTree, nil
|
||||
}
|
||||
|
||||
func (cache *TreeCache) Build() {
|
||||
func (cache *TreeCache) Build() error {
|
||||
var bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int
|
||||
|
||||
// case 1: layer compare (top tree SIZE is fixed (BUT floats forward), Bottom tree SIZE changes)
|
||||
@ -51,7 +58,10 @@ func (cache *TreeCache) Build() {
|
||||
topTreeStart = selectIdx
|
||||
}
|
||||
|
||||
cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
|
||||
_, err := cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// case 2: aggregated compare (bottom tree is ENTIRELY fixed, top tree SIZE changes)
|
||||
@ -66,8 +76,12 @@ func (cache *TreeCache) Build() {
|
||||
topTreeStart = 1
|
||||
}
|
||||
|
||||
cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
|
||||
_, err := cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFileTreeCache(refTrees []*FileTree) TreeCache {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -63,14 +62,22 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) {
|
||||
sizeBytes += curNode.Data.FileInfo.Size
|
||||
return nil
|
||||
}
|
||||
stackedTree := StackTreeRange(trees, 0, currentTree-1)
|
||||
stackedTree, err := StackTreeRange(trees, 0, currentTree-1)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to stack tree range: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
previousTreeNode, err := stackedTree.GetNode(node.Path())
|
||||
if err != nil {
|
||||
logrus.Debug(fmt.Sprintf("CurrentTree: %d : %s", currentTree, err))
|
||||
} else if previousTreeNode.Data.FileInfo.IsDir {
|
||||
return err
|
||||
}
|
||||
|
||||
if previousTreeNode.Data.FileInfo.IsDir {
|
||||
err = previousTreeNode.VisitDepthChildFirst(sizer, nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to propagate whiteout dir: %+v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,14 +367,15 @@ func (tree *FileTree) markRemoved(path string) error {
|
||||
}
|
||||
|
||||
// StackTreeRange combines an array of trees into a single tree
|
||||
func StackTreeRange(trees []*FileTree, start, stop int) *FileTree {
|
||||
func StackTreeRange(trees []*FileTree, start, stop int) (*FileTree, error) {
|
||||
|
||||
tree := trees[0].Copy()
|
||||
for idx := start; idx <= stop; idx++ {
|
||||
err := tree.Stack(trees[idx])
|
||||
if err != nil {
|
||||
logrus.Errorf("could not stack tree range: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return tree
|
||||
return tree, nil
|
||||
}
|
||||
|
@ -745,7 +745,10 @@ func TestStackRange(t *testing.T) {
|
||||
}
|
||||
}
|
||||
trees := []*FileTree{lowerTree, upperTree, tree}
|
||||
StackTreeRange(trees, 0, 2)
|
||||
_, err = StackTreeRange(trees, 0, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveOnIterate(t *testing.T) {
|
||||
|
@ -2,6 +2,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wagoodman/dive/dive"
|
||||
"github.com/wagoodman/dive/runtime/ci"
|
||||
"github.com/wagoodman/dive/runtime/export"
|
||||
@ -95,7 +96,11 @@ func Run(options Options) {
|
||||
|
||||
fmt.Println(utils.TitleFormat("Building cache..."))
|
||||
cache := filetree.NewFileTreeCache(result.RefTrees)
|
||||
cache.Build()
|
||||
err := cache.Build()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
utils.Exit(1)
|
||||
}
|
||||
|
||||
// it appears there is a race condition where termbox.Init() will
|
||||
// block nearly indefinitely when running as the first process in
|
||||
@ -104,6 +109,12 @@ func Run(options Options) {
|
||||
// enough sleep will prevent this behavior (todo: remove this hack)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
ui.Run(result, cache)
|
||||
err = ui.Run(result, cache)
|
||||
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
utils.Exit(1)
|
||||
}
|
||||
utils.Exit(0)
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,11 @@ func (vm *FileTreeViewModel) setTreeByLayer(bottomTreeStart, bottomTreeStop, top
|
||||
if topTreeStop > len(vm.RefTrees)-1 {
|
||||
return fmt.Errorf("invalid layer index given: %d of %d", topTreeStop, len(vm.RefTrees)-1)
|
||||
}
|
||||
newTree := vm.cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
|
||||
newTree, err := vm.cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to fetch layer tree from cache: %+v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// preserve vm state on copy
|
||||
visitor := func(node *filetree.FileNode) error {
|
||||
@ -112,7 +116,7 @@ func (vm *FileTreeViewModel) setTreeByLayer(bottomTreeStart, bottomTreeStop, top
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err := vm.ModelTree.VisitDepthChildFirst(visitor, nil)
|
||||
err = vm.ModelTree.VisitDepthChildFirst(visitor, nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to propagate layer tree: %+v", err)
|
||||
return err
|
||||
|
@ -78,11 +78,18 @@ func initializeTestViewModel(t *testing.T) *FileTreeViewModel {
|
||||
t.Fatalf("%s: unable to fetch analysis: %v", t.Name(), err)
|
||||
}
|
||||
cache := filetree.NewFileTreeCache(result.RefTrees)
|
||||
cache.Build()
|
||||
err = cache.Build()
|
||||
if err != nil {
|
||||
t.Fatalf("%s: unable to build cache: %+v", t.Name(), err)
|
||||
}
|
||||
|
||||
Formatting.Selected = color.New(color.ReverseVideo, color.Bold).SprintFunc()
|
||||
|
||||
return NewFileTreeViewModel(filetree.StackTreeRange(result.RefTrees, 0, 0), result.RefTrees, cache)
|
||||
treeStack, err := filetree.StackTreeRange(result.RefTrees, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: unable to create tree ViewModel: %v", t.Name(), err)
|
||||
}
|
||||
return NewFileTreeViewModel(treeStack, result.RefTrees, cache)
|
||||
}
|
||||
|
||||
func runTestCase(t *testing.T, vm *FileTreeViewModel, width, height int, filterRegex *regexp.Regexp) {
|
||||
|
@ -251,20 +251,32 @@ func layout(g *gocui.Gui) error {
|
||||
view, viewErr = g.SetView(Controllers.Layer.Name, -1, -1+headerRows, splitCols, layersHeight)
|
||||
header, headerErr = g.SetView(Controllers.Layer.Name+"header", -1, -1, splitCols, headerRows)
|
||||
if isNewView(viewErr, headerErr) {
|
||||
_ = Controllers.Layer.Setup(view, header)
|
||||
err = Controllers.Layer.Setup(view, header)
|
||||
if err != nil {
|
||||
logrus.Error("unable to setup layer controller", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = g.SetCurrentView(Controllers.Layer.Name); err != nil {
|
||||
logrus.Error("unable to set view to layer", err)
|
||||
return err
|
||||
}
|
||||
// since we are selecting the view, we should rerender to indicate it is selected
|
||||
_ = Controllers.Layer.Render()
|
||||
err = Controllers.Layer.Render()
|
||||
if err != nil {
|
||||
logrus.Error("unable to render layer view", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Details
|
||||
view, viewErr = g.SetView(Controllers.Details.Name, -1, -1+layersHeight+headerRows, splitCols, maxY-bottomRows)
|
||||
header, headerErr = g.SetView(Controllers.Details.Name+"header", -1, -1+layersHeight, splitCols, layersHeight+headerRows)
|
||||
if isNewView(viewErr, headerErr) {
|
||||
_ = Controllers.Details.Setup(view, header)
|
||||
err = Controllers.Details.Setup(view, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Filetree
|
||||
@ -275,40 +287,64 @@ func layout(g *gocui.Gui) error {
|
||||
view, viewErr = g.SetView(Controllers.Tree.Name, splitCols, -1+headerRows-offset, debugCols, maxY-bottomRows)
|
||||
header, headerErr = g.SetView(Controllers.Tree.Name+"header", splitCols, -1, debugCols, headerRows-offset)
|
||||
if isNewView(viewErr, headerErr) {
|
||||
_ = Controllers.Tree.Setup(view, header)
|
||||
err = Controllers.Tree.Setup(view, header)
|
||||
if err != nil {
|
||||
logrus.Error("unable to setup tree controller", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = Controllers.Tree.onLayoutChange(resized)
|
||||
if err != nil {
|
||||
logrus.Error("unable to setup layer controller onLayoutChange", err)
|
||||
return err
|
||||
}
|
||||
_ = Controllers.Tree.onLayoutChange(resized)
|
||||
|
||||
// Status Bar
|
||||
view, viewErr = g.SetView(Controllers.Status.Name, -1, maxY-statusBarHeight-statusBarIndex, maxX, maxY-(statusBarIndex-1))
|
||||
if isNewView(viewErr, headerErr) {
|
||||
_ = Controllers.Status.Setup(view, nil)
|
||||
err = Controllers.Status.Setup(view, nil)
|
||||
if err != nil {
|
||||
logrus.Error("unable to setup status controller", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Filter Bar
|
||||
view, viewErr = g.SetView(Controllers.Filter.Name, len(Controllers.Filter.headerStr)-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1))
|
||||
header, headerErr = g.SetView(Controllers.Filter.Name+"header", -1, maxY-filterBarHeight-filterBarIndex, len(Controllers.Filter.headerStr), maxY-(filterBarIndex-1))
|
||||
if isNewView(viewErr, headerErr) {
|
||||
_ = Controllers.Filter.Setup(view, header)
|
||||
err = Controllers.Filter.Setup(view, header)
|
||||
if err != nil {
|
||||
logrus.Error("unable to setup filter controller", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update refreshes the state objects for future rendering.
|
||||
func Update() {
|
||||
func Update() error {
|
||||
for _, view := range Controllers.lookup {
|
||||
_ = view.Update()
|
||||
err := view.Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render flushes the state objects to the screen.
|
||||
func Render() {
|
||||
func Render() error {
|
||||
for _, view := range Controllers.lookup {
|
||||
if view.IsVisible() {
|
||||
_ = view.Render()
|
||||
err := view.Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// renderStatusOption formats key help bindings-to-title pairs.
|
||||
@ -321,7 +357,7 @@ func renderStatusOption(control, title string, selected bool) string {
|
||||
}
|
||||
|
||||
// Run is the UI entrypoint.
|
||||
func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) {
|
||||
func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) error {
|
||||
|
||||
Formatting.Selected = color.New(color.ReverseVideo, color.Bold).SprintFunc()
|
||||
Formatting.Header = color.New(color.Bold).SprintFunc()
|
||||
@ -335,20 +371,20 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) {
|
||||
var err error
|
||||
GlobalKeybindings.quit, err = keybinding.ParseAll(viper.GetString("keybinding.quit"))
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
GlobalKeybindings.toggleView, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-view"))
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
GlobalKeybindings.filterView, err = keybinding.ParseAll(viper.GetString("keybinding.filter-files"))
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return err
|
||||
}
|
||||
utils.SetUi(g)
|
||||
defer g.Close()
|
||||
@ -358,7 +394,11 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) {
|
||||
Controllers.Layer = NewLayerController("side", g, analysis.Layers)
|
||||
Controllers.lookup[Controllers.Layer.Name] = Controllers.Layer
|
||||
|
||||
Controllers.Tree = NewFileTreeController("main", g, filetree.StackTreeRange(analysis.RefTrees, 0, 0), analysis.RefTrees, cache)
|
||||
treeStack, err := filetree.StackTreeRange(analysis.RefTrees, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Controllers.Tree = NewFileTreeController("main", g, treeStack, analysis.RefTrees, cache)
|
||||
Controllers.lookup[Controllers.Tree.Name] = Controllers.Tree
|
||||
|
||||
Controllers.Status = NewStatusController("status", g)
|
||||
@ -381,15 +421,23 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) {
|
||||
// }
|
||||
|
||||
// perform the first update and render now that all resources have been loaded
|
||||
Update()
|
||||
Render()
|
||||
err = Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = Render()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := keyBindings(g); err != nil {
|
||||
logrus.Error("keybinding error: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
logrus.Error("main loop error: ", err)
|
||||
return err
|
||||
}
|
||||
utils.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user