feat: initial terraform-git project

Manage Gitea resources via Terraform/Terragrunt with YAML-driven config.

Resources managed:
- Organisation (unkin)
- 28 repositories with settings
- 6 teams with members
- 13 branch protection rules
- 9 Woodpecker CI repo activations
- Deploy key module (ready, no keys yet)

Config structure: config/<service>/<org>/<type>/<object>.yaml
Consul backend for state, Vault for auth tokens.
This commit is contained in:
2026-06-10 23:31:19 +10:00
parent a0c1f3e630
commit c87b3ac471
66 changed files with 1413 additions and 0 deletions
+219
View File
@@ -0,0 +1,219 @@
import {
to = module.organisation["git.unkin.net/unkin"].gitea_org.this
id = "9"
}
import {
to = module.repository["git.unkin.net/unkin/puppet-prod"].gitea_repository.this
id = "2"
}
import {
to = module.repository["git.unkin.net/unkin/puppet-r10k"].gitea_repository.this
id = "3"
}
import {
to = module.repository["git.unkin.net/unkin/rpmbuild-gonic"].gitea_repository.this
id = "23"
}
import {
to = module.repository["git.unkin.net/unkin/docker-almalinux-base"].gitea_repository.this
id = "24"
}
import {
to = module.repository["git.unkin.net/unkin/rpmbuild-internal-ca-certificates"].gitea_repository.this
id = "27"
}
import {
to = module.repository["git.unkin.net/unkin/rpmbuild-template"].gitea_repository.this
id = "29"
}
import {
to = module.repository["git.unkin.net/unkin/rpmbuild-jellyfin-web"].gitea_repository.this
id = "31"
}
import {
to = module.repository["git.unkin.net/unkin/rpmbuild-proxlb"].gitea_repository.this
id = "33"
}
import {
to = module.repository["git.unkin.net/unkin/docker-almalinux-buildrunner"].gitea_repository.this
id = "36"
}
import {
to = module.repository["git.unkin.net/unkin/docker-template"].gitea_repository.this
id = "38"
}
import {
to = module.repository["git.unkin.net/unkin/terraform-vault"].gitea_repository.this
id = "39"
}
import {
to = module.repository["git.unkin.net/unkin/docker-almalinux-jupyterinstance"].gitea_repository.this
id = "40"
}
import {
to = module.repository["git.unkin.net/unkin/rpmbuilder"].gitea_repository.this
id = "41"
}
import {
to = module.repository["git.unkin.net/unkin/docker-almalinux-runnerdnd"].gitea_repository.this
id = "43"
}
import {
to = module.repository["git.unkin.net/unkin/initbuilder"].gitea_repository.this
id = "47"
}
import {
to = module.repository["git.unkin.net/unkin/puppetapi"].gitea_repository.this
id = "50"
}
import {
to = module.repository["git.unkin.net/unkin/terraform-nomad"].gitea_repository.this
id = "53"
}
import {
to = module.repository["git.unkin.net/unkin/packer-images"].gitea_repository.this
id = "59"
}
import {
to = module.repository["git.unkin.net/unkin/app-sudaporn-research-normalised"].gitea_repository.this
id = "60"
}
import {
to = module.repository["git.unkin.net/unkin/app-sudaporn-research-individual"].gitea_repository.this
id = "63"
}
import {
to = module.repository["git.unkin.net/unkin/terraform-incus"].gitea_repository.this
id = "66"
}
import {
to = module.repository["git.unkin.net/unkin/artifactapi"].gitea_repository.this
id = "67"
}
import {
to = module.repository["git.unkin.net/unkin/argocd-apps"].gitea_repository.this
id = "100"
}
import {
to = module.repository["git.unkin.net/unkin/certmanager"].gitea_repository.this
id = "101"
}
import {
to = module.repository["git.unkin.net/unkin/node-lookup"].gitea_repository.this
id = "102"
}
import {
to = module.repository["git.unkin.net/unkin/container-devcompute"].gitea_repository.this
id = "135"
}
import {
to = module.repository["git.unkin.net/unkin/streamstack"].gitea_repository.this
id = "136"
}
import {
to = module.repository["git.unkin.net/unkin/terraform-provider-artifactapi"].gitea_repository.this
id = "137"
}
import {
to = module.team["git.unkin.net/unkin/Owners"].gitea_team.this
id = "3"
}
import {
to = module.team["git.unkin.net/unkin/docker"].gitea_team.this
id = "5"
}
import {
to = module.team["git.unkin.net/unkin/rpmbuild"].gitea_team.this
id = "6"
}
import {
to = module.team["git.unkin.net/unkin/puppet"].gitea_team.this
id = "7"
}
import {
to = module.team["git.unkin.net/unkin/pybuild"].gitea_team.this
id = "9"
}
import {
to = module.team["git.unkin.net/unkin/terraform"].gitea_team.this
id = "12"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/puppet-prod"].woodpecker_repository.this
id = "unkin/puppet-prod"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/puppet-r10k"].woodpecker_repository.this
id = "unkin/puppet-r10k"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/terraform-vault"].woodpecker_repository.this
id = "unkin/terraform-vault"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/rpmbuilder"].woodpecker_repository.this
id = "unkin/rpmbuilder"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/artifactapi"].woodpecker_repository.this
id = "unkin/artifactapi"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/argocd-apps"].woodpecker_repository.this
id = "unkin/argocd-apps"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/certmanager"].woodpecker_repository.this
id = "unkin/certmanager"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/node-lookup"].woodpecker_repository.this
id = "unkin/node-lookup"
}
import {
to = module.woodpecker_repository["git.unkin.net/unkin/terraform-provider-artifactapi"].woodpecker_repository.this
id = "unkin/terraform-provider-artifactapi"
}
+102
View File
@@ -0,0 +1,102 @@
module "organisation" {
source = "./modules/organisation"
for_each = var.organisation
name = each.value.name
description = each.value.description
full_name = each.value.full_name
visibility = each.value.visibility
website = each.value.website
location = each.value.location
repo_admin_change_team_access = each.value.repo_admin_change_team_access
}
module "repository" {
source = "./modules/repository"
for_each = var.repository
name = each.value.name
organisation = each.value.organisation
description = each.value.description
private = each.value.private
default_branch = each.value.default_branch
has_issues = each.value.has_issues
has_wiki = each.value.has_wiki
has_pull_requests = each.value.has_pull_requests
has_projects = each.value.has_projects
allow_merge_commits = each.value.allow_merge_commits
allow_rebase = each.value.allow_rebase
allow_rebase_explicit = each.value.allow_rebase_explicit
allow_squash_merge = each.value.allow_squash_merge
archived = each.value.archived
repo_template = each.value.repo_template
website = each.value.website
autodetect_manual_merge = each.value.autodetect_manual_merge
depends_on = [module.organisation]
}
module "team" {
source = "./modules/team"
for_each = var.team
name = each.value.name
organisation = each.value.organisation
description = each.value.description
permission = each.value.permission
include_all_repositories = each.value.include_all_repositories
can_create_repos = each.value.can_create_repos
repositories = each.value.repositories
members = each.value.members
depends_on = [module.organisation, module.repository]
}
module "woodpecker_repository" {
source = "./modules/woodpecker_repository"
for_each = {
for k, v in var.repository : k => v
if try(v.woodpecker, false)
}
full_name = "${each.value.organisation}/${each.value.name}"
visibility = each.value.private ? "private" : "public"
depends_on = [module.repository]
}
module "branch_protection" {
source = "./modules/branch_protection"
for_each = var.branch_protection
repository = each.value.repository
organisation = each.value.organisation
rule_name = each.value.rule_name
enable_push = each.value.enable_push
push_whitelist_users = each.value.push_whitelist_users
push_whitelist_teams = each.value.push_whitelist_teams
push_whitelist_deploy_keys = each.value.push_whitelist_deploy_keys
merge_whitelist_users = each.value.merge_whitelist_users
merge_whitelist_teams = each.value.merge_whitelist_teams
required_approvals = each.value.required_approvals
approval_whitelist_users = each.value.approval_whitelist_users
approval_whitelist_teams = each.value.approval_whitelist_teams
status_check_contexts = each.value.status_check_contexts
block_on_rejected_reviews = each.value.block_on_rejected_reviews
block_on_official_review_requests = each.value.block_on_official_review_requests
block_on_outdated_branch = each.value.block_on_outdated_branch
dismiss_stale_approvals = each.value.dismiss_stale_approvals
require_signed_commits = each.value.require_signed_commits
protected_file_patterns = each.value.protected_file_patterns
unprotected_file_patterns = each.value.unprotected_file_patterns
depends_on = [module.repository]
}
# TODO: enable when deploy keys are needed
# module "deploy_key" { ... }
@@ -0,0 +1,27 @@
resource "gitea_repository_branch_protection" "this" {
username = var.organisation
name = var.repository
rule_name = var.rule_name
enable_push = var.enable_push
push_whitelist_users = var.push_whitelist_users
push_whitelist_teams = var.push_whitelist_teams
push_whitelist_deploy_keys = var.push_whitelist_deploy_keys
merge_whitelist_users = var.merge_whitelist_users
merge_whitelist_teams = var.merge_whitelist_teams
required_approvals = var.required_approvals
approval_whitelist_users = var.approval_whitelist_users
approval_whitelist_teams = var.approval_whitelist_teams
status_check_patterns = var.status_check_contexts
block_merge_on_rejected_reviews = var.block_on_rejected_reviews
block_merge_on_official_review_requests = var.block_on_official_review_requests
block_merge_on_outdated_branch = var.block_on_outdated_branch
dismiss_stale_approvals = var.dismiss_stale_approvals
require_signed_commits = var.require_signed_commits
protected_file_patterns = var.protected_file_patterns
unprotected_file_patterns = var.unprotected_file_patterns
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
}
}
}
@@ -0,0 +1,96 @@
variable "repository" {
type = string
}
variable "organisation" {
type = string
}
variable "rule_name" {
type = string
}
variable "enable_push" {
type = bool
default = false
}
variable "push_whitelist_users" {
type = list(string)
default = []
}
variable "push_whitelist_teams" {
type = list(string)
default = []
}
variable "push_whitelist_deploy_keys" {
type = bool
default = false
}
variable "merge_whitelist_users" {
type = list(string)
default = []
}
variable "merge_whitelist_teams" {
type = list(string)
default = []
}
variable "required_approvals" {
type = number
default = 0
}
variable "approval_whitelist_users" {
type = list(string)
default = []
}
variable "approval_whitelist_teams" {
type = list(string)
default = []
}
variable "status_check_contexts" {
type = list(string)
default = []
}
variable "block_on_rejected_reviews" {
type = bool
default = false
}
variable "block_on_official_review_requests" {
type = bool
default = false
}
variable "block_on_outdated_branch" {
type = bool
default = false
}
variable "dismiss_stale_approvals" {
type = bool
default = false
}
variable "require_signed_commits" {
type = bool
default = false
}
variable "protected_file_patterns" {
type = string
default = ""
}
variable "unprotected_file_patterns" {
type = string
default = ""
}
@@ -0,0 +1,6 @@
resource "gitea_repository_key" "this" {
repository = var.repository_id
title = var.title
key = var.key
read_only = var.read_only
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
}
}
}
@@ -0,0 +1,16 @@
variable "repository_id" {
type = number
}
variable "title" {
type = string
}
variable "key" {
type = string
}
variable "read_only" {
type = bool
default = true
}
@@ -0,0 +1,9 @@
resource "gitea_org" "this" {
name = var.name
description = var.description
full_name = var.full_name
visibility = var.visibility
website = var.website
location = var.location
repo_admin_change_team_access = var.repo_admin_change_team_access
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
}
}
}
@@ -0,0 +1,33 @@
variable "name" {
type = string
}
variable "description" {
type = string
default = ""
}
variable "full_name" {
type = string
default = null
}
variable "visibility" {
type = string
default = "public"
}
variable "website" {
type = string
default = ""
}
variable "location" {
type = string
default = ""
}
variable "repo_admin_change_team_access" {
type = bool
default = false
}
@@ -0,0 +1,20 @@
resource "gitea_repository" "this" {
username = var.organisation
name = var.name
description = var.description
private = var.private
default_branch = var.default_branch
has_issues = var.has_issues
has_wiki = var.has_wiki
has_pull_requests = var.has_pull_requests
has_projects = var.has_projects
allow_merge_commits = var.allow_merge_commits
allow_rebase = var.allow_rebase
allow_rebase_explicit = var.allow_rebase_explicit
allow_squash_merge = var.allow_squash_merge
archived = var.archived
repo_template = var.repo_template
website = var.website
autodetect_manual_merge = var.autodetect_manual_merge
archive_on_destroy = true
}
@@ -0,0 +1,3 @@
output "id" {
value = gitea_repository.this.id
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
}
}
}
@@ -0,0 +1,82 @@
variable "name" {
type = string
}
variable "organisation" {
type = string
}
variable "description" {
type = string
default = null
}
variable "private" {
type = bool
default = null
}
variable "default_branch" {
type = string
default = null
}
variable "has_issues" {
type = bool
default = true
}
variable "has_wiki" {
type = bool
default = false
}
variable "has_pull_requests" {
type = bool
default = true
}
variable "has_projects" {
type = bool
default = false
}
variable "allow_merge_commits" {
type = bool
default = false
}
variable "allow_rebase" {
type = bool
default = false
}
variable "allow_rebase_explicit" {
type = bool
default = false
}
variable "allow_squash_merge" {
type = bool
default = true
}
variable "archived" {
type = bool
default = null
}
variable "repo_template" {
type = bool
default = null
}
variable "website" {
type = string
default = null
}
variable "autodetect_manual_merge" {
type = bool
default = null
}
@@ -0,0 +1,14 @@
resource "gitea_team" "this" {
name = var.name
organisation = var.organisation
description = var.description
permission = var.permission
include_all_repositories = var.include_all_repositories
can_create_repos = var.can_create_repos
repositories = var.repositories
}
resource "gitea_team_members" "this" {
team_id = gitea_team.this.id
members = toset(var.members)
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
gitea = {
source = "go-gitea/gitea"
version = "0.7.0"
}
}
}
@@ -0,0 +1,37 @@
variable "name" {
type = string
}
variable "organisation" {
type = string
}
variable "description" {
type = string
default = ""
}
variable "permission" {
type = string
default = "read"
}
variable "include_all_repositories" {
type = bool
default = false
}
variable "can_create_repos" {
type = bool
default = false
}
variable "repositories" {
type = list(string)
default = []
}
variable "members" {
type = list(string)
default = []
}
@@ -0,0 +1,4 @@
resource "woodpecker_repository" "this" {
full_name = var.full_name
visibility = var.visibility
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.10"
required_providers {
woodpecker = {
source = "Kichiyaki/woodpecker"
version = "0.5.0"
}
}
}
@@ -0,0 +1,8 @@
variable "full_name" {
type = string
}
variable "visibility" {
type = string
default = "internal"
}
+93
View File
@@ -0,0 +1,93 @@
variable "organisation" {
description = "Map of organisations to create"
type = map(object({
name = string
description = optional(string, "")
full_name = optional(string)
visibility = optional(string, "public")
website = optional(string, "")
location = optional(string, "")
repo_admin_change_team_access = optional(bool, false)
}))
default = {}
}
variable "repository" {
description = "Map of repositories to create"
type = map(object({
name = string
organisation = string
description = optional(string)
private = optional(bool)
default_branch = optional(string)
has_issues = optional(bool)
has_wiki = optional(bool)
has_pull_requests = optional(bool)
has_projects = optional(bool)
allow_merge_commits = optional(bool)
allow_rebase = optional(bool)
allow_rebase_explicit = optional(bool)
allow_squash_merge = optional(bool)
archived = optional(bool)
repo_template = optional(bool)
website = optional(string)
autodetect_manual_merge = optional(bool)
woodpecker = optional(bool, false)
}))
default = {}
}
variable "branch_protection" {
description = "Map of branch protection rules to create"
type = map(object({
repository = string
organisation = string
rule_name = string
enable_push = optional(bool, false)
push_whitelist_users = optional(list(string), [])
push_whitelist_teams = optional(list(string), [])
push_whitelist_deploy_keys = optional(bool, false)
merge_whitelist_users = optional(list(string), [])
merge_whitelist_teams = optional(list(string), [])
required_approvals = optional(number, 0)
approval_whitelist_users = optional(list(string), [])
approval_whitelist_teams = optional(list(string), [])
status_check_contexts = optional(list(string), [])
block_on_rejected_reviews = optional(bool, false)
block_on_official_review_requests = optional(bool, false)
block_on_outdated_branch = optional(bool, false)
dismiss_stale_approvals = optional(bool, false)
require_signed_commits = optional(bool, false)
protected_file_patterns = optional(string, "")
unprotected_file_patterns = optional(string, "")
}))
default = {}
}
variable "deploy_key" {
description = "Map of deploy keys to create"
type = map(object({
repository = string
organisation = string
gitea_url = string
title = string
key = string
read_only = optional(bool, true)
}))
default = {}
}
variable "team" {
description = "Map of teams to create"
type = map(object({
name = string
organisation = string
description = optional(string, "")
permission = optional(string, "read")
include_all_repositories = optional(bool, false)
can_create_repos = optional(bool, false)
repositories = optional(list(string), [])
members = optional(list(string), [])
}))
default = {}
}