feat: [CODE-3256]: Add merge verify rule info related to minimum required approvals count for default reviewers (#3483)

* Merge remote-tracking branch 'origin/main' into dd/merge-verify-rules
* Replace princiapl ids with infos in merge verify response
* Populate DefaultReviewerApprovalsResponse with principal ids and current count
* Add MergeVerifyRulesResponse type
This commit is contained in:
Darko Draskovic 2025-02-26 17:56:59 +00:00 committed by Harness
parent d226aded79
commit cad0fbdf98
5 changed files with 47 additions and 27 deletions

View File

@ -40,6 +40,7 @@ import (
"github.com/gotidy/ptr" "github.com/gotidy/ptr"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"golang.org/x/exp/maps"
) )
type MergeInput struct { type MergeInput struct {
@ -230,6 +231,7 @@ func (c *Controller) Merge(
RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests, RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests,
MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount, MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount,
MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest, MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest,
DefaultReviewerApprovals: ruleOut.DefaultReviewerApprovals,
}, nil, nil }, nil, nil
} }
@ -330,6 +332,14 @@ func (c *Controller) Merge(
conflicts = pr.MergeConflicts 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 // With in.DryRun=true this function never returns types.MergeViolations
out := &types.MergeResponse{ out := &types.MergeResponse{
BranchDeleted: ruleOut.DeleteSourceBranch, BranchDeleted: ruleOut.DeleteSourceBranch,
@ -346,8 +356,7 @@ func (c *Controller) Merge(
RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests, RequiresNoChangeRequests: ruleOut.RequiresNoChangeRequests,
MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount, MinimumRequiredApprovalsCount: ruleOut.MinimumRequiredApprovalsCount,
MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest, MinimumRequiredApprovalsCountLatest: ruleOut.MinimumRequiredApprovalsCountLatest,
MinimumRequiredDefaultReviewerApprovalsCount: ruleOut.MinimumRequiredDefaultReviewerApprovalsCount, DefaultReviewerApprovals: ruleOut.DefaultReviewerApprovals,
MinimumRequiredDefaultReviewerApprovalsCountLatest: ruleOut.MinimumRequiredDefaultReviewerApprovalsCountLatest,
} }
return out, nil, nil return out, nil, nil

View File

@ -54,24 +54,28 @@ func (s ruleSet) MergeVerify(
out.DeleteSourceBranch = out.DeleteSourceBranch || rOut.DeleteSourceBranch out.DeleteSourceBranch = out.DeleteSourceBranch || rOut.DeleteSourceBranch
out.MinimumRequiredApprovalsCount = maxInt(out.MinimumRequiredApprovalsCount, rOut.MinimumRequiredApprovalsCount) out.MinimumRequiredApprovalsCount = maxInt(out.MinimumRequiredApprovalsCount, rOut.MinimumRequiredApprovalsCount)
out.MinimumRequiredApprovalsCountLatest = maxInt(out.MinimumRequiredApprovalsCountLatest, rOut.MinimumRequiredApprovalsCountLatest) //nolint:lll 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.DefaultReviewerIDs = append(out.DefaultReviewerIDs, rOut.DefaultReviewerIDs...)
out.RequiresCodeOwnersApproval = out.RequiresCodeOwnersApproval || rOut.RequiresCodeOwnersApproval out.RequiresCodeOwnersApproval = out.RequiresCodeOwnersApproval || rOut.RequiresCodeOwnersApproval
out.RequiresCodeOwnersApprovalLatest = out.RequiresCodeOwnersApprovalLatest || rOut.RequiresCodeOwnersApprovalLatest out.RequiresCodeOwnersApprovalLatest = out.RequiresCodeOwnersApprovalLatest || rOut.RequiresCodeOwnersApprovalLatest
out.RequiresCommentResolution = out.RequiresCommentResolution || rOut.RequiresCommentResolution out.RequiresCommentResolution = out.RequiresCommentResolution || rOut.RequiresCommentResolution
out.RequiresNoChangeRequests = out.RequiresNoChangeRequests || rOut.RequiresNoChangeRequests 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 return nil
}) })
if err != nil { if err != nil {
return out, nil, fmt.Errorf("failed to process each rule in ruleSet: %w", err) return out, nil, fmt.Errorf("failed to process each rule in ruleSet: %w", err)
} }
out.DefaultReviewerIDs = deduplicateInt64Slice(out.DefaultReviewerIDs)
return out, violations, nil return out, violations, nil
} }

View File

@ -45,7 +45,6 @@ func TestRuleSet_MergeVerify(t *testing.T) {
expOut: MergeVerifyOutput{ expOut: MergeVerifyOutput{
DeleteSourceBranch: false, DeleteSourceBranch: false,
AllowedMethods: enum.MergeMethods, AllowedMethods: enum.MergeMethods,
DefaultReviewerIDs: []int64{},
}, },
expViol: nil, expViol: nil,
}, },
@ -60,7 +59,6 @@ func TestRuleSet_MergeVerify(t *testing.T) {
expOut: MergeVerifyOutput{ expOut: MergeVerifyOutput{
DeleteSourceBranch: false, DeleteSourceBranch: false,
AllowedMethods: enum.MergeMethods, AllowedMethods: enum.MergeMethods,
DefaultReviewerIDs: []int64{},
}, },
expViol: nil, expViol: nil,
}, },
@ -102,7 +100,6 @@ func TestRuleSet_MergeVerify(t *testing.T) {
DeleteSourceBranch: true, DeleteSourceBranch: true,
MinimumRequiredApprovalsCount: 1, MinimumRequiredApprovalsCount: 1,
AllowedMethods: []enum.MergeMethod{enum.MergeMethodMerge}, AllowedMethods: []enum.MergeMethod{enum.MergeMethodMerge},
DefaultReviewerIDs: []int64{},
}, },
expViol: []types.RuleViolations{ expViol: []types.RuleViolations{
{ {
@ -171,7 +168,6 @@ func TestRuleSet_MergeVerify(t *testing.T) {
expOut: MergeVerifyOutput{ expOut: MergeVerifyOutput{
DeleteSourceBranch: false, DeleteSourceBranch: false,
AllowedMethods: []enum.MergeMethod{enum.MergeMethodRebase}, AllowedMethods: []enum.MergeMethod{enum.MergeMethodRebase},
DefaultReviewerIDs: []int64{},
}, },
expViol: []types.RuleViolations{}, expViol: []types.RuleViolations{},
}, },
@ -279,7 +275,6 @@ func TestRuleSet_MergeVerify(t *testing.T) {
RequiresCodeOwnersApprovalLatest: true, RequiresCodeOwnersApprovalLatest: true,
RequiresCommentResolution: true, RequiresCommentResolution: true,
RequiresNoChangeRequests: true, RequiresNoChangeRequests: true,
DefaultReviewerIDs: []int64{},
}, },
expViol: []types.RuleViolations{ expViol: []types.RuleViolations{
{ {

View File

@ -60,6 +60,8 @@ type (
RequiresCommentResolution bool RequiresCommentResolution bool
RequiresNoChangeRequests bool RequiresNoChangeRequests bool
DefaultReviewerIDs []int64 DefaultReviewerIDs []int64
DefaultReviewerApprovalsCount int
DefaultReviewerApprovals []*types.DefaultReviewerApprovalsResponse
} }
RequiredChecksInput struct { RequiredChecksInput struct {
@ -216,6 +218,7 @@ func (v *DefPullReq) MergeVerify(
} }
} }
out.DefaultReviewerIDs = v.Reviewers.DefaultReviewerIDs out.DefaultReviewerIDs = v.Reviewers.DefaultReviewerIDs
out.DefaultReviewerApprovalsCount = defaultReviewersCount
if v.Approvals.RequireCodeOwners { if v.Approvals.RequireCodeOwners {
for _, entry := range in.CodeOwners.EvaluationEntries { for _, entry := range in.CodeOwners.EvaluationEntries {

View File

@ -251,6 +251,15 @@ type PullReqFileView struct {
Updated int64 `json:"-"` 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 { type MergeResponse struct {
SHA string `json:"sha,omitempty"` SHA string `json:"sha,omitempty"`
BranchDeleted bool `json:"branch_deleted,omitempty"` BranchDeleted bool `json:"branch_deleted,omitempty"`
@ -265,8 +274,8 @@ type MergeResponse struct {
MinimumRequiredApprovalsCount int `json:"minimum_required_approvals_count,omitempty"` MinimumRequiredApprovalsCount int `json:"minimum_required_approvals_count,omitempty"`
MinimumRequiredApprovalsCountLatest int `json:"minimum_required_approvals_count_latest,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 DefaultReviewerApprovals []*DefaultReviewerApprovalsResponse `json:"default_reviewer_aprovals,omitempty"`
RequiresCodeOwnersApproval bool `json:"requires_code_owners_approval,omitempty"` RequiresCodeOwnersApproval bool `json:"requires_code_owners_approval,omitempty"`
RequiresCodeOwnersApprovalLatest bool `json:"requires_code_owners_approval_latest,omitempty"` RequiresCodeOwnersApprovalLatest bool `json:"requires_code_owners_approval_latest,omitempty"`