Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ Use a feature branch and open a pull request rather than pushing directly to
environment gates approve it. Do not push any branch unless explicitly
requested.

## Branch Protection

Branch protection on `main` is intentionally relaxed for this solo-maintainer,
personal-dev organization: no required status check contexts and zero required
approving reviews (see the comment in `gh-protections.tf`). PRs are used for CI
validation, plan output, and change history, not as a review gate. Do not
tighten `contexts` or `required_approving_review_count` unless explicitly
requested.

## Pre-commit Configuration

Pre-commit configuration is centralized at
Expand Down Expand Up @@ -40,6 +49,16 @@ tracked copy.
**Pre-commit failures:** If hooks fail unexpectedly, the canonical config may
have changed. Re-run `make test` to refresh it and run the checks.

## Dependency Updates

Dependabot version updates are managed here in `gh-dependabot.tf` via
`github_repository_file` resources, so all active repositories receive their
`.github/dependabot.yml` from one place. To change update policy, edit the
`dependabot_ecosystems` map; do not hand-edit `.github/dependabot.yml` in
downstream repositories. Pre-commit hook revisions are not covered by
Dependabot: they are owned by the canonical config in
`images/tfroot-runner/pre-commit-config.yaml`.

## Related Repositories

- `images` - Contains tfroot-runner image and canonical pre-commit config
Expand All @@ -53,3 +72,4 @@ objects:
- repositories: `<repository>`
- `main` branch protections: `<repository>:main`
- Actions secrets: `<repository>/<secret-name>`
- repository files: `<repository>/<file-path>` (append `:<branch>` for non-default branches)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ No modules.
| [github_branch_protection.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 |
| [github_team.admins](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team) | resource |
| [github_team.developers](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team) | resource |
| [github_team_membership.admins_xnoto](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership) | resource |
Expand Down
71 changes: 71 additions & 0 deletions gh-dependabot.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Dependabot version updates are managed centrally here so every active
# repository receives the same `.github/dependabot.yml` policy. Do not add
# hand-maintained dependabot configs to downstream repositories.
#
# OpenTofu roots use the dedicated `opentofu` ecosystem (GA since 2025-12-16),
# not `terraform`. Lock files are not git-tracked in these repos, so only
# version constraints in .tf files are bumped.
#
# Deliberately not covered:
# - pre-commit hook revisions: owned by images/tfroot-runner/pre-commit-config.yaml
# - tool ARG pins in images/*/Containerfile: Dependabot only updates FROM tags
# - Kubernetes image tags in kustomize-cluster: no Dependabot ecosystem exists

locals {
dependabot_ecosystems = {
".github" = ["github-actions"]
"cflan" = ["github-actions", "pip"]
"images" = ["github-actions", "docker"]
"kustomize-cluster" = ["github-actions"]
"shared-workflows" = ["github-actions"]
"terraform-libvirt-domain" = ["github-actions", "opentofu"]
"tfroot-aws" = ["github-actions", "opentofu"]
"tfroot-cloudflare" = ["github-actions", "opentofu"]
"tfroot-github" = ["github-actions", "opentofu"]
"tfroot-libvirt" = ["github-actions", "opentofu"]
"www" = ["github-actions"]
}
dependabot_docker_directories = ["gh-cli", "tfroot-runner"]

dependabot_configs = {
for repo, ecosystems in local.dependabot_ecosystems : repo => {
version = 2
updates = concat(
[for ecosystem in ecosystems : {
package-ecosystem = ecosystem
directory = "/"
schedule = {
interval = "weekly"
}
groups = {
(ecosystem) = {
patterns = ["*"]
}
}
} if ecosystem != "docker"],
[for directory in local.dependabot_docker_directories : {
package-ecosystem = "docker"
directory = "/${directory}"
schedule = {
interval = "weekly"
}
groups = {
docker = {
patterns = ["*"]
}
}
} if contains(ecosystems, "docker")]
)
}
}
}

resource "github_repository_file" "dependabot" {
for_each = local.dependabot_configs

repository = github_repository.repositories[each.key].name
file = ".github/dependabot.yml"
content = "# Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten.\n${yamlencode(each.value)}"
commit_message = "chore: sync managed dependabot configuration"
overwrite_on_create = true
}
6 changes: 6 additions & 0 deletions gh-protections.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Branch protection here is intentionally relaxed for a solo-maintainer,
# personal-dev organization: `contexts` is empty (CI is advisory, not a merge
# gate) and `required_approving_review_count` is zero. The PR workflow exists
# for CI validation, plan output, and change history — not review ceremony.
# If collaborators join or a repo gains external contributors, tighten
# `contexts` and `required_approving_review_count` at that time.
resource "github_branch_protection" "protections" {
for_each = toset([for repo in local.github_repositories : repo if !contains(local.archived_github_repositories, repo)])
repository_id = github_repository.repositories[each.key].node_id
Expand Down
Loading