Compare commits
5
Commits
fix-cleanup
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28e2c76215 | ||
|
|
1c4ac7f136 | ||
|
|
037edd905a | ||
|
|
4abb613f35 | ||
|
|
629a534e15 |
@@ -18,6 +18,14 @@ test-results/
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# certificates / keys (never commit)
|
||||
*.crt
|
||||
*.key
|
||||
*.pem
|
||||
*.pfx
|
||||
*.p12
|
||||
caddy-local-root.crt
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
|
||||
@@ -12,3 +12,8 @@ backend.hcl
|
||||
*.auto.tfvars
|
||||
terraform.tfvars
|
||||
!terraform.tfvars.example
|
||||
|
||||
# Local backend overrides (developer-specific)
|
||||
backend_override.tf
|
||||
*_override.tf
|
||||
!*_override.tf.example
|
||||
|
||||
Generated
+24
@@ -0,0 +1,24 @@
|
||||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/integrations/github" {
|
||||
version = "6.12.1"
|
||||
constraints = "~> 6.2"
|
||||
hashes = [
|
||||
"h1:bGz4LIep/7PVrqy6P8cTYbAJpdxXGrupUJjkCczlzIs=",
|
||||
"zh:3e1a4081ecb9518fdf0074db83c16ad00dc81ffe8249a6e3cf1894e947e28df6",
|
||||
"zh:4cb8224b7f530795b674ac044675f6b22a7c9154f55eb9f76c5af6c7534056a4",
|
||||
"zh:560bc08637926191f6871a89e986022ca67c70afda5bebca34b5216e6fac69c9",
|
||||
"zh:5a70b5d2ac650c5c9819a1875411ebda229d0fcc6c9f57f9d751852ca3cd77ac",
|
||||
"zh:8668d93bd4dc2ffa2545e1473af600a925d479b16033a71a4498a16f3b683c0c",
|
||||
"zh:86eacc6059fd057948e178b665ba5cce74bd5488a9e1035734e60ff5ef1b6f8f",
|
||||
"zh:a329fac98881d8dfc211a9bdc0ec6f2948f0b0c2704d1b6cbe5307403c7ad1b2",
|
||||
"zh:dadd44abab3c52b9d572955afaef1658790e17ea355ee22b58996d81d28e02d8",
|
||||
"zh:de9f455ef342cc38fb76bce844bfcd376fb81a4b9f9bc2fae023ff99efdf1338",
|
||||
"zh:f8c6d2e8351b334491790358574e0a30a7c6d7f5b80f7daf32a7c0f3e9b1ab19",
|
||||
"zh:fab41971a3edee04ab6eceaeab4eeb9a2b2f38a2af3b06eda93e2117b64994be",
|
||||
"zh:fb1279b566dd9c8c117b2e4e0cc8344413b8fc8f2a3e24be22a9b2610551777b",
|
||||
"zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25",
|
||||
"zh:fe79d2a861fb9af420fa5bd7f02c031b2a0a3edf5dbc46022c8ecc7a33cf2b6d",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# GitHub repo configuration (OpenTofu)
|
||||
|
||||
Manages branch protection (via repository rulesets) and deployment
|
||||
environments for `JMR-dev/dev_blog`.
|
||||
|
||||
This module is also the source-of-truth copy that is mirrored to the
|
||||
[`gh-repo-bootstrap`](https://github.com/JMR-dev/gh-repo-bootstrap) repo,
|
||||
which packages it as a reusable module + `gh` CLI extension
|
||||
(`gh repo-bootstrap <owner>/<repo>`).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [OpenTofu](https://opentofu.org/) >= 1.8
|
||||
- A GitHub token with `repo` + `admin:repo_hook` scopes. Easiest:
|
||||
```sh
|
||||
export GITHUB_TOKEN=$(gh auth token)
|
||||
```
|
||||
- (For remote state) Cloudflare R2 credentials — copy
|
||||
`backend.hcl.example` to `backend.hcl` and fill it in.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
# First time only:
|
||||
tofu init -backend-config=backend.hcl
|
||||
|
||||
# Apply:
|
||||
tofu apply
|
||||
```
|
||||
|
||||
To run with **local state** (no R2 needed) for experimentation, comment
|
||||
out the `backend "s3"` block in `versions.tf`, then `tofu init` again.
|
||||
|
||||
## What it manages
|
||||
|
||||
- A repository ruleset on the default branch enforcing:
|
||||
- No deletion
|
||||
- No force-push
|
||||
- Required PR with N approving reviews (configurable)
|
||||
- Resolved review threads
|
||||
- Optional signed commits
|
||||
- The set of GitHub deployment environments listed in `environments`.
|
||||
|
||||
It does **not** create the repository itself, and does not manage
|
||||
repo-level settings (merge button options, default branch, topic, etc.).
|
||||
Add those via `gh api` or import the `github_repository` resource if you
|
||||
need them under OpenTofu control.
|
||||
@@ -0,0 +1,8 @@
|
||||
# Example Cloudflare R2 backend configuration.
|
||||
# Copy to `backend.hcl` (gitignored) and fill in your values, then run:
|
||||
# tofu init -backend-config=backend.hcl
|
||||
|
||||
bucket = "dev-blog-tfstate"
|
||||
endpoints = { s3 = "https://<account-id>.r2.cloudflarestorage.com" }
|
||||
access_key = "<R2_ACCESS_KEY_ID>"
|
||||
secret_key = "<R2_SECRET_ACCESS_KEY>"
|
||||
@@ -0,0 +1,46 @@
|
||||
data "github_repository" "this" {
|
||||
name = var.repo_name
|
||||
}
|
||||
|
||||
resource "github_repository_ruleset" "default_branch" {
|
||||
repository = data.github_repository.this.name
|
||||
name = var.ruleset_name
|
||||
target = "branch"
|
||||
enforcement = "active"
|
||||
|
||||
conditions {
|
||||
ref_name {
|
||||
include = ["refs/heads/${var.default_branch}"]
|
||||
exclude = []
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "bypass_actors" {
|
||||
for_each = var.bypass_actors
|
||||
content {
|
||||
actor_id = bypass_actors.value.actor_id
|
||||
actor_type = bypass_actors.value.actor_type
|
||||
bypass_mode = bypass_actors.value.bypass_mode
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
deletion = true
|
||||
non_fast_forward = true
|
||||
required_signatures = var.require_signed_commits
|
||||
|
||||
pull_request {
|
||||
required_approving_review_count = var.required_reviews
|
||||
dismiss_stale_reviews_on_push = true
|
||||
require_code_owner_review = false
|
||||
require_last_push_approval = false
|
||||
required_review_thread_resolution = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "github_repository_environment" "envs" {
|
||||
for_each = toset(var.environments)
|
||||
repository = data.github_repository.this.name
|
||||
environment = each.value
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
output "repository_full_name" {
|
||||
value = data.github_repository.this.full_name
|
||||
description = "Full name (owner/repo) of the repository being managed."
|
||||
}
|
||||
|
||||
output "ruleset_id" {
|
||||
value = github_repository_ruleset.default_branch.id
|
||||
description = "ID of the branch protection ruleset."
|
||||
}
|
||||
|
||||
output "environments" {
|
||||
value = sort([for e in github_repository_environment.envs : e.environment])
|
||||
description = "Environments managed by this configuration."
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
repo_owner = "JMR-dev"
|
||||
repo_name = "dev_blog"
|
||||
default_branch = "main"
|
||||
required_reviews = 1
|
||||
require_signed_commits = false
|
||||
environments = [
|
||||
"production",
|
||||
"staging",
|
||||
"preview",
|
||||
"development",
|
||||
]
|
||||
@@ -0,0 +1,56 @@
|
||||
variable "repo_owner" {
|
||||
description = "GitHub user or organization that owns the repository."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "repo_name" {
|
||||
description = "Repository name (without owner prefix)."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "default_branch" {
|
||||
description = "Branch protected by the ruleset."
|
||||
type = string
|
||||
default = "main"
|
||||
}
|
||||
|
||||
variable "required_reviews" {
|
||||
description = "Number of required approving reviews on PRs targeting the default branch."
|
||||
type = number
|
||||
default = 1
|
||||
}
|
||||
|
||||
variable "require_signed_commits" {
|
||||
description = "Require signed commits on the protected branch."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "environments" {
|
||||
description = "List of GitHub deployment environments to ensure exist."
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "ruleset_name" {
|
||||
description = "Name to give the branch protection ruleset."
|
||||
type = string
|
||||
default = "default-branch-protection"
|
||||
}
|
||||
|
||||
variable "bypass_actors" {
|
||||
description = <<-EOT
|
||||
Actors permitted to bypass the ruleset. Each entry needs:
|
||||
- actor_id: numeric ID (for built-in repo roles: 1=read, 2=triage,
|
||||
3=write, 4=maintain, 5=admin)
|
||||
- actor_type: one of RepositoryRole, Team, Integration,
|
||||
OrganizationAdmin, DeployKey
|
||||
- bypass_mode: "always" or "pull_request"
|
||||
EOT
|
||||
type = list(object({
|
||||
actor_id = number
|
||||
actor_type = string
|
||||
bypass_mode = string
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
terraform {
|
||||
required_version = ">= 1.8.0"
|
||||
|
||||
required_providers {
|
||||
github = {
|
||||
source = "integrations/github"
|
||||
version = "~> 6.2"
|
||||
}
|
||||
}
|
||||
|
||||
# Cloudflare R2 (S3-compatible) backend, mirroring infra/versions.tf.
|
||||
# Account-specific values are supplied at init time:
|
||||
# tofu init -backend-config=backend.hcl
|
||||
# For local-only experimentation, comment this entire block out and
|
||||
# OpenTofu will fall back to the local backend.
|
||||
backend "s3" {
|
||||
key = "dev_blog/github.tfstate"
|
||||
region = "auto"
|
||||
|
||||
skip_credentials_validation = true
|
||||
skip_metadata_api_check = true
|
||||
skip_region_validation = true
|
||||
skip_requesting_account_id = true
|
||||
skip_s3_checksum = true
|
||||
use_path_style = true
|
||||
}
|
||||
}
|
||||
|
||||
provider "github" {
|
||||
owner = var.repo_owner
|
||||
# Token is read from the GITHUB_TOKEN environment variable.
|
||||
# Easiest source: `export GITHUB_TOKEN=$(gh auth token)`.
|
||||
}
|
||||
Reference in New Issue
Block a user