From f6e2742860e0749a153bed69c18e11d54aca4ab4 Mon Sep 17 00:00:00 2001 From: Ameya Sathe Date: Tue, 4 Aug 2026 15:43:07 +0530 Subject: [PATCH 1/2] fix(OSAC-3445): guard sub-resources for archived repos in common_repository module GitHub rejects writes (409) to pages, labels, branch protection, rulesets, and environments on archived repos. archived=true was already threaded through repositories.tf but the module never skipped these sub-resources, which broke tofu apply once any repo was archived. Assisted-by: Claude Code (claude-sonnet-5) Signed-off-by: Ameya Sathe rh-pre-commit.version: 2.4.0 rh-pre-commit.check-secrets: ENABLED --- modules/common_repository/main.tf | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/common_repository/main.tf b/modules/common_repository/main.tf index 74aeb4f..2047aea 100644 --- a/modules/common_repository/main.tf +++ b/modules/common_repository/main.tf @@ -28,7 +28,8 @@ resource "github_repository" "repo" { dynamic "pages" { # enable this block if `pages` is not null - for_each = var.pages[*] + # Skip pages config for archived repos (GitHub rejects it with 409) + for_each = var.archived ? [] : var.pages[*] content { cname = pages.value.cname @@ -51,7 +52,8 @@ resource "github_issue_label" "repo_labels" { # Generate label blocks from the value of local.values, which by default is initialized # by the contents of the "labels.csv" file. - for_each = { + # Skip labels for archived repos (GitHub rejects writes with 409) + for_each = var.archived ? {} : { for label in local.labels : label.name => label } @@ -65,8 +67,9 @@ resource "github_issue_label" "repo_labels" { resource "github_branch_protection" "repo_protection" { # This odd looking construct lets us control the creation of the - # branch protection resource with a boolean variable. - count = var.visibility == "private" ? 0 : var.branch_protection ? 1 : 0 + # branch protection resource with a boolean variable, and skip it + # entirely for archived repos (GitHub rejects writes with 409). + count = var.visibility == "private" ? 0 : var.archived ? 0 : var.branch_protection ? 1 : 0 repository_id = var.name pattern = "main" @@ -102,7 +105,7 @@ resource "github_branch_protection" "repo_protection" { } resource "github_repository_ruleset" "status_checks" { - count = var.visibility == "private" ? 0 : var.branch_protection && length(var.required_status_checks) > 0 ? 1 : 0 + count = var.visibility == "private" ? 0 : var.archived ? 0 : var.branch_protection && length(var.required_status_checks) > 0 ? 1 : 0 name = "ci-status-checks" repository = github_repository.repo.name @@ -149,7 +152,8 @@ resource "github_repository_ruleset" "status_checks" { } resource "github_repository_environment" "env" { - for_each = { + # Skip environments for archived repos (GitHub rejects writes with 409) + for_each = var.archived ? {} : { for env in var.environments : env.name => env } From 56ad35632e1d81b2ac9b2cbf4445e8adc7ed75ec Mon Sep 17 00:00:00 2001 From: Elior Erez Date: Wed, 5 Aug 2026 18:02:17 -0400 Subject: [PATCH 2/2] Use removed blocks for branch_protection/environment on archived repos count=0/empty for_each is safe for github_issue_label and github_repository_ruleset -- the provider (v6.12.1) already skips their delete against an archived repo and just drops them from state. It is not safe for github_branch_protection or github_repository_environment: both call the GitHub API directly to delete with no archived-repo handling, so flipping their count/for_each to 0 for the 7 repos already archived in this repo's config would make Terraform attempt to destroy those existing resources with unverified behavior. Add a removed block per already-archived repo for these two resource types instead, so Terraform stops managing them without ever issuing a delete. See the review thread on this PR (CodeRabbit's follow-up after the "chai-bot" dismissal) for the full analysis this is resolving. Signed-off-by: Elior Erez --- removed_archived_repos.tf | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 removed_archived_repos.tf diff --git a/removed_archived_repos.tf b/removed_archived_repos.tf new file mode 100644 index 0000000..ed6926c --- /dev/null +++ b/removed_archived_repos.tf @@ -0,0 +1,101 @@ +# github_issue_label and github_repository_ruleset are safe to guard with a +# plain count=0/empty for_each for archived repos: the underlying Terraform +# GitHub provider (v6.12.1) already handles deleting those two resource +# types gracefully against an archived repository (skips the delete, drops +# it from state). github_branch_protection and github_repository_environment +# are not -- their delete codepaths call the GitHub API directly with no +# archived-repo handling, so flipping their count/for_each to zero for an +# archived repo (in modules/common_repository/main.tf) would make Terraform +# attempt to destroy the existing resource with unverified behavior against +# an archived repo. `removed` blocks tell Terraform to stop managing these +# specific already-existing instances without ever issuing a delete. +# +# See the review discussion on osac-project/github-config#165 for the full +# analysis (CodeRabbit + follow-up). +# +# One block is required per already-archived repo, since `removed` targets +# a concrete resource address rather than a dynamic set. + +removed { + from = module.repo_fulfillment_service.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_fulfillment_service.github_repository_environment.env + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_cloudkit_operator.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_cloudkit_operator.github_repository_environment.env + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_cloudkit_aap.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_cloudkit_aap.github_repository_environment.env + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_osac_installer.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_osac_installer.github_repository_environment.env + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_osac_csi_driver.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_osac_csi_driver.github_repository_environment.env + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_host_management_openstack.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +} + +removed { + from = module.repo_bare_metal_operator.github_branch_protection.repo_protection + lifecycle { + destroy = false + } +}