diff --git a/README.md b/README.md index 3792988..149a348 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ No modules. | ---- | ---- | | [github_actions_secret.secrets](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_secret) | resource | | [github_branch_protection.protections](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection) | resource | +| [github_branch_protection.relaxed_protections](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection) | resource | | [github_membership.admin](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/membership) | resource | | [github_repository.repositories](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository) | resource | | [github_repository_file.dependabot](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository_file) | resource | diff --git a/gh-protections.tf b/gh-protections.tf index 54ea415..ac2eeed 100644 --- a/gh-protections.tf +++ b/gh-protections.tf @@ -1,8 +1,14 @@ # Every active repository requires a pull request with its configured CI checks -# passing before merge. The check map lives in main.tf because check-run names -# differ by repository and GitHub treats an unknown required check as pending. +# passing before merge, except repositories explicitly assigned the relaxed +# protection profile below. The check map lives in main.tf because check-run +# names differ by repository and GitHub treats an unknown required check as +# pending. resource "github_branch_protection" "protections" { - for_each = toset([for repo in local.github_repositories : repo if !contains(local.archived_github_repositories, repo)]) + for_each = toset([ + for repo in local.github_repositories : repo + if !contains(local.archived_github_repositories, repo) && !contains(local.relaxed_branch_protection_github_repositories, repo) + ]) + repository_id = github_repository.repositories[each.key].node_id pattern = "main" enforce_admins = true @@ -36,3 +42,34 @@ resource "github_branch_protection" "protections" { github_team_repository.admins, ] } + +# Personal knowledge repositories retain pull-request-only writes and basic +# branch integrity, while allowing any pull request to merge without a CI, +# approval, code-owner, or conversation-resolution gate. +resource "github_branch_protection" "relaxed_protections" { + for_each = toset([ + for repo in local.relaxed_branch_protection_github_repositories : repo + if !contains(local.archived_github_repositories, repo) + ]) + + repository_id = github_repository.repositories[each.key].node_id + pattern = "main" + enforce_admins = true + allows_force_pushes = false + required_linear_history = true + required_pull_request_reviews { + require_code_owner_reviews = false + required_approving_review_count = 0 + require_last_push_approval = false + } + restrict_pushes { + push_allowances = [ + "${var.github_owner}/${github_team.admins.slug}" + ] + } + depends_on = [ + github_repository.repositories, + github_team.admins, + github_team_repository.admins, + ] +} diff --git a/gh-repositories.tf b/gh-repositories.tf index 6c37b58..9ce2d94 100644 --- a/gh-repositories.tf +++ b/gh-repositories.tf @@ -2,7 +2,7 @@ resource "github_repository" "repositories" { for_each = local.github_repositories name = each.key archived = contains(local.archived_github_repositories, each.key) - visibility = var.github_visibility + visibility = contains(local.private_github_repositories, each.key) ? "private" : var.github_visibility auto_init = true allow_squash_merge = true allow_merge_commit = true diff --git a/main.tf b/main.tf index 083afb1..5771d43 100644 --- a/main.tf +++ b/main.tf @@ -10,6 +10,7 @@ locals { "ansible-role-crc", "cflan", "charts", + "agent-knowledge", "kustomize-cluster", "images", "shared-workflows", @@ -27,15 +28,16 @@ locals { "ansible-site-cluster", "ansible-role-crc" ]) - # Non-archived repositories. Secrets cannot be written to archived repos, - # so org-wide secrets must target this list rather than github_repositories. + private_github_repositories = toset([ + "agent-knowledge" + ]) + relaxed_branch_protection_github_repositories = toset([ + "agent-knowledge" + ]) active_github_repositories = toset([ for repo in local.github_repositories : repo if !contains(local.archived_github_repositories, repo) ]) - # Status-check names are GitHub check-run names, not workflow filenames. - # Keep this exhaustive for active repositories so a newly managed repository - # cannot silently receive a branch rule without a required CI check. required_status_checks_by_repository = { ".github" = ["pre-commit"] "cflan" = ["lint-and-test (3.10)", "lint-and-test (3.11)", "lint-and-test (3.12)", "lint-and-test (3.13)", "type-check"] @@ -124,18 +126,14 @@ locals { repositories = ["charts"] } "ssh_private_key" = { - name = "SSH_PRIVATE_KEY" - value = data.sops_file.secret_vars.data["ssh_private_key"] - repositories = [ - "tfroot-libvirt" - ] + name = "SSH_PRIVATE_KEY" + value = data.sops_file.secret_vars.data["ssh_private_key"] + repositories = ["tfroot-libvirt"] } "ssh_known_hosts" = { - name = "SSH_KNOWN_HOSTS" - value = data.sops_file.secret_vars.data["ssh_known_hosts"] - repositories = [ - "tfroot-libvirt" - ] + name = "SSH_KNOWN_HOSTS" + value = data.sops_file.secret_vars.data["ssh_known_hosts"] + repositories = ["tfroot-libvirt"] } } }