dashboard: allow multiple known-issues for builders

We may know of more than one issue affecting a builder. In some
contexts (such as the build dashboard) we only have enough room to
display one of those, but in other contexts (such as SlowBot failure
messages) it may be useful to see all of them as a list.

Moreover, this way we don't have to remember to check for other known
issues when removing an issue that has been resolved.

For golang/go#52653.

Change-Id: I9da42a3bf99ad24ec4b3ba9c4e6ce214b9f9cac7
Reviewed-on: https://go-review.googlesource.com/c/build/+/408696
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alex Rakoczy <alex@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/coordinator/builders.go b/cmd/coordinator/builders.go
index b585497..5ae7169 100644
--- a/cmd/coordinator/builders.go
+++ b/cmd/coordinator/builders.go
@@ -99,7 +99,7 @@
 	<td>{{.Name}}</td>
 	<td><a href='#{{.HostType}}'>{{.HostType}}</a></td>
 	<td>{{builderOwners .}}</td>
-	<td>{{with .KnownIssue}}<a href="https://go.dev/issue/{{.}}" title="This builder has a known issue. See go.dev/issue/{{.}}.">#{{.}}</a>{{end}}</td>
+	<td>{{range $i, $issue := .KnownIssues}}{{if ne $i 0}}, {{end}}<a href="https://go.dev/issue/{{$issue}}" title="This builder has a known issue. See: go.dev/issue/{{$issue}}.">#{{$issue}}</a>{{end}}</td>
 	<td>{{.Notes}}</td>
 </tr>
 {{end}}
diff --git a/cmd/coordinator/coordinator.go b/cmd/coordinator/coordinator.go
index b6a4d42..4d881e1 100644
--- a/cmd/coordinator/coordinator.go
+++ b/cmd/coordinator/coordinator.go
@@ -1495,13 +1495,18 @@
 	if len(ts.slowBots) > 0 {
 		name = "SlowBots"
 	}
-	msg := name + " beginning. Status page: " + ts.statusPage()
+	msg := name + " beginning. Status page: " + ts.statusPage() + "\n"
 
 	// If any of the requested SlowBot builders
 	// have a known issue, give users a warning.
 	for _, b := range ts.slowBots {
-		if b.KnownIssue != 0 {
-			msg += fmt.Sprintf("\nNote that builder %s has a known issue golang.org/issue/%d.", b.Name, b.KnownIssue)
+		if len(b.KnownIssues) > 0 {
+			issueBlock := new(strings.Builder)
+			issueBlock.WriteString("Note that builder %s has known issues:\n")
+			for _, i := range b.KnownIssues {
+				fmt.Fprintf(issueBlock, "\thttps://go.dev/issue/%d\n", i)
+			}
+			msg += issueBlock.String()
 		}
 	}
 
diff --git a/cmd/coordinator/internal/legacydash/build.go b/cmd/coordinator/internal/legacydash/build.go
index f6896fe..4a540cd 100644
--- a/cmd/coordinator/internal/legacydash/build.go
+++ b/cmd/coordinator/internal/legacydash/build.go
@@ -318,7 +318,10 @@
 		// Unknown builder.
 		return 0
 	}
-	return bc.KnownIssue
+	if len(bc.KnownIssues) > 0 {
+		return bc.KnownIssues[0]
+	}
+	return 0
 }
 
 // Results returns the build Results for this Commit.
diff --git a/dashboard/builders.go b/dashboard/builders.go
index 2f0555e..7f99282 100644
--- a/dashboard/builders.go
+++ b/dashboard/builders.go
@@ -828,15 +828,15 @@
 	// For example, "host-linux-bullseye".
 	HostType string
 
-	// KnownIssue is a non-zero golang.org/issue/nnn number for a builder
-	// that may fail due to a known issue, such as because it is a new
+	// KnownIssues is a slice of non-zero golang.org/issue/nnn numbers for a
+	// builder that may fail due to a known issue, such as because it is a new
 	// builder still in development/testing, or because the feature
 	// or port that it's meant to test hasn't been added yet, etc.
 	//
 	// A non-zero value here means that failures on this builder should not
 	// be considered a serious regression and don't need investigation beyond
-	// what is already in scope of the listed issue.
-	KnownIssue int
+	// what is already in scope of the listed issues.
+	KnownIssues []int
 
 	Notes string // notes for humans
 
@@ -2781,6 +2781,11 @@
 	if c.SkipSnapshot && (c.numTestHelpers > 0 || c.numTryTestHelpers > 0) {
 		panic(fmt.Sprintf("config %q's SkipSnapshot is not compatible with sharded test helpers", c.Name))
 	}
+	for i, issue := range c.KnownIssues {
+		if issue == 0 {
+			panic(fmt.Errorf("config %q's KnownIssues slice has a zero issue at index %d", c.Name, i))
+		}
+	}
 
 	types := 0
 	for _, fn := range []func() bool{c.IsReverse, c.IsContainer, c.IsVM} {
@@ -2807,7 +2812,7 @@
 		Name:        "misc-compile" + suffix,
 		HostType:    "host-linux-bullseye",
 		buildsRepo:  func(repo, branch, goBranch string) bool { return repo == "go" && branch == "master" },
-		KnownIssue:  knownIssue,
+		KnownIssues: []int{knownIssue},
 		GoDeps:      goDeps,
 		env:         []string{"GO_DISABLE_OUTBOUND_NETWORK=1"},
 		CompileOnly: true,