diff options
author | Florian Klink <flokli@flokli.de> | 2019-11-18T14·40+0100 |
---|---|---|
committer | Florian Klink <flokli@flokli.de> | 2019-11-18T14·40+0100 |
commit | 987b539e335ebc93eba9549ebf8204d612b7a148 (patch) | |
tree | d41adf014d25d05be5d46b6935d168e0b9bbbf16 /gerrit |
initial import
Diffstat (limited to 'gerrit')
-rw-r--r-- | gerrit/changeset.go | 107 | ||||
-rw-r--r-- | gerrit/client.go | 119 |
2 files changed, 226 insertions, 0 deletions
diff --git a/gerrit/changeset.go b/gerrit/changeset.go new file mode 100644 index 000000000000..38a489ec7dd9 --- /dev/null +++ b/gerrit/changeset.go @@ -0,0 +1,107 @@ +package gerrit + +import ( + "bytes" + "fmt" + + goGerrit "github.com/andygrunwald/go-gerrit" + log "github.com/sirupsen/logrus" +) + +// Changeset represents a single changeset +// Relationships between different changesets are described in Series +type Changeset struct { + changeInfo *goGerrit.ChangeInfo + ChangeID string + Number int + IsVerified bool + IsCodeReviewed bool + HashTags []string + CommitID string + ParentCommitIDs []string + OwnerName string + Subject string +} + +// MakeChangeset creates a new Changeset object out of a goGerrit.ChangeInfo object +func MakeChangeset(changeInfo *goGerrit.ChangeInfo) *Changeset { + return &Changeset{ + changeInfo: changeInfo, + ChangeID: changeInfo.ChangeID, + Number: changeInfo.Number, + IsVerified: isVerified(changeInfo), + IsCodeReviewed: isCodeReviewed(changeInfo), + HashTags: changeInfo.Hashtags, + CommitID: changeInfo.CurrentRevision, // yes, this IS the commit ID. + ParentCommitIDs: getParentCommitIDs(changeInfo), + OwnerName: changeInfo.Owner.Name, + Subject: changeInfo.Subject, + } +} + +// 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 + for _, hashTag := range hashTags { + if hashTag == tag { + return true + } + } + return false +} + +func (c *Changeset) String() string { + var b bytes.Buffer + b.WriteString("Changeset") + b.WriteString(fmt.Sprintf("(commitID: %.7s, author: %s, subject: %s)", c.CommitID, c.OwnerName, c.Subject)) + return b.String() +} + +// FilterChangesets filters a list of Changeset by a given filter function +func FilterChangesets(changesets []*Changeset, f func(*Changeset) bool) []*Changeset { + newChangesets := make([]*Changeset, 0) + for _, changeset := range changesets { + if f(changeset) { + newChangesets = append(newChangesets, changeset) + } else { + log.WithField("changeset", changeset.String()).Debug("dropped by filter") + } + } + 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 +} + +// getParentCommitIDs returns the parent commit IDs of the goGerrit.ChangeInfo +// There is usually only one parent commit ID, except for merge commits. +func getParentCommitIDs(changeInfo *goGerrit.ChangeInfo) []string { + // obtain the RevisionInfo object + revisionInfo := changeInfo.Revisions[changeInfo.CurrentRevision] + + // obtain the Commit object + commit := revisionInfo.Commit + + commitIDs := make([]string, len(commit.Parents)) + for i, commit := range commit.Parents { + commitIDs[i] = commit.Commit + } + return commitIDs +} diff --git a/gerrit/client.go b/gerrit/client.go new file mode 100644 index 000000000000..5a13befe5463 --- /dev/null +++ b/gerrit/client.go @@ -0,0 +1,119 @@ +package gerrit + +import ( + goGerrit "github.com/andygrunwald/go-gerrit" + + "net/url" +) + +// passed to gerrit when retrieving changesets +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) + SubmitChangeset(changeset *Changeset) (*Changeset, error) + RebaseChangeset(changeset *Changeset, ref string) (*Changeset, error) + RemoveTag(changeset *Changeset, tag string) (*Changeset, error) +} + +var _ IClient = &Client{} + +// Client provides some ways to interact with a gerrit instance +type Client struct { + client *goGerrit.Client +} + +// NewClient initializes a new gerrit client +func NewClient(URL, username, password string) (*Client, error) { + urlParsed, err := url.Parse(URL) + if err != nil { + return nil, err + } + urlParsed.User = url.UserPassword(username, password) + + goGerritClient, err := goGerrit.NewClient(urlParsed.String(), nil) + if err != nil { + return nil, err + } + return &Client{client: goGerritClient}, nil +} + +// SearchChangesets fetches a list of changesets matching a passed query string +func (gerrit *Client) SearchChangesets(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) + if err != nil { + return nil, err + } + + changesets = make([]*Changeset, 0) + for _, change := range *changes { + changesets = append(changesets, MakeChangeset(&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 +// 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) { + opt := goGerrit.ChangeOptions{} + opt.AdditionalFields = []string{"LABELS", "DETAILED_ACCOUNTS"} + changeInfo, _, err := gerrit.client.Changes.GetChange(changeID, &opt) + if err != nil { + return nil, err + } + return MakeChangeset(changeInfo), nil +} + +// 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{}) + if err != nil { + return nil, err + } + return gerrit.GetChangeset(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{ + Base: ref, + }) + if err != nil { + return changeset, err + } + return gerrit.GetChangeset(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) { + hashTags := changeset.HashTags + newHashTags := []string{} + for _, hashTag := range hashTags { + if hashTag != tag { + newHashTags = append(newHashTags, hashTag) + } + } + // TODO: implement set hashtags api in go-gerrit and use here + // https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#set-hashtags + return changeset, nil +} |