blob: 671da7a36dfb6c0eadc31e89faa002cb0bccfcb9 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"context"
"fmt"
"go.chromium.org/luci/auth"
"go.chromium.org/luci/common/api/gerrit"
"go.chromium.org/luci/common/errors"
gerritpb "go.chromium.org/luci/common/proto/gerrit"
"go.chromium.org/luci/common/retry/transient"
)
const commitQueueLabel = "Commit-Queue"
// gerritClientWrapper provides utilities for interacting with a Gerrit project.
type gerritClientWrapper struct {
client gerritpb.GerritClient
project string
}
// newGerritClient returns an authenticated gerritClientWrapper.
func newGerritClient(ctx context.Context, host, project string, authOpts auth.Options) (*gerritClientWrapper, error) {
authClient, err := newAuthClient(ctx, authOpts)
if err != nil {
return nil, err
}
client, err := gerrit.NewRESTClient(authClient, host, true)
if err != nil {
return nil, err
}
return &gerritClientWrapper{client: client, project: project}, nil
}
// createChange creates an empty Gerrit change.
func (c *gerritClientWrapper) createChange(ctx context.Context, subject, baseCommit string) (*gerritpb.ChangeInfo, error) {
return c.client.CreateChange(ctx, &gerritpb.CreateChangeRequest{
Project: c.project,
Ref: "refs/heads/master",
Subject: subject,
BaseCommit: baseCommit,
})
}
// editFile edits a single file for a Gerrit change.
func (c *gerritClientWrapper) editFile(ctx context.Context, changeNum int64, filepath, content string) error {
_, err := c.client.ChangeEditFileContent(ctx, &gerritpb.ChangeEditFileContentRequest{
Number: changeNum,
Project: c.project,
FilePath: filepath,
Content: []byte(content),
})
return err
}
// publishEdits publishes all pending edits on a Gerrit change.
func (c *gerritClientWrapper) publishEdits(ctx context.Context, changeNum int64) error {
_, err := c.client.ChangeEditPublish(ctx, &gerritpb.ChangeEditPublishRequest{
Number: changeNum,
Project: c.project,
})
return err
}
// setCQLabel sets the CQ+1/2 label on a Gerrit change.
func (c *gerritClientWrapper) setCQLabel(ctx context.Context, changeNum int64, dryRun bool) error {
cqValue := int32(2)
if dryRun {
cqValue = int32(1)
}
changeRes, err := c.client.GetChange(ctx, &gerritpb.GetChangeRequest{
Number: changeNum,
Options: []gerritpb.QueryOption{gerritpb.QueryOption_CURRENT_REVISION},
})
if err != nil {
return err
}
reviewRes, err := c.client.SetReview(ctx, &gerritpb.SetReviewRequest{
Number: changeNum,
Project: c.project,
RevisionId: changeRes.CurrentRevision,
Labels: map[string]int32{commitQueueLabel: cqValue},
})
if err != nil {
return err
}
setValue, ok := reviewRes.Labels[commitQueueLabel]
if !ok {
return fmt.Errorf("%s label was rejected", commitQueueLabel)
}
if setValue != cqValue {
return fmt.Errorf("%s label is %d; expected %d", commitQueueLabel, setValue, cqValue)
}
return nil
}
// checkCQCompletion checks if a Gerrit change's CQ label is unset.
func (c *gerritClientWrapper) checkCQCompletion(ctx context.Context, changeNum int64) error {
changeInfo, err := c.client.GetChange(ctx, &gerritpb.GetChangeRequest{
Number: changeNum,
Options: []gerritpb.QueryOption{gerritpb.QueryOption_LABELS},
})
if err != nil {
return err
}
labelInfo, ok := changeInfo.Labels[commitQueueLabel]
if !ok {
return fmt.Errorf("%s label was not returned", commitQueueLabel)
}
// The CQ label will be unset eventually, so tag this error as transient.
if labelInfo.Value != 0 {
return errors.New(fmt.Sprintf("%s label is still set", commitQueueLabel), transient.Tag)
}
return nil
}