From 79dd4f320ac7a82d5befa3f418b0e3489de041b3 Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Tue, 27 Feb 2024 13:50:43 -0500 Subject: [PATCH] cmd/coordinator/internal/legacydash: add LUCI build result support The better way to do this would've been to separate the display logic from database reading/writing logic. But that's what the incomplete dashboard v2 attempt is about. It's a bit too late to try to do more here, so instead try to make minimal changes to get the job done and make it easier to be confident it won't break existing functionality. For golang/go#65913. Change-Id: I8c43c25759929c8bb2c4bb18613258f4401577f9 Reviewed-on: https://go-review.googlesource.com/c/build/+/567577 Reviewed-by: Dmitri Shuralyov LUCI-TryBot-Result: Go LUCI Auto-Submit: Dmitri Shuralyov Reviewed-by: Michael Knyszek --- cmd/coordinator/coordinator.go | 2 +- cmd/coordinator/internal/legacydash/build.go | 70 +++-- cmd/coordinator/internal/legacydash/dash.go | 11 +- cmd/coordinator/internal/legacydash/ui.go | 265 +++++++++++++++--- cmd/coordinator/internal/legacydash/ui.html | 8 +- .../internal/legacydash/ui_test.go | 9 +- 6 files changed, 306 insertions(+), 59 deletions(-) diff --git a/cmd/coordinator/coordinator.go b/cmd/coordinator/coordinator.go index 05f8ba7da6..336e536ad6 100644 --- a/cmd/coordinator/coordinator.go +++ b/cmd/coordinator/coordinator.go @@ -360,7 +360,7 @@ func main() { // grpcServer is a shared gRPC server. It is global, as it needs to be used in places that aren't factored otherwise. grpcServer := grpc.NewServer(opts...) - dashV1 := legacydash.Handler(gce.GoDSClient(), maintnerClient, string(masterKey()), grpcServer) + dashV1 := legacydash.Handler(gce.GoDSClient(), maintnerClient, nil, string(masterKey()), grpcServer) dashV2 := &builddash.Handler{Datastore: gce.GoDSClient(), Maintner: maintnerClient} gs := &gRPCServer{dashboardURL: "https://build.golang.org"} setSessionPool(sp) diff --git a/cmd/coordinator/internal/legacydash/build.go b/cmd/coordinator/internal/legacydash/build.go index 146791cffa..5e310638d0 100644 --- a/cmd/coordinator/internal/legacydash/build.go +++ b/cmd/coordinator/internal/legacydash/build.go @@ -138,6 +138,8 @@ type Commit struct { // For non-Go commits, only the Results for the current Go tip, weekly, // and release Tags are stored here. This is purely de-normalized data. // The complete data set is stored in Result entities. + // + // Each string is formatted as builder|OK|LogHash|GoHash. ResultData []string `datastore:",noindex"` } @@ -225,23 +227,27 @@ func min(a, b int) int { // // For the main Go repo, goHash is the empty string. func (c *Commit) Result(builder, goHash string) *Result { - return result(c.ResultData, c.Hash, c.PackagePath, builder, goHash) + r := result(c.ResultData, c.Hash, c.PackagePath, builder, goHash) + if r == nil { + return nil + } + return &r.Result } // Result returns the build Result for this commit for the given builder/goHash. // // For the main Go repo, goHash is the empty string. -func (c *CommitInfo) Result(builder, goHash string) *Result { +func (c *CommitInfo) Result(builder, goHash string) *DisplayResult { if r := result(c.ResultData, c.Hash, c.PackagePath, builder, goHash); r != nil { return r } if u, ok := c.BuildingURLs[builderAndGoHash{builder, goHash}]; ok { - return &Result{ + return &DisplayResult{Result: Result{ Builder: builder, BuildingURL: u, Hash: c.Hash, GoHash: goHash, - } + }} } if fakeResults { // Create a fake random result. @@ -249,25 +255,38 @@ func (c *CommitInfo) Result(builder, goHash string) *Result { default: return nil case 1: - return &Result{ + return &DisplayResult{Result: Result{ Builder: builder, Hash: c.Hash, GoHash: goHash, OK: true, - } + }} case 2: - return &Result{ + return &DisplayResult{Result: Result{ Builder: builder, Hash: c.Hash, GoHash: goHash, LogHash: "fakefailureurl", - } + }} } } return nil } -func result(resultData []string, hash, packagePath, builder, goHash string) *Result { +type DisplayResult struct { + Result + Noise bool +} + +func (r *DisplayResult) LogURL() string { + if strings.HasPrefix(r.LogHash, "https://") { + return r.LogHash + } else { + return "/log/" + r.LogHash + } +} + +func result(resultData []string, hash, packagePath, builder, goHash string) *DisplayResult { for _, r := range resultData { if !strings.HasPrefix(r, builder) { // Avoid strings.SplitN alloc in the common case. @@ -292,6 +311,14 @@ func result(resultData []string, hash, packagePath, builder, goHash string) *Res // As a special case, "tip" is an alias for "master", since this app // still uses a bunch of hg terms from when we used hg. func isUntested(builder, repo, branch, goBranch string) bool { + if strings.HasSuffix(builder, "-🐇") { + // LUCI builders are never considered untested. + // + // Note: It would be possible to improve this by reporting + // whether a given LUCI builder exists for a given x/ repo. + // That needs more bookkeeping and code, so left for later. + return false + } if branch == "tip" { branch = "master" } @@ -320,8 +347,8 @@ func knownIssue(builder string) int { return 0 } -// Results returns the build Results for this Commit. -func (c *CommitInfo) Results() (results []*Result) { +// Results returns the build results for this Commit. +func (c *CommitInfo) Results() (results []*DisplayResult) { for _, r := range c.ResultData { p := strings.SplitN(r, "|", 4) if len(p) != 4 { @@ -376,15 +403,18 @@ func reverse(s []string) { } } -// partsToResult creates a Result from ResultData substrings. -func partsToResult(hash, packagePath string, p []string) *Result { - return &Result{ - Builder: p[0], - Hash: hash, - PackagePath: packagePath, - GoHash: p[3], - OK: p[1] == "true", - LogHash: p[2], +// partsToResult creates a DisplayResult from ResultData substrings. +func partsToResult(hash, packagePath string, p []string) *DisplayResult { + return &DisplayResult{ + Result: Result{ + Builder: p[0], + Hash: hash, + PackagePath: packagePath, + GoHash: p[3], + OK: p[1] == "true", + LogHash: p[2], + }, + Noise: p[1] == "infra_failure", } } diff --git a/cmd/coordinator/internal/legacydash/dash.go b/cmd/coordinator/internal/legacydash/dash.go index df5c12e66f..4af4aee306 100644 --- a/cmd/coordinator/internal/legacydash/dash.go +++ b/cmd/coordinator/internal/legacydash/dash.go @@ -19,11 +19,16 @@ import ( "cloud.google.com/go/datastore" "github.com/NYTimes/gziphandler" + "golang.org/x/build/cmd/coordinator/internal/lucipoll" "golang.org/x/build/maintner/maintnerd/apipb" "golang.org/x/build/repos" "google.golang.org/grpc" ) +type luciClient interface { + PostSubmitSnapshot() lucipoll.Snapshot +} + type handler struct { mux *http.ServeMux @@ -34,6 +39,9 @@ type handler struct { // Maintner client for the maintner service. // Typically the one at maintner.golang.org. maintnerCl apipb.MaintnerServiceClient + + // LUCI is a client for LUCI, used for fetching build results from there. + LUCI luciClient } func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { h.mux.ServeHTTP(w, req) } @@ -43,11 +51,12 @@ const fakeResults = false // Handler sets a datastore client, maintner client, builder master key and // GRPC server at the package scope, and returns an HTTP mux for the legacy dashboard. -func Handler(dc *datastore.Client, mc apipb.MaintnerServiceClient, key string, grpcServer *grpc.Server) http.Handler { +func Handler(dc *datastore.Client, mc apipb.MaintnerServiceClient, lc luciClient, key string, grpcServer *grpc.Server) http.Handler { h := handler{ mux: http.NewServeMux(), datastoreCl: dc, maintnerCl: mc, + LUCI: lc, } kc := keyCheck{masterKey: key} diff --git a/cmd/coordinator/internal/legacydash/ui.go b/cmd/coordinator/internal/legacydash/ui.go index 9f251af37d..1c25b86373 100644 --- a/cmd/coordinator/internal/legacydash/ui.go +++ b/cmd/coordinator/internal/legacydash/ui.go @@ -21,6 +21,8 @@ import ( "time" "cloud.google.com/go/datastore" + bbpb "go.chromium.org/luci/buildbucket/proto" + "golang.org/x/build/cmd/coordinator/internal/lucipoll" "golang.org/x/build/dashboard" "golang.org/x/build/internal/releasetargets" "golang.org/x/build/maintner/maintnerd/apipb" @@ -66,7 +68,11 @@ func (h handler) uiHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "maintner.GetDashboard: "+err.Error(), httpStatusOfErr(err)) return } - data, err := tb.buildTemplateData(ctx, h.datastoreCl) + var luci lucipoll.Snapshot + if h.LUCI != nil && tb.showLUCI() { + luci = h.LUCI.PostSubmitSnapshot() + } + data, err := tb.buildTemplateData(ctx, h.datastoreCl, luci) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -96,7 +102,11 @@ func viewForRequest(r *http.Request) (dashboardView, error) { case "json": return jsonView{}, nil case "": - return htmlView{}, nil + showLUCI, _ := strconv.ParseBool(r.URL.Query().Get("showluci")) + if legacyOnly, _ := strconv.ParseBool(r.URL.Query().Get("legacyonly")); legacyOnly { + showLUCI = false + } + return htmlView{ShowLUCI: showLUCI}, nil } return nil, errors.New("unsupported mode argument") } @@ -151,19 +161,6 @@ func (tb *uiTemplateDataBuilder) getCommitsToLoad() map[commitInPackage]bool { // contain items that didn't exist in the datastore. (It is not an // error if 1 or all don't exist.) func (tb *uiTemplateDataBuilder) loadDatastoreCommits(ctx context.Context, cl *datastore.Client, want map[commitInPackage]bool) (map[string]*Commit, error) { - ret := map[string]*Commit{} - - // Allow tests to fake what the datastore would've loaded, and - // thus also allow tests to be run without a real (or fake) datastore. - if m := tb.testCommitData; m != nil { - for k := range want { - if c, ok := m[k.commit]; ok { - ret[k.commit] = c - } - } - return ret, nil - } - var keys []*datastore.Key for k := range want { key := (&Commit{ @@ -176,6 +173,7 @@ func (tb *uiTemplateDataBuilder) loadDatastoreCommits(ctx context.Context, cl *d if err != nil { return nil, fmt.Errorf("fetchCommits: %v", err) } + var ret = make(map[string]*Commit) for _, c := range commits { ret[c.Hash] = c } @@ -211,7 +209,7 @@ func (tb *uiTemplateDataBuilder) newCommitInfo(dsCommits map[string]*Commit, rep Hash: dc.Commit, PackagePath: repo, User: formatGitAuthor(dc.AuthorName, dc.AuthorEmail), - Desc: cleanTitle(dc.Title, tb.req.Branch), + Desc: cleanTitle(dc.Title, tb.branch()), Time: time.Unix(dc.CommitTimeSec, 0), Branch: branch, } @@ -231,6 +229,15 @@ func (tb *uiTemplateDataBuilder) newCommitInfo(dsCommits map[string]*Commit, rep return ci } +func (tb *uiTemplateDataBuilder) showLUCI() bool { + v, ok := tb.view.(htmlView) + return ok && v == htmlView{ShowLUCI: true} && + // Only show LUCI build results for the default top-level view. + tb.req.Page == 0 && + tb.req.Repo == "" && + tb.branch() == "master" +} + // showXRepoSection reports whether the dashboard should show the state of the x/foo repos at the bottom of // the page in the three branches (master, latest release branch, two releases ago). func (tb *uiTemplateDataBuilder) showXRepoSection() bool { @@ -275,10 +282,162 @@ func repoImportPath(rh *apipb.DashRepoHead) string { return ri.ImportPath } -func (tb *uiTemplateDataBuilder) buildTemplateData(ctx context.Context, datastoreCl *datastore.Client) (*uiTemplateData, error) { - dsCommits, err := tb.loadDatastoreCommits(ctx, datastoreCl, tb.getCommitsToLoad()) - if err != nil { - return nil, err +// appendLUCIResults appends result data polled from LUCI to commits. +func appendLUCIResults(luci lucipoll.Snapshot, commits []*CommitInfo, repo string) { + commitBuilds, ok := luci.RepoCommitBuilds[repo] + if !ok { + return + } + for _, c := range commits { + builds, ok := commitBuilds[c.Hash] + if !ok { + // No builds for this commit. + continue + } + for _, b := range builds { + builder := luci.Builders[b.BuilderName] + if builder.Repo != repo || builder.GoBranch != "master" { + // TODO: Support builder.GoBranch != master for x/ repos. + // Or maybe it's fine to leave that to appendLUCIResultsXRepo only, + // and have this appendLUCIResults function deal repo == "go" only. + // Hmm, maybe it needs to handle repo != "go" for when viewing the + // individual repo commit views... I'll see later. + continue + } + switch b.Status { + case bbpb.Status_STARTED, bbpb.Status_SUCCESS, bbpb.Status_FAILURE, bbpb.Status_INFRA_FAILURE: + default: + continue + } + tagFriendly := builder.Name + if after, ok := strings.CutPrefix(tagFriendly, fmt.Sprintf("gotip-%s-%s_", builder.Target.GOOS, builder.Target.GOARCH)); ok { + // Convert os-arch_osversion-mod1-mod2 (an underscore at start of "_osversion") + // to have os-arch-osversion-mod1-mod2 (a dash at start of "-osversion") form. + // The tag computation below uses this to find both "osversion-mod1" or "mod1". + tagFriendly = fmt.Sprintf("gotip-%s-%s-", builder.Target.GOOS, builder.Target.GOARCH) + after + } + builderName := strings.TrimPrefix(tagFriendly, "gotip-") + "-🐇" + // Note: goHash is empty for the main Go repo. + goHash := "" + if repo != "go" { + // TODO: Generalize goHash for non-go builds. + // + // If the non-go build was triggered by an x/ repo trigger, + // its b.GetInput().GetGitilesCommit().GetId() will be the + // x/ repo commit, and the Go repo commit will be selected + // at runtime... + // + // Can get it out of output properties but that would mean + // not being able to support STARTED builds. + } + switch b.Status { + case bbpb.Status_STARTED: + if c.BuildingURLs == nil { + c.BuildingURLs = make(map[builderAndGoHash]string) + } + c.BuildingURLs[builderAndGoHash{builderName, goHash}] = buildURL(b.ID) + case bbpb.Status_SUCCESS, bbpb.Status_FAILURE: + c.ResultData = append(c.ResultData, fmt.Sprintf("%s|%t|%s|%s", + builderName, + b.Status == bbpb.Status_SUCCESS, + buildURL(b.ID), + goHash, + )) + case bbpb.Status_INFRA_FAILURE: + c.ResultData = append(c.ResultData, fmt.Sprintf("%s|%s|%s|%s", + builderName, + "infra_failure", + buildURL(b.ID), + goHash, + )) + } + } + } +} + +func appendLUCIResultsXRepo(luci lucipoll.Snapshot, c *CommitInfo, repo, goBranch, goHash string) { + commitBuilds, ok := luci.RepoCommitBuilds[repo] + if !ok { + return + } + builds, ok := commitBuilds[c.Hash] + if !ok { + // No builds for this commit. + return + } + for _, b := range builds { + builder := luci.Builders[b.BuilderName] + if builder.Repo != repo || builder.GoBranch != goBranch { + continue + } + switch b.Status { + case bbpb.Status_STARTED, bbpb.Status_SUCCESS, bbpb.Status_FAILURE, bbpb.Status_INFRA_FAILURE: + default: + continue + } + // TODO: Maybe dedup builder name calculation with addLUCIBuilders and more places. + // That said, those few places have slightly different details, so maybe it's fine. + shortGoBranch := "tip" + if after, ok := strings.CutPrefix(goBranch, "release-branch.go"); ok { + shortGoBranch = after + } + tagFriendly := builder.Name + if after, ok := strings.CutPrefix(tagFriendly, fmt.Sprintf("x_%s-go%s-%s-%s_", repo, shortGoBranch, builder.Target.GOOS, builder.Target.GOARCH)); ok { + // Convert os-arch_osversion-mod1-mod2 (an underscore at start of "_osversion") + // to have os-arch-osversion-mod1-mod2 (a dash at start of "-osversion") form. + // The tag computation below uses this to find both "osversion-mod1" or "mod1". + tagFriendly = fmt.Sprintf("x_%s-go%s-%s-%s-", repo, shortGoBranch, builder.Target.GOOS, builder.Target.GOARCH) + after + } + // The builder name is "os-arch{-suffix}-🐇". The "🐇" is used to avoid collisions + // in builder names between LUCI builders and coordinator builders, and to make it + // possible to tell LUCI builders apart by their name. + builderName := strings.TrimPrefix(tagFriendly, fmt.Sprintf("x_%s-go%s-", repo, shortGoBranch)) + "-🐇" + switch b.Status { + case bbpb.Status_STARTED: + if c.BuildingURLs == nil { + c.BuildingURLs = make(map[builderAndGoHash]string) + } + c.BuildingURLs[builderAndGoHash{builderName, goHash}] = buildURL(b.ID) + case bbpb.Status_SUCCESS, bbpb.Status_FAILURE: + c.ResultData = append(c.ResultData, fmt.Sprintf("%s|%t|%s|%s", + builderName, + b.Status == bbpb.Status_SUCCESS, + buildURL(b.ID), + goHash, + )) + case bbpb.Status_INFRA_FAILURE: + c.ResultData = append(c.ResultData, fmt.Sprintf("%s|%s|%s|%s", + builderName, + "infra_failure", + buildURL(b.ID), + goHash, + )) + } + } +} + +func buildURL(buildID int64) string { + return fmt.Sprintf("https://ci.chromium.org/b/%d", buildID) +} + +func (tb *uiTemplateDataBuilder) buildTemplateData(ctx context.Context, datastoreCl *datastore.Client, luci lucipoll.Snapshot) (*uiTemplateData, error) { + wantCommits := tb.getCommitsToLoad() + var dsCommits map[string]*Commit + if datastoreCl != nil { + var err error + dsCommits, err = tb.loadDatastoreCommits(ctx, datastoreCl, wantCommits) + if err != nil { + return nil, err + } + } else if m := tb.testCommitData; m != nil { + // Allow tests to fake what the datastore would've loaded, and + // thus also allow tests to be run without a real (or fake) datastore. + dsCommits = make(map[string]*Commit) + for k := range wantCommits { + if c, ok := m[k.commit]; ok { + dsCommits[k.commit] = c + } + } } var commits []*CommitInfo @@ -286,6 +445,7 @@ func (tb *uiTemplateDataBuilder) buildTemplateData(ctx context.Context, datastor ci := tb.newCommitInfo(dsCommits, tb.req.Repo, dc) commits = append(commits, ci) } + appendLUCIResults(luci, commits, tb.repoGerritProj()) // x/ repo sections at bottom (each is a "TagState", for historical reasons) var xRepoSections []*TagState @@ -302,18 +462,21 @@ func (tb *uiTemplateDataBuilder) buildTemplateData(ctx context.Context, datastor if path == "" { continue } + ci := tb.newCommitInfo(dsCommits, path, rh.Commit) + appendLUCIResultsXRepo(luci, ci, rh.GerritProject, ts.Branch(), gorel.BranchCommit) ts.Packages = append(ts.Packages, &PackageState{ Package: &Package{ Name: rh.GerritProject, Path: path, }, - Commit: tb.newCommitInfo(dsCommits, path, rh.Commit), + Commit: ci, }) } builders := map[string]bool{} for _, pkg := range ts.Packages { addBuilders(builders, pkg.Package.Name, ts.Branch()) } + addLUCIBuilders(luci, builders, ts.Packages, gorel.BranchName) ts.Builders = builderKeys(builders) sort.Slice(ts.Packages, func(i, j int) bool { @@ -335,7 +498,7 @@ func (tb *uiTemplateDataBuilder) buildTemplateData(ctx context.Context, datastor TagState: xRepoSections, Pagination: &Pagination{}, Branches: tb.res.Branches, - Branch: tb.req.Branch, + Branch: tb.branch(), Repo: gerritProject, } @@ -360,13 +523,23 @@ func (tb *uiTemplateDataBuilder) buildTemplateData(ctx context.Context, datastor if tb.view.ShowsActiveBuilds() { // Populate building URLs for the HTML UI only. data.populateBuildingURLs(ctx, tb.activeBuilds) + + // Appending LUCI active builds can be done here, but it's done + // earlier in appendLUCIResults instead because it's convenient + // to do there in the case of LUCI. } return data, nil } // htmlView renders the HTML (default) form of https://build.golang.org/ with no mode parameter. -type htmlView struct{} +type htmlView struct { + // ShowLUCI controls whether to show build results from the LUCI post-submit dashboard + // in addition to coordinator-backed build results from Datastore. + // + // It has no effect if there's no LUCI client. + ShowLUCI bool +} func (htmlView) ShowsActiveBuilds() bool { return true } func (htmlView) ServeDashboard(w http.ResponseWriter, r *http.Request, data *uiTemplateData) { @@ -398,20 +571,16 @@ func dashboardRequest(view dashboardView, r *http.Request) (*apipb.DashboardRequ } } - repo := r.FormValue("repo") // empty for main go repo, else e.g. "golang.org/x/net" - - branch := r.FormValue("branch") - if branch == "" { - branch = "master" - } + repo := r.FormValue("repo") // empty for main go repo, else e.g. "golang.org/x/net" + branch := r.FormValue("branch") // empty means "master" maxCommits := commitsPerPage if branch == "mixed" { maxCommits = 0 // let maintner decide } return &apipb.DashboardRequest{ Page: int32(page), - Branch: branch, Repo: repo, + Branch: branch, MaxCommits: int32(maxCommits), }, nil } @@ -471,7 +640,7 @@ func (jsonView) ServeDashboard(w http.ResponseWriter, r *http.Request, data *uiT func toBuildStatus(host string, data *uiTemplateData) types.BuildStatus { // cell returns one of "" (no data), "ok", or a failure URL. - cell := func(res *Result) string { + cell := func(res *DisplayResult) string { switch { case res == nil: return "" @@ -599,6 +768,36 @@ func addBuilders(builders map[string]bool, gerritProj, branch string) { } } +// addLUCIBuilders adds LUCI builders that match the given xRepos and goBranch +// to the provided builders map. +func addLUCIBuilders(luci lucipoll.Snapshot, builders map[string]bool, xRepos []*PackageState, goBranch string) { + for _, r := range xRepos { + repoName := r.Package.Name + for _, b := range luci.Builders { + if b.Repo != repoName || b.GoBranch != goBranch { + // Filter out builders whose repo and Gobranch doesn't match. + continue + } + shortGoBranch := "tip" + if after, ok := strings.CutPrefix(b.GoBranch, "release-branch.go"); ok { + shortGoBranch = after + } + tagFriendly := b.Name + if after, ok := strings.CutPrefix(tagFriendly, fmt.Sprintf("x_%s-go%s-%s-%s_", b.Repo, shortGoBranch, b.Target.GOOS, b.Target.GOARCH)); ok { + // Convert os-arch_osversion-mod1-mod2 (an underscore at start of "_osversion") + // to have os-arch-osversion-mod1-mod2 (a dash at start of "-osversion") form. + // The tag computation below uses this to find both "osversion-mod1" or "mod1". + tagFriendly = fmt.Sprintf("x_%s-go%s-%s-%s-", b.Repo, shortGoBranch, b.Target.GOOS, b.Target.GOARCH) + after + } + // The builder name is "os-arch{-suffix}-🐇". The "🐇" is used to avoid collisions + // in builder names between LUCI builders and coordinator builders, and to make to + // possible to tell LUCI builders apart by their name. + builderName := strings.TrimPrefix(tagFriendly, fmt.Sprintf("x_%s-go%s-", b.Repo, shortGoBranch)) + "-🐇" + builders[builderName] = true + } + } +} + func builderKeys(m map[string]bool) (s []string) { s = make([]string, 0, len(m)) for k := range m { @@ -680,7 +879,9 @@ type PackageState struct { type CommitInfo struct { Hash string - // ResultData is a copy of the Commit.ResultData field from datastore. + // ResultData is a copy of the [Commit.ResultData] field from datastore, + // with an additional rule that the second '|'-separated value may be "infra_failure" + // to indicate a problem with the infrastructure rather than the code being tested. ResultData []string // BuildingURLs contains the status URL values for builds that diff --git a/cmd/coordinator/internal/legacydash/ui.html b/cmd/coordinator/internal/legacydash/ui.html index c0199f9111..976a1d11d6 100644 --- a/cmd/coordinator/internal/legacydash/ui.html +++ b/cmd/coordinator/internal/legacydash/ui.html @@ -165,9 +165,9 @@

{{if .BuildingURL}} {{else}} - {{if .OK}}ok{{else}}fail{{end}} @@ -277,9 +277,9 @@

{{if .BuildingURL}} {{else}} - {{if .OK}}ok{{else}}fail{{end}} diff --git a/cmd/coordinator/internal/legacydash/ui_test.go b/cmd/coordinator/internal/legacydash/ui_test.go index ca732a7bcb..518fc8e52a 100644 --- a/cmd/coordinator/internal/legacydash/ui_test.go +++ b/cmd/coordinator/internal/legacydash/ui_test.go @@ -11,6 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "golang.org/x/build/cmd/coordinator/internal/lucipoll" "golang.org/x/build/dashboard" "golang.org/x/build/maintner/maintnerd/apipb" "golang.org/x/build/types" @@ -46,6 +47,7 @@ func TestUITemplateDataBuilder(t *testing.T) { want: &uiTemplateData{ Dashboard: goDash, Repo: "go", + Branch: "master", Package: &Package{Name: "Go", Path: ""}, Branches: []string{"release.foo", "release.bar", "dev.blah"}, Builders: []string{"linux-386", "linux-amd64"}, @@ -98,6 +100,7 @@ func TestUITemplateDataBuilder(t *testing.T) { want: &uiTemplateData{ Dashboard: goDash, Repo: "go", + Branch: "master", Package: &Package{Name: "Go", Path: ""}, Branches: []string{"release.foo", "release.bar", "dev.blah"}, Builders: []string{"linux-386", "linux-amd64", "openbsd-amd64"}, @@ -195,6 +198,7 @@ func TestUITemplateDataBuilder(t *testing.T) { want: &uiTemplateData{ Dashboard: goDash, Repo: "go", + Branch: "master", Package: &Package{Name: "Go", Path: ""}, Branches: []string{"release.foo", "release.bar", "dev.blah"}, Builders: []string{"linux-386", "linux-amd64"}, @@ -295,6 +299,7 @@ func TestUITemplateDataBuilder(t *testing.T) { want: &uiTemplateData{ Dashboard: goDash, Repo: "net", + Branch: "master", Package: &Package{Name: "net", Path: "golang.org/x/net"}, Branches: []string{"master", "dev.blah"}, Builders: []string{"linux-386", "linux-amd64"}, @@ -325,7 +330,7 @@ func TestUITemplateDataBuilder(t *testing.T) { activeBuilds: tt.activeBuilds, testCommitData: tt.testCommitData, } - data, err := tb.buildTemplateData(context.Background(), nil) + data, err := tb.buildTemplateData(context.Background(), nil, lucipoll.Snapshot{}) if err != nil { t.Fatal(err) } @@ -348,6 +353,7 @@ func TestToBuildStatus(t *testing.T) { data: &uiTemplateData{ Dashboard: goDash, Repo: "go", + Branch: "master", Package: &Package{Name: "Go", Path: ""}, Branches: []string{"release.foo", "release.bar", "dev.blah"}, Builders: []string{"linux-386", "linux-amd64"}, @@ -486,6 +492,7 @@ func TestToBuildStatus(t *testing.T) { data: &uiTemplateData{ Dashboard: goDash, Repo: "tools", + Branch: "master", Builders: []string{"linux", "windows"}, Commits: []*CommitInfo{ {