From aa655123925bfb7c695aea89213fd27d202ba0b2 Mon Sep 17 00:00:00 2001 From: JacobPEvans <20714140+JacobPEvans-personal@users.noreply.github.com> Date: Fri, 29 May 2026 07:08:05 -0400 Subject: [PATCH 1/2] feat: seed org governance IaC with markdown-lint required-workflow ruleset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First slice of dryvist org governance as code. Defines an org-level ruleset that requires dryvist/.github's markdownlint workflow to pass on every repo's default branch — single source of truth (workflow + config both in dryvist/.github), replacing per-repo .markdownlint* files and per-repo `uses:` wiring. Enforcement defaults to "evaluate" (dry-run) for a safe org-wide rollout; flip to "active" once the fleet is green. Assisted-by: Claude --- .envrc | 1 + .gitignore | 18 +++++++++++ .terraform-version | 1 + README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++- providers.tf | 7 +++++ rulesets.tf | 39 ++++++++++++++++++++++++ variables.tf | 19 ++++++++++++ versions.tf | 16 ++++++++++ 8 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 .terraform-version create mode 100644 providers.tf create mode 100644 rulesets.tf create mode 100644 variables.tf create mode 100644 versions.tf diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..e0e83cd --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake "github:JacobPEvans/nix-devenv?dir=shells/terraform" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d301f20 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Terraform / OpenTofu +.terraform/ +.terraform.lock.hcl +*.tfstate +*.tfstate.* +*.tfstate.backup +crash.log +*.tfvars +*.tfvars.json +override.tf +override.tf.json +*_override.tf +*_override.tf.json +.terraformrc +terraform.rc + +# direnv +.direnv/ diff --git a/.terraform-version b/.terraform-version new file mode 100644 index 0000000..e34208c --- /dev/null +++ b/.terraform-version @@ -0,0 +1 @@ +1.15.4 diff --git a/README.md b/README.md index 2c95081..2e7b626 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,74 @@ # terraform-github -Terraform-managed GitHub org governance for dryvist: rulesets, required workflows, and org/repo settings as code. + +Terraform-managed GitHub **organization governance** for the `dryvist` org: +rulesets, required workflows, and (over time) org/repo settings — as code, in +one place, instead of click-ops scattered across 32 repos. + +## Why this exists + +GitHub doesn't auto-inherit CI config across repos. The old pattern was a +reusable workflow plus a per-repo `uses:` call and a per-repo `.markdownlint*` +file in every repo — N copies that drift. Now that `dryvist` is a real org (on +the Team plan, which gained org rulesets in June 2025), governance can be +defined **once** here and applied to **every** repo automatically. + +## What it manages today + +| Resource | Effect | +| --- | --- | +| `github_organization_ruleset.markdown_lint` | Requires the markdownlint workflow in `dryvist/.github` to pass on the default branch of **every** repo in the org. Single source of truth: the workflow + `.markdownlint-cli2.yaml` both live in `dryvist/.github`. | + +Start small — this is the seed. Branch protection, the verified-signature +policy, file-size limits, and repo settings can move here next. + +## Layout + +```text +versions.tf # terraform + provider pins, S3 backend (partial) +providers.tf # github provider (owner = dryvist), GITHUB_TOKEN auth +variables.tf # markdown_lint_enforcement (evaluate | active | disabled) +rulesets.tf # org rulesets +``` + +## Installation + +Clone and enter the dev shell (direnv auto-loads OpenTofu via the org's Nix +flake): + +```bash +git clone git@github.com:dryvist/terraform-github.git +cd terraform-github +direnv allow # provides tofu, terraform, terragrunt +``` + +**Auth and tier:** the provider reads `GITHUB_TOKEN`. Managing org rulesets +requires `admin:org` — the **ORG_ADMIN** token tier (`gh-claude-org-admin`). +The default `DRYVIST` tier is read-only on org rulesets and will `403` on apply. + +## Usage + +State lives in S3 (org convention). Backend values (bucket / key / region) are +supplied at init — never committed, because the bucket name embeds the AWS +account ID: + +```bash +tofu init -backend-config=bucket= \ + -backend-config=key=terraform-github/terraform.tfstate \ + -backend-config=region=us-east-2 +``` + +Validation needs no backend or credentials: + +```bash +tofu init -backend=false && tofu validate +``` + +**Rolling out a rule safely.** Org-wide enforcement can block merges everywhere +at once. Default the enforcement to `evaluate` (dry-run — reports in +**Rulesets / Insights** without blocking), confirm the fleet is green, then flip +to `active`: + +```bash +tofu apply # evaluate (default) +tofu apply -var markdown_lint_enforcement=active # enforce +``` diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..089e325 --- /dev/null +++ b/providers.tf @@ -0,0 +1,7 @@ +# Authentication: the provider reads the GITHUB_TOKEN environment variable. +# Managing org-level rulesets requires a token with `admin:org`. Apply with the +# ORG_ADMIN token tier (gh-claude-org-admin); the default DRYVIST tier is +# read-only on org rulesets and will 403 on apply. +provider "github" { + owner = "dryvist" +} diff --git a/rulesets.tf b/rulesets.tf new file mode 100644 index 0000000..43266b6 --- /dev/null +++ b/rulesets.tf @@ -0,0 +1,39 @@ +locals { + # dryvist/.github holds the single source of truth for org CI: the reusable + # markdownlint workflow AND the canonical .markdownlint-cli2.yaml config. + # This numeric id is stable for the life of the repo (rename-safe). + dot_github_repository_id = 1220572589 +} + +# Org-wide markdown linting, enforced as a Required Workflow. +# +# Every repo's default-branch PRs must pass dryvist/.github's markdownlint +# workflow. The rule references ONE workflow + ONE config (both in +# dryvist/.github), so there are no per-repo markdownlint files to drift — +# this is the org-native replacement for per-repo `uses:` wiring. +resource "github_organization_ruleset" "markdown_lint" { + name = "org-markdown-lint" + target = "branch" + enforcement = var.markdown_lint_enforcement + + conditions { + ref_name { + include = ["~DEFAULT_BRANCH"] + exclude = [] + } + repository_name { + include = ["~ALL"] + exclude = [] + } + } + + rules { + required_workflows { + required_workflow { + repository_id = local.dot_github_repository_id + path = ".github/workflows/markdownlint.yml" + ref = "refs/heads/main" + } + } + } +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..48638a6 --- /dev/null +++ b/variables.tf @@ -0,0 +1,19 @@ +variable "markdown_lint_enforcement" { + description = <<-EOT + Enforcement mode for the org-wide markdown-lint ruleset. + + Start at "evaluate" (dry-run): the ruleset reports results in the org + Rulesets > Insights tab WITHOUT blocking any merges. This lets the rule roll + out across every repo safely before the whole org's markdown is compliant. + Flip to "active" once Insights shows the fleet is green. + + One of: disabled, evaluate, active. + EOT + type = string + default = "evaluate" + + validation { + condition = contains(["disabled", "evaluate", "active"], var.markdown_lint_enforcement) + error_message = "markdown_lint_enforcement must be one of: disabled, evaluate, active." + } +} diff --git a/versions.tf b/versions.tf new file mode 100644 index 0000000..cec21cc --- /dev/null +++ b/versions.tf @@ -0,0 +1,16 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } + + # Remote state in S3 (org convention). Backend values are supplied at init + # time (bucket / key / region) via `-backend-config` or terragrunt and are + # never hardcoded here, because the bucket name embeds the AWS account ID. + # See README.md → "State". Use `tofu init -backend=false` for validation only. + backend "s3" {} +} From 057d83e980838aaec5a8caea7f20b109c4d0968f Mon Sep 17 00:00:00 2001 From: JacobPEvans <20714140+JacobPEvans-personal@users.noreply.github.com> Date: Fri, 29 May 2026 08:12:05 -0400 Subject: [PATCH 2/2] chore: adopt terraform-proxmox conventions; pin all refs to dryvist Mirrors terraform-proxmox's meta/quality/CI conventions: tflint, pre-commit (terraform hooks + checkov + tofu test), CI-gate Merge Gate, release-please, renovate, LICENSE, gitattributes, CHANGELOG, AGENTS/CLAUDE, terragrunt S3 backend. Deliberate deviations: every reference is dryvist-only (no personal-account owners); no local markdownlint config (the org ruleset this repo defines is the single source of truth); terraform_docs hook omitted (its README injection trips that same org markdownlint). .envrc now points at dryvist/nix-devenv. Assisted-by: Claude --- .envrc | 2 +- .gitattributes | 2 + .github/workflows/ci-gate.yml | 103 +++++++++++++++++ .pre-commit-config.yaml | 55 +++++++++ .readme-validator.yaml | 9 ++ .release-please-manifest.json | 3 + .tflint.hcl | 27 +++++ AGENTS.md | 48 ++++++++ CHANGELOG.md | 9 ++ CLAUDE.md | 3 + LICENSE | 202 ++++++++++++++++++++++++++++++++++ README.md | 22 ++-- VERSION | 1 + release-please-config.json | 20 ++++ renovate.json | 7 ++ terragrunt.hcl | 30 +++++ 16 files changed, 531 insertions(+), 12 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/ci-gate.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .readme-validator.yaml create mode 100644 .release-please-manifest.json create mode 100644 .tflint.hcl create mode 100644 AGENTS.md create mode 100644 CHANGELOG.md create mode 100644 CLAUDE.md create mode 100644 LICENSE create mode 100644 VERSION create mode 100644 release-please-config.json create mode 100644 renovate.json create mode 100644 terragrunt.hcl diff --git a/.envrc b/.envrc index e0e83cd..a2f8c2b 100644 --- a/.envrc +++ b/.envrc @@ -1 +1 @@ -use flake "github:JacobPEvans/nix-devenv?dir=shells/terraform" +use flake "github:dryvist/nix-devenv?dir=shells/terraform" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f2914ac --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +.github/workflows/*.lock.yml linguist-generated=true merge=ours +.github/aw/** linguist-generated=true diff --git a/.github/workflows/ci-gate.yml b/.github/workflows/ci-gate.yml new file mode 100644 index 0000000..16e2110 --- /dev/null +++ b/.github/workflows/ci-gate.yml @@ -0,0 +1,103 @@ +# CI Gate - Conditional Required Checks +# +# FRAMEWORK: Merge Gatekeeper Pattern +# ==================================== +# Solves: "Required checks that only run when relevant files change" +# +# Problem: GitHub branch protection only supports "always required" or "not required" +# Path-filtered workflows that don't run = pending forever = blocks merge +# +# Solution: Single workflow that: +# 1. Always triggers on ALL PRs (no path filters at workflow level) +# 2. Detects which file categories changed +# 3. Runs check jobs conditionally (skipped = success) +# 4. Final "Merge Gate" job aggregates all results +# +# Branch Protection: Set ONLY "Merge Gate" as required check +# +# NOTE: This repo defines the org-wide markdown-lint ruleset, so markdown is +# covered org-side — there is no per-repo markdown-lint job here. File-size +# reusables are off-limits (they live only under the personal account). +# +# Adding New Checks: +# 1. Add a filter pattern under `changes.steps.filter.with.filters` +# 2. Add a job gated on that filter output +# 3. Add the job name to `gate.needs` array and `allowed-skips` + +name: CI Gate + +on: + pull_request: + types: [opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: read + +jobs: + # ========================================================================== + # CHANGE DETECTION + # ========================================================================== + changes: + name: Detect Changes + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + terraform: ${{ steps.filter.outputs.terraform }} + steps: + - uses: actions/checkout@v6 + - uses: dorny/paths-filter@v4 + id: filter + with: + filters: | + terraform: + - '**.tf' + - '**.tfvars' + - '.terraform.lock.hcl' + - 'tests/**' + + # ========================================================================== + # CONDITIONAL CHECKS + # ========================================================================== + terraform: + name: Terraform + needs: changes + if: needs.changes.outputs.terraform == 'true' + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Setup OpenTofu + uses: opentofu/setup-opentofu@v2 + - name: OpenTofu Format + run: tofu fmt -check + - name: OpenTofu Init + run: tofu init -backend=false + - name: OpenTofu Validate + run: tofu validate -no-color + - name: Setup TFLint + uses: terraform-linters/setup-tflint@v4 + - name: TFLint + run: tflint --init && tflint -f compact + + # ========================================================================== + # MERGE GATE - The ONLY required check in branch protection + # ========================================================================== + gate: + name: Merge Gate + needs: [changes, terraform] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + - name: Check all results + uses: re-actors/alls-green@release/v1 + with: + allowed-skips: terraform + jobs: ${{ toJSON(needs) }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..56c0be2 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,55 @@ +default_install_hook_types: [pre-commit, pre-push] + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + # Terraform static checks - run on every commit via direnv-provided tools + # (Nix shell at .envrc supplies tofu, terraform, tflint). + # Same checks run in CI via .github/workflows/ci-gate.yml as a safety net. + # NOTE: the README-docs-injection hook is intentionally omitted. It writes an + # HTML-anchored table into README.md that trips the org-wide markdownlint + # ruleset this repo itself defines (MD033 no-inline-html) — self-defeating + # governance. + - repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.92.0 + hooks: + - id: terraform_fmt + - id: terraform_validate + - id: terraform_tflint + args: + - --args=--only=terraform_deprecated_interpolation + - --args=--only=terraform_deprecated_index + - --args=--only=terraform_unused_declarations + - --args=--only=terraform_comment_syntax + - --args=--only=terraform_documented_outputs + - --args=--only=terraform_documented_variables + - --args=--only=terraform_typed_variables + - --args=--only=terraform_module_pinned_source + - --args=--only=terraform_naming_convention + - --args=--only=terraform_required_version + - --args=--only=terraform_required_providers + - --args=--only=terraform_standard_module_structure + + # Checkov - Terraform IaC security scanner + - repo: https://github.com/bridgecrewio/checkov.git + rev: 3.2.526 + hooks: + - id: checkov + args: ['-d', '.', '--framework', 'terraform', '--quiet'] + pass_filenames: false + + # Local hooks using nix toolchain + - repo: local + hooks: + - id: tofu-test + name: tofu test (mock providers) + entry: direnv exec . bash -c 'tofu init -backend=false -no-color && tofu test -no-color' + language: system + files: '\.(tf|hcl)$' + pass_filenames: false diff --git a/.readme-validator.yaml b/.readme-validator.yaml new file mode 100644 index 0000000..55bd2a2 --- /dev/null +++ b/.readme-validator.yaml @@ -0,0 +1,9 @@ +# README validator configuration for terraform-github +# Terraform governance READMEs use Usage + Requirements instead of Installation +required_sections: + - Usage + - Requirements +optional_sections: + - Contributing + - License + - API diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..466df71 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.0" +} diff --git a/.tflint.hcl b/.tflint.hcl new file mode 100644 index 0000000..bc6e085 --- /dev/null +++ b/.tflint.hcl @@ -0,0 +1,27 @@ +config { + format = "compact" + call_module_type = "local" +} + +plugin "terraform" { + enabled = true + preset = "recommended" + version = "0.14.1" + source = "github.com/terraform-linters/tflint-ruleset-terraform" +} + +rule "terraform_documented_variables" { + enabled = true +} + +rule "terraform_documented_outputs" { + enabled = true +} + +rule "terraform_required_providers" { + enabled = true +} + +rule "terraform_required_version" { + enabled = true +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..05fd3aa --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,48 @@ +# AI Agents Configuration + +## Repo Purpose + +`dryvist` organization governance as code: GitHub org-level rulesets, required +workflows, and (over time) org/repo settings — defined once and applied to +every repo in the org via the `integrations/github` provider, instead of +click-ops scattered across the fleet. + +This repo's META / quality / CI conventions are mirrored from +[terraform-proxmox](https://github.com/dryvist/terraform-proxmox), which is the +canonical source for the workspace's Terraform tooling patterns. Mirror its +tooling, not its Proxmox domain content. + +## Conventions + +- **dryvist-only references.** Every owner reference in this repo — provider + `owner`, `uses:`, renovate presets, remotes, links — is `dryvist`. Do not + introduce any personal-account owner references; this repo manages the + `dryvist` org and must point only at it. +- **No local markdownlint config.** This repo *defines* the org-wide + markdownlint ruleset (`github_organization_ruleset.markdown_lint`), whose + single source of truth is the workflow + `.markdownlint-cli2.yaml` in + `dryvist/.github`. Adding a local `.markdownlint*` file here would create the + exact per-repo drift this repo exists to eliminate — never add one. +- **`terraform_docs` is intentionally omitted** from `.pre-commit-config.yaml`: + it injects HTML-anchored tables into the README that trip the org markdownlint + ruleset this repo itself defines (MD033). +- Conventional commits: `type(scope): description`. +- All commits must be GPG-signed (signing key varies per machine; see local + `git config user.signingkey`). +- Never commit secrets — store references, not values. Backend bucket/key and + the `GITHUB_TOKEN` are supplied at runtime, never hardcoded. + +## Applying + +Org ruleset changes require the **ORG_ADMIN** token tier +(`gh-claude-org-admin`) — the provider needs `admin:org`. The default `DRYVIST` +tier is read-only on org rulesets and will `403` on apply. + +Roll out enforcement safely with the `evaluate` → `active` path: new org-wide +rules default to `evaluate` (dry-run, reports in Rulesets / Insights without +blocking merges). Confirm the fleet is green in Insights, then flip to `active`. + +```bash +tofu apply # evaluate (default) +tofu apply -var markdown_lint_enforcement=active # enforce +``` diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bcefb5d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +Releases are automated via release-please from Conventional Commits. + +## [Unreleased] diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5a1f43a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# AI Agents Configuration + +@AGENTS.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b45f513 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 2e7b626..30f9587 100644 --- a/README.md +++ b/README.md @@ -30,20 +30,20 @@ variables.tf # markdown_lint_enforcement (evaluate | active | disabled) rulesets.tf # org rulesets ``` -## Installation +## Requirements -Clone and enter the dev shell (direnv auto-loads OpenTofu via the org's Nix -flake): +- **OpenTofu** (>= 1.6) and the `integrations/github` provider, pinned in + `versions.tf`. The dev shell supplies the toolchain via direnv: -```bash -git clone git@github.com:dryvist/terraform-github.git -cd terraform-github -direnv allow # provides tofu, terraform, terragrunt -``` + ```bash + git clone git@github.com:dryvist/terraform-github.git + cd terraform-github && direnv allow # provides tofu, terraform, terragrunt + ``` -**Auth and tier:** the provider reads `GITHUB_TOKEN`. Managing org rulesets -requires `admin:org` — the **ORG_ADMIN** token tier (`gh-claude-org-admin`). -The default `DRYVIST` tier is read-only on org rulesets and will `403` on apply. +- **`GITHUB_TOKEN` with `admin:org`** (the ORG_ADMIN token tier, + `gh-claude-org-admin`) to create or modify org rulesets. The provider reads it + from the environment. +- **S3 state backend** access — bucket / key / region supplied at init (see Usage). ## Usage diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..0cb6f1e --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "terraform-module", + "version-file": "VERSION", + "include-v-in-tag": true, + "changelog-sections": [ + {"type": "feat", "section": "Features"}, + {"type": "fix", "section": "Bug Fixes"}, + {"type": "perf", "section": "Performance"}, + {"type": "docs", "section": "Documentation", "hidden": true}, + {"type": "chore", "section": "Miscellaneous", "hidden": true}, + {"type": "refactor", "section": "Refactoring", "hidden": true}, + {"type": "test", "section": "Tests", "hidden": true}, + {"type": "ci", "section": "CI", "hidden": true} + ] + } + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..73ee2cf --- /dev/null +++ b/renovate.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + "local>dryvist/.github:renovate-presets" + ] +} diff --git a/terragrunt.hcl b/terragrunt.hcl new file mode 100644 index 0000000..7541e1d --- /dev/null +++ b/terragrunt.hcl @@ -0,0 +1,30 @@ +# Terragrunt configuration for terraform-github (dryvist org governance). +# +# Governance state is small and has no SOPS/Doppler/deployment.json layers — +# this file only wires the shared S3 remote state backend. The github provider +# reads GITHUB_TOKEN from the environment (ORG_ADMIN tier to apply). + +terraform { + source = "." +} + +# Remote state backend configuration using S3 (org convention). +# The bucket name embeds the AWS account id, so it is resolved at runtime +# rather than committed. +remote_state { + backend = "s3" + generate = { + path = "backend.tf" + if_exists = "overwrite_terragrunt" + } + config = { + bucket = "terraform-proxmox-state-useast2-${get_aws_account_id()}" + key = "terraform-github/terraform.tfstate" + region = "us-east-2" + encrypt = true + use_lockfile = true + + # Retry configuration for transient S3 failures + max_retries = 5 + } +}