[autocorrelator] Add flags to ignore skipped build and/or tests
When checking for build failures, ignore try builds which skipped
building.
When checking for test failures, ignore try builds which skipped
testing.
Bug: 67694
Change-Id: Ic4c46ded825b2c4795478089c54538509f9a4e44
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/infra/+/472079
Fuchsia-Auto-Submit: Anthony Fandrianto <atyfto@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Reviewed-by: Anirudh Mathukumilli <rudymathu@google.com>
diff --git a/cmd/autocorrelator/check_try.go b/cmd/autocorrelator/check_try.go
index 705632b..7459a18 100644
--- a/cmd/autocorrelator/check_try.go
+++ b/cmd/autocorrelator/check_try.go
@@ -18,6 +18,7 @@
"go.chromium.org/luci/auth"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/grpc/prpc"
+ "go.chromium.org/luci/luciexe/exe"
"google.golang.org/genproto/protobuf/field_mask"
"go.fuchsia.dev/infra/buildbucket"
@@ -47,20 +48,20 @@
buildStatus int
searchRange int
summaryMarkdownPath string
+ ignoreSkippedBuild bool
+ ignoreSkippedTests bool
jsonOutput string
}
func (c *checkTryRun) Init(defaultAuthOpts auth.Options) {
c.commonFlags.Init(defaultAuthOpts)
- // TODO(atyfto): Add a ChangeId input and filter out tryjobs which match
- // the ChangeId. Failures caused by the same change should be ignored,
- // otherwise we can falsely imply systemic failure when a single CL is
- // responsible for many failures.
c.Flags.Var(&c.builder, "builder", "Fully-qualified Buildbucket try builder name to inspect.")
c.Flags.Int64Var(&c.changeNum, "change-num", 0, "Change number to filter out.")
c.Flags.IntVar(&c.buildStatus, "build-status", 20, "Build status to filter on. Defaults to FAILURE.")
c.Flags.IntVar(&c.searchRange, "search-range", 10, "Inspect up to this many recent builds.")
c.Flags.StringVar(&c.summaryMarkdownPath, "summary-markdown-path", "", "Path to summary markdown input file.")
+ c.Flags.BoolVar(&c.ignoreSkippedBuild, "ignore-skipped-build", false, "Whether to ignore builds with unaffected build graphs.")
+ c.Flags.BoolVar(&c.ignoreSkippedTests, "ignore-skipped-tests", false, "Whether to ignore builds which skipped testing.")
c.Flags.StringVar(&c.jsonOutput, "json-output", "-", "Path to output finding to.")
}
@@ -94,13 +95,35 @@
// The `comparator` argument is a text-based comparator that returns a
// similarity score between the input summary markdown and a build's summary
// markdown.
-func checkTry(builds []*buildbucketpb.Build, changeNum int64, status buildbucketpb.Status, summaryMarkdown string, comparator compare.TextComparator) []*findings.SummarySimilarity {
+func checkTry(builds []*buildbucketpb.Build, changeNum int64, status buildbucketpb.Status, summaryMarkdown string, comparator compare.TextComparator, ignoreSkippedBuild, ignoreSkippedTests bool) []*findings.SummarySimilarity {
var fs []*findings.SummarySimilarity
for _, build := range builds {
// If we find a build with an identical change number, skip it.
if changeNum == build.Input.GerritChanges[0].Change {
continue
}
+ // Conditionally ignore builds with unaffected build graph, e.g. when
+ // checking for build failures.
+ if ignoreSkippedBuild {
+ var skipped bool
+ exe.ParseProperties(build.Output.Properties, map[string]interface{}{
+ "skipped_because_unaffected": &skipped,
+ })
+ if skipped {
+ continue
+ }
+ }
+ // Conditionally ignore builds which skipped testing, e.g. when checking
+ // for test failures.
+ if ignoreSkippedTests {
+ var noWork bool
+ exe.ParseProperties(build.Output.Properties, map[string]interface{}{
+ "affected_tests_no_work": &noWork,
+ })
+ if noWork {
+ continue
+ }
+ }
// If we encounter a green build, add the finding: the existence of a
// green build should reduce the confidence we have in "try" being
// systemically busted.
@@ -148,6 +171,7 @@
Fields: &field_mask.FieldMask{Paths: []string{
"builds.*.id",
"builds.*.input.gerrit_changes",
+ "builds.*.output.properties",
"builds.*.status",
"builds.*.summary_markdown",
}},
@@ -166,7 +190,7 @@
if err != nil {
return fmt.Errorf("could not read summary markdown input: %v", err)
}
- fs := checkTry(builds, c.changeNum, buildbucketpb.Status(c.buildStatus), string(summaryMarkdown), compare.LevenshteinComparator{Opts: levenshtein.DefaultOptions})
+ fs := checkTry(builds, c.changeNum, buildbucketpb.Status(c.buildStatus), string(summaryMarkdown), compare.LevenshteinComparator{Opts: levenshtein.DefaultOptions}, c.ignoreSkippedBuild, c.ignoreSkippedTests)
// Emit summary similarity findings to -json-output.
out := os.Stdout
diff --git a/cmd/autocorrelator/check_try_test.go b/cmd/autocorrelator/check_try_test.go
index 9a003e2..95c1364 100644
--- a/cmd/autocorrelator/check_try_test.go
+++ b/cmd/autocorrelator/check_try_test.go
@@ -7,6 +7,7 @@
import (
"testing"
+ structpb "github.com/golang/protobuf/ptypes/struct"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"go.fuchsia.dev/infra/cmd/autocorrelator/findings"
@@ -17,11 +18,13 @@
t.Parallel()
var tests = []struct {
- builds []*buildbucketpb.Build
- changeNum int64
- status buildbucketpb.Status
- summaryMarkdown string
- expected []*findings.SummarySimilarity
+ builds []*buildbucketpb.Build
+ changeNum int64
+ status buildbucketpb.Status
+ summaryMarkdown string
+ ignoreSkippedBuild bool
+ ignoreSkippedTests bool
+ expected []*findings.SummarySimilarity
}{
{
builds: []*buildbucketpb.Build{
@@ -36,6 +39,11 @@
},
},
},
+ Output: &buildbucketpb.Build_Output{
+ Properties: &structpb.Struct{
+ Fields: map[string]*structpb.Value{},
+ },
+ },
},
{
Id: int64(999998),
@@ -48,10 +56,63 @@
},
},
},
+ Output: &buildbucketpb.Build_Output{
+ Properties: &structpb.Struct{
+ Fields: map[string]*structpb.Value{},
+ },
+ },
+ },
+ // This has an unaffected build graph, and should be ignored.
+ {
+ Id: int64(999997),
+ Status: buildbucketpb.Status_SUCCESS,
+ SummaryMarkdown: "",
+ Input: &buildbucketpb.Build_Input{
+ GerritChanges: []*buildbucketpb.GerritChange{
+ {
+ Change: int64(222222),
+ },
+ },
+ },
+ Output: &buildbucketpb.Build_Output{
+ Properties: &structpb.Struct{
+ Fields: map[string]*structpb.Value{
+ "skipped_because_unaffected": {
+ Kind: &structpb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ // This has no affected tests, and should be ignored.
+ {
+ Id: int64(999996),
+ Status: buildbucketpb.Status_SUCCESS,
+ SummaryMarkdown: "",
+ Input: &buildbucketpb.Build_Input{
+ GerritChanges: []*buildbucketpb.GerritChange{
+ {
+ Change: int64(222222),
+ },
+ },
+ },
+ Output: &buildbucketpb.Build_Output{
+ Properties: &structpb.Struct{
+ Fields: map[string]*structpb.Value{
+ "affected_tests_no_work": {
+ Kind: &structpb.Value_BoolValue{
+ BoolValue: true,
+ },
+ },
+ },
+ },
+ },
},
// This matches the input change number, and should be ignored.
{
- Id: int64(999998),
+ Id: int64(999995),
Status: buildbucketpb.Status_FAILURE,
SummaryMarkdown: "ERROR",
Input: &buildbucketpb.Build_Input{
@@ -61,11 +122,16 @@
},
},
},
+ Output: &buildbucketpb.Build_Output{
+ Properties: &structpb.Struct{
+ Fields: map[string]*structpb.Value{},
+ },
+ },
},
// This does not match the input status of FAILURE, and should
// be ignored.
{
- Id: int64(999997),
+ Id: int64(999994),
Status: buildbucketpb.Status_INFRA_FAILURE,
SummaryMarkdown: "ERROR: checkout failed",
Input: &buildbucketpb.Build_Input{
@@ -75,11 +141,18 @@
},
},
},
+ Output: &buildbucketpb.Build_Output{
+ Properties: &structpb.Struct{
+ Fields: map[string]*structpb.Value{},
+ },
+ },
},
},
- status: buildbucketpb.Status_FAILURE,
- changeNum: int64(123456),
- summaryMarkdown: "ERROR",
+ status: buildbucketpb.Status_FAILURE,
+ changeNum: int64(123456),
+ summaryMarkdown: "ERROR",
+ ignoreSkippedBuild: true,
+ ignoreSkippedTests: true,
expected: []*findings.SummarySimilarity{
{
Score: 0.0,
@@ -97,7 +170,7 @@
for _, test := range tests {
comparator := mockComparator{}
- ss := checkTry(test.builds, test.changeNum, test.status, test.summaryMarkdown, comparator)
+ ss := checkTry(test.builds, test.changeNum, test.status, test.summaryMarkdown, comparator, test.ignoreSkippedBuild, test.ignoreSkippedTests)
if diff := cmp.Diff(test.expected, ss); diff != "" {
t.Fatalf("different (-want +got):\n%s", diff)
}