diff --git a/frontend/frontend.go b/frontend/frontend.go index eef246ea3..abb5a3db4 100644 --- a/frontend/frontend.go +++ b/frontend/frontend.go @@ -13,9 +13,9 @@ import ( "github.com/tweag/gerrit-queue/gerrit" _ "github.com/tweag/gerrit-queue/statik" // register static assets "github.com/tweag/gerrit-queue/submitqueue" -) -//TODO: log last update + "github.com/apex/log/handlers/memory" +) //loadTemplate loads a list of templates, relative to the statikFS root, and a FuncMap, and returns a template object func loadTemplate(templateNames []string, funcMap template.FuncMap) (*template.Template, error) { @@ -47,46 +47,48 @@ func loadTemplate(templateNames []string, funcMap template.FuncMap) (*template.T return tmpl, nil } -// MakeFrontend configures the router and returns a new Frontend struct -func MakeFrontend(runner *submitqueue.Runner) http.Handler { +// MakeFrontend returns a http.Handler +func MakeFrontend(memoryHandler *memory.Handler, gerritClient *gerrit.Client, runner *submitqueue.Runner) http.Handler { router := gin.Default() - router.GET("/submit-queue.json", func(c *gin.Context) { - submitQueue, _, _ := runner.GetState() - c.JSON(http.StatusOK, submitQueue) - }) + projectName := gerritClient.GetProjectName() + branchName := gerritClient.GetBranchName() router.GET("/", func(c *gin.Context) { - submitQueue, currentlyRunning, results := runner.GetState() + var wipSerie *gerrit.Serie = nil + HEAD := "" + currentlyRunning := runner.IsCurrentlyRunning() + + // don't trigger operations requiring a lock + if !currentlyRunning { + wipSerie = runner.GetWIPSerie() + HEAD = gerritClient.GetHEAD() + } funcMap := template.FuncMap{ - "isAutoSubmittable": func(serie *submitqueue.Serie) bool { - return submitQueue.IsAutoSubmittable(serie) - }, "changesetURL": func(changeset *gerrit.Changeset) string { - return submitQueue.GetChangesetURL(changeset) + return gerritClient.GetChangesetURL(changeset) }, } tmpl := template.Must(loadTemplate([]string{ "submit-queue.tmpl.html", - "series.tmpl.html", "serie.tmpl.html", "changeset.tmpl.html", }, funcMap)) tmpl.ExecuteTemplate(c.Writer, "submit-queue.tmpl.html", gin.H{ // Config - "projectName": submitQueue.ProjectName, - "branchName": submitQueue.BranchName, + "projectName": projectName, + "branchName": branchName, // State "currentlyRunning": currentlyRunning, - "series": submitQueue.Series, - "HEAD": submitQueue.HEAD, + "wipSerie": wipSerie, + "HEAD": HEAD, // History - "results": results, + "memory": memoryHandler, }) }) return router diff --git a/gerrit/changeset.go b/gerrit/changeset.go index 38a489ec7..71310a712 100644 --- a/gerrit/changeset.go +++ b/gerrit/changeset.go @@ -5,7 +5,7 @@ import ( "fmt" goGerrit "github.com/andygrunwald/go-gerrit" - log "github.com/sirupsen/logrus" + "github.com/apex/log" ) // Changeset represents a single changeset @@ -14,8 +14,8 @@ type Changeset struct { changeInfo *goGerrit.ChangeInfo ChangeID string Number int - IsVerified bool - IsCodeReviewed bool + Verified int + CodeReviewed int HashTags []string CommitID string ParentCommitIDs []string @@ -29,8 +29,8 @@ func MakeChangeset(changeInfo *goGerrit.ChangeInfo) *Changeset { changeInfo: changeInfo, ChangeID: changeInfo.ChangeID, Number: changeInfo.Number, - IsVerified: isVerified(changeInfo), - IsCodeReviewed: isCodeReviewed(changeInfo), + Verified: labelInfoToInt(changeInfo.Labels["Verified"]), + CodeReviewed: labelInfoToInt(changeInfo.Labels["Code-Review"]), HashTags: changeInfo.Hashtags, CommitID: changeInfo.CurrentRevision, // yes, this IS the commit ID. ParentCommitIDs: getParentCommitIDs(changeInfo), @@ -39,12 +39,6 @@ func MakeChangeset(changeInfo *goGerrit.ChangeInfo) *Changeset { } } -// MakeMockChangeset creates a mock changeset -// func MakeMockChangeset(isVerified, IsCodeReviewed bool, hashTags []string, commitID string, parentCommitIDs []string, ownerName, subject string) *Changeset { -// //TODO impl -// return nil -//} - // HasTag returns true if a Changeset has the given tag. func (c *Changeset) HasTag(tag string) bool { hashTags := c.HashTags @@ -56,6 +50,18 @@ func (c *Changeset) HasTag(tag string) bool { return false } +// IsVerified returns true if the changeset passed CI, +// that's when somebody left the Approved (+1) on the "Verified" label +func (c *Changeset) IsVerified() bool { + return c.Verified == 1 +} + +// IsCodeReviewed returns true if the changeset passed code review, +// that's when somebody left the Recommended (+2) on the "Code-Review" label +func (c *Changeset) IsCodeReviewed() bool { + return c.CodeReviewed == 2 +} + func (c *Changeset) String() string { var b bytes.Buffer b.WriteString("Changeset") @@ -76,18 +82,21 @@ func FilterChangesets(changesets []*Changeset, f func(*Changeset) bool) []*Chang return newChangesets } -// isVerified returns true if the code passed CI, -// that's when somebody left the Approved (+1) on the "Verified" label -func isVerified(changeInfo *goGerrit.ChangeInfo) bool { - labels := changeInfo.Labels - return labels["Verified"].Approved.AccountID != 0 -} - -// isCodeReviewed returns true if the code passed code review, -// that's when somebody left the Recommended (+2) on the "Code-Review" label -func isCodeReviewed(changeInfo *goGerrit.ChangeInfo) bool { - labels := changeInfo.Labels - return labels["Code-Review"].Recommended.AccountID != 0 +// labelInfoToInt converts a goGerrit.LabelInfo to -2…+2 int +func labelInfoToInt(labelInfo goGerrit.LabelInfo) int { + if labelInfo.Recommended.AccountID != 0 { + return 2 + } + if labelInfo.Approved.AccountID != 0 { + return 1 + } + if labelInfo.Disliked.AccountID != 0 { + return -1 + } + if labelInfo.Rejected.AccountID != 0 { + return -2 + } + return 0 } // getParentCommitIDs returns the parent commit IDs of the goGerrit.ChangeInfo diff --git a/gerrit/client.go b/gerrit/client.go index c65b5016c..fd8a24415 100644 --- a/gerrit/client.go +++ b/gerrit/client.go @@ -1,35 +1,52 @@ package gerrit import ( + "fmt" + goGerrit "github.com/andygrunwald/go-gerrit" + "github.com/apex/log" "net/url" ) // passed to gerrit when retrieving changesets -var additionalFields = []string{"LABELS", "CURRENT_REVISION", "CURRENT_COMMIT", "DETAILED_ACCOUNTS"} +var additionalFields = []string{ + "LABELS", + "CURRENT_REVISION", + "CURRENT_COMMIT", + "DETAILED_ACCOUNTS", +} // IClient defines the gerrit.Client interface type IClient interface { - SearchChangesets(queryString string) (changesets []*Changeset, Error error) - GetHEAD(projectName string, branchName string) (string, error) - GetChangeset(changeID string) (*Changeset, error) + Refresh() error + GetHEAD() string + GetBaseURL() string + GetChangesetURL(changeset *Changeset) string SubmitChangeset(changeset *Changeset) (*Changeset, error) RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) RemoveTag(changeset *Changeset, tag string) (*Changeset, error) - GetBaseURL() string + ChangesetIsRebasedOnHEAD(changeset *Changeset) bool + SerieIsRebasedOnHEAD(serie *Serie) bool + FilterSeries(filter func(s *Serie) bool) []*Serie + FindSerie(filter func(s *Serie) bool) *Serie } var _ IClient = &Client{} // Client provides some ways to interact with a gerrit instance type Client struct { - client *goGerrit.Client - baseURL string + client *goGerrit.Client + logger *log.Logger + baseURL string + projectName string + branchName string + series []*Serie + head string } // NewClient initializes a new gerrit client -func NewClient(URL, username, password string) (*Client, error) { +func NewClient(logger *log.Logger, URL, username, password, projectName, branchName string) (*Client, error) { urlParsed, err := url.Parse(URL) if err != nil { return nil, err @@ -43,17 +60,58 @@ func NewClient(URL, username, password string) (*Client, error) { return &Client{ client: goGerritClient, baseURL: URL, + logger: logger, }, nil } -// SearchChangesets fetches a list of changesets matching a passed query string -func (gerrit *Client) SearchChangesets(queryString string) (changesets []*Changeset, Error error) { +// refreshHEAD queries the commit ID of the selected project and branch +func (c *Client) refreshHEAD() (string, error) { + branchInfo, _, err := c.client.Projects.GetBranch(c.projectName, c.branchName) + if err != nil { + return "", err + } + return branchInfo.Revision, nil +} + +// GetHEAD returns the internally stored HEAD +func (c *Client) GetHEAD() string { + return c.head +} + +// Refresh causes the client to refresh internal view of gerrit +func (c *Client) Refresh() error { + c.logger.Debug("refreshing from gerrit") + HEAD, err := c.refreshHEAD() + if err != nil { + return err + } + c.head = HEAD + + var queryString = fmt.Sprintf("status:open project:%s branch:%s", c.projectName, c.branchName) + c.logger.Debugf("fetching changesets: %s", queryString) + changesets, err := c.fetchChangesets(queryString) + if err != nil { + return err + } + + c.logger.Warnf("assembling series…") + series, err := AssembleSeries(changesets, c.logger) + if err != nil { + return err + } + series = SortSeries(series) + c.series = series + return nil +} + +// fetchChangesets fetches a list of changesets matching a passed query string +func (c *Client) fetchChangesets(queryString string) (changesets []*Changeset, Error error) { opt := &goGerrit.QueryChangeOptions{} opt.Query = []string{ queryString, } - opt.AdditionalFields = additionalFields //TODO: check DETAILED_ACCOUNTS is needed - changes, _, err := gerrit.client.Changes.QueryChanges(opt) + opt.AdditionalFields = additionalFields + changes, _, err := c.client.Changes.QueryChanges(opt) if err != nil { return nil, err } @@ -66,22 +124,13 @@ func (gerrit *Client) SearchChangesets(queryString string) (changesets []*Change return changesets, nil } -// GetHEAD returns the commit ID of a selected branch -func (gerrit *Client) GetHEAD(projectName string, branchName string) (string, error) { - branchInfo, _, err := gerrit.client.Projects.GetBranch(projectName, branchName) - if err != nil { - return "", err - } - return branchInfo.Revision, nil -} - -// GetChangeset downloads an existing Changeset from gerrit, by its ID +// fetchChangeset downloads an existing Changeset from gerrit, by its ID // Gerrit's API is a bit sparse, and only returns what you explicitly ask it // This is used to refresh an existing changeset with more data. -func (gerrit *Client) GetChangeset(changeID string) (*Changeset, error) { +func (c *Client) fetchChangeset(changeID string) (*Changeset, error) { opt := goGerrit.ChangeOptions{} opt.AdditionalFields = []string{"LABELS", "DETAILED_ACCOUNTS"} - changeInfo, _, err := gerrit.client.Changes.GetChange(changeID, &opt) + changeInfo, _, err := c.client.Changes.GetChange(changeID, &opt) if err != nil { return nil, err } @@ -89,28 +138,30 @@ func (gerrit *Client) GetChangeset(changeID string) (*Changeset, error) { } // SubmitChangeset submits a given changeset, and returns a changeset afterwards. -func (gerrit *Client) SubmitChangeset(changeset *Changeset) (*Changeset, error) { - changeInfo, _, err := gerrit.client.Changes.SubmitChange(changeset.ChangeID, &goGerrit.SubmitInput{}) +// TODO: update HEAD +func (c *Client) SubmitChangeset(changeset *Changeset) (*Changeset, error) { + changeInfo, _, err := c.client.Changes.SubmitChange(changeset.ChangeID, &goGerrit.SubmitInput{}) if err != nil { return nil, err } - return gerrit.GetChangeset(changeInfo.ChangeID) + return c.fetchChangeset(changeInfo.ChangeID) } // RebaseChangeset rebases a given changeset on top of a given ref -func (gerrit *Client) RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) { - changeInfo, _, err := gerrit.client.Changes.RebaseChange(changeset.ChangeID, &goGerrit.RebaseInput{ +// TODO: update HEAD +func (c *Client) RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) { + changeInfo, _, err := c.client.Changes.RebaseChange(changeset.ChangeID, &goGerrit.RebaseInput{ Base: ref, }) if err != nil { return changeset, err } - return gerrit.GetChangeset(changeInfo.ChangeID) + return c.fetchChangeset(changeInfo.ChangeID) } // RemoveTag removes the submit queue tag from a changeset and updates gerrit // we never add, that's something users should do in the GUI. -func (gerrit *Client) RemoveTag(changeset *Changeset, tag string) (*Changeset, error) { +func (c *Client) RemoveTag(changeset *Changeset, tag string) (*Changeset, error) { hashTags := changeset.HashTags newHashTags := []string{} for _, hashTag := range hashTags { @@ -118,12 +169,66 @@ func (gerrit *Client) RemoveTag(changeset *Changeset, tag string) (*Changeset, e newHashTags = append(newHashTags, hashTag) } } - // TODO: implement set hashtags api in go-gerrit and use here + // TODO: implement setting hashtags api in go-gerrit and use here // https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#set-hashtags return changeset, nil } // GetBaseURL returns the gerrit base URL -func (gerrit *Client) GetBaseURL() string { - return gerrit.baseURL +func (c *Client) GetBaseURL() string { + return c.baseURL +} + +// GetProjectName returns the configured gerrit project name +func (c *Client) GetProjectName() string { + return c.projectName +} + +// GetBranchName returns the configured gerrit branch name +func (c *Client) GetBranchName() string { + return c.branchName +} + +// GetChangesetURL returns the URL to view a given changeset +func (c *Client) GetChangesetURL(changeset *Changeset) string { + return fmt.Sprintf("%s/c/%s/+/%d", c.GetBaseURL(), c.projectName, changeset.Number) +} + +// ChangesetIsRebasedOnHEAD returns true if the changeset is rebased on the current HEAD +func (c *Client) ChangesetIsRebasedOnHEAD(changeset *Changeset) bool { + if len(changeset.ParentCommitIDs) != 1 { + return false + } + return changeset.ParentCommitIDs[0] == c.head +} + +// SerieIsRebasedOnHEAD returns true if the whole series is rebased on the current HEAD +// this is already the case if the first changeset in the series is rebased on the current HEAD +func (c *Client) SerieIsRebasedOnHEAD(serie *Serie) bool { + // an empty serie should not exist + if len(serie.ChangeSets) == 0 { + return false + } + return c.ChangesetIsRebasedOnHEAD(serie.ChangeSets[0]) +} + +// FilterSeries returns a subset of all Series, passing the given filter function +func (c *Client) FilterSeries(filter func(s *Serie) bool) []*Serie { + matchedSeries := []*Serie{} + for _, serie := range c.series { + if filter(serie) { + matchedSeries = append(matchedSeries, serie) + } + } + return matchedSeries +} + +// FindSerie returns the first serie that matches the filter, or nil if none was found +func (c *Client) FindSerie(filter func(s *Serie) bool) *Serie { + for _, serie := range c.series { + if filter(serie) { + return serie + } + } + return nil } diff --git a/submitqueue/serie.go b/gerrit/serie.go similarity index 92% rename from submitqueue/serie.go rename to gerrit/serie.go index d4cd739cd..788cf46f4 100644 --- a/submitqueue/serie.go +++ b/gerrit/serie.go @@ -1,18 +1,16 @@ -package submitqueue +package gerrit import ( "fmt" "strings" - "github.com/tweag/gerrit-queue/gerrit" - - log "github.com/sirupsen/logrus" + "github.com/apex/log" ) // Serie represents a list of successive changesets with an unbroken parent -> child relation, // starting from the parent. type Serie struct { - ChangeSets []*gerrit.Changeset + ChangeSets []*Changeset } // GetParentCommitIDs returns the parent commit IDs @@ -33,9 +31,7 @@ func (s *Serie) GetLeafCommitID() (string, error) { // CheckIntegrity checks that the series contains a properly ordered and connected chain of commits func (s *Serie) CheckIntegrity() error { - logger := log.WithFields(log.Fields{ - "serie": s, - }) + logger := log.WithField("serie", s) // an empty serie is invalid if len(s.ChangeSets) == 0 { return fmt.Errorf("An empty serie is invalid") @@ -71,7 +67,7 @@ func (s *Serie) CheckIntegrity() error { // FilterAllChangesets applies a filter function on all of the changesets in the series. // returns true if it returns true for all changesets, false otherwise -func (s *Serie) FilterAllChangesets(f func(c *gerrit.Changeset) bool) bool { +func (s *Serie) FilterAllChangesets(f func(c *Changeset) bool) bool { for _, changeset := range s.ChangeSets { if f(changeset) == false { return false diff --git a/submitqueue/series.go b/gerrit/series.go similarity index 78% rename from submitqueue/series.go rename to gerrit/series.go index c607e3ec2..06d49805d 100644 --- a/submitqueue/series.go +++ b/gerrit/series.go @@ -1,34 +1,33 @@ -package submitqueue +package gerrit import ( "sort" - "github.com/tweag/gerrit-queue/gerrit" - - "github.com/sirupsen/logrus" + "github.com/apex/log" ) // AssembleSeries consumes a list of `Changeset`, and groups them together to series // +// We initially put every Changeset in its own Serie +// // As we have no control over the order of the passed changesets, -// we maintain two lookup tables, -// mapLeafToSerie, which allows to lookup a serie by its leaf commit id, -// to append to an existing serie -// and mapParentToSeries, which allows to lookup all series having a certain parent commit id, -// to prepend to any of the existing series -// if we can't find anything, we create a new series -func AssembleSeries(changesets []*gerrit.Changeset, log *logrus.Logger) ([]*Serie, error) { +// we maintain a lookup table, mapLeafToSerie, +// which allows to lookup a serie by its leaf commit id +// We concat series in a fixpoint approach +// because both appending and prepending is much more complex. +// Concatenation moves changesets of the later changeset in the previous one +// in a cleanup phase, we remove orphaned series (those without any changesets inside) +// afterwards, we do an integrity check, just to be on the safe side. +func AssembleSeries(changesets []*Changeset, log *log.Logger) ([]*Serie, error) { series := make([]*Serie, 0) mapLeafToSerie := make(map[string]*Serie, 0) for _, changeset := range changesets { - logger := log.WithFields(logrus.Fields{ - "changeset": changeset.String(), - }) + logger := log.WithField("changeset", changeset.String()) logger.Debug("creating initial serie") serie := &Serie{ - ChangeSets: []*gerrit.Changeset{changeset}, + ChangeSets: []*Changeset{changeset}, } series = append(series, serie) mapLeafToSerie[changeset.CommitID] = serie @@ -72,7 +71,7 @@ func AssembleSeries(changesets []*gerrit.Changeset, log *logrus.Logger) ([]*Seri mapLeafToSerie[myLeafCommitID] = otherSerie // orphan our serie - serie.ChangeSets = []*gerrit.Changeset{} + serie.ChangeSets = []*Changeset{} // remove the orphaned serie from the lookup table delete(mapLeafToSerie, myLeafCommitID) @@ -90,9 +89,7 @@ func AssembleSeries(changesets []*gerrit.Changeset, log *logrus.Logger) ([]*Seri // Check integrity, just to be on the safe side. for _, serie := range series { - logger := log.WithFields(logrus.Fields{ - "serie": serie.String(), - }) + logger := log.WithField("serie", serie.String()) logger.Debugf("checking integrity") err := serie.CheckIntegrity() if err != nil { diff --git a/go.mod b/go.mod index c428ff3cb..c03e8c43e 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.12 require ( github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8 + github.com/apex/log v1.1.1 github.com/gin-gonic/gin v1.4.0 github.com/google/go-querystring v1.0.0 // indirect github.com/rakyll/statik v0.1.6 - github.com/sirupsen/logrus v1.4.2 github.com/urfave/cli v1.22.1 ) diff --git a/go.sum b/go.sum index b5f1dd9f9..12ab50bbd 100644 --- a/go.sum +++ b/go.sum @@ -1,55 +1,87 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8 h1:9PvNa6zH6gOW4VVfbAx5rjDLpxunG+RSaXQB+8TEv4w= github.com/andygrunwald/go-gerrit v0.0.0-20190825170856-5959a9bf9ff8/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk= +github.com/apex/log v1.1.1 h1:BwhRZ0qbjYtTob0I+2M+smavV0kOC8XgcnGZcyL9liA= +github.com/apex/log v1.1.1/go.mod h1:Ls949n1HFtXfbDcjiTTFQqkVUrte0puoIBfO3SVgwOA= +github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE= +github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys= +github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= +github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= +github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= +github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4= github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 143202763..1be379904 100644 --- a/main.go +++ b/main.go @@ -14,16 +14,13 @@ import ( "github.com/urfave/cli" - log "github.com/sirupsen/logrus" + "github.com/apex/log" + "github.com/apex/log/handlers/memory" + "github.com/apex/log/handlers/multi" + "github.com/apex/log/handlers/text" ) func main() { - // configure logging - log.SetFormatter(&log.TextFormatter{}) - //log.SetFormatter(&log.JSONFormatter{}) - log.SetOutput(os.Stdout) - log.SetLevel(log.DebugLevel) - var URL, username, password, projectName, branchName, submitQueueTag string var fetchOnly bool @@ -81,25 +78,33 @@ func main() { }, } + memoryLogHandler := memory.New() + l := &log.Logger{ + Handler: multi.New( + text.New(os.Stderr), + memoryLogHandler, + ), + Level: log.DebugLevel, + } + app.Action = func(c *cli.Context) error { - gerritClient, err := gerrit.NewClient(URL, username, password) + gerrit, err := gerrit.NewClient(l, URL, username, password, projectName, branchName) if err != nil { return err } - log.Printf("Successfully connected to gerrit at %s", URL) + log.Infof("Successfully connected to gerrit at %s", URL) - submitQueue := submitqueue.MakeSubmitQueue(gerritClient, projectName, branchName, submitQueueTag) - runner := submitqueue.NewRunner(submitQueue) + runner := submitqueue.NewRunner(l, gerrit, submitQueueTag) - handler := frontend.MakeFrontend(runner) + handler := frontend.MakeFrontend(memoryLogHandler, gerrit, runner) // fetch only on first run - runner.Trigger(true) + runner.Trigger(fetchOnly) // ticker go func() { for { - time.Sleep(time.Minute * 10) + time.Sleep(time.Minute * 5) runner.Trigger(fetchOnly) } }() @@ -111,7 +116,7 @@ func main() { server.ListenAndServe() if err != nil { - log.Fatal(err) + log.Fatalf(err.Error()) } return nil @@ -119,21 +124,9 @@ func main() { err := app.Run(os.Args) if err != nil { - log.Fatal(err) + log.Fatal(err.Error()) } - // mux := http.NewServeMux() - - // options := &gerrit.EventsLogOptions{} - // events, _, _, err := gerritClient.EventsLog.GetEvents(options) - // TODOS: - // - create submit queue user // - handle event log, either by accepting webhooks, or by streaming events? - - //n := negroni.Classic() - //n.UseHandler(mux) - - //fmt.Println("Listening on :3000…") - //http.ListenAndServe(":3000", n) } diff --git a/public/serie.tmpl.html b/public/serie.tmpl.html index 7db666c17..8c653946c 100644 --- a/public/serie.tmpl.html +++ b/public/serie.tmpl.html @@ -1,6 +1,6 @@ {{ define "serie" }}
Owner | -Changeset | -Flags | -
---|
HEAD: | -{{ .HEAD }} |
-
---|---|
Error: | -{{ $result.Error }} | - -
Log: | -
- {{ range $logEntry := $result.LogEntries }}
- {{ $logEntry }} - {{ end }} - |
-
- {{ block "series" $result.Series}}{{ end }} - | -
+ {{ range $entry := .memory.Entries }}
+ {{ $entry }} + {{ end }} + |
+