Use Runner

This revamps code quite a bit. Series handling has been moved into the
gerrit client, it also handles caching.

The Runner logic itself has been greatly simplified.

The runner logic has been moved into the runner.go, submitqueue.go is
gone.

The "per-run result object" concept has been dropped - we instead just
use annotated logs.

Also, we switched to apex/log
This commit is contained in:
Florian Klink 2019-12-02 10:00:32 +01:00
parent 7bafef7a84
commit 04a24a0c60
14 changed files with 486 additions and 537 deletions

View file

@ -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
}