diff --git a/cmd/greplogs/flags.go b/cmd/greplogs/flags.go index d07d05ab80..ee1ef80ecc 100644 --- a/cmd/greplogs/flags.go +++ b/cmd/greplogs/flags.go @@ -4,7 +4,12 @@ package main -import "regexp" +import ( + "fmt" + "regexp" + "sort" + "strings" +) type regexpList []*regexp.Regexp @@ -58,3 +63,45 @@ func (x *regexpList) Matches(data []byte) [][]int { } return matches } + +type regexpMap map[string]*regexp.Regexp + +func (x *regexpMap) Set(s string) error { + if *x == nil { + *x = regexpMap{} + } + k, v, ok := strings.Cut(s, "=") + if !ok { + return fmt.Errorf("missing key, expected key=value in %q", s) + } + re, err := regexp.Compile("(?m)" + v) + if err != nil { + // Get an error without our modifications. + _, err2 := regexp.Compile(v) + if err2 != nil { + err = err2 + } + return err + } + (*x)[k] = re + return nil +} + +func (x *regexpMap) String() string { + var result []string + for k, v := range *x { + result = append(result, fmt.Sprintf("%v=%v", k, v)) + } + return strings.Join(result, ",") +} + +func (x *regexpMap) Matches(data []byte) []string { + var matches []string + for k, r := range *x { + if r.Match(data) { + matches = append(matches, k) + } + } + sort.Strings(matches) + return matches +} diff --git a/cmd/greplogs/main.go b/cmd/greplogs/main.go index 3df4261b86..bf9cad9eca 100644 --- a/cmd/greplogs/main.go +++ b/cmd/greplogs/main.go @@ -46,6 +46,7 @@ var ( fileRegexps regexpList failRegexps regexpList omit regexpList + knownIssues regexpMap flagDashboard = flag.Bool("dashboard", true, "search dashboard logs from fetchlogs") flagMD = flag.Bool("md", true, "output in Markdown") @@ -69,6 +70,7 @@ var brokenBuilders []string func main() { // XXX What I want right now is just to point it at a bunch of // logs and have it extract the failures. + flag.Var(&knownIssues, "known-issue", "add an issue=regexp mapping; if a log matches regexp it will be categorized under issue. One mapping per flag.") flag.Var(&fileRegexps, "e", "show files matching `regexp`; if provided multiple times, files must match all regexps") flag.Var(&failRegexps, "E", "show only errors matching `regexp`; if provided multiple times, an error must match all regexps") flag.Var(&omit, "omit", "omit results for builder names and/or revisions matching `regexp`; if provided multiple times, logs matching any regexp are omitted") @@ -244,10 +246,17 @@ func process(path, nicePath string) (found bool, err error) { } printPath := nicePath + kiMatches := 0 if *flagMD && logURL != "" { prefix := "" if *flagTriage { - prefix = "- [ ] " + matches := knownIssues.Matches(data) + if len(matches) == 0 { + prefix = "- [ ] " + } else { + kiMatches++ + prefix = fmt.Sprintf("- [x] (%v) ", strings.Join(matches, ", ")) + } } printPath = fmt.Sprintf("%s[%s](%s)", prefix, nicePath, logURL) }