Added debug panel; annotate filetree with changeinfo (#7)
This commit is contained in:
parent
acec670354
commit
e67734d38d
4
Makefile
4
Makefile
@ -3,9 +3,9 @@ BIN = die
|
||||
all: clean build
|
||||
|
||||
run: build
|
||||
./build/$(BIN)
|
||||
./build/$(BIN) die-test
|
||||
|
||||
build: deps
|
||||
build: #deps
|
||||
go build -o build/$(BIN) ./cmd/...
|
||||
|
||||
install: deps
|
||||
|
@ -1,7 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/wagoodman/docker-image-explorer/image"
|
||||
"github.com/wagoodman/docker-image-explorer/ui"
|
||||
)
|
||||
@ -11,15 +15,22 @@ const version = "v0.0.0"
|
||||
const author = "wagoodman"
|
||||
|
||||
func main() {
|
||||
os.Exit(run(os.Args))
|
||||
app := cli.NewApp()
|
||||
app.Name = "die"
|
||||
app.Usage = "Explore your docker images"
|
||||
app.Action = func(c *cli.Context) error {
|
||||
userImage := c.Args().Get(0)
|
||||
if userImage == "" {
|
||||
fmt.Println("No image argument given")
|
||||
os.Exit(1)
|
||||
}
|
||||
manifest, refTrees := image.InitializeData(userImage)
|
||||
ui.Run(manifest, refTrees)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func run(args []string) int {
|
||||
image.WriteImage()
|
||||
manifest, refTrees := image.InitializeData()
|
||||
|
||||
ui.Run(manifest, refTrees)
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FileChangeInfo struct {
|
||||
@ -22,6 +25,27 @@ const (
|
||||
Removed
|
||||
)
|
||||
|
||||
func NewFileChangeInfo(reader *tar.Reader, header *tar.Header, path string) FileChangeInfo {
|
||||
if header.Typeflag == tar.TypeDir {
|
||||
return FileChangeInfo{
|
||||
Path: path,
|
||||
Typeflag: header.Typeflag,
|
||||
MD5sum: [16]byte{},
|
||||
}
|
||||
}
|
||||
fileBytes := make([]byte, header.Size)
|
||||
_, err := reader.Read(fileBytes)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
return FileChangeInfo{
|
||||
Path: path,
|
||||
Typeflag: header.Typeflag,
|
||||
MD5sum: md5.Sum(fileBytes),
|
||||
DiffType: Unchanged,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DiffType) String() string {
|
||||
switch d {
|
||||
case Unchanged:
|
||||
|
@ -140,6 +140,7 @@ func (tree *FileTree) GetNode(path string) (*FileNode, error) {
|
||||
}
|
||||
|
||||
func (tree *FileTree) AddPath(path string, data *FileChangeInfo) (*FileNode, error) {
|
||||
// fmt.Printf("ADDPATH: %s %+v\n", path, data)
|
||||
nodeNames := strings.Split(path, "/")
|
||||
node := tree.Root
|
||||
for idx, name := range nodeNames {
|
||||
@ -172,7 +173,7 @@ func (tree *FileTree) RemovePath(path string) error {
|
||||
return node.Remove()
|
||||
}
|
||||
|
||||
func (tree *FileTree) compare(upper *FileTree) error {
|
||||
func (tree *FileTree) Compare(upper *FileTree) error {
|
||||
graft := func(node *FileNode) error {
|
||||
if node.IsWhiteout() {
|
||||
err := tree.MarkRemoved(node.Path())
|
||||
@ -183,14 +184,14 @@ func (tree *FileTree) compare(upper *FileTree) error {
|
||||
existingNode, _ := tree.GetNode(node.Path())
|
||||
if existingNode == nil {
|
||||
newNode, err := tree.AddPath(node.Path(), node.Data)
|
||||
fmt.Printf("added new node at %s\n", newNode.Path())
|
||||
// fmt.Printf("added new node at %s\n", newNode.Path())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot add new node %s: %v", node.Path(), err.Error())
|
||||
}
|
||||
newNode.AssignDiffType(Added)
|
||||
} else {
|
||||
diffType := existingNode.compare(node)
|
||||
fmt.Printf("found existing node at %s\n", existingNode.Path())
|
||||
// fmt.Printf("found existing node at %s\n", existingNode.Path())
|
||||
existingNode.deriveDiffType(diffType)
|
||||
}
|
||||
}
|
||||
@ -209,7 +210,7 @@ func (tree *FileTree) MarkRemoved(path string) error {
|
||||
|
||||
func StackRange(trees []*FileTree, index uint) *FileTree {
|
||||
tree := trees[1].Copy()
|
||||
for idx := uint(2); idx < index; idx++ {
|
||||
for idx := uint(1); idx <= index; idx++ {
|
||||
tree.Stack(trees[idx])
|
||||
}
|
||||
return tree
|
||||
|
@ -186,7 +186,7 @@ func TestCompareWithNoChanges(t *testing.T) {
|
||||
lowerTree.AddPath(value, &fakeData)
|
||||
upperTree.AddPath(value, &fakeData)
|
||||
}
|
||||
lowerTree.compare(upperTree)
|
||||
lowerTree.Compare(upperTree)
|
||||
asserter := func(n *FileNode) error {
|
||||
if n.Path() == "/" {
|
||||
return nil
|
||||
@ -232,7 +232,7 @@ func TestCompareWithAdds(t *testing.T) {
|
||||
upperTree.AddPath(value, &fakeData)
|
||||
}
|
||||
|
||||
lowerTree.compare(upperTree)
|
||||
lowerTree.Compare(upperTree)
|
||||
asserter := func(n *FileNode) error {
|
||||
|
||||
p := n.Path()
|
||||
@ -283,7 +283,7 @@ func TestCompareWithChanges(t *testing.T) {
|
||||
upperTree.AddPath(value, &fakeData)
|
||||
}
|
||||
|
||||
lowerTree.compare(upperTree)
|
||||
lowerTree.Compare(upperTree)
|
||||
asserter := func(n *FileNode) error {
|
||||
p := n.Path()
|
||||
if p == "/" {
|
||||
|
183
image/image.go
183
image/image.go
@ -1,12 +1,19 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"github.com/docker/docker/client"
|
||||
"fmt"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -16,24 +23,112 @@ func check(e error) {
|
||||
}
|
||||
}
|
||||
|
||||
type ImageManifest struct {
|
||||
Config string
|
||||
RepoTags []string
|
||||
Layers []string
|
||||
}
|
||||
|
||||
func saveImage(readCloser io.ReadCloser) {
|
||||
defer readCloser.Close()
|
||||
func NewManifest(reader *tar.Reader, header *tar.Header) ImageManifest {
|
||||
size := header.Size
|
||||
manifestBytes := make([]byte, size)
|
||||
_, err := reader.Read(manifestBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var m []ImageManifest
|
||||
err = json.Unmarshal(manifestBytes, &m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m[0]
|
||||
}
|
||||
|
||||
path := ".image"
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
os.Mkdir(path, 0755)
|
||||
func InitializeData(imageID string) (*ImageManifest, []*filetree.FileTree) {
|
||||
imageTarPath, tmpDir := saveImage(imageID)
|
||||
|
||||
f, err := os.Open(imageTarPath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fo, err := os.Create(".image/cache.tar")
|
||||
defer f.Close()
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
tarReader := tar.NewReader(f)
|
||||
targetName := "manifest.json"
|
||||
var manifest ImageManifest
|
||||
var layerMap map[string]*filetree.FileTree
|
||||
layerMap = make(map[string]*filetree.FileTree)
|
||||
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
name := header.Name
|
||||
if name == targetName {
|
||||
manifest = NewManifest(tarReader, header)
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
continue
|
||||
case tar.TypeReg:
|
||||
if strings.HasSuffix(name, "layer.tar") {
|
||||
tree := filetree.NewFileTree()
|
||||
tree.Name = name
|
||||
fileInfos := getFileList(tarReader, header)
|
||||
for _, element := range fileInfos {
|
||||
tree.AddPath(element.Path, &element)
|
||||
}
|
||||
layerMap[tree.Name] = tree
|
||||
}
|
||||
default:
|
||||
fmt.Printf("ERRG: unknown tar entry: %v: %s\n", header.Typeflag, name)
|
||||
}
|
||||
}
|
||||
var trees []*filetree.FileTree
|
||||
trees = make([]*filetree.FileTree, 0)
|
||||
for _, treeName := range manifest.Layers {
|
||||
trees = append(trees, layerMap[treeName])
|
||||
}
|
||||
|
||||
return &manifest, trees
|
||||
}
|
||||
|
||||
func saveImage(imageID string) (string, string) {
|
||||
ctx := context.Background()
|
||||
dockerClient, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
readCloser, err := dockerClient.ImageSave(ctx, []string{imageID})
|
||||
check(err)
|
||||
defer readCloser.Close()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "docker-image-explorer")
|
||||
check(err)
|
||||
|
||||
imageTarPath := filepath.Join(tmpDir, "image.tar")
|
||||
imageFile, err := os.Create(imageTarPath)
|
||||
check(err)
|
||||
|
||||
defer func() {
|
||||
if err := fo.Close(); err != nil {
|
||||
if err := imageFile.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
w := bufio.NewWriter(fo)
|
||||
imageWriter := bufio.NewWriter(imageFile)
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
@ -45,60 +140,50 @@ func saveImage(readCloser io.ReadCloser) {
|
||||
break
|
||||
}
|
||||
|
||||
if _, err := w.Write(buf[:n]); err != nil {
|
||||
if _, err := imageWriter.Write(buf[:n]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = w.Flush(); err != nil {
|
||||
if err = imageWriter.Flush(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return imageTarPath, tmpDir
|
||||
}
|
||||
|
||||
|
||||
func WriteImage() {
|
||||
ctx := context.Background()
|
||||
cli, err := client.NewEnvClient()
|
||||
func getFileList(parentReader *tar.Reader, h *tar.Header) []filetree.FileChangeInfo {
|
||||
var files []filetree.FileChangeInfo
|
||||
size := h.Size
|
||||
tarredBytes := make([]byte, size)
|
||||
_, err := parentReader.Read(tarredBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// imageID := "golang:alpine"
|
||||
imageID := "die-test:latest"
|
||||
|
||||
fmt.Println("Saving Image...")
|
||||
readCloser, err := cli.ImageSave(ctx, []string{imageID})
|
||||
check(err)
|
||||
saveImage(readCloser)
|
||||
|
||||
r := bytes.NewReader(tarredBytes)
|
||||
tarReader := tar.NewReader(r)
|
||||
for {
|
||||
inspect, _, err := cli.ImageInspectWithRaw(ctx, imageID)
|
||||
check(err)
|
||||
header, err := tarReader.Next()
|
||||
|
||||
history, err := cli.ImageHistory(ctx, imageID)
|
||||
check(err)
|
||||
|
||||
historyStr, err := json.MarshalIndent(history, "", " ")
|
||||
check(err)
|
||||
|
||||
layerStr := ""
|
||||
for idx, layer := range inspect.RootFS.Layers {
|
||||
prefix := "├── "
|
||||
if idx == len(inspect.RootFS.Layers)-1 {
|
||||
prefix = "└── "
|
||||
}
|
||||
layerStr += fmt.Sprintf("%s%s\n", prefix, layer)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Printf("Image: %s\nId: %s\nParent: %s\nLayers: %d\n%sHistory: %s\n", imageID, inspect.ID, inspect.Parent, len(inspect.RootFS.Layers), layerStr, historyStr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
name := header.Name
|
||||
|
||||
if inspect.Parent == "" {
|
||||
break
|
||||
} else {
|
||||
imageID = inspect.Parent
|
||||
switch header.Typeflag {
|
||||
case tar.TypeXGlobalHeader:
|
||||
fmt.Printf("ERRG: XGlobalHeader: %v: %s\n", header.Typeflag, name)
|
||||
case tar.TypeXHeader:
|
||||
fmt.Printf("ERRG: XHeader: %v: %s\n", header.Typeflag, name)
|
||||
default:
|
||||
files = append(files, filetree.NewFileChangeInfo(tarReader, header, name))
|
||||
}
|
||||
}
|
||||
fmt.Println("See './.image' for the cached image tar")
|
||||
return files
|
||||
}
|
||||
|
@ -1,166 +0,0 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
)
|
||||
|
||||
func InitializeData() (*Manifest, []*filetree.FileTree) {
|
||||
f, err := os.Open("./.image/cache.tar")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
tarReader := tar.NewReader(f)
|
||||
targetName := "manifest.json"
|
||||
var manifest Manifest
|
||||
var layerMap map[string]*filetree.FileTree
|
||||
layerMap = make(map[string]*filetree.FileTree)
|
||||
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
name := header.Name
|
||||
if name == targetName {
|
||||
manifest = handleManifest(tarReader, header)
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
continue
|
||||
case tar.TypeReg:
|
||||
|
||||
if strings.HasSuffix(name, "layer.tar") {
|
||||
fmt.Println("Containing:")
|
||||
tree := filetree.NewFileTree()
|
||||
tree.Name = name
|
||||
fmt.Printf("%s\n", tree.Name)
|
||||
fileInfos := getFileList(tarReader, header)
|
||||
for _, element := range fileInfos {
|
||||
tree.AddPath(element.Path, &element)
|
||||
}
|
||||
layerMap[tree.Name] = tree
|
||||
}
|
||||
default:
|
||||
fmt.Printf("%s : %c %s %s\n",
|
||||
"hmmm?",
|
||||
header.Typeflag,
|
||||
"in file",
|
||||
name,
|
||||
)
|
||||
}
|
||||
}
|
||||
var trees []*filetree.FileTree
|
||||
trees = make([]*filetree.FileTree, 0)
|
||||
for _, treeName := range manifest.Layers {
|
||||
trees = append(trees, layerMap[treeName])
|
||||
}
|
||||
|
||||
return &manifest, trees
|
||||
}
|
||||
|
||||
func getFileList(parentReader *tar.Reader, h *tar.Header) []filetree.FileChangeInfo {
|
||||
var files []filetree.FileChangeInfo
|
||||
size := h.Size
|
||||
tarredBytes := make([]byte, size)
|
||||
_, err := parentReader.Read(tarredBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r := bytes.NewReader(tarredBytes)
|
||||
tarReader := tar.NewReader(r)
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
name := header.Name
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
files = append(files, makeEntry(tarReader, header, name))
|
||||
case tar.TypeReg:
|
||||
files = append(files, makeEntry(tarReader, header, name))
|
||||
continue
|
||||
case tar.TypeSymlink:
|
||||
files = append(files, makeEntry(tarReader, header, name))
|
||||
default:
|
||||
fmt.Printf("%s : %c %s %s\n",
|
||||
"hmmm?",
|
||||
header.Typeflag,
|
||||
"in file",
|
||||
name,
|
||||
)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func makeEntry(r *tar.Reader, h *tar.Header, path string) filetree.FileChangeInfo {
|
||||
if h.Typeflag == tar.TypeDir {
|
||||
return filetree.FileChangeInfo{
|
||||
Path: path,
|
||||
Typeflag: h.Typeflag,
|
||||
MD5sum: [16]byte{},
|
||||
}
|
||||
}
|
||||
fileBytes := make([]byte, h.Size)
|
||||
_, err := r.Read(fileBytes)
|
||||
if err != nil && err != io.EOF {
|
||||
panic(err)
|
||||
}
|
||||
hash := md5.Sum(fileBytes)
|
||||
return filetree.FileChangeInfo{
|
||||
Path: path,
|
||||
Typeflag: h.Typeflag,
|
||||
MD5sum: hash,
|
||||
DiffType: filetree.Unchanged,
|
||||
}
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
Config string
|
||||
RepoTags []string
|
||||
Layers []string
|
||||
}
|
||||
|
||||
func handleManifest(r *tar.Reader, header *tar.Header) Manifest {
|
||||
size := header.Size
|
||||
manifestBytes := make([]byte, size)
|
||||
_, err := r.Read(manifestBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var m [1]Manifest
|
||||
err = json.Unmarshal(manifestBytes, &m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m[0]
|
||||
}
|
@ -7,14 +7,13 @@ import (
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
)
|
||||
|
||||
|
||||
type FileTreeView struct {
|
||||
Name string
|
||||
gui *gocui.Gui
|
||||
view *gocui.View
|
||||
Name string
|
||||
gui *gocui.Gui
|
||||
view *gocui.View
|
||||
TreeIndex uint
|
||||
Tree *filetree.FileTree
|
||||
RefTrees []*filetree.FileTree
|
||||
Tree *filetree.FileTree
|
||||
RefTrees []*filetree.FileTree
|
||||
}
|
||||
|
||||
func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree) (treeview *FileTreeView) {
|
||||
@ -55,9 +54,12 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mehh, this is just a bad method
|
||||
func (view *FileTreeView) reset(tree *filetree.FileTree) error {
|
||||
view.Tree = tree
|
||||
func (view *FileTreeView) setLayer(layerIndex uint) error {
|
||||
view.Tree = filetree.StackRange(view.RefTrees, layerIndex-1)
|
||||
view.Tree.Compare(view.RefTrees[layerIndex])
|
||||
v, _ := view.gui.View("debug")
|
||||
v.Clear()
|
||||
_, _ = fmt.Fprintln(v, view.RefTrees[layerIndex])
|
||||
view.view.SetCursor(0, 0)
|
||||
view.TreeIndex = 0
|
||||
return view.Render()
|
||||
|
@ -5,19 +5,17 @@ import (
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/wagoodman/docker-image-explorer/image"
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
)
|
||||
|
||||
|
||||
type LayerView struct {
|
||||
Name string
|
||||
gui *gocui.Gui
|
||||
view *gocui.View
|
||||
LayerIndex uint
|
||||
Manifest *image.Manifest
|
||||
Manifest *image.ImageManifest
|
||||
}
|
||||
|
||||
func NewLayerView(name string, gui *gocui.Gui, manifest *image.Manifest) (layerview *LayerView) {
|
||||
func NewLayerView(name string, gui *gocui.Gui, manifest *image.ImageManifest) (layerview *LayerView) {
|
||||
layerview = new(LayerView)
|
||||
|
||||
// populate main fields
|
||||
@ -38,10 +36,10 @@ func (view *LayerView) Setup(v *gocui.View) error {
|
||||
view.view.SelFgColor = gocui.ColorBlack
|
||||
|
||||
// set keybindings
|
||||
if err := view.gui.SetKeybinding("side", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
|
||||
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := view.gui.SetKeybinding("side", gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorUp() }); err != nil {
|
||||
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorUp() }); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -67,8 +65,7 @@ func (view *LayerView) CursorDown() error {
|
||||
CursorDown(view.gui, view.view)
|
||||
view.LayerIndex++
|
||||
view.Render()
|
||||
// this line is evil
|
||||
Views.Tree.reset(filetree.StackRange(Views.Tree.RefTrees, view.LayerIndex))
|
||||
Views.Tree.setLayer(view.LayerIndex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -79,7 +76,7 @@ func (view *LayerView) CursorUp() error {
|
||||
view.LayerIndex--
|
||||
view.Render()
|
||||
// this line is evil
|
||||
Views.Tree.reset(filetree.StackRange(Views.Tree.RefTrees, view.LayerIndex))
|
||||
Views.Tree.setLayer(view.LayerIndex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
18
ui/ui.go
18
ui/ui.go
@ -1,9 +1,10 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||
"log"
|
||||
"github.com/wagoodman/docker-image-explorer/image"
|
||||
)
|
||||
|
||||
@ -76,14 +77,15 @@ func keybindings(g *gocui.Gui) error {
|
||||
func layout(g *gocui.Gui) error {
|
||||
maxX, maxY := g.Size()
|
||||
splitCol := 50
|
||||
if view, err := g.SetView("side", -1, -1, splitCol, maxY); err != nil {
|
||||
debugCol := maxX - 100
|
||||
if view, err := g.SetView(Views.Layer.Name, -1, -1, splitCol, maxY); err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
Views.Layer.Setup(view)
|
||||
|
||||
}
|
||||
if view, err := g.SetView("main", splitCol, -1, maxX, maxY); err != nil {
|
||||
if view, err := g.SetView(Views.Tree.Name, splitCol, -1, debugCol, maxY); err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
@ -94,10 +96,16 @@ func layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := g.SetView("debug", debugCol, -1, maxX, maxY); err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Run(manifest *image.Manifest, refTrees []*filetree.FileTree) {
|
||||
func Run(manifest *image.ImageManifest, refTrees []*filetree.FileTree) {
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
if err != nil {
|
||||
@ -119,4 +127,4 @@ func Run(manifest *image.Manifest, refTrees []*filetree.FileTree) {
|
||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user