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  }