diff --git a/github/rules.go b/github/rules.go index 8f858b3a8dd..d16207fea15 100644 --- a/github/rules.go +++ b/github/rules.go @@ -73,6 +73,7 @@ const ( // Branch or tag target rules. RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern" RulesetRuleTypeCodeCoverage RepositoryRuleType = "code_coverage" + RulesetRuleTypeCodeQuality RepositoryRuleType = "code_quality" RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning" RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern" RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern" @@ -321,6 +322,7 @@ type RepositoryRulesetRules struct { Workflows *WorkflowsRuleParameters CodeScanning *CodeScanningRuleParameters CodeCoverage *CodeCoverageRuleParameters + CodeQuality *CodeQualityRuleParameters CopilotCodeReview *CopilotCodeReviewRuleParameters // Push target rules. @@ -358,6 +360,7 @@ type BranchRules struct { TagNamePattern []*PatternBranchRule Workflows []*WorkflowsBranchRule CodeScanning []*CodeScanningBranchRule + CodeQuality []*CodeQualityBranchRule CopilotCodeReview []*CopilotCodeReviewBranchRule // Push target rules. @@ -446,6 +449,12 @@ type CodeScanningBranchRule struct { Parameters CodeScanningRuleParameters `json:"parameters"` } +// CodeQualityBranchRule represents a code quality branch rule. +type CodeQualityBranchRule struct { + BranchRuleMetadata + Parameters CodeQualityRuleParameters `json:"parameters"` +} + // CopilotCodeReviewBranchRule represents a copilot code review branch rule. type CopilotCodeReviewBranchRule struct { BranchRuleMetadata @@ -611,6 +620,11 @@ type CodeCoverageRuleParameters struct { MinimumCoverage *float64 `json:"minimum_coverage,omitempty"` } +// CodeQualityRuleParameters represents the code_quality rule parameters. +type CodeQualityRuleParameters struct { + Severity string `json:"severity,omitempty"` +} + // CopilotCodeReviewRuleParameters represents the copilot_code_review rule parameters. type CopilotCodeReviewRuleParameters struct { ReviewOnPush bool `json:"review_on_push"` @@ -823,6 +837,14 @@ func (r RepositoryRulesetRules) MarshalJSON() ([]byte, error) { rawRules = append(rawRules, json.RawMessage(bytes)) } + if r.CodeQuality != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCodeQuality, r.CodeQuality) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + if r.CopilotCodeReview != nil { bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCopilotCodeReview, r.CopilotCodeReview) if err != nil { @@ -1070,6 +1092,14 @@ func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error { return err } } + case RulesetRuleTypeCodeQuality: + r.CodeQuality = &CodeQualityRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CodeQuality); err != nil { + return err + } + } case RulesetRuleTypeCopilotCodeReview: r.CopilotCodeReview = &CopilotCodeReviewRuleParameters{} @@ -1293,6 +1323,16 @@ func (r *BranchRules) UnmarshalJSON(data []byte) error { } r.CodeScanning = append(r.CodeScanning, &CodeScanningBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCodeQuality: + params := &CodeQualityRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CodeQuality = append(r.CodeQuality, &CodeQualityBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) case RulesetRuleTypeCopilotCodeReview: params := &CopilotCodeReviewRuleParameters{} @@ -1462,6 +1502,16 @@ func (r *RepositoryRule) UnmarshalJSON(data []byte) error { } } + r.Parameters = p + case RulesetRuleTypeCodeQuality: + p := &CodeQualityRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + r.Parameters = p case RulesetRuleTypeCopilotCodeReview: p := &CopilotCodeReviewRuleParameters{} diff --git a/github/rules_test.go b/github/rules_test.go index ea3e64217d4..d1acc8fabc0 100644 --- a/github/rules_test.go +++ b/github/rules_test.go @@ -100,6 +100,67 @@ func TestRepositoryRulesetRules_CodeCoverageMarshalError(t *testing.T) { } } +func TestCodeQualityRuleRoundTrip(t *testing.T) { + t.Parallel() + tests := []struct { + name string + params CodeQualityRuleParameters + json string + }{ + { + "with_severity", + CodeQualityRuleParameters{Severity: "errors"}, + `{"severity":"errors"}`, + }, + { + "empty_parameters", + CodeQualityRuleParameters{}, + `{}`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ruleJSON := `{"type":"code_quality","parameters":` + tt.json + `}` + testJSONMarshal(t, &RepositoryRule{ + Type: RulesetRuleTypeCodeQuality, + Parameters: &tt.params, + }, ruleJSON) + testJSONMarshal(t, &RepositoryRulesetRules{CodeQuality: &tt.params}, `[`+ruleJSON+`]`) + }) + } + + t.Run("missing_parameters", func(t *testing.T) { + t.Parallel() + testJSONUnmarshalOnly(t, &RepositoryRule{ + Type: RulesetRuleTypeCodeQuality, + Parameters: &CodeQualityRuleParameters{}, + }, `{"type":"code_quality"}`) + testJSONUnmarshalOnly(t, &RepositoryRulesetRules{ + CodeQuality: &CodeQualityRuleParameters{}, + }, `[{"type":"code_quality"}]`) + }) +} + +func TestCodeQualityRuleInvalidParameters(t *testing.T) { + t.Parallel() + for _, params := range []string{ + `{"severity":123}`, + `"not_an_object"`, + } { + t.Run(params, func(t *testing.T) { + t.Parallel() + data := `{"type":"code_quality","parameters":` + params + `}` + if err := json.Unmarshal([]byte(data), &RepositoryRule{}); err == nil { + t.Errorf("Expected error unmarshaling %q, got nil", data) + } + if err := json.Unmarshal([]byte(`[`+data+`]`), &RepositoryRulesetRules{}); err == nil { + t.Errorf("Expected error unmarshaling [%v], got nil", data) + } + }) + } +} + func TestRepositoryRulesetRules(t *testing.T) { t.Parallel() tests := []struct {