internal/task: make Tag idempotent

Proceed if we're trying to write a tag that already exists and matches
the desired commit. That way, if we fail just after writing a tag, or
need to entirely restart a workflow for whatever reason, we can keep
going.

For golang/go#51797.

Change-Id: Ib2fdb13eea6f8cd6d3dd83cdc6cf7d97f12ca6f5
Reviewed-on: https://go-review.googlesource.com/c/build/+/411195
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Heschi Kreinick 2022-06-08 20:31:51 +00:00 коммит произвёл Gopher Robot
Родитель 27978e86d9
Коммит 179cc6f7e0
2 изменённых файлов: 33 добавлений и 8 удалений

Просмотреть файл

@ -774,15 +774,14 @@ func (c *Client) PublishChangeEdit(ctx context.Context, changeID string) error {
return c.do(ctx, nil, "POST", "/changes/"+changeID+"/edit:publish", wantResStatus(http.StatusNoContent))
}
// ErrProjectNotExist is returned when a project doesn't exist.
// ErrXNotExist is returned when the requested X doesn't exist.
// It is not necessarily returned unless a method is documented as
// returning it.
var ErrProjectNotExist = errors.New("gerrit: requested project does not exist")
// ErrChangeNotExist is returned when a change doesn't exist.
// It is not necessarily returned unless a method is documented as
// returning it.
var ErrChangeNotExist = errors.New("gerrit: requested change does not exist")
var (
ErrProjectNotExist = errors.New("gerrit: requested project does not exist")
ErrChangeNotExist = errors.New("gerrit: requested change does not exist")
ErrTagNotExist = errors.New("gerrit: requested tag does not exist")
)
// GetProjectInfo returns info about a project.
// If the project doesn't exist, the error will be ErrProjectNotExist.
@ -883,6 +882,19 @@ func (c *Client) GetProjectTags(ctx context.Context, name string) (map[string]Ta
return m, nil
}
// GetTag returns a particular tag on project. If the tag doesn't exist, the
// error will be ErrTagNotExist.
//
// See https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#get-tag.
func (c *Client) GetTag(ctx context.Context, project, tag string) (TagInfo, error) {
var res TagInfo
err := c.do(ctx, &res, "GET", fmt.Sprintf("/projects/%s/tags/%s", project, tag))
if he, ok := err.(*HTTPError); ok && he.Res.StatusCode == 404 {
return TagInfo{}, ErrTagNotExist
}
return res, err
}
// TagInput contains information for creating a tag.
// See https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#tag-input
type TagInput struct {

Просмотреть файл

@ -85,7 +85,20 @@ func (c *RealGerritClient) AwaitSubmit(ctx context.Context, changeID string) (st
}
func (c *RealGerritClient) Tag(ctx context.Context, project, tag, commit string) error {
_, err := c.Client.CreateTag(ctx, project, tag, gerrit.TagInput{
info, err := c.Client.GetTag(ctx, project, tag)
if err != nil && err != gerrit.ErrTagNotExist {
return fmt.Errorf("checking if tag already exists: %v", err)
}
if err == nil {
if info.Revision != commit {
return fmt.Errorf("tag %q already exists on revision %q rather than our %q", tag, info.Revision, commit)
} else {
// Nothing to do.
return nil
}
}
_, err = c.Client.CreateTag(ctx, project, tag, gerrit.TagInput{
Revision: commit,
})
return err