go.tools/dashboard: add gccgo build dashboard.
This change adds a new build dashboard url to the existing appengine app: $dashurl/gccgo/ which will show the build status of gccgo. * Added Dashboard struct with exported Name, Rel(ative)Path, and Packages fields. * Added Dashboard Context method that returns an appengine context with a namespace corresponding to the dashboard's name. * Modified HandlerFuncs to use Dashboard's Context method for all appengine requests. * Modified ui template to show different title/header for separate dashboard and added dashboard tab. R=adg CC=golang-dev https://golang.org/cl/13753043
This commit is contained in:
parent
0e6d095d11
commit
7bcc81e644
|
@ -13,8 +13,8 @@ handlers:
|
|||
static_dir: static
|
||||
- url: /log/.+
|
||||
script: _go_app
|
||||
- url: /(|commit|packages|result|tag|todo)
|
||||
- url: /(|gccgo/)(|commit|packages|result|tag|todo)
|
||||
script: _go_app
|
||||
- url: /(init|buildtest|key|_ah/queue/go/delay)
|
||||
- url: /(|gccgo/)(init|buildtest|key|_ah/queue/go/delay)
|
||||
script: _go_app
|
||||
login: admin
|
|
@ -84,7 +84,7 @@ func GetPackage(c appengine.Context, path string) (*Package, error) {
|
|||
// In other words, all Commits with the same PackagePath belong to the same
|
||||
// datastore entity group.
|
||||
type Commit struct {
|
||||
PackagePath string // (empty for Go commits)
|
||||
PackagePath string // (empty for main repo commits)
|
||||
Hash string
|
||||
ParentHash string
|
||||
Num int // Internal monotonic counter unique to this package.
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"appengine"
|
||||
)
|
||||
|
||||
// Dashboard describes a unique build dashboard.
|
||||
type Dashboard struct {
|
||||
Name string // This dashboard's name and namespace
|
||||
RelPath string // The relative url path
|
||||
Packages []*Package // The project's packages to build
|
||||
}
|
||||
|
||||
// dashboardForRequest returns the appropriate dashboard for a given URL path.
|
||||
func dashboardForRequest(r *http.Request) *Dashboard {
|
||||
if strings.HasPrefix(r.URL.Path, gccgoDash.RelPath) {
|
||||
return gccgoDash
|
||||
}
|
||||
return goDash
|
||||
}
|
||||
|
||||
// Context returns a namespaced context for this dashboard, or panics if it
|
||||
// fails to create a new context.
|
||||
func (d *Dashboard) Context(c appengine.Context) appengine.Context {
|
||||
// No namespace needed for the original Go dashboard.
|
||||
if d.Name == "Go" {
|
||||
return c
|
||||
}
|
||||
n, err := appengine.Namespace(c, d.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// the currently known dashboards.
|
||||
var dashboards = []*Dashboard{goDash, gccgoDash}
|
||||
|
||||
// goDash is the dashboard for the main go repository.
|
||||
var goDash = &Dashboard{
|
||||
Name: "Go",
|
||||
RelPath: "/",
|
||||
Packages: goPackages,
|
||||
}
|
||||
|
||||
// goPackages is a list of all of the packages built by the main go repository.
|
||||
var goPackages = []*Package{
|
||||
{
|
||||
Kind: "go",
|
||||
Name: "Go",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.blog",
|
||||
Path: "code.google.com/p/go.blog",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.codereview",
|
||||
Path: "code.google.com/p/go.codereview",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.crypto",
|
||||
Path: "code.google.com/p/go.crypto",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.exp",
|
||||
Path: "code.google.com/p/go.exp",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.image",
|
||||
Path: "code.google.com/p/go.image",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.net",
|
||||
Path: "code.google.com/p/go.net",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.talks",
|
||||
Path: "code.google.com/p/go.talks",
|
||||
},
|
||||
{
|
||||
Kind: "subrepo",
|
||||
Name: "go.tools",
|
||||
Path: "code.google.com/p/go.tools",
|
||||
},
|
||||
}
|
||||
|
||||
// gccgoDash is the dashboard for gccgo.
|
||||
var gccgoDash = &Dashboard{
|
||||
Name: "Gccgo",
|
||||
RelPath: "/gccgo/",
|
||||
Packages: []*Package{
|
||||
{
|
||||
Kind: "gccgo",
|
||||
Name: "Gccgo",
|
||||
},
|
||||
},
|
||||
}
|
|
@ -33,7 +33,7 @@ const commitsPerPage = 30
|
|||
//
|
||||
// This handler is used by a gobuilder process in -commit mode.
|
||||
func commitHandler(r *http.Request) (interface{}, error) {
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
com := new(Commit)
|
||||
|
||||
if r.Method == "GET" {
|
||||
|
@ -132,7 +132,7 @@ func tagHandler(r *http.Request) (interface{}, error) {
|
|||
if err := t.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
defer cache.Tick(c)
|
||||
_, err := datastore.Put(c, t.Key(c), t)
|
||||
return nil, err
|
||||
|
@ -148,7 +148,7 @@ type Todo struct {
|
|||
// 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 := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
now := cache.Now(c)
|
||||
key := "build-todo-" + r.Form.Encode()
|
||||
var todo *Todo
|
||||
|
@ -257,7 +257,7 @@ func buildTodo(c appengine.Context, builder, packagePath, goHash string) (interf
|
|||
// by the dashboard.
|
||||
func packagesHandler(r *http.Request) (interface{}, error) {
|
||||
kind := r.FormValue("kind")
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
now := cache.Now(c)
|
||||
key := "build-packages-" + kind
|
||||
var p []*Package
|
||||
|
@ -282,7 +282,7 @@ func resultHandler(r *http.Request) (interface{}, error) {
|
|||
return nil, errBadMethod(r.Method)
|
||||
}
|
||||
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
res := new(Result)
|
||||
defer r.Body.Close()
|
||||
if err := json.NewDecoder(r.Body).Decode(res); err != nil {
|
||||
|
@ -326,7 +326,7 @@ func resultHandler(r *http.Request) (interface{}, error) {
|
|||
// It handles paths like "/log/hash".
|
||||
func logHandler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-type", "text/plain; charset=utf-8")
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
hash := r.URL.Path[len("/log/"):]
|
||||
key := datastore.NewKey(c, "Log", hash, 0, nil)
|
||||
l := new(Log)
|
||||
|
@ -361,7 +361,7 @@ func (e errBadMethod) Error() string {
|
|||
// supplied key and builder query parameters.
|
||||
func AuthHandler(h dashHandler) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
|
||||
// Put the URL Query values into r.Form to avoid parsing the
|
||||
// request body when calling r.FormValue.
|
||||
|
@ -401,24 +401,26 @@ func keyHandler(w http.ResponseWriter, r *http.Request) {
|
|||
logErr(w, r, errors.New("must supply builder in query string"))
|
||||
return
|
||||
}
|
||||
c := appengine.NewContext(r)
|
||||
c := contextForRequest(r)
|
||||
fmt.Fprint(w, builderKey(c, builder))
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, d := range dashboards {
|
||||
// admin handlers
|
||||
http.HandleFunc("/init", initHandler)
|
||||
http.HandleFunc("/key", keyHandler)
|
||||
http.HandleFunc(d.RelPath+"init", initHandler)
|
||||
http.HandleFunc(d.RelPath+"key", keyHandler)
|
||||
|
||||
// authenticated handlers
|
||||
http.HandleFunc("/commit", AuthHandler(commitHandler))
|
||||
http.HandleFunc("/packages", AuthHandler(packagesHandler))
|
||||
http.HandleFunc("/result", AuthHandler(resultHandler))
|
||||
http.HandleFunc("/tag", AuthHandler(tagHandler))
|
||||
http.HandleFunc("/todo", AuthHandler(todoHandler))
|
||||
http.HandleFunc(d.RelPath+"commit", AuthHandler(commitHandler))
|
||||
http.HandleFunc(d.RelPath+"packages", AuthHandler(packagesHandler))
|
||||
http.HandleFunc(d.RelPath+"result", AuthHandler(resultHandler))
|
||||
http.HandleFunc(d.RelPath+"tag", AuthHandler(tagHandler))
|
||||
http.HandleFunc(d.RelPath+"todo", AuthHandler(todoHandler))
|
||||
|
||||
// public handlers
|
||||
http.HandleFunc("/log/", logHandler)
|
||||
http.HandleFunc(d.RelPath+"log/", logHandler)
|
||||
}
|
||||
}
|
||||
|
||||
func validHash(hash string) bool {
|
||||
|
@ -443,7 +445,11 @@ func builderKey(c appengine.Context, builder string) string {
|
|||
}
|
||||
|
||||
func logErr(w http.ResponseWriter, r *http.Request, err error) {
|
||||
appengine.NewContext(r).Errorf("Error: %v", err)
|
||||
contextForRequest(r).Errorf("Error: %v", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, "Error: ", err)
|
||||
}
|
||||
|
||||
func contextForRequest(r *http.Request) appengine.Context {
|
||||
return dashboardForRequest(r).Context(appengine.NewContext(r))
|
||||
}
|
||||
|
|
|
@ -15,39 +15,11 @@ import (
|
|||
"cache"
|
||||
)
|
||||
|
||||
// defaultPackages specifies the Package records to be created by initHandler.
|
||||
var defaultPackages = []*Package{
|
||||
{Name: "Go", Kind: "go"},
|
||||
}
|
||||
|
||||
// subRepos specifies the Go project sub-repositories.
|
||||
var subRepos = []string{
|
||||
"blog",
|
||||
"codereview",
|
||||
"crypto",
|
||||
"exp",
|
||||
"image",
|
||||
"net",
|
||||
"talks",
|
||||
"tools",
|
||||
}
|
||||
|
||||
// Put subRepos into defaultPackages.
|
||||
func init() {
|
||||
for _, name := range subRepos {
|
||||
p := &Package{
|
||||
Kind: "subrepo",
|
||||
Name: "go." + name,
|
||||
Path: "code.google.com/p/go." + name,
|
||||
}
|
||||
defaultPackages = append(defaultPackages, p)
|
||||
}
|
||||
}
|
||||
|
||||
func initHandler(w http.ResponseWriter, r *http.Request) {
|
||||
c := appengine.NewContext(r)
|
||||
d := dashboardForRequest(r)
|
||||
c := d.Context(appengine.NewContext(r))
|
||||
defer cache.Tick(c)
|
||||
for _, p := range defaultPackages {
|
||||
for _, p := range d.Packages {
|
||||
err := datastore.Get(c, p.Key(c), new(Package))
|
||||
if _, ok := err.(*datastore.ErrFieldMismatch); ok {
|
||||
// Some fields have been removed, so it's okay to ignore this error.
|
||||
|
|
|
@ -25,12 +25,15 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/", uiHandler)
|
||||
for _, d := range dashboards {
|
||||
http.HandleFunc(d.RelPath, uiHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// uiHandler draws the build status page.
|
||||
func uiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
c := appengine.NewContext(r)
|
||||
d := dashboardForRequest(r)
|
||||
c := d.Context(appengine.NewContext(r))
|
||||
now := cache.Now(c)
|
||||
const key = "build-ui"
|
||||
|
||||
|
@ -48,7 +51,7 @@ func uiHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
commits, err := goCommits(c, page)
|
||||
commits, err := dashCommits(c, page)
|
||||
if err != nil {
|
||||
logErr(w, r, err)
|
||||
return
|
||||
|
@ -73,7 +76,7 @@ func uiHandler(w http.ResponseWriter, r *http.Request) {
|
|||
p.Prev = page - 1
|
||||
p.HasPrev = true
|
||||
}
|
||||
data := &uiTemplateData{commits, builders, tipState, p}
|
||||
data := &uiTemplateData{d, commits, builders, tipState, p}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := uiTemplate.Execute(&buf, data); err != nil {
|
||||
|
@ -94,9 +97,9 @@ type Pagination struct {
|
|||
HasPrev bool
|
||||
}
|
||||
|
||||
// goCommits gets a slice of the latest Commits to the Go repository.
|
||||
// dashCommits gets a slice of the latest Commits to the current dashboard.
|
||||
// If page > 0 it paginates by commitsPerPage.
|
||||
func goCommits(c appengine.Context, page int) ([]*Commit, error) {
|
||||
func dashCommits(c appengine.Context, page int) ([]*Commit, error) {
|
||||
q := datastore.NewQuery("Commit").
|
||||
Ancestor((&Package{}).Key(c)).
|
||||
Order("-Num").
|
||||
|
@ -166,6 +169,7 @@ func TagStateByName(c appengine.Context, name string) (*TagState, error) {
|
|||
}
|
||||
|
||||
type uiTemplateData struct {
|
||||
Dashboard *Dashboard
|
||||
Commits []*Commit
|
||||
Builders []string
|
||||
TipState *TagState
|
||||
|
@ -183,6 +187,7 @@ var tmplFuncs = template.FuncMap{
|
|||
"builderArchChar": builderArchChar,
|
||||
"builderTitle": builderTitle,
|
||||
"builderSpans": builderSpans,
|
||||
"buildDashboards": buildDashboards,
|
||||
"repoURL": repoURL,
|
||||
"shortDesc": shortDesc,
|
||||
"shortHash": shortHash,
|
||||
|
@ -265,6 +270,11 @@ 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 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Go Build Dashboard</title>
|
||||
<title>{{$.Dashboard.Name}} Build Dashboard</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
|
@ -53,10 +53,10 @@
|
|||
.build .desc, .build .time, .build .user {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.paginate {
|
||||
.dashboards, .paginate {
|
||||
padding: 0.5em;
|
||||
}
|
||||
.paginate a {
|
||||
.dashboards a, .paginate a {
|
||||
padding: 0.5em;
|
||||
background: #eee;
|
||||
color: blue;
|
||||
|
@ -70,8 +70,12 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Go Build Status</h1>
|
||||
<h1>{{$.Dashboard.Name}} Build Status</h1>
|
||||
<nav class="dashboards">
|
||||
{{range buildDashboards}}
|
||||
<a href="{{.RelPath}}">{{.Name}}</a>
|
||||
{{end}}
|
||||
</nav>
|
||||
|
||||
{{if $.Commits}}
|
||||
|
||||
|
@ -138,6 +142,7 @@
|
|||
|
||||
{{with $.TipState}}
|
||||
{{$goHash := .Tag.Hash}}
|
||||
{{if .Packages}}
|
||||
<h2>
|
||||
Sub-repositories at tip
|
||||
<small>(<a href="{{repoURL .Tag.Hash ""}}">{{shortHash .Tag.Hash}}</a>)</small>
|
||||
|
@ -204,6 +209,7 @@
|
|||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue