From 7795ea4721edfcbce199a5cc9a2cbca9199518e8 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Mon, 2 May 2022 14:39:49 -0400 Subject: [PATCH] greplogs: refine output for issue triage When --md is set, echo the shell-escaped 'greplogs' command line itself at the beginning of the output to make the command easier to reproduce when pasted into an issue report. Add a --triage flag that adds checkboxes and a summary to the Markdown output. Apply the --omit flag to commits as well as builders, to more easily filter out commits (especially subrepo commits) that are broken across the board. --- go.mod | 1 + go.sum | 2 ++ greplogs/main.go | 39 +++++++++++++++++++++++++++++++-------- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 81096f5..78b95cf 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/aclements/go-gg v0.0.0-20170323211221-abd1f791f5ee github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 golang.org/x/build v0.0.0-20210804225706-d1bc548deb19 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d diff --git a/go.sum b/go.sum index be4638c..12a6841 100644 --- a/go.sum +++ b/go.sum @@ -245,6 +245,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= diff --git a/greplogs/main.go b/greplogs/main.go index a0f8f4f..42e8a8b 100644 --- a/greplogs/main.go +++ b/greplogs/main.go @@ -29,6 +29,7 @@ import ( "time" "github.com/aclements/go-misc/internal/loganal" + "github.com/kballard/go-shellquote" ) // TODO: If searching dashboard logs, optionally print to builder URLs @@ -48,6 +49,7 @@ var ( flagDashboard = flag.Bool("dashboard", true, "search dashboard logs from fetchlogs") flagMD = flag.Bool("md", true, "output in Markdown") + flagTriage = flag.Bool("triage", false, "adjust Markdown output for failure triage") flagFilesOnly = flag.Bool("l", false, "print only names of matching files") flagColor = flag.String("color", "auto", "highlight output in color: `mode` is never, always, or auto") @@ -66,7 +68,7 @@ func main() { // logs and have it extract the failures. 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 matching `regexp`; if provided multiple times, builders matching any regexp are omitted") + flag.Var(&omit, "omit", "omit results for builder names and/or revisions matching `regexp`; if provided multiple times, logs matching any regexp are omitted") flag.Var(&since, "since", "list only failures on revisions since this date, as an RFC-3339 date or date-time") flag.Var(&before, "before", "list only failures on revisions before this date, in the same format as -since") flag.Parse() @@ -88,6 +90,19 @@ func main() { os.Exit(2) } + status := 1 + defer func() { os.Exit(status) }() + + numMatching := 0 + if *flagMD { + args := append([]string{filepath.Base(os.Args[0])}, os.Args[1:]...) + fmt.Printf("`%s`\n", shellquote.Join(args...)) + + if *flagTriage { + defer func() { fmt.Printf("\n(%d matching logs)\n", numMatching) }() + } + } + // Gather paths. var paths []string var stripDir string @@ -111,7 +126,6 @@ func main() { } // Process files - status := 1 for _, path := range paths { filepath.Walk(path, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -132,16 +146,18 @@ func main() { if err != nil { status = 2 fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) - } else if found && status == 1 { - status = 0 + } else if found { + numMatching++ + if status == 1 { + status = 0 + } } return nil }) } - os.Exit(status) } -var pathDateRE = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})-[0-9a-f]+(?:-[0-9a-f]+)?$`) +var pathDateRE = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})-([0-9a-f]+(?:-[0-9a-f]+)?)$`) func process(path, nicePath string) (found bool, err error) { // If this is from the dashboard, filter by builder and date and get the builder URL. @@ -154,10 +170,13 @@ func process(path, nicePath string) (found bool, err error) { revDir := filepath.Dir(nicePath) revDirBase := filepath.Base(revDir) match := pathDateRE.FindStringSubmatch(revDirBase) - if len(match) != 2 { + if len(match) != 3 { // Without a valid log date we can't filter by it. return false, fmt.Errorf("timestamp not found in rev dir name: %q", revDirBase) } + if omit.AnyMatchString(match[2]) { + return false, nil + } revTime, err := time.Parse("2006-01-02T15:04:05", match[1]) if err != nil { return false, err @@ -190,7 +209,11 @@ func process(path, nicePath string) (found bool, err error) { printPath := nicePath if *flagMD && logURL != "" { - printPath = fmt.Sprintf("[%s](%s)", nicePath, logURL) + prefix := "" + if *flagTriage { + prefix = "- [ ] " + } + printPath = fmt.Sprintf("%s[%s](%s)", prefix, nicePath, logURL) } if *flagFilesOnly {