Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#115: add upgrade command and fix last '%' on progress bar #116

Merged
merged 3 commits into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading