Skip to content

Commit

Permalink
Merge pull request #22 from kcmvp/hook
Browse files Browse the repository at this point in the history
#20: add test for hook
  • Loading branch information
kcmvp authored Dec 26, 2023
2 parents a22cccd + 82ed479 commit a0edcab
Show file tree
Hide file tree
Showing 14 changed files with 295 additions and 305 deletions.
9 changes: 6 additions & 3 deletions .golangci.yaml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ run:
tests: false
output:
sort-results: true
format: html
format: tab
linters:
disable-all: true
enable:
Expand All @@ -14,8 +14,11 @@ linters:
- ineffassign
- staticcheck
- unused
- varcheck
- gosec
- gofumpt
- nosnakecase
- goconst
- revive
issues:
new: true
new-from-rev: HEAD
fix: true
146 changes: 146 additions & 0 deletions cmd/action/action.go
Original file line number Diff line number Diff line change
@@ -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)
}
47 changes: 22 additions & 25 deletions cmd/builder/builder_func.go → cmd/action/builder.go
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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)
}
Expand All @@ -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"}
Expand All @@ -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...)
}
32 changes: 13 additions & 19 deletions cmd/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
},
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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")
}
28 changes: 14 additions & 14 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand All @@ -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
}
}

Expand All @@ -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]))
}
Expand All @@ -79,5 +80,4 @@ var execCmd = &cobra.Command{

func init() {
builderCmd.AddCommand(execCmd)
// initialize from configuration
}
Loading

0 comments on commit a0edcab

Please sign in to comment.