Skip to content

Commit fbcab61

Browse files
authored
feat: Add support for Copilot code review in repository and organization rulesets (#2965)
* feat: Add support for Copilot code review in repository rulesets This change adds support for GitHub's Copilot code review feature in repository rulesets, allowing automatic code review requests for pull requests. Changes: - Add copilot_code_review rule to repository ruleset schema - Add CopilotCodeReviewRuleParameters and related types to go-github - Implement expand/flatten logic for copilot_code_review rules The copilot_code_review rule supports two configuration options: - review_new_pushes: Automatically review each new push to the PR - review_draft_pull_requests: Automatically review draft PRs Both options default to false, matching GitHub UI behavior. * test: Add unit test for copilot_code_review rule * test: Add copilot_code_review to acceptance test * docs: Add copilot_code_review to repository_ruleset documentation * feat: Add copilot_code_review support to organization ruleset This extends the copilot_code_review feature to github_organization_ruleset resource, matching the functionality already available in github_repository_ruleset. Changes: - Add copilot_code_review schema to organization ruleset resource - Update organization ruleset acceptance test with copilot_code_review - Add documentation for copilot_code_review in organization_ruleset.html.markdown
1 parent dd9e21f commit fbcab61

8 files changed

Lines changed: 149 additions & 0 deletions

github/resource_github_organization_ruleset.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,28 @@ func resourceGithubOrganizationRuleset() *schema.Resource {
241241
},
242242
},
243243
},
244+
"copilot_code_review": {
245+
Type: schema.TypeList,
246+
Optional: true,
247+
MaxItems: 1,
248+
Description: "Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit.",
249+
Elem: &schema.Resource{
250+
Schema: map[string]*schema.Schema{
251+
"review_on_push": {
252+
Type: schema.TypeBool,
253+
Optional: true,
254+
Default: false,
255+
Description: "Copilot automatically reviews each new push to the pull request. Defaults to `false`.",
256+
},
257+
"review_draft_pull_requests": {
258+
Type: schema.TypeBool,
259+
Optional: true,
260+
Default: false,
261+
Description: "Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`.",
262+
},
263+
},
264+
},
265+
},
244266
"required_status_checks": {
245267
Type: schema.TypeList,
246268
MaxItems: 1,

github/resource_github_organization_ruleset_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ resource "github_organization_ruleset" "test" {
103103
require_last_push_approval = true
104104
}
105105
106+
copilot_code_review {
107+
review_on_push = true
108+
review_draft_pull_requests = false
109+
}
110+
106111
required_status_checks {
107112
108113
required_check {
@@ -166,6 +171,8 @@ resource "github_organization_ruleset" "test" {
166171
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.required_code_scanning.0.required_code_scanning_tool.0.alerts_threshold", "errors"),
167172
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.required_code_scanning.0.required_code_scanning_tool.0.security_alerts_threshold", "high_or_higher"),
168173
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.required_code_scanning.0.required_code_scanning_tool.0.tool", "CodeQL"),
174+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.copilot_code_review.0.review_on_push", "true"),
175+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.copilot_code_review.0.review_draft_pull_requests", "false"),
169176
),
170177
},
171178
},

github/resource_github_repository_ruleset.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,28 @@ func resourceGithubRepositoryRuleset() *schema.Resource {
594594
},
595595
},
596596
},
597+
"copilot_code_review": {
598+
Type: schema.TypeList,
599+
Optional: true,
600+
MaxItems: 1,
601+
Description: "Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit.",
602+
Elem: &schema.Resource{
603+
Schema: map[string]*schema.Schema{
604+
"review_on_push": {
605+
Type: schema.TypeBool,
606+
Optional: true,
607+
Default: false,
608+
Description: "Copilot automatically reviews each new push to the pull request. Defaults to `false`.",
609+
},
610+
"review_draft_pull_requests": {
611+
Type: schema.TypeBool,
612+
Optional: true,
613+
Default: false,
614+
Description: "Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`.",
615+
},
616+
},
617+
},
618+
},
597619
},
598620
},
599621
},

github/resource_github_repository_ruleset_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ resource "github_repository_ruleset" "test" {
7373
min_entries_to_merge_wait_minutes = 60
7474
}
7575
76+
copilot_code_review {
77+
review_on_push = true
78+
review_draft_pull_requests = false
79+
}
80+
7681
required_deployments {
7782
required_deployment_environments = [github_repository_environment.example.environment]
7883
}
@@ -128,6 +133,8 @@ resource "github_repository_ruleset" "test" {
128133
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.required_code_scanning.0.required_code_scanning_tool.0.alerts_threshold", "errors"),
129134
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.required_code_scanning.0.required_code_scanning_tool.0.security_alerts_threshold", "high_or_higher"),
130135
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.required_code_scanning.0.required_code_scanning_tool.0.tool", "CodeQL"),
136+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.copilot_code_review.0.review_on_push", "true"),
137+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.copilot_code_review.0.review_draft_pull_requests", "false"),
131138
),
132139
},
133140
},

github/util_rules.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,16 @@ func expandRules(input []any, org bool) *github.RepositoryRulesetRules {
518518
rulesetRules.FileExtensionRestriction = params
519519
}
520520

521+
// Copilot code review rule
522+
if v, ok := rulesMap["copilot_code_review"].([]any); ok && len(v) != 0 {
523+
copilotCodeReviewMap := v[0].(map[string]any)
524+
params := &github.CopilotCodeReviewRuleParameters{
525+
ReviewOnPush: copilotCodeReviewMap["review_on_push"].(bool),
526+
ReviewDraftPullRequests: copilotCodeReviewMap["review_draft_pull_requests"].(bool),
527+
}
528+
rulesetRules.CopilotCodeReview = params
529+
}
530+
521531
return rulesetRules
522532
}
523533

@@ -734,6 +744,16 @@ func flattenRules(rules *github.RepositoryRulesetRules, org bool) []any {
734744
rulesMap["file_extension_restriction"] = fileExtensionRestrictionSlice
735745
}
736746

747+
// Copilot code review rule
748+
if rules.CopilotCodeReview != nil {
749+
copilotCodeReviewSlice := make([]map[string]any, 0)
750+
copilotCodeReviewSlice = append(copilotCodeReviewSlice, map[string]any{
751+
"review_on_push": rules.CopilotCodeReview.ReviewOnPush,
752+
"review_draft_pull_requests": rules.CopilotCodeReview.ReviewDraftPullRequests,
753+
})
754+
rulesMap["copilot_code_review"] = copilotCodeReviewSlice
755+
}
756+
737757
return []any{rulesMap}
738758
}
739759

github/util_rules_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,58 @@ func TestCompletePushRulesetSupport(t *testing.T) {
418418
t.Errorf("Expected 3 restricted file extensions, got %d", len(restrictedExts))
419419
}
420420
}
421+
422+
func TestCopilotCodeReviewRoundTrip(t *testing.T) {
423+
// Test that copilot_code_review rule survives expand -> flatten round trip
424+
rulesMap := map[string]any{
425+
"copilot_code_review": []any{
426+
map[string]any{
427+
"review_on_push": true,
428+
"review_draft_pull_requests": false,
429+
},
430+
},
431+
}
432+
433+
input := []any{rulesMap}
434+
435+
// Expand to GitHub API format
436+
expandedRules := expandRules(input, false)
437+
438+
if expandedRules == nil {
439+
t.Fatal("Expected expandedRules to not be nil")
440+
}
441+
442+
if expandedRules.CopilotCodeReview == nil {
443+
t.Fatal("Expected CopilotCodeReview rule to be set")
444+
}
445+
446+
if expandedRules.CopilotCodeReview.ReviewOnPush != true {
447+
t.Errorf("Expected ReviewOnPush to be true, got %v", expandedRules.CopilotCodeReview.ReviewOnPush)
448+
}
449+
450+
if expandedRules.CopilotCodeReview.ReviewDraftPullRequests != false {
451+
t.Errorf("Expected ReviewDraftPullRequests to be false, got %v", expandedRules.CopilotCodeReview.ReviewDraftPullRequests)
452+
}
453+
454+
// Flatten back to terraform format
455+
flattenedResult := flattenRules(expandedRules, false)
456+
457+
if len(flattenedResult) != 1 {
458+
t.Fatalf("Expected 1 flattened result, got %d", len(flattenedResult))
459+
}
460+
461+
flattenedRulesMap := flattenedResult[0].(map[string]any)
462+
copilotRules := flattenedRulesMap["copilot_code_review"].([]map[string]any)
463+
464+
if len(copilotRules) != 1 {
465+
t.Fatalf("Expected 1 copilot_code_review rule after round trip, got %d", len(copilotRules))
466+
}
467+
468+
if copilotRules[0]["review_on_push"] != true {
469+
t.Errorf("Expected review_on_push to be true, got %v", copilotRules[0]["review_on_push"])
470+
}
471+
472+
if copilotRules[0]["review_draft_pull_requests"] != false {
473+
t.Errorf("Expected review_draft_pull_requests to be false, got %v", copilotRules[0]["review_draft_pull_requests"])
474+
}
475+
}

website/docs/r/organization_ruleset.html.markdown

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ The `rules` block supports the following:
136136

137137
* `pull_request` - (Optional) (Block List, Max: 1) Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. (see [below for nested schema](#rules.pull_request))
138138

139+
* `copilot_code_review` - (Optional) (Block List, Max: 1) Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit. (see [below for nested schema](#rulescopilot_code_review))
140+
139141
* `required_linear_history` - (Optional) (Boolean) Prevent merge commits from being pushed to matching branches.
140142

141143
* `required_signatures` - (Optional) (Boolean) Commits pushed to matching branches must have verified signatures.
@@ -210,6 +212,12 @@ The `rules` block supports the following:
210212

211213
* `required_review_thread_resolution` - (Optional) (Boolean) All conversations on code must be resolved before a pull request can be merged. Defaults to `false`.
212214

215+
#### rules.copilot_code_review ####
216+
217+
* `review_on_push` - (Optional) (Boolean) Copilot automatically reviews each new push to the pull request. Defaults to `false`.
218+
219+
* `review_draft_pull_requests` - (Optional) (Boolean) Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`.
220+
213221
#### rules.required_status_checks ####
214222

215223
* `required_check` - (Required) (Block Set, Min: 1) Status checks that are required. Several can be defined. (see [below for nested schema](#rules.required_status_checks.required_check))

website/docs/r/repository_ruleset.html.markdown

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ The `rules` block supports the following:
124124

125125
* `pull_request` - (Optional) (Block List, Max: 1) Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. (see [below for nested schema](#rulespull_request))
126126

127+
* `copilot_code_review` - (Optional) (Block List, Max: 1) Automatically request Copilot code review for new pull requests if the author has access to Copilot code review and their premium requests quota has not reached the limit. (see [below for nested schema](#rulescopilot_code_review))
128+
127129
* `required_deployments` - (Optional) (Block List, Max: 1) Choose which environments must be successfully deployed to before branches can be merged into a branch that matches this rule. (see [below for nested schema](#rulesrequired_deployments))
128130

129131
* `required_linear_history` - (Optional) (Boolean) Prevent merge commits from being pushed to matching branches.
@@ -215,6 +217,12 @@ The `rules` block supports the following:
215217

216218
* `required_review_thread_resolution` - (Optional) (Boolean) All conversations on code must be resolved before a pull request can be merged. Defaults to `false`.
217219

220+
#### rules.copilot_code_review ####
221+
222+
* `review_on_push` - (Optional) (Boolean) Copilot automatically reviews each new push to the pull request. Defaults to `false`.
223+
224+
* `review_draft_pull_requests` - (Optional) (Boolean) Copilot automatically reviews draft pull requests before they are marked as ready for review. Defaults to `false`.
225+
218226
#### rules.required_deployments ####
219227

220228
* `required_deployment_environments` - (Required) (List of String) The environments that must be successfully deployed to before branches can be merged.

0 commit comments

Comments
 (0)