fix(pr_merge): send Gitea's merge_title_field; merge_message_title was silently ignored (gitea-mcp#77)
CD / Lint / Test / Vet (pull_request) Successful in 28s
CD / Build & Import (pull_request) Skipped
CD / Deploy via GitOps (pull_request) Skipped

Gitea 1.27's MergePullRequestOption has merge_title_field, not merge_message_title; an unknown field is
dropped without an error, so a custom merge title never took effect. The tool's argument keeps its name.
The new test checks the field name on the wire (the existing fake never did).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SZkhi49hfeSqBp5aZJNXsM
This commit is contained in:
2026-10-10 10:48:55 +02:00
co-authored by Claude Sonnet 5.5
parent e8521c2555
commit eb8e6d5169
2 changed files with 17 additions and 1 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ func (c *Client) GetPullRequestDiff(ctx context.Context, owner, repo string, ind
type MergePRArgs struct {
Do string `json:"Do"`
Title string `json:"merge_message_title,omitempty"`
Title string `json:"merge_title_field,omitempty"`
Body string `json:"merge_message_field,omitempty"`
// HeadCommitID binds the merge to the commit whose CI was checked: if the PR branch moved, Gitea refuses.
HeadCommitID string `json:"head_commit_id,omitempty"`
+16
View File
@@ -166,6 +166,22 @@ func TestPRMergeDefaultsToMergeStyleAndPassesStyleThrough(t *testing.T) {
assert.Equal(t, "squash", payload["Do"])
}
// Gitea 1.27's MergePullRequestOption has merge_title_field and merge_message_field. A field it does not know is
// ignored without an error, so a custom merge title silently did nothing while this tool sent merge_message_title
// (gitea-mcp#77). The tool's own argument keeps its name; the wire field must be Gitea's.
func TestPRMerge_ACustomMergeTitleTravelsAsGiteasMergeTitleField(t *testing.T) {
f := newPRMergeFake(t, prMergeFakeOpts{})
_, err := callMerge(t, f, `{"owner":"owner","repo":"repo","number":7,"issue":42,"merge_message_title":"feat: one line","merge_message_field":"body text"}`)
require.NoError(t, err)
var payload map[string]any
require.NoError(t, json.Unmarshal(f.mergeBody, &payload))
assert.Equal(t, "feat: one line", payload["merge_title_field"])
assert.Equal(t, "body text", payload["merge_message_field"])
assert.NotContains(t, payload, "merge_message_title", "not a Gitea field: it is ignored without an error")
}
// TOCTOU: the gate read CI for ONE head commit. The merge must be bound to that commit, so a push to the PR branch
// after the check makes Gitea refuse the merge instead of merging code nobody checked.
func TestPRMerge_TheMergeIsBoundToTheCommitTheGateChecked(t *testing.T) {