app/appengine: delete more dead code

This is part of a series of CLs to clean up the build.golang.org App
Engine app in prep for it to be modernized, refactored, and replaced,
starting with deleting dead code.

Updates golang/go#34744

Change-Id: I6cddbb44a63597a308f1d4399d21e5b70a8d83bf
Reviewed-on: https://go-review.googlesource.com/c/build/+/208324
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/app/appengine/app.yaml b/app/appengine/app.yaml
index 8061c29..6a84ec4 100644
--- a/app/appengine/app.yaml
+++ b/app/appengine/app.yaml
@@ -5,7 +5,7 @@
   - url: /static
     static_dir: app/appengine/static
     secure: always
-  - url: /(init|buildtest)
+  - url: /init
     script: auto
     login: admin
     secure: always
diff --git a/app/appengine/build.go b/app/appengine/build.go
index 5214ec5..9fd907e 100644
--- a/app/appengine/build.go
+++ b/app/appengine/build.go
@@ -12,7 +12,6 @@
 	"fmt"
 	"io"
 	"io/ioutil"
-	"strconv"
 	"strings"
 	"time"
 
@@ -23,7 +22,6 @@
 
 const (
 	maxDatastoreStringLen = 500
-	PerfRunLength         = 1024
 )
 
 // A Package describes a package that is listed on the dashboard.
@@ -101,12 +99,6 @@
 	// The complete data set is stored in Result entities.
 	ResultData []string `datastore:",noindex"`
 
-	// PerfResults holds a set of “builder|benchmark” tuples denoting
-	// what benchmarks have been executed on the commit.
-	PerfResults []string `datastore:",noindex"`
-
-	FailNotificationSent bool
-
 	buildingURLs map[builderAndGoHash]string
 }
 
@@ -183,25 +175,6 @@
 	com.ResultData = rd
 }
 
-// AddPerfResult remembers that the builder has run the benchmark on the commit.
-// It must be called from inside a datastore transaction.
-func (com *Commit) AddPerfResult(c context.Context, builder, benchmark string) error {
-	if err := datastore.Get(c, com.Key(c), com); err != nil {
-		return fmt.Errorf("getting Commit: %v", err)
-	}
-	if !com.NeedsBenchmarking {
-		return fmt.Errorf("trying to add perf result to Commit(%v) that does not require benchmarking", com.Hash)
-	}
-	s := builder + "|" + benchmark
-	for _, v := range com.PerfResults {
-		if v == s {
-			return nil
-		}
-	}
-	com.PerfResults = append(com.PerfResults, s)
-	return putCommit(c, com)
-}
-
 func trim(s []string, n int) []string {
 	l := min(len(s), n)
 	return s[len(s)-l:]
@@ -314,116 +287,6 @@
 	}
 }
 
-// A CommitRun provides summary information for commits [StartCommitNum, StartCommitNum + PerfRunLength).
-// Descendant of Package.
-type CommitRun struct {
-	PackagePath       string // (empty for main repo commits)
-	StartCommitNum    int
-	Hash              []string    `datastore:",noindex"`
-	User              []string    `datastore:",noindex"`
-	Desc              []string    `datastore:",noindex"` // Only first line.
-	Time              []time.Time `datastore:",noindex"`
-	NeedsBenchmarking []bool      `datastore:",noindex"`
-}
-
-func (cr *CommitRun) Key(c context.Context) *datastore.Key {
-	p := Package{Path: cr.PackagePath}
-	key := strconv.Itoa(cr.StartCommitNum)
-	return datastore.NewKey(c, "CommitRun", key, 0, p.Key(c))
-}
-
-// GetCommitRun loads and returns CommitRun that contains information
-// for commit commitNum.
-func GetCommitRun(c context.Context, commitNum int) (*CommitRun, error) {
-	cr := &CommitRun{StartCommitNum: commitNum / PerfRunLength * PerfRunLength}
-	err := datastore.Get(c, cr.Key(c), cr)
-	if err != nil && err != datastore.ErrNoSuchEntity {
-		return nil, fmt.Errorf("getting CommitRun: %v", err)
-	}
-	if len(cr.Hash) != PerfRunLength {
-		cr.Hash = make([]string, PerfRunLength)
-		cr.User = make([]string, PerfRunLength)
-		cr.Desc = make([]string, PerfRunLength)
-		cr.Time = make([]time.Time, PerfRunLength)
-		cr.NeedsBenchmarking = make([]bool, PerfRunLength)
-	}
-	return cr, nil
-}
-
-func (cr *CommitRun) AddCommit(c context.Context, com *Commit) error {
-	if com.Num < cr.StartCommitNum || com.Num >= cr.StartCommitNum+PerfRunLength {
-		return fmt.Errorf("AddCommit: commit num %v out of range [%v, %v)",
-			com.Num, cr.StartCommitNum, cr.StartCommitNum+PerfRunLength)
-	}
-	i := com.Num - cr.StartCommitNum
-	// Be careful with string lengths,
-	// we need to fit 1024 commits into 1 MB.
-	cr.Hash[i] = com.Hash
-	cr.User[i] = shortDesc(com.User)
-	cr.Desc[i] = shortDesc(com.Desc)
-	cr.Time[i] = com.Time
-	cr.NeedsBenchmarking[i] = com.NeedsBenchmarking
-	if _, err := datastore.Put(c, cr.Key(c), cr); err != nil {
-		return fmt.Errorf("putting CommitRun: %v", err)
-	}
-	return nil
-}
-
-// GetCommits returns [startCommitNum, startCommitNum+n) commits.
-// Commits information is partial (obtained from CommitRun),
-// do not store them back into datastore.
-func GetCommits(c context.Context, startCommitNum, n int) ([]*Commit, error) {
-	if startCommitNum < 0 || n <= 0 {
-		return nil, fmt.Errorf("GetCommits: invalid args (%v, %v)", startCommitNum, n)
-	}
-
-	p := &Package{}
-	t := datastore.NewQuery("CommitRun").
-		Ancestor(p.Key(c)).
-		Filter("StartCommitNum >=", startCommitNum/PerfRunLength*PerfRunLength).
-		Order("StartCommitNum").
-		Limit(100).
-		Run(c)
-
-	res := make([]*Commit, n)
-	for {
-		cr := new(CommitRun)
-		_, err := t.Next(cr)
-		if err == datastore.Done {
-			break
-		}
-		if err != nil {
-			return nil, err
-		}
-		if cr.StartCommitNum >= startCommitNum+n {
-			break
-		}
-		// Calculate start index for copying.
-		i := 0
-		if cr.StartCommitNum < startCommitNum {
-			i = startCommitNum - cr.StartCommitNum
-		}
-		// Calculate end index for copying.
-		e := PerfRunLength
-		if cr.StartCommitNum+e > startCommitNum+n {
-			e = startCommitNum + n - cr.StartCommitNum
-		}
-		for ; i < e; i++ {
-			com := new(Commit)
-			com.Hash = cr.Hash[i]
-			com.User = cr.User[i]
-			com.Desc = cr.Desc[i]
-			com.Time = cr.Time[i]
-			com.NeedsBenchmarking = cr.NeedsBenchmarking[i]
-			res[cr.StartCommitNum-startCommitNum+i] = com
-		}
-		if e != PerfRunLength {
-			break
-		}
-	}
-	return res, nil
-}
-
 // partsToResult converts a Commit and ResultData substrings to a Result.
 func partsToResult(c *Commit, p []string) *Result {
 	return &Result{
diff --git a/app/appengine/config.go b/app/appengine/config.go
deleted file mode 100644
index 4d1dd80..0000000
--- a/app/appengine/config.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
-	"context"
-	"sync"
-
-	"google.golang.org/appengine/datastore"
-	"google.golang.org/appengine/log"
-)
-
-// A global map of rarely-changing configuration values.
-var config = struct {
-	sync.RWMutex
-	m map[string]string
-}{
-	m: make(map[string]string),
-}
-
-// A list of config keys that should be created by initConfig.
-// (Any configuration keys should be listed here.)
-var configKeys = []string{
-	"GerritUsername",
-	"GerritPassword",
-}
-
-// configEntity is how config values are represented in the datastore.
-type configEntity struct {
-	Value string
-}
-
-// Config returns the value for the given key
-// or the empty string if no such key exists.
-func Config(c context.Context, key string) string {
-	config.RLock()
-	v, ok := config.m[key]
-	config.RUnlock()
-	if ok {
-		return v
-	}
-
-	config.Lock()
-	defer config.Unlock()
-
-	// Lookup might have happened after RUnlock; check again.
-	if v, ok := config.m[key]; ok {
-		return v
-	}
-
-	// Lookup config value in datastore.
-	k := datastore.NewKey(c, "Config", key, 0, nil)
-	ent := configEntity{}
-	if err := datastore.Get(c, k, &ent); err != nil {
-		log.Errorf(c, "Get Config: %v", err)
-		return ""
-	}
-	// Don't return or cache the dummy value.
-	if ent.Value == configDummy {
-		return ""
-	}
-	config.m[key] = ent.Value
-	return ent.Value
-}
-
-// initConfig is invoked by the initHandler to create an entity for each key in
-// configKeys. This makes it easy to edit the configuration values using the
-// Datastore Viewer in the App Engine dashboard.
-func initConfig(c context.Context) {
-	for _, key := range configKeys {
-		err := datastore.RunInTransaction(c, func(c context.Context) error {
-			k := datastore.NewKey(c, "Config", key, 0, nil)
-			ent := configEntity{}
-			if err := datastore.Get(c, k, &ent); err == nil {
-				log.Infof(c, "huh? %v", key)
-				return nil
-			} else if err != datastore.ErrNoSuchEntity {
-				return err
-			}
-			ent.Value = configDummy
-			_, err := datastore.Put(c, k, &ent)
-			log.Infof(c, "BLAH BLAH %v", key)
-			return err
-		}, nil)
-		if err != nil {
-			log.Errorf(c, "initConfig %v: %v", key, err)
-		}
-	}
-}
-
-const configDummy = "[config value unset]"
diff --git a/app/appengine/dash.go b/app/appengine/dash.go
index ee7a6f4..0e84386 100644
--- a/app/appengine/dash.go
+++ b/app/appengine/dash.go
@@ -24,13 +24,10 @@
 	handleFunc("/packages", AuthHandler(packagesHandler))
 	handleFunc("/result", AuthHandler(resultHandler))
 	handleFunc("/tag", AuthHandler(tagHandler))
-	handleFunc("/todo", AuthHandler(todoHandler))
 
 	// public handlers
 	handleFunc("/", uiHandler)
 	handleFunc("/log/", logHandler)
-	handleFunc("/updatebenchmark", updateBenchmark)
-	handleFunc("/buildtest", testHandler)
 
 	appengine.Main()
 }
@@ -48,10 +45,12 @@
 }
 
 // Dashboard describes a unique build dashboard.
+//
+// (There used to be more than one dashboard, so this is now somewhat
+// less important than it once was.)
 type Dashboard struct {
-	Name      string     // This dashboard's name (eg, "Go")
-	Namespace string     // This dashboard's namespace (eg, "" (default), "Git")
-	Prefix    string     // The path prefix (no trailing /)
+	Name      string     // This dashboard's name (always "Go" nowadays)
+	Namespace string     // This dashboard's namespace (always "Git" nowadays)
 	Packages  []*Package // The project's packages to build
 }
 
@@ -72,7 +71,6 @@
 var goDash = &Dashboard{
 	Name:      "Go",
 	Namespace: "Git",
-	Prefix:    "",
 	Packages:  goPackages,
 }
 
diff --git a/app/appengine/handler.go b/app/appengine/handler.go
index 04ab46f..58be380 100644
--- a/app/appengine/handler.go
+++ b/app/appengine/handler.go
@@ -227,140 +227,6 @@
 	return nil, err
 }
 
-// Todo is a todoHandler response.
-type Todo struct {
-	Kind string // "build-go-commit" or "build-package"
-	Data interface{}
-}
-
-// todoHandler returns the next action to be performed by a builder.
-// It expects "builder" and "kind" query parameters and returns a *Todo value.
-// Multiple "kind" parameters may be specified.
-func todoHandler(r *http.Request) (interface{}, error) {
-	c := contextForRequest(r)
-	now := cache.Now(c)
-	key := "build-todo-" + r.Form.Encode()
-	var todo *Todo
-	if cache.Get(c, r, now, key, &todo) {
-		// Hack to avoid storing nil in memcache.
-		if todo.Kind == "none" {
-			return nil, nil
-		}
-		return todo, nil
-	}
-	var err error
-	builder := r.FormValue("builder")
-	if builderKeyRevoked(builder) {
-		return nil, fmt.Errorf("builder key revoked; no work given")
-	}
-	for _, kind := range r.Form["kind"] {
-		var com *Commit
-		switch kind {
-		case "build-go-commit":
-			com, err = buildTodo(c, builder, "", "")
-		case "build-package":
-			packagePath := r.FormValue("packagePath")
-			goHash := r.FormValue("goHash")
-			com, err = buildTodo(c, builder, packagePath, goHash)
-		}
-		if com != nil || err != nil {
-			if com != nil {
-				// ResultData can be large and not needed on builder.
-				com.ResultData = []string{}
-			}
-			todo = &Todo{Kind: kind, Data: com}
-			break
-		}
-	}
-	if err == nil {
-		// Hack to avoid storing nil in memcache.
-		if todo == nil {
-			todo = &Todo{Kind: "none"}
-		}
-		cache.Set(c, r, now, key, todo)
-	}
-	// Hack to avoid storing nil in memcache.
-	if todo.Kind == "none" {
-		return nil, nil
-	}
-	return todo, err
-}
-
-// buildTodo returns the next Commit to be built (or nil if none available).
-//
-// If packagePath and goHash are empty, it scans the first 20 Go Commits in
-// Num-descending order and returns the first one it finds that doesn't have a
-// Result for this builder.
-//
-// If provided with non-empty packagePath and goHash args, it scans the first
-// 20 Commits in Num-descending order for the specified packagePath and
-// returns the first that doesn't have a Result for this builder and goHash.
-func buildTodo(c context.Context, builder, packagePath, goHash string) (*Commit, error) {
-	p, err := GetPackage(c, packagePath)
-	if err != nil {
-		return nil, err
-	}
-
-	t := datastore.NewQuery("Commit").
-		Ancestor(p.Key(c)).
-		Limit(commitsPerPage).
-		Order("-Num").
-		Run(c)
-	for {
-		com := new(Commit)
-		if _, err := t.Next(com); err == datastore.Done {
-			break
-		} else if err != nil {
-			return nil, err
-		}
-		if com.Result(builder, goHash) == nil {
-			return com, nil
-		}
-	}
-
-	// Nothing left to do if this is a package (not the Go tree).
-	if packagePath != "" {
-		return nil, nil
-	}
-
-	// If there are no Go tree commits left to build,
-	// see if there are any subrepo commits that need to be built at tip.
-	// If so, ask the builder to build a go tree at the tip commit.
-	// TODO(adg): do the same for "weekly" and "release" tags.
-
-	tag, err := GetTag(c, "tip", "")
-	if err != nil {
-		return nil, err
-	}
-
-	// Check that this Go commit builds OK for this builder.
-	// If not, don't re-build as the subrepos will never get built anyway.
-	com, err := tag.Commit(c)
-	if err != nil {
-		return nil, err
-	}
-	if r := com.Result(builder, ""); r != nil && !r.OK {
-		return nil, nil
-	}
-
-	pkgs, err := Packages(c, "subrepo")
-	if err != nil {
-		return nil, err
-	}
-	for _, pkg := range pkgs {
-		com, err := pkg.LastCommit(c)
-		if err != nil {
-			log.Warningf(c, "%v: no Commit found: %v", pkg, err)
-			continue
-		}
-		if com.Result(builder, tag.Hash) == nil {
-			return tag.Commit(c)
-		}
-	}
-
-	return nil, nil
-}
-
 // packagesHandler returns a list of the non-Go Packages monitored
 // by the dashboard.
 func packagesHandler(r *http.Request) (interface{}, error) {
@@ -600,8 +466,10 @@
 	}
 }
 
+// validHash reports whether hash looks like a valid git commit hash.
 func validHash(hash string) bool {
-	// TODO(adg): correctly validate a hash
+	// TODO: correctly validate a hash: check that it's exactly 40
+	// lowercase hex digits. But this is what we historically did:
 	return hash != ""
 }
 
diff --git a/app/appengine/index.yaml b/app/appengine/index.yaml
index 5dbdcf5..31f97c4 100644
--- a/app/appengine/index.yaml
+++ b/app/appengine/index.yaml
@@ -18,44 +18,10 @@
   properties:
   - name: Time
     direction: desc
-    
+
 - kind: Commit
   ancestor: yes
   properties:
   - name: NeedsBenchmarking
   - name: Num
     direction: desc
-
-- kind: CommitRun
-  ancestor: yes
-  properties:
-  - name: StartCommitNum
-    direction: desc
-
-- kind: PerfResult
-  ancestor: yes
-  properties:
-  - name: CommitNum
-    direction: desc
-
-- kind: PerfResult   
-  ancestor: yes
-  properties:
-  - name: CommitNum
-    direction: asc
-
-- kind: CommitRun
-  ancestor: yes
-  properties:
-  - name: StartCommitNum
-    direction: asc
-
-- kind: PerfMetricRun
-  ancestor: yes
-  properties:
-  - name: Builder
-  - name: Benchmark
-  - name: Metric
-  - name: StartCommitNum
-    direction: asc
-
diff --git a/app/appengine/init.go b/app/appengine/init.go
index e02cde9..ffc1dc2 100644
--- a/app/appengine/init.go
+++ b/app/appengine/init.go
@@ -41,9 +41,6 @@
 	// Create secret key.
 	key.Secret(c)
 
-	// Create dummy config values.
-	initConfig(c)
-
 	// Populate Go 1.4 tag. This is for bootstrapping the new feature of
 	// building sub-repos against the stable release.
 	// TODO(adg): remove this after Go 1.5 is released, at which point the
diff --git a/app/appengine/test.go b/app/appengine/test.go
deleted file mode 100644
index f8e1f5c..0000000
--- a/app/appengine/test.go
+++ /dev/null
@@ -1,345 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-// TODO(adg): test authentication
-// TODO(adg): refactor to use appengine/aetest instead
-
-import (
-	"bytes"
-	"context"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"io"
-	"net/http"
-	"net/http/httptest"
-	"net/url"
-	"strings"
-	"time"
-
-	"google.golang.org/appengine"
-	"google.golang.org/appengine/datastore"
-	"google.golang.org/appengine/log"
-)
-
-var testEntityKinds = []string{
-	"Package",
-	"Commit",
-	"CommitRun",
-	"Result",
-	"Log",
-}
-
-const testPkg = "golang.org/x/test"
-
-var testPackage = &Package{Name: "Test", Kind: "subrepo", Path: testPkg}
-
-var testPackages = []*Package{
-	{Name: "Go", Path: ""},
-	testPackage,
-}
-
-var tCommitTime = time.Now().Add(-time.Hour * 24 * 7)
-
-func tCommit(hash, parentHash, path string, bench bool) *Commit {
-	tCommitTime.Add(time.Hour) // each commit should have a different time
-	return &Commit{
-		PackagePath:       path,
-		Hash:              hash,
-		ParentHash:        parentHash,
-		Time:              tCommitTime,
-		User:              "adg",
-		Desc:              "change description " + hash,
-		NeedsBenchmarking: bench,
-	}
-}
-
-var testRequests = []struct {
-	path string
-	vals url.Values
-	req  interface{}
-	res  interface{}
-}{
-	// Packages
-	{"/packages", url.Values{"kind": {"subrepo"}}, nil, []*Package{testPackage}},
-
-	// Go repo
-	{"/commit", nil, tCommit("0001", "0000", "", true), nil},
-	{"/commit", nil, tCommit("0002", "0001", "", false), nil},
-	{"/commit", nil, tCommit("0003", "0002", "", true), nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0001", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0002", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-
-	// Other builders, to test the UI.
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "linux-amd64-race", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "netbsd-386", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "plan9-386", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "windows-386", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "windows-amd64", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "windows-amd64-race", Hash: "0001", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "linux-amd64-temp", Hash: "0001", OK: true}, nil},
-
-	// multiple builders
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0003", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0002"}}},
-
-	// branches
-	{"/commit", nil, tCommit("0004", "0003", "", false), nil},
-	{"/commit", nil, tCommit("0005", "0002", "", false), nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0005", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0004"}}},
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0004", OK: false}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
-
-	// logs
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil},
-	{"/log/a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", nil, nil, "test"},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil},
-
-	// repeat failure (shouldn't re-send mail)
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil},
-
-	// non-Go repos
-	{"/commit", nil, tCommit("1001", "0000", testPkg, false), nil},
-	{"/commit", nil, tCommit("1002", "1001", testPkg, false), nil},
-	{"/commit", nil, tCommit("1003", "1002", testPkg, false), nil},
-	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1003", GoHash: "0001", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1002"}}},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1002", GoHash: "0001", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1001"}}},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0001", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, nil},
-	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0002"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}},
-
-	// re-build Go revision for stale subrepos
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0005", OK: false, Log: "boo"}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil},
-
-	// benchmarks
-	// build-go-commit must have precedence over benchmark-go-commit
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
-	// drain build-go-commit todo
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0005", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0004"}}},
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0004", OK: true}, nil},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0002"}}},
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0002", OK: true}, nil},
-	// drain sub-repo todos
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1001", GoHash: "0005", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1002", GoHash: "0005", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1003", GoHash: "0005", OK: false}, nil},
-	// now we must get benchmark todo
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "benchmark-go-commit", Data: &Commit{Hash: "0003", PerfResults: []string{}}}},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, nil},
-	// create new commit, it must appear in todo
-	{"/commit", nil, tCommit("0006", "0005", "", true), nil},
-	// drain build-go-commit todo
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0006"}}},
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0006", OK: true}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1003", GoHash: "0006", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1002", GoHash: "0006", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1001", GoHash: "0006", OK: false}, nil},
-	// now we must get benchmark todo
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "benchmark-go-commit", Data: &Commit{Hash: "0006", PerfResults: []string{}}}},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, nil},
-	// create new benchmark, all commits must re-appear in todo
-	{"/commit", nil, tCommit("0007", "0006", "", true), nil},
-	// drain build-go-commit todo
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0007"}}},
-	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0007", OK: true}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1003", GoHash: "0007", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1002", GoHash: "0007", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-amd64", Hash: "1001", GoHash: "0007", OK: false}, nil},
-	// now we must get benchmark todo
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "benchmark-go-commit", Data: &Commit{Hash: "0007", PerfResults: []string{}}}},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-amd64"}}, nil, nil},
-	// attach second builder
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0007"}}},
-	// drain build-go-commit todo
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0007", OK: true}, nil},
-	{"/result", nil, &Result{Builder: "linux-386", Hash: "0006", OK: true}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1003", GoHash: "0007", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1002", GoHash: "0007", OK: false}, nil},
-	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0007", OK: false}, nil},
-	// now we must get benchmark todo
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "benchmark-go-commit", Data: &Commit{Hash: "0007"}}},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "benchmark-go-commit", Data: &Commit{Hash: "0003"}}},
-	{"/todo", url.Values{"kind": {"build-go-commit", "benchmark-go-commit"}, "builder": {"linux-386"}}, nil, nil},
-}
-
-func testHandler(w http.ResponseWriter, r *http.Request) {
-	if !appengine.IsDevAppServer() {
-		fmt.Fprint(w, "These tests must be run under the dev_appserver.")
-		return
-	}
-	c := appengine.NewContext(r)
-	if err := nukeEntities(c, testEntityKinds); err != nil {
-		logErr(w, r, err)
-		return
-	}
-	if r.FormValue("nukeonly") != "" {
-		fmt.Fprint(w, "OK")
-		return
-	}
-
-	for _, p := range testPackages {
-		if _, err := datastore.Put(c, p.Key(c), p); err != nil {
-			logErr(w, r, err)
-			return
-		}
-	}
-
-	origReq := *r
-	defer func() {
-		// HACK: We need to clobber the original request (see below)
-		// so make sure we fix it before exiting the handler.
-		*r = origReq
-	}()
-	for i, t := range testRequests {
-		log.Infof(c, "running test %d %s vals='%q' req='%q' res='%q'", i, t.path, t.vals, t.req, t.res)
-		errorf := func(format string, args ...interface{}) {
-			fmt.Fprintf(w, "%d %s: ", i, t.path)
-			fmt.Fprintf(w, format, args...)
-			fmt.Fprintln(w)
-		}
-		var body io.ReadWriter
-		if t.req != nil {
-			body = new(bytes.Buffer)
-			json.NewEncoder(body).Encode(t.req)
-		}
-		const domain = "build.golang.org"
-		url := "http://" + domain + t.path
-		if t.vals != nil {
-			url += "?" + t.vals.Encode() + "&version=3"
-		} else {
-			url += "?version=3"
-		}
-		req, err := http.NewRequest("POST", url, body)
-		if err != nil {
-			logErr(w, r, err)
-			return
-		}
-		if t.req != nil {
-			req.Method = "POST"
-		}
-		req.Header = origReq.Header
-		rec := httptest.NewRecorder()
-
-		// Make the request
-		*r = *req // HACK: App Engine uses the request pointer
-		// as a map key to resolve Contexts.
-		http.DefaultServeMux.ServeHTTP(rec, r)
-
-		if rec.Code != 0 && rec.Code != 200 {
-			errorf(rec.Body.String())
-			return
-		}
-		log.Infof(c, "response='%v'", rec.Body.String())
-		resp := new(dashResponse)
-
-		// If we're expecting a *Todo value,
-		// prime the Response field with a Todo and a Commit inside it.
-		if t.path == "/todo" {
-			resp.Response = &Todo{Data: &Commit{}}
-		}
-
-		if strings.HasPrefix(t.path, "/log/") {
-			resp.Response = rec.Body.String()
-		} else {
-			err := json.NewDecoder(rec.Body).Decode(resp)
-			if err != nil {
-				errorf("decoding response: %v", err)
-				return
-			}
-		}
-		if e, ok := t.res.(string); ok {
-			g, ok := resp.Response.(string)
-			if !ok {
-				errorf("Response not string: %T", resp.Response)
-				return
-			}
-			if g != e {
-				errorf("response mismatch: got %q want %q", g, e)
-				return
-			}
-		}
-		if e, ok := t.res.(*Todo); ok {
-			g, ok := resp.Response.(*Todo)
-			if !ok {
-				errorf("Response not *Todo: %T", resp.Response)
-				return
-			}
-			if e.Data == nil && g.Data != nil {
-				errorf("Response.Data should be nil, got: %v", g.Data)
-				return
-			}
-			if g.Data == nil {
-				errorf("Response.Data is nil, want: %v", e.Data)
-				return
-			}
-			gd, ok := g.Data.(*Commit)
-			if !ok {
-				errorf("Response.Data not *Commit: %T", g.Data)
-				return
-			}
-			if g.Kind != e.Kind {
-				errorf("kind don't match: got %q, want %q", g.Kind, e.Kind)
-				return
-			}
-			ed := e.Data.(*Commit)
-			if ed.Hash != gd.Hash {
-				errorf("hashes don't match: got %q, want %q", gd.Hash, ed.Hash)
-				return
-			}
-			if len(gd.PerfResults) != len(ed.PerfResults) {
-				errorf("result data len don't match: got %v, want %v", len(gd.PerfResults), len(ed.PerfResults))
-				return
-			}
-			for i := range gd.PerfResults {
-				if gd.PerfResults[i] != ed.PerfResults[i] {
-					errorf("result data %v don't match: got %v, want %v", i, gd.PerfResults[i], ed.PerfResults[i])
-					return
-				}
-			}
-		}
-		if t.res == nil && resp.Response != nil {
-			errorf("response mismatch: got %q expected <nil>", resp.Response)
-			return
-		}
-	}
-	fmt.Fprint(w, "PASS\nYou should see only one mail notification (for 0003/linux-386) in the dev_appserver logs.")
-}
-
-func nukeEntities(c context.Context, kinds []string) error {
-	if !appengine.IsDevAppServer() {
-		return errors.New("can't nuke production data")
-	}
-	var keys []*datastore.Key
-	for _, kind := range kinds {
-		q := datastore.NewQuery(kind).KeysOnly()
-		for t := q.Run(c); ; {
-			k, err := t.Next(nil)
-			if err == datastore.Done {
-				break
-			}
-			if err != nil {
-				return err
-			}
-			keys = append(keys, k)
-		}
-	}
-	return datastore.DeleteMulti(c, keys)
-}
diff --git a/app/appengine/ui.go b/app/appengine/ui.go
index 1b788d0..4e6c69a 100644
--- a/app/appengine/ui.go
+++ b/app/appengine/ui.go
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// TODO(adg): packages at weekly/release
-// TODO(adg): some means to register new packages
-
 package main
 
 import (
@@ -217,14 +214,13 @@
 //    hash builder failure-url
 func failuresHandler(w http.ResponseWriter, r *http.Request, data *uiTemplateData) {
 	w.Header().Set("Content-Type", "text/plain")
-	d := goDash
 	for _, c := range data.Commits {
 		for _, b := range data.Builders {
 			res := c.Result(b, "")
 			if res == nil || res.OK || res.LogHash == "" {
 				continue
 			}
-			url := fmt.Sprintf("https://%v%v/log/%v", r.Host, d.Prefix, res.LogHash)
+			url := fmt.Sprintf("https://%v/log/%v", r.Host, res.LogHash)
 			fmt.Fprintln(w, c.Hash, b, url)
 		}
 	}
@@ -233,8 +229,6 @@
 // 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 := goDash
-
 	// cell returns one of "" (no data), "ok", or a failure URL.
 	cell := func(res *Result) string {
 		switch {
@@ -243,7 +237,7 @@
 		case res.OK:
 			return "ok"
 		}
-		return fmt.Sprintf("https://%v%v/log/%v", r.Host, d.Prefix, res.LogHash)
+		return fmt.Sprintf("https://%v/log/%v", r.Host, res.LogHash)
 	}
 
 	var res types.BuildStatus
diff --git a/app/appengine/update.go b/app/appengine/update.go
deleted file mode 100644
index 14406bc..0000000
--- a/app/appengine/update.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"net/http"
-
-	"context"
-
-	"google.golang.org/appengine"
-	"google.golang.org/appengine/datastore"
-)
-
-func updateBenchmark(w http.ResponseWriter, r *http.Request) {
-	if !appengine.IsDevAppServer() {
-		fmt.Fprint(w, "Update must not run on real server.")
-		return
-	}
-
-	if r.Method != "POST" {
-		fmt.Fprintf(w, "bad request method")
-		return
-	}
-
-	c := contextForRequest(r)
-	if !validKey(c, r.FormValue("key"), r.FormValue("builder")) {
-		fmt.Fprintf(w, "bad builder/key")
-		return
-	}
-
-	defer r.Body.Close()
-	var hashes []string
-	if err := json.NewDecoder(r.Body).Decode(&hashes); err != nil {
-		fmt.Fprintf(w, "failed to decode request: %v", err)
-		return
-	}
-
-	ncommit := 0
-	nrun := 0
-	tx := func(c context.Context) error {
-		var cr *CommitRun
-		for _, hash := range hashes {
-			// Update Commit.
-			com := &Commit{Hash: hash}
-			err := datastore.Get(c, com.Key(c), com)
-			if err != nil && err != datastore.ErrNoSuchEntity {
-				return fmt.Errorf("fetching Commit: %v", err)
-			}
-			if err == datastore.ErrNoSuchEntity {
-				continue
-			}
-			com.NeedsBenchmarking = true
-			com.PerfResults = nil
-			if err := putCommit(c, com); err != nil {
-				return err
-			}
-			ncommit++
-
-			// Update CommitRun.
-			if cr != nil && cr.StartCommitNum != com.Num/PerfRunLength*PerfRunLength {
-				if _, err := datastore.Put(c, cr.Key(c), cr); err != nil {
-					return fmt.Errorf("putting CommitRun: %v", err)
-				}
-				nrun++
-				cr = nil
-			}
-			if cr == nil {
-				var err error
-				cr, err = GetCommitRun(c, com.Num)
-				if err != nil {
-					return fmt.Errorf("getting CommitRun: %v", err)
-				}
-			}
-			if com.Num < cr.StartCommitNum || com.Num >= cr.StartCommitNum+PerfRunLength {
-				return fmt.Errorf("commit num %v out of range [%v, %v)", com.Num, cr.StartCommitNum, cr.StartCommitNum+PerfRunLength)
-			}
-			idx := com.Num - cr.StartCommitNum
-			cr.Hash[idx] = com.Hash
-			cr.User[idx] = shortDesc(com.User)
-			cr.Desc[idx] = shortDesc(com.Desc)
-			cr.Time[idx] = com.Time
-			cr.NeedsBenchmarking[idx] = com.NeedsBenchmarking
-		}
-		if cr != nil {
-			if _, err := datastore.Put(c, cr.Key(c), cr); err != nil {
-				return fmt.Errorf("putting CommitRun: %v", err)
-			}
-			nrun++
-		}
-		return nil
-	}
-	if err := datastore.RunInTransaction(c, tx, nil); err != nil {
-		fmt.Fprintf(w, "failed to execute tx: %v", err)
-		return
-	}
-	fmt.Fprintf(w, "OK (updated %v commits and %v commit runs)", ncommit, nrun)
-}