Reject an empty pr edit title Gitea would ignore
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline failed

This commit is contained in:
2026-09-19 12:38:12 +10:00
parent 7bc4082cb0
commit 78d83b7a61
4 changed files with 30 additions and 7 deletions
+5
View File
@@ -163,6 +163,11 @@ func newPREditCmd() *cobra.Command {
// leave the title as it is, not blank it.
var opts agent.EditPROptions
if cmd.Flags().Changed("title") {
// Gitea ignores an empty title, so sending one would report
// success while changing nothing.
if title == "" {
return fmt.Errorf("--title cannot be empty: a title can be set but not cleared")
}
opts.Title = &title
}
if cmd.Flags().Changed("body") {
+16
View File
@@ -33,3 +33,19 @@ func TestPREditRequiresTitleOrBody(t *testing.T) {
t.Errorf("error = %q, want it to name the missing flags", err)
}
}
// 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) {
cmd := newRootCmd()
cmd.SetArgs([]string{"pr", "edit", "--repo", "unkin/repo", "--pr", "7", "--title", ""})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
err := cmd.Execute()
if err == nil {
t.Fatal("Execute() = nil, want an error for an empty --title")
}
if !strings.Contains(err.Error(), "--title cannot be empty") {
t.Errorf("error = %q, want it to reject the empty title", err)
}
}
+5 -5
View File
@@ -155,19 +155,19 @@ func TestEditPRSendsOnlySuppliedFields(t *testing.T) {
func TestEditPRAPIError(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/api/v1/repos/unkin/repo/pulls/7", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnprocessableEntity)
_, _ = io.WriteString(w, `{"message":"title cannot be empty"}`)
w.WriteHeader(http.StatusNotFound)
_, _ = io.WriteString(w, `{"message":"pull request does not exist"}`)
})
srv := httptest.NewServer(mux)
defer srv.Close()
title := ""
title := "new title"
c := &GiteaClient{BaseURL: srv.URL, Token: "t", HTTP: srv.Client()}
_, err := c.EditPR("unkin/repo", 7, EditPROptions{Title: &title})
if err == nil {
t.Fatal("expected error on 422")
t.Fatal("expected error on 404")
}
if !strings.Contains(err.Error(), "title cannot be empty") {
if !strings.Contains(err.Error(), "pull request does not exist") {
t.Errorf("error %q should carry the API message", err)
}
}
+4 -2
View File
@@ -215,8 +215,10 @@ func (c *GiteaClient) CreatePR(repoPath string, opts CreatePROptions) (PullReque
}
// EditPROptions are the fields an edit may change. Pointers so an unset field
// is omitted from the payload entirely: Gitea overwrites whatever it is sent,
// so a nil Title leaves the title alone while a pointer to "" clears it.
// is omitted from the payload entirely, leaving that field as it is. The two
// fields are not symmetric: Gitea only applies a title when it is non-empty,
// so Title can be set but never cleared and a "" title is a silent no-op,
// while a pointer to "" Body really does blank the body.
type EditPROptions struct {
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`