| // For a given time period, find all the bots that failed when a CL, that was |
| // later reverted, first landed. The count of failed bots does not include bots |
| // that failed at both the initial commit and at the revert. Note that "-All" |
| // is removed from bot names. |
| // |
| // Running this requires a client_secret.json file in the current directory that |
| // is good for accessing the swarming API. |
| package main |
| |
| import ( |
| "context" |
| "flag" |
| "fmt" |
| "regexp" |
| "sort" |
| "strings" |
| |
| apipb "go.chromium.org/luci/swarming/proto/api_v2" |
| "go.skia.org/infra/go/common" |
| "go.skia.org/infra/go/git" |
| "go.skia.org/infra/go/httputils" |
| "go.skia.org/infra/go/sklog" |
| "go.skia.org/infra/go/swarming" |
| swarmingv2 "go.skia.org/infra/go/swarming/v2" |
| "golang.org/x/oauth2/google" |
| ) |
| |
| var ( |
| repo = flag.String("repo", ".", "Path to Git repo.") |
| since = flag.String("since", "6months", "How far back to search in git history.") |
| findRevertString = regexp.MustCompile("Revert") |
| extractRevertHash = regexp.MustCompile("This reverts commit ([a-z0-9]+)") |
| removeAll = regexp.MustCompile("-All") |
| ) |
| |
| func failingTasksAtACommit(ctx context.Context, swarmApi swarmingv2.SwarmingV2Client, hash string) map[string]bool { |
| resp, err := swarmingv2.ListTasksHelper(ctx, swarmApi, &apipb.TasksWithPerfRequest{ |
| State: apipb.StateQuery_QUERY_COMPLETED_FAILURE, |
| Tags: []string{fmt.Sprintf("source_revision:%s", hash)}, |
| }) |
| if err != nil { |
| sklog.Fatal(err) |
| } |
| ret := map[string]bool{} |
| names := []string{} |
| for _, r := range resp { |
| name := removeAll.ReplaceAllLiteralString(r.Name, "") |
| ret[name] = true |
| names = append(names, name) |
| } |
| sort.Strings(names) |
| sklog.Infof("Failing: %v", names) |
| return ret |
| } |
| |
| func main() { |
| common.Init() |
| |
| ctx := context.Background() |
| |
| gd := git.GitDir(*repo) |
| ts, err := google.DefaultTokenSource(ctx, swarming.AUTH_SCOPE) |
| if err != nil { |
| sklog.Fatal(err) |
| } |
| httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client() |
| swarmApi := swarmingv2.NewDefaultClient(httpClient, swarming.SWARMING_SERVER) |
| |
| // Get all the commits since *since with one line per commit. |
| output, err := gd.Git(ctx, "log", "--format=oneline", fmt.Sprintf("--since=%s", *since)) |
| if err != nil { |
| sklog.Fatal(err) |
| } |
| lines := strings.Split(output, "\n") |
| counts := map[string]int{} |
| numCommitsReverted := 0 |
| for _, line := range lines { |
| // Only include commits that have an odd number of occurrences of the string "Revert" in their title. |
| sklog.Infof("Line: %s", line) |
| revertHash := strings.Split(line, " ")[0] |
| numReverts := len(findRevertString.FindAllString(line, -1)) |
| sklog.Infof("numReverts: %d", numReverts) |
| if numReverts%2 == 1 { |
| // From the revert CL, find the git hash of the initial commit we are reverting. |
| ci, err := gd.Details(ctx, revertHash) |
| if err != nil { |
| sklog.Fatal(err) |
| } |
| match := extractRevertHash.FindStringSubmatch(ci.Body) |
| sklog.Infof("match: %#v", match) |
| if len(match) == 2 { |
| numCommitsReverted += 1 |
| badHash := match[1] |
| sklog.Infof("%s was reverted by %s", badHash, line) |
| sklog.Info("Which tasks failed?") |
| failed := failingTasksAtACommit(ctx, swarmApi, badHash) |
| sklog.Info("Which tasks were still failing upon revert?") |
| failingEvenAfterRevert := failingTasksAtACommit(ctx, swarmApi, revertHash) |
| // Only count bots that appear in failed and not in failingEvenAfterRevert. |
| for k := range failed { |
| if failingEvenAfterRevert[k] { |
| continue |
| } |
| counts[k] = counts[k] + 1 |
| } |
| } else { |
| sklog.Infof("Failed to find revert hash in: %s", ci.Body) |
| } |
| } |
| } |
| for k, v := range counts { |
| fmt.Printf("%d %s\n", v, k) |
| } |
| fmt.Printf("\n\nFound %d commits reverted in %d commits. %.2f%%\n", numCommitsReverted, len(lines), float32(100*numCommitsReverted)/float32(len(lines))) |
| } |