Merge branch 'master' into format-fixes

This commit is contained in:
Alex Goodman 2019-07-21 15:44:52 -04:00 committed by GitHub
commit 9384069d76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 710 additions and 315 deletions

View File

@ -1,8 +1,10 @@
/.git
/.data
/.cover
/dist
/ui
/utils
/image
/cmd
/build
coverage.txt

1
.gitignore vendored
View File

@ -20,3 +20,4 @@
*.log
/dist
.cover
coverage.txt

26
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,26 @@
image: golang:1.12.5
cache:
paths:
- .cache
variables:
GOPATH: $CI_PROJECT_DIR/.cache
stages:
- setup
- validation
setup:
stage: setup
script:
- mkdir -p .cache
- go get ./...
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.17.1
validation:
stage: validation
before_script:
- export PATH="$GOPATH/bin:$PATH"
script:
- make ci

View File

@ -1,34 +0,0 @@
language: go
os:
- linux
- linux-ppc64le
go:
- '1.9.x'
- '1.10.x'
- '1.11.x'
- 'master'
# Skip the install step. Don't `go get` dependencies. Only build with the
# code in vendor/
install: true
matrix:
# It's ok if our code fails on unstable development versions of Go.
allow_failures:
- go: master
# Don't wait for tip tests to finish. Mark the test run green if the
# tests pass on the stable versions of Go.
fast_finish: true
notifications:
email: false
before_script:
- go get -t ./...
# Note: scripts always run to completion
script:
- make validate
- make test

View File

@ -14,23 +14,26 @@ run-large: build
build:
go build -o build/$(BIN)
release: test validate
release: test-coverage validate
./.scripts/tag.sh
goreleaser --rm-dist
install:
go install ./...
test: build
go test -cover -v ./...
ci: clean validate test-coverage
coverage: build
./.scripts/test.sh
test: build
go test -cover -v -race ./...
test-coverage: build
./.scripts/test-coverage.sh
validate:
grep -R 'const allowTestDataCapture = false' ui/
go vet ./...
@! gofmt -s -d -l . 2>&1 | grep -vE '^\.git/'
@! gofmt -s -l . 2>&1 | grep -vE '^\.git/' | grep -vE '^\.cache/'
golangci-lint run
lint: build
golint -set_exit_status $$(go list ./...)
@ -40,7 +43,6 @@ generate-test-data:
clean:
rm -rf build
rm -rf vendor
go clean
.PHONY: build install test lint clean release validate generate-test-data
.PHONY: build install test lint clean release validate generate-test-data test-coverage ci

View File

@ -1,6 +1,6 @@
# dive
[![Go Report Card](https://goreportcard.com/badge/github.com/wagoodman/dive)](https://goreportcard.com/report/github.com/wagoodman/dive)
[![Pipeline Status](https://api.travis-ci.org/wagoodman/dive.svg?branch=master)](https://travis-ci.org/wagoodman/dive)
[![Pipeline Status](https://gitlab.com/wagoodman/dive/badges/master/pipeline.svg)](https://gitlab.com/wagoodman/dive/pipelines?scope=branches&page=1)
**A tool for exploring a docker image, layer contents, and discovering ways to shrink your Docker image size.**
@ -147,7 +147,7 @@ or if you are running with a docker image:
```bash
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DOCKER_API_VERSION=1.37
-e DOCKER_API_VERSION=1.37 \
wagoodman/dive:latest <dive arguments...>
```
@ -189,6 +189,8 @@ Key Binding | Description
<kbd>Ctrl + M</kbd> | Filetree view: show/hide modified files
<kbd>Ctrl + U</kbd> | Filetree view: show/hide unmodified files
<kbd>Ctrl + B</kbd> | Filetree view: show/hide file attributes
<kbd>PageUp</kbd> | Filetree view: scroll up a page
<kbd>PageDown</kbd> | Filetree view: scroll down a page
## UI Configuration

View File

@ -20,14 +20,14 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) {
}
fmt.Println("No image argument given")
cmd.Help()
_ = cmd.Help()
utils.Exit(1)
}
userImage := args[0]
if userImage == "" {
fmt.Println("No image argument given")
cmd.Help()
_ = cmd.Help()
utils.Exit(1)
}

View File

@ -70,7 +70,7 @@ func initConfig() {
viper.SetDefault("keybinding.toggle-added-files", "ctrl+a")
viper.SetDefault("keybinding.toggle-removed-files", "ctrl+r")
viper.SetDefault("keybinding.toggle-modified-files", "ctrl+m")
viper.SetDefault("keybinding.toggle-unchanged-files", "ctrl+u")
viper.SetDefault("keybinding.toggle-unmodified-files", "ctrl+u")
viper.SetDefault("keybinding.page-up", "pgup")
viper.SetDefault("keybinding.page-down", "pgdn")

View File

@ -17,8 +17,6 @@ func (cache *TreeCache) Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTr
key := TreeCacheKey{bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop}
if value, exists := cache.cache[key]; exists {
return value
} else {
}
value := cache.buildTree(key)
cache.cache[key] = value

View File

@ -10,8 +10,8 @@ import (
)
const (
Unchanged DiffType = iota
Changed
Unmodified DiffType = iota
Modified
Added
Removed
)
@ -23,7 +23,7 @@ func NewNodeData() *NodeData {
return &NodeData{
ViewInfo: *NewViewInfo(),
FileInfo: FileInfo{},
DiffType: Unchanged,
DiffType: Unmodified,
}
}
@ -64,7 +64,10 @@ func getHashFromReader(reader io.Reader) uint64 {
break
}
h.Write(buf[:n])
_, err = h.Write(buf[:n])
if err != nil {
logrus.Panic(err)
}
}
return h.Sum64()
@ -126,19 +129,19 @@ func (data *FileInfo) Compare(other FileInfo) DiffType {
data.Mode == other.Mode &&
data.Uid == other.Uid &&
data.Gid == other.Gid {
return Unchanged
return Unmodified
}
}
return Changed
return Modified
}
// String of a DiffType
func (diff DiffType) String() string {
switch diff {
case Unchanged:
return "Unchanged"
case Changed:
return "Changed"
case Unmodified:
return "Unmodified"
case Modified:
return "Modified"
case Added:
return "Added"
case Removed:
@ -154,5 +157,5 @@ func (diff DiffType) merge(other DiffType) DiffType {
if diff == other {
return diff
}
return Changed
return Modified
}

View File

@ -10,23 +10,23 @@ func TestAssignDiffType(t *testing.T) {
if err != nil {
t.Errorf("Expected no error from fetching path. got: %v", err)
}
node.Data.DiffType = Changed
if tree.Root.Children["usr"].Data.DiffType != Changed {
node.Data.DiffType = Modified
if tree.Root.Children["usr"].Data.DiffType != Modified {
t.Fail()
}
}
func TestMergeDiffTypes(t *testing.T) {
a := Unchanged
b := Unchanged
a := Unmodified
b := Unmodified
merged := a.merge(b)
if merged != Unchanged {
if merged != Unmodified {
t.Errorf("Expected Unchaged (0) but got %v", merged)
}
a = Changed
b = Unchanged
a = Modified
b = Unmodified
merged = a.merge(b)
if merged != Changed {
if merged != Modified {
t.Errorf("Expected Unchaged (0) but got %v", merged)
}
}

View File

@ -4,19 +4,31 @@ import (
"testing"
)
func checkError(t *testing.T, err error, message string) {
if err != nil {
t.Errorf(message+": %+v", err)
}
}
func TestEfficency(t *testing.T) {
trees := make([]*FileTree, 3)
for idx := range trees {
trees[idx] = NewFileTree()
}
trees[0].AddPath("/etc/nginx/nginx.conf", FileInfo{Size: 2000})
trees[0].AddPath("/etc/nginx/public", FileInfo{Size: 3000})
_, _, err := trees[0].AddPath("/etc/nginx/nginx.conf", FileInfo{Size: 2000})
checkError(t, err, "could not setup test")
trees[1].AddPath("/etc/nginx/nginx.conf", FileInfo{Size: 5000})
trees[1].AddPath("/etc/athing", FileInfo{Size: 10000})
_, _, err = trees[0].AddPath("/etc/nginx/public", FileInfo{Size: 3000})
checkError(t, err, "could not setup test")
trees[2].AddPath("/etc/.wh.nginx", *BlankFileChangeInfo("/etc/.wh.nginx"))
_, _, err = trees[1].AddPath("/etc/nginx/nginx.conf", FileInfo{Size: 5000})
checkError(t, err, "could not setup test")
_, _, err = trees[1].AddPath("/etc/athing", FileInfo{Size: 10000})
checkError(t, err, "could not setup test")
_, _, err = trees[2].AddPath("/etc/.wh.nginx", *BlankFileChangeInfo("/etc/.wh.nginx"))
checkError(t, err, "could not setup test")
var expectedScore = 0.75
var expectedMatches = EfficiencySlice{
@ -50,7 +62,8 @@ func TestEfficency_ScratchImage(t *testing.T) {
trees[idx] = NewFileTree()
}
trees[0].AddPath("/nothing", FileInfo{Size: 0})
_, _, err := trees[0].AddPath("/nothing", FileInfo{Size: 0})
checkError(t, err, "could not setup test")
var expectedScore = 1.0
var expectedMatches = EfficiencySlice{}

View File

@ -18,10 +18,10 @@ const (
)
var diffTypeColor = map[DiffType]*color.Color{
Added: color.New(color.FgGreen),
Removed: color.New(color.FgRed),
Changed: color.New(color.FgYellow),
Unchanged: color.New(color.Reset),
Added: color.New(color.FgGreen),
Removed: color.New(color.FgRed),
Modified: color.New(color.FgYellow),
Unmodified: color.New(color.Reset),
}
// NewNode creates a new FileNode relative to the given parent node with a payload.
@ -101,7 +101,10 @@ func (node *FileNode) Remove() error {
return fmt.Errorf("cannot remove the tree root")
}
for _, child := range node.Children {
child.Remove()
err := child.Remove()
if err != nil {
return err
}
}
delete(node.Parent.Children, node.Name)
node.Tree.Size--
@ -290,7 +293,7 @@ func (node *FileNode) AssignDiffType(diffType DiffType) error {
// compare the current node against the given node, returning a definitive DiffType.
func (node *FileNode) compare(other *FileNode) DiffType {
if node == nil && other == nil {
return Unchanged
return Unmodified
}
if node == nil && other != nil {

View File

@ -56,7 +56,8 @@ func TestRemoveChild(t *testing.T) {
forth := two.AddChild("forth", FileInfo{})
two.AddChild("fifth", FileInfo{})
forth.Remove()
err := forth.Remove()
checkError(t, err, "unable to setup test")
expected, actual = 4, tree.Size
if expected != actual {
@ -67,7 +68,8 @@ func TestRemoveChild(t *testing.T) {
t.Errorf("Expected 'forth' node to be deleted.")
}
two.Remove()
err = two.Remove()
checkError(t, err, "unable to setup test")
expected, actual = 2, tree.Size
if expected != actual {
@ -113,7 +115,7 @@ func TestIsWhiteout(t *testing.T) {
func TestDiffTypeFromAddedChildren(t *testing.T) {
tree := NewFileTree()
node, _, _ := tree.AddPath("/usr", *BlankFileChangeInfo("/usr"))
node.Data.DiffType = Unchanged
node.Data.DiffType = Unmodified
node, _, _ = tree.AddPath("/usr/bin", *BlankFileChangeInfo("/usr/bin"))
node.Data.DiffType = Added
@ -121,37 +123,42 @@ func TestDiffTypeFromAddedChildren(t *testing.T) {
node, _, _ = tree.AddPath("/usr/bin2", *BlankFileChangeInfo("/usr/bin2"))
node.Data.DiffType = Removed
tree.Root.Children["usr"].deriveDiffType(Unchanged)
err := tree.Root.Children["usr"].deriveDiffType(Unmodified)
checkError(t, err, "unable to setup test")
if tree.Root.Children["usr"].Data.DiffType != Changed {
t.Errorf("Expected Changed but got %v", tree.Root.Children["usr"].Data.DiffType)
if tree.Root.Children["usr"].Data.DiffType != Modified {
t.Errorf("Expected Modified but got %v", tree.Root.Children["usr"].Data.DiffType)
}
}
func TestDiffTypeFromRemovedChildren(t *testing.T) {
tree := NewFileTree()
node, _, _ := tree.AddPath("/usr", *BlankFileChangeInfo("/usr"))
_, _, _ = tree.AddPath("/usr", *BlankFileChangeInfo("/usr"))
info1 := BlankFileChangeInfo("/usr/.wh.bin")
node, _, _ = tree.AddPath("/usr/.wh.bin", *info1)
node, _, _ := tree.AddPath("/usr/.wh.bin", *info1)
node.Data.DiffType = Removed
info2 := BlankFileChangeInfo("/usr/.wh.bin2")
node, _, _ = tree.AddPath("/usr/.wh.bin2", *info2)
node.Data.DiffType = Removed
tree.Root.Children["usr"].deriveDiffType(Unchanged)
err := tree.Root.Children["usr"].deriveDiffType(Unmodified)
checkError(t, err, "unable to setup test")
if tree.Root.Children["usr"].Data.DiffType != Changed {
t.Errorf("Expected Changed but got %v", tree.Root.Children["usr"].Data.DiffType)
if tree.Root.Children["usr"].Data.DiffType != Modified {
t.Errorf("Expected Modified but got %v", tree.Root.Children["usr"].Data.DiffType)
}
}
func TestDirSize(t *testing.T) {
tree1 := NewFileTree()
tree1.AddPath("/etc/nginx/public1", FileInfo{Size: 100})
tree1.AddPath("/etc/nginx/thing1", FileInfo{Size: 200})
tree1.AddPath("/etc/nginx/public3/thing2", FileInfo{Size: 300})
_, _, err := tree1.AddPath("/etc/nginx/public1", FileInfo{Size: 100})
checkError(t, err, "unable to setup test")
_, _, err = tree1.AddPath("/etc/nginx/thing1", FileInfo{Size: 200})
checkError(t, err, "unable to setup test")
_, _, err = tree1.AddPath("/etc/nginx/public3/thing2", FileInfo{Size: 300})
checkError(t, err, "unable to setup test")
node, _ := tree1.GetNode("/etc/nginx")
expected, actual := "---------- 0:0 600 B ", node.MetadataString()

View File

@ -242,6 +242,11 @@ func (tree *FileTree) AddPath(path string, data FileInfo) (*FileNode, []*FileNod
if node.Children[name] != nil {
node = node.Children[name]
} else {
// don't add paths that should be deleted
if strings.HasPrefix(name, doubleWhiteoutPrefix) {
return nil, addedNodes, nil
}
// don't attach the payload. The payload is destined for the
// Path's end node, not any intermediary node.
node = node.AddChild(name, FileInfo{})
@ -249,7 +254,7 @@ func (tree *FileTree) AddPath(path string, data FileInfo) (*FileNode, []*FileNod
if node == nil {
// the child could not be added
return node, addedNodes, fmt.Errorf(fmt.Sprintf("could not add child node '%s'", name))
return node, addedNodes, fmt.Errorf(fmt.Sprintf("could not add child node: '%s' (path:'%s')", name, path))
}
}
@ -326,9 +331,15 @@ func (tree *FileTree) CompareAndMark(upper *FileTree) error {
// take note of the comparison results on each note in the owning tree.
for _, pair := range modifications {
if pair.final > 0 {
pair.lowerNode.AssignDiffType(pair.final)
} else if pair.lowerNode.Data.DiffType == Unchanged {
pair.lowerNode.deriveDiffType(pair.tentative)
err = pair.lowerNode.AssignDiffType(pair.final)
if err != nil {
return err
}
} else if pair.lowerNode.Data.DiffType == Unmodified {
err = pair.lowerNode.deriveDiffType(pair.tentative)
if err != nil {
return err
}
}
// persist the upper's payload on the owning tree

View File

@ -87,12 +87,30 @@ func TestString(t *testing.T) {
func TestStringBetween(t *testing.T) {
tree := NewFileTree()
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
tree.AddPath("/etc/nginx/public", FileInfo{})
tree.AddPath("/var/run/systemd", FileInfo{})
tree.AddPath("/var/run/bashful", FileInfo{})
tree.AddPath("/tmp", FileInfo{})
tree.AddPath("/tmp/nonsense", FileInfo{})
_, _, err := tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/etc/nginx/public", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/systemd", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp/nonsense", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
expected :=
` public
@ -109,12 +127,30 @@ func TestStringBetween(t *testing.T) {
func TestAddPath(t *testing.T) {
tree := NewFileTree()
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
tree.AddPath("/etc/nginx/public", FileInfo{})
tree.AddPath("/var/run/systemd", FileInfo{})
tree.AddPath("/var/run/bashful", FileInfo{})
tree.AddPath("/tmp", FileInfo{})
tree.AddPath("/tmp/nonsense", FileInfo{})
_, _, err := tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/etc/nginx/public", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/systemd", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp/nonsense", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
expected :=
` etc
@ -136,17 +172,65 @@ func TestAddPath(t *testing.T) {
}
func TestAddWhiteoutPath(t *testing.T) {
tree := NewFileTree()
node, _, err := tree.AddPath("usr/local/lib/python3.7/site-packages/pip/.wh..wh..opq", FileInfo{})
if err != nil {
t.Errorf("expected no error but got: %v", err)
}
if node != nil {
t.Errorf("expected node to be nil, but got: %v", node)
}
expected :=
` usr
local
lib
python3.7
site-packages
pip
`
actual := tree.String(false)
if expected != actual {
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
}
}
func TestRemovePath(t *testing.T) {
tree := NewFileTree()
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
tree.AddPath("/etc/nginx/public", FileInfo{})
tree.AddPath("/var/run/systemd", FileInfo{})
tree.AddPath("/var/run/bashful", FileInfo{})
tree.AddPath("/tmp", FileInfo{})
tree.AddPath("/tmp/nonsense", FileInfo{})
_, _, err := tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/etc/nginx/public", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/systemd", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp/nonsense", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
tree.RemovePath("/var/run/bashful")
tree.RemovePath("/tmp")
err = tree.RemovePath("/var/run/bashful")
if err != nil {
t.Errorf("could not setup test: %v", err)
}
err = tree.RemovePath("/tmp")
if err != nil {
t.Errorf("could not setup test: %v", err)
}
expected :=
` etc
@ -173,24 +257,57 @@ func TestStack(t *testing.T) {
tree1 := NewFileTree()
tree1.AddPath("/etc/nginx/public", FileInfo{})
tree1.AddPath(payloadKey, FileInfo{})
tree1.AddPath("/var/run/bashful", FileInfo{})
tree1.AddPath("/tmp", FileInfo{})
tree1.AddPath("/tmp/nonsense", FileInfo{})
_, _, err := tree1.AddPath("/etc/nginx/public", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree1.AddPath(payloadKey, FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree1.AddPath("/var/run/bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree1.AddPath("/tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree1.AddPath("/tmp/nonsense", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
tree2 := NewFileTree()
// add new files
tree2.AddPath("/etc/nginx/nginx.conf", FileInfo{})
_, _, err = tree2.AddPath("/etc/nginx/nginx.conf", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
// modify current files
tree2.AddPath(payloadKey, payloadValue)
_, _, err = tree2.AddPath(payloadKey, payloadValue)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
// whiteout the following files
tree2.AddPath("/var/run/.wh.bashful", FileInfo{})
tree2.AddPath("/.wh.tmp", FileInfo{})
_, _, err = tree2.AddPath("/var/run/.wh.bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree2.AddPath("/.wh.tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
// ignore opaque whiteout files entirely
tree2.AddPath("/.wh..wh..opq", FileInfo{})
node, _, err := tree2.AddPath("/.wh..wh..opq", FileInfo{})
if err != nil {
t.Errorf("expected no error on whiteout file add, but got %v", err)
}
if node != nil {
t.Errorf("expected no node on whiteout file add, but got %v", node)
}
err := tree1.Stack(tree2)
err = tree1.Stack(tree2)
if err != nil {
t.Errorf("Could not stack refTrees: %v", err)
@ -206,12 +323,12 @@ func TestStack(t *testing.T) {
systemd
`
node, err := tree1.GetNode(payloadKey)
node, err = tree1.GetNode(payloadKey)
if err != nil {
t.Errorf("Expected '%s' to still exist, but it doesn't", payloadKey)
}
if node.Data.FileInfo.Path != payloadValue.Path {
if node == nil || node.Data.FileInfo.Path != payloadValue.Path {
t.Errorf("Expected '%s' value to be %+v but got %+v", payloadKey, payloadValue, node.Data.FileInfo)
}
@ -225,15 +342,39 @@ func TestStack(t *testing.T) {
func TestCopy(t *testing.T) {
tree := NewFileTree()
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
tree.AddPath("/etc/nginx/public", FileInfo{})
tree.AddPath("/var/run/systemd", FileInfo{})
tree.AddPath("/var/run/bashful", FileInfo{})
tree.AddPath("/tmp", FileInfo{})
tree.AddPath("/tmp/nonsense", FileInfo{})
_, _, err := tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/etc/nginx/public", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/systemd", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp/nonsense", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
tree.RemovePath("/var/run/bashful")
tree.RemovePath("/tmp")
err = tree.RemovePath("/var/run/bashful")
if err != nil {
t.Errorf("could not setup test: %v", err)
}
err = tree.RemovePath("/tmp")
if err != nil {
t.Errorf("could not setup test: %v", err)
}
expected :=
` etc
@ -265,20 +406,29 @@ func TestCompareWithNoChanges(t *testing.T) {
TypeFlag: 1,
hash: 123,
}
lowerTree.AddPath(value, fakeData)
upperTree.AddPath(value, fakeData)
_, _, err := lowerTree.AddPath(value, fakeData)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = upperTree.AddPath(value, fakeData)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
err := lowerTree.CompareAndMark(upperTree)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
lowerTree.CompareAndMark(upperTree)
asserter := func(n *FileNode) error {
if n.Path() == "/" {
return nil
}
if (n.Data.DiffType) != Unchanged {
if (n.Data.DiffType) != Unmodified {
t.Errorf("Expecting node at %s to have DiffType unchanged, but had %v", n.Path(), n.Data.DiffType)
}
return nil
}
err := lowerTree.VisitDepthChildFirst(asserter, nil)
err = lowerTree.VisitDepthChildFirst(asserter, nil)
if err != nil {
t.Error(err)
}
@ -291,19 +441,25 @@ func TestCompareWithAdds(t *testing.T) {
upperPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin", "/usr/bin/bash", "/a/new/path"}
for _, value := range lowerPaths {
lowerTree.AddPath(value, FileInfo{
_, _, err := lowerTree.AddPath(value, FileInfo{
Path: value,
TypeFlag: 1,
hash: 123,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
for _, value := range upperPaths {
upperTree.AddPath(value, FileInfo{
_, _, err := upperTree.AddPath(value, FileInfo{
Path: value,
TypeFlag: 1,
hash: 123,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
failedAssertions := []error{}
@ -321,11 +477,11 @@ func TestCompareWithAdds(t *testing.T) {
failedAssertions = append(failedAssertions, err)
}
} else if stringInSlice(p, []string{"/usr/bin", "/usr"}) {
if err := AssertDiffType(n, Changed); err != nil {
if err := AssertDiffType(n, Modified); err != nil {
failedAssertions = append(failedAssertions, err)
}
} else {
if err := AssertDiffType(n, Unchanged); err != nil {
if err := AssertDiffType(n, Unmodified); err != nil {
failedAssertions = append(failedAssertions, err)
}
}
@ -351,39 +507,51 @@ func TestCompareWithChanges(t *testing.T) {
changedPaths := []string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
for _, value := range changedPaths {
lowerTree.AddPath(value, FileInfo{
_, _, err := lowerTree.AddPath(value, FileInfo{
Path: value,
TypeFlag: 1,
hash: 123,
})
upperTree.AddPath(value, FileInfo{
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = upperTree.AddPath(value, FileInfo{
Path: value,
TypeFlag: 1,
hash: 456,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
chmodPath := "/etc/non-data-change"
lowerTree.AddPath(chmodPath, FileInfo{
_, _, err := lowerTree.AddPath(chmodPath, FileInfo{
Path: chmodPath,
TypeFlag: 1,
hash: 123,
Mode: 0,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
upperTree.AddPath(chmodPath, FileInfo{
_, _, err = upperTree.AddPath(chmodPath, FileInfo{
Path: chmodPath,
TypeFlag: 1,
hash: 123,
Mode: 1,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
changedPaths = append(changedPaths, chmodPath)
chownPath := "/etc/non-data-change-2"
lowerTree.AddPath(chmodPath, FileInfo{
_, _, err = lowerTree.AddPath(chmodPath, FileInfo{
Path: chownPath,
TypeFlag: 1,
hash: 123,
@ -391,8 +559,11 @@ func TestCompareWithChanges(t *testing.T) {
Gid: 0,
Uid: 0,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
upperTree.AddPath(chmodPath, FileInfo{
_, _, err = upperTree.AddPath(chmodPath, FileInfo{
Path: chownPath,
TypeFlag: 1,
hash: 123,
@ -400,27 +571,34 @@ func TestCompareWithChanges(t *testing.T) {
Gid: 12,
Uid: 12,
})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
changedPaths = append(changedPaths, chownPath)
lowerTree.CompareAndMark(upperTree)
err = lowerTree.CompareAndMark(upperTree)
if err != nil {
t.Errorf("unable to compare and mark: %+v", err)
}
failedAssertions := []error{}
asserter := func(n *FileNode) error {
p := n.Path()
if p == "/" {
return nil
} else if stringInSlice(p, changedPaths) {
if err := AssertDiffType(n, Changed); err != nil {
if err := AssertDiffType(n, Modified); err != nil {
failedAssertions = append(failedAssertions, err)
}
} else {
if err := AssertDiffType(n, Unchanged); err != nil {
if err := AssertDiffType(n, Unmodified); err != nil {
failedAssertions = append(failedAssertions, err)
}
}
return nil
}
err := lowerTree.VisitDepthChildFirst(asserter, nil)
err = lowerTree.VisitDepthChildFirst(asserter, nil)
if err != nil {
t.Errorf("Expected no errors when visiting nodes, got: %+v", err)
}
@ -446,7 +624,10 @@ func TestCompareWithRemoves(t *testing.T) {
TypeFlag: 1,
hash: 123,
}
lowerTree.AddPath(value, fakeData)
_, _, err := lowerTree.AddPath(value, fakeData)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
for _, value := range upperPaths {
@ -455,10 +636,16 @@ func TestCompareWithRemoves(t *testing.T) {
TypeFlag: 1,
hash: 123,
}
upperTree.AddPath(value, fakeData)
_, _, err := upperTree.AddPath(value, fakeData)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
lowerTree.CompareAndMark(upperTree)
err := lowerTree.CompareAndMark(upperTree)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
failedAssertions := []error{}
asserter := func(n *FileNode) error {
p := n.Path()
@ -469,17 +656,17 @@ func TestCompareWithRemoves(t *testing.T) {
failedAssertions = append(failedAssertions, err)
}
} else if stringInSlice(p, []string{"/usr", "/root"}) {
if err := AssertDiffType(n, Changed); err != nil {
if err := AssertDiffType(n, Modified); err != nil {
failedAssertions = append(failedAssertions, err)
}
} else {
if err := AssertDiffType(n, Unchanged); err != nil {
if err := AssertDiffType(n, Unmodified); err != nil {
failedAssertions = append(failedAssertions, err)
}
}
return nil
}
err := lowerTree.VisitDepthChildFirst(asserter, nil)
err = lowerTree.VisitDepthChildFirst(asserter, nil)
if err != nil {
t.Errorf("Expected no errors when visiting nodes, got: %+v", err)
}
@ -495,15 +682,39 @@ func TestCompareWithRemoves(t *testing.T) {
func TestStackRange(t *testing.T) {
tree := NewFileTree()
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
tree.AddPath("/etc/nginx/public", FileInfo{})
tree.AddPath("/var/run/systemd", FileInfo{})
tree.AddPath("/var/run/bashful", FileInfo{})
tree.AddPath("/tmp", FileInfo{})
tree.AddPath("/tmp/nonsense", FileInfo{})
_, _, err := tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/etc/nginx/public", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/systemd", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/var/run/bashful", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
_, _, err = tree.AddPath("/tmp/nonsense", FileInfo{})
if err != nil {
t.Errorf("could not setup test: %v", err)
}
tree.RemovePath("/var/run/bashful")
tree.RemovePath("/tmp")
err = tree.RemovePath("/var/run/bashful")
if err != nil {
t.Errorf("could not setup test: %v", err)
}
err = tree.RemovePath("/tmp")
if err != nil {
t.Errorf("could not setup test: %v", err)
}
lowerTree := NewFileTree()
upperTree := NewFileTree()
@ -516,7 +727,10 @@ func TestStackRange(t *testing.T) {
TypeFlag: 1,
hash: 123,
}
lowerTree.AddPath(value, fakeData)
_, _, err = lowerTree.AddPath(value, fakeData)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
for _, value := range upperPaths {
@ -525,7 +739,10 @@ func TestStackRange(t *testing.T) {
TypeFlag: 1,
hash: 456,
}
upperTree.AddPath(value, fakeData)
_, _, err = upperTree.AddPath(value, fakeData)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
trees := []*FileTree{lowerTree, upperTree, tree}
StackTreeRange(trees, 0, 2)
@ -548,12 +765,18 @@ func TestRemoveOnIterate(t *testing.T) {
}
}
tree.VisitDepthChildFirst(func(node *FileNode) error {
err := tree.VisitDepthChildFirst(func(node *FileNode) error {
if node.Data.ViewInfo.Hidden {
tree.RemovePath(node.Path())
err := tree.RemovePath(node.Path())
if err != nil {
t.Errorf("could not setup test: %v", err)
}
}
return nil
}, nil)
if err != nil {
t.Errorf("could not setup test: %v", err)
}
expected :=
` usr

12
go.mod
View File

@ -2,7 +2,6 @@ module github.com/wagoodman/dive
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/Microsoft/go-winio v0.4.11 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/cespare/xxhash v1.1.0
@ -13,32 +12,25 @@ require (
github.com/docker/go-units v0.3.3 // indirect
github.com/dustin/go-humanize v1.0.0
github.com/fatih/color v1.7.0
github.com/gogo/protobuf v1.1.1 // indirect
github.com/google/go-cmp v0.2.0 // indirect
github.com/golangci/golangci-lint v1.17.1 // indirect
github.com/google/uuid v1.1.0
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/mux v1.6.2 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jroimartin/gocui v0.4.0
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/mitchellh/go-homedir v1.0.0
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/phayes/permbits v0.0.0-20180830030258-59f2482cd460
github.com/pkg/errors v0.8.0 // indirect
github.com/sergi/go-diff v1.0.0
github.com/sirupsen/logrus v1.2.0
github.com/spf13/cobra v0.0.3
github.com/spf13/viper v1.2.1
github.com/wagoodman/keybinding v0.0.0-20181213133715-6a824da6df05
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 // indirect
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sys v0.0.0-20181116161606-93218def8b18 // indirect
golang.org/x/net v0.0.0-20190313220215-9f648a60d977
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
google.golang.org/grpc v1.16.0 // indirect
gotest.tools v2.2.0+incompatible // indirect

157
go.sum
View File

@ -1,10 +1,14 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2 h1:HTOmFEEYrWi4MW5ZKUx6xfeyM10Sx3kQF65xiQJMPYA=
github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@ -21,54 +25,145 @@ github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540 h1:7CU1IXBpPvxpQ/NqJrpuMXMHAw+FB2vfqtRF8tgW9fw=
github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6 h1:i2jIkQFb8RG45DuQs+ElyROY848cSJIoIkBM+7XXypA=
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196 h1:9rtVlONXLF1rJZzvLt4tfOXtnAFUEhxCJ64Ibzj6ECo=
github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98 h1:ir6/L2ZOJfFrJlOTsuf/hlzdPuUwXV/VzkSlgS6f1vs=
github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.17.1 h1:lc8Hf9GPCjIr0hg3S/xhvFT1+Hydass8F1xchr8jkME=
github.com/golangci/golangci-lint v1.17.1/go.mod h1:+5sJSl2h3aly+fpmL2meSP8CaSKua2E4Twi9LPy7b1g=
github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547 h1:qMomh8bv+kDazm1dSLZ9S3zZ2PJZMHL4ilfBjxFOlmI=
github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU=
github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc h1:XRFao922N8F3EcIXBSNX8Iywk+GI0dxD/8FicMX2D/c=
github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217 h1:r7vyX+SN24x6+5AnpnrRn/bdwBb7U+McZqCHOVtXDuk=
github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8=
github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I=
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663 h1:Ri1EhipkbhWsffPJ3IPlrb4SkTOPa2PfRXp3jchBczw=
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed h1:bAVGG6B+R5qpSylrrA+BAMrzYkdAoiTaKPVxRB+4cyM=
github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/phayes/permbits v0.0.0-20180830030258-59f2482cd460 h1:B9xJsGjeteSbA5LYAmW9KF9/jQcmrJkmpgVWsqRxc0k=
@ -76,51 +171,113 @@ github.com/phayes/permbits v0.0.0-20180830030258-59f2482cd460/go.mod h1:3uODdxMg
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M=
github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb h1:lI9ufgFfvuqRctP9Ny8lDDLbSWCMxBPletcSqrnyFYM=
github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/wagoodman/keybinding v0.0.0-20181213133715-6a824da6df05 h1:YMcRwVDe8DLDZ/vrhuImCfqjjG/+gZs6SF61DDQkL/8=
github.com/wagoodman/keybinding v0.0.0-20181213133715-6a824da6df05/go.mod h1:gXFkc2sM2o06uzn5Lgo6Ql76uweGdxNfeAlFyKiHAdk=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 h1:kkXA53yGe04D0adEYJwEVQjeBppL01Exg+fnMjfUraU=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116161606-93218def8b18 h1:Wh+XCfg3kNpjhdq2LXrsiOProjtQZKme5XUx7VcxwAw=
golang.org/x/sys v0.0.0-20181116161606-93218def8b18/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd h1:7E3PabyysDSEjnaANKBgums/hyvMI/HoHQ50qZEzTrg=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34 h1:B1LAOfRqg2QUyCdzfjf46quTSYUTAK5OCwbh6pljHbM=
mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@ -97,7 +97,10 @@ func (image *dockerImageAnalyzer) Fetch() (io.ReadCloser, error) {
if err != nil {
// don't use the API, the CLI has more informative output
fmt.Println("Image not available locally. Trying to pull '" + image.id + "'...")
utils.RunDockerCmd("pull", image.id)
err = utils.RunDockerCmd("pull", image.id)
if err != nil {
return nil, err
}
}
readCloser, err := image.client.ImageSave(ctx, []string{image.id})
@ -243,7 +246,10 @@ func (image *dockerImageAnalyzer) processLayerTar(name string, layerIdx uint, re
for _, element := range fileInfos {
tree.FileSize += uint64(element.Size)
tree.AddPath(element.Path, element)
_, _, err := tree.AddPath(element.Path, element)
if err != nil {
return err
}
}
image.layerMap[tree.Name] = tree

View File

@ -43,10 +43,7 @@ func (ci *Evaluator) LoadConfig(configFile string) error {
func (ci *Evaluator) isRuleEnabled(rule Rule) bool {
value := ci.Config.GetString(rule.Key())
if value == "disabled" {
return false
}
return true
return value != "disabled"
}
func (ci *Evaluator) Evaluate(analysis *image.AnalysisResult) bool {

View File

@ -63,10 +63,7 @@ func (controller *DetailsController) Setup(v *gocui.View, header *gocui.View) er
// IsVisible indicates if the details view pane is currently initialized.
func (controller *DetailsController) IsVisible() bool {
if controller == nil {
return false
}
return true
return controller != nil
}
// CursorDown moves the cursor down in the details pane (currently indicates nothing).
@ -124,23 +121,23 @@ 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)))
_, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(layerHeaderStr, false)))
// update contents
controller.view.Clear()
fmt.Fprintln(controller.view, Formatting.Header("Digest: ")+currentLayer.Id())
_, _ = fmt.Fprintln(controller.view, Formatting.Header("Digest: ")+currentLayer.Id())
// TODO: add back in with controller model
// fmt.Fprintln(view.view, Formatting.Header("Tar ID: ")+currentLayer.TarId())
fmt.Fprintln(controller.view, Formatting.Header("Command:"))
fmt.Fprintln(controller.view, currentLayer.Command())
_, _ = fmt.Fprintln(controller.view, Formatting.Header("Command:"))
_, _ = fmt.Fprintln(controller.view, currentLayer.Command())
fmt.Fprintln(controller.view, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false)))
_, _ = fmt.Fprintln(controller.view, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false)))
fmt.Fprintln(controller.view, imageSizeStr)
fmt.Fprintln(controller.view, wastedSpaceStr)
fmt.Fprintln(controller.view, effStr+"\n")
_, _ = fmt.Fprintln(controller.view, imageSizeStr)
_, _ = fmt.Fprintln(controller.view, wastedSpaceStr)
_, _ = fmt.Fprintln(controller.view, effStr+"\n")
fmt.Fprintln(controller.view, inefficiencyReport)
_, _ = fmt.Fprintln(controller.view, inefficiencyReport)
return nil
})
return nil

View File

@ -36,7 +36,7 @@ type FileTreeController struct {
keybindingToggleAdded []keybinding.Key
keybindingToggleRemoved []keybinding.Key
keybindingToggleModified []keybinding.Key
keybindingToggleUnchanged []keybinding.Key
keybindingToggleUnmodified []keybinding.Key
keybindingPageDown []keybinding.Key
keybindingPageUp []keybinding.Key
}
@ -81,9 +81,13 @@ func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree,
logrus.Error(err)
}
controller.keybindingToggleUnchanged, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-unchanged-files"))
// support legacy behavior first, then use default behavior
controller.keybindingToggleUnmodified, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-unchanged-files"))
if err != nil {
logrus.Error(err)
controller.keybindingToggleUnmodified, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-unmodified-files"))
if err != nil {
logrus.Error(err)
}
}
controller.keybindingPageUp, err = keybinding.ParseAll(viper.GetString("keybinding.page-up"))
@ -163,35 +167,32 @@ func (controller *FileTreeController) Setup(v *gocui.View, header *gocui.View) e
}
}
for _, key := range controller.keybindingToggleModified {
if err := controller.gui.SetKeybinding(controller.Name, key.Value, key.Modifier, func(*gocui.Gui, *gocui.View) error { return controller.toggleShowDiffType(filetree.Changed) }); err != nil {
if err := controller.gui.SetKeybinding(controller.Name, key.Value, key.Modifier, func(*gocui.Gui, *gocui.View) error { return controller.toggleShowDiffType(filetree.Modified) }); err != nil {
return err
}
}
for _, key := range controller.keybindingToggleUnchanged {
if err := controller.gui.SetKeybinding(controller.Name, key.Value, key.Modifier, func(*gocui.Gui, *gocui.View) error { return controller.toggleShowDiffType(filetree.Unchanged) }); err != nil {
for _, key := range controller.keybindingToggleUnmodified {
if err := controller.gui.SetKeybinding(controller.Name, key.Value, key.Modifier, func(*gocui.Gui, *gocui.View) error { return controller.toggleShowDiffType(filetree.Unmodified) }); err != nil {
return err
}
}
_, height := controller.view.Size()
controller.vm.Setup(0, height)
controller.Update()
controller.Render()
_ = controller.Update()
_ = controller.Render()
return nil
}
// IsVisible indicates if the file tree view pane is currently initialized
func (controller *FileTreeController) IsVisible() bool {
if controller == nil {
return false
}
return true
return controller != nil
}
// resetCursor moves the cursor back to the top of the buffer and translates to the top of the buffer.
func (controller *FileTreeController) resetCursor() {
controller.view.SetCursor(0, 0)
_ = controller.view.SetCursor(0, 0)
controller.vm.resetCursor()
}
@ -203,7 +204,7 @@ func (controller *FileTreeController) setTreeByLayer(bottomTreeStart, bottomTree
}
// controller.resetCursor()
controller.Update()
_ = controller.Update()
return controller.Render()
}
@ -235,7 +236,7 @@ func (controller *FileTreeController) CursorLeft() error {
if err != nil {
return err
}
controller.Update()
_ = controller.Update()
return controller.Render()
}
@ -245,7 +246,7 @@ func (controller *FileTreeController) CursorRight() error {
if err != nil {
return err
}
controller.Update()
_ = controller.Update()
return controller.Render()
}
@ -268,9 +269,9 @@ func (controller *FileTreeController) PageUp() error {
}
// getAbsPositionNode determines the selected screen cursor's location in the file tree, returning the selected FileNode.
func (controller *FileTreeController) getAbsPositionNode() (node *filetree.FileNode) {
return controller.vm.getAbsPositionNode(filterRegex())
}
// func (controller *FileTreeController) getAbsPositionNode() (node *filetree.FileNode) {
// return controller.vm.getAbsPositionNode(filterRegex())
// }
// toggleCollapse will collapse/expand the selected FileNode.
func (controller *FileTreeController) toggleCollapse() error {
@ -278,7 +279,7 @@ func (controller *FileTreeController) toggleCollapse() error {
if err != nil {
return err
}
controller.Update()
_ = controller.Update()
return controller.Render()
}
@ -291,7 +292,7 @@ func (controller *FileTreeController) toggleCollapseAll() error {
if controller.vm.CollapseAll {
controller.resetCursor()
}
controller.Update()
_ = controller.Update()
return controller.Render()
}
@ -336,7 +337,7 @@ func filterRegex() *regexp.Regexp {
// onLayoutChange is called by the UI framework to inform the view-model of the new screen dimensions
func (controller *FileTreeController) onLayoutChange(resized bool) error {
controller.Update()
_ = controller.Update()
if resized {
return controller.Render()
}
@ -378,12 +379,12 @@ func (controller *FileTreeController) Render() error {
headerStr += fmt.Sprintf(filetree.AttributeFormat+" %s", "P", "ermission", "UID:GID", "Size", "Filetree")
}
fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false)))
_, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false)))
// update the contents
controller.view.Clear()
controller.vm.Render()
fmt.Fprint(controller.view, controller.vm.mainBuf.String())
_ = controller.vm.Render()
_, _ = fmt.Fprint(controller.view, controller.vm.mainBuf.String())
return nil
})
@ -396,7 +397,7 @@ func (controller *FileTreeController) KeyHelp() string {
renderStatusOption(controller.keybindingToggleCollapseAll[0].String(), "Collapse all dir", false) +
renderStatusOption(controller.keybindingToggleAdded[0].String(), "Added", !controller.vm.HiddenDiffTypes[filetree.Added]) +
renderStatusOption(controller.keybindingToggleRemoved[0].String(), "Removed", !controller.vm.HiddenDiffTypes[filetree.Removed]) +
renderStatusOption(controller.keybindingToggleModified[0].String(), "Modified", !controller.vm.HiddenDiffTypes[filetree.Changed]) +
renderStatusOption(controller.keybindingToggleUnchanged[0].String(), "Unmodified", !controller.vm.HiddenDiffTypes[filetree.Unchanged]) +
renderStatusOption(controller.keybindingToggleModified[0].String(), "Modified", !controller.vm.HiddenDiffTypes[filetree.Modified]) +
renderStatusOption(controller.keybindingToggleUnmodified[0].String(), "Unmodified", !controller.vm.HiddenDiffTypes[filetree.Unmodified]) +
renderStatusOption(controller.keybindingToggleAttributes[0].String(), "Attributes", controller.vm.ShowAttributes)
}

View File

@ -54,10 +54,10 @@ func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree
treeViewModel.HiddenDiffTypes[filetree.Added] = true
case "removed":
treeViewModel.HiddenDiffTypes[filetree.Removed] = true
case "changed":
treeViewModel.HiddenDiffTypes[filetree.Changed] = true
case "unchanged":
treeViewModel.HiddenDiffTypes[filetree.Unchanged] = true
case "modified":
treeViewModel.HiddenDiffTypes[filetree.Modified] = true
case "unmodified":
treeViewModel.HiddenDiffTypes[filetree.Unmodified] = true
default:
utils.PrintAndExit(fmt.Sprintf("unknown diff.hide value: %s", t))
}
@ -87,10 +87,7 @@ func (vm *FileTreeViewModel) bufferIndexUpperBound() int {
// IsVisible indicates if the file tree view pane is currently initialized
func (vm *FileTreeViewModel) IsVisible() bool {
if vm == nil {
return false
}
return true
return vm != nil
}
// resetCursor moves the cursor back to the top of the buffer and translates to the top of the buffer.
@ -358,10 +355,8 @@ func (vm *FileTreeViewModel) toggleAttributes() error {
}
// toggleShowDiffType will show/hide the selected DiffType in the filetree pane.
func (vm *FileTreeViewModel) toggleShowDiffType(diffType filetree.DiffType) error {
func (vm *FileTreeViewModel) toggleShowDiffType(diffType filetree.DiffType) {
vm.HiddenDiffTypes[diffType] = !vm.HiddenDiffTypes[diffType]
return nil
}
// Update refreshes the state objects for future rendering.
@ -396,7 +391,10 @@ func (vm *FileTreeViewModel) Update(filterRegex *regexp.Regexp, width, height in
vm.ViewTree = vm.ModelTree.Copy()
err = vm.ViewTree.VisitDepthParentFirst(func(node *filetree.FileNode) error {
if node.Data.ViewInfo.Hidden {
vm.ViewTree.RemovePath(node.Path())
err1 := vm.ViewTree.RemovePath(node.Path())
if err1 != nil {
return err1
}
}
return nil
}, nil)

View File

@ -219,9 +219,10 @@ func TestFileTreePageDown(t *testing.T) {
width, height := 100, 10
vm.Setup(0, height)
vm.ShowAttributes = true
vm.Update(nil, width, height)
err := vm.Update(nil, width, height)
checkError(t, err, "unable to update")
err := vm.PageDown()
err = vm.PageDown()
checkError(t, err, "unable to page down")
err = vm.PageDown()
@ -241,9 +242,10 @@ func TestFileTreePageUp(t *testing.T) {
vm.ShowAttributes = true
// these operations have a render step for intermediate results, which require at least one update to be done first
vm.Update(nil, width, height)
err := vm.Update(nil, width, height)
checkError(t, err, "unable to update")
err := vm.PageDown()
err = vm.PageDown()
checkError(t, err, "unable to page down")
err = vm.PageDown()
@ -320,16 +322,13 @@ func TestFileTreeHideAddedRemovedModified(t *testing.T) {
}
// hide added files
err = vm.toggleShowDiffType(filetree.Added)
checkError(t, err, "unable hide added files")
vm.toggleShowDiffType(filetree.Added)
// hide modified files
err = vm.toggleShowDiffType(filetree.Changed)
checkError(t, err, "unable hide added files")
vm.toggleShowDiffType(filetree.Modified)
// hide removed files
err = vm.toggleShowDiffType(filetree.Removed)
checkError(t, err, "unable hide added files")
vm.toggleShowDiffType(filetree.Removed)
runTestCase(t, vm, width, height, nil)
}
@ -352,8 +351,7 @@ func TestFileTreeHideUnmodified(t *testing.T) {
}
// hide unmodified files
err = vm.toggleShowDiffType(filetree.Unchanged)
checkError(t, err, "unable hide added files")
vm.toggleShowDiffType(filetree.Unmodified)
runTestCase(t, vm, width, height, nil)
}
@ -376,8 +374,7 @@ func TestFileTreeHideTypeWithFilter(t *testing.T) {
}
// hide added files
err = vm.toggleShowDiffType(filetree.Added)
checkError(t, err, "unable hide added files")
vm.toggleShowDiffType(filetree.Added)
regex, err := regexp.Compile("saved")
if err != nil {

View File

@ -48,9 +48,7 @@ func (controller *FilterController) Setup(v *gocui.View, header *gocui.View) err
controller.header.Wrap = false
controller.header.Frame = false
controller.Render()
return nil
return controller.Render()
}
// IsVisible indicates if the filter view pane is currently initialized
@ -89,8 +87,8 @@ func (controller *FilterController) Edit(v *gocui.View, key gocui.Key, ch rune,
v.EditDelete(true)
}
if Controllers.Tree != nil {
Controllers.Tree.Update()
Controllers.Tree.Render()
_ = Controllers.Tree.Update()
_ = Controllers.Tree.Render()
}
}
@ -103,9 +101,9 @@ func (controller *FilterController) Update() error {
func (controller *FilterController) Render() error {
controller.gui.Update(func(g *gocui.Gui) error {
// render the header
fmt.Fprintln(controller.header, Formatting.Header(controller.headerStr))
_, err := fmt.Fprintln(controller.header, Formatting.Header(controller.headerStr))
return nil
return err
})
return nil
}

View File

@ -136,10 +136,7 @@ func (controller *LayerController) height() uint {
// IsVisible indicates if the layer view pane is currently initialized.
func (controller *LayerController) IsVisible() bool {
if controller == nil {
return false
}
return true
return controller != nil
}
// PageDown moves to next page putting the cursor on top
@ -149,13 +146,12 @@ func (controller *LayerController) PageDown() error {
if targetLayerIndex > len(controller.Layers) {
step -= targetLayerIndex - (len(controller.Layers) - 1)
targetLayerIndex = controller.LayerIndex + step
}
if step > 0 {
err := CursorStep(controller.gui, controller.view, step)
if err == nil {
controller.SetCursor(controller.LayerIndex + step)
return controller.SetCursor(controller.LayerIndex + step)
}
}
return nil
@ -168,13 +164,12 @@ func (controller *LayerController) PageUp() error {
if targetLayerIndex < 0 {
step += targetLayerIndex
targetLayerIndex = controller.LayerIndex - step
}
if step > 0 {
err := CursorStep(controller.gui, controller.view, -step)
if err == nil {
controller.SetCursor(controller.LayerIndex - step)
return controller.SetCursor(controller.LayerIndex - step)
}
}
return nil
@ -185,7 +180,7 @@ func (controller *LayerController) CursorDown() error {
if controller.LayerIndex < len(controller.Layers) {
err := CursorDown(controller.gui, controller.view)
if err == nil {
controller.SetCursor(controller.LayerIndex + 1)
return controller.SetCursor(controller.LayerIndex + 1)
}
}
return nil
@ -196,7 +191,7 @@ func (controller *LayerController) CursorUp() error {
if controller.LayerIndex > 0 {
err := CursorUp(controller.gui, controller.view)
if err == nil {
controller.SetCursor(controller.LayerIndex - 1)
return controller.SetCursor(controller.LayerIndex - 1)
}
}
return nil
@ -205,11 +200,14 @@ func (controller *LayerController) CursorUp() error {
// SetCursor resets the cursor and orients the file tree view based on the given layer index.
func (controller *LayerController) SetCursor(layer int) error {
controller.LayerIndex = layer
Controllers.Tree.setTreeByLayer(controller.getCompareIndexes())
Controllers.Details.Render()
controller.Render()
err := Controllers.Tree.setTreeByLayer(controller.getCompareIndexes())
if err != nil {
return err
}
return nil
_ = Controllers.Details.Render()
return controller.Render()
}
// currentLayer returns the Layer object currently selected.
@ -286,7 +284,7 @@ func (controller *LayerController) Render() error {
headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2))
// headerStr += fmt.Sprintf("Cmp "+image.LayerFormat, "Layer Digest", "Size", "Command")
headerStr += fmt.Sprintf("Cmp"+image.LayerFormat, "Size", "Command")
fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false)))
_, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false)))
// update contents
controller.view.Clear()
@ -298,9 +296,9 @@ func (controller *LayerController) Render() error {
compareBar := controller.renderCompareBar(idx)
if idx == controller.LayerIndex {
fmt.Fprintln(controller.view, compareBar+" "+Formatting.Selected(layerStr))
_, _ = fmt.Fprintln(controller.view, compareBar+" "+Formatting.Selected(layerStr))
} else {
fmt.Fprintln(controller.view, compareBar+" "+layerStr)
_, _ = fmt.Fprintln(controller.view, compareBar+" "+layerStr)
}
}

View File

@ -34,17 +34,12 @@ func (controller *StatusController) Setup(v *gocui.View, header *gocui.View) err
controller.view = v
controller.view.Frame = false
controller.Render()
return nil
return controller.Render()
}
// IsVisible indicates if the status view pane is currently initialized.
func (controller *StatusController) IsVisible() bool {
if controller == nil {
return false
}
return true
return controller != nil
}
// CursorDown moves the cursor down in the details pane (currently indicates nothing).
@ -66,7 +61,7 @@ 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)))
_, _ = fmt.Fprintln(controller.view, controller.KeyHelp()+Controllers.lookup[controller.gui.CurrentView().Name()].KeyHelp()+Formatting.StatusNormal("▏"+strings.Repeat(" ", 1000)))
return nil
})

View File

@ -2,7 +2,6 @@ package ui
import (
"errors"
"fmt"
"github.com/fatih/color"
"github.com/jroimartin/gocui"
@ -20,17 +19,17 @@ const debug = false
// var onExit func()
// debugPrint writes the given string to the debug pane (if the debug pane is enabled)
func debugPrint(s string) {
if debug && Controllers.Tree != nil && Controllers.Tree.gui != nil {
v, _ := Controllers.Tree.gui.View("debug")
if v != nil {
if len(v.BufferLines()) > 20 {
v.Clear()
}
_, _ = fmt.Fprintln(v, s)
}
}
}
// func debugPrint(s string) {
// if Controllers.Tree != nil && Controllers.Tree.gui != nil {
// v, _ := Controllers.Tree.gui.View("debug")
// if v != nil {
// if len(v.BufferLines()) > 20 {
// v.Clear()
// }
// _, _ = fmt.Fprintln(v, s)
// }
// }
// }
// Formatting defines standard functions for formatting UI sections.
var Formatting struct {
@ -89,7 +88,10 @@ func toggleView(g *gocui.Gui, v *gocui.View) (err error) {
func toggleFilterView(g *gocui.Gui, v *gocui.View) error {
// delete all user input from the tree view
Controllers.Filter.view.Clear()
Controllers.Filter.view.SetCursor(0, 0)
err := Controllers.Filter.view.SetCursor(0, 0)
if err != nil {
return err
}
// toggle hiding
Controllers.Filter.hidden = !Controllers.Filter.hidden
@ -102,7 +104,7 @@ func toggleFilterView(g *gocui.Gui, v *gocui.View) error {
Update()
Render()
} else {
toggleView(g, v)
return toggleView(g, v)
}
return nil
@ -125,7 +127,7 @@ func CursorStep(g *gocui.Gui, v *gocui.View, step int) error {
// if there isn't a next line
line, err := v.Line(cy + step)
if err != nil {
// todo: handle error
return err
}
if len(line) == 0 {
return errors.New("unable to move the cursor, empty line")
@ -177,7 +179,7 @@ func isNewView(errs ...error) bool {
if err == nil {
return false
}
if err != nil && err != gocui.ErrUnknownView {
if err != gocui.ErrUnknownView {
return false
}
}
@ -245,20 +247,20 @@ 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)
_ = Controllers.Layer.Setup(view, header)
if _, err = g.SetCurrentView(Controllers.Layer.Name); err != nil {
return err
}
// since we are selecting the view, we should rerender to indicate it is selected
Controllers.Layer.Render()
_ = Controllers.Layer.Render()
}
// 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)
_ = Controllers.Details.Setup(view, header)
}
// Filetree
@ -269,21 +271,21 @@ 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)
_ = Controllers.Tree.Setup(view, header)
}
Controllers.Tree.onLayoutChange(resized)
_ = 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)
_ = Controllers.Status.Setup(view, nil)
}
// 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)
_ = Controllers.Filter.Setup(view, header)
}
return nil
@ -292,7 +294,7 @@ func layout(g *gocui.Gui) error {
// Update refreshes the state objects for future rendering.
func Update() {
for _, view := range Controllers.lookup {
view.Update()
_ = view.Update()
}
}
@ -300,7 +302,7 @@ func Update() {
func Render() {
for _, view := range Controllers.lookup {
if view.IsVisible() {
view.Render()
_ = view.Render()
}
}
}