feat: foundation scaffolding for org-wide config (Step 0 of 9)#2
Merged
Conversation
Sets up the no-magic-numbers convention and the directory layout for the
planned expansion of terraform-github into the single source of truth for
all dryvist org-level configuration (rulesets, actions settings, per-repo
file content, labels). No new resources; no apply required.
Changes
-------
- variables.tf: add `dot_github_repository_id` (number, default
1220572589, validated positive). Canonical example of the
no-magic-numbers rule.
- rulesets.tf: drop the `locals { }` block; reference
`var.dot_github_repository_id` instead of
`local.dot_github_repository_id`.
- main.tf, outputs.tf: new. Resolves the two pre-existing tflint
terraform_standard_module_structure warnings. main.tf documents the
file-split convention (resources organized by topic in named files);
outputs.tf is intentionally empty per the check's own warning text.
- config/rulesets-defaults.yml: new. Holds default thresholds (max file
size, banned extensions) the upcoming `org_push_protection` ruleset
will consume via yamldecode(file(...)).
- templates/LICENSE.mit.tmpl: new. MIT template for Step 7's per-repo
`github_repository_file.license` for_each.
- AGENTS.md, README.md: document the no-magic-numbers convention, the
config/ and templates/ directories, and the rule that new rulesets
default to `active` enforcement. Existing `markdown_lint_enforcement`
keeps its legacy `evaluate` default - changing it would silently flip
enforcement on the next apply for any operator running `tofu apply`
without overrides.
What this does NOT do
---------------------
- No new rulesets - those land in Step 1 (org_push_protection: native
max_file_size + file_extension_restriction push rules) and beyond.
- No apply - only `tofu validate` was run. Apply requires ORG_ADMIN.
- terraform-github's own LICENSE is still Apache 2.0; Step 7 will
reconcile it to MIT along with every other dryvist repo via
github_repository_file.
Verification
------------
- tofu init -backend=false and tofu validate -> green
- pre-commit run --all-files -> all hooks pass
Assisted-by: Claude <noreply@anthropic.com>
…emplate; org-agnostic prose
PR feedback corrections:
- variables.tf: drop `dot_github_repository_id`. The literal default
(1220572589) was a magic number; relocating it from a local to a
variable did not solve that.
- data.tf: new. Adds `data "github_repository" "dot_github"` lookup
that resolves the .github repo's numeric ID at apply time via the
provider's already-configured `owner`. Reference as
`data.github_repository.dot_github.repo_id`.
- rulesets.tf: switch `repository_id` from `var.dot_github_repository_id`
to `data.github_repository.dot_github.repo_id`. Resource comment
rewritten to reference the repo by role ("the org's `.github` repo")
instead of by org-prefixed path.
- templates/LICENSE.mit.tmpl: removed. Canonical license text belongs
to its upstream; Step 7's LICENSE enforcement fetches the MIT body
via `data "http"` against https://api.github.com/licenses/mit at
apply time, with `${year}` / `${holder}` substituted via `replace()`.
No local copy to drift from the canonical source.
- AGENTS.md, README.md: prose now org-agnostic — references the repo
by role ("the org's `.github` repo", "the org owner declared in the
provider"). The `dryvist-only references` convention bullet is
reworded to "no personal-account references" with explicit note that
`providers.tf` `owner` is the single allowed mention of the org login.
templates/ removed from the layout block. Mentions of `templatefile()`
for bundled templates replaced by the data-http fetch pattern.
Verification
------------
- tofu init -backend=false and tofu validate -> green
- pre-commit run --all-files -> all hooks pass
Assisted-by: Claude <noreply@anthropic.com>
Merged
8 tasks
JacobPEvans-personal
added a commit
that referenced
this pull request
May 31, 2026
…ate, conventional commits (#4) Codifies the pre-Terraform live state of the org-level rulesets and applies the directives from #2/#3 review. Single PR because the imports must land together with the new resources to avoid duplicate ruleset creation on next apply. New rulesets ------------ - `github_organization_ruleset.org_branch_protection` — non-bypassable quality gate on every default branch. Reverse-engineered from the pre-Terraform "main" org ruleset (id 15555419) and extended with strict Conventional Commits enforcement. Rules: - required_linear_history = true - required_signatures = true - branch_name_pattern = starts_with (main|develop|feat|fix|hotfix|release|chore) - commit_message_pattern = regex enforcing Conventional Commits v1.0.0 - pull_request = thread resolution required, 0 approvers (the approver requirement lives in org_review_gate with admin bypass) No bypass_actors: signed commits, linear history, and Conventional Commits apply to every actor including OrganizationAdmins. - `github_organization_ruleset.org_review_gate` — separate ruleset so the admin bypass below doesn't accidentally weaken the quality gates above. Rules: - pull_request { required_approving_review_count = 1, require_code_owner_review = true, required_review_thread_resolution = true, allowed_merge_methods = [squash, rebase] } bypass_actors: OrganizationAdmin (actor_id=1, the API constant) in pull_request mode. Any OrganizationAdmin merges their own PRs without external review; non-admin actors must obtain the review. GitHub rulesets do not support per-author conditional rules. The closest approximation — "everyone but an admin needs a reviewer" — is implemented via OrganizationAdmin bypass. Granting a new account the OrganizationAdmin role extends this bypass to them. Existing ruleset reconciled --------------------------- - `github_organization_ruleset.markdown_lint` updated to match live state for clean import: ref_name = ~ALL (was ~DEFAULT_BRANCH), do_not_enforce_on_create = true. Default enforcement stays "evaluate" (legacy default); pass `-var markdown_lint_enforcement=active` to flip on. Live is currently "disabled" — apply moves it to "evaluate". Import blocks ------------- Declared in rulesets.tf so `tofu apply` adopts pre-Terraform state in the same run that adds the new resources: import { to = github_organization_ruleset.org_branch_protection id = "15555419" } import { to = github_organization_ruleset.markdown_lint id = "17062292" } org_review_gate is new (no existing live ruleset), so no import needed. Config — no magic numbers ------------------------- - config/rulesets-defaults.yml gains a `branch_protection` block with the branch name pattern, the Conventional Commits regex, and the allowed merge methods. Patterns are tunable here without editing .tf. - locals.tf decodes the full config file once and exposes per-section locals (push_protection_defaults, branch_protection_defaults). Conventions — identities out of source tree ------------------------------------------- - AGENTS.md adds a "No identities anywhere except providers.tf owner" rule. No usernames, account logins, or person-tied identifiers in .tf resource bodies, comments, variable descriptions, config/*.yml, or CODEOWNERS-style files committed in this repo. CODEOWNERS for managed repos materializes at apply time from a Terraform variable. - The `.github/CODEOWNERS` file that an earlier commit attempt added is intentionally NOT in this PR — it would have hardcoded an owner login. CODEOWNERS for this repo and all dryvist repos lands in a follow-up PR via `github_repository_file` with the owner identity as a `-var` input. Conventions — cost policy ------------------------- - AGENTS.md adds a "Cost policy" section documenting GitHub's pricing model and the rule "never apply a policy or enable a feature that costs money unless the PR body declares the cost and the operator approves it." Includes a matrix of free / GHAS-gated / metered / subscription features as of 2026-05, with sources, and a per-PR cost-impact checklist for any change that touches Actions, GHAS, Codespaces, Copilot, or org/repo settings affecting those. Variables --------- - New: org_branch_protection_enforcement (default "active") - New: org_review_gate_enforcement (default "active") Apply ----- Requires the ORG_ADMIN token tier (gh-claude-org-admin). Apply will: 1. Adopt rulesets 15555419 and 17062292 into Terraform state 2. Rename the imported rulesets to org-branch-protection / org-markdown-lint 3. Add commit_message_pattern rule to the imported branch-protection ruleset 4. Create new org-review-gate ruleset 5. Move markdown_lint enforcement from disabled -> evaluate Cost impact ----------- Free — all ruleset rules are native GitHub features at zero cost on any plan. The markdown_lint workflow runs on Actions; public repos are free, private repos count against the Team-plan free-tier minutes (3000/month GitHub-hosted, $0.002/min self-hosted as of 2026-03-01). No GHAS feature flipped on by this PR. Verification ------------ - tofu init -backend=false and tofu validate -> green - pre-commit run --all-files -> all hooks pass Assisted-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 0 of a multi-step plan to make
terraform-githubthe single source of truth for all org-level configuration — rulesets, actions settings, per-repo file content, labels. Foundation only: no new resources, no apply needed.Updated 2026-05-30 to address review feedback (see commit
666f625):var.dot_github_repository_idwith default1220572589and a description mentioning the org login was a magic number wrapped in another magic number. Replaced withdata "github_repository" "dot_github" { name = ".github" }in a newdata.tf. The org is implied by the provider'sowner; the numeric ID is resolved at apply time.templates/LICENSE.mit.tmplremoved. Canonical license text belongs to its upstream — Step 7's LICENSE enforcement fetches the MIT body viadata "http"againsthttps://api.github.com/licenses/mitat apply time, not from a committed copy..githubis referenced by role ("the org's.githubrepo"), not by the literaldryvist/.githubpath. Theproviders.tfowneris the only place the org login appears.What lands
data "<resource>" { … }indata.tffor identifiers GitHub already knows;variables.tffor true inputs;config/<name>.ymlfor thresholds and lists.config/rulesets-defaults.yml— push-protection defaults (1 MB max file size, banned extensions.env .pem .key .p12 .pfx). Step 1's nativeorg_push_protectionruleset consumes this viayamldecode(file(...)). The file-size enforcement is native (max_file_sizepush rule) — no custom workflow.main.tf+outputs.tf— resolves the two pre-existing tflintterraform_standard_module_structurewarnings.main.tfdocuments the file-split convention;outputs.tfis intentionally empty per the check's own warning text.AGENTS.md+README.md— document the no-magic-numbers / org-agnostic-prose convention; describedata.tfandconfig/; note that canonical text (LICENSE, COC, etc.) is fetched at apply time. Themarkdown_lint_enforcementvariable keeps its legacyevaluatedefault — flipping silently changes apply behavior — but new rulesets default toactive.Plan context (revised)
After this PR, the actual
terraform apply-bearing steps. Step ordering changed since PR #9 indryvist/.githubwas the wrong direction (custom file-size workflow) and got closed.org_push_protectionruleset — nativemax_file_size+file_extension_restrictionpush rules. Consumesconfig/rulesets-defaults.yml. Needsgh-claude-org-adminrelaunch for apply.for_each: labels (canonical list fromconfig/labels.yml), MIT LICENSE (body fetched fromhttps://api.github.com/licenses/mitat apply time, no local template), CODEOWNERS..file-size.ymloverrides, the now-redundant_file-size.ymlinJacobPEvans-personal/.github).dryvist/.github.JacobPEvans-personal/.githubCLAUDE.md → AGENTS.md restructure (separate, not in this PR's chain).Test plan
tofu init -backend=false && tofu validate— green (with the new data source)pre-commit run --all-files(insidedirenv exec .) — every hook passes, tflint cleandata.github_repository.dot_githubwill be resolved on the next live apply; on its own the PR is observably a no-op against the org.