Skip to content

feat: manage Dependabot configs centrally and document branch protection posture - #14

Open
xnoto wants to merge 2 commits into
mainfrom
feat/managed-dependabot
Open

feat: manage Dependabot configs centrally and document branch protection posture#14
xnoto wants to merge 2 commits into
mainfrom
feat/managed-dependabot

Conversation

@xnoto

@xnoto xnoto commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

What

1. Centrally managed Dependabot (gh-dependabot.tf, new)

Closes the dependency-update gap (no Dependabot/Renovate anywhere in the org today) by generating .github/dependabot.yml for all 11 active repositories via github_repository_file — same central-management pattern this repo already uses for branch protections and Actions secrets.

Ecosystem Repositories
github-actions all active repos
opentofu tfroot-aws, tfroot-cloudflare, tfroot-github, tfroot-libvirt, terraform-libvirt-domain
docker images (gh-cli/ and tfroot-runner/ Containerfile dirs)
pip cflan
  • OpenTofu roots use the dedicated opentofu ecosystem (GA since 2025-12-16), not terraform — the old workaround has known issues with OpenTofu >= 1.8. Lock files are not git-tracked in these repos, so only .tf version constraints are bumped.
  • Weekly cadence, all updates per ecosystem grouped into a single PR to keep noise low.
  • Archived ansible repos excluded.
  • Generated files carry a "managed by tfroot-github" header comment.

2. Branch-protection posture documented (gh-protections.tf, AGENTS.md)

The relaxed protection (empty required_status_checks.contexts, zero required approvals) is intentional for this solo-maintainer personal-dev organization, but that was previously undocumented. Now recorded in code comments and AGENTS.md so future contributors/agents don't "fix" it.

Also: AGENTS.md gains a Dependency Updates section (policy changes happen here, not downstream) and the github_repository_file import ID format. README.md regenerated by terraform-docs.

On apply

11 github_repository_file resources will be created, producing one direct commit to each active repo's main adding .github/dependabot.yml. Dependabot activates automatically once the file lands on the default branch. First batch of update PRs should appear within a week.

Deliberately not covered

  • pre-commit hook revisions — owned by the canonical images/tfroot-runner/pre-commit-config.yaml
  • Containerfile ARG tool pins — Dependabot only bumps FROM tags
  • kustomize-cluster image tags — no Dependabot ecosystem exists (Renovate/agent territory)

Validation

  • make test — all 17 hooks pass (terraform_validate, tflint, checkov, fmt, terraform_docs, gitleaks, …)
  • Rendered YAML verified via tofu console for both the multi-directory docker case (images) and pip case (cflan)
  • No tofu plan/apply run locally — live backend; credentialed plan runs in CI on this PR

Follow-up (not in this PR)

Canonical pre-commit pin pre-commit-terraform has a new release (v1.108.1 → v1.109.0, adds --tool-version pinning support). Per ownership rules that update belongs in images/tfroot-runner/pre-commit-config.yaml with a runner image rebuild.

…ction posture

- add gh-dependabot.tf generating .github/dependabot.yml for all active
  repositories via github_repository_file (github-actions everywhere,
  terraform for OpenTofu roots, docker for images, pip for cflan)
- document the intentional branch-protection relaxation for this
  solo-maintainer personal-dev organization
- document dependabot management and repository_file import ID format
  in AGENTS.md
@xnoto xnoto self-assigned this Aug 21, 2026
GitHub shipped a native opentofu package-ecosystem on 2025-12-16.
Using terraform for OpenTofu repos was the pre-GA workaround and has
known issues with OpenTofu >= 1.8. Also documents that lock files are
not git-tracked here, so only .tf version constraints are bumped.
@github-actions

Copy link
Copy Markdown

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_repository_file.dependabot[".github"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = ".github"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["cflan"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/"
              "groups":
                "pip":
                  "patterns":
                  - "*"
              "package-ecosystem": "pip"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "cflan"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["images"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/gh-cli"
              "groups":
                "docker":
                  "patterns":
                  - "*"
              "package-ecosystem": "docker"
              "schedule":
                "interval": "weekly"
            - "directory": "/tfroot-runner"
              "groups":
                "docker":
                  "patterns":
                  - "*"
              "package-ecosystem": "docker"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "images"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["kustomize-cluster"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "kustomize-cluster"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["shared-workflows"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "shared-workflows"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["terraform-libvirt-domain"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/"
              "groups":
                "opentofu":
                  "patterns":
                  - "*"
              "package-ecosystem": "opentofu"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "terraform-libvirt-domain"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["tfroot-aws"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/"
              "groups":
                "opentofu":
                  "patterns":
                  - "*"
              "package-ecosystem": "opentofu"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "tfroot-aws"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["tfroot-cloudflare"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/"
              "groups":
                "opentofu":
                  "patterns":
                  - "*"
              "package-ecosystem": "opentofu"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "tfroot-cloudflare"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["tfroot-github"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/"
              "groups":
                "opentofu":
                  "patterns":
                  - "*"
              "package-ecosystem": "opentofu"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "tfroot-github"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["tfroot-libvirt"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            - "directory": "/"
              "groups":
                "opentofu":
                  "patterns":
                  - "*"
              "package-ecosystem": "opentofu"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "tfroot-libvirt"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

  # github_repository_file.dependabot["www"] will be created
  + resource "github_repository_file" "dependabot" {
      + autocreate_branch_source_sha = (known after apply)
      + branch                       = (known after apply)
      + commit_message               = "chore: sync managed dependabot configuration"
      + commit_sha                   = (known after apply)
      + content                      = <<-EOT
            # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.
            "updates":
            - "directory": "/"
              "groups":
                "github-actions":
                  "patterns":
                  - "*"
              "package-ecosystem": "github-actions"
              "schedule":
                "interval": "weekly"
            "version": 2
        EOT
      + file                         = ".github/dependabot.yml"
      + id                           = (known after apply)
      + overwrite_on_create          = true
      + ref                          = (known after apply)
      + repository                   = "www"
      + repository_id                = (known after apply)
      + sha                          = (known after apply)
    }

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

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