From 44872135810b4b8dca6c78e7f95ce519f5492315 Mon Sep 17 00:00:00 2001 From: Norwin Date: Tue, 27 Sep 2022 04:35:59 +0800 Subject: [PATCH] Allow batch operations on multiple entities (#512) commands now accept multiple arguments where it makes sense. #### before ``` NAME: tea issues close - Change state of an issue to 'closed' USAGE: tea issues close [command options] ``` #### after ``` NAME: tea issues close - Change state of one ore more issues to 'closed' USAGE: tea issues close [command options] [...] ``` Co-authored-by: Norwin Reviewed-on: https://gitea.com/gitea/tea/pulls/512 Reviewed-by: 6543 <6543@obermui.de> Reviewed-by: justusbunsi Co-authored-by: Norwin Co-committed-by: Norwin --- cmd/issues/close.go | 25 ++++++++++++------- cmd/issues/reopen.go | 6 ++--- cmd/milestones/close.go | 6 ++--- cmd/milestones/reopen.go | 36 +++++++++++++++++++++------- cmd/open.go | 3 +-- cmd/pulls/close.go | 6 ++--- cmd/pulls/edit.go | 19 ++++++++++----- cmd/pulls/reopen.go | 6 ++--- cmd/releases/delete.go | 35 ++++++++++++++------------- cmd/releases/edit.go | 49 +++++++++++++++++++++----------------- modules/context/context.go | 8 +++++++ modules/utils/parse.go | 12 ++++++++++ 12 files changed, 134 insertions(+), 77 deletions(-) diff --git a/cmd/issues/close.go b/cmd/issues/close.go index 99751b5..136f59e 100644 --- a/cmd/issues/close.go +++ b/cmd/issues/close.go @@ -19,9 +19,9 @@ import ( // CmdIssuesClose represents a sub command of issues to close an issue var CmdIssuesClose = cli.Command{ Name: "close", - Usage: "Change state of an issue to 'closed'", - Description: `Change state of an issue to 'closed'`, - ArgsUsage: "", + Usage: "Change state of one ore more issues to 'closed'", + Description: `Change state of one ore more issues to 'closed'`, + ArgsUsage: " [...]", Action: func(ctx *cli.Context) error { var s = gitea.StateClosed return editIssueState(ctx, gitea.EditIssueOption{State: &s}) @@ -37,16 +37,23 @@ func editIssueState(cmd *cli.Context, opts gitea.EditIssueOption) error { return fmt.Errorf(ctx.Command.ArgsUsage) } - index, err := utils.ArgToIndex(ctx.Args().First()) + indices, err := utils.ArgsToIndices(ctx.Args().Slice()) if err != nil { return err } - issue, _, err := ctx.Login.Client().EditIssue(ctx.Owner, ctx.Repo, index, opts) - if err != nil { - return err - } + client := ctx.Login.Client() + for _, index := range indices { + issue, _, err := client.EditIssue(ctx.Owner, ctx.Repo, index, opts) + if err != nil { + return err + } - print.IssueDetails(issue, nil) + if len(indices) > 1 { + fmt.Println(issue.HTMLURL) + } else { + print.IssueDetails(issue, nil) + } + } return nil } diff --git a/cmd/issues/reopen.go b/cmd/issues/reopen.go index 2d78990..30047bc 100644 --- a/cmd/issues/reopen.go +++ b/cmd/issues/reopen.go @@ -15,9 +15,9 @@ import ( var CmdIssuesReopen = cli.Command{ Name: "reopen", Aliases: []string{"open"}, - Usage: "Change state of an issue to 'open'", - Description: `Change state of an issue to 'open'`, - ArgsUsage: "", + Usage: "Change state of one or more issues to 'open'", + Description: `Change state of one or more issues to 'open'`, + ArgsUsage: " [...]", Action: func(ctx *cli.Context) error { var s = gitea.StateOpen return editIssueState(ctx, gitea.EditIssueOption{State: &s}) diff --git a/cmd/milestones/close.go b/cmd/milestones/close.go index 1ce65ef..b30231f 100644 --- a/cmd/milestones/close.go +++ b/cmd/milestones/close.go @@ -13,9 +13,9 @@ import ( // CmdMilestonesClose represents a sub command of milestones to close an milestone var CmdMilestonesClose = cli.Command{ Name: "close", - Usage: "Change state of an milestone to 'closed'", - Description: `Change state of an milestone to 'closed'`, - ArgsUsage: "", + Usage: "Change state of one or more milestones to 'closed'", + Description: `Change state of one or more milestones to 'closed'`, + ArgsUsage: " [...]", Action: func(ctx *cli.Context) error { if ctx.Bool("force") { return deleteMilestone(ctx) diff --git a/cmd/milestones/reopen.go b/cmd/milestones/reopen.go index fd1d398..3e6e73b 100644 --- a/cmd/milestones/reopen.go +++ b/cmd/milestones/reopen.go @@ -5,8 +5,11 @@ package milestones import ( + "fmt" + "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" "github.com/urfave/cli/v2" @@ -16,9 +19,9 @@ import ( var CmdMilestonesReopen = cli.Command{ Name: "reopen", Aliases: []string{"open"}, - Usage: "Change state of an milestone to 'open'", - Description: `Change state of an milestone to 'open'`, - ArgsUsage: "", + Usage: "Change state of one or more milestones to 'open'", + Description: `Change state of one or more milestones to 'open'`, + ArgsUsage: " [ ...]", Action: func(ctx *cli.Context) error { return editMilestoneStatus(ctx, false) }, @@ -28,16 +31,31 @@ var CmdMilestonesReopen = cli.Command{ func editMilestoneStatus(cmd *cli.Context, close bool) error { ctx := context.InitCommand(cmd) ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) - client := ctx.Login.Client() + if ctx.Args().Len() == 0 { + return fmt.Errorf(ctx.Command.ArgsUsage) + } state := gitea.StateOpen if close { state = gitea.StateClosed } - _, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ctx.Args().First(), gitea.EditMilestoneOption{ - State: &state, - Title: ctx.Args().First(), - }) - return err + client := ctx.Login.Client() + for _, ms := range ctx.Args().Slice() { + opts := gitea.EditMilestoneOption{ + State: &state, + Title: ms, + } + milestone, _, err := client.EditMilestoneByName(ctx.Owner, ctx.Repo, ms, opts) + if err != nil { + return err + } + + if ctx.Args().Len() > 1 { + fmt.Printf("%s/milestone/%d\n", ctx.GetRemoteRepoHTMLURL(), milestone.ID) + } else { + print.MilestoneDetails(milestone) + } + } + return nil } diff --git a/cmd/open.go b/cmd/open.go index 250c8a8..4cb6710 100644 --- a/cmd/open.go +++ b/cmd/open.go @@ -74,6 +74,5 @@ func runOpen(cmd *cli.Context) error { suffix = number } - u := path.Join(ctx.Login.URL, ctx.Owner, ctx.Repo, suffix) - return open.Run(u) + return open.Run(path.Join(ctx.GetRemoteRepoHTMLURL(), suffix)) } diff --git a/cmd/pulls/close.go b/cmd/pulls/close.go index 9a08c69..647b2b0 100644 --- a/cmd/pulls/close.go +++ b/cmd/pulls/close.go @@ -14,9 +14,9 @@ import ( // CmdPullsClose closes a given open pull request var CmdPullsClose = cli.Command{ Name: "close", - Usage: "Change state of a pull request to 'closed'", - Description: `Change state of a pull request to 'closed'`, - ArgsUsage: "", + Usage: "Change state of one or more pull requests to 'closed'", + Description: `Change state of one or more pull requests to 'closed'`, + ArgsUsage: " [...]", Action: func(ctx *cli.Context) error { var s = gitea.StateClosed return editPullState(ctx, gitea.EditPullRequestOption{State: &s}) diff --git a/cmd/pulls/edit.go b/cmd/pulls/edit.go index 3c0e46e..2343ec0 100644 --- a/cmd/pulls/edit.go +++ b/cmd/pulls/edit.go @@ -23,16 +23,23 @@ func editPullState(cmd *cli.Context, opts gitea.EditPullRequestOption) error { return fmt.Errorf("Please provide a Pull Request index") } - index, err := utils.ArgToIndex(ctx.Args().First()) + indices, err := utils.ArgsToIndices(ctx.Args().Slice()) if err != nil { return err } - pr, _, err := ctx.Login.Client().EditPullRequest(ctx.Owner, ctx.Repo, index, opts) - if err != nil { - return err - } + client := ctx.Login.Client() + for _, index := range indices { + pr, _, err := client.EditPullRequest(ctx.Owner, ctx.Repo, index, opts) + if err != nil { + return err + } - print.PullDetails(pr, nil, nil) + if len(indices) > 1 { + fmt.Println(pr.HTMLURL) + } else { + print.PullDetails(pr, nil, nil) + } + } return nil } diff --git a/cmd/pulls/reopen.go b/cmd/pulls/reopen.go index 8cd4c84..2b29afe 100644 --- a/cmd/pulls/reopen.go +++ b/cmd/pulls/reopen.go @@ -15,9 +15,9 @@ import ( var CmdPullsReopen = cli.Command{ Name: "reopen", Aliases: []string{"open"}, - Usage: "Change state of a pull request to 'open'", - Description: `Change state of a pull request to 'open'`, - ArgsUsage: "", + Usage: "Change state of one or more pull requests to 'open'", + Description: `Change state of one or more pull requests to 'open'`, + ArgsUsage: " [...]", Action: func(ctx *cli.Context) error { var s = gitea.StateOpen return editPullState(ctx, gitea.EditPullRequestOption{State: &s}) diff --git a/cmd/releases/delete.go b/cmd/releases/delete.go index 5d1d224..43a0ea0 100644 --- a/cmd/releases/delete.go +++ b/cmd/releases/delete.go @@ -17,9 +17,9 @@ import ( var CmdReleaseDelete = cli.Command{ Name: "delete", Aliases: []string{"rm"}, - Usage: "Delete a release", - Description: `Delete a release`, - ArgsUsage: "", + Usage: "Delete one or more releases", + Description: `Delete one or more releases`, + ArgsUsage: " [...]", Action: runReleaseDelete, Flags: append([]cli.Flag{ &cli.BoolFlag{ @@ -39,9 +39,8 @@ func runReleaseDelete(cmd *cli.Context) error { ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) client := ctx.Login.Client() - tag := ctx.Args().First() - if len(tag) == 0 { - fmt.Println("Release tag needed to delete") + if !ctx.Args().Present() { + fmt.Println("Release tag needed to edit") return nil } @@ -50,18 +49,20 @@ func runReleaseDelete(cmd *cli.Context) error { return nil } - release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client) - if err != nil { - return err - } - _, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID) - if err != nil { - return err - } + for _, tag := range ctx.Args().Slice() { + release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client) + if err != nil { + return err + } + _, err = client.DeleteRelease(ctx.Owner, ctx.Repo, release.ID) + if err != nil { + return err + } - if ctx.Bool("delete-tag") { - _, err = client.DeleteTag(ctx.Owner, ctx.Repo, tag) - return err + if ctx.Bool("delete-tag") { + _, err = client.DeleteTag(ctx.Owner, ctx.Repo, tag) + return err + } } return nil diff --git a/cmd/releases/edit.go b/cmd/releases/edit.go index 0c7e4f3..ee6b221 100644 --- a/cmd/releases/edit.go +++ b/cmd/releases/edit.go @@ -19,9 +19,9 @@ import ( var CmdReleaseEdit = cli.Command{ Name: "edit", Aliases: []string{"e"}, - Usage: "Edit a release", - Description: `Edit a release`, - ArgsUsage: "", + Usage: "Edit one or more releases", + Description: `Edit one or more releases`, + ArgsUsage: " [...]", Action: runReleaseEdit, Flags: append([]cli.Flag{ &cli.StringFlag{ @@ -62,16 +62,6 @@ func runReleaseEdit(cmd *cli.Context) error { ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) client := ctx.Login.Client() - tag := ctx.Args().First() - if len(tag) == 0 { - fmt.Println("Release tag needed to edit") - return nil - } - - release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client) - if err != nil { - return err - } var isDraft, isPre *bool if ctx.IsSet("draft") { isDraft = gitea.OptionalBool(strings.ToLower(ctx.String("draft"))[:1] == "t") @@ -80,13 +70,28 @@ func runReleaseEdit(cmd *cli.Context) error { isPre = gitea.OptionalBool(strings.ToLower(ctx.String("prerelease"))[:1] == "t") } - _, _, err = client.EditRelease(ctx.Owner, ctx.Repo, release.ID, gitea.EditReleaseOption{ - TagName: ctx.String("tag"), - Target: ctx.String("target"), - Title: ctx.String("title"), - Note: ctx.String("note"), - IsDraft: isDraft, - IsPrerelease: isPre, - }) - return err + if !ctx.Args().Present() { + fmt.Println("Release tag needed to edit") + return nil + } + + for _, tag := range ctx.Args().Slice() { + release, err := getReleaseByTag(ctx.Owner, ctx.Repo, tag, client) + if err != nil { + return err + } + + _, _, err = client.EditRelease(ctx.Owner, ctx.Repo, release.ID, gitea.EditReleaseOption{ + TagName: ctx.String("tag"), + Target: ctx.String("target"), + Title: ctx.String("title"), + Note: ctx.String("note"), + IsDraft: isDraft, + IsPrerelease: isPre, + }) + if err != nil { + return err + } + } + return nil } diff --git a/modules/context/context.go b/modules/context/context.go index 4ec42bf..1af71d8 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -9,6 +9,7 @@ import ( "fmt" "log" "os" + "path" "strings" "code.gitea.io/sdk/gitea" @@ -51,6 +52,13 @@ func (ctx *TeaContext) GetListOptions() gitea.ListOptions { } } +// GetRemoteRepoHTMLURL returns the web-ui url of the remote repo, +// after ensuring a remote repo is present in the context. +func (ctx *TeaContext) GetRemoteRepoHTMLURL() string { + ctx.Ensure(CtxRequirement{RemoteRepo: true}) + return path.Join(ctx.Login.URL, ctx.Owner, ctx.Repo) +} + // Ensure checks if requirements on the context are set, and terminates otherwise. func (ctx *TeaContext) Ensure(req CtxRequirement) { if req.LocalRepo && ctx.LocalRepo == nil { diff --git a/modules/utils/parse.go b/modules/utils/parse.go index 63fd19e..94aa8e9 100644 --- a/modules/utils/parse.go +++ b/modules/utils/parse.go @@ -10,6 +10,18 @@ import ( "strings" ) +// ArgsToIndices take issue/pull index as string and returns int64s +func ArgsToIndices(args []string) ([]int64, error) { + indices := make([]int64, len(args)) + for i, arg := range args { + var err error + if indices[i], err = ArgToIndex(arg); err != nil { + return nil, err + } + } + return indices, nil +} + // ArgToIndex take issue/pull index as string and return int64 func ArgToIndex(arg string) (int64, error) { if strings.HasPrefix(arg, "#") {