Add --base to agentpr pr edit to retarget a PR
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful

This commit is contained in:
2026-10-17 12:52:36 +11:00
parent 5b6e15b50f
commit 4ef8f458e2
5 changed files with 45 additions and 11 deletions
+14 -4
View File
@@ -5,7 +5,7 @@
//
// agentpr pr create --repo owner/repo --base main --head feature --title T --body B
// agentpr pr comment --repo owner/repo --pr 12 --body "..."
// agentpr pr edit --repo owner/repo --pr 12 --title T --body B
// agentpr pr edit --repo owner/repo --pr 12 --title T --body B --base B
// agentpr issue create --repo owner/repo --title T --body B
// agentpr issue comment --repo owner/repo --issue 12 --body "..."
// agentpr issue edit --repo owner/repo --issue 12 --title T --body B
@@ -169,11 +169,11 @@ func newCommentCmd(numFlag, noun, short string) *cobra.Command {
}
func newPREditCmd() *cobra.Command {
var repo, title, body string
var repo, title, body, base string
var pr int
cmd := &cobra.Command{
Use: "edit",
Short: "Edit a pull request's title and/or body",
Short: "Edit a pull request's title, body and/or base branch",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
owner, name, err := agent.ParseRepo(repo)
@@ -187,6 +187,12 @@ func newPREditCmd() *cobra.Command {
if err != nil {
return err
}
if cmd.Flags().Changed("base") {
if base == "" {
return fmt.Errorf("--base cannot be empty")
}
opts.Base = &base
}
c, err := client()
if err != nil {
return err
@@ -204,6 +210,7 @@ func newPREditCmd() *cobra.Command {
f.IntVar(&pr, "pr", 0, "PR number (required)")
f.StringVar(&title, "title", "", "New PR title (unchanged when omitted)")
f.StringVar(&body, "body", "", "New PR body (unchanged when omitted)")
f.StringVar(&base, "base", "", "New base branch (unchanged when omitted)")
_ = cmd.MarkFlagRequired("repo")
_ = cmd.MarkFlagRequired("pr")
return cmd
@@ -225,7 +232,10 @@ func editOptions(cmd *cobra.Command, title, body string) (agent.EditOptions, err
if cmd.Flags().Changed("body") {
opts.Body = &body
}
if opts.Title == nil && opts.Body == nil {
if opts.Title == nil && opts.Body == nil && !cmd.Flags().Changed("base") {
if cmd.Flags().Lookup("base") != nil {
return opts, fmt.Errorf("at least one of --title, --body or --base is required")
}
return opts, fmt.Errorf("at least one of --title or --body is required")
}
return opts, nil
+22 -3
View File
@@ -20,7 +20,7 @@ func TestExecuteBadRepoErrors(t *testing.T) {
}
}
// `pr edit` with neither --title nor --body has nothing to send; it must fail
// `pr edit` with none of --title, --body or --base has nothing to send; it must fail
// with a usage error before any Vault/Gitea call, so this stays hermetic.
func TestPREditRequiresTitleOrBody(t *testing.T) {
cmd := newRootCmd()
@@ -29,13 +29,32 @@ func TestPREditRequiresTitleOrBody(t *testing.T) {
cmd.SetErr(io.Discard)
err := cmd.Execute()
if err == nil {
t.Fatal("Execute() = nil, want an error when neither --title nor --body is given")
t.Fatal("Execute() = nil, want an error when no edit flag is given")
}
if !strings.Contains(err.Error(), "--title or --body") {
if !strings.Contains(err.Error(), "--title, --body or --base") {
t.Errorf("error = %q, want it to name the missing flags", err)
}
}
// An empty --base names no branch to retarget to; it must fail before any
// Vault/Gitea call rather than be sent.
func TestPREditRejectsEmptyBase(t *testing.T) {
err := execute("pr", "edit", "--repo", "unkin/repo", "--pr", "7", "--base", "")
if err == nil {
t.Fatal("Execute() = nil, want an error for an empty --base")
}
if !strings.Contains(err.Error(), "--base cannot be empty") {
t.Errorf("error = %q, want it to reject the empty base", err)
}
}
// issue edit has no --base, so its usage error must not suggest one.
func TestIssueEditHasNoBaseFlag(t *testing.T) {
if err := execute("issue", "edit", "--repo", "unkin/repo", "--issue", "12", "--base", "x"); err == nil {
t.Fatal("Execute() = nil, want an unknown-flag error for issue edit --base")
}
}
// Gitea silently ignores an empty title, so `pr edit --title ""` would report
// success while changing nothing; it must fail before any Vault/Gitea call.
func TestPREditRejectsEmptyTitle(t *testing.T) {