diff --git a/app/api/controller/pullreq/merge.go b/app/api/controller/pullreq/merge.go index 33d44122c..e640a4a66 100644 --- a/app/api/controller/pullreq/merge.go +++ b/app/api/controller/pullreq/merge.go @@ -40,6 +40,7 @@ import ( "github.com/gotidy/ptr" "github.com/rs/zerolog/log" + "golang.org/x/exp/maps" ) type MergeInput struct { @@ -230,6 +231,7 @@ func (c *Controller) Merge( RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests, MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount, MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest, + DefaultReviewerApprovals: ruleOut.DefaultReviewerApprovals, }, nil, nil } @@ -330,24 +332,31 @@ func (c *Controller) Merge( conflicts = pr.MergeConflicts } + for _, approvals := range ruleOut.DefaultReviewerApprovals { + principalInfos, err := c.principalInfoCache.Map(ctx, approvals.PrincipalIDs) + if err != nil { + return nil, nil, fmt.Errorf("failed to fetch principal infos from info cache: %w", err) + } + approvals.PrincipalInfos = maps.Values(principalInfos) + } + // With in.DryRun=true this function never returns types.MergeViolations out := &types.MergeResponse{ BranchDeleted: ruleOut.DeleteSourceBranch, RuleViolations: violations, // values only returned by dry run - DryRun: true, - Mergeable: len(conflicts) == 0, - ConflictFiles: conflicts, - AllowedMethods: ruleOut.AllowedMethods, - RequiresCodeOwnersApproval: ruleOut.RequiresCodeOwnersApproval, - RequiresCodeOwnersApprovalLatest: ruleOut.RequiresCodeOwnersApprovalLatest, - RequiresCommentResolution: ruleOut.RequiresCommentResolution, - RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests, - MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount, - MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest, - MinimumRequiredDefaultReviewerApprovalsCount: ruleOut.MinimumRequiredDefaultReviewerApprovalsCount, - MinimumRequiredDefaultReviewerApprovalsCountLatest: ruleOut.MinimumRequiredDefaultReviewerApprovalsCountLatest, + DryRun: true, + Mergeable: len(conflicts) == 0, + ConflictFiles: conflicts, + AllowedMethods: ruleOut.AllowedMethods, + RequiresCodeOwnersApproval: ruleOut.RequiresCodeOwnersApproval, + RequiresCodeOwnersApprovalLatest: ruleOut.RequiresCodeOwnersApprovalLatest, + RequiresCommentResolution: ruleOut.RequiresCommentResolution, + RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests, + MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount, + MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest, + DefaultReviewerApprovals: ruleOut.DefaultReviewerApprovals, } return out, nil, nil diff --git a/app/services/protection/set.go b/app/services/protection/set.go index 1c17e975d..e951870da 100644 --- a/app/services/protection/set.go +++ b/app/services/protection/set.go @@ -54,24 +54,28 @@ func (s ruleSet) MergeVerify( out.DeleteSourceBranch = out.DeleteSourceBranch || rOut.DeleteSourceBranch out.MinimumRequiredApprovalsCount = maxInt(out.MinimumRequiredApprovalsCount, rOut.MinimumRequiredApprovalsCount) out.MinimumRequiredApprovalsCountLatest = maxInt(out.MinimumRequiredApprovalsCountLatest, rOut.MinimumRequiredApprovalsCountLatest) //nolint:lll - out.MinimumRequiredDefaultReviewerApprovalsCount = - maxInt(out.MinimumRequiredDefaultReviewerApprovalsCount, rOut.MinimumRequiredDefaultReviewerApprovalsCount) - out.MinimumRequiredDefaultReviewerApprovalsCountLatest = - maxInt(out.MinimumRequiredDefaultReviewerApprovalsCountLatest, rOut.MinimumRequiredDefaultReviewerApprovalsCountLatest) //nolint:lll out.DefaultReviewerIDs = append(out.DefaultReviewerIDs, rOut.DefaultReviewerIDs...) out.RequiresCodeOwnersApproval = out.RequiresCodeOwnersApproval || rOut.RequiresCodeOwnersApproval out.RequiresCodeOwnersApprovalLatest = out.RequiresCodeOwnersApprovalLatest || rOut.RequiresCodeOwnersApprovalLatest out.RequiresCommentResolution = out.RequiresCommentResolution || rOut.RequiresCommentResolution out.RequiresNoChangeRequests = out.RequiresNoChangeRequests || rOut.RequiresNoChangeRequests + if len(out.DefaultReviewerIDs) > 0 { + out.DefaultReviewerApprovals = append(out.DefaultReviewerApprovals, &types.DefaultReviewerApprovalsResponse{ + RuleInfo: r.RuleInfo, + MinimumRequiredCount: rOut.MinimumRequiredDefaultReviewerApprovalsCount, + MinimumRequiredCountLatest: rOut.MinimumRequiredDefaultReviewerApprovalsCountLatest, + PrincipalIDs: rOut.DefaultReviewerIDs, + CurrentCount: rOut.DefaultReviewerApprovalsCount, + }) + } + return nil }) if err != nil { return out, nil, fmt.Errorf("failed to process each rule in ruleSet: %w", err) } - out.DefaultReviewerIDs = deduplicateInt64Slice(out.DefaultReviewerIDs) - return out, violations, nil } diff --git a/app/services/protection/set_test.go b/app/services/protection/set_test.go index d68299f3a..fef55d73d 100644 --- a/app/services/protection/set_test.go +++ b/app/services/protection/set_test.go @@ -45,7 +45,6 @@ func TestRuleSet_MergeVerify(t *testing.T) { expOut: MergeVerifyOutput{ DeleteSourceBranch: false, AllowedMethods: enum.MergeMethods, - DefaultReviewerIDs: []int64{}, }, expViol: nil, }, @@ -60,7 +59,6 @@ func TestRuleSet_MergeVerify(t *testing.T) { expOut: MergeVerifyOutput{ DeleteSourceBranch: false, AllowedMethods: enum.MergeMethods, - DefaultReviewerIDs: []int64{}, }, expViol: nil, }, @@ -102,7 +100,6 @@ func TestRuleSet_MergeVerify(t *testing.T) { DeleteSourceBranch: true, MinimumRequiredApprovalsCount: 1, AllowedMethods: []enum.MergeMethod{enum.MergeMethodMerge}, - DefaultReviewerIDs: []int64{}, }, expViol: []types.RuleViolations{ { @@ -171,7 +168,6 @@ func TestRuleSet_MergeVerify(t *testing.T) { expOut: MergeVerifyOutput{ DeleteSourceBranch: false, AllowedMethods: []enum.MergeMethod{enum.MergeMethodRebase}, - DefaultReviewerIDs: []int64{}, }, expViol: []types.RuleViolations{}, }, @@ -279,7 +275,6 @@ func TestRuleSet_MergeVerify(t *testing.T) { RequiresCodeOwnersApprovalLatest: true, RequiresCommentResolution: true, RequiresNoChangeRequests: true, - DefaultReviewerIDs: []int64{}, }, expViol: []types.RuleViolations{ { diff --git a/app/services/protection/verify_pullreq.go b/app/services/protection/verify_pullreq.go index 91aba6a07..1ae1d2169 100644 --- a/app/services/protection/verify_pullreq.go +++ b/app/services/protection/verify_pullreq.go @@ -60,6 +60,8 @@ type ( RequiresCommentResolution bool RequiresNoChangeRequests bool DefaultReviewerIDs []int64 + DefaultReviewerApprovalsCount int + DefaultReviewerApprovals []*types.DefaultReviewerApprovalsResponse } RequiredChecksInput struct { @@ -216,6 +218,7 @@ func (v *DefPullReq) MergeVerify( } } out.DefaultReviewerIDs = v.Reviewers.DefaultReviewerIDs + out.DefaultReviewerApprovalsCount = defaultReviewersCount if v.Approvals.RequireCodeOwners { for _, entry := range in.CodeOwners.EvaluationEntries { diff --git a/types/pullreq.go b/types/pullreq.go index 03d5e183c..93d5fddd1 100644 --- a/types/pullreq.go +++ b/types/pullreq.go @@ -251,6 +251,15 @@ type PullReqFileView struct { Updated int64 `json:"-"` } +type DefaultReviewerApprovalsResponse struct { + RuleInfo RuleInfo `json:"rule_info"` + MinimumRequiredCount int `json:"minimum_required_count"` + MinimumRequiredCountLatest int `json:"minimum_required_count_latest"` + CurrentCount int `json:"current_count"` + PrincipalIDs []int64 `json:"-"` + PrincipalInfos []*PrincipalInfo `json:"principals"` +} + type MergeResponse struct { SHA string `json:"sha,omitempty"` BranchDeleted bool `json:"branch_deleted,omitempty"` @@ -263,10 +272,10 @@ type MergeResponse struct { ConflictFiles []string `json:"conflict_files,omitempty"` AllowedMethods []enum.MergeMethod `json:"allowed_methods,omitempty"` - MinimumRequiredApprovalsCount int `json:"minimum_required_approvals_count,omitempty"` - MinimumRequiredApprovalsCountLatest int `json:"minimum_required_approvals_count_latest,omitempty"` - MinimumRequiredDefaultReviewerApprovalsCount int `json:"minimum_required_default_reviewer_approvals_count,omitempty"` //nolint:lll - MinimumRequiredDefaultReviewerApprovalsCountLatest int `json:"minimum_required_default_reviewer_approvals_count_latest,omitempty"` //nolint:lll + MinimumRequiredApprovalsCount int `json:"minimum_required_approvals_count,omitempty"` + MinimumRequiredApprovalsCountLatest int `json:"minimum_required_approvals_count_latest,omitempty"` + + DefaultReviewerApprovals []*DefaultReviewerApprovalsResponse `json:"default_reviewer_aprovals,omitempty"` RequiresCodeOwnersApproval bool `json:"requires_code_owners_approval,omitempty"` RequiresCodeOwnersApprovalLatest bool `json:"requires_code_owners_approval_latest,omitempty"`