mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
fix: apply signed-merge checks by merge style (#11403)
Fixes #6438 When a protected branch requires signed commits and no signing key is available, fast-forward-only merges should still be allowed because they do not create a new commit. This patch applies signing checks by merge behaviour/style instead of one global gate: - pass `mergeStyle` through `CheckPullMergeable(...)` in web/API/automerge paths - require signing for commit-creating styles (`merge`, `rebase`, `rebase-merge`, `squash`) - bypass signing precheck only for `fast-forward-only` - align merge UI options with backend behaviour so signing-dependent styles are unavailable when signing cannot happen - add Go unit tests for merge-style signing requirements - add frontend unit coverage for the no-allowed-merge-styles guard Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11403 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: abdo <dev@abdo.wtf> Co-committed-by: abdo <dev@abdo.wtf>
This commit is contained in:
parent
4e6a782a89
commit
e16dc2ebfd
8 changed files with 129 additions and 25 deletions
|
|
@ -215,7 +215,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := pull_service.CheckPullMergeable(ctx, doer, &perm, pr, pull_service.MergeCheckTypeGeneral, false); err != nil {
|
||||
if err := pull_service.CheckPullMergeable(ctx, doer, &perm, pr, pull_service.MergeCheckTypeGeneral, false, scheduledPRM.MergeStyle); err != nil {
|
||||
if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
|
||||
log.Info("%-v was scheduled to automerge by an unauthorized user", pr)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ const (
|
|||
)
|
||||
|
||||
// CheckPullMergeable check if the pull mergeable based on all conditions (branch protection, merge options, ...)
|
||||
func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error {
|
||||
func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool, mergeStyle repo_model.MergeStyle) error {
|
||||
return db.WithTx(stdCtx, func(ctx context.Context) error {
|
||||
if pr.HasMerged {
|
||||
return ErrHasMerged
|
||||
|
|
@ -136,7 +136,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc
|
|||
}
|
||||
}
|
||||
|
||||
if _, err := isSignedIfRequired(ctx, pr, doer); err != nil {
|
||||
if _, err := isSignedIfRequired(ctx, pr, doer, mergeStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ func CheckPullMergeable(stdCtx context.Context, doer *user_model.User, perm *acc
|
|||
}
|
||||
|
||||
// isSignedIfRequired check if merge will be signed if required
|
||||
func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) (bool, error) {
|
||||
func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, mergeStyle repo_model.MergeStyle) (bool, error) {
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
@ -161,11 +161,22 @@ func isSignedIfRequired(ctx context.Context, pr *issues_model.PullRequest, doer
|
|||
return true, nil
|
||||
}
|
||||
|
||||
if !isMergeSigningRequired(mergeStyle) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sign, _, _, err := asymkey_service.SignMerge(ctx, pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName())
|
||||
|
||||
return sign, err
|
||||
}
|
||||
|
||||
func isMergeSigningRequired(mergeStyle repo_model.MergeStyle) bool {
|
||||
// Only fast-forward-only is guaranteed not to create a new commit. Rebase
|
||||
// rewrites commits when the pull request is behind, and it can also amend
|
||||
// the tip commit when a REBASE_TEMPLATE is configured.
|
||||
return mergeStyle != repo_model.MergeStyleFastForwardOnly
|
||||
}
|
||||
|
||||
// checkAndUpdateStatus checks if pull request is possible to leaving checking status,
|
||||
// and set to be either conflict or mergeable.
|
||||
func checkAndUpdateStatus(ctx context.Context, pr *issues_model.PullRequest) bool {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"forgejo.org/models/db"
|
||||
issues_model "forgejo.org/models/issues"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/queue"
|
||||
"forgejo.org/modules/setting"
|
||||
|
|
@ -67,3 +68,53 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
|
|||
prPatchCheckerQueue.ShutdownWait(5 * time.Second)
|
||||
prPatchCheckerQueue = nil
|
||||
}
|
||||
|
||||
func TestIsMergeSigningRequired(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
mergeStyle repo_model.MergeStyle
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "fast-forward never requires signing",
|
||||
mergeStyle: repo_model.MergeStyleFastForwardOnly,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "rebase requires signing even when up to date",
|
||||
mergeStyle: repo_model.MergeStyleRebase,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "rebase-merge requires signing",
|
||||
mergeStyle: repo_model.MergeStyleRebaseMerge,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "squash commits require signing",
|
||||
mergeStyle: repo_model.MergeStyleSquash,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "merge commits require signing",
|
||||
mergeStyle: repo_model.MergeStyleMerge,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "rebase-update style still requires signing",
|
||||
mergeStyle: repo_model.MergeStyleRebaseUpdate,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "empty merge style requires signing",
|
||||
mergeStyle: "",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
assert.Equal(t, testCase.expected, isMergeSigningRequired(testCase.mergeStyle))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue