Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions github/github-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions github/github-accessors_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

192 changes: 166 additions & 26 deletions github/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ package github

import (
"encoding/json"
"reflect"
"fmt"
)

// RulesetTarget represents a GitHub ruleset target.
type RulesetTarget string

// This is the set of GitHub ruleset targets.
const (
RulesetTargetBranch RulesetTarget = "branch"
RulesetTargetTag RulesetTarget = "tag"
RulesetTargetPush RulesetTarget = "push"
RulesetTargetBranch RulesetTarget = "branch"
RulesetTargetTag RulesetTarget = "tag"
RulesetTargetPush RulesetTarget = "push"
RulesetTargetRepository RulesetTarget = "repository"
)

// RulesetSourceType represents a GitHub ruleset source type.
Expand Down Expand Up @@ -68,27 +69,37 @@ type RepositoryRuleType string

// This is the set of GitHub ruleset rule types.
const (
// Branch or tag target rules.
RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern"
RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning"
RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern"
RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern"
RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern"
RulesetRuleTypeCreation RepositoryRuleType = "creation"
RulesetRuleTypeUpdate RepositoryRuleType = "update"
RulesetRuleTypeDeletion RepositoryRuleType = "deletion"
RulesetRuleTypeRequiredLinearHistory RepositoryRuleType = "required_linear_history"
RulesetRuleTypeMergeQueue RepositoryRuleType = "merge_queue"
RulesetRuleTypeNonFastForward RepositoryRuleType = "non_fast_forward"
RulesetRuleTypePullRequest RepositoryRuleType = "pull_request"
RulesetRuleTypeRequiredDeployments RepositoryRuleType = "required_deployments"
RulesetRuleTypeRequiredLinearHistory RepositoryRuleType = "required_linear_history"
RulesetRuleTypeRequiredSignatures RepositoryRuleType = "required_signatures"
RulesetRuleTypePullRequest RepositoryRuleType = "pull_request"
RulesetRuleTypeRequiredStatusChecks RepositoryRuleType = "required_status_checks"
RulesetRuleTypeNonFastForward RepositoryRuleType = "non_fast_forward"
RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern"
RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern"
RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern"
RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern"
RulesetRuleTypeTagNamePattern RepositoryRuleType = "tag_name_pattern"
RulesetRuleTypeUpdate RepositoryRuleType = "update"
RulesetRuleTypeWorkflows RepositoryRuleType = "workflows"

// Push target rules.
RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction"
RulesetRuleTypeFilePathRestriction RepositoryRuleType = "file_path_restriction"
RulesetRuleTypeMaxFilePathLength RepositoryRuleType = "max_file_path_length"
RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction"
RulesetRuleTypeMaxFileSize RepositoryRuleType = "max_file_size"
RulesetRuleTypeWorkflows RepositoryRuleType = "workflows"
RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning"

// Repository target rules.
RulesetRuleTypeRepositoryCreate RepositoryRuleType = "repository_create"
RulesetRuleTypeRepositoryDelete RepositoryRuleType = "repository_delete"
RulesetRuleTypeRepositoryName RepositoryRuleType = "repository_name"
RulesetRuleTypeRepositoryTransfer RepositoryRuleType = "repository_transfer"
RulesetRuleTypeRepositoryVisibility RepositoryRuleType = "repository_visibility"
)

// MergeGroupingStrategy models a GitHub merge grouping strategy.
Expand Down Expand Up @@ -277,6 +288,7 @@ type RepositoryRule struct {
// RepositoryRulesetRules represents a GitHub ruleset rules object.
// This type doesn't have JSON annotations as it uses custom marshaling.
type RepositoryRulesetRules struct {
// Branch or tag target rules.
Creation *EmptyRuleParameters
Update *UpdateRuleParameters
Deletion *EmptyRuleParameters
Expand All @@ -292,17 +304,27 @@ type RepositoryRulesetRules struct {
CommitterEmailPattern *PatternRuleParameters
BranchNamePattern *PatternRuleParameters
TagNamePattern *PatternRuleParameters
Workflows *WorkflowsRuleParameters
CodeScanning *CodeScanningRuleParameters

// Push target rules.
FileExtensionRestriction *FileExtensionRestrictionRuleParameters
FilePathRestriction *FilePathRestrictionRuleParameters
MaxFilePathLength *MaxFilePathLengthRuleParameters
FileExtensionRestriction *FileExtensionRestrictionRuleParameters
MaxFileSize *MaxFileSizeRuleParameters
Workflows *WorkflowsRuleParameters
CodeScanning *CodeScanningRuleParameters

// Repository target rules.
RepositoryCreate *EmptyRuleParameters
RepositoryDelete *EmptyRuleParameters
RepositoryName *SimplePatternRuleParameters
RepositoryTransfer *EmptyRuleParameters
RepositoryVisibility *RepositoryVisibilityRuleParameters
}

// BranchRules represents the rules active for a GitHub repository branch.
// This type doesn't have JSON annotations as it uses custom marshaling.
type BranchRules struct {
// Branch or tag target rules.
Creation []*BranchRuleMetadata
Update []*UpdateBranchRule
Deletion []*BranchRuleMetadata
Expand All @@ -318,12 +340,14 @@ type BranchRules struct {
CommitterEmailPattern []*PatternBranchRule
BranchNamePattern []*PatternBranchRule
TagNamePattern []*PatternBranchRule
Workflows []*WorkflowsBranchRule
CodeScanning []*CodeScanningBranchRule

// Push target rules.
FileExtensionRestriction []*FileExtensionRestrictionBranchRule
FilePathRestriction []*FilePathRestrictionBranchRule
MaxFilePathLength []*MaxFilePathLengthBranchRule
FileExtensionRestriction []*FileExtensionRestrictionBranchRule
MaxFileSize []*MaxFileSizeBranchRule
Workflows []*WorkflowsBranchRule
CodeScanning []*CodeScanningBranchRule
}

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

// SimplePatternRuleParameters represents the parameters for a simple pattern rule.
type SimplePatternRuleParameters struct {
Negate bool `json:"negate"`
Pattern string `json:"pattern"`
}

// RepositoryVisibilityRuleParameters represents the repository visibility rule parameters.
type RepositoryVisibilityRuleParameters struct {
Internal bool `json:"internal"`
Private bool `json:"private"`
}

// repositoryRulesetRuleWrapper is a helper type to marshal & unmarshal a ruleset rule.
type repositoryRulesetRuleWrapper struct {
Type RepositoryRuleType `json:"type"`
Expand Down Expand Up @@ -702,17 +738,74 @@ func (r *RepositoryRulesetRules) MarshalJSON() ([]byte, error) {
rawRules = append(rawRules, json.RawMessage(bytes))
}

if r.RepositoryCreate != nil {
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryCreate, r.RepositoryCreate)
if err != nil {
return nil, err
}
rawRules = append(rawRules, json.RawMessage(bytes))
}

if r.RepositoryDelete != nil {
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryDelete, r.RepositoryDelete)
if err != nil {
return nil, err
}
rawRules = append(rawRules, json.RawMessage(bytes))
}

if r.RepositoryName != nil {
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryName, r.RepositoryName)
if err != nil {
return nil, err
}
rawRules = append(rawRules, json.RawMessage(bytes))
}

if r.RepositoryTransfer != nil {
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryTransfer, r.RepositoryTransfer)
if err != nil {
return nil, err
}
rawRules = append(rawRules, json.RawMessage(bytes))
}

if r.RepositoryVisibility != nil {
bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryVisibility, r.RepositoryVisibility)
if err != nil {
return nil, err
}
rawRules = append(rawRules, json.RawMessage(bytes))
}

return json.Marshal(rawRules)
}

// marshalRepositoryRulesetRule is a helper function to marshal a ruleset rule.
//
// TODO: Benchmark the code that uses reflection.
// TODO: Use a generator here instead of reflection if there is a significant performance hit.
func marshalRepositoryRulesetRule[T any](t RepositoryRuleType, params T) ([]byte, error) {
paramsType := reflect.TypeFor[T]()
hasParams := true

switch t {
case RulesetRuleTypeCreation,
RulesetRuleTypeDeletion,
RulesetRuleTypeRequiredLinearHistory,
RulesetRuleTypeRequiredSignatures,
RulesetRuleTypeNonFastForward,
RulesetRuleTypeRepositoryCreate,
RulesetRuleTypeRepositoryDelete,
RulesetRuleTypeRepositoryTransfer:
hasParams = false
case RulesetRuleTypeUpdate:
paramsTyped, ok := any(params).(*UpdateRuleParameters)
if !ok {
return nil, fmt.Errorf("expected UpdateRuleParameters for rule type %v", t)
}
if paramsTyped == nil || *paramsTyped == (UpdateRuleParameters{}) {
hasParams = false
}
}

if paramsType.Kind() == reflect.Pointer && (reflect.ValueOf(params).IsNil() || reflect.ValueOf(params).Elem().IsZero()) {
if !hasParams {
return json.Marshal(repositoryRulesetRuleWrapper{Type: t})
}

Expand Down Expand Up @@ -872,6 +965,28 @@ func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error {
return err
}
}
case RulesetRuleTypeRepositoryCreate:
r.RepositoryCreate = &EmptyRuleParameters{}
case RulesetRuleTypeRepositoryDelete:
r.RepositoryDelete = &EmptyRuleParameters{}
case RulesetRuleTypeRepositoryName:
r.RepositoryName = &SimplePatternRuleParameters{}

if w.Parameters != nil {
if err := json.Unmarshal(w.Parameters, r.RepositoryName); err != nil {
return err
}
}
case RulesetRuleTypeRepositoryTransfer:
r.RepositoryTransfer = &EmptyRuleParameters{}
case RulesetRuleTypeRepositoryVisibility:
r.RepositoryVisibility = &RepositoryVisibilityRuleParameters{}

if w.Parameters != nil {
if err := json.Unmarshal(w.Parameters, r.RepositoryVisibility); err != nil {
return err
}
}
}
}

Expand Down Expand Up @@ -1251,6 +1366,31 @@ func (r *RepositoryRule) UnmarshalJSON(data []byte) error {
}
}

r.Parameters = p
case RulesetRuleTypeRepositoryCreate:
r.Parameters = nil
case RulesetRuleTypeRepositoryDelete:
r.Parameters = nil
case RulesetRuleTypeRepositoryName:
p := &SimplePatternRuleParameters{}

if w.Parameters != nil {
if err := json.Unmarshal(w.Parameters, p); err != nil {
return err
}
}

r.Parameters = p
case RulesetRuleTypeRepositoryTransfer:
r.Parameters = nil
case RulesetRuleTypeRepositoryVisibility:
p := &RepositoryVisibilityRuleParameters{}

if w.Parameters != nil {
if err := json.Unmarshal(w.Parameters, p); err != nil {
return err
}
}
r.Parameters = p
}

Expand Down
Loading
Loading