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 }