app/appengine: remove gccgo, show little dot for untested x builder cells

And misc dead code & comment cleanups.

Change-Id: I52308376df8eb1bd1bf919365bd9c7b7848741a9
Reviewed-on: https://go-review.googlesource.com/c/build/+/167198
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/app/appengine/app.yaml b/app/appengine/app.yaml
index 9657aff..7a6ab93 100644
--- a/app/appengine/app.yaml
+++ b/app/appengine/app.yaml
@@ -5,14 +5,10 @@
 - url: /static
   static_dir: static
   secure: always
-- url: /(|gccgo/|hg/)log/.+
-  script: _go_app
-  secure: always
-- url: /(|gccgo/|hg/)(|building|clear-results|commit|packages|result|perf-result|tag|todo|perf|perfdetail|perfgraph|updatebenchmark)
-  script: _go_app
-  secure: always
-- url: /(|gccgo/|hg/)(init|buildtest|key|perflearn|_ah/queue/go/delay)
+- url: /(init|buildtest|key|perflearn|_ah/queue/go/delay)
   script: _go_app
   login: admin
   secure: always
-
+- url: /.*
+  script: _go_app
+  secure: always
diff --git a/app/appengine/build.go b/app/appengine/build.go
index 5f90719..c9a8559 100644
--- a/app/appengine/build.go
+++ b/app/appengine/build.go
@@ -20,11 +20,10 @@
 	"strings"
 	"time"
 
-	"google.golang.org/appengine/datastore"
-
 	"golang.org/x/build/app/cache"
-
+	"golang.org/x/build/dashboard"
 	"golang.org/x/build/internal/loghash"
+	"google.golang.org/appengine/datastore"
 )
 
 const (
@@ -35,8 +34,8 @@
 // A Package describes a package that is listed on the dashboard.
 type Package struct {
 	Kind    string // "subrepo", "external", or empty for the main Go tree
-	Name    string
-	Path    string // (empty for the main Go tree)
+	Name    string // "Go", "arch", "net", ...
+	Path    string // empty for the main Go tree, else "golang.org/x/foo"
 	NextNum int    // Num of the next head Commit
 }
 
@@ -244,6 +243,31 @@
 	return nil
 }
 
+// isUntested reports whether a cell in the build.golang.org grid is
+// an untested configuration.
+//
+// repo is "go", "net", etc.
+// branch is the branch of repo "master" or "release-branch.go1.12"
+// goBranch applies only if repo != "go" and is of form "master" or "release-branch.go1.N"
+//
+// 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 branch == "tip" {
+		branch = "master"
+	}
+	if goBranch == "tip" {
+		goBranch = "master"
+	}
+	bc, ok := dashboard.Builders[builder]
+	if !ok {
+		// Not managed by coordinator. Might be an old-style builder.
+		// TODO: remove this once the old-style builders are all dead.
+		return false
+	}
+	return !bc.BuildsRepoPostSubmit(repo, branch, goBranch)
+}
+
 // Results returns the build Results for this Commit.
 func (c *Commit) Results() (results []*Result) {
 	for _, r := range c.ResultData {
@@ -875,7 +899,7 @@
 }
 
 // A Tag is used to keep track of the most recent Go weekly and release tags.
-// Typically there will be one Tag entity for each kind of hg tag.
+// Typically there will be one Tag entity for each kind of git tag.
 type Tag struct {
 	Kind string // "release", or "tip"
 	Name string // the tag itself (for example: "release.r60")
diff --git a/app/appengine/dash.go b/app/appengine/dash.go
index add375c..75340a3 100644
--- a/app/appengine/dash.go
+++ b/app/appengine/dash.go
@@ -9,15 +9,12 @@
 import (
 	"context"
 	"net/http"
-	"strings"
 
 	"google.golang.org/appengine"
 )
 
 func handleFunc(path string, h http.HandlerFunc) {
-	for _, d := range dashboards {
-		http.Handle(d.Prefix+path, hstsHandler(h))
-	}
+	http.Handle(path, hstsHandler(h))
 }
 
 // hstsHandler wraps an http.HandlerFunc such that it sets the HSTS header.
@@ -36,40 +33,19 @@
 	Packages  []*Package // The project's packages to build
 }
 
-// dashboardForRequest returns the appropriate dashboard for a given URL path.
-func dashboardForRequest(r *http.Request) *Dashboard {
-	for _, d := range dashboards[1:] {
-		if d.Prefix == "" {
-			panic("prefix can be empty only for the first dashboard")
-		}
-		if strings.HasPrefix(r.URL.Path, d.Prefix) {
-			return d
-		}
-	}
-	if dashboards[0].Prefix != "" {
-		panic("prefix for the first dashboard should be empty")
-	}
-	return dashboards[0]
-}
-
 // Context returns a namespaced context for this dashboard, or panics if it
 // fails to create a new context.
-func (d *Dashboard) Context(c context.Context) context.Context {
+func (d *Dashboard) Context(ctx context.Context) context.Context {
 	if d.Namespace == "" {
-		return c
+		return ctx
 	}
-	n, err := appengine.Namespace(c, d.Namespace)
+	n, err := appengine.Namespace(ctx, d.Namespace)
 	if err != nil {
 		panic(err)
 	}
 	return n
 }
 
-// The currently known dashboards.
-// The first one should have an empty prefix and
-// the other ones a non empty prefix.
-var dashboards = []*Dashboard{goDash, gccgoDash}
-
 // goDash is the dashboard for the main go repository.
 var goDash = &Dashboard{
 	Name:      "Go",
@@ -186,19 +162,6 @@
 	},
 }
 
-// gccgoDash is the dashboard for gccgo.
-var gccgoDash = &Dashboard{
-	Name:      "Gccgo",
-	Namespace: "Gccgo",
-	Prefix:    "/gccgo",
-	Packages: []*Package{
-		{
-			Kind: "gccgo",
-			Name: "Gccgo",
-		},
-	},
-}
-
 // hiddenBranches specifies branches that
 // should not be displayed on the build dashboard.
 // This also prevents the builder infrastructure
diff --git a/app/appengine/handler.go b/app/appengine/handler.go
index 8c76c1e..6073ad1 100644
--- a/app/appengine/handler.go
+++ b/app/appengine/handler.go
@@ -92,14 +92,10 @@
 		return nil, errors.New("can only POST commits with master key")
 	}
 
-	// For now, the commit watcher doesn't support gccgo.
-	// TODO(adg,cmang): remove this exception when gccgo is supported.
-	if dashboardForRequest(r) != gccgoDash {
-		v, _ := strconv.Atoi(r.FormValue("version"))
-		if v != watcherVersion {
-			return nil, fmt.Errorf("rejecting POST from commit watcher; need version %v instead of %v",
-				watcherVersion, v)
-		}
+	v, _ := strconv.Atoi(r.FormValue("version"))
+	if v != watcherVersion {
+		return nil, fmt.Errorf("rejecting POST from commit watcher; need version %v instead of %v",
+			watcherVersion, v)
 	}
 
 	// POST request
@@ -577,14 +573,10 @@
 		return nil, errBadMethod(r.Method)
 	}
 
-	// For now, the gccgo builders are using the old stuff.
-	// TODO(adg,cmang): remove this exception when gccgo is updated.
-	if dashboardForRequest(r) != gccgoDash {
-		v, _ := strconv.Atoi(r.FormValue("version"))
-		if v != builderVersion {
-			return nil, fmt.Errorf("rejecting POST from builder; need version %v instead of %v",
-				builderVersion, v)
-		}
+	v, _ := strconv.Atoi(r.FormValue("version"))
+	if v != builderVersion {
+		return nil, fmt.Errorf("rejecting POST from builder; need version %v instead of %v",
+			builderVersion, v)
 	}
 
 	c := contextForRequest(r)
@@ -1031,7 +1023,7 @@
 }
 
 func contextForRequest(r *http.Request) context.Context {
-	return dashboardForRequest(r).Context(appengine.NewContext(r))
+	return goDash.Context(appengine.NewContext(r))
 }
 
 // limitStringLength essentially does return s[:max],
diff --git a/app/appengine/init.go b/app/appengine/init.go
index 592eafe..616de44 100644
--- a/app/appengine/init.go
+++ b/app/appengine/init.go
@@ -18,7 +18,7 @@
 )
 
 func initHandler(w http.ResponseWriter, r *http.Request) {
-	d := dashboardForRequest(r)
+	d := goDash
 	c := d.Context(appengine.NewContext(r))
 	defer cache.Tick(c)
 	for _, p := range d.Packages {
diff --git a/app/appengine/perf_changes.go b/app/appengine/perf_changes.go
index d5f2e44..4ded513 100644
--- a/app/appengine/perf_changes.go
+++ b/app/appengine/perf_changes.go
@@ -24,7 +24,7 @@
 
 // perfSummaryHandler draws the main benchmarking page.
 func perfChangesHandler(w http.ResponseWriter, r *http.Request) {
-	d := dashboardForRequest(r)
+	d := goDash
 	c := d.Context(appengine.NewContext(r))
 
 	page, _ := strconv.Atoi(r.FormValue("page"))
diff --git a/app/appengine/perf_detail.go b/app/appengine/perf_detail.go
index 395f068..cefdd08 100644
--- a/app/appengine/perf_detail.go
+++ b/app/appengine/perf_detail.go
@@ -24,7 +24,7 @@
 }
 
 func perfDetailUIHandler(w http.ResponseWriter, r *http.Request) {
-	d := dashboardForRequest(r)
+	d := goDash
 	c := d.Context(appengine.NewContext(r))
 	pc, err := GetPerfConfig(c, r)
 	if err != nil {
diff --git a/app/appengine/perf_graph.go b/app/appengine/perf_graph.go
index 9566ea1..8959ffb 100644
--- a/app/appengine/perf_graph.go
+++ b/app/appengine/perf_graph.go
@@ -22,7 +22,7 @@
 }
 
 func perfGraphHandler(w http.ResponseWriter, r *http.Request) {
-	d := dashboardForRequest(r)
+	d := goDash
 	c := d.Context(appengine.NewContext(r))
 	pc, err := GetPerfConfig(c, r)
 	if err != nil {
diff --git a/app/appengine/perf_learn.go b/app/appengine/perf_learn.go
index 2354174..737628b 100644
--- a/app/appengine/perf_learn.go
+++ b/app/appengine/perf_learn.go
@@ -8,12 +8,12 @@
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"html/template"
 	"net/http"
 	"sort"
 
-	"context"
 	"google.golang.org/appengine"
 	"google.golang.org/appengine/datastore"
 )
@@ -29,7 +29,7 @@
 )
 
 func perfLearnHandler(w http.ResponseWriter, r *http.Request) {
-	d := dashboardForRequest(r)
+	d := goDash
 	c := d.Context(appengine.NewContext(r))
 
 	pc, err := GetPerfConfig(c, r)
diff --git a/app/appengine/ui.go b/app/appengine/ui.go
index 507d395..ba0103d 100644
--- a/app/appengine/ui.go
+++ b/app/appengine/ui.go
@@ -42,7 +42,7 @@
 
 // uiHandler draws the build status page.
 func uiHandler(w http.ResponseWriter, r *http.Request) {
-	d := dashboardForRequest(r)
+	d := goDash
 	c := d.Context(appengine.NewContext(r))
 	now := cache.Now(c)
 	key := "build-ui"
@@ -207,7 +207,7 @@
 //    hash builder failure-url
 func failuresHandler(w http.ResponseWriter, r *http.Request, data *uiTemplateData) {
 	w.Header().Set("Content-Type", "text/plain")
-	d := dashboardForRequest(r)
+	d := goDash
 	for _, c := range data.Commits {
 		for _, b := range data.Builders {
 			res := c.Result(b, "")
@@ -223,7 +223,7 @@
 // jsonHandler is https://build.golang.org/?mode=json
 // The output is a types.BuildStatus JSON object.
 func jsonHandler(w http.ResponseWriter, r *http.Request, data *uiTemplateData) {
-	d := dashboardForRequest(r)
+	d := goDash
 
 	// cell returns one of "" (no data), "ok", or a failure URL.
 	cell := func(res *Result) string {
@@ -444,6 +444,15 @@
 	Packages []*PackageState
 }
 
+// Branch returns the git branch name, converting from the old
+// terminology we used from Go's hg days into git terminology.
+func (ts *TagState) Branch() string {
+	if ts.Name == "tip" {
+		return "master"
+	}
+	return ts.Name
+}
+
 // PackageState represents the state of a Package at a Tag.
 type PackageState struct {
 	Package *Package
@@ -563,17 +572,15 @@
 )
 
 var tmplFuncs = template.FuncMap{
-	"buildDashboards":    buildDashboards,
-	"builderOS":          builderOS,
 	"builderSpans":       builderSpans,
 	"builderSubheading":  builderSubheading,
 	"builderSubheading2": builderSubheading2,
-	"builderTitle":       builderTitle,
 	"shortDesc":          shortDesc,
 	"shortHash":          shortHash,
 	"shortUser":          shortUser,
 	"tail":               tail,
 	"unsupported":        unsupported,
+	"isUntested":         isUntested,
 }
 
 func splitDash(s string) (string, string) {
@@ -650,16 +657,6 @@
 	return sp
 }
 
-// builderTitle formats "linux-amd64-foo" as "linux amd64 foo".
-func builderTitle(s string) string {
-	return strings.Replace(s, "-", " ", -1)
-}
-
-// buildDashboards returns the known public dashboards.
-func buildDashboards() []*Dashboard {
-	return dashboards
-}
-
 // shortDesc returns the first line of a description.
 func shortDesc(desc string) string {
 	if i := strings.Index(desc, "\n"); i != -1 {
diff --git a/app/appengine/ui.html b/app/appengine/ui.html
index 6b7a0ad..8fd03c7 100644
--- a/app/appengine/ui.html
+++ b/app/appengine/ui.html
@@ -26,9 +26,6 @@
 
     <form action="." method="GET">
     <nav class="dashboards">
-      {{range buildDashboards}}
-        <a href="{{.Prefix}}/">{{.Name}}</a>
-      {{end}}
       {{if not (eq .Branch "")}}
       <label>
         <select name="branch" onchange="this.form.submit()">
@@ -120,9 +117,9 @@
           <td class="time">{{$c.Time.Format "02 Jan 15:04"}}</td>
           <td class="desc" title="{{$c.Desc}}">{{shortDesc $c.Desc}}</td>
         {{end}}
-          {{range $.Builders}}
+          {{range $builderName := $.Builders}}
             <td class="result{{if (unsupported .)}} unsupported{{end}}">
-              {{with $c.Result . $h}}
+              {{with $c.Result $builderName $h}}
                 {{if .BuildingURL}}
                   <a href="{{.BuildingURL}}"><img src="https://golang.org/favicon.ico" height=16 width=16 border=0></a>
                 {{else if .OK}}
@@ -156,9 +153,10 @@
 
   {{range $.TagState}}
     {{$goHash := .Tag.Hash}}
+    {{$goBranch := .Branch}}
     {{if .Packages}}
       <h2>
-        Sub-repositories at {{.Name}}
+        golang.org/x repos at Go {{$goBranch}}
         <small>(<a href="https://go-review.googlesource.com/q/{{.Tag.Hash}}">{{shortHash .Tag.Hash}}</a>)</small>
       </h2>
 
@@ -216,18 +214,22 @@
           <td class="time">{{.Time.Format "02 Jan 15:04"}}</td>
           <td class="desc" title="{{.Desc}}">{{shortDesc .Desc}}</td>
         {{end}}
-        {{range $.Builders}}
+        {{range $builderName := $.Builders}}
           <td class="result{{if (unsupported .)}} unsupported{{end}}">
-            {{with $pkg.Commit.Result . $goHash}}
-              {{if .BuildingURL}}
-                <a href="{{.BuildingURL}}"><img src="https://golang.org/favicon.ico" height=16 width=16 border=0></a>
-              {{else if .OK}}
-                <span class="ok">ok</span>
-              {{else}}
-                <a href="{{$.Dashboard.Prefix}}/log/{{.LogHash}}" class="fail">fail</a>
-              {{end}}
+            {{if isUntested $builderName $pkg.Package.Name "master" $goBranch}}
+              •
             {{else}}
-              &nbsp;
+              {{with $pkg.Commit.Result $builderName $goHash}}
+                {{if .BuildingURL}}
+                  <a href="{{.BuildingURL}}"><img src="https://golang.org/favicon.ico" height=16 width=16 border=0></a>
+                {{else if .OK}}
+                  <span class="ok">ok</span>
+                {{else}}
+                  <a href="{{$.Dashboard.Prefix}}/log/{{.LogHash}}" class="fail">fail</a>
+                {{end}}
+              {{else}}
+                &nbsp;
+              {{end}}
             {{end}}
           </td>
         {{end}}