github.com/kcmvp/gob@v1.0.17/cmd/gbc/command/deps.go (about) 1 package command 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 11 "github.com/kcmvp/gob/cmd/gbc/artifact" //nolint 12 13 "github.com/fatih/color" 14 "github.com/samber/lo" 15 "github.com/spf13/cobra" 16 "github.com/xlab/treeprint" 17 ) 18 19 var ( 20 green = color.New(color.FgGreen) 21 yellow = color.New(color.FgYellow) 22 ) 23 24 // parseMod return a tuple which the fourth element is the indicator of direct or indirect reference 25 func parseMod(mod *os.File) (string, string, []*lo.Tuple4[string, string, string, int], error) { 26 scanner := bufio.NewScanner(mod) 27 var deps []*lo.Tuple4[string, string, string, int] 28 var module, version string 29 for scanner.Scan() { 30 line := strings.TrimSpace(scanner.Text()) 31 if len(line) == 0 || line == ")" || line == "//" || strings.HasPrefix(line, "require") { 32 continue 33 } 34 if strings.HasPrefix(line, "module ") { 35 module = strings.Split(line, " ")[1] 36 } else if strings.HasPrefix(line, "go ") { 37 version = strings.Split(line, " ")[1] 38 } else { 39 entry := strings.Split(line, " ") 40 m := strings.TrimSpace(entry[0]) 41 v := strings.TrimSpace(entry[1]) 42 dep := lo.T4(m, v, v, lo.If(len(entry) > 2, 0).Else(1)) 43 deps = append(deps, &dep) 44 } 45 } 46 return module, version, deps, scanner.Err() 47 } 48 49 // dependencyTree build dependency tree of the project, an empty tree returns when runs into error 50 func dependencyTree() (treeprint.Tree, error) { 51 mod, err := os.Open(filepath.Join(artifact.CurProject().Root(), "go.mod")) 52 if err != nil { 53 return nil, fmt.Errorf(color.RedString(err.Error())) 54 } 55 exec.Command("go", "mod", "tidy").CombinedOutput() //nolint 56 if output, err := exec.Command("go", "build", "./...").CombinedOutput(); err != nil { 57 return nil, fmt.Errorf(color.RedString(string(output))) 58 } 59 module, _, dependencies, err := parseMod(mod) 60 if len(dependencies) < 1 { 61 return nil, nil 62 } 63 if err != nil { 64 return nil, fmt.Errorf(err.Error()) 65 } 66 tree := treeprint.New() 67 tree.SetValue(module) 68 direct := lo.FilterMap(dependencies, func(item *lo.Tuple4[string, string, string, int], _ int) (string, bool) { 69 return item.A, item.D == 1 70 }) 71 // get the latest version 72 versions := artifact.LatestVersion(direct...) 73 for _, dep := range dependencies { 74 if version, ok := lo.Find(versions, func(t lo.Tuple2[string, string]) bool { 75 return dep.A == t.A && dep.B != t.B 76 }); ok { 77 dep.C = version.B 78 } 79 } 80 // parse the dependency tree 81 cache := []string{os.Getenv("GOPATH"), "pkg", "mod", "cache", "download"} 82 for _, dependency := range dependencies { 83 if dependency.D == 1 { 84 label := lo.IfF(dependency.B == dependency.C, func() string { 85 return fmt.Sprintf("%s@%s", dependency.A, dependency.B) 86 }).ElseF(func() string { 87 return yellow.Sprintf("* %s@%s (%s)", dependency.A, dependency.B, dependency.C) 88 }) 89 child := tree.AddBranch(label) 90 dir := append(cache, strings.Split(dependency.A, "/")...) 91 dir = append(dir, []string{"@v", fmt.Sprintf("%s.mod", dependency.B)}...) 92 mod, err = os.Open(filepath.Join(dir...)) 93 if err != nil { 94 return tree, fmt.Errorf(color.RedString(err.Error())) 95 } 96 _, _, cDeps, err := parseMod(mod) 97 if err != nil { 98 return tree, fmt.Errorf(color.RedString(err.Error())) 99 } 100 inter := lo.Filter(cDeps, func(c *lo.Tuple4[string, string, string, int], _ int) bool { 101 return lo.ContainsBy(dependencies, func(p *lo.Tuple4[string, string, string, int]) bool { 102 return p.A == c.A 103 }) 104 }) 105 for _, l := range inter { 106 child.AddNode(fmt.Sprintf("%s@%s", l.A, l.B)) 107 } 108 } 109 } 110 return tree, err 111 } 112 113 // depCmd represents the dep command 114 var depCmd = &cobra.Command{ 115 Use: "deps", 116 Short: "Show the dependency tree of the project", 117 Long: `Show the dependency tree of the project 118 and indicate available updates which take an green * indicator`, 119 RunE: func(cmd *cobra.Command, args []string) error { 120 tree, err := dependencyTree() 121 if err != nil { 122 return err 123 } else if tree == nil { 124 yellow.Println("No dependencies !") 125 return nil 126 } 127 green.Println("Dependencies of the projects:") 128 fmt.Println(tree.String()) 129 return nil 130 }, 131 } 132 133 func init() { 134 depCmd.SetUsageTemplate(usageTemplate()) 135 depCmd.SetErrPrefix(color.RedString("Error:")) 136 rootCmd.AddCommand(depCmd) 137 }