diff --git a/dashboard/app/app.yaml b/dashboard/app/app.yaml index c5a1f6cb..3fc87bd9 100644 --- a/dashboard/app/app.yaml +++ b/dashboard/app/app.yaml @@ -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 + login: admin \ No newline at end of file diff --git a/dashboard/app/build/build.go b/dashboard/app/build/build.go index 03dc173b..6cf08a26 100644 --- a/dashboard/app/build/build.go +++ b/dashboard/app/build/build.go @@ -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. diff --git a/dashboard/app/build/dash.go b/dashboard/app/build/dash.go new file mode 100644 index 00000000..e30729f8 --- /dev/null +++ b/dashboard/app/build/dash.go @@ -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", + }, + }, +} diff --git a/dashboard/app/build/handler.go b/dashboard/app/build/handler.go index 01bf95e6..49089d9e 100644 --- a/dashboard/app/build/handler.go +++ b/dashboard/app/build/handler.go @@ -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() { - // admin handlers - http.HandleFunc("/init", initHandler) - http.HandleFunc("/key", keyHandler) + for _, d := range dashboards { + // admin handlers + 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)) + // authenticated handlers + 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) + // public handlers + 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)) +} diff --git a/dashboard/app/build/init.go b/dashboard/app/build/init.go index da1f78b0..8f94714a 100644 --- a/dashboard/app/build/init.go +++ b/dashboard/app/build/init.go @@ -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. diff --git a/dashboard/app/build/ui.go b/dashboard/app/build/ui.go index 9d286136..b47f0ed4 100644 --- a/dashboard/app/build/ui.go +++ b/dashboard/app/build/ui.go @@ -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 { diff --git a/dashboard/app/build/ui.html b/dashboard/app/build/ui.html index 5b5f4ebe..dc274982 100644 --- a/dashboard/app/build/ui.html +++ b/dashboard/app/build/ui.html @@ -1,7 +1,7 @@
-