diff --git a/.golangci.yaml b/.golangci.yaml old mode 100644 new mode 100755 index 5938667..1a27dcb --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,7 +3,7 @@ run: tests: false output: sort-results: true - format: html + format: tab linters: disable-all: true enable: @@ -14,8 +14,11 @@ linters: - ineffassign - staticcheck - unused - - varcheck - gosec - gofumpt - - nosnakecase - goconst + - revive +issues: + new: true + new-from-rev: HEAD + fix: true diff --git a/cmd/action/action.go b/cmd/action/action.go new file mode 100644 index 0000000..e61afcf --- /dev/null +++ b/cmd/action/action.go @@ -0,0 +1,146 @@ +package action + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/fatih/color" + "github.com/kcmvp/gob/internal" + "github.com/samber/lo" + "github.com/spf13/cobra" +) + +type ( + Execution func(cmd *cobra.Command, args ...string) error + CmdAction lo.Tuple2[string, Execution] +) + +func StreamExtCmdOutput(cmd *exec.Cmd, file string, errWords ...string) error { + // Create a pipe to capture the command's combined output + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + cmd.Stderr = cmd.Stdout + outputFile, err := os.Create(file) + if err != nil { + return err + } + defer outputFile.Close() + writer := io.MultiWriter(os.Stdout, outputFile) + err = cmd.Start() + if err != nil { + return err + } + scanner := bufio.NewScanner(stdout) + go func() { + for scanner.Scan() { + line := scanner.Text() + if lo.SomeBy(errWords, func(item string) bool { + return strings.Contains(line, item) + }) { + color.Red(line) + fmt.Fprintln(outputFile, line) + } else { + fmt.Fprintln(writer, line) + } + } + }() + // Wait for the command to finish + return cmd.Wait() +} + +func ValidBuilderArgs() []string { + builtIn := lo.Map(builtinActions, func(action CmdAction, _ int) string { + return action.A + }) + plugin := lo.Map(internal.CurProject().PluginCommands(), func(t3 lo.Tuple3[string, string, string], _ int) string { + return t3.A + }) + return append(builtIn, plugin...) +} + +func Execute(cmd *cobra.Command, args ...string) error { + var exeCmd *exec.Cmd + if plugin, ok := lo.Find(internal.CurProject().PluginCommands(), func(plugin lo.Tuple3[string, string, string]) bool { + return plugin.A == args[0] + }); ok { + exeCmd = exec.Command(plugin.B, lo.Map(strings.Split(plugin.C, ","), func(cmd string, _ int) string { + return strings.TrimSpace(cmd) + })...) // #nosec G204 + stdout, err := exeCmd.StdoutPipe() + if err != nil { + return err + } + outputFile, err := os.Create(filepath.Join(internal.CurProject().Target(), fmt.Sprintf("%s.log", args[0]))) + if err != nil { + return err + } + defer outputFile.Close() + err = exeCmd.Start() + if err != nil { + return err + } + scanner := bufio.NewScanner(stdout) + go func() { + for scanner.Scan() { + line := scanner.Text() + fmt.Fprintln(outputFile, line) + } + }() + // Wait for the command to finish + err = exeCmd.Wait() + fmt.Printf("%s report is generated at %s \n", args[0], filepath.Join(internal.CurProject().Target(), fmt.Sprintf("%s.log", args[0]))) + return err + } + if action, ok := lo.Find(builtinActions, func(action CmdAction) bool { + return action.A == args[0] + }); ok { + return action.B(cmd, args...) + } + return fmt.Errorf("can not find command %s", args[0]) +} + +// LatestVersion return the latest version of the tool by module name and version filter(eg 'v1.5.*'). +// it will return the latest version if success otherwise return an error +// This function may fail due the fact the domain 'https://github.com' is not accessible, +func LatestVersion(module, filter string) (string, error) { + ch := make(chan string, 1) + defer close(ch) + parts := strings.Split(module, "/") + module = strings.Join(parts[0:3], "/") + url := fmt.Sprintf("https://%s.git", module) + ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) //nolint govet + args := []string{"ls-remote", "--sort=-version:refname", "--tags"} + args = append(args, url) + if len(filter) > 0 { + args = append(args, filter) + } + cmd := exec.CommandContext(ctx, "git", args...) + go func() { + data, err := cmd.CombinedOutput() + if err == nil { + v, _ := lo.Last(strings.Split(strings.Split(string(data), "\n")[0], "/")) + ch <- v + } + }() + var ver string + select { + case <-ctx.Done(): + cmd.Process.Kill() //nolint errcheck + case ver = <-ch: + } + return ver, lo.IfF(ctx.Err() != nil, func() error { + return ctx.Err() + }).ElseIfF(len(ver) == 0, func() error { + return errors.New("failed to get module version") + }).Else(nil) +} diff --git a/cmd/builder/builder_func.go b/cmd/action/builder.go similarity index 73% rename from cmd/builder/builder_func.go rename to cmd/action/builder.go index 7d268e3..4d359f4 100644 --- a/cmd/builder/builder_func.go +++ b/cmd/action/builder.go @@ -1,33 +1,33 @@ -package builder +package action import ( "bufio" "fmt" - "github.com/fatih/color" - "github.com/kcmvp/gob/cmd/shared" - "github.com/kcmvp/gob/internal" - "github.com/spf13/cobra" "io/fs" "os" "os/exec" "path/filepath" "regexp" "strings" + + "github.com/fatih/color" + "github.com/kcmvp/gob/internal" + "github.com/spf13/cobra" ) -var CleanCache bool -var CleanTestCache bool -var CleanModCache bool -var LintAll bool +var ( + CleanCache bool + CleanTestCache bool + CleanModCache bool +) const ( CleanCacheFlag = "cache" CleanTestCacheFlag = "testcache" CleanModCacheFlag = "modcache" - LintAllFlag = "all" ) -var builtinActions = []shared.CmdAction{ +var builtinActions = []CmdAction{ {A: "build", B: buildCommand}, {A: "clean", B: cleanCommand}, {A: "test", B: testCommand}, @@ -84,10 +84,9 @@ var buildCommand = func(_ *cobra.Command, args ...string) error { output := filepath.Join(internal.CurProject().Root(), internal.CurProject().Target(), binary) if _, err := exec.Command("go", "build", "-o", output, mf).CombinedOutput(); err != nil { //nolint return err - } else { - fmt.Printf("Build %s to %s successfully\n", mf, output) - bm[binary] = output } + fmt.Printf("Build %s to %s successfully\n", mf, output) + bm[binary] = output } else { color.Yellow("Can not find main function in package %s", dir) } @@ -98,7 +97,7 @@ var buildCommand = func(_ *cobra.Command, args ...string) error { var cleanCommand = func(cmd *cobra.Command, _ ...string) error { // clean target folder os.RemoveAll(internal.CurProject().Target()) - os.Mkdir(internal.CurProject().Target(), os.ModePerm) + os.Mkdir(internal.CurProject().Target(), os.ModePerm) //nolint errcheck fmt.Println("Clean target folder successfully !") // clean cache args := []string{"clean"} @@ -117,20 +116,18 @@ var cleanCommand = func(cmd *cobra.Command, _ ...string) error { } return nil } + var testCommand = func(_ *cobra.Command, args ...string) error { coverProfile := fmt.Sprintf("-coverprofile=%s/cover.out", internal.CurProject().Target()) - testCmd := exec.Command("go", []string{"test", "-v", coverProfile, "./..."}...) - err := shared.StreamExtCmdOutput(testCmd, fmt.Sprintf("%s/test.log", internal.CurProject().Target()), "FAIL:") + testCmd := exec.Command("go", []string{"test", "-v", coverProfile, "./..."}...) //nolint + err := StreamExtCmdOutput(testCmd, fmt.Sprintf("%s/test.log", internal.CurProject().Target()), "FAIL:") if err != nil { return err } - exec.Command("go", []string{"tool", "cover", fmt.Sprintf("-html=%s/cover.out", internal.CurProject().Target()), fmt.Sprintf("-o=%s/cover.html", internal.CurProject().Target())}...).CombinedOutput() - color.Green("Test report is generated at %s/test.log \n", internal.CurProject().Target()) - color.Green("Coverage report is generated at %s/cover.html \n", internal.CurProject().Target()) + _, err = exec.Command("go", []string{"tool", "cover", fmt.Sprintf("-html=%s/cover.out", internal.CurProject().Target()), fmt.Sprintf("-o=%s/cover.html", internal.CurProject().Target())}...).CombinedOutput() //nolint + if err == nil { + color.Green("Test report is generated at %s/test.log \n", internal.CurProject().Target()) + color.Green("Coverage report is generated at %s/cover.html \n", internal.CurProject().Target()) + } return nil } - -func Actions() []shared.CmdAction { - pluginActions := shared.PluginActions() - return append(builtinActions, pluginActions...) -} diff --git a/cmd/builder.go b/cmd/builder.go index 66f430e..1159cd7 100644 --- a/cmd/builder.go +++ b/cmd/builder.go @@ -6,8 +6,7 @@ import ( "errors" "fmt" "github.com/fatih/color" - "github.com/kcmvp/gob/cmd/builder" - "github.com/kcmvp/gob/cmd/shared" + "github.com/kcmvp/gob/cmd/action" "github.com/kcmvp/gob/internal" "github.com/samber/lo" "github.com/spf13/cobra" @@ -16,13 +15,11 @@ import ( // builderCmd represents the base command when called without any subcommands var builderCmd = &cobra.Command{ - Use: "gob", - Short: "Go project boot", - Long: `Supply most frequently used tool and best practices for go project development`, - ValidArgs: lo.Map(builder.Actions(), func(item shared.CmdAction, _ int) string { - return item.A - }), - Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)), + Use: "gob", + Short: "Go project boot", + Long: `Supply most frequently used tool and best practices for go project development`, + ValidArgs: action.ValidBuilderArgs(), + Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)), PersistentPreRun: func(cmd *cobra.Command, args []string) { internal.CurProject().Setup(false) }, @@ -35,13 +32,11 @@ var builderCmd = &cobra.Command{ } func build(cmd *cobra.Command, args []string) error { - actions := lo.Filter(builder.Actions(), func(action shared.CmdAction, _ int) bool { - return lo.Contains(args, action.A) - }) - for _, action := range actions { - msg := fmt.Sprintf("Start %s project", action.A) + args = lo.Uniq(args) + for _, arg := range args { + msg := fmt.Sprintf("Start %s project", arg) fmt.Printf("%-20s ...... \n", msg) - if err := action.B(cmd, action.A); err != nil { + if err := action.Execute(cmd, arg); err != nil { return err } } @@ -73,8 +68,7 @@ func init() { }).Else(nil) }) builderCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - builderCmd.Flags().BoolVar(&builder.CleanCache, builder.CleanCacheFlag, false, "to remove the entire go build cache") - builderCmd.Flags().BoolVar(&builder.CleanTestCache, builder.CleanTestCacheFlag, false, "to expire all test results in the go build cache") - builderCmd.Flags().BoolVar(&builder.CleanModCache, builder.CleanModCacheFlag, false, "to remove the entire module download cache") - builderCmd.Flags().BoolVar(&builder.LintAll, builder.LintAllFlag, false, "lint scan all source code, default only on changed source code") + builderCmd.Flags().BoolVar(&action.CleanCache, action.CleanCacheFlag, false, "to remove the entire go build cache") + builderCmd.Flags().BoolVar(&action.CleanTestCache, action.CleanTestCacheFlag, false, "to expire all test results in the go build cache") + builderCmd.Flags().BoolVar(&action.CleanModCache, action.CleanModCacheFlag, false, "to remove the entire module download cache") } diff --git a/cmd/exec.go b/cmd/exec.go index 50876f6..7b59953 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -7,8 +7,7 @@ import ( "errors" "fmt" "github.com/fatih/color" - "github.com/kcmvp/gob/cmd/builder" - "github.com/kcmvp/gob/cmd/shared" + "github.com/kcmvp/gob/cmd/action" "github.com/kcmvp/gob/internal" "github.com/samber/lo" "github.com/spf13/cobra" @@ -18,8 +17,11 @@ import ( // validateCommitMsg invoked by git commit-msg hook, an error returns when it fails to validate // the commit message -var validateCommitMsg shared.Execution = func(cmd *cobra.Command, args ...string) error { - input, _ := os.ReadFile(os.Args[1]) +var validateCommitMsg action.Execution = func(cmd *cobra.Command, args ...string) error { + if len(args) < 2 { + return fmt.Errorf("please input commit message") + } + input, _ := os.ReadFile(args[1]) regex := regexp.MustCompile(`\r?\n`) commitMsg := regex.ReplaceAllString(string(input), "") pattern, _ := lo.Last(args) @@ -41,17 +43,13 @@ func exec(execution internal.Execution, cmd *cobra.Command, args ...string) erro args = append(args, execution.Actions...) return validateCommitMsg(cmd, args...) } else { - var err error - for _, action := range execution.Actions { - for _, cmdAction := range builder.Actions() { - if action == cmdAction.A { - if err = cmdAction.B(cmd, args...); err != nil { - return err - } - } + for _, arg := range execution.Actions { + fmt.Printf("start %s \n", arg) + if err := action.Execute(cmd, arg); err != nil { + return errors.New(color.RedString("failed to %s the project \n", arg)) } } - return err + return nil } } @@ -64,6 +62,9 @@ var execCmd = &cobra.Command{ if err := cobra.MaximumNArgs(3)(cmd, args); err != nil { return errors.New(color.RedString(err.Error())) } + if err := cobra.MinimumNArgs(1)(cmd, args); err != nil { + return errors.New(color.RedString(err.Error())) + } if !lo.Contains(execValidArgs, args[0]) { return errors.New(color.RedString("invalid arg %s", args[0])) } @@ -79,5 +80,4 @@ var execCmd = &cobra.Command{ func init() { builderCmd.AddCommand(execCmd) - // initialize from configuration } diff --git a/cmd/exec_test.go b/cmd/exec_test.go index 41e67fe..8757583 100644 --- a/cmd/exec_test.go +++ b/cmd/exec_test.go @@ -4,6 +4,7 @@ import ( "github.com/kcmvp/gob/internal" "github.com/samber/lo" "github.com/stretchr/testify/assert" + "os" "testing" ) @@ -18,6 +19,7 @@ func TestCmdArgs(t *testing.T) { args []string wantErr bool }{ + {"no args", []string{}, true}, {"no match", []string{lo.RandomString(10, lo.LettersCharset)}, true}, {"first match", []string{internal.CommitMsgCmd, lo.RandomString(10, lo.LettersCharset)}, false}, {"second match", []string{lo.RandomString(10, lo.LettersCharset), "msghook"}, true}, @@ -34,3 +36,27 @@ func TestCmdArgs(t *testing.T) { }) } } + +func TestValidateCommitMsg(t *testing.T) { + f, _ := os.CreateTemp("", "commit") + defer func() { + f.Close() + os.Remove(f.Name()) + }() + f.WriteString("#123: just for testing") + tests := []struct { + name string + args []string + wantErr bool + }{ + {"no msg", []string{lo.RandomString(10, lo.LettersCharset)}, true}, + {"random msg", []string{lo.RandomString(10, lo.LettersCharset), lo.RandomString(10, lo.LettersCharset)}, true}, + {"valid msg", []string{lo.RandomString(10, lo.LettersCharset), f.Name(), "^#[0-9]+:\\s*.{10,}$"}, false}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := validateCommitMsg(execCmd, test.args...) + assert.True(t, test.wantErr == (err != nil)) + }) + } +} diff --git a/cmd/initializer.go b/cmd/initializer.go index 09bcac1..241425f 100644 --- a/cmd/initializer.go +++ b/cmd/initializer.go @@ -6,7 +6,7 @@ package cmd import ( _ "embed" "fmt" - "github.com/kcmvp/gob/cmd/shared" + "github.com/kcmvp/gob/cmd/action" "github.com/kcmvp/gob/internal" "github.com/samber/lo" "github.com/spf13/cobra" @@ -27,7 +27,7 @@ var initializerFunc = func(_ *cobra.Command, _ []string) { return strings.HasPrefix(plugin.D, golangCiLinter) }) if !ok { - latest, err := shared.LatestVersion(golangCiLinter, "v1.55.*") + latest, err := action.LatestVersion(golangCiLinter, "v1.55.*") if err != nil { latest = defaultVersion } diff --git a/cmd/plugin.go b/cmd/plugin.go index 024228d..2768907 100644 --- a/cmd/plugin.go +++ b/cmd/plugin.go @@ -4,21 +4,63 @@ Copyright © 2023 NAME HERE package cmd import ( + "errors" "fmt" "github.com/fatih/color" - "github.com/kcmvp/gob/cmd/plugin" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/kcmvp/gob/internal" + "github.com/samber/lo" "github.com/spf13/cobra" + //nolint + "strings" ) +// alias is the tool alias +var alias string + +// command is the tool command +var command string + +// Install the specified tool as gob plugin +func install(args ...string) error { + if strings.HasSuffix(args[0], "@master") || strings.HasSuffix(args[0], "@latest") { + return fmt.Errorf("please use specific version instead of 'master' or 'latest'") + } + err := internal.CurProject().InstallPlugin(args[0], alias, command) + if errors.Is(err, internal.PluginExists) { + color.Yellow("Plugin %s exists", args[0]) + err = nil + } + return err +} + +func list() { + plugins := internal.CurProject().Plugins() + ct := table.Table{} + ct.SetTitle("Installed Plugins") + ct.AppendRow(table.Row{"Command", "Alias", "Method", "URL"}) + style := table.StyleDefault + style.Options.DrawBorder = true + style.Options.SeparateRows = true + style.Options.SeparateColumns = true + style.Title.Align = text.AlignCenter + style.HTML.CSSClass = table.DefaultHTMLCSSClass + ct.SetStyle(style) + rows := lo.Map(plugins, func(item lo.Tuple4[string, string, string, string], index int) table.Row { + return table.Row{item.A, item.B, item.C, item.D} + }) + ct.AppendRows(rows) + fmt.Println(ct.Render()) +} + // pluginCmd represents the plugin command var pluginCmd = &cobra.Command{ Use: "plugin", Short: "List all configured plugins", Long: `List all configured plugins`, - RunE: func(cmd *cobra.Command, args []string) error { - // run 'gob plugin' will list all the configured plugins - // run 'gob plugin -u' will list all the configured plugins and install the uninstalled tools. - return plugin.List(cmd) + Run: func(cmd *cobra.Command, args []string) { + list() }, } @@ -34,7 +76,7 @@ var installPluginCmd = &cobra.Command{ return nil }, RunE: func(cmd *cobra.Command, args []string) error { - return plugin.Install(cmd, args...) + return install(args...) }, } @@ -44,6 +86,6 @@ func init() { // init installPluginCmd pluginCmd.AddCommand(installPluginCmd) - installPluginCmd.Flags().StringVarP(&plugin.ToolAlias, "alias", "a", "", "alias of the tool") - installPluginCmd.Flags().StringVarP(&plugin.ToolCommand, "command", "c", "", "default command of this tool") + installPluginCmd.Flags().StringVarP(&alias, "alias", "a", "", "alias of the tool") + installPluginCmd.Flags().StringVarP(&command, "command", "c", "", "default command of this tool") } diff --git a/cmd/plugin/plugin_func.go b/cmd/plugin/plugin_func.go deleted file mode 100644 index fb24c8e..0000000 --- a/cmd/plugin/plugin_func.go +++ /dev/null @@ -1,68 +0,0 @@ -package plugin - -import ( - "errors" - "fmt" - "github.com/fatih/color" - "github.com/jedib0t/go-pretty/v6/table" - "github.com/jedib0t/go-pretty/v6/text" - "github.com/kcmvp/gob/cmd/shared" - "github.com/kcmvp/gob/internal" - "github.com/samber/lo" - "github.com/spf13/cobra" - "os" - "path/filepath" - "strings" -) - -// ToolAlias is the tool alias, for the convenience of run 'gob alias' -var ToolAlias string - -// ToolCommand is the tool command, it's the default command when run 'gob alias' -var ToolCommand string - -// Install the specified tool as gob plugin -var Install shared.Execution = func(cmd *cobra.Command, args ...string) error { - if strings.HasSuffix(args[0], "@master") || strings.HasSuffix(args[0], "@latest") { - return fmt.Errorf("please use specific version instead of 'master' or 'latest'") - } - err := internal.CurProject().InstallPlugin(args[0], ToolAlias, ToolCommand) - if errors.Is(err, internal.PluginExists) { - color.Yellow("Plugin %s exists", args[0]) - err = nil - } - return err -} - -var UpdateList bool -var List shared.Execution = func(cmd *cobra.Command, _ ...string) error { - plugins := internal.CurProject().Plugins() - ct := table.Table{} - ct.SetTitle("Installed Plugins") - ct.AppendRow(table.Row{"Command", "Alias", "Method", "URL"}) - style := table.StyleDefault - style.Options.DrawBorder = true - style.Options.SeparateRows = true - style.Options.SeparateColumns = true - style.Title.Align = text.AlignCenter - style.HTML.CSSClass = table.DefaultHTMLCSSClass - ct.SetStyle(style) - rows := lo.Map(plugins, func(item lo.Tuple4[string, string, string, string], index int) table.Row { - return table.Row{item.A, item.B, item.C, item.D} - }) - ct.AppendRows(rows) - shared.PrintCmd(cmd, ct.Render()) - if UpdateList { - for _, plugin := range plugins { - _, name := internal.NormalizePlugin(plugin.D) - if _, err := os.Stat(filepath.Join(os.Getenv("GOPATH"), "bin", name)); err != nil { - if err = internal.CurProject().InstallPlugin(plugin.D, plugin.A, plugin.C); err != nil { - color.Yellow("Waring: failed to install %s", plugin.D) - } - } else { - fmt.Printf("%s exists on the system \n", plugin.D) - } - } - } - return nil -} diff --git a/cmd/resources/.golangci.yaml b/cmd/resources/.golangci.yaml index 61b4b2e..1a27dcb 100755 --- a/cmd/resources/.golangci.yaml +++ b/cmd/resources/.golangci.yaml @@ -3,7 +3,7 @@ run: tests: false output: sort-results: true - format: html + format: tab linters: disable-all: true enable: @@ -18,3 +18,7 @@ linters: - gofumpt - goconst - revive +issues: + new: true + new-from-rev: HEAD + fix: true diff --git a/cmd/shared/action.go b/cmd/shared/action.go deleted file mode 100644 index 468e14c..0000000 --- a/cmd/shared/action.go +++ /dev/null @@ -1,93 +0,0 @@ -package shared - -import ( - "bufio" - "fmt" - "github.com/fatih/color" - "github.com/kcmvp/gob/internal" - "github.com/samber/lo" - "github.com/spf13/cobra" - "io" - "os" - "os/exec" - "path/filepath" - "strings" -) - -type Execution func(cmd *cobra.Command, args ...string) error - -type CmdAction lo.Tuple2[string, Execution] - -func PrintCmd(cmd *cobra.Command, msg string) error { - if ok, file := internal.TestCallee(); ok { - // Get the call stack - outputFile, err := os.Create(filepath.Join(internal.CurProject().Target(), file)) - if err != nil { - return err - } - defer outputFile.Close() - writer := io.MultiWriter(os.Stdout, outputFile) - fmt.Fprintln(writer, msg) - } else { - cmd.Println(msg) - } - return nil -} - -func StreamExtCmdOutput(cmd *exec.Cmd, file string, errWords ...string) error { - // Create a pipe to capture the command's combined output - stdout, err := cmd.StdoutPipe() - if err != nil { - return err - } - cmd.Stderr = cmd.Stdout - outputFile, err := os.Create(file) - if err != nil { - return err - } - defer outputFile.Close() - writer := io.MultiWriter(os.Stdout, outputFile) - err = cmd.Start() - if err != nil { - return err - } - scanner := bufio.NewScanner(stdout) - go func() { - for scanner.Scan() { - line := scanner.Text() - if lo.SomeBy(errWords, func(item string) bool { - return strings.Contains(line, item) - }) { - color.Red(line) - fmt.Fprintln(outputFile, line) - } else { - fmt.Fprintln(writer, line) - } - } - }() - // Wait for the command to finish - return cmd.Wait() -} - -func execPlugin(cmd *cobra.Command, args ...string) error { - plugin, ok := lo.Find(internal.CurProject().PluginCommands(), func(plugin lo.Tuple3[string, string, string]) bool { - return plugin.A == args[0] - }) - if !ok { - return fmt.Errorf("plugin %s is not conigured", args[0]) - } - cmds := strings.Split(plugin.C, ",") - exeCmd := exec.Command(plugin.B, lo.Map(cmds, func(cmd string, _ int) string { - return strings.TrimSpace(cmd) - })...) - return StreamExtCmdOutput(exeCmd, filepath.Join(internal.CurProject().Target(), fmt.Sprintf("%s.html", args[0])), "") -} - -func PluginActions() []CmdAction { - return lo.Map(internal.CurProject().PluginCommands(), func(plugin lo.Tuple3[string, string, string], _ int) CmdAction { - return CmdAction{ - A: plugin.A, - B: execPlugin, - } - }) -} diff --git a/cmd/shared/action_test.go b/cmd/shared/action_test.go deleted file mode 100644 index 9be0a61..0000000 --- a/cmd/shared/action_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package shared - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestLatestTag(t *testing.T) { - // git@github.com:golangci/golangci-lint.git - //ver, err := LatestVersion("https://github.com/golangci/golangci-lint.git", "v1.55.*") - ver, err := LatestVersion("github.com/golangci/golangci-lint/cmd/golangci-lint", "v1.55.*") - assert.NoError(t, err) - assert.Equal(t, "v1.55.2", ver) -} diff --git a/cmd/shared/func.go b/cmd/shared/func.go deleted file mode 100644 index f34cb7d..0000000 --- a/cmd/shared/func.go +++ /dev/null @@ -1,47 +0,0 @@ -package shared - -import ( - "context" - "errors" - "fmt" - "github.com/samber/lo" - "os/exec" - "strings" - "time" -) - -// LatestVersion return the latest version of the tool by module name and version filter(eg 'v1.5.*'). -// it will return the latest version if success otherwise return an error -// This function may fail due the fact the domain 'https://github.com' is not accessible, -func LatestVersion(module, filter string) (string, error) { - ch := make(chan string, 1) - defer close(ch) - parts := strings.Split(module, "/") - module = strings.Join(parts[0:3], "/") - url := fmt.Sprintf("https://%s.git", module) - ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) - args := []string{"ls-remote", "--sort=-version:refname", "--tags"} - args = append(args, url) - if len(filter) > 0 { - args = append(args, filter) - } - cmd := exec.CommandContext(ctx, "git", args...) - go func() { - data, err := cmd.CombinedOutput() - if err == nil { - v, _ := lo.Last(strings.Split(strings.Split(string(data), "\n")[0], "/")) - ch <- v - } - }() - var ver string - select { - case <-ctx.Done(): - cmd.Process.Kill() - case ver = <-ch: - } - return ver, lo.IfF(ctx.Err() != nil, func() error { - return ctx.Err() - }).ElseIfF(len(ver) == 0, func() error { - return errors.New("failed to get module version") - }).Else(nil) -} diff --git a/gob.yaml b/gob.yaml index 0b18674..35ff56a 100644 --- a/gob.yaml +++ b/gob.yaml @@ -1,13 +1,13 @@ exec: - commit-msg-hook: ^#[0-9]+:\s*.{10,}$ - pre-commit-hook: - - list - - test - pre-push-hook: - - list - - test + commit-msg-hook: ^#[0-9]+:\s*.{10,}$ + pre-commit-hook: + - lint + - test + pre-push-hook: + - lint + - test plugins: - golangci-lint: - alias: lint - command: run, ./... - url: github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.1 + golangci-lint: + alias: lint + command: run, ./... + url: github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.1