@@ -555,7 +555,7 @@ resource "github_branch_protection" "openstack_branch_protection_caracal" {
555555}
556556
557557resource "github_branch_protection" "openstack_branch_protection_epoxy" {
558- for_each = toset (var. repositories [" OpenStack" ])
558+ for_each = toset ([ for r in var . repositories [" OpenStack" ] : r if ! contains (var . repositories [ " ZuulOnly " ], r) ])
559559 repository_id = data. github_repository . repositories [each . key ]. node_id
560560
561561 pattern = " stackhpc/2025.1"
@@ -594,7 +594,7 @@ resource "github_branch_protection" "openstack_branch_protection_epoxy" {
594594}
595595
596596resource "github_branch_protection" "openstack_branch_protection_gazpacho" {
597- for_each = toset (var. repositories [" OpenStack" ])
597+ for_each = toset ([ for r in var . repositories [" OpenStack" ] : r if ! contains (var . repositories [ " ZuulOnly " ], r) ])
598598 repository_id = data. github_repository . repositories [each . key ]. node_id
599599
600600 pattern = " stackhpc/2026.1"
@@ -669,6 +669,74 @@ resource "github_branch_protection" "openstack_branch_protection_master" {
669669 }
670670}
671671
672+ # ZuulOnly: push_allowances contains only the Zuul GitHub App, so PRs can only be
673+ # merged by Zuul (typically after the "gate" label enqueues the change into its gate
674+ # pipeline). Neither the Developers team nor the owning team can merge directly.
675+ # Only the stackhpc/2025.1 and stackhpc/2026.1 branches are gated this way for now
676+ resource "github_branch_protection" "zuulonly_branch_protection_epoxy" {
677+ for_each = toset (var. repositories [" ZuulOnly" ])
678+ repository_id = data. github_repository . repositories [each . key ]. node_id
679+
680+ pattern = " stackhpc/2025.1"
681+ require_conversation_resolution = true
682+ allows_deletions = false
683+ allows_force_pushes = false
684+
685+ restrict_pushes {
686+ blocks_creations = false
687+ push_allowances = [
688+ local . zuul_app_node_id
689+ ]
690+ }
691+
692+ required_pull_request_reviews {
693+ dismiss_stale_reviews = true
694+ require_code_owner_reviews = true
695+ required_approving_review_count = 1
696+ }
697+
698+ required_status_checks {
699+ contexts = [" stackhpc/check" ]
700+ strict = false
701+ }
702+
703+ lifecycle {
704+ prevent_destroy = true
705+ }
706+ }
707+
708+ resource "github_branch_protection" "zuulonly_branch_protection_gazpacho" {
709+ for_each = toset (var. repositories [" ZuulOnly" ])
710+ repository_id = data. github_repository . repositories [each . key ]. node_id
711+
712+ pattern = " stackhpc/2026.1"
713+ require_conversation_resolution = true
714+ allows_deletions = false
715+ allows_force_pushes = false
716+
717+ restrict_pushes {
718+ blocks_creations = false
719+ push_allowances = [
720+ local . zuul_app_node_id
721+ ]
722+ }
723+
724+ required_pull_request_reviews {
725+ dismiss_stale_reviews = true
726+ require_code_owner_reviews = true
727+ required_approving_review_count = 1
728+ }
729+
730+ required_status_checks {
731+ contexts = [" stackhpc/check" ]
732+ strict = false
733+ }
734+
735+ lifecycle {
736+ prevent_destroy = true
737+ }
738+ }
739+
672740resource "github_branch_protection" "platform_branch_protection" {
673741 for_each = toset (var. repositories [" Platform" ])
674742 repository_id = data. github_repository . repositories [each . key ]. node_id
0 commit comments