[cl-util] Add abandon-cl subcommand
Add a subcommand to abandon a CL. This may be used to clean up CLs
after they've outlived their use.
Change-Id: I792f5f5803b19323a8106134c6d1ce1cbf3b1009
Bug: 52176
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/infra/+/389665
Commit-Queue: Anthony Fandrianto <atyfto@google.com>
Reviewed-by: Marc-Antoine Ruel <maruel@google.com>
diff --git a/cmd/cl-util/abandon_cl.go b/cmd/cl-util/abandon_cl.go
new file mode 100644
index 0000000..e67e2eb
--- /dev/null
+++ b/cmd/cl-util/abandon_cl.go
@@ -0,0 +1,69 @@
+// 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"
+ "errors"
+ "fmt"
+
+ "github.com/maruel/subcommands"
+ "go.chromium.org/luci/auth"
+)
+
+func cmdAbandonCL(authOpts auth.Options) *subcommands.Command {
+ return &subcommands.Command{
+ UsageLine: "abandon-cl -host <gerrit-host> -project <gerrit-project> -change-num <change-num>",
+ ShortDesc: "Abandon a CL.",
+ LongDesc: "Abandon a CL.",
+ CommandRun: func() subcommands.CommandRun {
+ c := &abandonCLRun{}
+ c.Init(authOpts)
+ return c
+ },
+ }
+}
+
+type abandonCLRun struct {
+ commonFlags
+ changeNum int64
+}
+
+func (c *abandonCLRun) Init(defaultAuthOpts auth.Options) {
+ c.commonFlags.Init(defaultAuthOpts)
+ c.Flags.Int64Var(&c.changeNum, "change-num", 0, "Gerrit change number.")
+}
+
+func (c *abandonCLRun) Parse(a subcommands.Application, args []string) error {
+ if err := c.commonFlags.Parse(); err != nil {
+ return err
+ }
+ if c.changeNum == 0 {
+ return errors.New("-change-num is required")
+ }
+ return nil
+}
+
+func (c *abandonCLRun) main(a subcommands.Application) error {
+ ctx := context.Background()
+ gerritClient, err := newGerritClient(ctx, c.gerritHost, c.gerritProject, c.parsedAuthOpts)
+ if err != nil {
+ return err
+ }
+ return gerritClient.abandonChange(ctx, c.changeNum)
+}
+
+func (c *abandonCLRun) Run(a subcommands.Application, args []string, env subcommands.Env) int {
+ if err := c.Parse(a, args); err != nil {
+ fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
+ return 1
+ }
+
+ if err := c.main(a); err != nil {
+ fmt.Fprintf(a.GetErr(), "%s: %s\n", a.GetName(), err)
+ return 1
+ }
+ return 0
+}
diff --git a/cmd/cl-util/create_cl.go b/cmd/cl-util/create_cl.go
index 086da14..8459f2b 100644
--- a/cmd/cl-util/create_cl.go
+++ b/cmd/cl-util/create_cl.go
@@ -6,6 +6,7 @@
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -43,10 +44,10 @@
return err
}
if c.subject == "" {
- return fmt.Errorf("-subject is required")
+ return errors.New("-subject is required")
}
if len(c.edits) == 0 {
- return fmt.Errorf("at least one -file-edit is required")
+ return errors.New("at least one -file-edit is required")
}
return nil
}
diff --git a/cmd/cl-util/gerrit.go b/cmd/cl-util/gerrit.go
index 671da7a..5221364 100644
--- a/cmd/cl-util/gerrit.go
+++ b/cmd/cl-util/gerrit.go
@@ -117,3 +117,12 @@
}
return nil
}
+
+// abandonChange abandons a Gerrit change.
+func (c *gerritClientWrapper) abandonChange(ctx context.Context, changeNum int64) error {
+ _, err := c.client.AbandonChange(ctx, &gerritpb.AbandonChangeRequest{
+ Number: changeNum,
+ Project: c.project,
+ })
+ return err
+}
diff --git a/cmd/cl-util/gerrit_test.go b/cmd/cl-util/gerrit_test.go
index 087ef80..3571c3a 100644
--- a/cmd/cl-util/gerrit_test.go
+++ b/cmd/cl-util/gerrit_test.go
@@ -410,3 +410,36 @@
}
}
}
+
+func TestAbandonChange(t *testing.T) {
+ t.Parallel()
+ var tests = []struct {
+ project string
+ changeNum int64
+ }{
+ {
+ project: "test-project",
+ changeNum: int64(123456),
+ },
+ }
+ for _, test := range tests {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ mockGerritClient := gerritpb.NewMockGerritClient(ctrl)
+ req := gerritpb.AbandonChangeRequest{
+ Number: test.changeNum,
+ Project: test.project,
+ }
+ resp := gerritpb.ChangeInfo{}
+ mockGerritClient.EXPECT().AbandonChange(gomock.Any(), &req).Return(&resp, nil)
+ mockGerritClientWrapper := gerritClientWrapper{
+ client: mockGerritClient,
+ project: test.project,
+ }
+ ctx := context.Background()
+ err := mockGerritClientWrapper.abandonChange(ctx, test.changeNum)
+ if err != nil {
+ t.Fatalf("got unexpected err: %v", err)
+ }
+ }
+}
diff --git a/cmd/cl-util/main.go b/cmd/cl-util/main.go
index c0525e9..4dd6ae3 100644
--- a/cmd/cl-util/main.go
+++ b/cmd/cl-util/main.go
@@ -19,7 +19,7 @@
)
// Version must be updated on functional change (behavior, arguments, supported commands).
-const version = "0.0.1"
+const version = "0.0.2"
func getApplication(defaultAuthOpts auth.Options) *subcommands.DefaultApplication {
defaultAuthOpts.Scopes = []string{gitiles.OAuthScope, gerrit.OAuthScope}
@@ -29,6 +29,7 @@
Commands: []*subcommands.Command{
cmdCreateCL(defaultAuthOpts),
cmdTriggerCQ(defaultAuthOpts),
+ cmdAbandonCL(defaultAuthOpts),
authcli.SubcommandInfo(defaultAuthOpts, "whoami", false),
authcli.SubcommandLogin(defaultAuthOpts, "login", false),
authcli.SubcommandLogout(defaultAuthOpts, "logout", false),
diff --git a/cmd/cl-util/trigger_cq.go b/cmd/cl-util/trigger_cq.go
index 3557530..9cfbd0f 100644
--- a/cmd/cl-util/trigger_cq.go
+++ b/cmd/cl-util/trigger_cq.go
@@ -6,6 +6,7 @@
import (
"context"
+ "errors"
"fmt"
"time"
@@ -49,7 +50,7 @@
return err
}
if c.changeNum == 0 {
- return fmt.Errorf("-change-num is required")
+ return errors.New("-change-num is required")
}
return nil
}