feat: support analyzed and show available update dependencies

Signed-off-by: Young Xu <xuthus5@gmail.com>
This commit is contained in:
Young Xu 2024-09-04 01:05:14 +08:00
parent 56cead31aa
commit 0e30bfcb46
5 changed files with 290 additions and 18 deletions

View File

@ -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
- [x] `gomod a` analyzed project dependencies

10
go.mod
View File

@ -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
)

26
go.sum
View File

@ -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=

206
gomod.go
View File

@ -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()
}
}

View File

@ -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()
},
}
}