blob: 248c850d0d4cf05d8b413e1ee120ab36e1f7a9c1 [file] [log] [blame]
package autorollerv2
import (
"fmt"
"go.skia.org/infra/autoroll/go/recent_rolls"
"go.skia.org/infra/autoroll/go/repo_manager"
"go.skia.org/infra/autoroll/go/state_machine"
"go.skia.org/infra/go/autoroll"
"go.skia.org/infra/go/gerrit"
"go.skia.org/infra/go/sklog"
)
type RollImpl interface {
state_machine.RollCLImpl
// Insert the roll into the DB.
InsertIntoDB() error
}
func retrieveGerritIssue(g *gerrit.Gerrit, rm repo_manager.RepoManager, rollIntoAndroid bool, issueNum int64) (*gerrit.ChangeInfo, *autoroll.AutoRollIssue, error) {
info, err := g.GetIssueProperties(issueNum)
if err != nil {
return nil, nil, fmt.Errorf("Failed to get issue properties: %s", err)
}
a, err := autoroll.FromGerritChangeInfo(info, rm.FullChildHash, rollIntoAndroid)
if err != nil {
return nil, nil, fmt.Errorf("Failed to convert issue format: %s", err)
}
tryResults, err := autoroll.GetTryResultsFromGerrit(g, a)
if err != nil {
return nil, nil, fmt.Errorf("Failed to retrieve try results: %s", err)
}
a.TryResults = tryResults
return info, a, nil
}
// gerritRoll is an implementation of RollImpl.
type gerritRoll struct {
ci *gerrit.ChangeInfo
issue *autoroll.AutoRollIssue
g *gerrit.Gerrit
recent *recent_rolls.RecentRolls
rm repo_manager.RepoManager
}
// newGerritRoll obtains a gerritRoll instance from the given Gerrit issue
// number.
func newGerritRoll(g *gerrit.Gerrit, rm repo_manager.RepoManager, recent *recent_rolls.RecentRolls, issueNum int64) (RollImpl, error) {
ci, issue, err := retrieveGerritIssue(g, rm, false, issueNum)
if err != nil {
return nil, err
}
return &gerritRoll{
ci: ci,
issue: issue,
g: g,
recent: recent,
rm: rm,
}, nil
}
// See documentation for RollImpl interface.
func (r *gerritRoll) InsertIntoDB() error {
return r.recent.Add(r.issue)
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) AddComment(msg string) error {
return r.g.AddComment(r.ci, msg)
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) Close(result, msg string) error {
sklog.Infof("Closing issue %d (result %q) with message: %s", r.ci.Issue, result, msg)
if err := r.g.Abandon(r.ci, msg); err != nil {
return err
}
return r.Update()
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) IsFinished() bool {
return r.ci.IsClosed() || !r.issue.CommitQueue
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) IsSuccess() bool {
return r.ci.Status == gerrit.CHANGE_STATUS_MERGED
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) IsDryRunFinished() bool {
// The CQ removes the CQ+1 label when the dry run finishes, regardless
// of success or failure. Since we uploaded with the dry run label set,
// we know the roll is in progress if the label is still set, and done
// otherwise.
return !r.issue.CommitQueueDryRun
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) IsDryRunSuccess() bool {
return r.issue.AllTrybotsSucceeded()
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) RollingTo() string {
return r.issue.RollingTo
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) SwitchToDryRun() error {
if err := r.g.SendToDryRun(r.ci, ""); err != nil {
return err
}
return r.Update()
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) SwitchToNormal() error {
if err := r.g.SendToCQ(r.ci, ""); err != nil {
return err
}
return r.Update()
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritRoll) Update() error {
ci, issue, err := retrieveGerritIssue(r.g, r.rm, false, r.issue.Issue)
if err != nil {
return nil
}
r.ci = ci
r.issue = issue
return r.recent.Update(r.issue)
}
// Special type for Android rolls.
type gerritAndroidRoll struct {
*gerritRoll
}
// newGerritAndroidRoll obtains a gerritAndroidRoll instance from the given Gerrit issue number.
func newGerritAndroidRoll(g *gerrit.Gerrit, rm repo_manager.RepoManager, recent *recent_rolls.RecentRolls, issueNum int64) (RollImpl, error) {
ci, issue, err := retrieveGerritIssue(g, rm, true, issueNum)
if err != nil {
return nil, err
}
return &gerritAndroidRoll{&gerritRoll{
ci: ci,
issue: issue,
g: g,
recent: recent,
rm: rm,
}}, nil
}
// See documentation for RollImpl interface.
func (r *gerritAndroidRoll) InsertIntoDB() error {
return r.recent.Add(r.issue)
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritAndroidRoll) IsDryRunFinished() bool {
if _, ok := r.ci.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL]; ok {
for _, lb := range r.ci.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL].All {
if lb.Value != 0 {
return true
}
}
}
return false
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritAndroidRoll) IsDryRunSuccess() bool {
for _, lb := range r.ci.Labels[gerrit.PRESUBMIT_VERIFIED_LABEL].All {
if lb.Value == -1 {
return false
}
}
return true
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritAndroidRoll) SwitchToDryRun() error {
if err := r.g.SetReview(r.ci, "Mode was changed to dry run", map[string]interface{}{gerrit.AUTOSUBMIT_LABEL: gerrit.AUTOSUBMIT_LABEL_NONE}); err != nil {
return err
}
return r.Update()
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritAndroidRoll) SwitchToNormal() error {
if err := r.g.SetReview(r.ci, "Mode was changed to normal", map[string]interface{}{gerrit.AUTOSUBMIT_LABEL: gerrit.AUTOSUBMIT_LABEL_SUBMIT}); err != nil {
return err
}
return r.Update()
}
// See documentation for state_machine.RollCLImpl interface.
func (r *gerritAndroidRoll) Update() error {
ci, issue, err := retrieveGerritIssue(r.g, r.rm, true, r.issue.Issue)
if err != nil {
return nil
}
r.ci = ci
r.issue = issue
return r.recent.Update(r.issue)
}