Skip to content

Commit e119eb8

Browse files
authored
feat: Add repository target to ruleset (#3850)
1 parent 841abae commit e119eb8

File tree

4 files changed

+295
-28
lines changed

4 files changed

+295
-28
lines changed

github/github-accessors.go

Lines changed: 40 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: 40 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: 166 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,18 @@ package github
77

88
import (
99
"encoding/json"
10-
"reflect"
10+
"fmt"
1111
)
1212

1313
// RulesetTarget represents a GitHub ruleset target.
1414
type RulesetTarget string
1515

1616
// This is the set of GitHub ruleset targets.
1717
const (
18-
RulesetTargetBranch RulesetTarget = "branch"
19-
RulesetTargetTag RulesetTarget = "tag"
20-
RulesetTargetPush RulesetTarget = "push"
18+
RulesetTargetBranch RulesetTarget = "branch"
19+
RulesetTargetTag RulesetTarget = "tag"
20+
RulesetTargetPush RulesetTarget = "push"
21+
RulesetTargetRepository RulesetTarget = "repository"
2122
)
2223

2324
// RulesetSourceType represents a GitHub ruleset source type.
@@ -68,27 +69,37 @@ type RepositoryRuleType string
6869

6970
// This is the set of GitHub ruleset rule types.
7071
const (
72+
// Branch or tag target rules.
73+
RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern"
74+
RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning"
75+
RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern"
76+
RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern"
77+
RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern"
7178
RulesetRuleTypeCreation RepositoryRuleType = "creation"
72-
RulesetRuleTypeUpdate RepositoryRuleType = "update"
7379
RulesetRuleTypeDeletion RepositoryRuleType = "deletion"
74-
RulesetRuleTypeRequiredLinearHistory RepositoryRuleType = "required_linear_history"
7580
RulesetRuleTypeMergeQueue RepositoryRuleType = "merge_queue"
81+
RulesetRuleTypeNonFastForward RepositoryRuleType = "non_fast_forward"
82+
RulesetRuleTypePullRequest RepositoryRuleType = "pull_request"
7683
RulesetRuleTypeRequiredDeployments RepositoryRuleType = "required_deployments"
84+
RulesetRuleTypeRequiredLinearHistory RepositoryRuleType = "required_linear_history"
7785
RulesetRuleTypeRequiredSignatures RepositoryRuleType = "required_signatures"
78-
RulesetRuleTypePullRequest RepositoryRuleType = "pull_request"
7986
RulesetRuleTypeRequiredStatusChecks RepositoryRuleType = "required_status_checks"
80-
RulesetRuleTypeNonFastForward RepositoryRuleType = "non_fast_forward"
81-
RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern"
82-
RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern"
83-
RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern"
84-
RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern"
8587
RulesetRuleTypeTagNamePattern RepositoryRuleType = "tag_name_pattern"
88+
RulesetRuleTypeUpdate RepositoryRuleType = "update"
89+
RulesetRuleTypeWorkflows RepositoryRuleType = "workflows"
90+
91+
// Push target rules.
92+
RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction"
8693
RulesetRuleTypeFilePathRestriction RepositoryRuleType = "file_path_restriction"
8794
RulesetRuleTypeMaxFilePathLength RepositoryRuleType = "max_file_path_length"
88-
RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction"
8995
RulesetRuleTypeMaxFileSize RepositoryRuleType = "max_file_size"
90-
RulesetRuleTypeWorkflows RepositoryRuleType = "workflows"
91-
RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning"
96+
97+
// Repository target rules.
98+
RulesetRuleTypeRepositoryCreate RepositoryRuleType = "repository_create"
99+
RulesetRuleTypeRepositoryDelete RepositoryRuleType = "repository_delete"
100+
RulesetRuleTypeRepositoryName RepositoryRuleType = "repository_name"
101+
RulesetRuleTypeRepositoryTransfer RepositoryRuleType = "repository_transfer"
102+
RulesetRuleTypeRepositoryVisibility RepositoryRuleType = "repository_visibility"
92103
)
93104

94105
// MergeGroupingStrategy models a GitHub merge grouping strategy.
@@ -277,6 +288,7 @@ type RepositoryRule struct {
277288
// RepositoryRulesetRules represents a GitHub ruleset rules object.
278289
// This type doesn't have JSON annotations as it uses custom marshaling.
279290
type RepositoryRulesetRules struct {
291+
// Branch or tag target rules.
280292
Creation *EmptyRuleParameters
281293
Update *UpdateRuleParameters
282294
Deletion *EmptyRuleParameters
@@ -292,17 +304,27 @@ type RepositoryRulesetRules struct {
292304
CommitterEmailPattern *PatternRuleParameters
293305
BranchNamePattern *PatternRuleParameters
294306
TagNamePattern *PatternRuleParameters
307+
Workflows *WorkflowsRuleParameters
308+
CodeScanning *CodeScanningRuleParameters
309+
310+
// Push target rules.
311+
FileExtensionRestriction *FileExtensionRestrictionRuleParameters
295312
FilePathRestriction *FilePathRestrictionRuleParameters
296313
MaxFilePathLength *MaxFilePathLengthRuleParameters
297-
FileExtensionRestriction *FileExtensionRestrictionRuleParameters
298314
MaxFileSize *MaxFileSizeRuleParameters
299-
Workflows *WorkflowsRuleParameters
300-
CodeScanning *CodeScanningRuleParameters
315+
316+
// Repository target rules.
317+
RepositoryCreate *EmptyRuleParameters
318+
RepositoryDelete *EmptyRuleParameters
319+
RepositoryName *SimplePatternRuleParameters
320+
RepositoryTransfer *EmptyRuleParameters
321+
RepositoryVisibility *RepositoryVisibilityRuleParameters
301322
}
302323

303324
// BranchRules represents the rules active for a GitHub repository branch.
304325
// This type doesn't have JSON annotations as it uses custom marshaling.
305326
type BranchRules struct {
327+
// Branch or tag target rules.
306328
Creation []*BranchRuleMetadata
307329
Update []*UpdateBranchRule
308330
Deletion []*BranchRuleMetadata
@@ -318,12 +340,14 @@ type BranchRules struct {
318340
CommitterEmailPattern []*PatternBranchRule
319341
BranchNamePattern []*PatternBranchRule
320342
TagNamePattern []*PatternBranchRule
343+
Workflows []*WorkflowsBranchRule
344+
CodeScanning []*CodeScanningBranchRule
345+
346+
// Push target rules.
347+
FileExtensionRestriction []*FileExtensionRestrictionBranchRule
321348
FilePathRestriction []*FilePathRestrictionBranchRule
322349
MaxFilePathLength []*MaxFilePathLengthBranchRule
323-
FileExtensionRestriction []*FileExtensionRestrictionBranchRule
324350
MaxFileSize []*MaxFileSizeBranchRule
325-
Workflows []*WorkflowsBranchRule
326-
CodeScanning []*CodeScanningBranchRule
327351
}
328352

329353
// BranchRuleMetadata represents the metadata for a branch rule.
@@ -522,6 +546,18 @@ type RuleCodeScanningTool struct {
522546
Tool string `json:"tool"`
523547
}
524548

549+
// SimplePatternRuleParameters represents the parameters for a simple pattern rule.
550+
type SimplePatternRuleParameters struct {
551+
Negate bool `json:"negate"`
552+
Pattern string `json:"pattern"`
553+
}
554+
555+
// RepositoryVisibilityRuleParameters represents the repository visibility rule parameters.
556+
type RepositoryVisibilityRuleParameters struct {
557+
Internal bool `json:"internal"`
558+
Private bool `json:"private"`
559+
}
560+
525561
// repositoryRulesetRuleWrapper is a helper type to marshal & unmarshal a ruleset rule.
526562
type repositoryRulesetRuleWrapper struct {
527563
Type RepositoryRuleType `json:"type"`
@@ -702,17 +738,74 @@ func (r *RepositoryRulesetRules) MarshalJSON() ([]byte, error) {
702738
rawRules = append(rawRules, json.RawMessage(bytes))
703739
}
704740

741+
if r.RepositoryCreate != nil {
742+
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryCreate, r.RepositoryCreate)
743+
if err != nil {
744+
return nil, err
745+
}
746+
rawRules = append(rawRules, json.RawMessage(bytes))
747+
}
748+
749+
if r.RepositoryDelete != nil {
750+
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryDelete, r.RepositoryDelete)
751+
if err != nil {
752+
return nil, err
753+
}
754+
rawRules = append(rawRules, json.RawMessage(bytes))
755+
}
756+
757+
if r.RepositoryName != nil {
758+
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryName, r.RepositoryName)
759+
if err != nil {
760+
return nil, err
761+
}
762+
rawRules = append(rawRules, json.RawMessage(bytes))
763+
}
764+
765+
if r.RepositoryTransfer != nil {
766+
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryTransfer, r.RepositoryTransfer)
767+
if err != nil {
768+
return nil, err
769+
}
770+
rawRules = append(rawRules, json.RawMessage(bytes))
771+
}
772+
773+
if r.RepositoryVisibility != nil {
774+
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryVisibility, r.RepositoryVisibility)
775+
if err != nil {
776+
return nil, err
777+
}
778+
rawRules = append(rawRules, json.RawMessage(bytes))
779+
}
780+
705781
return json.Marshal(rawRules)
706782
}
707783

708784
// marshalRepositoryRulesetRule is a helper function to marshal a ruleset rule.
709-
//
710-
// TODO: Benchmark the code that uses reflection.
711-
// TODO: Use a generator here instead of reflection if there is a significant performance hit.
712785
func marshalRepositoryRulesetRule[T any](t RepositoryRuleType, params T) ([]byte, error) {
713-
paramsType := reflect.TypeFor[T]()
786+
hasParams := true
787+
788+
switch t {
789+
case RulesetRuleTypeCreation,
790+
RulesetRuleTypeDeletion,
791+
RulesetRuleTypeRequiredLinearHistory,
792+
RulesetRuleTypeRequiredSignatures,
793+
RulesetRuleTypeNonFastForward,
794+
RulesetRuleTypeRepositoryCreate,
795+
RulesetRuleTypeRepositoryDelete,
796+
RulesetRuleTypeRepositoryTransfer:
797+
hasParams = false
798+
case RulesetRuleTypeUpdate:
799+
paramsTyped, ok := any(params).(*UpdateRuleParameters)
800+
if !ok {
801+
return nil, fmt.Errorf("expected UpdateRuleParameters for rule type %v", t)
802+
}
803+
if paramsTyped == nil || *paramsTyped == (UpdateRuleParameters{}) {
804+
hasParams = false
805+
}
806+
}
714807

715-
if paramsType.Kind() == reflect.Pointer && (reflect.ValueOf(params).IsNil() || reflect.ValueOf(params).Elem().IsZero()) {
808+
if !hasParams {
716809
return json.Marshal(repositoryRulesetRuleWrapper{Type: t})
717810
}
718811

@@ -872,6 +965,28 @@ func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error {
872965
return err
873966
}
874967
}
968+
case RulesetRuleTypeRepositoryCreate:
969+
r.RepositoryCreate = &EmptyRuleParameters{}
970+
case RulesetRuleTypeRepositoryDelete:
971+
r.RepositoryDelete = &EmptyRuleParameters{}
972+
case RulesetRuleTypeRepositoryName:
973+
r.RepositoryName = &SimplePatternRuleParameters{}
974+
975+
if w.Parameters != nil {
976+
if err := json.Unmarshal(w.Parameters, r.RepositoryName); err != nil {
977+
return err
978+
}
979+
}
980+
case RulesetRuleTypeRepositoryTransfer:
981+
r.RepositoryTransfer = &EmptyRuleParameters{}
982+
case RulesetRuleTypeRepositoryVisibility:
983+
r.RepositoryVisibility = &RepositoryVisibilityRuleParameters{}
984+
985+
if w.Parameters != nil {
986+
if err := json.Unmarshal(w.Parameters, r.RepositoryVisibility); err != nil {
987+
return err
988+
}
989+
}
875990
}
876991
}
877992

@@ -1251,6 +1366,31 @@ func (r *RepositoryRule) UnmarshalJSON(data []byte) error {
12511366
}
12521367
}
12531368

1369+
r.Parameters = p
1370+
case RulesetRuleTypeRepositoryCreate:
1371+
r.Parameters = nil
1372+
case RulesetRuleTypeRepositoryDelete:
1373+
r.Parameters = nil
1374+
case RulesetRuleTypeRepositoryName:
1375+
p := &SimplePatternRuleParameters{}
1376+
1377+
if w.Parameters != nil {
1378+
if err := json.Unmarshal(w.Parameters, p); err != nil {
1379+
return err
1380+
}
1381+
}
1382+
1383+
r.Parameters = p
1384+
case RulesetRuleTypeRepositoryTransfer:
1385+
r.Parameters = nil
1386+
case RulesetRuleTypeRepositoryVisibility:
1387+
p := &RepositoryVisibilityRuleParameters{}
1388+
1389+
if w.Parameters != nil {
1390+
if err := json.Unmarshal(w.Parameters, p); err != nil {
1391+
return err
1392+
}
1393+
}
12541394
r.Parameters = p
12551395
}
12561396

0 commit comments

Comments
 (0)