| Index: milo/frontend/view_console.go
|
| diff --git a/milo/frontend/view_console.go b/milo/frontend/view_console.go
|
| index b314fecf4da2344e12e9c1cf6b90ce0fe4af5e84..a5cb3126272975c80f2f5a9a1c967846647393be 100644
|
| --- a/milo/frontend/view_console.go
|
| +++ b/milo/frontend/view_console.go
|
| @@ -15,33 +15,29 @@
|
| package frontend
|
|
|
| import (
|
| + "encoding/hex"
|
| "fmt"
|
| + "html/template"
|
| "net/http"
|
| "strings"
|
|
|
| "golang.org/x/net/context"
|
|
|
| - "github.com/luci/luci-go/common/api/gitiles"
|
| "github.com/luci/luci-go/common/clock"
|
| "github.com/luci/luci-go/common/errors"
|
| "github.com/luci/luci-go/common/logging"
|
| + "github.com/luci/luci-go/common/proto/google"
|
| "github.com/luci/luci-go/server/router"
|
| "github.com/luci/luci-go/server/templates"
|
|
|
| "github.com/luci/luci-go/milo/api/config"
|
| "github.com/luci/luci-go/milo/api/resp"
|
| + "github.com/luci/luci-go/milo/buildsource"
|
| "github.com/luci/luci-go/milo/common"
|
| "github.com/luci/luci-go/milo/common/model"
|
| + "github.com/luci/luci-go/milo/git"
|
| )
|
|
|
| -// Returns results of build[commit_index][builder_index]
|
| -func getConsoleBuilds(
|
| - c context.Context, builders []resp.BuilderRef, commits []string) (
|
| - [][]*model.BuildSummary, error) {
|
| -
|
| - panic("Nothing to see here, check back later.")
|
| -}
|
| -
|
| // getConsoleDef finds the console definition as defined by any project.
|
| // If the user is not a reader of the project, this will return a 404.
|
| // TODO(hinoka): If the user is not a reader of any of of the builders returned,
|
| @@ -55,85 +51,127 @@ func getConsoleDef(c context.Context, project, name string) (*config.Console, er
|
| return cs, nil
|
| }
|
|
|
| -func summaryToConsole(bs []*model.BuildSummary) []*resp.ConsoleBuild {
|
| - cb := make([]*resp.ConsoleBuild, 0, len(bs))
|
| - for _, b := range bs {
|
| - cb = append(cb, &resp.ConsoleBuild{
|
| - // TODO(hinoka): This should link to the actual build.
|
| - Link: resp.NewLink(b.BuildKey.String(), "#"),
|
| - Status: b.Summary.Status,
|
| - })
|
| - }
|
| - return cb
|
| -}
|
| -
|
| func console(c context.Context, project, name string) (*resp.Console, error) {
|
| tStart := clock.Now(c)
|
| def, err := getConsoleDef(c, project, name)
|
| if err != nil {
|
| return nil, err
|
| }
|
| - commits, err := getCommits(c, def.RepoURL, def.Branch, 25)
|
| + commitInfo, err := git.GetHistory(c, def.RepoURL, def.Branch, 25)
|
| if err != nil {
|
| return nil, err
|
| }
|
| tGitiles := clock.Now(c)
|
| logging.Debugf(c, "Loading commits took %s.", tGitiles.Sub(tStart))
|
| - commitNames := make([]string, len(commits))
|
| - commitLinks := make([]*resp.Link, len(commits))
|
| - for i, commit := range commits {
|
| - commitNames[i] = commit.Revision.Label
|
| - commitLinks[i] = commit.Revision
|
| - }
|
|
|
| - // HACK(hinoka): This only supports buildbot....
|
| + builderNames := make([]string, len(def.Builders))
|
| builders := make([]resp.BuilderRef, len(def.Builders))
|
| for i, b := range def.Builders {
|
| - builders[i] = resp.BuilderRef{
|
| - b.Name, strings.Split(b.Category, "|"), b.ShortName,
|
| - }
|
| + builderNames[i] = b.Name
|
| + builders[i].Name = b.Name
|
| + builders[i].Category = strings.Split(b.Category, "|")
|
| + builders[i].ShortName = b.ShortName
|
| + }
|
| +
|
| + commitNames := make([]string, len(commitInfo.Commits))
|
| + for i, commit := range commitInfo.Commits {
|
| + commitNames[i] = hex.EncodeToString(commit.Hash)
|
| }
|
| - cb, err := getConsoleBuilds(c, builders, commitNames)
|
| + rows, err := buildsource.GetConsoleRows(c, project, def, commitNames, builderNames)
|
| tConsole := clock.Now(c)
|
| logging.Debugf(c, "Loading the console took a total of %s.", tConsole.Sub(tGitiles))
|
| if err != nil {
|
| return nil, err
|
| }
|
| - ccb := make([]resp.CommitBuild, len(commits))
|
| - for i, commit := range commitLinks {
|
| - // TODO(hinoka): Not like this
|
| - ccb[i].Commit = resp.Commit{Revision: commit}
|
| - ccb[i].Build = summaryToConsole(cb[i])
|
| +
|
| + ccb := make([]resp.CommitBuild, len(commitInfo.Commits))
|
| + for row, commit := range commitInfo.Commits {
|
| + ccb[row].Build = make([]*model.BuildSummary, len(builders))
|
| + ccb[row].Commit = resp.Commit{
|
| + AuthorName: commit.AuthorName,
|
| + AuthorEmail: commit.AuthorEmail,
|
| + CommitTime: google.TimeFromProto(commit.CommitTime),
|
| + Repo: def.RepoURL,
|
| + Branch: def.Branch,
|
| + Description: commit.Msg,
|
| + Revision: resp.NewLink(commitNames[row], def.RepoURL+"/+/"+commitNames[row]),
|
| + }
|
| +
|
| + for col, b := range builders {
|
| + name := buildsource.BuilderID(b.Name)
|
| + if summaries := rows[row].Builds[name]; len(summaries) > 0 {
|
| + ccb[row].Build[col] = summaries[0]
|
| + }
|
| + }
|
| }
|
|
|
| - cs := &resp.Console{
|
| + return &resp.Console{
|
| Name: def.Name,
|
| Commit: ccb,
|
| BuilderRef: builders,
|
| + }, nil
|
| +}
|
| +
|
| +// consoleRenderer is a wrapper around Console to provide additional methods.
|
| +type consoleRenderer struct {
|
| + *resp.Console
|
| +}
|
| +
|
| +// Header generates the console header html.
|
| +func (c consoleRenderer) Header() template.HTML {
|
| + // First, split things into nice rows and find the max depth.
|
| + cat := make([][]string, len(c.BuilderRef))
|
| + depth := 0
|
| + for i, b := range c.BuilderRef {
|
| + cat[i] = b.Category
|
| + if len(cat[i]) > depth {
|
| + depth = len(cat[i])
|
| + }
|
| }
|
|
|
| - return cs, nil
|
| + result := ""
|
| + for row := 0; row < depth; row++ {
|
| + result += "<tr><th></th>"
|
| + // "" is the first node, " " is an empty node.
|
| + current := ""
|
| + colspan := 0
|
| + for _, br := range cat {
|
| + colspan++
|
| + var s string
|
| + if row >= len(br) {
|
| + s = " "
|
| + } else {
|
| + s = br[row]
|
| + }
|
| + if s != current || current == " " {
|
| + if current != "" || current == " " {
|
| + result += fmt.Sprintf(`<th colspan="%d">%s</th>`, colspan, current)
|
| + colspan = 0
|
| + }
|
| + current = s
|
| + }
|
| + }
|
| + if colspan != 0 {
|
| + result += fmt.Sprintf(`<th colspan="%d">%s</th>`, colspan, current)
|
| + }
|
| + result += "</tr>"
|
| + }
|
| +
|
| + // Last row: The actual builder shortnames.
|
| + result += "<tr><th></th>"
|
| + for _, br := range c.BuilderRef {
|
| + result += fmt.Sprintf("<th>%s</th>", br.ShortName)
|
| + }
|
| + result += "</tr>"
|
| + return template.HTML(result)
|
| }
|
|
|
| -func getCommits(c context.Context, repoURL, treeish string, limit int) ([]resp.Commit, error) {
|
| - commits, err := gitiles.Log(c, repoURL, treeish, limit)
|
| +func (c consoleRenderer) BuilderLink(bs *model.BuildSummary) (*resp.Link, error) {
|
| + _, _, builderName, err := buildsource.BuilderID(bs.BuilderID).Split()
|
| if err != nil {
|
| return nil, err
|
| }
|
| - result := make([]resp.Commit, len(commits))
|
| - for i, log := range commits {
|
| - result[i] = resp.Commit{
|
| - AuthorName: log.Author.Name,
|
| - AuthorEmail: log.Author.Email,
|
| - Repo: repoURL,
|
| - Revision: resp.NewLink(log.Commit, repoURL+"/+/"+log.Commit),
|
| - Description: log.Message,
|
| - Title: strings.SplitN(log.Message, "\n", 2)[0],
|
| - // TODO(hinoka): Fill in the rest of resp.Commit and add those details
|
| - // in the html.
|
| - }
|
| - }
|
| - return result, nil
|
| + return resp.NewLink(builderName, "/"+bs.BuilderID), nil
|
| }
|
|
|
| // ConsoleHandler renders the console page.
|
| @@ -152,7 +190,7 @@ func ConsoleHandler(c *router.Context) {
|
| }
|
|
|
| templates.MustRender(c.Context, c.Writer, "pages/console.html", templates.Args{
|
| - "Console": result,
|
| + "Console": consoleRenderer{result},
|
| })
|
| }
|
|
|
|
|