github.com/mongodb/grip@v0.0.0-20240213223901-f906268d82b9/cmd/run-linter/run-linter.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 "time" 12 ) 13 14 type result struct { 15 name string 16 cmd string 17 passed bool 18 duration time.Duration 19 output []string 20 } 21 22 // String prints the results of a linter run in gotest format. 23 func (r *result) String() string { 24 buf := &bytes.Buffer{} 25 26 fmt.Fprintln(buf, "=== RUN", r.name) 27 if r.passed { 28 fmt.Fprintf(buf, "--- PASS: %s (%s)", r.name, r.duration) 29 } else { 30 fmt.Fprintf(buf, strings.Join(r.output, "\n")) 31 fmt.Fprintf(buf, "--- FAIL: %s (%s)", r.name, r.duration) 32 } 33 34 return buf.String() 35 } 36 37 // fixup goes through the output and improves the output generated by 38 // specific linters so that all output includes the relative path to the 39 // error, instead of mixing relative and absolute paths. 40 func (r *result) fixup(dirname string) { 41 for idx, ln := range r.output { 42 if strings.HasPrefix(ln, dirname) { 43 r.output[idx] = ln[len(dirname)+1:] 44 } 45 } 46 } 47 48 // runs the golangci-lint on a list of packages; integrating with the "make lint" target. 49 func main() { 50 var ( 51 lintArgs string 52 lintBin string 53 customLintersFlag string 54 customLinters []string 55 packageList string 56 output string 57 packages []string 58 results []*result 59 hasFailingTest bool 60 ) 61 62 flag.StringVar(&lintArgs, "lintArgs", "", "args to pass to golangci-lint") 63 flag.StringVar(&lintBin, "lintBin", "", "path to golangci-lint") 64 flag.StringVar(&packageList, "packages", "", "list of space separated packages") 65 flag.StringVar(&customLintersFlag, "customLinters", "", "list of comma-separated custom linter commands") 66 flag.StringVar(&output, "output", "", "output file for to write results.") 67 flag.Parse() 68 69 if len(customLintersFlag) != 0 { 70 customLinters = strings.Split(customLintersFlag, ",") 71 } 72 packages = strings.Split(strings.Replace(packageList, "-", "/", -1), " ") 73 dirname, _ := os.Getwd() 74 cwd := filepath.Base(dirname) 75 76 for _, pkg := range packages { 77 pkgDir := "./" 78 if cwd != pkg { 79 pkgDir += pkg 80 } 81 splitLintArgs := strings.Split(lintArgs, " ") 82 args := []string{lintBin, "run"} 83 args = append(args, splitLintArgs...) 84 args = append(args, pkgDir) 85 86 startAt := time.Now() 87 cmd := exec.Command(args[0], args[1:]...) 88 cmd.Dir = dirname 89 out, err := cmd.CombinedOutput() 90 r := &result{ 91 cmd: strings.Join(args, " "), 92 name: "lint-" + strings.Replace(pkg, "/", "-", -1), 93 passed: err == nil, 94 duration: time.Since(startAt), 95 output: strings.Split(string(out), "\n"), 96 } 97 98 for _, linter := range customLinters { 99 customLinterStart := time.Now() 100 linterArgs := strings.Split(linter, " ") 101 linterArgs = append(linterArgs, pkgDir) 102 cmd := exec.Command(linterArgs[0], linterArgs[1:]...) 103 cmd.Dir = dirname 104 out, err := cmd.CombinedOutput() 105 r.passed = r.passed && err == nil 106 r.duration += time.Since(customLinterStart) 107 r.output = append(r.output, strings.Split(string(out), "\n")...) 108 } 109 r.fixup(dirname) 110 111 if !r.passed { 112 hasFailingTest = true 113 } 114 115 results = append(results, r) 116 fmt.Println(r) 117 } 118 119 if output != "" { 120 f, err := os.Create(output) 121 if err != nil { 122 os.Exit(1) 123 } 124 defer func() { 125 if err != f.Close() { 126 panic(err) 127 } 128 }() 129 130 for _, r := range results { 131 if _, err = f.WriteString(r.String() + "\n"); err != nil { 132 fmt.Fprintf(os.Stderr, "%s", err) 133 os.Exit(1) 134 } 135 } 136 } 137 138 if hasFailingTest { 139 os.Exit(1) 140 } 141 }