1
0
mirror of https://github.com/golang/go synced 2024-11-20 07:24:40 -07:00
go/misc/dashboard/app/build/ui.go
2011-12-19 16:57:25 +11:00

209 lines
4.7 KiB
Go

// 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.
// TODO(adg): packages at weekly/release
// TODO(adg): some means to register new packages
package build
import (
"appengine"
"appengine/datastore"
"exp/template/html"
"http"
"os"
"regexp"
"sort"
"strconv"
"strings"
"template"
)
func init() {
http.HandleFunc("/", uiHandler)
html.Escape(uiTemplate)
}
// uiHandler draws the build status page.
func uiHandler(w http.ResponseWriter, r *http.Request) {
// TODO(adg): put the HTML in memcache and invalidate on updates
c := appengine.NewContext(r)
page, _ := strconv.Atoi(r.FormValue("page"))
if page < 0 {
page = 0
}
commits, err := goCommits(c, page)
if err != nil {
logErr(w, r, err)
return
}
builders := commitBuilders(commits)
tipState, err := TagState(c, "tip")
if err != nil {
logErr(w, r, err)
return
}
p := &Pagination{}
if len(commits) == commitsPerPage {
p.Next = page + 1
}
if page > 0 {
p.Prev = page - 1
p.HasPrev = true
}
data := &uiTemplateData{commits, builders, tipState, p}
if err := uiTemplate.Execute(w, data); err != nil {
logErr(w, r, err)
}
}
type Pagination struct {
Next, Prev int
HasPrev bool
}
// goCommits gets a slice of the latest Commits to the Go repository.
// If page > 0 it paginates by commitsPerPage.
func goCommits(c appengine.Context, page int) ([]*Commit, os.Error) {
q := datastore.NewQuery("Commit").
Ancestor((&Package{}).Key(c)).
Order("-Time").
Limit(commitsPerPage).
Offset(page * commitsPerPage)
var commits []*Commit
_, err := q.GetAll(c, &commits)
return commits, err
}
// commitBuilders returns the names of the builders that provided
// Results for the provided commits.
func commitBuilders(commits []*Commit) []string {
builders := make(map[string]bool)
for _, commit := range commits {
for _, r := range commit.Results("") {
builders[r.Builder] = true
}
}
return keys(builders)
}
func keys(m map[string]bool) (s []string) {
for k := range m {
s = append(s, k)
}
sort.Strings(s)
return
}
// PackageState represents the state of a Package at a tag.
type PackageState struct {
*Package
*Commit
Results []*Result
OK bool
}
// TagState fetches the results for all non-Go packages at the specified tag.
func TagState(c appengine.Context, name string) ([]*PackageState, os.Error) {
tag, err := GetTag(c, name)
if err != nil {
return nil, err
}
pkgs, err := Packages(c)
if err != nil {
return nil, err
}
var states []*PackageState
for _, pkg := range pkgs {
commit, err := pkg.LastCommit(c)
if err != nil {
c.Errorf("no Commit found: %v", pkg)
continue
}
results := commit.Results(tag.Hash)
ok := len(results) > 0
for _, r := range results {
ok = ok && r.OK
}
states = append(states, &PackageState{
pkg, commit, results, ok,
})
}
return states, nil
}
type uiTemplateData struct {
Commits []*Commit
Builders []string
TipState []*PackageState
Pagination *Pagination
}
var uiTemplate = template.Must(
template.New("ui").
Funcs(template.FuncMap{
"builderTitle": builderTitle,
"shortDesc": shortDesc,
"shortHash": shortHash,
"shortUser": shortUser,
"repoURL": repoURL,
}).
ParseFile("build/ui.html"),
)
// builderTitle formats "linux-amd64-foo" as "linux amd64 foo".
func builderTitle(s string) string {
return strings.Replace(s, "-", " ", -1)
}
// shortDesc returns the first line of a description.
func shortDesc(desc string) string {
if i := strings.Index(desc, "\n"); i != -1 {
desc = desc[:i]
}
return desc
}
// shortHash returns a short version of a hash.
func shortHash(hash string) string {
if len(hash) > 12 {
hash = hash[:12]
}
return hash
}
// shortUser returns a shortened version of a user string.
func shortUser(user string) string {
if i, j := strings.Index(user, "<"), strings.Index(user, ">"); i != -1 && j > i {
user = user[i+1 : j]
if k := strings.Index(user, "@golang.org"); k != -1 {
user = user[:k]
}
}
return user
}
// repoRe matches Google Code repositories and subrepositories (without paths).
var repoRe = regexp.MustCompile(`^code\.google\.com/p/([a-z0-9\-]+)(\.[a-z0-9\-]+)?$`)
// repoURL returns the URL of a change at a Google Code repository or subrepo.
func repoURL(hash, packagePath string) (string, os.Error) {
if packagePath == "" {
return "https://code.google.com/p/go/source/detail?r=" + hash, nil
}
m := repoRe.FindStringSubmatch(packagePath)
if m == nil {
return "", os.NewError("unrecognized package: " + packagePath)
}
url := "https://code.google.com/p/" + m[1] + "/source/detail?r=" + hash
if len(m) > 2 {
url += "&repo=" + m[2][1:]
}
return url, nil
}