Skip to content

Commit c7473ca

Browse files
authored
feat: Add support for copilot_code_review rule type (google#3857)
1 parent e716505 commit c7473ca

File tree

4 files changed

+147
-4
lines changed

4 files changed

+147
-4
lines changed

github/github-accessors.go

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

github/github-accessors_test.go

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

github/rules.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const (
7575
RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern"
7676
RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern"
7777
RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern"
78+
RulesetRuleTypeCopilotCodeReview RepositoryRuleType = "copilot_code_review"
7879
RulesetRuleTypeCreation RepositoryRuleType = "creation"
7980
RulesetRuleTypeDeletion RepositoryRuleType = "deletion"
8081
RulesetRuleTypeMergeQueue RepositoryRuleType = "merge_queue"
@@ -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:
@@ -1366,6 +1390,16 @@ func (r *RepositoryRule) UnmarshalJSON(data []byte) error {
13661390
}
13671391
}
13681392

1393+
r.Parameters = p
1394+
case RulesetRuleTypeCopilotCodeReview:
1395+
p := &CopilotCodeReviewRuleParameters{}
1396+
1397+
if w.Parameters != nil {
1398+
if err := json.Unmarshal(w.Parameters, p); err != nil {
1399+
return err
1400+
}
1401+
}
1402+
13691403
r.Parameters = p
13701404
case RulesetRuleTypeRepositoryCreate:
13711405
r.Parameters = nil

github/rules_test.go

Lines changed: 95 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

@@ -298,6 +306,39 @@ func TestRulesetRules(t *testing.T) {
298306
})
299307
}
300308
})
309+
310+
t.Run("UnmarshalJSON_Error", func(t *testing.T) {
311+
t.Parallel()
312+
313+
tests := []struct {
314+
name string
315+
json string
316+
}{
317+
{
318+
"invalid_copilot_code_review_bool",
319+
`[{"type":"copilot_code_review","parameters":{"review_new_pushes":"invalid_bool"}}]`,
320+
},
321+
{
322+
"invalid_copilot_code_review_draft_pr",
323+
`[{"type":"copilot_code_review","parameters":{"review_new_pushes":true,"review_draft_pull_requests":"not_a_bool"}}]`,
324+
},
325+
{
326+
"invalid_copilot_code_review_parameters",
327+
`[{"type":"copilot_code_review","parameters":"not_an_object"}]`,
328+
},
329+
}
330+
331+
for _, tt := range tests {
332+
t.Run(tt.name, func(t *testing.T) {
333+
t.Parallel()
334+
got := &RepositoryRulesetRules{}
335+
err := json.Unmarshal([]byte(tt.json), got)
336+
if err == nil {
337+
t.Errorf("Expected error unmarshaling %q, got nil", tt.json)
338+
}
339+
})
340+
}
341+
})
301342
}
302343

303344
func TestBranchRules(t *testing.T) {
@@ -929,6 +970,25 @@ func TestRepositoryRule(t *testing.T) {
929970
},
930971
`{"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"}]}}`,
931972
},
973+
{
974+
"copilot_code_review",
975+
&RepositoryRule{
976+
Type: RulesetRuleTypeCopilotCodeReview,
977+
Parameters: &CopilotCodeReviewRuleParameters{
978+
ReviewNewPushes: true,
979+
ReviewDraftPullRequests: false,
980+
},
981+
},
982+
`{"type":"copilot_code_review","parameters":{"review_new_pushes":true,"review_draft_pull_requests":false}}`,
983+
},
984+
{
985+
"copilot_code_review_empty_params",
986+
&RepositoryRule{
987+
Type: RulesetRuleTypeCopilotCodeReview,
988+
Parameters: &CopilotCodeReviewRuleParameters{},
989+
},
990+
`{"type":"copilot_code_review","parameters":{"review_new_pushes":false,"review_draft_pull_requests":false}}`,
991+
},
932992
{
933993
"repository_create",
934994
&RepositoryRule{Type: RulesetRuleTypeRepositoryCreate, Parameters: nil},
@@ -992,4 +1052,37 @@ func TestRepositoryRule(t *testing.T) {
9921052
})
9931053
}
9941054
})
1055+
1056+
t.Run("UnmarshalJSON_Error", func(t *testing.T) {
1057+
t.Parallel()
1058+
1059+
tests := []struct {
1060+
name string
1061+
json string
1062+
}{
1063+
{
1064+
"invalid_copilot_code_review_bool",
1065+
`{"type":"copilot_code_review","parameters":{"review_new_pushes":"invalid_bool"}}`,
1066+
},
1067+
{
1068+
"invalid_copilot_code_review_draft_pr",
1069+
`{"type":"copilot_code_review","parameters":{"review_new_pushes":true,"review_draft_pull_requests":"not_a_bool"}}`,
1070+
},
1071+
{
1072+
"invalid_copilot_code_review_parameters",
1073+
`{"type":"copilot_code_review","parameters":"not_an_object"}`,
1074+
},
1075+
}
1076+
1077+
for _, tt := range tests {
1078+
t.Run(tt.name, func(t *testing.T) {
1079+
t.Parallel()
1080+
got := &RepositoryRule{}
1081+
err := json.Unmarshal([]byte(tt.json), got)
1082+
if err == nil {
1083+
t.Errorf("Expected error unmarshaling %q, got nil", tt.json)
1084+
}
1085+
})
1086+
}
1087+
})
9951088
}

0 commit comments

Comments
 (0)