Skip to content
Merged
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
81 changes: 81 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!--
PR body rules:
- One paragraph per section, no hard-wrap (GitHub flows the text).
- Explain the shipped feature shape, not every implementation detail.
- No design rationale narration here — link out to a contributor doc instead.
- No file-by-file changelog (use the diff). No full check list (use CI output).
- Every Verify-locally command is copy-pasteable and states its expected
outcome when a bare exit code does not disambiguate pass from fail.
- Drop the headings you do not need. "Related pull requests" is only when the
PR spans multiple repos (for example coordinated parity changes across the
github-terraform repos). "Behavior changes" is only when it adds signal
beyond "What ships". "Not included" is only when scope boundaries or
deferred work are useful to call out.
-->

## Related pull requests

<When this PR is part of a coordinated set spanning multiple repositories, list every PR here — just the link, no description. Drop this section entirely when the PR stands alone.>

- <https://github.com/org/repo/pull/N>

## Summary

<One paragraph answering: what is this pull request for? Name the shipped change to organization or repository management, who runs it, and how it changes their flow. Keep this short.>

## What ships

<Feature-level bullets grouped by operator-visible outcome: repositories onboarded, policy enforced, provider or wiring changes, documentation changes. Avoid resource names and file lists — the diff already shows those.>

- <Capability or policy that now exists>
- <Configuration, documentation, or workflow change operators can rely on>

## Behavior changes

<Operator-visible deltas: changed required checks, changed merge settings, changed token or permission requirements, changed runbook steps. Drop this section when it would only repeat "What ships".>

- <Existing behavior that changes>

## What this addresses

<Bullets naming the practical problem, drift, or operational pain now resolved. Name the issue when one exists.>

- <Problem or gap addressed>

## Not included

<Scope boundaries and deferred work: repositories not yet onboarded, follow-up PRs, policy left unchanged deliberately. Drop this section entirely when nothing meaningful is excluded.>

- <Out-of-scope behavior or deferred follow-up>

## Verify locally

### Checkout

```sh
gh pr checkout <PR_NUMBER>
```

### Static checks

<The format and validate gates, exactly as CI runs them in the tofu-workspace job. Expected: `tofu validate` prints "Success! The configuration is valid.">

```sh
tofu fmt -check -recursive
tofu init -backend=false -input=false -lockfile=readonly
tofu validate -no-color
```

### Smoke

<Include when the PR changes plan-visible behavior: repositories, rulesets, org settings, providers. Drop when the PR only touches docs or comments. Requires GITHUB_TOKEN exported with org admin on tailrocks and Administration: write on every managed repository.>

```sh
tofu plan -no-color
```

Expected: <the plan renders with no provider or permission errors; name the key actions the reviewer should see, or "No changes" when the PR is already applied>.

## Migration notes

<One paragraph naming what operators must do — token permission changes, state moves, re-imports. "None." is fine. Drop the section when it would only say that.>
24 changes: 1 addition & 23 deletions .terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# github-terraform

Manages GitHub repository settings and branch/tag protection for the
`tailrocks` organization: 41 repositories, each with repository merge
`tailrocks` organization: 45 repositories, each with repository merge
settings plus `protect-main` and `protect-tags` rulesets.

## Layout
Expand All @@ -11,21 +11,27 @@ settings plus `protect-main` and `protect-tags` rulesets.
- `repositories.tf` — `module.repository_policy` wiring.
- `organization.tf` — organization settings, Actions/workflow permissions,
and the `velnor-trusted` runner group.
- `main.tf` — organization Actions secrets wiring (1Password-sourced).
- `data-sources.tf` — 1Password items and `secret_selected` repo lookups.
- `modules/repository-policy/` — shared policy module (identical across orgs).
- `checks.tf` — invariant guards (no scripts).
- `imports.tf` — bootstrap import triples (one per repo: settings + 2 rulesets).
- `providers.tf`, `versions.tf`, `outputs.tf` — provider, pins, outputs.

## Authentication

Run tofu with `GITHUB_TOKEN` exported in the shell, or via the mise tasks
`mise run tofu:plan` / `mise run tofu:apply`, which inject the organization
token from 1Password. The token needs Administration: write on every managed
repository. It also needs org admin on `tailrocks` for the organization
settings, Actions policies, and runner group in `organization.tf`.

## Adding a repository

1. Add one entry to `repository_policies` in `variables.tf`, copying a check
list from `locals.tf` (`dco_policy_ci` = `["DCO", "Policy", "ci-required"]`,
`control` = `["Control / Required"]`, `required_only` = `["Required"]`,
`dco_only` = `["DCO"]`, `policy` = `["Policy"]`); bespoke checks inline
with a comment citing the producing workflow. Repos with no CI use
`required_checks = []` plus a non-empty `no_ci_reason` and extend the
`required_checks = []` plus a non-empty `no_ci_reason`, extend the
allowlist check in `checks.tf`, and add the name to the mandatory list in
`mandatory_target_repositories_present` in `checks.tf` (updating its count
and message).
Expand All @@ -41,13 +47,6 @@ settings plus `protect-main` and `protect-tags` rulesets.
`velnor-trusted` runner group restricted to
`var.velnor_runner_group_repositories` (must be a subset of
`repository_policies`).
- Secrets wiring (`main.tf` + `data-sources.tf`): org Actions secrets come
from 1Password via the `external` provider + `op` CLI; run tofu through the
mise tasks so the GitHub token is injected from 1Password (never exported
by hand): `mise run tofu:plan -- <plan args>`,
`mise run tofu:apply -- <apply args>`. A secret with
`visibility = "selected"` lists its repos, resolved through
`data.github_repository.secret_selected`.
- GitHub does not expose organization package-creation visibility through the
REST API or the Terraform GitHub provider; manage those settings in
**Organization settings → Packages**. Making a package public is
Expand All @@ -56,12 +55,10 @@ settings plus `protect-main` and `protect-tags` rulesets.
## Verification

```sh
cd /Users/donbeave/Projects/one/tailrocks-github-terraform
tofu fmt -check -recursive
tofu init -backend=false -input=false -lockfile=readonly
tofu validate -no-color
```

OpenTofu is pinned via `mise.toml` (opentofu 1.12.5); existing repos are
imported via `imports.tf` on first apply. Related reference:
`jackin-project/github-terraform`.
OpenTofu is pinned via `mise.toml` (opentofu 1.13.1); existing repos are
imported via `imports.tf` on first apply.
6 changes: 3 additions & 3 deletions checks.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ check "mandatory_target_repositories_present" {
"schemalane", "tablerock", "tailrocks-asd-ste100-skills",
"tailrocks-code-quality-skills", "tailrocks-contribution-skills",
"tailrocks-gradle-conventions", "tailrocks-logo",
"tailrocks-macos-skills", "tailrocks-open-source-skills",
"tailrocks-macos-skills",
"tailrocks-pull-request-skills", "tailrocks-repository-skills",
"tailrocks-roadmap-skills", "tailrocks-rust-skills",
"tailrocks-skill-authoring-skills", "tailrocks-skills",
"tailrocks-sqldiff", "tailrocks-typescript-skills",
"terminal-components-claude", "termpane", "termrock",
"tracing-request-level", "tui-snap", "tuiscotti", "velnor",
"tracing-request-level", "tuiscotti", "velnor",
"velnor-actions-fixture", "velnor-apt", "velnor-new", "vision",
])
error_message = "repository_policies must contain exactly the 47 managed tailrocks repositories."
error_message = "repository_policies must contain exactly the 45 managed tailrocks repositories."
}
}

Expand Down
14 changes: 0 additions & 14 deletions data-sources.tf

This file was deleted.

40 changes: 30 additions & 10 deletions imports.tf
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,6 @@ import {
id = "tailrocks-macos-skills"
}

import {
to = module.repository_policy.github_repository.managed_settings["tailrocks-open-source-skills"]
id = "tailrocks-open-source-skills"
}

import {
to = module.repository_policy.github_repository.managed_settings["tailrocks-pull-request-skills"]
id = "tailrocks-pull-request-skills"
Expand Down Expand Up @@ -222,11 +217,6 @@ import {
id = "tracing-request-level"
}

import {
to = module.repository_policy.github_repository.managed_settings["tui-snap"]
id = "tui-snap"
}

import {
to = module.repository_policy.github_repository.managed_settings["tuiscotti"]
id = "tuiscotti"
Expand Down Expand Up @@ -337,6 +327,16 @@ import {
id = "renovate-rust:20575222"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_main["repo-scan"]
id = "repo-scan:24877074"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_main["rust-repository-policy"]
id = "rust-repository-policy:24877097"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_main["ruxel"]
id = "ruxel:19573057"
Expand Down Expand Up @@ -376,6 +376,11 @@ import {
id = "tailrocks-pull-request-skills:23746082"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_main["tailrocks-repository-skills"]
id = "tailrocks-repository-skills:24877099"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_main["tailrocks-roadmap-skills"]
id = "tailrocks-roadmap-skills:23746088"
Expand Down Expand Up @@ -532,6 +537,16 @@ import {
id = "renovate-rust:20575267"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_tags["repo-scan"]
id = "repo-scan:24877093"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_tags["rust-repository-policy"]
id = "rust-repository-policy:24877092"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_tags["ruxel"]
id = "ruxel:19572976"
Expand Down Expand Up @@ -571,6 +586,11 @@ import {
id = "tailrocks-pull-request-skills:23746069"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_tags["tailrocks-repository-skills"]
id = "tailrocks-repository-skills:24877095"
}

import {
to = module.repository_policy.github_repository_ruleset.protect_tags["tailrocks-roadmap-skills"]
id = "tailrocks-roadmap-skills:23746077"
Expand Down
18 changes: 0 additions & 18 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,6 @@
# SPDX-License-Identifier: Apache-2.0

locals {
# Fill this map with final secret names and metadata.
# value format: vault UUID, item title, section label, field label.
# Vault UUID: op vault get NAME --format json | jq -r .id
# Example:
# org_secrets = {
# GITHUB_WEBHOOK_SECRET = {
# vault_uuid = "xlm7pn5y6d3nfya5xy2os3vtoq"
# item = "org-credentials"
# section = "tokens"
# field = "webhook_secret"
# }
# }
org_secrets = {
# CARGO_REGISTRY_TOKEN removed 2026-10-01: termpane 0.1.0 published;
# crates.io Trusted Publisher (tailrocks/termpane, release.yml, release)
# replaces the bootstrap token path. No org secrets remain.
}

# Check templates for new inventory entries. Variable defaults must be
# literals, so variables.tf keeps literal lists — copy from here when
# adding a repo, and keep both in sync. Bespoke check lists stay allowed
Expand Down
12 changes: 0 additions & 12 deletions main.tf

This file was deleted.

8 changes: 2 additions & 6 deletions mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ description = "Run tofu plan"
run = """
set -eu
export OP_ACCOUNT='zhokhov.1password.com'
OP_SESSION_zhokhov="$(op signin --raw)" || { echo "op signin failed, refusing to run" >&2; exit 1; }
export OP_SESSION_zhokhov
GITHUB_TOKEN="$(op read 'op://tailrocks/GitHub/tokens/tofu')" || { echo "op read failed, refusing to run" >&2; exit 1; }
GITHUB_TOKEN="$(op read 'op://tailrocks/GitHub/tailrocks-opentofu')" || { echo "op read failed, refusing to run" >&2; exit 1; }
export GITHUB_TOKEN
[ -n "${GITHUB_TOKEN:-}" ] || { echo "GITHUB_TOKEN is empty, refusing to run" >&2; exit 1; }
exec tofu plan "$@"
Expand All @@ -25,9 +23,7 @@ description = "Run tofu apply"
run = """
set -eu
export OP_ACCOUNT='zhokhov.1password.com'
OP_SESSION_zhokhov="$(op signin --raw)" || { echo "op signin failed, refusing to run" >&2; exit 1; }
export OP_SESSION_zhokhov
GITHUB_TOKEN="$(op read 'op://tailrocks/GitHub/tokens/tofu')" || { echo "op read failed, refusing to run" >&2; exit 1; }
GITHUB_TOKEN="$(op read 'op://tailrocks/GitHub/tailrocks-opentofu')" || { echo "op read failed, refusing to run" >&2; exit 1; }
export GITHUB_TOKEN
[ -n "${GITHUB_TOKEN:-}" ] || { echo "GITHUB_TOKEN is empty, refusing to run" >&2; exit 1; }
exec tofu apply "$@"
Expand Down
3 changes: 2 additions & 1 deletion organization.tf
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ resource "github_organization_settings" "tailrocks" {
members_can_fork_private_repositories = false
web_commit_signoff_required = false

advanced_security_enabled_for_new_repositories = true
# advanced_security_enabled_for_new_repositories omitted: the org is on the
# free plan and the API silently ignores it (perpetual diff otherwise).
dependabot_alerts_enabled_for_new_repositories = true
dependabot_security_updates_enabled_for_new_repositories = true
dependency_graph_enabled_for_new_repositories = true
Expand Down
11 changes: 0 additions & 11 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
# SPDX-FileCopyrightText: 2026 Alexey Zhokhov
# SPDX-License-Identifier: Apache-2.0

output "managed_secrets" {
value = {
for name, secret in github_actions_organization_secret.org_secret :
name => {
visibility = secret.visibility
}
}

description = "Managed org secret names and visibility. Values are intentionally omitted."
}

output "organization" {
value = var.github_organization
}
Expand Down
3 changes: 0 additions & 3 deletions terraform.tfvars.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Optional overrides. Defaults live in variables.tf.
# github_organization = "tailrocks"
# op_vault = "TailRocks"
# secret_visibility = "selected"

# Prefer env auth (never commit real tokens):
# export GITHUB_TOKEN=...
# export OP_SERVICE_ACCOUNT_TOKEN=...
Loading