Skip to content

Commit

Permalink
Merge pull request #74 from kcmvp/dep-tree
Browse files Browse the repository at this point in the history
#62 #63: add gob deps commands
  • Loading branch information
kcmvp authored Jan 22, 2024
2 parents f75df0b + 57b3d5d commit d02bc46
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 0 deletions.
145 changes: 145 additions & 0 deletions cmd/deps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
Copyright © 2024 kcmvp <EMAIL ADDRESS>
*/
package cmd

import (
"bufio"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/fatih/color"
"github.com/kcmvp/gob/internal"
"github.com/samber/lo"
"github.com/spf13/cobra"
"github.com/xlab/treeprint"
)

var (
bold = color.New(color.Bold)
yellow = color.New(color.FgYellow, color.Bold)
)

// parseMod return a tuple which the fourth element is the indicator of direct or indirect reference
func parseMod(mod *os.File) (string, string, []*lo.Tuple4[string, string, string, int], error) {
scanner := bufio.NewScanner(mod)
start := false
var deps []*lo.Tuple4[string, string, string, int]
var module, version string
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "module ") {
module = strings.Split(line, " ")[1]
} else if strings.HasPrefix(line, "go ") {
version = strings.Split(line, " ")[1]
}
start = start && line != ")"
if start && len(line) > 0 {
entry := strings.Split(line, " ")
m := strings.TrimSpace(entry[0])
v := strings.TrimSpace(entry[1])
dep := lo.T4(m, v, v, lo.If(len(entry) > 2, 0).Else(1))
deps = append(deps, &dep)
}
start = start || strings.HasPrefix(line, "require")
}
return module, version, deps, scanner.Err()
}

// dependency build dependency tree of the project, an empty tree returns when runs into error
func dependency() (treeprint.Tree, error) {
tree := treeprint.New()
mod, err := os.Open(filepath.Join(internal.CurProject().Root(), "go.mod"))
if err != nil {
return tree, fmt.Errorf(color.RedString(err.Error()))
}
if _, err = exec.Command("go", "mod", "tidy").CombinedOutput(); err != nil {
return tree, fmt.Errorf(color.RedString(err.Error()))
}

if _, err = exec.Command("go", "build", "./...").CombinedOutput(); err != nil {
return tree, fmt.Errorf(color.RedString(err.Error()))
}

module, _, dependencies, err := parseMod(mod)
if err != nil {
return tree, fmt.Errorf(err.Error())
}
tree.SetValue(bold.Sprintf("%s", module))
direct := lo.FilterMap(dependencies, func(item *lo.Tuple4[string, string, string, int], _ int) (string, bool) {
return fmt.Sprintf("%s@latest", item.A), item.D == 1
})
// get the latest version
output, _ := exec.Command("go", append([]string{"list", "-m"}, direct...)...).CombinedOutput() //nolint
scanner := bufio.NewScanner(strings.NewReader(string(output)))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
entry := strings.Split(line, " ")
for _, dep := range dependencies {
if dep.A == entry[0] && dep.B != entry[1] {
dep.C = entry[1]
}
}
}

if err = scanner.Err(); err != nil {
return tree, fmt.Errorf(err.Error())
}
// parse the dependency tree
cache := []string{os.Getenv("GOPATH"), "pkg", "mod", "cache", "download"}
for _, dependency := range dependencies {
if dependency.D == 1 {
label := lo.IfF(dependency.B == dependency.C, func() string {
return fmt.Sprintf("%s@%s", dependency.A, dependency.B)
}).ElseF(func() string {
return yellow.Sprintf("* %s@%s (%s)", dependency.A, dependency.B, dependency.C)
})
child := tree.AddBranch(label)
dir := append(cache, strings.Split(dependency.A, "/")...)
dir = append(dir, []string{"@v", fmt.Sprintf("%s.mod", dependency.B)}...)
mod, err = os.Open(filepath.Join(dir...))
if err != nil {
return tree, fmt.Errorf(color.RedString(err.Error()))
}
_, _, cDeps, err := parseMod(mod)
if err != nil {
return tree, fmt.Errorf(color.RedString(err.Error()))
}
inter := lo.Filter(cDeps, func(c *lo.Tuple4[string, string, string, int], _ int) bool {
return lo.ContainsBy(dependencies, func(p *lo.Tuple4[string, string, string, int]) bool {
return p.A == c.A
})
})
for _, l := range inter {
child.AddNode(fmt.Sprintf("%s@%s", l.A, l.B))
}
}
}
return tree, err
}

// depCmd represents the dep command
var depCmd = &cobra.Command{
Use: "deps",
Short: "Show the dependency tree of the project",
Long: `Show the dependency tree of the project
and indicate available updates which take an green * indicator`,
RunE: func(cmd *cobra.Command, args []string) error {
tree, err := dependency()
if err != nil {
return err
}
bold.Print("\nDependencies of the projects:\n")
yellow.Print("* indicates new versions available\n")
fmt.Println("")
fmt.Println(tree.String())
return nil
},
}

func init() {
builderCmd.AddCommand(depCmd)
}
40 changes: 40 additions & 0 deletions cmd/deps_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cmd

import (
"fmt"
"github.com/kcmvp/gob/internal"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/xlab/treeprint"
"os"
"path/filepath"
"strings"
"testing"
)

func TestParseMod(t *testing.T) {
os.Chdir(internal.CurProject().Root())
mod, _ := os.Open(filepath.Join(internal.CurProject().Root(), "go.mod"))
m, _, deps, err := parseMod(mod)
assert.NoError(t, err)
assert.Equal(t, m, "github.com/kcmvp/gob")
assert.Equal(t, 10, len(lo.Filter(deps, func(item *lo.Tuple4[string, string, string, int], _ int) bool {
return item.D == 1
})))
assert.Equal(t, 43, len(deps))
}

func TestDependency(t *testing.T) {
os.Chdir(internal.CurProject().Root())
mod, _ := os.Open(filepath.Join(internal.CurProject().Root(), "go.mod"))
_, _, deps, _ := parseMod(mod)
tree, err := dependency()
assert.NoError(t, err)
tree.VisitAll(func(item *treeprint.Node) {
contains := lo.ContainsBy(deps, func(dep *lo.Tuple4[string, string, string, int]) bool {
return strings.Contains(fmt.Sprintf("%s", item.Value), fmt.Sprintf("%s", dep.A))
})
assert.True(t, contains)
})

}
5 changes: 5 additions & 0 deletions cmd/plugin_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ func (suite *PluginTestSuit) TestInstallPluginWithVersion() {
}
}

func (suite *PluginTestSuit) TestList() {
err := list(nil, "")
assert.NoError(suite.T(), err)
}

func (suite *PluginTestSuit) TestPluginArgs() {
tests := []struct {
name string
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4
github.com/tidwall/gjson v1.17.0
github.com/xlab/treeprint v1.2.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down

0 comments on commit d02bc46

Please sign in to comment.