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
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use flake "github:dryvist/nix-devenv?dir=shells/terraform"
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.github/workflows/*.lock.yml linguist-generated=true merge=ours
.github/aw/** linguist-generated=true
103 changes: 103 additions & 0 deletions .github/workflows/ci-gate.yml
Original file line number Diff line number Diff line change
@@ -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) }}
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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/
55 changes: 55 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions .readme-validator.yaml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.1.0"
}
1 change: 1 addition & 0 deletions .terraform-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.15.4
27 changes: 27 additions & 0 deletions .tflint.hcl
Original file line number Diff line number Diff line change
@@ -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
}
48 changes: 48 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -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
```
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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]
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# AI Agents Configuration

@AGENTS.md
Loading
Loading