From d71e22fc02421858838b50adc882aa277fc26eb4 Mon Sep 17 00:00:00 2001 From: b4b4r07 Date: Thu, 17 Feb 2022 04:50:48 +0900 Subject: [PATCH 1/6] Add dependency and depends-on field --- go.mod | 1 + go.sum | 2 + pkg/config/config.go | 49 +++++++++++++++++--- pkg/config/gist.go | 6 +++ pkg/config/github.go | 6 +++ pkg/config/http.go | 6 +++ pkg/config/local.go | 6 +++ pkg/config/package.go | 2 + pkg/dependency/graph.go | 99 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 pkg/dependency/graph.go diff --git a/go.mod b/go.mod index 8c4e168..20e0955 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/Netflix/go-expect v0.0.0-20190729225929-0e00d9168667 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/creativeprojects/go-selfupdate v0.6.1 + github.com/deckarep/golang-set v1.8.0 github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/fatih/color v1.10.0 github.com/frankban/quicktest v1.4.1 // indirect diff --git a/go.sum b/go.sum index 96ab476..235a702 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/creativeprojects/go-selfupdate v0.6.1/go.mod h1:Zr5CB9GEee179eQpho5k6 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= diff --git a/pkg/config/config.go b/pkg/config/config.go index 0643697..43971b1 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" + "github.com/b4b4r07/afx/pkg/dependency" "github.com/b4b4r07/afx/pkg/errors" "github.com/go-playground/validator/v10" "github.com/goccy/go-yaml" @@ -58,6 +59,7 @@ func Read(path string) (Config, error) { defer f.Close() validate := validator.New() + // validate.RegisterValidation("in-packages", validatePackageName) d := yaml.NewDecoder( bufio.NewReader(f), yaml.DisallowUnknownField(), @@ -72,7 +74,8 @@ func Read(path string) (Config, error) { } func parse(cfg Config) []Package { - var pkgs []Package + table := map[string]Package{} + for _, pkg := range cfg.GitHub { // TODO: Remove? if pkg.HasReleaseBlock() { @@ -94,17 +97,33 @@ func parse(cfg Config) []Package { pkg.Command = &Command{Link: defaultLinks} } } - pkgs = append(pkgs, pkg) + table[pkg.GetName()] = pkg } for _, pkg := range cfg.Gist { - pkgs = append(pkgs, pkg) + table[pkg.GetName()] = pkg } for _, pkg := range cfg.Local { - pkgs = append(pkgs, pkg) + table[pkg.GetName()] = pkg } for _, pkg := range cfg.HTTP { - pkgs = append(pkgs, pkg) + table[pkg.GetName()] = pkg + } + + var graph dependency.Graph + for name, pkg := range table { + graph = append(graph, dependency.NewNode(name, pkg.GetDependsOn()...)) + } + + resolved, err := dependency.Resolve(graph) + if err != nil { + // TODO: how do we return value + log.Printf("[ERROR] failed to resolve dependency graph: %v\n", err) + } + + var pkgs []Package + for _, node := range resolved { + pkgs = append(pkgs, table[node.Name]) } return pkgs @@ -146,3 +165,23 @@ func WalkDir(path string) ([]string, error) { } return files, nil } + +func validatePackageName(fl validator.FieldLevel) bool { + ls := []string{ + "google-cloud-sdk", + } + // https://golang.hotexamples.com/jp/examples/reflect/Value/Slice/golang-value-slice-method-examples.html + for _, v := range ls { + field := fl.Field() + slice := field.Slice(0, field.Len()) + if field.Len() == 0 { + return true + } + for i := 0; i < field.Len(); i++ { + if v == slice.Index(i).String() { + return true + } + } + } + return false +} diff --git a/pkg/config/gist.go b/pkg/config/gist.go index 5978b4b..b886986 100644 --- a/pkg/config/gist.go +++ b/pkg/config/gist.go @@ -23,6 +23,8 @@ type Gist struct { Plugin *Plugin `yaml:"plugin"` Command *Command `yaml:"command"` + + DependsOn []string `yaml:"depends-on"` } func NewGist(owner, id string) (Gist, error) { @@ -188,3 +190,7 @@ func (c Gist) GetName() string { func (c Gist) GetHome() string { return filepath.Join(os.Getenv("HOME"), ".afx", "gist.github.com", c.Owner, c.ID) } + +func (c Gist) GetDependsOn() []string { + return c.DependsOn +} diff --git a/pkg/config/github.go b/pkg/config/github.go index 4e0ed2f..f3daab4 100644 --- a/pkg/config/github.go +++ b/pkg/config/github.go @@ -39,6 +39,8 @@ type GitHub struct { Plugin *Plugin `yaml:"plugin"` Command *Command `yaml:"command"` // validate:"required_with=Release" + + DependsOn []string `yaml:"depends-on"` } type GitHubOption struct { @@ -599,3 +601,7 @@ func (c GitHub) GetName() string { func (c GitHub) GetHome() string { return filepath.Join(os.Getenv("HOME"), ".afx", "github.com", c.Owner, c.Repo) } + +func (c GitHub) GetDependsOn() []string { + return c.DependsOn +} diff --git a/pkg/config/http.go b/pkg/config/http.go index 3e06211..5b8bef8 100644 --- a/pkg/config/http.go +++ b/pkg/config/http.go @@ -25,6 +25,8 @@ type HTTP struct { Plugin *Plugin `yaml:"plugin"` Command *Command `yaml:"command"` + + DependsOn []string `yaml:"depends-on"` } // Init is @@ -213,3 +215,7 @@ func (c HTTP) GetHome() string { u, _ := url.Parse(c.URL) return filepath.Join(os.Getenv("HOME"), ".afx", u.Host, filepath.Dir(u.Path)) } + +func (c HTTP) GetDependsOn() []string { + return c.DependsOn +} diff --git a/pkg/config/local.go b/pkg/config/local.go index 500ac35..73401bd 100644 --- a/pkg/config/local.go +++ b/pkg/config/local.go @@ -16,6 +16,8 @@ type Local struct { Plugin *Plugin `yaml:"plugin"` Command *Command `yaml:"command"` + + DependsOn []string `yaml:"depends-on"` } // Init is @@ -80,3 +82,7 @@ func (c Local) GetName() string { func (c Local) GetHome() string { return expandTilda(os.ExpandEnv(c.Directory)) } + +func (c Local) GetDependsOn() []string { + return c.DependsOn +} diff --git a/pkg/config/package.go b/pkg/config/package.go index 07cbd01..54eb6df 100644 --- a/pkg/config/package.go +++ b/pkg/config/package.go @@ -28,6 +28,8 @@ type Handler interface { HasCommandBlock() bool GetPluginBlock() Plugin GetCommandBlock() Command + + GetDependsOn() []string } // Package is an interface related to package itself diff --git a/pkg/dependency/graph.go b/pkg/dependency/graph.go new file mode 100644 index 0000000..b126451 --- /dev/null +++ b/pkg/dependency/graph.go @@ -0,0 +1,99 @@ +package dependency + +// http://dnaeon.github.io/dependency-graph-resolution-algorithm-in-go/ + +import ( + "errors" + "fmt" + + mapset "github.com/deckarep/golang-set" +) + +// Node represents a single node in the graph with it's dependencies +type Node struct { + // Name of the node + Name string + + // Dependencies of the node + Deps []string +} + +// NewNode creates a new node +func NewNode(name string, deps ...string) *Node { + n := &Node{ + Name: name, + Deps: deps, + } + + return n +} + +type Graph []*Node + +// Displays the dependency graph +func displayGraph(graph Graph) { + for _, node := range graph { + for _, dep := range node.Deps { + fmt.Printf("%s -> %s\n", node.Name, dep) + } + } +} + +// Resolves the dependency graph +func Resolve(graph Graph) (Graph, error) { + // A map containing the node names and the actual node object + nodeNames := make(map[string]*Node) + + // A map containing the nodes and their dependencies + nodeDependencies := make(map[string]mapset.Set) + + // Populate the maps + for _, node := range graph { + nodeNames[node.Name] = node + + dependencySet := mapset.NewSet() + for _, dep := range node.Deps { + dependencySet.Add(dep) + } + nodeDependencies[node.Name] = dependencySet + } + + // Iteratively find and remove nodes from the graph which have no dependencies. + // If at some point there are still nodes in the graph and we cannot find + // nodes without dependencies, that means we have a circular dependency + var resolved Graph + for len(nodeDependencies) != 0 { + // Get all nodes from the graph which have no dependencies + readySet := mapset.NewSet() + for name, deps := range nodeDependencies { + if deps.Cardinality() == 0 { + readySet.Add(name) + } + } + + // If there aren't any ready nodes, then we have a cicular dependency + if readySet.Cardinality() == 0 { + var g Graph + for name := range nodeDependencies { + g = append(g, nodeNames[name]) + } + + return g, errors.New("Circular dependency found") + } + + // Remove the ready nodes and add them to the resolved graph + for name := range readySet.Iter() { + delete(nodeDependencies, name.(string)) + resolved = append(resolved, nodeNames[name.(string)]) + } + + // Also make sure to remove the ready nodes from the + // remaining node dependencies as well + for name, deps := range nodeDependencies { + diff := deps.Difference(readySet) + nodeDependencies[name] = diff + } + } + + return resolved, nil +} From cd4b0ea9e6f64e332b6e03dca11c1211bd0f8182 Mon Sep 17 00:00:00 2001 From: b4b4r07 Date: Thu, 17 Feb 2022 13:34:48 +0900 Subject: [PATCH 2/6] Refactor config parse --- pkg/config/config.go | 58 ++++++++++++++++------ pkg/dependency/{graph.go => dependency.go} | 0 2 files changed, 44 insertions(+), 14 deletions(-) rename pkg/dependency/{graph.go => dependency.go} (100%) diff --git a/pkg/config/config.go b/pkg/config/config.go index 43971b1..fecab21 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -74,7 +74,7 @@ func Read(path string) (Config, error) { } func parse(cfg Config) []Package { - table := map[string]Package{} + var pkgs []Package for _, pkg := range cfg.GitHub { // TODO: Remove? @@ -97,41 +97,62 @@ func parse(cfg Config) []Package { pkg.Command = &Command{Link: defaultLinks} } } - table[pkg.GetName()] = pkg + pkgs = append(pkgs, pkg) } for _, pkg := range cfg.Gist { - table[pkg.GetName()] = pkg + pkgs = append(pkgs, pkg) } for _, pkg := range cfg.Local { - table[pkg.GetName()] = pkg + pkgs = append(pkgs, pkg) } for _, pkg := range cfg.HTTP { + pkgs = append(pkgs, pkg) + } + + return pkgs +} + +// Parse parses a config given via yaml files and converts it into package interface +func (c Config) Parse() ([]Package, error) { + var pkgs []Package + + parsed := parse(c) + + table := map[string]Package{} + for _, pkg := range parsed { table[pkg.GetName()] = pkg } + var errs errors.Errors var graph dependency.Graph + for name, pkg := range table { - graph = append(graph, dependency.NewNode(name, pkg.GetDependsOn()...)) + dependencies := pkg.GetDependsOn() + for _, dep := range dependencies { + if !existence(parsed, dep) { + errs.Append( + fmt.Errorf("%q: not valid package name in depends-on: %s", dep, pkg.GetName()), + ) + } + } + graph = append(graph, dependency.NewNode(name, dependencies...)) + } + + if errs.ErrorOrNil() != nil { + return pkgs, errs.ErrorOrNil() } resolved, err := dependency.Resolve(graph) if err != nil { - // TODO: how do we return value - log.Printf("[ERROR] failed to resolve dependency graph: %v\n", err) + return pkgs, errors.Wrap(err, "failed to resolve dependency graph") } - var pkgs []Package for _, node := range resolved { pkgs = append(pkgs, table[node.Name]) } - return pkgs -} - -// Parse parses a config given via yaml files and converts it into package interface -func (c Config) Parse() ([]Package, error) { - return parse(c), nil + return pkgs, nil } func visitYAML(files *[]string) filepath.WalkFunc { @@ -185,3 +206,12 @@ func validatePackageName(fl validator.FieldLevel) bool { } return false } + +func existence(pkgs []Package, name string) bool { + for _, pkg := range pkgs { + if pkg.GetName() == name { + return true + } + } + return false +} diff --git a/pkg/dependency/graph.go b/pkg/dependency/dependency.go similarity index 100% rename from pkg/dependency/graph.go rename to pkg/dependency/dependency.go From 1e07c287e56aee91a0f7d598772a10b0c77deb19 Mon Sep 17 00:00:00 2001 From: b4b4r07 Date: Thu, 17 Feb 2022 14:46:35 +0900 Subject: [PATCH 3/6] Improve log output in dependency --- pkg/config/config.go | 29 ++++++++--------------------- pkg/dependency/dependency.go | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index fecab21..8d7d3fe 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -50,6 +50,8 @@ var DefaultAppConfig AppConfig = AppConfig{ // Read reads yaml file based on given path func Read(path string) (Config, error) { + log.Printf("[INFO] Reading config %s...", path) + var cfg Config f, err := os.Open(path) @@ -59,7 +61,6 @@ func Read(path string) (Config, error) { defer f.Close() validate := validator.New() - // validate.RegisterValidation("in-packages", validatePackageName) d := yaml.NewDecoder( bufio.NewReader(f), yaml.DisallowUnknownField(), @@ -115,6 +116,8 @@ func parse(cfg Config) []Package { // Parse parses a config given via yaml files and converts it into package interface func (c Config) Parse() ([]Package, error) { + log.Printf("[INFO] Parsing config...") + var pkgs []Package parsed := parse(c) @@ -143,6 +146,10 @@ func (c Config) Parse() ([]Package, error) { return pkgs, errs.ErrorOrNil() } + if dependency.Has(graph) { + log.Printf("[DEBUG] dependency graph is here: %s", graph) + } + resolved, err := dependency.Resolve(graph) if err != nil { return pkgs, errors.Wrap(err, "failed to resolve dependency graph") @@ -187,26 +194,6 @@ func WalkDir(path string) ([]string, error) { return files, nil } -func validatePackageName(fl validator.FieldLevel) bool { - ls := []string{ - "google-cloud-sdk", - } - // https://golang.hotexamples.com/jp/examples/reflect/Value/Slice/golang-value-slice-method-examples.html - for _, v := range ls { - field := fl.Field() - slice := field.Slice(0, field.Len()) - if field.Len() == 0 { - return true - } - for i := 0; i < field.Len(); i++ { - if v == slice.Index(i).String() { - return true - } - } - } - return false -} - func existence(pkgs []Package, name string) bool { for _, pkg := range pkgs { if pkg.GetName() == name { diff --git a/pkg/dependency/dependency.go b/pkg/dependency/dependency.go index b126451..e14b96b 100644 --- a/pkg/dependency/dependency.go +++ b/pkg/dependency/dependency.go @@ -1,8 +1,23 @@ package dependency +// This package is heavily inspired from +// https://github.com/dnaeon/go-dependency-graph-algorithm +// E.g. let's say these dependencies are defined +// C -> A +// D -> A +// A -> B +// B -> X +// then this package allows to resolve this dependency to this chain(order). +// X +// B +// A +// C +// D +// Read more about it and the motivation behind it at // http://dnaeon.github.io/dependency-graph-resolution-algorithm-in-go/ import ( + "bytes" "errors" "fmt" @@ -31,12 +46,27 @@ func NewNode(name string, deps ...string) *Node { type Graph []*Node // Displays the dependency graph -func displayGraph(graph Graph) { - for _, node := range graph { +func Display(graph Graph) { + fmt.Printf("%s", graph.String()) +} + +func (g Graph) String() string { + var buf bytes.Buffer + for _, node := range g { for _, dep := range node.Deps { - fmt.Printf("%s -> %s\n", node.Name, dep) + fmt.Fprintf(&buf, "%s -> %s\n", node.Name, dep) + } + } + return buf.String() +} + +func Has(graph Graph) bool { + for _, node := range graph { + if len(node.Deps) > 0 { + return true } } + return false } // Resolves the dependency graph From f161f120a804df7240e2d97c89307f4a0c907e9a Mon Sep 17 00:00:00 2001 From: b4b4r07 Date: Thu, 17 Feb 2022 19:04:47 +0900 Subject: [PATCH 4/6] Add docs --- docs/configuration/package/gist.md | 1 + docs/configuration/package/github.md | 1 + docs/configuration/package/http.md | 1 + docs/configuration/package/local.md | 1 + 4 files changed, 4 insertions(+) diff --git a/docs/configuration/package/gist.md b/docs/configuration/package/gist.md index 92fec65..e5c2e9a 100644 --- a/docs/configuration/package/gist.md +++ b/docs/configuration/package/gist.md @@ -24,3 +24,4 @@ owner | string | yes | Gist owner id | string | yes | Gist page id command | section | | See [Command](../command.md) page plugin | section | | See [Plugin](../plugin.md) page +depends-on | array | Dependency list (you can write package name here) diff --git a/docs/configuration/package/github.md b/docs/configuration/package/github.md index 0ea2f7f..e17af65 100644 --- a/docs/configuration/package/github.md +++ b/docs/configuration/package/github.md @@ -48,3 +48,4 @@ release.name | string | yes (in `release`) | GitHub release name release.tag | string | | GitHub release tag command | section | | See [Command](../command.md) page plugin | section | | See [Plugin](../plugin.md) page +depends-on | array | Dependency list (you can write package name here) diff --git a/docs/configuration/package/http.md b/docs/configuration/package/http.md index 187e4ce..6465044 100644 --- a/docs/configuration/package/http.md +++ b/docs/configuration/package/http.md @@ -23,3 +23,4 @@ url | string | yes | URL which can be downloaded. output | string | | TBD command | section | | See [Command](../command.md) page plugin | section | | See [Plugin](../plugin.md) page +depends-on | array | Dependency list (you can write package name here) diff --git a/docs/configuration/package/local.md b/docs/configuration/package/local.md index cffe953..fc25b8d 100644 --- a/docs/configuration/package/local.md +++ b/docs/configuration/package/local.md @@ -27,3 +27,4 @@ description | string | | A description of a package directory | string | yes | Directory path where the target files are placed command | section | | See [Command](../command.md) page plugin | section | | See [Plugin](../plugin.md) page +depends-on | array | Dependency list (you can write package name here) From d256404da32c0b92b1b8c52272989fdc7f50b812 Mon Sep 17 00:00:00 2001 From: b4b4r07 Date: Thu, 17 Feb 2022 23:03:09 +0900 Subject: [PATCH 5/6] Fix dependency bug between multiple files --- cmd/meta.go | 14 +++++- pkg/config/config.go | 90 +++++++++++++++++------------------- pkg/config/package.go | 15 ------ pkg/dependency/dependency.go | 2 +- pkg/state/state.go | 8 ++++ 5 files changed, 63 insertions(+), 66 deletions(-) diff --git a/cmd/meta.go b/cmd/meta.go index 37f8f9f..033bb56 100644 --- a/cmd/meta.go +++ b/cmd/meta.go @@ -49,17 +49,18 @@ func (m *meta) init(args []string) error { return errors.Wrapf(err, "%s: failed to walk dir", cfgRoot) } + var pkgs []config.Package app := &config.DefaultAppConfig for _, file := range files { cfg, err := config.Read(file) if err != nil { return errors.Wrapf(err, "%s: failed to read config", file) } - pkgs, err := cfg.Parse() + parsed, err := cfg.Parse() if err != nil { return errors.Wrapf(err, "%s: failed to parse config", file) } - m.Packages = append(m.Packages, pkgs...) + pkgs = append(pkgs, parsed...) if cfg.AppConfig != nil { app = cfg.AppConfig @@ -68,6 +69,15 @@ func (m *meta) init(args []string) error { m.AppConfig = app + pkgs, err = config.Sort(pkgs) + if err != nil { + return errors.Wrap(err, "%s: failed to resolve dependencies between packages") + } + + if len(pkgs) > 0 { + m.Packages = pkgs + } + if err := config.Validate(m.Packages); err != nil { return errors.Wrap(err, "%s: failed to validate packages") } diff --git a/pkg/config/config.go b/pkg/config/config.go index 8d7d3fe..e46e697 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -117,49 +117,8 @@ func parse(cfg Config) []Package { // Parse parses a config given via yaml files and converts it into package interface func (c Config) Parse() ([]Package, error) { log.Printf("[INFO] Parsing config...") - - var pkgs []Package - - parsed := parse(c) - - table := map[string]Package{} - for _, pkg := range parsed { - table[pkg.GetName()] = pkg - } - - var errs errors.Errors - var graph dependency.Graph - - for name, pkg := range table { - dependencies := pkg.GetDependsOn() - for _, dep := range dependencies { - if !existence(parsed, dep) { - errs.Append( - fmt.Errorf("%q: not valid package name in depends-on: %s", dep, pkg.GetName()), - ) - } - } - graph = append(graph, dependency.NewNode(name, dependencies...)) - } - - if errs.ErrorOrNil() != nil { - return pkgs, errs.ErrorOrNil() - } - - if dependency.Has(graph) { - log.Printf("[DEBUG] dependency graph is here: %s", graph) - } - - resolved, err := dependency.Resolve(graph) - if err != nil { - return pkgs, errors.Wrap(err, "failed to resolve dependency graph") - } - - for _, node := range resolved { - pkgs = append(pkgs, table[node.Name]) - } - - return pkgs, nil + // TODO: divide from parse() + return parse(c), nil } func visitYAML(files *[]string) filepath.WalkFunc { @@ -194,11 +153,46 @@ func WalkDir(path string) ([]string, error) { return files, nil } -func existence(pkgs []Package, name string) bool { - for _, pkg := range pkgs { - if pkg.GetName() == name { - return true +func Sort(given []Package) ([]Package, error) { + var pkgs []Package + var graph dependency.Graph + + table := map[string]Package{} + + for _, pkg := range given { + table[pkg.GetName()] = pkg + } + + var errs errors.Errors + for name, pkg := range table { + dependencies := pkg.GetDependsOn() + for _, dep := range pkg.GetDependsOn() { + if _, ok := table[dep]; !ok { + errs.Append( + fmt.Errorf("%q: not valid package name in depends-on: %s", dep, pkg.GetName()), + ) + } } + graph = append(graph, dependency.NewNode(name, dependencies...)) + } + + if dependency.Has(graph) { + log.Printf("[DEBUG] dependency graph is here: \n%s", graph) + } + + resolved, err := dependency.Resolve(graph) + if err != nil { + return pkgs, errors.Wrap(err, "failed to resolve dependency graph") + } + + for _, node := range resolved { + pkgs = append(pkgs, table[node.Name]) } - return false + + return pkgs, errs.ErrorOrNil() +} + +// Validate validates if packages are not violated some rules +func Validate(pkgs []Package) error { + return nil } diff --git a/pkg/config/package.go b/pkg/config/package.go index 54eb6df..0a0152e 100644 --- a/pkg/config/package.go +++ b/pkg/config/package.go @@ -2,7 +2,6 @@ package config import ( "context" - "fmt" "github.com/mattn/go-shellwords" ) @@ -82,17 +81,3 @@ func HasSudoInCommandBuildSteps(pkgs []Package) bool { } return false } - -// Validate checks if keys of given packages are not duplicated -func Validate(pkgs []Package) error { - done := make(map[string]bool, len(pkgs)) - for _, pkg := range pkgs { - name := pkg.GetName() - _, already := done[name] - if already { - return fmt.Errorf("%s: duplicated", name) - } - done[name] = true - } - return nil -} diff --git a/pkg/dependency/dependency.go b/pkg/dependency/dependency.go index e14b96b..08984a9 100644 --- a/pkg/dependency/dependency.go +++ b/pkg/dependency/dependency.go @@ -54,7 +54,7 @@ func (g Graph) String() string { var buf bytes.Buffer for _, node := range g { for _, dep := range node.Deps { - fmt.Fprintf(&buf, "%s -> %s\n", node.Name, dep) + fmt.Fprintf(&buf, "* %s -> %s\n", node.Name, dep) } } return buf.String() diff --git a/pkg/state/state.go b/pkg/state/state.go index 0a59b32..2fe8e0d 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -298,6 +298,14 @@ func (s *State) Update(pkg config.Package) { s.save() } +func (s *State) New() error { + s.Resources = map[string]Resource{} + for _, pkg := range s.packages { + add(pkg, s) + } + return s.save() +} + func (s *State) Refresh() error { s.mu.Lock() defer s.mu.Unlock() From 1046687c4eac1068b1a53ff1699ee4f91afaa067 Mon Sep 17 00:00:00 2001 From: b4b4r07 Date: Thu, 17 Feb 2022 23:49:47 +0900 Subject: [PATCH 6/6] Check duplicated packages --- cmd/meta.go | 14 ++++++-------- pkg/config/config.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/cmd/meta.go b/cmd/meta.go index 033bb56..a4394cc 100644 --- a/cmd/meta.go +++ b/cmd/meta.go @@ -69,18 +69,16 @@ func (m *meta) init(args []string) error { m.AppConfig = app - pkgs, err = config.Sort(pkgs) - if err != nil { - return errors.Wrap(err, "%s: failed to resolve dependencies between packages") + if err := config.Validate(pkgs); err != nil { + return errors.Wrap(err, "failed to validate packages") } - if len(pkgs) > 0 { - m.Packages = pkgs + pkgs, err = config.Sort(pkgs) + if err != nil { + return errors.Wrap(err, "failed to resolve dependencies between packages") } - if err := config.Validate(m.Packages); err != nil { - return errors.Wrap(err, "%s: failed to validate packages") - } + m.Packages = pkgs m.Env = env.New(cache) m.Env.Add(env.Variables{ diff --git a/pkg/config/config.go b/pkg/config/config.go index e46e697..874dc71 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,6 +6,7 @@ import ( "log" "os" "path/filepath" + "strings" "github.com/b4b4r07/afx/pkg/dependency" "github.com/b4b4r07/afx/pkg/errors" @@ -194,5 +195,22 @@ func Sort(given []Package) ([]Package, error) { // Validate validates if packages are not violated some rules func Validate(pkgs []Package) error { + m := make(map[string]bool) + var list []string + + for _, pkg := range pkgs { + name := pkg.GetName() + _, exist := m[name] + if exist { + list = append(list, name) + continue + } + m[name] = true + } + + if len(list) > 0 { + return fmt.Errorf("duplicated packages: [%s]", strings.Join(list, ",")) + } + return nil }