Skip to content

Commit 15a229c

Browse files
authored
Merge pull request #116 from kcmvp/upgrade
#115: add upgrade command and fix last '%' on progress bar
2 parents 42b8c83 + 63d79fd commit 15a229c

File tree

10 files changed

+117
-78
lines changed

10 files changed

+117
-78
lines changed

.github/workflows/workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Go
1818
uses: actions/setup-go@v4
1919
with:
20-
go-version: '1.21.4'
20+
go-version: '1.22.2'
2121
- name: Test
2222
run: go test -v ./... -coverprofile=cover.out
2323
- name: Upload coverage reports to Codecov

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ gob
2424
11.json
2525
target
2626
*.sql
27+
data

cmd/gbc/artifact/internal_plugin_test.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"github.com/kcmvp/gob/utils"
7-
"github.com/samber/lo"
7+
"github.com/samber/lo" //nolint
88
"github.com/stretchr/testify/assert"
99
"github.com/stretchr/testify/suite"
1010
"github.com/tidwall/gjson"
@@ -16,7 +16,8 @@ import (
1616

1717
type InternalPluginTestSuit struct {
1818
suite.Suite
19-
lintLatestVersion string
19+
lintLatestVersion string
20+
gotestsumLatestVersion string
2021
}
2122

2223
func (suite *InternalPluginTestSuit) TearDownSuite() {
@@ -26,7 +27,8 @@ func (suite *InternalPluginTestSuit) TearDownSuite() {
2627

2728
func TestInternalPluginSuite(t *testing.T) {
2829
suite.Run(t, &InternalPluginTestSuit{
29-
lintLatestVersion: LatestVersion("github.com/golangci/golangci-lint")[0].B,
30+
lintLatestVersion: LatestVersion(false, "github.com/golangci/golangci-lint")[0].B,
31+
gotestsumLatestVersion: LatestVersion(false, "gotest.tools/gotestsum")[0].B,
3032
})
3133
}
3234

@@ -68,7 +70,7 @@ func (suite *InternalPluginTestSuit) TestNewPlugin() {
6870
wantErr: false,
6971
},
7072
{
71-
name: "has @ but no version",
73+
name: "has_@ but no version",
7274
url: "github.com/golangci/golangci-lint/cmd/golangci-lint@",
7375
module: "github.com/golangci/golangci-lint",
7476
logName: "",
@@ -84,7 +86,7 @@ func (suite *InternalPluginTestSuit) TestNewPlugin() {
8486
wantErr: true,
8587
},
8688
{
87-
name: "multiple @",
89+
name: "multiple@",
8890
url: "github.com/golangci/golangci-lint/cmd/golangci@-lint@v1",
8991
module: "github.com/golangci/golangci-lint",
9092
logName: "",
@@ -96,7 +98,7 @@ func (suite *InternalPluginTestSuit) TestNewPlugin() {
9698
url: "gotest.tools/gotestsum",
9799
module: "gotest.tools/gotestsum",
98100
logName: "gotestsum",
99-
binary: "gotestsum-v1.11.0",
101+
binary: fmt.Sprintf("%s-%s", "gotestsum", suite.gotestsumLatestVersion),
100102
wantErr: false,
101103
},
102104
}
@@ -138,7 +140,7 @@ func (suite *InternalPluginTestSuit) TestUnmarshalJSON() {
138140
return plugin.Url == "gotest.tools/gotestsum"
139141
})
140142
assert.True(t, ok)
141-
assert.Equal(t, "v1.11.0", plugin.Version())
143+
assert.Equal(t, suite.gotestsumLatestVersion, plugin.Version())
142144
assert.Equal(t, "gotestsum", plugin.Name())
143145
assert.Equal(t, "gotest.tools/gotestsum", plugin.Module())
144146
assert.Equal(t, "test", plugin.Alias)
@@ -177,6 +179,6 @@ func (suite *InternalPluginTestSuit) TestExecute() {
177179
assert.Error(t, err)
178180
//'exit status 2' means the plugin is executed but no parameters,
179181
assert.Equal(t, "exit status 2", err.Error())
180-
_, err = os.Stat(filepath.Join(CurProject().Target(), "guru.log"))
182+
_, err = os.Stat(filepath.Join(CurProject().Target(), "start_guru.log"))
181183
assert.NoError(suite.T(), err)
182184
}

cmd/gbc/artifact/plugin.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package artifact
22

33
import (
4-
"bufio"
54
"encoding/json"
65
"fmt"
7-
"github.com/samber/lo"
6+
"github.com/fatih/color" //nolint
7+
"github.com/samber/lo" //nolint
88
"io/fs"
99
"os"
1010
"os/exec"
@@ -48,22 +48,23 @@ func (plugin *Plugin) init() error {
4848
}
4949
plugin.name, _ = lo.Last(strings.Split(plugin.module, "/"))
5050
if plugin.version == "latest" {
51-
plugin.version = LatestVersion(plugin.module)[0].B
51+
plugin.version = LatestVersion(true, plugin.module)[0].B
5252
}
5353
return nil
5454
}
5555

56-
func LatestVersion(modules ...string) []lo.Tuple2[string, string] {
56+
func LatestVersion(log bool, modules ...string) []lo.Tuple2[string, string] {
5757
modules = lo.Map(modules, func(item string, _ int) string {
5858
return fmt.Sprintf("%s@latest", item)
5959
})
60-
output, _ := exec.Command("go", append([]string{"list", "-m"}, modules...)...).CombinedOutput() //nolint
61-
scanner := bufio.NewScanner(strings.NewReader(string(output)))
60+
cmd := exec.Command("go", append([]string{"list", "-m"}, modules...)...) //nolint
6261
var tuple []lo.Tuple2[string, string]
63-
for scanner.Scan() {
64-
line := strings.TrimSpace(scanner.Text())
62+
if err := PtyCmdOutput(cmd, "checking dependencies ......", log, func(line string) string {
6563
entry := strings.Split(line, " ")
6664
tuple = append(tuple, lo.Tuple2[string, string]{A: entry[0], B: entry[1]})
65+
return ""
66+
}); err != nil {
67+
color.Yellow("failed to get latest version: %v", modules)
6768
}
6869
return tuple
6970
}
@@ -128,8 +129,7 @@ func (plugin Plugin) install() (string, error) {
128129
}
129130
return pair
130131
})
131-
task := fmt.Sprintf("%s installation", plugin.Name())
132-
if err := PtyCmdOutput(cmd, task, func(msg string) string {
132+
if err := PtyCmdOutput(cmd, fmt.Sprintf("install %s", plugin.Name()), false, func(msg string) string {
133133
return ""
134134
}); err != nil {
135135
return tempGoPath, err
@@ -160,7 +160,7 @@ func (plugin Plugin) Execute() error {
160160
}
161161
// always use absolute path
162162
pCmd := exec.Command(filepath.Join(GoPath(), plugin.Binary()), strings.Split(plugin.Args, " ")...) //nolint #gosec
163-
if err := PtyCmdOutput(pCmd, plugin.taskName(), nil); err != nil {
163+
if err := PtyCmdOutput(pCmd, fmt.Sprintf("start %s", plugin.taskName()), true, nil); err != nil {
164164
return err
165165
}
166166
if pCmd.ProcessState.ExitCode() != 0 {

cmd/gbc/artifact/project.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ var (
3030
)
3131

3232
type Project struct {
33-
root string
34-
mod *modfile.File
35-
cfgs sync.Map // store all the configuration
36-
pkgs []*packages.Package
33+
root string
34+
mod *modfile.File
35+
cfgs sync.Map // store all the configuration
36+
pkgs []*packages.Package
37+
cachDir string
3738
}
3839

3940
func (project *Project) load() *viper.Viper {
@@ -99,6 +100,16 @@ func init() {
99100
if err != nil {
100101
log.Fatal(color.RedString("failed to load project %s", err.Error()))
101102
}
103+
homeDir, err := os.UserHomeDir()
104+
if err != nil {
105+
return
106+
}
107+
project.cachDir = filepath.Join(homeDir, ".gob", project.Module())
108+
_ = os.MkdirAll(project.cachDir, os.ModePerm)
109+
}
110+
111+
func (project *Project) CacheDir() string {
112+
return project.cachDir
102113
}
103114

104115
// CurProject return Project struct

cmd/gbc/artifact/pty_writer.go

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package artifact
33
import (
44
"bufio"
55
"fmt"
6+
"github.com/kcmvp/gob/utils" //nolint
67
"io"
78
"os"
89
"os/exec"
@@ -11,15 +12,13 @@ import (
1112
"strings"
1213
"time"
1314

14-
"github.com/fatih/color"
15-
"github.com/samber/lo"
16-
1715
"github.com/creack/pty"
16+
"github.com/fatih/color"
1817
)
1918

2019
type consoleFormatter func(msg string) string
2120

22-
func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error {
21+
func PtyCmdOutput(cmd *exec.Cmd, task string, file bool, formatter consoleFormatter) error {
2322
// Start the command with a pty
2423
rc, err := func() (io.ReadCloser, error) {
2524
if Windows() {
@@ -36,14 +35,15 @@ func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error
3635
}
3736
defer rc.Close()
3837
scanner := bufio.NewScanner(rc)
39-
color.Green("start %s ......\n", task)
40-
// Create a file to save the output
41-
log, err := os.Create(filepath.Join(CurProject().Target(), fmt.Sprintf("%s.log", strings.ReplaceAll(task, " ", "_"))))
42-
if err != nil {
43-
return fmt.Errorf(color.RedString("Error creating file:", err))
38+
color.Green(task)
39+
var log *os.File
40+
if file {
41+
log, err = os.Create(filepath.Join(CurProject().Target(), fmt.Sprintf("%s.log", strings.ReplaceAll(task, " ", "_"))))
42+
if err != nil {
43+
return fmt.Errorf(color.RedString("Error creating file:", err.Error()))
44+
}
45+
defer log.Close()
4446
}
45-
defer log.Close()
46-
4747
// Create a regular expression to match color escape sequences
4848
colorRegex := regexp.MustCompile(`\x1b\[[0-9;]*m`)
4949
// Goroutine to remove color escape sequences, print the colored output, and write the modified output to the file
@@ -61,26 +61,29 @@ func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error
6161
}
6262
eof = true
6363
if err = scanner.Err(); err != nil {
64-
fmt.Println("Error reading output:", err)
64+
color.Red("Error reading output: %s", err.Error())
6565
}
6666
}()
67-
ticker := time.NewTicker(150 * time.Millisecond)
6867
overwrite := true
6968
progress := NewProgress()
69+
ticker := time.NewTicker(150 * time.Millisecond)
7070
for !eof {
7171
select {
72-
case line := <-ch:
72+
case msg := <-ch:
7373
progress.Reset()
74-
lineWithoutColor := colorRegex.ReplaceAllString(line, "")
75-
_, err = log.WriteString(lineWithoutColor + "\n")
76-
line = lo.IfF(overwrite, func() string {
74+
if file {
75+
lineWithoutColor := colorRegex.ReplaceAllString(msg, "")
76+
_, err = log.WriteString(lineWithoutColor + "\n")
77+
if err != nil {
78+
color.Red("Error writing to file: %s", err.Error())
79+
break
80+
}
81+
}
82+
if overwrite {
7783
overwrite = false
78-
return fmt.Sprintf("\r%-15s", line)
79-
}).Else(line)
80-
fmt.Println(line)
81-
if err != nil {
82-
fmt.Println("Error writing to file:", err)
83-
break
84+
fmt.Printf("\r%-15s\n", msg)
85+
} else {
86+
fmt.Println(msg)
8487
}
8588
case <-ticker.C:
8689
if !overwrite {
@@ -89,8 +92,11 @@ func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error
8992
_ = progress.Add(1)
9093
}
9194
}
92-
_ = progress.Finish()
93-
color.Green("\rfinished %s ......\n", task)
95+
if test, _ := utils.TestCaller(); test {
96+
fmt.Printf("\r%-15s\n", "")
97+
} else {
98+
progress.Clear() //nolint
99+
}
94100
ticker.Stop()
95101
return cmd.Wait()
96102
}

cmd/gbc/command/build_action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func cleanAction(_ *cobra.Command, _ ...string) error {
126126
func testAction(_ *cobra.Command, _ ...string) error {
127127
coverProfile := fmt.Sprintf("-coverprofile=%s/cover.out", artifact.CurProject().Target())
128128
testCmd := exec.Command("go", []string{"test", "-v", coverProfile, "./..."}...) //nolint
129-
return artifact.PtyCmdOutput(testCmd, "test", nil)
129+
return artifact.PtyCmdOutput(testCmd, "start test", true, nil)
130130
}
131131

132132
func coverReport(_ *cobra.Command, _ ...string) error {

cmd/gbc/command/deps.go

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,39 @@ var (
2121
yellow = color.New(color.FgYellow)
2222
)
2323

24-
// dependencyTree build dependency tree of the project, an empty tree returns when runs into error
25-
func dependencyTree() (treeprint.Tree, error) {
24+
func directLatest() []lo.Tuple2[string, string] {
2625
exec.Command("go", "mod", "tidy").CombinedOutput() //nolint
27-
tree := treeprint.New()
28-
tree.SetValue(artifact.CurProject().Module())
2926
directs := lo.FilterMap(artifact.CurProject().Dependencies(), func(item *modfile.Require, _ int) (lo.Tuple2[string, string], bool) {
3027
return lo.Tuple2[string, string]{A: item.Mod.Path, B: item.Mod.Version}, !item.Indirect
3128
})
32-
// get the latest version
33-
versions := artifact.LatestVersion(lo.Map(directs, func(item lo.Tuple2[string, string], _ int) string {
29+
return artifact.LatestVersion(false, lo.Map(directs, func(item lo.Tuple2[string, string], _ int) string {
3430
return item.A
3531
})...)
32+
}
33+
34+
func upgradeAll() error {
35+
candidates := lo.Filter(directLatest(), func(latest lo.Tuple2[string, string], _ int) bool {
36+
return lo.ContainsBy(artifact.CurProject().Dependencies(), func(dependency *modfile.Require) bool {
37+
return !dependency.Indirect && dependency.Mod.Path == latest.A && dependency.Mod.Version != latest.B
38+
})
39+
})
40+
args := lo.Union([]string{"get", "-u"}, lo.Map(candidates, func(latest lo.Tuple2[string, string], _ int) string {
41+
return latest.A
42+
}))
43+
cmd := exec.Command("go", args...)
44+
if err := artifact.PtyCmdOutput(cmd, "upgrading dependencies ......", false, nil); err != nil {
45+
color.Red("failed to upgrade dependencies: %s", err.Error())
46+
}
47+
exec.Command("go", "mod", "tidy").CombinedOutput() //nolint
48+
return nil
49+
}
50+
51+
// dependencyTree build dependency tree of the project, an empty tree returns when runs into error
52+
func dependencyTree() (treeprint.Tree, error) {
53+
tree := treeprint.New()
54+
tree.SetValue(artifact.CurProject().Module())
55+
// get the latest version
56+
versions := directLatest()
3657
// parse the dependency tree
3758
cache := []string{os.Getenv("GOPATH"), "pkg", "mod", "cache", "download"}
3859
for _, dependency := range artifact.CurProject().Dependencies() {
@@ -54,14 +75,13 @@ func dependencyTree() (treeprint.Tree, error) {
5475
continue
5576
}
5677
mod, _ := modfile.Parse("go.mod", data, nil)
57-
children := lo.Filter(artifact.CurProject().Dependencies(), func(p *modfile.Require, _ int) bool {
58-
return p.Indirect && lo.ContainsBy(mod.Require, func(c *modfile.Require) bool {
59-
return !c.Indirect && p.Mod.Path == c.Mod.Path
60-
})
78+
lo.ForEach(artifact.CurProject().Dependencies(), func(c *modfile.Require, index int) {
79+
if c.Indirect && lo.ContainsBy(mod.Require, func(m *modfile.Require) bool {
80+
return !m.Indirect && c.Mod.Path == m.Mod.Path
81+
}) {
82+
direct.AddNode(c.Mod.String())
83+
}
6184
})
62-
for _, c := range children {
63-
direct.AddNode(c.Mod.String())
64-
}
6585
}
6686
}
6787
return tree, nil
@@ -74,14 +94,18 @@ var depCmd = &cobra.Command{
7494
Long: `Show the dependency tree of the project
7595
and indicate available updates which take an green * indicator`,
7696
RunE: func(cmd *cobra.Command, args []string) error {
97+
upgrade, _ := cmd.Flags().GetBool("upgrade")
98+
if upgrade {
99+
return upgradeAll()
100+
}
77101
tree, err := dependencyTree()
78102
if err != nil {
79103
return err
80104
} else if tree == nil {
81105
yellow.Println("No dependencies !")
82106
return nil
83107
}
84-
green.Println("Dependencies of the projects:")
108+
fmt.Println("\rDependencies of the projects:")
85109
fmt.Println(tree.String())
86110
return nil
87111
},
@@ -90,5 +114,6 @@ and indicate available updates which take an green * indicator`,
90114
func init() {
91115
depCmd.SetUsageTemplate(usageTemplate())
92116
depCmd.SetErrPrefix(color.RedString("Error:"))
117+
depCmd.Flags().BoolP("upgrade", "u", false, "upgrade dependencies if outdated dependencies exist")
93118
rootCmd.AddCommand(depCmd)
94119
}

0 commit comments

Comments
 (0)