@@ -9,11 +9,59 @@ import (
99 "github.com/google/go-github/v81/github"
1010 "github.com/hashicorp/terraform-plugin-log/tflog"
1111 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1213)
1314
1415// This is a workaround for the SDK not setting the default value for the allowed_merge_methods field.
1516var defaultPullRequestMergeMethods = []github.PullRequestMergeMethod {github .PullRequestMergeMethodMerge , github .PullRequestMergeMethodRebase , github .PullRequestMergeMethodSquash }
1617
18+ // requiredReviewersSchema returns the schema definition for required_reviewers block.
19+ // This is shared between organization and repository rulesets.
20+ func requiredReviewersSchema () * schema.Schema {
21+ return & schema.Schema {
22+ Type : schema .TypeList ,
23+ Optional : true ,
24+ Description : "Require specific reviewers to approve pull requests targeting matching branches. Note: This feature is in beta and subject to change." ,
25+ Elem : & schema.Resource {
26+ Schema : map [string ]* schema.Schema {
27+ "reviewer" : {
28+ Type : schema .TypeList ,
29+ Required : true ,
30+ MaxItems : 1 ,
31+ Description : "The reviewer that must review matching files." ,
32+ Elem : & schema.Resource {
33+ Schema : map [string ]* schema.Schema {
34+ "id" : {
35+ Type : schema .TypeInt ,
36+ Required : true ,
37+ Description : "The ID of the reviewer that must review." ,
38+ },
39+ "type" : {
40+ Type : schema .TypeString ,
41+ Required : true ,
42+ ValidateDiagFunc : toDiagFunc (validation .StringInSlice ([]string {"Team" }, false ), "type" ),
43+ Description : "The type of reviewer. Currently only `Team` is supported." ,
44+ },
45+ },
46+ },
47+ },
48+ "file_patterns" : {
49+ Type : schema .TypeList ,
50+ Required : true ,
51+ MinItems : 1 ,
52+ Description : "File patterns (fnmatch syntax) that this reviewer must approve." ,
53+ Elem : & schema.Schema {Type : schema .TypeString },
54+ },
55+ "minimum_approvals" : {
56+ Type : schema .TypeInt ,
57+ Required : true ,
58+ Description : "Minimum number of approvals required from this reviewer. Set to 0 to make approval optional." ,
59+ },
60+ },
61+ },
62+ }
63+ }
64+
1765// Helper function to safely convert interface{} to int, handling both int and float64.
1866func toInt (v any ) int {
1967 switch val := v .(type ) {
@@ -57,6 +105,72 @@ func toPullRequestMergeMethods(input any) []github.PullRequestMergeMethod {
57105 return mergeMethods
58106}
59107
108+ // expandRequiredReviewers converts Terraform schema data to go-github RequiredReviewers.
109+ func expandRequiredReviewers (input []any ) []* github.RulesetRequiredReviewer {
110+ if len (input ) == 0 {
111+ return nil
112+ }
113+
114+ reviewers := make ([]* github.RulesetRequiredReviewer , 0 , len (input ))
115+ for _ , item := range input {
116+ reviewerMap := item .(map [string ]any )
117+
118+ var reviewer * github.RulesetReviewer
119+ if rv , ok := reviewerMap ["reviewer" ].([]any ); ok && len (rv ) != 0 {
120+ reviewerData := rv [0 ].(map [string ]any )
121+ reviewerType := github .RulesetReviewerType (reviewerData ["type" ].(string ))
122+ reviewer = & github.RulesetReviewer {
123+ ID : github .Ptr (int64 (reviewerData ["id" ].(int ))),
124+ Type : & reviewerType ,
125+ }
126+ }
127+
128+ filePatterns := make ([]string , 0 )
129+ if fp , ok := reviewerMap ["file_patterns" ].([]any ); ok {
130+ for _ , p := range fp {
131+ filePatterns = append (filePatterns , p .(string ))
132+ }
133+ }
134+
135+ reviewers = append (reviewers , & github.RulesetRequiredReviewer {
136+ MinimumApprovals : github .Ptr (reviewerMap ["minimum_approvals" ].(int )),
137+ FilePatterns : filePatterns ,
138+ Reviewer : reviewer ,
139+ })
140+ }
141+ return reviewers
142+ }
143+
144+ // flattenRequiredReviewers converts go-github RequiredReviewers to Terraform schema data.
145+ func flattenRequiredReviewers (reviewers []* github.RulesetRequiredReviewer ) []map [string ]any {
146+ if len (reviewers ) == 0 {
147+ return nil
148+ }
149+
150+ reviewersList := make ([]map [string ]any , 0 , len (reviewers ))
151+ for _ , rr := range reviewers {
152+ reviewerMap := map [string ]any {
153+ "file_patterns" : rr .FilePatterns ,
154+ "minimum_approvals" : 0 ,
155+ }
156+ if rr .MinimumApprovals != nil {
157+ reviewerMap ["minimum_approvals" ] = * rr .MinimumApprovals
158+ }
159+ if rr .Reviewer != nil {
160+ reviewerData := map [string ]any {}
161+ if rr .Reviewer .ID != nil {
162+ reviewerData ["id" ] = int (* rr .Reviewer .ID )
163+ }
164+ if rr .Reviewer .Type != nil {
165+ reviewerData ["type" ] = string (* rr .Reviewer .Type )
166+ }
167+ reviewerMap ["reviewer" ] = []map [string ]any {reviewerData }
168+ }
169+ reviewersList = append (reviewersList , reviewerMap )
170+ }
171+ return reviewersList
172+ }
173+
60174func resourceGithubRulesetObject (d * schema.ResourceData , org string ) github.RepositoryRuleset {
61175 isOrgLevel := len (org ) > 0
62176
@@ -334,6 +448,12 @@ func expandRules(input []any, org bool) *github.RepositoryRulesetRules {
334448 RequiredReviewThreadResolution : pullRequestMap ["required_review_thread_resolution" ].(bool ),
335449 AllowedMergeMethods : toPullRequestMergeMethods (allowedMergeMethods ),
336450 }
451+
452+ // Add required reviewers if provided
453+ if reqReviewers , ok := pullRequestMap ["required_reviewers" ].([]any ); ok && len (reqReviewers ) != 0 {
454+ params .RequiredReviewers = expandRequiredReviewers (reqReviewers )
455+ }
456+
337457 rulesetRules .PullRequest = params
338458 }
339459
@@ -574,6 +694,7 @@ func flattenRules(rules *github.RepositoryRulesetRules, org bool) []any {
574694 "required_approving_review_count" : rules .PullRequest .RequiredApprovingReviewCount ,
575695 "required_review_thread_resolution" : rules .PullRequest .RequiredReviewThreadResolution ,
576696 "allowed_merge_methods" : rules .PullRequest .AllowedMergeMethods ,
697+ "required_reviewers" : flattenRequiredReviewers (rules .PullRequest .RequiredReviewers ),
577698 })
578699 log .Printf ("[DEBUG] Flattened Pull Request rules slice: %#v" , pullRequestSlice )
579700 rulesMap ["pull_request" ] = pullRequestSlice
0 commit comments