Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wip #522

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions cmd/graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"fmt"
"path"

"github.com/radiofrance/dib/internal/logger"
"github.com/radiofrance/dib/pkg/dib"
"github.com/spf13/cobra"
)

type GraphOpts struct {
BuildPath string `mapstructure:"build_path"`
RegistryURL string `mapstructure:"registry_url"`
PlaceholderTag string `mapstructure:"placeholder_tag"`
HashListFilePath string `mapstructure:"hash_list_file_path"`
}

// buildCmd represents the build command.
var graphCmd = &cobra.Command{
Use: "graph",
Short: "Compute the graph of images, and print it.",
Long: "Compute the graph of images, and print it.",
Run: func(cmd *cobra.Command, _ []string) {
bindPFlagsSnakeCase(cmd.Flags())

opts := GraphOpts{}
hydrateOptsFromViper(&opts)

if err := doGraph(opts); err != nil {
logger.Fatalf("graph command failed: %v", err)
}
},
}

func init() {
rootCmd.AddCommand(graphCmd)
}

func doGraph(opts GraphOpts) error {
workingDir, err := getWorkingDir()
if err != nil {
logger.Fatalf("failed to get current working directory: %v", err)
}

buildPath := path.Join(workingDir, opts.BuildPath)
logger.Infof("Building images in directory \"%s\"", buildPath)

logger.Debugf("Generate DAG")
graph, err := dib.GenerateDAG(buildPath, opts.RegistryURL, opts.HashListFilePath, map[string]string{})
if err != nil {
return fmt.Errorf("generating DAG: %w", err)
}
logger.Debugf("Generate DAG -- Done")

logger.Debugf("Print DAG")
graph.Print(opts.BuildPath)
logger.Debugf("Print DAG -- Done")
return nil
}
22 changes: 22 additions & 0 deletions pkg/dag/dag.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package dag

import (
"cmp"
"slices"
"strings"
"sync"

"github.com/radiofrance/dib/internal/logger"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v3"
)
Expand Down Expand Up @@ -203,3 +207,21 @@ func createUniqueVisitorErr(visitor NodeVisitorFuncErr) NodeVisitorFuncErr {

return uniqueVisitor
}

func sort(a, b *Node) int {
return cmp.Compare(strings.ToLower(a.Image.ShortName), strings.ToLower(b.Image.ShortName))
}

func (d *DAG) Print(name string) {
d.WalkInDepth(func(node *Node) {
slices.SortFunc(node.Children(), sort)
})
slices.SortFunc(d.nodes, sort)
rootNode := &Node{
Image: &Image{Name: name},
children: d.nodes,
}
if err := DefaultTree.WithRoot(rootNode).Render(); err != nil {
logger.Fatalf(err.Error())
}
}
8 changes: 4 additions & 4 deletions pkg/dag/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (n *Node) Parents() []*Node {
// walk applies the visitor func to the current node, then to every children nodes, recursively.
func (n *Node) walk(visitor NodeVisitorFunc) {
visitor(n)
for _, childNode := range n.children {
for _, childNode := range n.Children() {
childNode.walk(visitor)
}
}
Expand All @@ -63,7 +63,7 @@ func (n *Node) walkErr(visitor NodeVisitorFuncErr) error {
if err != nil {
return err
}
for _, childNode := range n.children {
for _, childNode := range n.Children() {
err = childNode.walkErr(visitor)
if err != nil {
return err
Expand All @@ -79,7 +79,7 @@ func (n *Node) walkAsyncErr(visitor NodeVisitorFuncErr) error {
errG.Go(func() error {
return visitor(n)
})
for _, childNode := range n.children {
for _, childNode := range n.Children() {
errG.Go(func() error {
return childNode.walkAsyncErr(visitor)
})
Expand All @@ -90,7 +90,7 @@ func (n *Node) walkAsyncErr(visitor NodeVisitorFuncErr) error {
// walkInDepth makes a depth-first recursive walk through the graph.
// It applies the visitor func to every children node, then to the current node itself.
func (n *Node) walkInDepth(visitor NodeVisitorFunc) {
for _, childNode := range n.children {
for _, childNode := range n.Children() {
childNode.walkInDepth(visitor)
}
visitor(n)
Expand Down
142 changes: 142 additions & 0 deletions pkg/dag/printer.go
Original file line number Diff line number Diff line change
@@ -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.ShortName) + "\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.ShortName) + "\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.ShortName) + "\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.ShortName) + "\n"
ret += walkOverTree(node.Children(), printer,
prefix+strings.Repeat(" ", printer.Indent))
}
}
}
return ret
}
Loading