github.com/kcmvp/gob@v1.0.17/cmd/gbc/command/build_action.go (about)

     1  package command
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"github.com/kcmvp/gob/cmd/gbc/artifact" //nolint
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"regexp"
    13  	"slices"
    14  	"strings"
    15  
    16  	"github.com/fatih/color"
    17  	"github.com/samber/lo" //nolint
    18  	"github.com/spf13/cobra"
    19  )
    20  
    21  type (
    22  	Execution func(cmd *cobra.Command, args ...string) error
    23  	Action    lo.Tuple3[string, Execution, string]
    24  )
    25  
    26  func buildActions() []Action {
    27  	return []Action{
    28  		{A: "build", B: buildAction, C: "build all main methods which in main package and name the binary as file name"},
    29  		{A: "clean", B: cleanAction, C: "clean project target folder"},
    30  		{A: "test", B: testAction, C: "test the project and generate coverage report in target folder"},
    31  		{A: "after_test", B: coverReport},
    32  		{A: "after_lint", B: nolintReport},
    33  	}
    34  }
    35  func (a Action) String() string {
    36  	return fmt.Sprintf("%s: %s", a.A, a.A)
    37  }
    38  
    39  func beforeExecution(cmd *cobra.Command, arg string) error {
    40  	if action, ok := lo.Find(buildActions(), func(action Action) bool {
    41  		return action.A == fmt.Sprintf("before_%s", arg)
    42  	}); ok {
    43  		return action.B(cmd, arg)
    44  	}
    45  	return nil
    46  }
    47  
    48  func afterExecution(cmd *cobra.Command, arg string) {
    49  	if action, ok := lo.Find(buildActions(), func(action Action) bool {
    50  		return action.A == fmt.Sprintf("after_%s", arg)
    51  	}); ok {
    52  		action.B(cmd, arg) //nolint
    53  	}
    54  }
    55  
    56  func execute(cmd *cobra.Command, arg string) error {
    57  	beforeExecution(cmd, arg) //nolint
    58  	var err error
    59  	if plugin, ok := lo.Find(artifact.CurProject().Plugins(), func(plugin artifact.Plugin) bool {
    60  		return plugin.Alias == arg
    61  	}); ok {
    62  		err = plugin.Execute()
    63  	} else if action, ok := lo.Find(buildActions(), func(action Action) bool {
    64  		return action.A == arg
    65  	}); ok {
    66  		err = action.B(cmd, arg)
    67  	} else {
    68  		return fmt.Errorf(color.RedString("can not find command %s", arg))
    69  	}
    70  	if err != nil {
    71  		return err
    72  	}
    73  	afterExecution(cmd, arg)
    74  	return nil
    75  }
    76  
    77  func validBuilderArgs() []string {
    78  	builtIn := lo.Map(lo.Filter(buildActions(), func(item Action, _ int) bool {
    79  		return !strings.Contains(item.A, "_")
    80  	}), func(action Action, _ int) string {
    81  		return action.A
    82  	})
    83  	lo.ForEach(artifact.CurProject().Plugins(), func(item artifact.Plugin, _ int) {
    84  		if !lo.Contains(builtIn, item.Alias) {
    85  			builtIn = append(builtIn, item.Alias)
    86  		}
    87  	})
    88  	return builtIn
    89  }
    90  
    91  func buildAction(_ *cobra.Command, _ ...string) error {
    92  	bm := map[string]string{}
    93  	for _, mainFile := range artifact.CurProject().MainFiles() {
    94  		binary := strings.TrimSuffix(filepath.Base(mainFile), ".go")
    95  		if f, exists := bm[binary]; exists {
    96  			return fmt.Errorf("file %s has already built as %s, please rename %s", f, binary, mainFile)
    97  		}
    98  		output := filepath.Join(artifact.CurProject().Target(), binary)
    99  		versionFlag := fmt.Sprintf("-X 'main.buildVersion=%s'", artifact.Version())
   100  		if msg, err := exec.Command("go", "build", "-o", output, "-ldflags", versionFlag, mainFile).CombinedOutput(); err != nil { //nolint
   101  			return errors.New(color.RedString(string(msg)))
   102  		}
   103  		fmt.Printf("Build project successfully %s\n", output)
   104  		bm[binary] = output
   105  	}
   106  	if len(bm) == 0 {
   107  		color.Yellow("Can't find main methods")
   108  	}
   109  	return nil
   110  }
   111  
   112  func cleanAction(_ *cobra.Command, _ ...string) error {
   113  	// clean target folder
   114  	os.RemoveAll(artifact.CurProject().Target())
   115  	os.Mkdir(artifact.CurProject().Target(), os.ModePerm) //nolint errcheck
   116  	fmt.Println("Clean target folder successfully !")
   117  	// clean cache
   118  	args := []string{"clean"}
   119  	_, err := exec.Command("go", args...).CombinedOutput()
   120  	if err != nil {
   121  		color.Red("failed to clean the project cache %s", err.Error())
   122  	}
   123  	return err
   124  }
   125  
   126  func testAction(_ *cobra.Command, _ ...string) error {
   127  	coverProfile := fmt.Sprintf("-coverprofile=%s/cover.out", artifact.CurProject().Target())
   128  	testCmd := exec.Command("go", []string{"test", "-v", coverProfile, "./..."}...) //nolint
   129  	return artifact.StreamCmdOutput(testCmd, "test", nil)
   130  }
   131  
   132  func coverReport(_ *cobra.Command, _ ...string) error {
   133  	target := artifact.CurProject().Target()
   134  	_, err := os.Stat(filepath.Join(target, "cover.out"))
   135  	if err == nil {
   136  		if _, err = exec.Command("go", []string{"tool", "cover", fmt.Sprintf("-html=%s/cover.out", target), fmt.Sprintf("-o=%s/cover.html", target)}...).CombinedOutput(); err == nil { //nolint
   137  			fmt.Printf("Coverage report is generated at %s/cover.html \n", target)
   138  			return nil
   139  		}
   140  		return fmt.Errorf(color.RedString("Failed to generate coverage report %s", err.Error()))
   141  	}
   142  	return fmt.Errorf(color.RedString("Failed to generate coverage report %s", err.Error()))
   143  }
   144  
   145  func nolintReport(_ *cobra.Command, _ ...string) error {
   146  	_ = os.Chdir(artifact.CurProject().Root())
   147  	reg := regexp.MustCompile(`//\s*nolint`)
   148  	data, _ := exec.Command("go", "list", "-f", `{{.Dir}}:{{join .GoFiles " "}}`, `./...`).CombinedOutput()
   149  	scanner := bufio.NewScanner(bytes.NewBuffer(data))
   150  	var ignoreList []lo.Tuple3[string, int, int]
   151  	var maxLength, fOverAll, lOverAll int
   152  	for scanner.Scan() {
   153  		line := scanner.Text()
   154  		items := strings.Split(line, ":")
   155  		for _, file := range strings.Split(items[1], " ") {
   156  			var fIgnore, lIgnore int
   157  			abs := filepath.Join(items[0], file)
   158  			relative := strings.TrimPrefix(abs, artifact.CurProject().Root()+"/")
   159  			maxLength = lo.If(maxLength > len(relative), maxLength).Else(len(relative))
   160  			source, _ := os.ReadFile(abs)
   161  			sourceScanner := bufio.NewScanner(bytes.NewBuffer(source))
   162  			for sourceScanner.Scan() {
   163  				line = sourceScanner.Text()
   164  				if reg.MatchString(line) {
   165  					if strings.HasPrefix(strings.TrimPrefix(line, " "), "//") {
   166  						fIgnore++
   167  						lIgnore++
   168  					} else {
   169  						lIgnore++
   170  					}
   171  				}
   172  			}
   173  			fOverAll += fIgnore
   174  			lOverAll += lIgnore
   175  			if fIgnore+lIgnore > 0 {
   176  				ignoreList = append(ignoreList, lo.Tuple3[string, int, int]{A: relative, B: fIgnore, C: lIgnore})
   177  			}
   178  		}
   179  	}
   180  	maxLength += 5
   181  	if fOverAll+lOverAll > 0 {
   182  		color.Yellow("[Lint Report]:file level ignores: %d, line level ignores: %d", fOverAll, lOverAll)
   183  		slices.SortFunc(ignoreList, func(a, b lo.Tuple3[string, int, int]) int {
   184  			return b.C - a.C
   185  		})
   186  		// Open a file for writing
   187  		format := fmt.Sprintf("%%-%ds L:%%-10d F:%%-5d\n", maxLength)
   188  		file, _ := os.Create(filepath.Join(artifact.CurProject().Target(), "lint-ignore.log"))
   189  		defer file.Close()
   190  		for _, line := range ignoreList {
   191  			_, _ = file.WriteString(fmt.Sprintf(format, line.A, line.C, line.B))
   192  		}
   193  	}
   194  
   195  	return nil
   196  }