From 6c2b2a426221e8b88cb4fc733b26609a0a01c86a Mon Sep 17 00:00:00 2001 From: Sadique Azmi Date: Fri, 7 Nov 2025 11:41:55 +0530 Subject: [PATCH] feat: add required_reviewers support to PullRequestRuleParameters (resolves #3806) Signed-off-by: Sadique Azmi --- github/github-accessors.go | 32 +++++++++++++++++++++++++++ github/github-accessors_test.go | 38 +++++++++++++++++++++++++++++++++ github/rules.go | 36 +++++++++++++++++++++++++------ github/rules_test.go | 29 +++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 7 deletions(-) diff --git a/github/github-accessors.go b/github/github-accessors.go index 6a6ef9c9599..3fa6afbdd1f 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -25494,6 +25494,38 @@ func (r *Rule) GetSeverity() string { return *r.Severity } +// GetMinimumApprovals returns the MinimumApprovals field if it's non-nil, zero value otherwise. +func (r *RulesetRequiredReviewer) GetMinimumApprovals() int { + if r == nil || r.MinimumApprovals == nil { + return 0 + } + return *r.MinimumApprovals +} + +// GetReviewer returns the Reviewer field. +func (r *RulesetRequiredReviewer) GetReviewer() *RulesetReviewer { + if r == nil { + return nil + } + return r.Reviewer +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RulesetReviewer) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetType returns the Type field. +func (r *RulesetReviewer) GetType() *RulesetReviewerType { + if r == nil { + return nil + } + return r.Type +} + // GetIntegrationID returns the IntegrationID field if it's non-nil, zero value otherwise. func (r *RuleStatusCheck) GetIntegrationID() int64 { if r == nil || r.IntegrationID == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 33f25e90ee3..663341b9ca4 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -32869,6 +32869,44 @@ func TestRule_GetSeverity(tt *testing.T) { r.GetSeverity() } +func TestRulesetRequiredReviewer_GetMinimumApprovals(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RulesetRequiredReviewer{MinimumApprovals: &zeroValue} + r.GetMinimumApprovals() + r = &RulesetRequiredReviewer{} + r.GetMinimumApprovals() + r = nil + r.GetMinimumApprovals() +} + +func TestRulesetRequiredReviewer_GetReviewer(tt *testing.T) { + tt.Parallel() + r := &RulesetRequiredReviewer{} + r.GetReviewer() + r = nil + r.GetReviewer() +} + +func TestRulesetReviewer_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RulesetReviewer{ID: &zeroValue} + r.GetID() + r = &RulesetReviewer{} + r.GetID() + r = nil + r.GetID() +} + +func TestRulesetReviewer_GetType(tt *testing.T) { + tt.Parallel() + r := &RulesetReviewer{} + r.GetType() + r = nil + r.GetType() +} + func TestRuleStatusCheck_GetIntegrationID(tt *testing.T) { tt.Parallel() var zeroValue int64 diff --git a/github/rules.go b/github/rules.go index 7b474f00e68..03add8e93f6 100644 --- a/github/rules.go +++ b/github/rules.go @@ -130,6 +130,14 @@ const ( MergeQueueMergeMethodSquash MergeQueueMergeMethod = "SQUASH" ) +// RulesetReviewerType represents the type of reviewer in a ruleset required reviewer. +type RulesetReviewerType string + +// This is the set of GitHub ruleset reviewer types. +const ( + RulesetReviewerTypeTeam RulesetReviewerType = "Team" +) + // PatternRuleOperator models a GitHub pattern rule operator. type PatternRuleOperator string @@ -416,13 +424,27 @@ type RequiredDeploymentsRuleParameters struct { // PullRequestRuleParameters represents the pull_request rule parameters. type PullRequestRuleParameters struct { - AllowedMergeMethods []PullRequestMergeMethod `json:"allowed_merge_methods"` - AutomaticCopilotCodeReviewEnabled *bool `json:"automatic_copilot_code_review_enabled,omitempty"` - DismissStaleReviewsOnPush bool `json:"dismiss_stale_reviews_on_push"` - RequireCodeOwnerReview bool `json:"require_code_owner_review"` - RequireLastPushApproval bool `json:"require_last_push_approval"` - RequiredApprovingReviewCount int `json:"required_approving_review_count"` - RequiredReviewThreadResolution bool `json:"required_review_thread_resolution"` + AllowedMergeMethods []PullRequestMergeMethod `json:"allowed_merge_methods"` + AutomaticCopilotCodeReviewEnabled *bool `json:"automatic_copilot_code_review_enabled,omitempty"` + DismissStaleReviewsOnPush bool `json:"dismiss_stale_reviews_on_push"` + RequireCodeOwnerReview bool `json:"require_code_owner_review"` + RequireLastPushApproval bool `json:"require_last_push_approval"` + RequiredApprovingReviewCount int `json:"required_approving_review_count"` + RequiredReviewers []*RulesetRequiredReviewer `json:"required_reviewers,omitempty"` + RequiredReviewThreadResolution bool `json:"required_review_thread_resolution"` +} + +// RulesetRequiredReviewer represents required reviewer parameters for pull requests in rulesets. +type RulesetRequiredReviewer struct { + MinimumApprovals *int `json:"minimum_approvals,omitempty"` + FilePatterns []string `json:"file_patterns,omitempty"` + Reviewer *RulesetReviewer `json:"reviewer,omitempty"` +} + +// RulesetReviewer represents a reviewer in a ruleset required reviewer rule. +type RulesetReviewer struct { + ID *int64 `json:"id,omitempty"` + Type *RulesetReviewerType `json:"type,omitempty"` } // RequiredStatusChecksRuleParameters represents the required status checks rule parameters. diff --git a/github/rules_test.go b/github/rules_test.go index 34a9f213ecd..562d385ec74 100644 --- a/github/rules_test.go +++ b/github/rules_test.go @@ -736,6 +736,35 @@ func TestRepositoryRule(t *testing.T) { }, `{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"automatic_copilot_code_review_enabled": true,"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}}`, }, + { + "pull_request_with_required_reviewers", + &RepositoryRule{ + Type: RulesetRuleTypePullRequest, + Parameters: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{ + PullRequestMergeMethodMerge, + PullRequestMergeMethodSquash, + PullRequestMergeMethodRebase, + }, + DismissStaleReviewsOnPush: false, + RequireCodeOwnerReview: false, + RequireLastPushApproval: false, + RequiredApprovingReviewCount: 0, + RequiredReviewThreadResolution: false, + RequiredReviewers: []*RulesetRequiredReviewer{ + { + MinimumApprovals: Ptr(1), + FilePatterns: []string{"*"}, + Reviewer: &RulesetReviewer{ + ID: Ptr(int64(123456)), + Type: Ptr(RulesetReviewerTypeTeam), + }, + }, + }, + }, + }, + `{"type":"pull_request","parameters":{"allowed_merge_methods":["merge","squash","rebase"],"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_last_push_approval":false,"required_approving_review_count":0,"required_reviewers":[{"minimum_approvals":1,"file_patterns":["*"],"reviewer":{"id":123456,"type":"Team"}}],"required_review_thread_resolution":false}}`, + }, { "required_status_checks", &RepositoryRule{