From d1d4cca8ac807f0e7734ead4c16ec7701d9a10e0 Mon Sep 17 00:00:00 2001 From: Antoine Gelloz Date: Fri, 19 Apr 2024 16:32:37 +0200 Subject: [PATCH] wip --- Makefile | 2 +- go.mod | 4 -- go.sum | 12 ---- pkg/dag/dag.go | 11 ++-- pkg/dag/node.go | 10 +-- pkg/dag/printer.go | 142 ++++++++++++++++++++++++++++++++++++++++ pkg/dib/generate_dag.go | 28 +------- 7 files changed, 150 insertions(+), 59 deletions(-) create mode 100644 pkg/dag/printer.go diff --git a/Makefile b/Makefile index 244d849c..651fca33 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ install: ## Generate binary and copy it to $GOPATH/bin (equivalent to go install goreleaser build --clean --snapshot --single-target -o $(GOPATH)/bin/dib build: ## Build the CLI binary. - CGO_ENABLED=1 go build -o ./dist/dib ./cmd + CGO_ENABLED=0 go build -o ./dist/dib ./cmd docs: build ./dist/dib docgen diff --git a/go.mod b/go.mod index c40bd0c5..5f034b3d 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.27.11 github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 github.com/docker/cli v26.0.1+incompatible - github.com/goccy/go-graphviz v0.1.2 github.com/google/uuid v1.6.0 github.com/mholt/archiver/v3 v3.5.1 github.com/moby/patternmatcher v0.6.0 @@ -67,7 +66,6 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/fogleman/gg v1.3.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect @@ -76,7 +74,6 @@ require ( github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.2 // indirect github.com/google/btree v1.0.1 // indirect @@ -139,7 +136,6 @@ require ( go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/image v0.14.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/sys v0.18.0 // indirect diff --git a/go.sum b/go.sum index cd3b574b..8e288363 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,6 @@ github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMP github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= -github.com/corona10/goimagehash v1.0.2 h1:pUfB0LnsJASMPGEZLj7tGY251vF+qLGqOgEP4rUs6kA= -github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -115,8 +113,6 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -135,12 +131,8 @@ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/goccy/go-graphviz v0.1.2 h1:sWSJ6w13BCm/ZOUTHDVrdvbsxqN8yyzaFcHrH/hQ9Yg= -github.com/goccy/go-graphviz v0.1.2/go.mod h1:pMYpbAqJT10V8dzV1JN/g/wUlG/0imKPzn3ZsrchGCI= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 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= @@ -266,8 +258,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY= -github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -381,8 +371,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= -golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/pkg/dag/dag.go b/pkg/dag/dag.go index 7b23eed4..af869a1d 100644 --- a/pkg/dag/dag.go +++ b/pkg/dag/dag.go @@ -1,11 +1,8 @@ package dag import ( - "os" "sync" - "github.com/goccy/go-graphviz" - "github.com/goccy/go-graphviz/cgraph" "github.com/radiofrance/dib/internal/logger" "golang.org/x/sync/errgroup" "gopkg.in/yaml.v3" @@ -14,13 +11,13 @@ import ( // DAG represents a direct acyclic graph. type DAG struct { nodes []*Node // Root nodes of the graph. - GV *graphviz.Graphviz - CG *cgraph.Graph } func (d *DAG) Print() { - if err := d.GV.Render(d.CG, graphviz.XDOT, os.Stdout); err != nil { - logger.Fatalf(err.Error()) + for _, t := range d.nodes { + if err := DefaultTree.WithRoot(t).Render(); err != nil { + logger.Fatalf(err.Error()) + } } } diff --git a/pkg/dag/node.go b/pkg/dag/node.go index bd03f987..99e10458 100644 --- a/pkg/dag/node.go +++ b/pkg/dag/node.go @@ -3,7 +3,6 @@ package dag import ( "sync" - "github.com/goccy/go-graphviz/cgraph" "golang.org/x/sync/errgroup" ) @@ -22,20 +21,13 @@ type Node struct { parents []*Node children []*Node - - N *cgraph.Node } // NewNode creates a new instance of a Node. -func NewNode(image *Image, n ...*cgraph.Node) *Node { - var cNode *cgraph.Node - if len(n) > 0 { - cNode = n[0] - } +func NewNode(image *Image) *Node { return &Node{ Image: image, waitCond: sync.NewCond(&sync.Mutex{}), - N: cNode, } } diff --git a/pkg/dag/printer.go b/pkg/dag/printer.go new file mode 100644 index 00000000..5eabb456 --- /dev/null +++ b/pkg/dag/printer.go @@ -0,0 +1,142 @@ +package dag + +import ( + "io" + "strings" + + "github.com/pterm/pterm" +) + +// DefaultTree contains standards, which can be used to render a TreePrinter. +var DefaultTree = TreePrinter{ + TreeStyle: &pterm.ThemeDefault.TreeStyle, + TextStyle: &pterm.ThemeDefault.TreeTextStyle, + TopRightCornerString: "└", + HorizontalString: "─", + TopRightDownString: "├", + VerticalString: "│", + RightDownLeftString: "┬", + Indent: 2, +} + +// TreePrinter is able to render a list. +type TreePrinter struct { + Root *Node + TreeStyle *pterm.Style + TextStyle *pterm.Style + TopRightCornerString string + TopRightDownString string + HorizontalString string + VerticalString string + RightDownLeftString string + Indent int + Writer io.Writer +} + +// WithTreeStyle returns a new list with a specific tree style. +func (p TreePrinter) WithTreeStyle(style *pterm.Style) *TreePrinter { + p.TreeStyle = style + return &p +} + +// WithTopRightCornerString returns a new list with a specific TopRightCornerString. +func (p TreePrinter) WithTopRightCornerString(s string) *TreePrinter { + p.TopRightCornerString = s + return &p +} + +// WithTopRightDownStringOngoing returns a new list with a specific TopRightDownString. +func (p TreePrinter) WithTopRightDownStringOngoing(s string) *TreePrinter { + p.TopRightDownString = s + return &p +} + +// WithHorizontalString returns a new list with a specific HorizontalString. +func (p TreePrinter) WithHorizontalString(s string) *TreePrinter { + p.HorizontalString = s + return &p +} + +// WithVerticalString returns a new list with a specific VerticalString. +func (p TreePrinter) WithVerticalString(s string) *TreePrinter { + p.VerticalString = s + return &p +} + +// WithRoot returns a new list with a specific Root. +func (p TreePrinter) WithRoot(root *Node) *TreePrinter { + p.Root = root + return &p +} + +// WithIndent returns a new list with a specific amount of spacing between the levels. +// Indent must be at least 1. +func (p TreePrinter) WithIndent(indent int) *TreePrinter { + if indent < 1 { + indent = 1 + } + p.Indent = indent + return &p +} + +// Render prints the list to the terminal. +func (p TreePrinter) Render() error { + s, _ := p.Srender() + pterm.Fprintln(p.Writer, s) + + return nil +} + +// Srender renders the list as a string. +func (p TreePrinter) Srender() (string, error) { + if p.TreeStyle == nil { + p.TreeStyle = pterm.NewStyle() + } + if p.TextStyle == nil { + p.TextStyle = pterm.NewStyle() + } + + var result string + if p.Root.Image.Name != "" { + result += p.TextStyle.Sprint(p.Root.Image.Name) + "\n" + } + result += walkOverTree(p.Root.Children(), p, "") + return result, nil +} + +// walkOverTree is a recursive function, +// which analyzes a TreePrinter and connects the items with specific characters. +// Returns TreePrinter as string. +func walkOverTree(nodes []*Node, printer TreePrinter, prefix string) string { + var ret string + for nodeIndex, node := range nodes { + if len(nodes) > nodeIndex+1 { // if not last in nodes + if len(node.Children()) == 0 { // if there are no children + ret += prefix + printer.TreeStyle.Sprint(printer.TopRightDownString) + + strings.Repeat(printer.TreeStyle.Sprint(printer.HorizontalString), printer.Indent) + + printer.TextStyle.Sprint(node.Image.Name) + "\n" + } else { // if there are children + ret += prefix + printer.TreeStyle.Sprint(printer.TopRightDownString) + + strings.Repeat(printer.TreeStyle.Sprint(printer.HorizontalString), printer.Indent-1) + + printer.TreeStyle.Sprint(printer.RightDownLeftString) + + printer.TextStyle.Sprint(node.Image.Name) + "\n" + ret += walkOverTree(node.Children(), printer, + prefix+printer.TreeStyle.Sprint(printer.VerticalString)+strings.Repeat(" ", printer.Indent-1)) + } + } else if len(nodes) == nodeIndex+1 { // if last in nodes + if len(node.Children()) == 0 { // if there are no children + ret += prefix + printer.TreeStyle.Sprint(printer.TopRightCornerString) + + strings.Repeat(printer.TreeStyle.Sprint(printer.HorizontalString), printer.Indent) + + printer.TextStyle.Sprint(node.Image.Name) + "\n" + } else { // if there are children + ret += prefix + printer.TreeStyle.Sprint(printer.TopRightCornerString) + + strings.Repeat(printer.TreeStyle.Sprint(printer.HorizontalString), printer.Indent-1) + + printer.TreeStyle.Sprint(printer.RightDownLeftString) + + printer.TextStyle.Sprint(node.Image.Name) + "\n" + ret += walkOverTree(node.Children(), printer, + prefix+strings.Repeat(" ", printer.Indent)) + } + } + } + return ret +} diff --git a/pkg/dib/generate_dag.go b/pkg/dib/generate_dag.go index 85754f0d..392d2cb6 100644 --- a/pkg/dib/generate_dag.go +++ b/pkg/dib/generate_dag.go @@ -13,7 +13,6 @@ import ( "strings" "github.com/docker/cli/cli/command/image/build" - "github.com/goccy/go-graphviz" "github.com/moby/patternmatcher" "github.com/radiofrance/dib/internal/logger" "github.com/radiofrance/dib/pkg/dag" @@ -29,18 +28,6 @@ const ( // GenerateDAG discovers and parses all Dockerfiles at a given path, // and generates the DAG representing the relationships between images. func GenerateDAG(buildPath, registryPrefix, customHashListPath string, buildArgs map[string]string) (*dag.DAG, error) { - gGraph := graphviz.New() - cGraph, err := gGraph.Graph() - if err != nil { - logger.Fatalf(err.Error()) - } - defer func() { - if err := cGraph.Close(); err != nil { - logger.Fatalf(err.Error()) - } - gGraph.Close() - }() - var allFiles []string cache := make(map[string]*dag.Node) allParents := make(map[string][]dockerfile.ImageRef) @@ -89,11 +76,7 @@ func GenerateDAG(buildPath, registryPrefix, customHashListPath string, buildArgs img.IgnorePatterns = ignorePatterns allParents[img.Name] = dckfile.From - n, err := cGraph.CreateNode(img.Name) - if err != nil { - return err - } - cache[img.Name] = dag.NewNode(img, n) + cache[img.Name] = dag.NewNode(img) } return nil }); err != nil { @@ -120,18 +103,11 @@ func GenerateDAG(buildPath, registryPrefix, customHashListPath string, buildArgs continue } - _, err := cGraph.CreateEdge(fmt.Sprintf("%s -> %s", node.Image.Name, name), node.N, cache[name].N) - if err != nil { - return nil, err - } node.AddChild(cache[name]) } } - graph := &dag.DAG{ - GV: gGraph, - CG: cGraph, - } + graph := &dag.DAG{} // If an image has no parents in the DAG, we consider it a root image for name, img := range cache { if len(img.Parents()) == 0 {