Skip to content

Commit

Permalink
Merge pull request #116 from kcmvp/upgrade
Browse files Browse the repository at this point in the history
 #115: add upgrade command and fix last '%' on progress bar
  • Loading branch information
kcmvp authored Jun 16, 2024
2 parents 42b8c83 + 63d79fd commit 15a229c
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 78 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21.4'
go-version: '1.22.2'
- name: Test
run: go test -v ./... -coverprofile=cover.out
- name: Upload coverage reports to Codecov
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ gob
11.json
target
*.sql
data
18 changes: 10 additions & 8 deletions cmd/gbc/artifact/internal_plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/kcmvp/gob/utils"
"github.com/samber/lo"
"github.com/samber/lo" //nolint
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/tidwall/gjson"
Expand All @@ -16,7 +16,8 @@ import (

type InternalPluginTestSuit struct {
suite.Suite
lintLatestVersion string
lintLatestVersion string
gotestsumLatestVersion string
}

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

func TestInternalPluginSuite(t *testing.T) {
suite.Run(t, &InternalPluginTestSuit{
lintLatestVersion: LatestVersion("github.com/golangci/golangci-lint")[0].B,
lintLatestVersion: LatestVersion(false, "github.com/golangci/golangci-lint")[0].B,
gotestsumLatestVersion: LatestVersion(false, "gotest.tools/gotestsum")[0].B,
})
}

Expand Down Expand Up @@ -68,7 +70,7 @@ func (suite *InternalPluginTestSuit) TestNewPlugin() {
wantErr: false,
},
{
name: "has @ but no version",
name: "has_@ but no version",
url: "github.com/golangci/golangci-lint/cmd/golangci-lint@",
module: "github.com/golangci/golangci-lint",
logName: "",
Expand All @@ -84,7 +86,7 @@ func (suite *InternalPluginTestSuit) TestNewPlugin() {
wantErr: true,
},
{
name: "multiple @",
name: "multiple@",
url: "github.com/golangci/golangci-lint/cmd/golangci@-lint@v1",
module: "github.com/golangci/golangci-lint",
logName: "",
Expand All @@ -96,7 +98,7 @@ func (suite *InternalPluginTestSuit) TestNewPlugin() {
url: "gotest.tools/gotestsum",
module: "gotest.tools/gotestsum",
logName: "gotestsum",
binary: "gotestsum-v1.11.0",
binary: fmt.Sprintf("%s-%s", "gotestsum", suite.gotestsumLatestVersion),
wantErr: false,
},
}
Expand Down Expand Up @@ -138,7 +140,7 @@ func (suite *InternalPluginTestSuit) TestUnmarshalJSON() {
return plugin.Url == "gotest.tools/gotestsum"
})
assert.True(t, ok)
assert.Equal(t, "v1.11.0", plugin.Version())
assert.Equal(t, suite.gotestsumLatestVersion, plugin.Version())
assert.Equal(t, "gotestsum", plugin.Name())
assert.Equal(t, "gotest.tools/gotestsum", plugin.Module())
assert.Equal(t, "test", plugin.Alias)
Expand Down Expand Up @@ -177,6 +179,6 @@ func (suite *InternalPluginTestSuit) TestExecute() {
assert.Error(t, err)
//'exit status 2' means the plugin is executed but no parameters,
assert.Equal(t, "exit status 2", err.Error())
_, err = os.Stat(filepath.Join(CurProject().Target(), "guru.log"))
_, err = os.Stat(filepath.Join(CurProject().Target(), "start_guru.log"))
assert.NoError(suite.T(), err)
}
22 changes: 11 additions & 11 deletions cmd/gbc/artifact/plugin.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package artifact

import (
"bufio"
"encoding/json"
"fmt"
"github.com/samber/lo"
"github.com/fatih/color" //nolint
"github.com/samber/lo" //nolint
"io/fs"
"os"
"os/exec"
Expand Down Expand Up @@ -48,22 +48,23 @@ func (plugin *Plugin) init() error {
}
plugin.name, _ = lo.Last(strings.Split(plugin.module, "/"))
if plugin.version == "latest" {
plugin.version = LatestVersion(plugin.module)[0].B
plugin.version = LatestVersion(true, plugin.module)[0].B
}
return nil
}

func LatestVersion(modules ...string) []lo.Tuple2[string, string] {
func LatestVersion(log bool, modules ...string) []lo.Tuple2[string, string] {
modules = lo.Map(modules, func(item string, _ int) string {
return fmt.Sprintf("%s@latest", item)
})
output, _ := exec.Command("go", append([]string{"list", "-m"}, modules...)...).CombinedOutput() //nolint
scanner := bufio.NewScanner(strings.NewReader(string(output)))
cmd := exec.Command("go", append([]string{"list", "-m"}, modules...)...) //nolint
var tuple []lo.Tuple2[string, string]
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if err := PtyCmdOutput(cmd, "checking dependencies ......", log, func(line string) string {
entry := strings.Split(line, " ")
tuple = append(tuple, lo.Tuple2[string, string]{A: entry[0], B: entry[1]})
return ""
}); err != nil {
color.Yellow("failed to get latest version: %v", modules)
}
return tuple
}
Expand Down Expand Up @@ -128,8 +129,7 @@ func (plugin Plugin) install() (string, error) {
}
return pair
})
task := fmt.Sprintf("%s installation", plugin.Name())
if err := PtyCmdOutput(cmd, task, func(msg string) string {
if err := PtyCmdOutput(cmd, fmt.Sprintf("install %s", plugin.Name()), false, func(msg string) string {
return ""
}); err != nil {
return tempGoPath, err
Expand Down Expand Up @@ -160,7 +160,7 @@ func (plugin Plugin) Execute() error {
}
// always use absolute path
pCmd := exec.Command(filepath.Join(GoPath(), plugin.Binary()), strings.Split(plugin.Args, " ")...) //nolint #gosec
if err := PtyCmdOutput(pCmd, plugin.taskName(), nil); err != nil {
if err := PtyCmdOutput(pCmd, fmt.Sprintf("start %s", plugin.taskName()), true, nil); err != nil {
return err
}
if pCmd.ProcessState.ExitCode() != 0 {
Expand Down
19 changes: 15 additions & 4 deletions cmd/gbc/artifact/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ var (
)

type Project struct {
root string
mod *modfile.File
cfgs sync.Map // store all the configuration
pkgs []*packages.Package
root string
mod *modfile.File
cfgs sync.Map // store all the configuration
pkgs []*packages.Package
cachDir string
}

func (project *Project) load() *viper.Viper {
Expand Down Expand Up @@ -99,6 +100,16 @@ func init() {
if err != nil {
log.Fatal(color.RedString("failed to load project %s", err.Error()))
}
homeDir, err := os.UserHomeDir()
if err != nil {
return
}
project.cachDir = filepath.Join(homeDir, ".gob", project.Module())
_ = os.MkdirAll(project.cachDir, os.ModePerm)
}

func (project *Project) CacheDir() string {
return project.cachDir
}

// CurProject return Project struct
Expand Down
56 changes: 31 additions & 25 deletions cmd/gbc/artifact/pty_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package artifact
import (
"bufio"
"fmt"
"github.com/kcmvp/gob/utils" //nolint
"io"
"os"
"os/exec"
Expand All @@ -11,15 +12,13 @@ import (
"strings"
"time"

"github.com/fatih/color"
"github.com/samber/lo"

"github.com/creack/pty"
"github.com/fatih/color"
)

type consoleFormatter func(msg string) string

func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error {
func PtyCmdOutput(cmd *exec.Cmd, task string, file bool, formatter consoleFormatter) error {
// Start the command with a pty
rc, err := func() (io.ReadCloser, error) {
if Windows() {
Expand All @@ -36,14 +35,15 @@ func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error
}
defer rc.Close()
scanner := bufio.NewScanner(rc)
color.Green("start %s ......\n", task)
// Create a file to save the output
log, err := os.Create(filepath.Join(CurProject().Target(), fmt.Sprintf("%s.log", strings.ReplaceAll(task, " ", "_"))))
if err != nil {
return fmt.Errorf(color.RedString("Error creating file:", err))
color.Green(task)
var log *os.File
if file {
log, err = os.Create(filepath.Join(CurProject().Target(), fmt.Sprintf("%s.log", strings.ReplaceAll(task, " ", "_"))))
if err != nil {
return fmt.Errorf(color.RedString("Error creating file:", err.Error()))
}
defer log.Close()
}
defer log.Close()

// Create a regular expression to match color escape sequences
colorRegex := regexp.MustCompile(`\x1b\[[0-9;]*m`)
// Goroutine to remove color escape sequences, print the colored output, and write the modified output to the file
Expand All @@ -61,26 +61,29 @@ func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error
}
eof = true
if err = scanner.Err(); err != nil {
fmt.Println("Error reading output:", err)
color.Red("Error reading output: %s", err.Error())
}
}()
ticker := time.NewTicker(150 * time.Millisecond)
overwrite := true
progress := NewProgress()
ticker := time.NewTicker(150 * time.Millisecond)
for !eof {
select {
case line := <-ch:
case msg := <-ch:
progress.Reset()
lineWithoutColor := colorRegex.ReplaceAllString(line, "")
_, err = log.WriteString(lineWithoutColor + "\n")
line = lo.IfF(overwrite, func() string {
if file {
lineWithoutColor := colorRegex.ReplaceAllString(msg, "")
_, err = log.WriteString(lineWithoutColor + "\n")
if err != nil {
color.Red("Error writing to file: %s", err.Error())
break
}
}
if overwrite {
overwrite = false
return fmt.Sprintf("\r%-15s", line)
}).Else(line)
fmt.Println(line)
if err != nil {
fmt.Println("Error writing to file:", err)
break
fmt.Printf("\r%-15s\n", msg)
} else {
fmt.Println(msg)
}
case <-ticker.C:
if !overwrite {
Expand All @@ -89,8 +92,11 @@ func PtyCmdOutput(cmd *exec.Cmd, task string, formatter consoleFormatter) error
_ = progress.Add(1)
}
}
_ = progress.Finish()
color.Green("\rfinished %s ......\n", task)
if test, _ := utils.TestCaller(); test {
fmt.Printf("\r%-15s\n", "")
} else {
progress.Clear() //nolint
}
ticker.Stop()
return cmd.Wait()
}
2 changes: 1 addition & 1 deletion cmd/gbc/command/build_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func cleanAction(_ *cobra.Command, _ ...string) error {
func testAction(_ *cobra.Command, _ ...string) error {
coverProfile := fmt.Sprintf("-coverprofile=%s/cover.out", artifact.CurProject().Target())
testCmd := exec.Command("go", []string{"test", "-v", coverProfile, "./..."}...) //nolint
return artifact.PtyCmdOutput(testCmd, "test", nil)
return artifact.PtyCmdOutput(testCmd, "start test", true, nil)
}

func coverReport(_ *cobra.Command, _ ...string) error {
Expand Down
53 changes: 39 additions & 14 deletions cmd/gbc/command/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,39 @@ var (
yellow = color.New(color.FgYellow)
)

// dependencyTree build dependency tree of the project, an empty tree returns when runs into error
func dependencyTree() (treeprint.Tree, error) {
func directLatest() []lo.Tuple2[string, string] {
exec.Command("go", "mod", "tidy").CombinedOutput() //nolint
tree := treeprint.New()
tree.SetValue(artifact.CurProject().Module())
directs := lo.FilterMap(artifact.CurProject().Dependencies(), func(item *modfile.Require, _ int) (lo.Tuple2[string, string], bool) {
return lo.Tuple2[string, string]{A: item.Mod.Path, B: item.Mod.Version}, !item.Indirect
})
// get the latest version
versions := artifact.LatestVersion(lo.Map(directs, func(item lo.Tuple2[string, string], _ int) string {
return artifact.LatestVersion(false, lo.Map(directs, func(item lo.Tuple2[string, string], _ int) string {
return item.A
})...)
}

func upgradeAll() error {
candidates := lo.Filter(directLatest(), func(latest lo.Tuple2[string, string], _ int) bool {
return lo.ContainsBy(artifact.CurProject().Dependencies(), func(dependency *modfile.Require) bool {
return !dependency.Indirect && dependency.Mod.Path == latest.A && dependency.Mod.Version != latest.B
})
})
args := lo.Union([]string{"get", "-u"}, lo.Map(candidates, func(latest lo.Tuple2[string, string], _ int) string {
return latest.A
}))
cmd := exec.Command("go", args...)
if err := artifact.PtyCmdOutput(cmd, "upgrading dependencies ......", false, nil); err != nil {
color.Red("failed to upgrade dependencies: %s", err.Error())
}
exec.Command("go", "mod", "tidy").CombinedOutput() //nolint
return nil
}

// dependencyTree build dependency tree of the project, an empty tree returns when runs into error
func dependencyTree() (treeprint.Tree, error) {
tree := treeprint.New()
tree.SetValue(artifact.CurProject().Module())
// get the latest version
versions := directLatest()
// parse the dependency tree
cache := []string{os.Getenv("GOPATH"), "pkg", "mod", "cache", "download"}
for _, dependency := range artifact.CurProject().Dependencies() {
Expand All @@ -54,14 +75,13 @@ func dependencyTree() (treeprint.Tree, error) {
continue
}
mod, _ := modfile.Parse("go.mod", data, nil)
children := lo.Filter(artifact.CurProject().Dependencies(), func(p *modfile.Require, _ int) bool {
return p.Indirect && lo.ContainsBy(mod.Require, func(c *modfile.Require) bool {
return !c.Indirect && p.Mod.Path == c.Mod.Path
})
lo.ForEach(artifact.CurProject().Dependencies(), func(c *modfile.Require, index int) {
if c.Indirect && lo.ContainsBy(mod.Require, func(m *modfile.Require) bool {
return !m.Indirect && c.Mod.Path == m.Mod.Path
}) {
direct.AddNode(c.Mod.String())
}
})
for _, c := range children {
direct.AddNode(c.Mod.String())
}
}
}
return tree, nil
Expand All @@ -74,14 +94,18 @@ var depCmd = &cobra.Command{
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 {
upgrade, _ := cmd.Flags().GetBool("upgrade")
if upgrade {
return upgradeAll()
}
tree, err := dependencyTree()
if err != nil {
return err
} else if tree == nil {
yellow.Println("No dependencies !")
return nil
}
green.Println("Dependencies of the projects:")
fmt.Println("\rDependencies of the projects:")
fmt.Println(tree.String())
return nil
},
Expand All @@ -90,5 +114,6 @@ and indicate available updates which take an green * indicator`,
func init() {
depCmd.SetUsageTemplate(usageTemplate())
depCmd.SetErrPrefix(color.RedString("Error:"))
depCmd.Flags().BoolP("upgrade", "u", false, "upgrade dependencies if outdated dependencies exist")
rootCmd.AddCommand(depCmd)
}
Loading

0 comments on commit 15a229c

Please sign in to comment.