From 0e30bfcb463e83c3beba09110f971c50adf0e1a5 Mon Sep 17 00:00:00 2001 From: Young Xu Date: Wed, 4 Sep 2024 01:05:14 +0800 Subject: [PATCH] feat: support analyzed and show available update dependencies Signed-off-by: Young Xu --- README.md | 62 +++++++++++++-- go.mod | 10 ++- go.sum | 26 ++++++- gomod.go | 206 ++++++++++++++++++++++++++++++++++++++++++++++++-- gomod/main.go | 4 +- 5 files changed, 290 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 594792b..8fe5d50 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,24 @@ go install gitter.top/apps/gomod/...@latest ``` - - ### usage -you can upgrade go.mod using: +you can show go.mod available updates using `gomod`: + +```shell +x@fedora:~/GolandProjects/gomod$ gomod + PACKAGE | RELATION | CURRENT | LATEST +--------------------------------+----------+------------------------------------+------------------------------------- + github.com/fatih/color | direct | v1.7.0 | v1.17.0 + github.com/mattn/go-colorable | indirect | v0.1.2 | v0.1.13 + github.com/mattn/go-isatty | indirect | v0.0.8 | v0.0.20 + github.com/mattn/go-runewidth | indirect | v0.0.9 | v0.0.16 + golang.org/x/tools | indirect | v0.13.0 | v0.24.0 + golang.org/x/xerrors | indirect | v0.0.0-20191204190536-9bdfabe68543 | v0.0.0-20240903120638-7835f813f4da + gopkg.in/check.v1 | indirect | v0.0.0-20161208181325-20d25e280405 | v1.0.0-20201130134442-10cb98267c6c +``` + +upgrade go.mod using `gomod u`: ```shell x@fedora:~/GolandProjects/gomod$ gomod u @@ -25,8 +38,47 @@ x@fedora:~/GolandProjects/gomod$ gomod u [INFO] [url=golang.org/x/net@v0.28.0] upgrade success ``` +analyzed project dependencies: + +```shell +x@fedora:~/GolandProjects/gomod$ gomod u + PACKAGE | RELATION | VERSION | GOVERSION | TOOLCHAINS +---------------------------------------+----------+------------------------------------+-----------+------------- + gitter.top/apps/gomod | main | | 1.20 | + github.com/briandowns/spinner | direct | v1.23.1 | 1.17 | + github.com/cpuguy83/go-md2man/v2 | indirect | v2.0.4 | 1.11 | + github.com/davecgh/go-spew | indirect | v1.1.1 | | + github.com/fatih/color | direct | v1.7.0 | | + github.com/google/go-cmp | indirect | v0.6.0 | | + github.com/google/go-github/v64 | direct | v64.0.0 | 1.21 | + github.com/google/go-querystring | indirect | v1.1.0 | 1.10 | + github.com/inconshreveable/mousetrap | indirect | v1.1.0 | 1.18 | + github.com/mattn/go-colorable | indirect | v0.1.2 | | + github.com/mattn/go-isatty | indirect | v0.0.8 | | + github.com/mattn/go-runewidth | indirect | v0.0.9 | 1.9 | + github.com/olekukonko/tablewriter | direct | v0.0.5 | 1.12 | + github.com/pmezard/go-difflib | indirect | v1.0.0 | | + github.com/russross/blackfriday/v2 | indirect | v2.1.0 | | + github.com/sirupsen/logrus | direct | v1.9.3 | 1.13 | + github.com/spf13/cobra | direct | v1.8.1 | 1.15 | + github.com/spf13/pflag | indirect | v1.0.5 | 1.12 | + github.com/stretchr/objx | indirect | v0.5.2 | | + github.com/stretchr/testify | direct | v1.9.0 | 1.17 | + gitter.top/common/lormatter | direct | v0.0.1 | 1.21 | + golang.org/x/crypto | indirect | v0.26.0 | | + golang.org/x/mod | direct | v0.20.0 | 1.18 | + golang.org/x/net | direct | v0.28.0 | 1.18 | + golang.org/x/sys | indirect | v0.24.0 | 1.18 | + golang.org/x/term | indirect | v0.23.0 | 1.18 | + golang.org/x/text | indirect | v0.17.0 | | + golang.org/x/tools | indirect | v0.13.0 | | + golang.org/x/xerrors | indirect | v0.0.0-20191204190536-9bdfabe68543 | 1.11 | + gopkg.in/check.v1 | indirect | v0.0.0-20161208181325-20d25e280405 | | + gopkg.in/yaml.v3 | indirect | v3.0.1 | | +``` + ### feature -- [ ] `gomod` show go.mod available updates +- [x] `gomod` show go.mod available updates - [x] `gomod u` upgrade go.mod -- [ ] `gomod a` analyzed project dependencies \ No newline at end of file +- [x] `gomod a` analyzed project dependencies \ No newline at end of file diff --git a/go.mod b/go.mod index cf80b76..04a6ee5 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,13 @@ module gitter.top/apps/gomod go 1.20 require ( + github.com/briandowns/spinner v1.23.1 + github.com/fatih/color v1.17.0 github.com/google/go-github/v64 v64.0.0 + github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.9.1-0.20240613200951-b074924938f8 gitter.top/common/lormatter v0.0.1 golang.org/x/mod v0.20.0 golang.org/x/net v0.28.0 @@ -16,8 +19,13 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 086b488..bf23d3c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,11 @@ +github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650= +github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-github/v64 v64.0.0 h1:4G61sozmY3eiPAjjoOHponXDBONm+utovTKbyUb2Qdg= @@ -10,8 +14,22 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77 h1:3bMMZ1f+GPXFQ1uNaYbO/uECWvSfqEA+ZEXn1rFAT88= +github.com/olekukonko/tablewriter v0.0.6-0.20230925090304-df64c4bbad77/go.mod h1:8Hf+pH6thup1sPZPD+NLg7d6vbpsdilu9CPIeikvgMQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -21,8 +39,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.9.1-0.20240613200951-b074924938f8 h1:lMSOB3NNaQcNDK9eCjQb0LFfpXW03lzF2SN6vUziALo= +github.com/stretchr/testify v1.9.1-0.20240613200951-b074924938f8/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gitter.top/common/lormatter v0.0.1 h1:RwNmDsgXl6gjU1KBgaVdDgG+dYDLjhM76TQu/C6bArY= gitter.top/common/lormatter v0.0.1/go.mod h1:P3v4TVOF52RRh41UgcABmDWyK8ZRo6ekbG/uln8PH+w= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= @@ -30,8 +48,12 @@ golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/gomod.go b/gomod.go index 86e1409..1004fbe 100644 --- a/gomod.go +++ b/gomod.go @@ -1,20 +1,24 @@ package gomod import ( + "bytes" "context" + "encoding/json" "errors" "fmt" + "github.com/briandowns/spinner" + "github.com/fatih/color" + "github.com/google/go-github/v64/github" + "github.com/olekukonko/tablewriter" + "github.com/sirupsen/logrus" + "golang.org/x/mod/modfile" + "golang.org/x/net/html" "io" "net/http" "os" "os/exec" "strings" "time" - - "github.com/google/go-github/v64/github" - "github.com/sirupsen/logrus" - "golang.org/x/mod/modfile" - "golang.org/x/net/html" ) const ( @@ -26,6 +30,8 @@ var ( Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, Timeout: time.Second * 5, } + + whiteList = []string{"golang.org"} ) func goGet(u, v string) error { @@ -177,15 +183,24 @@ func (r *repo) upgrade() error { return nil } -func ModUpgrade(upgradeIndirect bool) { - modFileData, err := os.ReadFile("go.mod") +func GetModFile(mf string) (*modfile.File, error) { + modFileData, err := os.ReadFile(mf) if err != nil { logrus.Errorf("read go.mod file failed: %v", err) - return + return nil, err } modFile, err := modfile.Parse("go.mod", modFileData, nil) if err != nil { logrus.Errorf("parse go.mod file failed: %v", err) + return nil, err + } + return modFile, nil +} + +func ModUpgrade(upgradeIndirect bool) { + modFile, err := GetModFile("go.mod") + if err != nil { + logrus.Errorf("get go.mod file failed: %v", err) return } for _, mod := range modFile.Require { @@ -220,3 +235,178 @@ func fallback(repo string) { } logrus.WithField("url", repo+"@"+defaultVersion).Infof("upgrade success") } + +type Module struct { + Path string // module path + Query string // version query corresponding to this version + Version string // module version + Versions []string // available module versions + Replace *Module // replaced by this module + Time *time.Time // time version was created + Update *Module // available update (with -u) + Main bool // is this the main module? + Indirect bool // module is only indirectly needed by main module + Dir string // directory holding local copy of files, if any + GoMod string // path to go.mod file describing module, if any + GoVersion string // go version used in module + Retracted []string // retraction information, if any (with -retracted or -u) + Deprecated string // deprecation message, if any (with -u) + Error *ModuleError // error loading module + Origin any // provenance of module + Reuse bool // reuse of old module info is safe +} + +type ModuleError struct { + Err string // the error itself +} + +func Analyzed() { + spin := spinner.New(spinner.CharSets[14], 100*time.Millisecond) + spin.Suffix = " Checking for updates..." + spin.Start() + + //modFile, err := GetModFile("go.mod") + //if err != nil { + // spin.Stop() + // logrus.Errorf("get go.mod file failed: %v", err) + // return + //} + modules, err := analyzed("go", "list", "-m", "-json", "-mod=readonly", "all") + if err != nil { + spin.Stop() + logrus.Errorf("analyzed project dependencies failed: %v", err) + return + } + + spin.Stop() + + var tableRows [][]string + for _, mod := range modules { + tableRows = append(tableRows, []string{ + mod.Path, + getRelation(mod), + mod.Version, + mod.GoVersion, + getToolChains(mod.GoMod), + }) + } + + if len(tableRows) == 0 { + awesome := color.New(color.FgHiGreen, color.Bold).Sprint("✔ Awesome!") + fmt.Printf(" %s All of your dependencies are up-to-date.\n", awesome) + } else { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Package", "Relation", "Version", "GoVersion", "ToolChains"}) + table.SetBorder(false) + table.AppendBulk(tableRows) + table.Render() + } +} + +func getRelation(m *Module) string { + if m.Main { + return "main" + } + if m.Indirect { + return "indirect" + } + return "direct" +} + +func getToolChains(f string) string { + if f == "" { + return "" + } + file, err := GetModFile(f) + if err != nil { + return "" + } + if file.Toolchain == nil { + return "" + } + return file.Toolchain.Name +} + +func analyzed(cmd string, args ...string) ([]*Module, error) { + execBuf, err := execute(cmd, args...) + if err != nil { + logrus.Errorf("go list failed: %s, return: %v", execBuf, err) + return nil, err + } + var modules []*Module + var buf bytes.Buffer + var depth int32 + for _, ch := range execBuf { + switch ch { + case '{': + depth++ + buf.WriteByte(ch) + case '}': + depth-- + if depth == 0 { + buf.WriteByte(ch) + var m = new(Module) + if err := json.Unmarshal(buf.Bytes(), m); err != nil { + return nil, err + } + buf.Reset() + modules = append(modules, m) + } else { + buf.WriteByte(ch) + } + default: + buf.WriteByte(ch) + } + } + return modules, nil +} + +func execute(command string, args ...string) ([]byte, error) { + cmd := exec.Command(command, args...) + return cmd.CombinedOutput() +} + +func UpdateList() { + spin := spinner.New(spinner.CharSets[14], 100*time.Millisecond) + spin.Suffix = " Checking for updates..." + spin.Start() + + //modFile, err := GetModFile("go.mod") + //if err != nil { + // spin.Stop() + // logrus.Errorf("get go.mod file failed: %v", err) + // return + //} + modules, err := analyzed("go", "list", "-m", "-u", "-json", "-mod=readonly", "all") + if err != nil { + spin.Stop() + logrus.Errorf("analyzed project dependencies failed: %v", err) + return + } + + spin.Stop() + + var tableRows [][]string + for _, mod := range modules { + if mod.Update == nil { + continue + } + tableRows = append(tableRows, []string{ + mod.Path, + getRelation(mod), + mod.Version, + mod.Update.Version, + }) + } + + if len(tableRows) == 0 { + awesome := color.New(color.FgHiGreen, color.Bold).Sprint("✔ Awesome!") + fmt.Printf(" %s All of your dependencies are up-to-date.\n", awesome) + } else { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Package", "Relation", "Current", "Latest"}) + table.SetBorder(false) + table.AppendBulk(tableRows) + table.Render() + } +} diff --git a/gomod/main.go b/gomod/main.go index ab117fb..dc80d89 100644 --- a/gomod/main.go +++ b/gomod/main.go @@ -33,7 +33,7 @@ func init() { } }, Run: func(cmd *cobra.Command, args []string) { - + gomod.UpdateList() }, } mod.AddCommand(upgrade()) @@ -59,7 +59,7 @@ func analyzed() *cobra.Command { Short: "analyzed project dependencies", Aliases: []string{"a"}, Run: func(cmd *cobra.Command, args []string) { - + gomod.Analyzed() }, } }