diff --git a/AGENTS.md b/AGENTS.md index dd92037..90f6b2f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,6 +59,29 @@ 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`. +## Dependabot PR Alerting + +When Dependabot opens a PR, the managed caller workflow +(`.github/workflows/dependabot-notify.yml`, from `gh-dependabot.tf`) invokes +the `dependabot-notify` reusable workflow in `shared-workflows`, which posts a +synthetic alert to the cluster Grafana's Alertmanager API. Delivery goes to +Discord via the `GrafanaContactPoint`/`GrafanaNotificationPolicy` CRs in +`kustomize-cluster/workloads/grafana`. + +Two secrets make this work, both distributed here to all active repositories: + +- `CLOUDFLARE_AUTH_CLIENT_ID` / `CLOUDFLARE_AUTH_CLIENT_SECRET` — the existing + "GitHub Actions" Cloudflare Access service token, allowed by the path-scoped + Access app managed in `tfroot-cloudflare/cf-access-grafana.tf`. +- `GRAFANA_ALERTS_TOKEN` — a Grafana service account token. The account and + token are managed as code by the `GrafanaServiceAccount` CR in + `kustomize-cluster/workloads/grafana/serviceaccount-alerts.yaml`; the + operator writes the generated token to the `grafana-alerts-token` cluster + Secret (key `token`). Until that value is copied here into + `secrets/secrets.yaml` as `grafana_alerts_token`, the distributed secret is + a placeholder and alert delivery fails with a 401 in the caller repo's + Actions log. + ## Related Repositories - `images` - Contains tfroot-runner image and canonical pre-commit config diff --git a/README.md b/README.md index d2447b7..68c4a63 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ No modules. | [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_repository_file.dependabot_notify](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 | diff --git a/gh-dependabot.tf b/gh-dependabot.tf index b88b011..71f1198 100644 --- a/gh-dependabot.tf +++ b/gh-dependabot.tf @@ -69,3 +69,36 @@ resource "github_repository_file" "dependabot" { commit_message = "chore: sync managed dependabot configuration" overwrite_on_create = true } + +locals { + # Caller for the dependabot-notify reusable workflow in shared-workflows. + # Fires only when Dependabot itself opens the PR; posts a synthetic alert + # to the cluster Grafana (see AGENTS.md, "Dependabot PR Alerting"). + dependabot_notify_workflow = <<-EOT + --- + # Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten. + name: dependabot-notify + + on: + pull_request: + types: [opened, reopened] + + permissions: {} + + jobs: + notify: + if: github.actor == 'dependabot[bot]' + uses: makeitworkcloud/shared-workflows/.github/workflows/dependabot-notify.yml@main + secrets: inherit + EOT +} + +resource "github_repository_file" "dependabot_notify" { + for_each = local.dependabot_configs + + repository = github_repository.repositories[each.key].name + file = ".github/workflows/dependabot-notify.yml" + content = local.dependabot_notify_workflow + commit_message = "chore: sync managed dependabot notification workflow" + overwrite_on_create = true +} diff --git a/main.tf b/main.tf index 48479bf..1b9d4c9 100644 --- a/main.tf +++ b/main.tf @@ -24,6 +24,12 @@ 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. + active_github_repositories = toset([ + for repo in local.github_repositories : repo + if !contains(local.archived_github_repositories, repo) + ]) secrets = { "onion_s3_bucket" = { name = "ONION_AWS_S3_BUCKET" @@ -76,22 +82,21 @@ locals { repositories = ["www"] } "cloudflare_auth_client_id" = { - name = "CLOUDFLARE_AUTH_CLIENT_ID" - value = data.sops_file.secret_vars.data["cloudflare_auth_client_id"] - repositories = [ - "images", - "kustomize-cluster", - "tfroot-github" - ] + name = "CLOUDFLARE_AUTH_CLIENT_ID" + value = data.sops_file.secret_vars.data["cloudflare_auth_client_id"] + repositories = local.active_github_repositories } "cloudflare_auth_client_secret" = { - name = "CLOUDFLARE_AUTH_CLIENT_SECRET" - value = data.sops_file.secret_vars.data["cloudflare_auth_client_secret"] - repositories = [ - "images", - "kustomize-cluster", - "tfroot-github" - ] + name = "CLOUDFLARE_AUTH_CLIENT_SECRET" + value = data.sops_file.secret_vars.data["cloudflare_auth_client_secret"] + repositories = local.active_github_repositories + } + "grafana_alerts_token" = { + name = "GRAFANA_ALERTS_TOKEN" + # Placeholder until the operator-generated token is copied from the + # cluster (see AGENTS.md, "Dependabot PR Alerting"). + value = lookup(data.sops_file.secret_vars.data, "grafana_alerts_token", "pending-grafana-service-account-token") + repositories = local.active_github_repositories } "ssh_private_key" = { name = "SSH_PRIVATE_KEY"