From bedd4e33eba4068a2686b4299bfce71064500f73 Mon Sep 17 00:00:00 2001 From: Yurii Soldak Date: Sat, 15 Jul 2023 14:06:08 +0200 Subject: [PATCH] Support repository_id in org ruleset conditions (#2825) --- github/github-accessors.go | 12 +- github/github-accessors_test.go | 13 +- github/orgs_rules_test.go | 322 +++++++++++++++++++++++++++++++- github/repos_rules.go | 15 +- 4 files changed, 347 insertions(+), 15 deletions(-) diff --git a/github/github-accessors.go b/github/github-accessors.go index 19d7bd4711..009b1bebef 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -19102,8 +19102,16 @@ func (r *RulesetConditions) GetRefName() *RulesetRefConditionParameters { return r.RefName } +// GetRepositoryID returns the RepositoryID field. +func (r *RulesetConditions) GetRepositoryID() *RulesetRepositoryIDsConditionParameters { + if r == nil { + return nil + } + return r.RepositoryID +} + // GetRepositoryName returns the RepositoryName field. -func (r *RulesetConditions) GetRepositoryName() *RulesetRepositoryConditionParameters { +func (r *RulesetConditions) GetRepositoryName() *RulesetRepositoryNamesConditionParameters { if r == nil { return nil } @@ -19127,7 +19135,7 @@ func (r *RulesetLinks) GetSelf() *RulesetLink { } // GetProtected returns the Protected field if it's non-nil, zero value otherwise. -func (r *RulesetRepositoryConditionParameters) GetProtected() bool { +func (r *RulesetRepositoryNamesConditionParameters) GetProtected() bool { if r == nil || r.Protected == nil { return false } diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 5cdf5ff592..4671670ce0 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -22270,6 +22270,13 @@ func TestRulesetConditions_GetRefName(tt *testing.T) { r.GetRefName() } +func TestRulesetConditions_GetRepositoryID(tt *testing.T) { + r := &RulesetConditions{} + r.GetRepositoryID() + r = nil + r.GetRepositoryID() +} + func TestRulesetConditions_GetRepositoryName(tt *testing.T) { r := &RulesetConditions{} r.GetRepositoryName() @@ -22294,11 +22301,11 @@ func TestRulesetLinks_GetSelf(tt *testing.T) { r.GetSelf() } -func TestRulesetRepositoryConditionParameters_GetProtected(tt *testing.T) { +func TestRulesetRepositoryNamesConditionParameters_GetProtected(tt *testing.T) { var zeroValue bool - r := &RulesetRepositoryConditionParameters{Protected: &zeroValue} + r := &RulesetRepositoryNamesConditionParameters{Protected: &zeroValue} r.GetProtected() - r = &RulesetRepositoryConditionParameters{} + r = &RulesetRepositoryNamesConditionParameters{} r.GetProtected() r = nil r.GetProtected() diff --git a/github/orgs_rules_test.go b/github/orgs_rules_test.go index 7719e142fb..48c6c5e2bd 100644 --- a/github/orgs_rules_test.go +++ b/github/orgs_rules_test.go @@ -71,7 +71,7 @@ func TestOrganizationsService_GetAllOrganizationRulesets(t *testing.T) { }) } -func TestOrganizationsService_CreateOrganizationRuleset(t *testing.T) { +func TestOrganizationsService_CreateOrganizationRuleset_RepoNames(t *testing.T) { client, mux, _, teardown := setup() defer teardown() @@ -227,7 +227,7 @@ func TestOrganizationsService_CreateOrganizationRuleset(t *testing.T) { Include: []string{"refs/heads/main", "refs/heads/master"}, Exclude: []string{"refs/heads/dev*"}, }, - RepositoryName: &RulesetRepositoryConditionParameters{ + RepositoryName: &RulesetRepositoryNamesConditionParameters{ Include: []string{"important_repository", "another_important_repository"}, Exclude: []string{"unimportant_repository"}, Protected: Bool(true), @@ -313,7 +313,7 @@ func TestOrganizationsService_CreateOrganizationRuleset(t *testing.T) { Include: []string{"refs/heads/main", "refs/heads/master"}, Exclude: []string{"refs/heads/dev*"}, }, - RepositoryName: &RulesetRepositoryConditionParameters{ + RepositoryName: &RulesetRepositoryNamesConditionParameters{ Include: []string{"important_repository", "another_important_repository"}, Exclude: []string{"unimportant_repository"}, Protected: Bool(true), @@ -392,6 +392,316 @@ func TestOrganizationsService_CreateOrganizationRuleset(t *testing.T) { }) } +func TestOrganizationsService_CreateOrganizationRuleset_RepoIDs(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 21, + "name": "ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_id": { + "repository_ids": [ 123, 456 ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + } + ] + }`) + }) + + ctx := context.Background() + ruleset, _, err := client.Organizations.CreateOrganizationRuleset(ctx, "o", &Ruleset{ + ID: 21, + Name: "ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Int64(234), + ActorType: String("Team"), + }, + }, + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryID: &RulesetRepositoryIDsConditionParameters{ + RepositoryIDs: []int64{123, 456}, + }, + }, + Rules: []*RepositoryRule{ + NewCreationRule(), + NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }), + NewDeletionRule(), + NewRequiredLinearHistoryRule(), + NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }), + NewRequiredSignaturesRule(), + NewPullRequestRule(&PullRequestRuleParameters{ + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + DismissStaleReviewsOnPush: true, + }), + NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []RuleRequiredStatusChecks{ + { + Context: "test", + IntegrationID: Int64(1), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }), + NewNonFastForwardRule(), + NewCommitMessagePatternRule(&RulePatternParameters{ + Name: String("avoid test commits"), + Negate: Bool(true), + Operator: "starts_with", + Pattern: "[test]", + }), + NewCommitAuthorEmailPatternRule(&RulePatternParameters{ + Operator: "contains", + Pattern: "github", + }), + NewCommitterEmailPatternRule(&RulePatternParameters{ + Name: String("avoid commit emails"), + Negate: Bool(true), + Operator: "ends_with", + Pattern: "abc", + }), + NewBranchNamePatternRule(&RulePatternParameters{ + Name: String("avoid branch names"), + Negate: Bool(true), + Operator: "regex", + Pattern: "github$", + }), + NewTagNamePatternRule(&RulePatternParameters{ + Name: String("avoid tag names"), + Negate: Bool(true), + Operator: "contains", + Pattern: "github", + }), + }, + }) + if err != nil { + t.Errorf("Organizations.CreateOrganizationRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 21, + Name: "ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Int64(234), + ActorType: String("Team"), + }, + }, + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryID: &RulesetRepositoryIDsConditionParameters{ + RepositoryIDs: []int64{123, 456}, + }, + }, + Rules: []*RepositoryRule{ + NewCreationRule(), + NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }), + NewDeletionRule(), + NewRequiredLinearHistoryRule(), + NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }), + NewRequiredSignaturesRule(), + NewPullRequestRule(&PullRequestRuleParameters{ + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + DismissStaleReviewsOnPush: true, + }), + NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []RuleRequiredStatusChecks{ + { + Context: "test", + IntegrationID: Int64(1), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }), + NewNonFastForwardRule(), + NewCommitMessagePatternRule(&RulePatternParameters{ + Name: String("avoid test commits"), + Negate: Bool(true), + Operator: "starts_with", + Pattern: "[test]", + }), + NewCommitAuthorEmailPatternRule(&RulePatternParameters{ + Operator: "contains", + Pattern: "github", + }), + NewCommitterEmailPatternRule(&RulePatternParameters{ + Name: String("avoid commit emails"), + Negate: Bool(true), + Operator: "ends_with", + Pattern: "abc", + }), + NewBranchNamePatternRule(&RulePatternParameters{ + Name: String("avoid branch names"), + Negate: Bool(true), + Operator: "regex", + Pattern: "github$", + }), + NewTagNamePatternRule(&RulePatternParameters{ + Name: String("avoid tag names"), + Negate: Bool(true), + Operator: "contains", + Pattern: "github", + }), + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Organizations.CreateOrganizationRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateOrganizationRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateOrganizationRuleset(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestOrganizationsService_GetOrganizationRuleset(t *testing.T) { client, mux, _, teardown := setup() defer teardown() @@ -464,7 +774,7 @@ func TestOrganizationsService_GetOrganizationRuleset(t *testing.T) { Include: []string{"refs/heads/main", "refs/heads/master"}, Exclude: []string{"refs/heads/dev*"}, }, - RepositoryName: &RulesetRepositoryConditionParameters{ + RepositoryName: &RulesetRepositoryNamesConditionParameters{ Include: []string{"important_repository", "another_important_repository"}, Exclude: []string{"unimportant_repository"}, Protected: Bool(true), @@ -549,7 +859,7 @@ func TestOrganizationsService_UpdateOrganizationRuleset(t *testing.T) { Include: []string{"refs/heads/main", "refs/heads/master"}, Exclude: []string{"refs/heads/dev*"}, }, - RepositoryName: &RulesetRepositoryConditionParameters{ + RepositoryName: &RulesetRepositoryNamesConditionParameters{ Include: []string{"important_repository", "another_important_repository"}, Exclude: []string{"unimportant_repository"}, Protected: Bool(true), @@ -581,7 +891,7 @@ func TestOrganizationsService_UpdateOrganizationRuleset(t *testing.T) { Include: []string{"refs/heads/main", "refs/heads/master"}, Exclude: []string{"refs/heads/dev*"}, }, - RepositoryName: &RulesetRepositoryConditionParameters{ + RepositoryName: &RulesetRepositoryNamesConditionParameters{ Include: []string{"important_repository", "another_important_repository"}, Exclude: []string{"unimportant_repository"}, Protected: Bool(true), diff --git a/github/repos_rules.go b/github/repos_rules.go index 9299d3e7f3..ee9c82e714 100644 --- a/github/repos_rules.go +++ b/github/repos_rules.go @@ -34,17 +34,24 @@ type RulesetRefConditionParameters struct { Exclude []string `json:"exclude"` } -// RulesetRepositoryConditionParameters represents the conditions object for repository_names. -type RulesetRepositoryConditionParameters struct { +// RulesetRepositoryNamesConditionParameters represents the conditions object for repository_names. +type RulesetRepositoryNamesConditionParameters struct { Include []string `json:"include,omitempty"` Exclude []string `json:"exclude,omitempty"` Protected *bool `json:"protected,omitempty"` } +// RulesetRepositoryIDsConditionParameters represents the conditions object for repository_ids. +type RulesetRepositoryIDsConditionParameters struct { + RepositoryIDs []int64 `json:"repository_ids,omitempty"` +} + // RulesetCondition represents the conditions object in a ruleset. +// Set either RepositoryName or RepositoryID, not both. type RulesetConditions struct { - RefName *RulesetRefConditionParameters `json:"ref_name,omitempty"` - RepositoryName *RulesetRepositoryConditionParameters `json:"repository_name,omitempty"` + RefName *RulesetRefConditionParameters `json:"ref_name,omitempty"` + RepositoryName *RulesetRepositoryNamesConditionParameters `json:"repository_name,omitempty"` + RepositoryID *RulesetRepositoryIDsConditionParameters `json:"repository_id,omitempty"` } // RulePatternParameters represents the rule pattern parameters.