From c9ce7e447c0168a217b79b4c25d751a7317519ae Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sat, 23 May 2026 20:51:03 +0200 Subject: [PATCH] feat(actions): add before/after to PR synchronize event payload (#37827) ## Summary - Add `before` and `after` fields to `PullRequestPayload` for `synchronize` events - Thread push old/new commit SHAs through the PR synchronize notifier path (regular and Agit flows) - Populate the fields in webhook and Actions event payloads so workflows can access them via `github.event.before` and `github.event.after` Fixes #33395 --------- Co-authored-by: Cursor --- modules/structs/hook.go | 4 +++ modules/structs/hook_test.go | 59 ++++++++++++++++++++++++++++++++++++ services/actions/notifier.go | 4 ++- services/agit/agit.go | 2 +- services/notify/notifier.go | 2 +- services/notify/notify.go | 4 +-- services/notify/null.go | 2 +- services/pull/pull.go | 2 +- services/webhook/notifier.go | 4 ++- 9 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 modules/structs/hook_test.go diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 99c1535155e..176b913515d 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -434,6 +434,10 @@ type ChangesPayload struct { type PullRequestPayload struct { // The action performed on the pull request Action HookIssueAction `json:"action"` + // The SHA of the most recent commit on the PR head branch before the push + Before string `json:"before,omitempty"` + // The SHA of the most recent commit on the PR head branch after the push + After string `json:"after,omitempty"` // The index number of the pull request Index int64 `json:"number"` // Changes made to the pull request (for edit actions) diff --git a/modules/structs/hook_test.go b/modules/structs/hook_test.go new file mode 100644 index 00000000000..a593b4eb98c --- /dev/null +++ b/modules/structs/hook_test.go @@ -0,0 +1,59 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs + +import ( + "testing" + + "code.gitea.io/gitea/modules/json" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPullRequestPayloadSynchronizeBeforeAfter(t *testing.T) { + payload := &PullRequestPayload{ + Action: HookIssueSynchronized, + Before: "1111111111111111111111111111111111111111", + After: "2222222222222222222222222222222222222222", + Index: 12, + } + + data, err := json.Marshal(payload) + require.NoError(t, err) + + assert.JSONEq(t, `{ + "action": "synchronized", + "before": "1111111111111111111111111111111111111111", + "after": "2222222222222222222222222222222222222222", + "number": 12, + "commit_id": "", + "pull_request": null, + "repository": null, + "requested_reviewer": null, + "review": null, + "sender": null + }`, string(data)) +} + +func TestPullRequestPayloadNonSynchronizeOmitsBeforeAfter(t *testing.T) { + payload := &PullRequestPayload{ + Action: HookIssueOpened, + Index: 12, + } + + data, err := json.Marshal(payload) + require.NoError(t, err) + + assert.JSONEq(t, `{ + "action": "opened", + "number": 12, + "commit_id": "", + "pull_request": null, + "repository": null, + "requested_reviewer": null, + "review": null, + "sender": null + }`, string(data)) +} diff --git a/services/actions/notifier.go b/services/actions/notifier.go index 4b2e87afad2..0069da8c6b9 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -687,7 +687,7 @@ func (n *actionsNotifier) AutoMergePullRequest(ctx context.Context, doer *user_m n.MergePullRequest(ctx, doer, pr) } -func (n *actionsNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { +func (n *actionsNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, before, after string) { ctx = withMethod(ctx, "PullRequestSynchronized") if err := pr.LoadIssue(ctx); err != nil { @@ -703,6 +703,8 @@ func (n *actionsNotifier) PullRequestSynchronized(ctx context.Context, doer *use newNotifyInput(pr.Issue.Repo, doer, webhook_module.HookEventPullRequestSync). WithPayload(&api.PullRequestPayload{ Action: api.HookIssueSynchronized, + Before: before, + After: after, Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pr, nil), Repository: convert.ToRepo(ctx, pr.Issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}), diff --git a/services/agit/agit.go b/services/agit/agit.go index c7c46651c02..66f0d1c0dbb 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -286,7 +286,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. } else if commentCreated { notify_service.PullRequestPushCommits(ctx, pusher, pr, comment) } - notify_service.PullRequestSynchronized(ctx, pusher, pr) + notify_service.PullRequestSynchronized(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i]) results = append(results, private.HookProcReceiveRefResult{ OldOID: oldCommitID, diff --git a/services/notify/notifier.go b/services/notify/notifier.go index 875a70e5644..bc990e9cb61 100644 --- a/services/notify/notifier.go +++ b/services/notify/notifier.go @@ -45,7 +45,7 @@ type Notifier interface { NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) - PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) + PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, before, after string) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) PullRequestCodeComment(ctx context.Context, pr *issues_model.PullRequest, comment *issues_model.Comment, mentions []*user_model.User) PullRequestChangeTargetBranch(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, oldBranch string) diff --git a/services/notify/notify.go b/services/notify/notify.go index 152d53b01c9..aee2047e1c9 100644 --- a/services/notify/notify.go +++ b/services/notify/notify.go @@ -120,9 +120,9 @@ func NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions } // PullRequestSynchronized notifies Synchronized pull request -func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { +func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, before, after string) { for _, notifier := range notifiers { - notifier.PullRequestSynchronized(ctx, doer, pr) + notifier.PullRequestSynchronized(ctx, doer, pr, before, after) } } diff --git a/services/notify/null.go b/services/notify/null.go index c3085d7c9eb..f10a132da76 100644 --- a/services/notify/null.go +++ b/services/notify/null.go @@ -63,7 +63,7 @@ func (*NullNotifier) AutoMergePullRequest(ctx context.Context, doer *user_model. } // PullRequestSynchronized places a place holder function -func (*NullNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { +func (*NullNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, before, after string) { } // PullRequestChangeTargetBranch places a place holder function diff --git a/services/pull/pull.go b/services/pull/pull.go index 4f76df31dac..64d9f60dae7 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -479,7 +479,7 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) { } } - notify_service.PullRequestSynchronized(ctx, opts.Doer, pr) + notify_service.PullRequestSynchronized(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID) } } } diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 7627935a321..d53dfaaa6ab 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -818,7 +818,7 @@ func (m *webhookNotifier) CreateRef(ctx context.Context, pusher *user_model.User } } -func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { +func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, before, after string) { if err := pr.LoadIssue(ctx); err != nil { log.Error("LoadIssue: %v", err) return @@ -830,6 +830,8 @@ func (m *webhookNotifier) PullRequestSynchronized(ctx context.Context, doer *use if err := PrepareWebhooks(ctx, EventSource{Repository: pr.Issue.Repo}, webhook_module.HookEventPullRequestSync, &api.PullRequestPayload{ Action: api.HookIssueSynchronized, + Before: before, + After: after, Index: pr.Issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, pr, doer), Repository: convert.ToRepo(ctx, pr.Issue.Repo, access_model.Permission{AccessMode: perm.AccessModeOwner}),