From 7d2dffe5246977e4d9140caa54df072958122240 Mon Sep 17 00:00:00 2001 From: Martyn Cross Date: Thu, 5 Sep 2024 15:41:22 +0700 Subject: [PATCH 1/4] feat: add repository ruleset merge queue support --- github/resource_github_repository_ruleset.go | 52 +++++++++++++++++++ ...resource_github_repository_ruleset_test.go | 10 ++++ github/respository_rules_utils.go | 16 ++++++ 3 files changed, 78 insertions(+) diff --git a/github/resource_github_repository_ruleset.go b/github/resource_github_repository_ruleset.go index 5fe4c2b37..9c25fc78c 100644 --- a/github/resource_github_repository_ruleset.go +++ b/github/resource_github_repository_ruleset.go @@ -258,6 +258,58 @@ func resourceGithubRepositoryRuleset() *schema.Resource { }, }, }, + "merge_queue": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "Merges must be performed via a merge queue.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "check_response_timeout_minutes": { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 360), "check_response_timeout_minutes"), + Description: "Maximum time for a required status check to report a conclusion. After this much time has elapsed, checks that have not reported a conclusion will be assumed to have failed.", + }, + "grouping_strategy": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"ALLGREEN", "HEADGREEN"}, false), "grouping_strategy"), + Description: "When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. Can be one of: ALLGREEN, HEADGREEN.", + }, + "max_entries_to_build": { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 100), "max_entries_to_merge"), + Description: "Limit the number of queued pull requests requesting checks and workflow runs at the same time.", + }, + "max_entries_to_merge": { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 100), "max_entries_to_merge"), + Description: "The maximum number of PRs that will be merged together in a group.", + }, + "merge_method": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"MERGE", "SQUASH", "REBASE"}, false), "merge_method"), + Description: "Method to use when merging changes from queued pull requests. Can be one of: MERGE, SQUASH, REBASE.", + }, + "min_entries_to_merge": { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 100), "min_entries_to_merge"), + Description: "The minimum number of PRs that will be merged together in a group.", + }, + "min_entries_to_merge_wait_minutes": { + Type: schema.TypeInt, + Optional: true, + ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 360), "min_entries_to_merge_wait_minutes"), + Description: "The time merge queue should wait after the first PR is added to the queue for the minimum group size to be met. After this time has elapsed, the minimum group size will be ignored and a smaller group will be merged.", + }, + }, + }, + }, "non_fast_forward": { Type: schema.TypeBool, Optional: true, diff --git a/github/resource_github_repository_ruleset_test.go b/github/resource_github_repository_ruleset_test.go index 35bc80fbd..2efbcdb65 100644 --- a/github/resource_github_repository_ruleset_test.go +++ b/github/resource_github_repository_ruleset_test.go @@ -55,6 +55,16 @@ func TestGithubRepositoryRulesets(t *testing.T) { required_signatures = false + merge_queue { + check_response_timeout_minutes = 10 + grouping_strategy = "ALLGREEN" + max_entries_to_build = 5 + max_entries_to_merge = 5 + merge_method = "MERGE" + min_entries_to_merge = 1 + min_entries_to_merge_wait_minutes = 60 + } + pull_request { required_approving_review_count = 2 required_review_thread_resolution = true diff --git a/github/respository_rules_utils.go b/github/respository_rules_utils.go index 9d9516175..d5c8ba6e4 100644 --- a/github/respository_rules_utils.go +++ b/github/respository_rules_utils.go @@ -300,6 +300,22 @@ func expandRules(input []interface{}, org bool) []*github.RepositoryRule { rulesSlice = append(rulesSlice, github.NewPullRequestRule(params)) } + // Merge queue rule + if v, ok := rulesMap["merge_queue"].([]interface{}); ok && len(v) != 0 { + mergeQueueMap := v[0].(map[string]interface{}) + params := &github.MergeQueueRuleParameters{ + CheckResponseTimeoutMinutes: mergeQueueMap["check_response_timeout_minutes"].(int), + GroupingStrategy: mergeQueueMap["grouping_strategy"].(string), + MaxEntriesToBuild: mergeQueueMap["max_entries_to_build"].(int), + MaxEntriesToMerge: mergeQueueMap["max_entries_to_merge"].(int), + MergeMethod: mergeQueueMap["merge_method"].(string), + MinEntriesToMerge: mergeQueueMap["min_entries_to_merge"].(int), + MinEntriesToMergeWaitMinutes: mergeQueueMap["min_entries_to_merge_wait_minutes"].(int), + } + + rulesSlice = append(rulesSlice, github.NewMergeQueueRule(params)) + } + // Required status checks rule if v, ok := rulesMap["required_status_checks"].([]interface{}); ok && len(v) != 0 { requiredStatusMap := v[0].(map[string]interface{}) From 0def5fb973b8c19666896f55b2afeb543ca67aea Mon Sep 17 00:00:00 2001 From: Martyn Cross Date: Fri, 6 Sep 2024 15:14:46 +0700 Subject: [PATCH 2/4] docs: add docs for repository rulesets merge queue --- .../docs/r/repository_ruleset.html.markdown | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/website/docs/r/repository_ruleset.html.markdown b/website/docs/r/repository_ruleset.html.markdown index 09ff72ee1..8a63ab510 100644 --- a/website/docs/r/repository_ruleset.html.markdown +++ b/website/docs/r/repository_ruleset.html.markdown @@ -74,7 +74,6 @@ resource "github_repository_ruleset" "example" { The `rules` block supports the following: - * `branch_name_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the branch_name_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. Conflicts with `tag_name_pattern` as it only applied to rulesets with target `branch`. (see [below for nested schema](#rules.branch_name_pattern)) * `commit_author_email_pattern` - (Optional) (Block List, Max: 1) Parameters to be used for the commit_author_email_pattern rule. This rule only applies to repositories within an enterprise, it cannot be applied to repositories owned by individuals or regular organizations. (see [below for nested schema](#rules.commit_author_email_pattern)) @@ -89,6 +88,8 @@ The `rules` block supports the following: * `non_fast_forward` - (Optional) (Boolean) Prevent users with push access from force pushing to branches. +* `merge_queue` - (Optional) (Block List, Max: 1) Merges must be performed via a merge queue. + * `pull_request` - (Optional) (Block List, Max: 1) Require all commits be made to a non-target branch and submitted via a pull request before they can be merged. (see [below for nested schema](#rules.pull_request)) * `required_deployments` - (Optional) (Block List, Max: 1) Choose which environments must be successfully deployed to before branches can be merged into a branch that matches this rule. (see [below for nested schema](#rules.required_deployments)) @@ -117,7 +118,6 @@ The `rules` block supports the following: * `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - #### rules.commit_author_email_pattern #### * `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. @@ -128,7 +128,6 @@ The `rules` block supports the following: * `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - #### rules.commit_message_pattern #### * `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. @@ -139,7 +138,6 @@ The `rules` block supports the following: * `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. - #### rules.committer_email_pattern #### * `operator` - (Required) (String) The operator to use for matching. Can be one of: `starts_with`, `ends_with`, `contains`, `regex`. @@ -150,6 +148,21 @@ The `rules` block supports the following: * `negate` - (Optional) (Boolean) If true, the rule will fail if the pattern matches. +#### rules.merge_queue #### + +* `check_response_timeout_minutes` - (Required) (Number)Maximum time for a required status check to report a conclusion. After this much time has elapsed, checks that have not reported a conclusion will be assumed to have failed. + +* `grouping_strategy` - (Required) (String)When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. Can be one of: ALLGREEN, HEADGREEN. + +* `max_entries_to_build` - (Required) (Number) Limit the number of queued pull requests requesting checks and workflow runs at the same time. + +* `max_entries_to_merge` - (Required) (Number) Limit the number of queued pull requests requesting checks and workflow runs at the same time. + +* `merge_method` - (Required) (String) Method to use when merging changes from queued pull requests. Can be one of: MERGE, SQUASH, REBASE. + +* `min_entries_to_merge` - (Required) (Number) The minimum number of PRs that will be merged together in a group. + +* `min_entries_to_merge_wait_minutes` - (Required) (Number) The time merge queue should wait after the first PR is added to the queue for the minimum group size to be met. After this time has elapsed, the minimum group size will be ignored and a smaller group will be merged. #### rules.pull_request #### @@ -163,12 +176,10 @@ The `rules` block supports the following: * `required_review_thread_resolution` - (Optional) (Boolean) All conversations on code must be resolved before a pull request can be merged. Defaults to `false`. - #### rules.required_deployments #### * `required_deployment_environments` - (Required) (List of String) The environments that must be successfully deployed to before branches can be merged. - #### rules.required_status_checks #### * `required_check` - (Required) (Block Set, Min: 1) Status checks that are required. Several can be defined. (see [below for nested schema](#rules.required_status_checks.required_check)) @@ -214,13 +225,13 @@ The `rules` block supports the following: * `bypass_mode` - (Optional) (String) When the specified actor can bypass the ruleset. pull_request means that an actor can only bypass rules on pull requests. Can be one of: `always`, `pull_request`. ~> Note: at the time of writing this, the following actor types correspond to the following actor IDs: + * `OrganizationAdmin` -> `1` * `RepositoryRole` (This is the actor type, the following are the base repository roles and their associated IDs.) * `maintain` -> `2` * `write` -> `4` * `admin` -> `5` - #### conditions #### * `ref_name` - (Required) (Block List, Min: 1, Max: 1) (see [below for nested schema](#conditions.ref_name)) @@ -235,14 +246,12 @@ The `rules` block supports the following: The following additional attributes are exported: - * `etag` (String) * `node_id` (String) GraphQL global node id for use with v4 API. * `ruleset_id` (Number) GitHub ID for the ruleset. - ## Import GitHub Repository Rulesets can be imported using the GitHub repository name and ruleset ID e.g. From ae3b259f98b567a8d027ccd84dc866c49a63f2f7 Mon Sep 17 00:00:00 2001 From: Martyn Cross Date: Sun, 8 Sep 2024 11:00:38 +0700 Subject: [PATCH 3/4] feat: add default values for merge_queue on rulesets --- github/resource_github_repository_ruleset.go | 21 ++++++++++++------- .../docs/r/repository_ruleset.html.markdown | 14 ++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/github/resource_github_repository_ruleset.go b/github/resource_github_repository_ruleset.go index 9c25fc78c..cac663024 100644 --- a/github/resource_github_repository_ruleset.go +++ b/github/resource_github_repository_ruleset.go @@ -268,44 +268,51 @@ func resourceGithubRepositoryRuleset() *schema.Resource { "check_response_timeout_minutes": { Type: schema.TypeInt, Optional: true, + Default: 60, ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 360), "check_response_timeout_minutes"), - Description: "Maximum time for a required status check to report a conclusion. After this much time has elapsed, checks that have not reported a conclusion will be assumed to have failed.", + Description: "Maximum time for a required status check to report a conclusion. After this much time has elapsed, checks that have not reported a conclusion will be assumed to have failed. Defaults to `60`.", }, "grouping_strategy": { Type: schema.TypeString, Optional: true, + Default: "ALLGREEN", ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"ALLGREEN", "HEADGREEN"}, false), "grouping_strategy"), - Description: "When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. Can be one of: ALLGREEN, HEADGREEN.", + Description: "When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. Can be one of: ALLGREEN, HEADGREEN. Defaults to `ALLGREEN`.", }, "max_entries_to_build": { Type: schema.TypeInt, Optional: true, + Default: 5, ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 100), "max_entries_to_merge"), - Description: "Limit the number of queued pull requests requesting checks and workflow runs at the same time.", + Description: "Limit the number of queued pull requests requesting checks and workflow runs at the same time. Defaults to `5`.", }, "max_entries_to_merge": { Type: schema.TypeInt, Optional: true, + Default: 5, ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 100), "max_entries_to_merge"), - Description: "The maximum number of PRs that will be merged together in a group.", + Description: "The maximum number of PRs that will be merged together in a group. Defaults to `5`.", }, "merge_method": { Type: schema.TypeString, Optional: true, + Default: "MERGE", ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"MERGE", "SQUASH", "REBASE"}, false), "merge_method"), - Description: "Method to use when merging changes from queued pull requests. Can be one of: MERGE, SQUASH, REBASE.", + Description: "Method to use when merging changes from queued pull requests. Can be one of: MERGE, SQUASH, REBASE. Defaults to `MERGE`.", }, "min_entries_to_merge": { Type: schema.TypeInt, Optional: true, + Default: 1, ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 100), "min_entries_to_merge"), - Description: "The minimum number of PRs that will be merged together in a group.", + Description: "The minimum number of PRs that will be merged together in a group. Defaults to `1`.", }, "min_entries_to_merge_wait_minutes": { Type: schema.TypeInt, Optional: true, + Default: 5, ValidateDiagFunc: toDiagFunc(validation.IntBetween(0, 360), "min_entries_to_merge_wait_minutes"), - Description: "The time merge queue should wait after the first PR is added to the queue for the minimum group size to be met. After this time has elapsed, the minimum group size will be ignored and a smaller group will be merged.", + Description: "The time merge queue should wait after the first PR is added to the queue for the minimum group size to be met. After this time has elapsed, the minimum group size will be ignored and a smaller group will be merged. Defaults to `5`.", }, }, }, diff --git a/website/docs/r/repository_ruleset.html.markdown b/website/docs/r/repository_ruleset.html.markdown index 8a63ab510..173ffcf23 100644 --- a/website/docs/r/repository_ruleset.html.markdown +++ b/website/docs/r/repository_ruleset.html.markdown @@ -150,19 +150,19 @@ The `rules` block supports the following: #### rules.merge_queue #### -* `check_response_timeout_minutes` - (Required) (Number)Maximum time for a required status check to report a conclusion. After this much time has elapsed, checks that have not reported a conclusion will be assumed to have failed. +* `check_response_timeout_minutes` - (Required) (Number)Maximum time for a required status check to report a conclusion. After this much time has elapsed, checks that have not reported a conclusion will be assumed to have failed. Defaults to `60`. -* `grouping_strategy` - (Required) (String)When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. Can be one of: ALLGREEN, HEADGREEN. +* `grouping_strategy` - (Required) (String)When set to ALLGREEN, the merge commit created by merge queue for each PR in the group must pass all required checks to merge. When set to HEADGREEN, only the commit at the head of the merge group, i.e. the commit containing changes from all of the PRs in the group, must pass its required checks to merge. Can be one of: ALLGREEN, HEADGREEN. Defaults to `ALLGREEN`. -* `max_entries_to_build` - (Required) (Number) Limit the number of queued pull requests requesting checks and workflow runs at the same time. +* `max_entries_to_build` - (Required) (Number) Limit the number of queued pull requests requesting checks and workflow runs at the same time. Defaults to `5`. -* `max_entries_to_merge` - (Required) (Number) Limit the number of queued pull requests requesting checks and workflow runs at the same time. +* `max_entries_to_merge` - (Required) (Number) Limit the number of queued pull requests requesting checks and workflow runs at the same time. Defaults to `5`. -* `merge_method` - (Required) (String) Method to use when merging changes from queued pull requests. Can be one of: MERGE, SQUASH, REBASE. +* `merge_method` - (Required) (String) Method to use when merging changes from queued pull requests. Can be one of: MERGE, SQUASH, REBASE. Defaults to `MERGE`. -* `min_entries_to_merge` - (Required) (Number) The minimum number of PRs that will be merged together in a group. +* `min_entries_to_merge` - (Required) (Number) The minimum number of PRs that will be merged together in a group. Defaults to `1`. -* `min_entries_to_merge_wait_minutes` - (Required) (Number) The time merge queue should wait after the first PR is added to the queue for the minimum group size to be met. After this time has elapsed, the minimum group size will be ignored and a smaller group will be merged. +* `min_entries_to_merge_wait_minutes` - (Required) (Number) The time merge queue should wait after the first PR is added to the queue for the minimum group size to be met. After this time has elapsed, the minimum group size will be ignored and a smaller group will be merged. Defaults to `5`. #### rules.pull_request #### From 0421ea5a5bdb9b40a382d5abc411c55da69738b6 Mon Sep 17 00:00:00 2001 From: Martyn Cross Date: Thu, 24 Oct 2024 09:34:34 +0700 Subject: [PATCH 4/4] test: add merge_queue block to ruleset create and import tests --- ...resource_github_repository_ruleset_test.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/github/resource_github_repository_ruleset_test.go b/github/resource_github_repository_ruleset_test.go index 2efbcdb65..42922fffc 100644 --- a/github/resource_github_repository_ruleset_test.go +++ b/github/resource_github_repository_ruleset_test.go @@ -73,6 +73,16 @@ func TestGithubRepositoryRulesets(t *testing.T) { require_last_push_approval = true } + merge_queue { + check_response_timeout_minutes = 30 + grouping_strategy = "HEADGREEN" + max_entries_to_build = 4 + max_entries_to_merge = 4 + merge_method = SQUASH + min_entries_to_merge = 2 + min_entries_to_merge_wait_minutes = 10 + } + required_status_checks { required_check { @@ -323,6 +333,16 @@ func TestGithubRepositoryRulesets(t *testing.T) { require_last_push_approval = true } + merge_queue { + check_response_timeout_minutes = 30 + grouping_strategy = "HEADGREEN" + max_entries_to_build = 4 + max_entries_to_merge = 4 + merge_method = SQUASH + min_entries_to_merge = 2 + min_entries_to_merge_wait_minutes = 10 + } + required_status_checks { required_check {