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
1415type Target string
@@ -22,6 +23,53 @@ const (
2223// This is a workaround for the SDK not setting the default value for the allowed_merge_methods field.
2324var defaultPullRequestMergeMethods = []github.PullRequestMergeMethod {github .PullRequestMergeMethodMerge , github .PullRequestMergeMethodRebase , github .PullRequestMergeMethodSquash }
2425
26+ // requiredReviewersSchema returns the schema definition for required_reviewers block.
27+ // This is shared between organization and repository rulesets.
28+ func requiredReviewersSchema () * schema.Schema {
29+ return & schema.Schema {
30+ Type : schema .TypeList ,
31+ Optional : true ,
32+ Description : "Require specific reviewers to approve pull requests targeting matching branches. Note: This feature is in beta and subject to change." ,
33+ Elem : & schema.Resource {
34+ Schema : map [string ]* schema.Schema {
35+ "reviewer" : {
36+ Type : schema .TypeList ,
37+ Required : true ,
38+ MaxItems : 1 ,
39+ Description : "The reviewer that must review matching files." ,
40+ Elem : & schema.Resource {
41+ Schema : map [string ]* schema.Schema {
42+ "id" : {
43+ Type : schema .TypeInt ,
44+ Required : true ,
45+ Description : "The ID of the reviewer that must review." ,
46+ },
47+ "type" : {
48+ Type : schema .TypeString ,
49+ Required : true ,
50+ ValidateDiagFunc : toDiagFunc (validation .StringInSlice ([]string {"Team" }, false ), "type" ),
51+ Description : "The type of reviewer. Currently only `Team` is supported." ,
52+ },
53+ },
54+ },
55+ },
56+ "file_patterns" : {
57+ Type : schema .TypeList ,
58+ Required : true ,
59+ MinItems : 1 ,
60+ Description : "File patterns (fnmatch syntax) that this reviewer must approve." ,
61+ Elem : & schema.Schema {Type : schema .TypeString },
62+ },
63+ "minimum_approvals" : {
64+ Type : schema .TypeInt ,
65+ Required : true ,
66+ Description : "Minimum number of approvals required from this reviewer. Set to 0 to make approval optional." ,
67+ },
68+ },
69+ },
70+ }
71+ }
72+
2573// Helper function to safely convert interface{} to int, handling both int and float64.
2674func toInt (v any ) int {
2775 switch val := v .(type ) {
@@ -65,6 +113,72 @@ func toPullRequestMergeMethods(input any) []github.PullRequestMergeMethod {
65113 return mergeMethods
66114}
67115
116+ // expandRequiredReviewers converts Terraform schema data to go-github RequiredReviewers.
117+ func expandRequiredReviewers (input []any ) []* github.RulesetRequiredReviewer {
118+ if len (input ) == 0 {
119+ return nil
120+ }
121+
122+ reviewers := make ([]* github.RulesetRequiredReviewer , 0 , len (input ))
123+ for _ , item := range input {
124+ reviewerMap := item .(map [string ]any )
125+
126+ var reviewer * github.RulesetReviewer
127+ if rv , ok := reviewerMap ["reviewer" ].([]any ); ok && len (rv ) != 0 {
128+ reviewerData := rv [0 ].(map [string ]any )
129+ reviewerType := github .RulesetReviewerType (reviewerData ["type" ].(string ))
130+ reviewer = & github.RulesetReviewer {
131+ ID : github .Ptr (int64 (reviewerData ["id" ].(int ))),
132+ Type : & reviewerType ,
133+ }
134+ }
135+
136+ filePatterns := make ([]string , 0 )
137+ if fp , ok := reviewerMap ["file_patterns" ].([]any ); ok {
138+ for _ , p := range fp {
139+ filePatterns = append (filePatterns , p .(string ))
140+ }
141+ }
142+
143+ reviewers = append (reviewers , & github.RulesetRequiredReviewer {
144+ MinimumApprovals : github .Ptr (reviewerMap ["minimum_approvals" ].(int )),
145+ FilePatterns : filePatterns ,
146+ Reviewer : reviewer ,
147+ })
148+ }
149+ return reviewers
150+ }
151+
152+ // flattenRequiredReviewers converts go-github RequiredReviewers to Terraform schema data.
153+ func flattenRequiredReviewers (reviewers []* github.RulesetRequiredReviewer ) []map [string ]any {
154+ if len (reviewers ) == 0 {
155+ return nil
156+ }
157+
158+ reviewersList := make ([]map [string ]any , 0 , len (reviewers ))
159+ for _ , rr := range reviewers {
160+ reviewerMap := map [string ]any {
161+ "file_patterns" : rr .FilePatterns ,
162+ "minimum_approvals" : 0 ,
163+ }
164+ if rr .MinimumApprovals != nil {
165+ reviewerMap ["minimum_approvals" ] = * rr .MinimumApprovals
166+ }
167+ if rr .Reviewer != nil {
168+ reviewerData := map [string ]any {}
169+ if rr .Reviewer .ID != nil {
170+ reviewerData ["id" ] = int (* rr .Reviewer .ID )
171+ }
172+ if rr .Reviewer .Type != nil {
173+ reviewerData ["type" ] = string (* rr .Reviewer .Type )
174+ }
175+ reviewerMap ["reviewer" ] = []map [string ]any {reviewerData }
176+ }
177+ reviewersList = append (reviewersList , reviewerMap )
178+ }
179+ return reviewersList
180+ }
181+
68182func resourceGithubRulesetObject (d * schema.ResourceData , org string ) github.RepositoryRuleset {
69183 isOrgLevel := len (org ) > 0
70184
@@ -342,6 +456,12 @@ func expandRules(input []any, org bool) *github.RepositoryRulesetRules {
342456 RequiredReviewThreadResolution : pullRequestMap ["required_review_thread_resolution" ].(bool ),
343457 AllowedMergeMethods : toPullRequestMergeMethods (allowedMergeMethods ),
344458 }
459+
460+ // Add required reviewers if provided
461+ if reqReviewers , ok := pullRequestMap ["required_reviewers" ].([]any ); ok && len (reqReviewers ) != 0 {
462+ params .RequiredReviewers = expandRequiredReviewers (reqReviewers )
463+ }
464+
345465 rulesetRules .PullRequest = params
346466 }
347467
@@ -592,6 +712,7 @@ func flattenRules(rules *github.RepositoryRulesetRules, org bool) []any {
592712 "required_approving_review_count" : rules .PullRequest .RequiredApprovingReviewCount ,
593713 "required_review_thread_resolution" : rules .PullRequest .RequiredReviewThreadResolution ,
594714 "allowed_merge_methods" : rules .PullRequest .AllowedMergeMethods ,
715+ "required_reviewers" : flattenRequiredReviewers (rules .PullRequest .RequiredReviewers ),
595716 })
596717 log .Printf ("[DEBUG] Flattened Pull Request rules slice: %#v" , pullRequestSlice )
597718 rulesMap ["pull_request" ] = pullRequestSlice
0 commit comments