From e23d96b4fcc307ce076fcdc3f95ad2163032f493 Mon Sep 17 00:00:00 2001 From: omer-vishlitzky Date: Wed, 15 Jul 2026 17:37:10 +0300 Subject: [PATCH 1/4] OSAC-2333: Enable merge queues on 5 core repos Adds a `merge_queue` variable to the common_repository module and enables it on fulfillment-service, osac-operator, osac-aap, osac-installer, and osac-test-infra. Co-Authored-By: Claude Opus 4.6 (1M context) --- modules/common_repository/main.tf | 27 ++++++++++++++++++++++++++ modules/common_repository/variables.tf | 12 ++++++++++++ repositories.tf | 5 +++++ 3 files changed, 44 insertions(+) diff --git a/modules/common_repository/main.tf b/modules/common_repository/main.tf index fa0f0b1..e5e1b7d 100644 --- a/modules/common_repository/main.tf +++ b/modules/common_repository/main.tf @@ -105,6 +105,33 @@ resource "github_branch_protection" "repo_protection" { depends_on = [github_repository.repo, github_repository_collaborators.repo_collaborators] } +resource "github_repository_ruleset" "merge_queue" { + count = var.merge_queue != null ? 1 : 0 + name = "merge-queue" + repository = github_repository.repo.name + target = "branch" + enforcement = "active" + + conditions { + ref_name { + include = ["~DEFAULT_BRANCH"] + exclude = [] + } + } + + rules { + merge_queue { + merge_method = var.merge_queue.merge_method + min_entries_to_merge = var.merge_queue.min_entries_to_merge + max_entries_to_merge = var.merge_queue.max_entries_to_merge + check_response_timeout_minutes = var.merge_queue.check_response_timeout_minutes + grouping_strategy = var.merge_queue.grouping_strategy + } + } + + depends_on = [github_repository.repo] +} + resource "github_repository_environment" "env" { for_each = { for env in var.environments : diff --git a/modules/common_repository/variables.tf b/modules/common_repository/variables.tf index 4512a3a..223d653 100644 --- a/modules/common_repository/variables.tf +++ b/modules/common_repository/variables.tf @@ -175,6 +175,18 @@ variable "environments" { } } +variable "merge_queue" { + description = "Enable GitHub merge queue for the default branch. When set, a repository ruleset is created that requires PRs to pass through the merge queue before merging." + type = object({ + merge_method = optional(string, "SQUASH") + min_entries_to_merge = optional(number, 1) + max_entries_to_merge = optional(number, 5) + check_response_timeout_minutes = optional(number, 90) + grouping_strategy = optional(string, "ALLGREEN") + }) + default = null +} + variable "all_members_permission" { description = "Permission for all organization members" type = string diff --git a/repositories.tf b/repositories.tf index 0a1f401..22e22b4 100644 --- a/repositories.tf +++ b/repositories.tf @@ -112,6 +112,7 @@ module "repo_fulfillment_service" { ] push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] + merge_queue = {} pages = { build_type = "workflow" source = { @@ -143,6 +144,7 @@ module "repo_cloudkit_operator" { ] push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] + merge_queue = {} } module "repo_cloudkit_aap" { @@ -167,6 +169,7 @@ module "repo_cloudkit_aap" { ] push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] + merge_queue = {} } module "repo_cloudkit_aap_ee" { @@ -218,6 +221,7 @@ module "repo_osac_installer" { required_approvals = null push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] + merge_queue = {} } module "repo_enhancement_proposals" { @@ -247,6 +251,7 @@ module "repo_osac_test_infra" { required_approvals = null push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] + merge_queue = {} } module "repo_massopencloud_templates" { From 305546712112afb44e0aa53c2533092e5dfc41b0 Mon Sep 17 00:00:00 2001 From: omer-vishlitzky Date: Wed, 15 Jul 2026 17:39:24 +0300 Subject: [PATCH 2/4] Disable strict status checks on repos with merge queues The merge queue automatically tests PRs against the latest main, making strict (require branch to be up-to-date) redundant and adding unnecessary rebase friction. Co-Authored-By: Claude Opus 4.6 (1M context) --- modules/common_repository/main.tf | 2 +- modules/common_repository/variables.tf | 6 +++++ repositories.tf | 35 +++++++++++++++----------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/modules/common_repository/main.tf b/modules/common_repository/main.tf index e5e1b7d..d0d534f 100644 --- a/modules/common_repository/main.tf +++ b/modules/common_repository/main.tf @@ -98,7 +98,7 @@ resource "github_branch_protection" "repo_protection" { } required_status_checks { - strict = true + strict = var.strict_status_checks contexts = var.required_status_checks } diff --git a/modules/common_repository/variables.tf b/modules/common_repository/variables.tf index 223d653..a08ea22 100644 --- a/modules/common_repository/variables.tf +++ b/modules/common_repository/variables.tf @@ -26,6 +26,12 @@ variable "required_status_checks" { default = [] } +variable "strict_status_checks" { + description = "Require the PR branch to be up to date with the base branch before merging. Disable when using merge queues, which handle this automatically." + type = bool + default = true +} + variable "visibility" { description = "Repository visibility (public or private)" type = string diff --git a/repositories.tf b/repositories.tf index 22e22b4..bd470d5 100644 --- a/repositories.tf +++ b/repositories.tf @@ -110,9 +110,10 @@ module "repo_fulfillment_service" { "ci/prow/unit", "e2e-vmaas-full-install / e2e" ] - push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] - environments = [{ name = "e2e-test" }] - merge_queue = {} + push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] + environments = [{ name = "e2e-test" }] + merge_queue = {} + strict_status_checks = false pages = { build_type = "workflow" source = { @@ -142,9 +143,10 @@ module "repo_cloudkit_operator" { "ci/prow/temp", "e2e-vmaas-full-install / e2e" ] - push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] - environments = [{ name = "e2e-test" }] - merge_queue = {} + push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] + environments = [{ name = "e2e-test" }] + merge_queue = {} + strict_status_checks = false } module "repo_cloudkit_aap" { @@ -167,9 +169,10 @@ module "repo_cloudkit_aap" { "ci/prow/temp", "e2e-vmaas-full-install / e2e" ] - push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] - environments = [{ name = "e2e-test" }] - merge_queue = {} + push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] + environments = [{ name = "e2e-test" }] + merge_queue = {} + strict_status_checks = false } module "repo_cloudkit_aap_ee" { @@ -219,9 +222,10 @@ module "repo_osac_installer" { ] required_approvals = null - push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] - environments = [{ name = "e2e-test" }] - merge_queue = {} + push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] + environments = [{ name = "e2e-test" }] + merge_queue = {} + strict_status_checks = false } module "repo_enhancement_proposals" { @@ -249,9 +253,10 @@ module "repo_osac_test_infra" { } ] required_approvals = null - push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] - environments = [{ name = "e2e-test" }] - merge_queue = {} + push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] + environments = [{ name = "e2e-test" }] + merge_queue = {} + strict_status_checks = false } module "repo_massopencloud_templates" { From 78429c5276de3c17dfec5b6829a0a2e95d031844 Mon Sep 17 00:00:00 2001 From: omer-vishlitzky Date: Wed, 15 Jul 2026 17:54:24 +0300 Subject: [PATCH 3/4] Add merge_queue validations and regenerate module README - Validate merge_method (MERGE/SQUASH/REBASE) and grouping_strategy (ALLGREEN/HEADGREEN) at plan time instead of failing at apply - Regenerate README.md with terraform-docs to include new variables and resources Co-Authored-By: Claude Opus 4.6 (1M context) --- modules/common_repository/README.md | 20 +++++++++++++++----- modules/common_repository/variables.tf | 10 ++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/common_repository/README.md b/modules/common_repository/README.md index 6b66bd5..7ddfa01 100644 --- a/modules/common_repository/README.md +++ b/modules/common_repository/README.md @@ -32,38 +32,48 @@ module "repo_docs" { ## Requirements | Name | Version | -|------|---------| +| ---- | ------- | | [terraform](#requirement\_terraform) | >= 1.9.0 | | [github](#requirement\_github) | ~> 6.0 | ## Providers | Name | Version | -|------|---------| +| ---- | ------- | | [github](#provider\_github) | ~> 6.0 | ## Resources | Name | Type | -|------|------| +| ---- | ---- | | [github_branch_protection.repo_protection](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection) | resource | | [github_issue_label.repo_labels](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/issue_label) | resource | | [github_repository.repo](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository) | resource | | [github_repository_collaborators.repo_collaborators](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_collaborators) | resource | +| [github_repository_environment.env](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_environment) | resource | +| [github_repository_ruleset.merge_queue](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_ruleset) | resource | ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| +| ---- | ----------- | ---- | ------- | :------: | | [all\_members\_permission](#input\_all\_members\_permission) | Permission for all organization members | `string` | `"triage"` | no | +| [allow\_merge\_commit](#input\_allow\_merge\_commit) | Allow merge commits on pull requests | `bool` | `true` | no | +| [allow\_rebase\_merge](#input\_allow\_rebase\_merge) | Allow rebase merging on pull requests | `bool` | `true` | no | +| [allow\_squash\_merge](#input\_allow\_squash\_merge) | Allow squash merging on pull requests | `bool` | `true` | no | +| [archived](#input\_archived) | Whether the repository is archived | `bool` | `false` | no | | [branch\_protection](#input\_branch\_protection) | Configure branch protection if true | `bool` | `true` | no | -| [description](#input\_description) | Repository description | `string` | `""` | no | +| [description](#input\_description) | Repository description | `string` | n/a | yes | +| [environments](#input\_environments) | GitHub environments to create for this repository |
list(object({
name = string
reviewers = optional(object({
teams = optional(list(string), [])
users = optional(list(string), [])
}))
deployment_branch_policy = optional(object({
protected_branches = optional(bool, true)
custom_branch_policies = optional(bool, false)
}))
}))
| `[]` | no | | [is\_template](#input\_is\_template) | Set this to true if this is a template repository | `bool` | `false` | no | | [labels](#input\_labels) | List of labels to configure on the repository |
list(object({
name = string
color = string
description = string
}))
| `null` | no | +| [merge\_queue](#input\_merge\_queue) | Enable GitHub merge queue for the default branch. When set, a repository ruleset is created that requires PRs to pass through the merge queue before merging. |
object({
merge_method = optional(string, "SQUASH")
min_entries_to_merge = optional(number, 1)
max_entries_to_merge = optional(number, 5)
check_response_timeout_minutes = optional(number, 90)
grouping_strategy = optional(string, "ALLGREEN")
})
| `null` | no | | [name](#input\_name) | The name of the repository | `string` | n/a | yes | | [pages](#input\_pages) | Configuration for github pages |
object({
source = optional(object({
branch = string
path = string
}))
build_type = optional(string, "legacy")
cname = optional(string)
})
| `null` | no | +| [push\_allowances](#input\_push\_allowances) | Actors allowed to push to the protected branch. When set, restricts both direct pushes and PR merges to these actors only. Actor names must begin with '/' for users or 'org-name/' for teams. | `list(string)` | `null` | no | | [required\_approvals](#input\_required\_approvals) | Number of approvals required before merging a pull request | `number` | `1` | no | | [required\_status\_checks](#input\_required\_status\_checks) | A list of status checks that must pass before a PR can merge | `list(string)` | `[]` | no | +| [strict\_status\_checks](#input\_strict\_status\_checks) | Require the PR branch to be up to date with the base branch before merging. Disable when using merge queues, which handle this automatically. | `bool` | `true` | no | | [teams](#input\_teams) | Teams with access to this repository |
list(object({
team_id = string
permission = string
}))
| `[]` | no | | [use\_public\_template](#input\_use\_public\_template) | Use the public\_template repository as the template for a new repository | `bool` | `true` | no | | [users](#input\_users) | Users with access to this repository |
list(object({
username = string
permission = string
}))
| `[]` | no | diff --git a/modules/common_repository/variables.tf b/modules/common_repository/variables.tf index a08ea22..0d9fd38 100644 --- a/modules/common_repository/variables.tf +++ b/modules/common_repository/variables.tf @@ -191,6 +191,16 @@ variable "merge_queue" { grouping_strategy = optional(string, "ALLGREEN") }) default = null + + validation { + condition = var.merge_queue == null || contains(["MERGE", "SQUASH", "REBASE"], var.merge_queue.merge_method) + error_message = "merge_method must be MERGE, SQUASH, or REBASE" + } + + validation { + condition = var.merge_queue == null || contains(["ALLGREEN", "HEADGREEN"], var.merge_queue.grouping_strategy) + error_message = "grouping_strategy must be ALLGREEN or HEADGREEN" + } } variable "all_members_permission" { From daea68383540b9983ea11b73b9c1eb417034e6e9 Mon Sep 17 00:00:00 2001 From: omer-vishlitzky Date: Wed, 15 Jul 2026 18:34:05 +0300 Subject: [PATCH 4/4] Remove dead Prow required-checks, add merge-queue bypass for org-admins/wg-infra ci/prow/unit and ci/prow/temp were never removed from required_status_checks when Prow was decommissioned for these repos; they haven't posted a status on any recent PR. Left as required checks, they would make every merge-queue entry on fulfillment-service/osac-operator/osac-aap time out and get evicted, since nothing produces that check on the merge-group ref. The new merge_queue ruleset also had no bypass_actors, unlike the classic branch protection beside it (enforce_admins = false) - rulesets don't inherit that exemption, so there was no way for org-admins or wg-infra to merge around a stuck queue. Added a Team-scoped bypass for both, resolved via data "github_team" since the ruleset API needs numeric team IDs, not slugs. Also ran tofu fmt, which fixes the currently-failing pre-commit check (required_approvals alignment drifted in the osac_installer/osac_test_infra blocks after the PR's new fields were added). --- modules/common_repository/main.tf | 14 ++++++++++++++ repositories.tf | 7 ++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/modules/common_repository/main.tf b/modules/common_repository/main.tf index d0d534f..e90bcc9 100644 --- a/modules/common_repository/main.tf +++ b/modules/common_repository/main.tf @@ -105,6 +105,11 @@ resource "github_branch_protection" "repo_protection" { depends_on = [github_repository.repo, github_repository_collaborators.repo_collaborators] } +data "github_team" "merge_queue_bypass" { + for_each = var.merge_queue != null ? toset(["wg-infra", "org-admins"]) : [] + slug = each.value +} + resource "github_repository_ruleset" "merge_queue" { count = var.merge_queue != null ? 1 : 0 name = "merge-queue" @@ -119,6 +124,15 @@ resource "github_repository_ruleset" "merge_queue" { } } + dynamic "bypass_actors" { + for_each = data.github_team.merge_queue_bypass + content { + actor_id = bypass_actors.value.id + actor_type = "Team" + bypass_mode = "pull_request" + } + } + rules { merge_queue { merge_method = var.merge_queue.merge_method diff --git a/repositories.tf b/repositories.tf index bd470d5..e7d4fbc 100644 --- a/repositories.tf +++ b/repositories.tf @@ -107,7 +107,6 @@ module "repo_fulfillment_service" { ] required_approvals = null required_status_checks = [ - "ci/prow/unit", "e2e-vmaas-full-install / e2e" ] push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] @@ -140,7 +139,6 @@ module "repo_cloudkit_operator" { ] required_approvals = null required_status_checks = [ - "ci/prow/temp", "e2e-vmaas-full-install / e2e" ] push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] @@ -166,7 +164,6 @@ module "repo_cloudkit_aap" { ] required_approvals = null required_status_checks = [ - "ci/prow/temp", "e2e-vmaas-full-install / e2e" ] push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] @@ -221,7 +218,7 @@ module "repo_osac_installer" { "e2e-vmaas-full-install / e2e" ] - required_approvals = null + required_approvals = null push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] merge_queue = {} @@ -252,7 +249,7 @@ module "repo_osac_test_infra" { permission = "admin" } ] - required_approvals = null + required_approvals = null push_allowances = ["/openshift-merge-robot", "osac-project/wg-infra", "osac-project/org-admins"] environments = [{ name = "e2e-test" }] merge_queue = {}