Why:
- The go-gitea/gitea provider schema defaults migration_mirror_interval to
"8h0m0s", but on read it writes repo.MirrorInterval straight back into state,
and Gitea returns an empty MirrorInterval for non-mirror repositories.
- So the stored value ("") never matches the schema default ("8h0m0s"), and tofu
plans an in-place update of every gitea_repository on every run even with no
config change, keeping terraform-git plans perpetually dirty.
- migration_mirror_interval is the only migration_* field read back from the API;
the rest are write-only migration options, so no sibling field churns.
Change:
- Add a targeted ignore_changes on migration_mirror_interval at the repository
module so the provider default no longer fights Gitea's empty value, making
every repository idempotent. It is a migration-only knob with no drift to track
for these non-mirror repos.
Why:
- The go-gitea/gitea provider's SDK (v0.25.1, already shipped in provider
0.8.1 on main) rejects a team permission of `none` on every EditTeam with
"permission mode invalid"; it only accepts read/write/admin/owner
client-side. The same check exists in the older 0.21.0 SDK, so the provider
version is not the lever here.
- The team module's `units` (server returns an unordered space-separated
string that never matches the provider's comma-ordered default) and
`repositories` (provider sorts state; `include_all_repositories` populates
every repo) drift on representation alone, so every apply plans an in-place
update for every team.
- Each update calls EditTeam, which replays the stored `permission=none` kept
by `ignore_changes` and fails, blocking apply for every non-owner team
(docker/terraform/rpmbuild/puppet/pybuild/forgebot/agents) and the whole
terraform-git apply, including the unkin-agent rollout.
Change:
- Add `units` and `repositories` to the team `ignore_changes` so provider
representation churn no longer triggers an EditTeam call.
- Preserve each team's current on-server permission and repository membership
(no access change).
Why:
- tofu apply fails with "Branch protection already exist" because the nine
repos protected in #60 already have live branch protection in Gitea that is
absent from Terraform state, so Terraform tries to create rules that already
exist and Gitea rejects them.
- The pinned go-gitea/gitea provider 0.7.0 defines no importer for
gitea_repository_branch_protection, so the existing rules cannot be adopted
on that version.
How:
- Bump the gitea provider from 0.7.0 to 0.8.1, which adds an importer for
gitea_repository_branch_protection (id format <org>/<repo>/<rule_name>).
- Add import blocks adopting the nine existing branch protections into state
instead of recreating them.
Why: the vault-plugin-secrets-gitea engine needs a purpose-built Gitea
site-admin credential to mint and delete per-user tokens. Provision that
account and its Vault seed here so the credential is generated once and never
exposed. Ben asked that the password be written to Vault exactly once and
never updated afterwards.
Change:
- Add config/git.unkin.net/user/gitea-vault-admin.yaml: a local site-admin bot
(admin: true, limited visibility, no org/repo creation).
- Extend the user module with an optional vault_seed_path/vault_seed_mount: when
set, write the account's generated password to Vault KV as admin_username and
admin_password via vault_kv_secret_v2, with lifecycle ignore_changes on
data_json so the write is create-only and never churns (random_password
already never regenerates). This keeps the seed stable and prevents a re-apply
from overwriting a password later rotated out-of-band by rotate-root.
- Add the hashicorp/vault provider (module plus root generate block); it reads
VAULT_ADDR and VAULT_TOKEN already exported by the Makefile k8s login.
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
Add a data-driven 'user' config kind so Gitea accounts are declared as
config/git.unkin.net/user/<name>.yaml, mirroring the existing repository
and team kinds. Wire the yaml into a new modules/user submodule that
creates a gitea_user (provider go-gitea/gitea 0.7.0, already pinned).
The provider's user resource requires a password; generate a per-user
random_password so nothing sensitive is hardcoded and only a placeholder
lives in state (tokens come later from vault-plugin-secrets-gitea).
Provision teabot's implementer and reviewer personality accounts with a
conservative posture: not site admins, no org creation, no repo creation,
limited profile visibility.
Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
- Remove woodpecker_repository module and provider (repos managed outside TF)
- Add removed block with destroy=false to drop state without destroying
- Add module.team to branch_protection depends_on to prevent race condition
- Add lifecycle ignore_changes for team permission (provider bug: API returns
"none" but rejects it on write)
- forgebot: branch protection on main with pre-commit, test, build CI checks
- forgebot-skills: branch protection on main with validate CI check
- Both repos enabled for Woodpecker CI
Manage Gitea resources via Terraform/Terragrunt with YAML-driven config.
Resources managed:
- Organisation (unkin)
- 28 repositories with settings
- 6 teams with members
- 13 branch protection rules
- 9 Woodpecker CI repo activations
- Deploy key module (ready, no keys yet)
Config structure: config/<service>/<org>/<type>/<object>.yaml
Consul backend for state, Vault for auth tokens.