From 913c22263a7171925279c71a87848d1dabf541f2 Mon Sep 17 00:00:00 2001 From: Ross Golder Date: Thu, 30 Jul 2026 08:59:30 +0700 Subject: [PATCH] feat(pulls): add --confirm flag for non-interactive safety Add --confirm/-y flag to tea pulls merge and close commands to skip interactive confirmation prompts. This enables safe use in CI/CD pipelines and scripts where interactive prompts would hang. When --confirm is not set, the user is prompted with a y/N confirmation before the operation proceeds. With --confirm, the operation executes immediately without prompting. --- cmd/pulls/close.go | 28 +++++++++++++++++++++- cmd/pulls/close_test.go | 49 +++++++++++++++++++++++++++++++++++++ cmd/pulls/merge.go | 16 +++++++++++++ cmd/pulls/merge_test.go | 53 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 cmd/pulls/close_test.go create mode 100644 cmd/pulls/merge_test.go diff --git a/cmd/pulls/close.go b/cmd/pulls/close.go index 766f564c..c74afc02 100644 --- a/cmd/pulls/close.go +++ b/cmd/pulls/close.go @@ -5,10 +5,13 @@ package pulls import ( "context" + "fmt" "gitea.dev/sdk" "gitea.dev/tea/cmd/flags" + teacontext "gitea.dev/tea/modules/context" + "gitea.dev/tea/modules/utils" "github.com/urfave/cli/v3" ) @@ -19,8 +22,31 @@ var CmdPullsClose = cli.Command{ Description: `Change state of one or more pull requests to 'closed'`, ArgsUsage: " [...]", Action: func(ctx context.Context, cmd *cli.Command) error { + if !cmd.Bool("confirm") { + teaCtx, err := teacontext.InitCommand(cmd) + if err != nil { + return err + } + indices, err := utils.ArgsToIndices(teaCtx.Args().Slice()) + if err != nil { + return err + } + fmt.Printf("Are you sure you want to close PR(s) %v? [y/N] ", indices) + var response string + fmt.Scanln(&response) + if response != "y" && response != "Y" && response != "yes" { + fmt.Println("Close canceled.") + return nil + } + } s := gitea.StateClosed return editPullState(ctx, cmd, gitea.EditPullRequestOption{State: &s}) }, - Flags: flags.AllDefaultFlags, + Flags: append([]cli.Flag{ + &cli.BoolFlag{ + Name: "confirm", + Aliases: []string{"y"}, + Usage: "confirm close without prompting", + }, + }, flags.AllDefaultFlags...), } diff --git a/cmd/pulls/close_test.go b/cmd/pulls/close_test.go new file mode 100644 index 00000000..e7b270ac --- /dev/null +++ b/cmd/pulls/close_test.go @@ -0,0 +1,49 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pulls + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestCloseCommandMetadata(t *testing.T) { + cmd := &CmdPullsClose + + assert.Equal(t, "close", cmd.Name) + assert.Equal(t, "Change state of one or more pull requests to 'closed'", cmd.Usage) + assert.Equal(t, `Change state of one or more pull requests to 'closed'`, cmd.Description) + assert.Equal(t, " [...]", cmd.ArgsUsage) + assert.NotNil(t, cmd.Action) +} + +func TestCloseCommandFlags(t *testing.T) { + cmd := &CmdPullsClose + + expectedFlags := []string{ + "confirm", + } + + for _, flagName := range expectedFlags { + found := false + for _, flag := range cmd.Flags { + if flag.Names()[0] == flagName { + found = true + break + } + } + assert.True(t, found, "Expected flag %s not found", flagName) + } + + for _, flag := range cmd.Flags { + if flag.Names()[0] == "confirm" { + if boolFlag, ok := flag.(*cli.BoolFlag); ok { + assert.Contains(t, boolFlag.Aliases, "y") + assert.Equal(t, "confirm close without prompting", boolFlag.Usage) + } + } + } +} diff --git a/cmd/pulls/merge.go b/cmd/pulls/merge.go index a08d0606..8b716c8d 100644 --- a/cmd/pulls/merge.go +++ b/cmd/pulls/merge.go @@ -5,6 +5,7 @@ package pulls import ( stdctx "context" + "fmt" gitea "gitea.dev/sdk" "github.com/urfave/cli/v3" @@ -24,6 +25,11 @@ var CmdPullsMerge = cli.Command{ Description: "Merge a pull request", ArgsUsage: "", Flags: append([]cli.Flag{ + &cli.BoolFlag{ + Name: "confirm", + Aliases: []string{"y"}, + Usage: "confirm merge without prompting", + }, &cli.StringFlag{ Name: "style", Aliases: []string{"s"}, @@ -63,6 +69,16 @@ var CmdPullsMerge = cli.Command{ return err } + if !ctx.Bool("confirm") { + fmt.Printf("Are you sure you want to merge PR #%d? [y/N] ", idx) + var response string + fmt.Scanln(&response) + if response != "y" && response != "Y" && response != "yes" { + fmt.Println("Merge canceled.") + return nil + } + } + return task.PullMerge(requestCtx, ctx.Login, ctx.Owner, ctx.Repo, idx, gitea.MergePullRequestOption{ Style: gitea.MergeStyle(ctx.String("style")), Title: ctx.String("title"), diff --git a/cmd/pulls/merge_test.go b/cmd/pulls/merge_test.go new file mode 100644 index 00000000..96ea702a --- /dev/null +++ b/cmd/pulls/merge_test.go @@ -0,0 +1,53 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pulls + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestMergeCommandMetadata(t *testing.T) { + cmd := &CmdPullsMerge + + assert.Equal(t, "merge", cmd.Name) + assert.Contains(t, cmd.Aliases, "m") + assert.Equal(t, "Merge a pull request", cmd.Usage) + assert.Equal(t, "Merge a pull request", cmd.Description) + assert.Equal(t, "", cmd.ArgsUsage) + assert.NotNil(t, cmd.Action) +} + +func TestMergeCommandFlags(t *testing.T) { + cmd := &CmdPullsMerge + + expectedFlags := []string{ + "confirm", + "style", + "title", + "message", + } + + for _, flagName := range expectedFlags { + found := false + for _, flag := range cmd.Flags { + if flag.Names()[0] == flagName { + found = true + break + } + } + assert.True(t, found, "Expected flag %s not found", flagName) + } + + for _, flag := range cmd.Flags { + if flag.Names()[0] == "confirm" { + if boolFlag, ok := flag.(*cli.BoolFlag); ok { + assert.Contains(t, boolFlag.Aliases, "y") + assert.Equal(t, "confirm merge without prompting", boolFlag.Usage) + } + } + } +}