Skip to content

Commit fab8541

Browse files
committed
feat: add copilot_code_review rule support to repository rulesets
Add support for GitHub's Copilot code review feature in repository rulesets, allowing automatic code review requests for pull requests. Changes: - Add RulesetRuleTypeCopilotCodeReview constant to rule types - Add CopilotCodeReviewRuleParameters struct with review_new_pushes and review_draft_pull_requests fields - Implement marshal/unmarshal logic for copilot_code_review rules - Add comprehensive test coverage for the new rule type 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
1 parent 66f826c commit fab8541

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

github/rules.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const (
8787
RulesetRuleTypeTagNamePattern RepositoryRuleType = "tag_name_pattern"
8888
RulesetRuleTypeUpdate RepositoryRuleType = "update"
8989
RulesetRuleTypeWorkflows RepositoryRuleType = "workflows"
90+
RulesetRuleTypeCopilotCodeReview RepositoryRuleType = "copilot_code_review"
9091

9192
// Push target rules.
9293
RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction"
@@ -306,6 +307,7 @@ type RepositoryRulesetRules struct {
306307
TagNamePattern *PatternRuleParameters
307308
Workflows *WorkflowsRuleParameters
308309
CodeScanning *CodeScanningRuleParameters
310+
CopilotCodeReview *CopilotCodeReviewRuleParameters
309311

310312
// Push target rules.
311313
FileExtensionRestriction *FileExtensionRestrictionRuleParameters
@@ -539,6 +541,12 @@ type CodeScanningRuleParameters struct {
539541
CodeScanningTools []*RuleCodeScanningTool `json:"code_scanning_tools"`
540542
}
541543

544+
// CopilotCodeReviewRuleParameters represents the copilot_code_review rule parameters.
545+
type CopilotCodeReviewRuleParameters struct {
546+
ReviewNewPushes bool `json:"review_new_pushes"`
547+
ReviewDraftPullRequests bool `json:"review_draft_pull_requests"`
548+
}
549+
542550
// RuleCodeScanningTool represents a single code scanning tool for the code scanning parameters.
543551
type RuleCodeScanningTool struct {
544552
AlertsThreshold CodeScanningAlertsThreshold `json:"alerts_threshold"`
@@ -566,9 +574,9 @@ type repositoryRulesetRuleWrapper struct {
566574

567575
// MarshalJSON is a custom JSON marshaler for RulesetRules.
568576
func (r *RepositoryRulesetRules) MarshalJSON() ([]byte, error) {
569-
// The RepositoryRulesetRules type marshals to between 1 and 21 rules.
577+
// The RepositoryRulesetRules type marshals to between 1 and 22 rules.
570578
// If new rules are added to RepositoryRulesetRules the capacity below needs increasing
571-
rawRules := make([]json.RawMessage, 0, 21)
579+
rawRules := make([]json.RawMessage, 0, 22)
572580

573581
if r.Creation != nil {
574582
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCreation, r.Creation)
@@ -738,6 +746,14 @@ func (r *RepositoryRulesetRules) MarshalJSON() ([]byte, error) {
738746
rawRules = append(rawRules, json.RawMessage(bytes))
739747
}
740748

749+
if r.CopilotCodeReview != nil {
750+
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCopilotCodeReview, r.CopilotCodeReview)
751+
if err != nil {
752+
return nil, err
753+
}
754+
rawRules = append(rawRules, json.RawMessage(bytes))
755+
}
756+
741757
if r.RepositoryCreate != nil {
742758
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryCreate, r.RepositoryCreate)
743759
if err != nil {
@@ -965,6 +981,14 @@ func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error {
965981
return err
966982
}
967983
}
984+
case RulesetRuleTypeCopilotCodeReview:
985+
r.CopilotCodeReview = &CopilotCodeReviewRuleParameters{}
986+
987+
if w.Parameters != nil {
988+
if err := json.Unmarshal(w.Parameters, r.CopilotCodeReview); err != nil {
989+
return err
990+
}
991+
}
968992
case RulesetRuleTypeRepositoryCreate:
969993
r.RepositoryCreate = &EmptyRuleParameters{}
970994
case RulesetRuleTypeRepositoryDelete:

github/rules_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,17 @@ func TestRulesetRules(t *testing.T) {
122122
},
123123
},
124124
},
125+
CopilotCodeReview: &CopilotCodeReviewRuleParameters{
126+
ReviewNewPushes: true,
127+
ReviewDraftPullRequests: false,
128+
},
125129
RepositoryCreate: &EmptyRuleParameters{},
126130
RepositoryDelete: &EmptyRuleParameters{},
127131
RepositoryName: &SimplePatternRuleParameters{Pattern: "^test-.+", Negate: false},
128132
RepositoryTransfer: &EmptyRuleParameters{},
129133
RepositoryVisibility: &RepositoryVisibilityRuleParameters{Internal: false, Private: false},
130134
},
131-
`[{"type":"creation"},{"type":"update"},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"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}},{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test1"},{"context":"test2"}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"workflows":[{"path":".github/workflows/test1.yaml"},{"path":".github/workflows/test2.yaml"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false}}]`,
135+
`[{"type":"creation"},{"type":"update"},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"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}},{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test1"},{"context":"test2"}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"workflows":[{"path":".github/workflows/test1.yaml"},{"path":".github/workflows/test2.yaml"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","parameters":{"review_new_pushes":true,"review_draft_pull_requests":false}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false}}]`,
132136
},
133137
{
134138
"all_rules_with_all_params",
@@ -240,13 +244,17 @@ func TestRulesetRules(t *testing.T) {
240244
},
241245
},
242246
},
247+
CopilotCodeReview: &CopilotCodeReviewRuleParameters{
248+
ReviewNewPushes: true,
249+
ReviewDraftPullRequests: false,
250+
},
243251
RepositoryCreate: &EmptyRuleParameters{},
244252
RepositoryDelete: &EmptyRuleParameters{},
245253
RepositoryName: &SimplePatternRuleParameters{Pattern: "^test-.+", Negate: false},
246254
RepositoryTransfer: &EmptyRuleParameters{},
247255
RepositoryVisibility: &RepositoryVisibilityRuleParameters{Internal: false, Private: false},
248256
},
249-
`[{"type":"creation"},{"type":"update","parameters":{"update_allows_fetch_and_merge":true}},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"automatic_copilot_code_review_enabled":false,"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}},{"type":"required_status_checks","parameters":{"do_not_enforce_on_create":true,"required_status_checks":[{"context":"test1","integration_id":1},{"context":"test2","integration_id":2}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"name":"cmp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"name":"caep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"name":"cep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"name":"bp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"name":"tp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"do_not_enforce_on_create":true,"workflows":[{"path":".github/workflows/test1.yaml","ref":"main","repository_id":1,"sha":"aaaa"},{"path":".github/workflows/test2.yaml","ref":"main","repository_id":2,"sha":"bbbb"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false}}]`,
257+
`[{"type":"creation"},{"type":"update","parameters":{"update_allows_fetch_and_merge":true}},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"automatic_copilot_code_review_enabled":false,"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}},{"type":"required_status_checks","parameters":{"do_not_enforce_on_create":true,"required_status_checks":[{"context":"test1","integration_id":1},{"context":"test2","integration_id":2}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"name":"cmp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"name":"caep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"name":"cep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"name":"bp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"name":"tp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"do_not_enforce_on_create":true,"workflows":[{"path":".github/workflows/test1.yaml","ref":"main","repository_id":1,"sha":"aaaa"},{"path":".github/workflows/test2.yaml","ref":"main","repository_id":2,"sha":"bbbb"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","parameters":{"review_new_pushes":true,"review_draft_pull_requests":false}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false}}]`,
250258
},
251259
}
252260

0 commit comments

Comments
 (0)