Skip to content

fix: grant admins team repo access so protection bypass actors converge - #20

Merged
xnoto merged 1 commit into
mainfrom
fix/admins-team-repo-grants
Aug 25, 2026
Merged

fix: grant admins team repo access so protection bypass actors converge#20
xnoto merged 1 commit into
mainfrom
fix/admins-team-repo-grants

Conversation

@xnoto

@xnoto xnoto commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

GitHub silently drops branch-protection bypass actors that have no repository access at write time — the apply succeeds but the stored rule omits them, so every plan re-added makeitworkcloud/admins to dismissal_restrictions/pull_request_bypassers/push_allowances on all 11 repos (perpetual diff).

  • gh-iam.tf: github_team_repository.admins grants the team admin on every active repository (xnoto is the only member and already owner — no effective permission change). Team description updated for the dual role.
  • gh-protections.tf: depends_on includes the grants so they land before protections are written — converges in a single apply instead of self-healing a cycle later.
  • AGENTS.md: Branch Protection note updated from under-investigation to the root cause and the grants/bypass linkage rule.

This plan will show the phantom protection diff one last time (computed before the grants exist); the next plan after this apply should show zero protection changes.

@github-actions

Copy link
Copy Markdown
Contributor

OpenTofu Plan

OpenTofu will perform the following actions:

  # github_branch_protection.protections[".github"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOPDNmL84EC8t_"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["cflan"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOOulWjM4EPi_e"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["images"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsexys4EPi_f"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["kustomize-cluster"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsxTyc4EPi_g"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["shared-workflows"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsfaHs4EPi_h"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["terraform-libvirt-domain"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsXn984EPi_i"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["tfroot-aws"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsXn6c4EPi_j"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["tfroot-cloudflare"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsXoEM4EPi_k"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["tfroot-github"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsXoMs4EPi_l"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["tfroot-libvirt"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOQsXoKM4EPi_n"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_branch_protection.protections["www"] will be updated in-place
  ~ resource "github_branch_protection" "protections" {
        id                              = "BPR_kwDOOuKZAc4EPi_o"
        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismissal_restrictions          = [
              + "makeitworkcloud/admins",
            ]
          ~ pull_request_bypassers          = [
              + "makeitworkcloud/admins",
            ]
            # (5 unchanged attributes hidden)
        }

      ~ restrict_pushes {
          ~ push_allowances  = [
              + "makeitworkcloud/admins",
            ]
            # (1 unchanged attribute hidden)
        }

        # (1 unchanged block hidden)
    }

  # github_team.admins will be updated in-place
  ~ resource "github_team" "admins" {
      ~ description               = "ArgoCD administrators" -> "Administrators — ArgoCD admins and GitHub branch-protection bypass actors"
        id                        = "15594330"
        name                      = "admins"
        # (7 unchanged attributes hidden)
    }

  # github_team_repository.admins[".github"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = ".github"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["cflan"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "cflan"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["images"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "images"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["kustomize-cluster"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "kustomize-cluster"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["shared-workflows"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "shared-workflows"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["terraform-libvirt-domain"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "terraform-libvirt-domain"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["tfroot-aws"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "tfroot-aws"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["tfroot-cloudflare"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "tfroot-cloudflare"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["tfroot-github"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "tfroot-github"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["tfroot-libvirt"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "tfroot-libvirt"
      + team_id    = "15594330"
    }

  # github_team_repository.admins["www"] will be created
  + resource "github_team_repository" "admins" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + permission = "admin"
      + repository = "www"
      + team_id    = "15594330"
    }

Plan: 11 to add, 12 to change, 0 to destroy.

@xnoto
xnoto merged commit 32db6e5 into main Aug 25, 2026
4 checks passed
@xnoto
xnoto deleted the fix/admins-team-repo-grants branch August 25, 2026 16:48
xnoto added a commit that referenced this pull request Aug 30, 2026
## Summary

Excludes every repository in `local.private_github_repositories` from
both standard and relaxed `github_branch_protection` resources.

This makes the policy explicit and future-proof: private personal
repositories are outside the organization review policy, and GitHub Free
cannot apply the configured branch restrictions to them.
`agent-knowledge` remains private and receives no Terraform-managed
branch protection.

## Why now

The just-merged auto-merge setting change partially applied:
`kustomize-cluster.allow_auto_merge` completed, then the apply failed
trying to create relaxed protection for private `agent-knowledge` with:

> Upgrade to GitHub Pro or make this repository public to enable this
feature.

This PR removes the unsupported resource from the desired state,
allowing the next environment-gated apply to converge successfully.

## Expected plan

- No `agent-knowledge` branch-protection creation.
- `kustomize-cluster.allow_auto_merge` should already be converged from
the partial apply; CI plan is authoritative for confirming that.
- No secrets, repository visibility, branch protections on public
repositories, or GitHub App permissions change.

## Follow-up

Once this root apply succeeds, charts PR #20 can merge. The next real
`opencode-server` release will then prove that GitHub auto-merges the
generated version-pin PR after `kustomize-cluster` `test` passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant