@@ -23,6 +23,7 @@ type PRComplianceResult struct {
2323 PR * github.PullRequest
2424 BaseBranch string
2525 Protection * github.Protection
26+ BranchRules * github.BranchRules
2627 Violations []ComplianceViolation
2728 UserHasBypass bool
2829 UserBypassReason string
@@ -51,36 +52,49 @@ func (c *Client) CheckPRCompliance(ctx context.Context, owner, repo string, prNu
5152
5253 baseBranch := * pr .Base .Ref
5354
54- protection , _ , err := c .client .Repositories .GetBranchProtection (ctx , owner , repo , baseBranch )
55- if err != nil {
56- return & PRComplianceResult {
57- PR : pr ,
58- BaseBranch : baseBranch ,
59- Violations : []ComplianceViolation {},
60- }, nil
61- }
62-
6355 result := & PRComplianceResult {
6456 PR : pr ,
6557 BaseBranch : baseBranch ,
66- Protection : protection ,
6758 Violations : []ComplianceViolation {},
6859 }
6960
70- c .checkReviewRequirements (ctx , owner , repo , pr , protection , result )
71- c .checkStatusRequirements (ctx , owner , repo , pr , protection , result )
61+ // fetch legacy branch protection rules
62+ protection , _ , err := c .client .Repositories .GetBranchProtection (ctx , owner , repo , baseBranch )
63+ if err == nil {
64+ result .Protection = protection
65+ }
66+
67+ // fetch repository rulesets for the branch
68+ branchRules , _ , err := c .client .Repositories .GetRulesForBranch (ctx , owner , repo , baseBranch , nil )
69+ if err == nil {
70+ result .BranchRules = branchRules
71+ }
72+
73+ c .checkReviewRequirements (ctx , owner , repo , pr , result )
74+ c .checkStatusRequirements (ctx , owner , repo , pr , result )
7275 c .checkUserBypassPermission (ctx , owner , repo , pr , result )
7376
7477 return result , nil
7578}
7679
7780// checkReviewRequirements validates that PR had required approving reviews.
78- func (c * Client ) checkReviewRequirements (ctx context.Context , owner , repo string , pr * github.PullRequest , protection * github.Protection , result * PRComplianceResult ) {
79- if protection .RequiredPullRequestReviews == nil {
80- return
81+ // checks both legacy branch protection and repository rulesets.
82+ func (c * Client ) checkReviewRequirements (ctx context.Context , owner , repo string , pr * github.PullRequest , result * PRComplianceResult ) {
83+ requiredApprovals := 0
84+
85+ // check legacy branch protection
86+ if result .Protection != nil && result .Protection .RequiredPullRequestReviews != nil {
87+ requiredApprovals = result .Protection .RequiredPullRequestReviews .RequiredApprovingReviewCount
8188 }
8289
83- requiredApprovals := protection .RequiredPullRequestReviews .RequiredApprovingReviewCount
90+ // check repository rulesets (use the highest requirement)
91+ if result .BranchRules != nil {
92+ for _ , rule := range result .BranchRules .PullRequest {
93+ if rule .Parameters .RequiredApprovingReviewCount > requiredApprovals {
94+ requiredApprovals = rule .Parameters .RequiredApprovingReviewCount
95+ }
96+ }
97+ }
8498
8599 if requiredApprovals == 0 {
86100 return
@@ -107,16 +121,36 @@ func (c *Client) checkReviewRequirements(ctx context.Context, owner, repo string
107121}
108122
109123// checkStatusRequirements validates that required status checks passed.
110- func (c * Client ) checkStatusRequirements (ctx context.Context , owner , repo string , pr * github.PullRequest , protection * github.Protection , result * PRComplianceResult ) {
111- if protection .RequiredStatusChecks == nil || protection .RequiredStatusChecks .Contexts == nil || len (* protection .RequiredStatusChecks .Contexts ) == 0 {
124+ // checks both legacy branch protection and repository rulesets.
125+ func (c * Client ) checkStatusRequirements (ctx context.Context , owner , repo string , pr * github.PullRequest , result * PRComplianceResult ) {
126+ if pr .Head == nil || pr .Head .SHA == nil {
112127 return
113128 }
114129
115- if pr .Head == nil || pr .Head .SHA == nil {
116- return
130+ // collect required checks from both sources
131+ requiredChecks := make (map [string ]bool )
132+
133+ // check legacy branch protection
134+ if result .Protection != nil &&
135+ result .Protection .RequiredStatusChecks != nil &&
136+ result .Protection .RequiredStatusChecks .Contexts != nil {
137+ for _ , check := range * result .Protection .RequiredStatusChecks .Contexts {
138+ requiredChecks [check ] = true
139+ }
117140 }
118141
119- requiredChecks := * protection .RequiredStatusChecks .Contexts
142+ // check repository rulesets
143+ if result .BranchRules != nil {
144+ for _ , rule := range result .BranchRules .RequiredStatusChecks {
145+ for _ , check := range rule .Parameters .RequiredStatusChecks {
146+ requiredChecks [check .Context ] = true
147+ }
148+ }
149+ }
150+
151+ if len (requiredChecks ) == 0 {
152+ return
153+ }
120154
121155 combinedStatus , _ , err := c .client .Repositories .GetCombinedStatus (ctx , owner , repo , * pr .Head .SHA , nil )
122156 if err != nil {
@@ -130,7 +164,7 @@ func (c *Client) checkStatusRequirements(ctx context.Context, owner, repo string
130164 }
131165 }
132166
133- for _ , required := range requiredChecks {
167+ for required := range requiredChecks {
134168 if ! passedChecks [required ] {
135169 result .Violations = append (result .Violations , ComplianceViolation {
136170 Type : "missing_status_check" ,
0 commit comments