diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae4a6bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.terraform +.terraform.lock.hcl +env +.terragrunt-cache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..646cd65 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,24 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + types: [yaml] + - id: trailing-whitespace + types: [yaml] + - repo: https://github.com/gruntwork-io/pre-commit + rev: v0.1.30 + hooks: + - id: tofu-fmt + - id: tofu-validate + - id: tflint + - id: terragrunt-hcl-fmt + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.37.1 + hooks: + - id: yamllint + args: + [ + "-d {extends: relaxed, rules: {line-length: disable}, ignore: chart}", + "-s", + ] diff --git a/.woodpecker/apply.yaml b/.woodpecker/apply.yaml new file mode 100644 index 0000000..36986f1 --- /dev/null +++ b/.woodpecker/apply.yaml @@ -0,0 +1,23 @@ +when: + - event: push + branch: main + +steps: + - name: apply + image: git.unkin.net/unkin/almalinux9-opentofu:20260606 + environment: + VAULT_AUTH_METHOD: kubernetes + commands: + - dnf install vault -y + - make plan + - make apply + backend_options: + kubernetes: + serviceAccountName: terraform-git + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/.woodpecker/plan.yaml b/.woodpecker/plan.yaml new file mode 100644 index 0000000..8639352 --- /dev/null +++ b/.woodpecker/plan.yaml @@ -0,0 +1,21 @@ +when: + - event: pull_request + +steps: + - name: plan + image: git.unkin.net/unkin/almalinux9-opentofu:20260606 + environment: + VAULT_AUTH_METHOD: kubernetes + commands: + - dnf install vault -y + - make plan + backend_options: + kubernetes: + serviceAccountName: terraform-git + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/.woodpecker/pre-commit.yaml b/.woodpecker/pre-commit.yaml new file mode 100644 index 0000000..5c5738f --- /dev/null +++ b/.woodpecker/pre-commit.yaml @@ -0,0 +1,18 @@ +when: + - event: pull_request + +steps: + - name: pre-commit + image: git.unkin.net/unkin/almalinux9-opentofu:20260606 + commands: + - uvx pre-commit run --all-files + backend_options: + kubernetes: + serviceAccountName: default + resources: + requests: + memory: 512Mi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..640077e --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +.PHONY: init plan apply format + +VAULT_AUTH_METHOD ?= approle +VAULT_K8S_ROLE ?= woodpecker_terraform_git +VAULT_K8S_MOUNT ?= auth/k8s/au/syd1 +VAULT_K8S_JWT_PATH ?= /var/run/secrets/kubernetes.io/serviceaccount/token + +define vault_env + @export VAULT_ADDR="https://vault.service.consul:8200" && \ + if [ "$(VAULT_AUTH_METHOD)" = "kubernetes" ]; then \ + export VAULT_TOKEN=$$(vault write -field=token $(VAULT_K8S_MOUNT)/login role=$(VAULT_K8S_ROLE) jwt=$$(cat $(VAULT_K8S_JWT_PATH))); \ + else \ + export VAULT_TOKEN=$$(vault write -field=token auth/approle/login role_id=$$VAULT_ROLEID); \ + fi && \ + export CONSUL_HTTP_TOKEN=$$(vault read -field=token consul_root/au/syd1/creds/terraform-git) && \ + export GITEA_TOKEN=$$(vault kv get -field=token kv/service/gitea/gitadmin/tokens/terraform-git) && \ + export WOODPECKER_TOKEN=$$(vault kv get -field=token kv/service/woodpecker/tokens/gitadmin) +endef + +init: + @$(call vault_env) && \ + terragrunt run --all --non-interactive init -- -upgrade + +plan: init + @$(call vault_env) && \ + terragrunt run --all --parallelism 4 --non-interactive plan + +apply: init + @$(call vault_env) && \ + terragrunt run --all --parallelism 2 --non-interactive apply + +format: + @echo "Formatting OpenTofu files..." + @tofu fmt -recursive . + @echo "Formatting Terragrunt files..." + @terragrunt hcl fmt diff --git a/config/config.hcl b/config/config.hcl new file mode 100644 index 0000000..b86e152 --- /dev/null +++ b/config/config.hcl @@ -0,0 +1,58 @@ +locals { + config_files = fileset(".", "**/*.yaml") + + all_configs = { + for file_path in local.config_files : + file_path => yamldecode(file(file_path)) + } + + config = { + organisation = { + for file_path, content in local.all_configs : + trimsuffix(replace(file_path, "/config.yaml", ""), ".yaml") => merge(content, { + name = split("/", file_path)[1] + gitea_url = split("/", file_path)[0] + }) + if endswith(file_path, "/config.yaml") && length(split("/", file_path)) == 3 + } + repository = { + for file_path, content in local.all_configs : + "${split("/", file_path)[0]}/${split("/", file_path)[1]}/${trimsuffix(basename(file_path), ".yaml")}" => merge(content, { + name = trimsuffix(basename(file_path), ".yaml") + organisation = split("/", file_path)[1] + gitea_url = split("/", file_path)[0] + }) + if length(regexall("/repository/", file_path)) > 0 + } + team = { + for file_path, content in local.all_configs : + "${split("/", file_path)[0]}/${split("/", file_path)[1]}/${trimsuffix(basename(file_path), ".yaml")}" => merge(content, { + name = trimsuffix(basename(file_path), ".yaml") + organisation = split("/", file_path)[1] + gitea_url = split("/", file_path)[0] + }) + if length(regexall("/team/", file_path)) > 0 + } + branch_protection = merge([ + for file_path, content in local.all_configs : { + for idx, rule in try(content.branch_protection, []) : + "${split("/", file_path)[0]}/${split("/", file_path)[1]}/${trimsuffix(basename(file_path), ".yaml")}/${rule.rule_name}" => merge(rule, { + repository = trimsuffix(basename(file_path), ".yaml") + organisation = split("/", file_path)[1] + gitea_url = split("/", file_path)[0] + }) + } + if length(regexall("/repository/", file_path)) > 0 + ]...) + deploy_key = { + for file_path, content in local.all_configs : + "${split("/", file_path)[0]}/${split("/", file_path)[1]}/${split("/", replace(file_path, "deploy_key/", ""))[2]}/${trimsuffix(basename(file_path), ".yaml")}" => merge(content, { + title = trimsuffix(basename(file_path), ".yaml") + repository = split("/", replace(file_path, "deploy_key/", ""))[2] + organisation = split("/", file_path)[1] + gitea_url = split("/", file_path)[0] + }) + if length(regexall("/deploy_key/", file_path)) > 0 + } + } +} diff --git a/config/git.unkin.net/unkin/config.yaml b/config/git.unkin.net/unkin/config.yaml new file mode 100644 index 0000000..37976f6 --- /dev/null +++ b/config/git.unkin.net/unkin/config.yaml @@ -0,0 +1,3 @@ +description: "" +visibility: public +repo_admin_change_team_access: true diff --git a/config/git.unkin.net/unkin/repository/app-sudaporn-research-individual.yaml b/config/git.unkin.net/unkin/repository/app-sudaporn-research-individual.yaml new file mode 100644 index 0000000..fafdc8b --- /dev/null +++ b/config/git.unkin.net/unkin/repository/app-sudaporn-research-individual.yaml @@ -0,0 +1,4 @@ +description: "Sudaporn's Research Data visualisation, normalised " +private: false +default_branch: "master" +default_delete_branch_after_merge: false diff --git a/config/git.unkin.net/unkin/repository/app-sudaporn-research-normalised.yaml b/config/git.unkin.net/unkin/repository/app-sudaporn-research-normalised.yaml new file mode 100644 index 0000000..ec10ad0 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/app-sudaporn-research-normalised.yaml @@ -0,0 +1,4 @@ +description: "Sudaporn's Research Data visualisation, normalised" +private: false +default_branch: "master" +default_delete_branch_after_merge: false diff --git a/config/git.unkin.net/unkin/repository/argocd-apps.yaml b/config/git.unkin.net/unkin/repository/argocd-apps.yaml new file mode 100644 index 0000000..1a6e1d0 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/argocd-apps.yaml @@ -0,0 +1,13 @@ +description: "GitOps for ArgoCD" +private: false +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "main" + enable_push: false + status_check_contexts: + - "ci/woodpecker/pr/pre-commit" + - "ci/woodpecker/pr/kubeconform" + approval_whitelist_users: + - "unkinben" +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/artifactapi.yaml b/config/git.unkin.net/unkin/repository/artifactapi.yaml new file mode 100644 index 0000000..d4fc949 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/artifactapi.yaml @@ -0,0 +1,15 @@ +description: "My terrible vibe coded artifact cache" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "master" + enable_push: false + status_check_contexts: + - "ci/woodpecker/pr/pre-commit" + - "ci/woodpecker/pr/test" + - "ci/woodpecker/pr/build" + approval_whitelist_users: + - "unkinben" +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/certmanager.yaml b/config/git.unkin.net/unkin/repository/certmanager.yaml new file mode 100644 index 0000000..092bda9 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/certmanager.yaml @@ -0,0 +1,12 @@ +description: "Vault PKI certificate issuance and SSH host key signing tool for Puppet-managed infrastructure" +private: false +default_branch: "master" +default_delete_branch_after_merge: false +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_users: + - "unkinben" + approval_whitelist_users: + - "unkinben" +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/container-devcompute.yaml b/config/git.unkin.net/unkin/repository/container-devcompute.yaml new file mode 100644 index 0000000..f00589d --- /dev/null +++ b/config/git.unkin.net/unkin/repository/container-devcompute.yaml @@ -0,0 +1,3 @@ +description: "Docker image to be used in Kubernetes as a developers container" +private: false +default_delete_branch_after_merge: false diff --git a/config/git.unkin.net/unkin/repository/docker-almalinux-base.yaml b/config/git.unkin.net/unkin/repository/docker-almalinux-base.yaml new file mode 100644 index 0000000..6a6c677 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/docker-almalinux-base.yaml @@ -0,0 +1,5 @@ +description: "Create base almalinux docker images" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true diff --git a/config/git.unkin.net/unkin/repository/docker-almalinux-buildrunner.yaml b/config/git.unkin.net/unkin/repository/docker-almalinux-buildrunner.yaml new file mode 100644 index 0000000..a16ed74 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/docker-almalinux-buildrunner.yaml @@ -0,0 +1,5 @@ +description: "Create almalinux docker images for buildagents" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true diff --git a/config/git.unkin.net/unkin/repository/docker-almalinux-jupyterinstance.yaml b/config/git.unkin.net/unkin/repository/docker-almalinux-jupyterinstance.yaml new file mode 100644 index 0000000..72bccc0 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/docker-almalinux-jupyterinstance.yaml @@ -0,0 +1,5 @@ +description: "Jupyter single-user instance" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true diff --git a/config/git.unkin.net/unkin/repository/docker-almalinux-runnerdnd.yaml b/config/git.unkin.net/unkin/repository/docker-almalinux-runnerdnd.yaml new file mode 100644 index 0000000..bd3cc92 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/docker-almalinux-runnerdnd.yaml @@ -0,0 +1,5 @@ +description: "Gitea Runner for Docker in Docker" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true diff --git a/config/git.unkin.net/unkin/repository/docker-template.yaml b/config/git.unkin.net/unkin/repository/docker-template.yaml new file mode 100644 index 0000000..42999f1 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/docker-template.yaml @@ -0,0 +1,5 @@ +description: "Docker template repository" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +repo_template: true diff --git a/config/git.unkin.net/unkin/repository/initbuilder.yaml b/config/git.unkin.net/unkin/repository/initbuilder.yaml new file mode 100644 index 0000000..fbbe769 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/initbuilder.yaml @@ -0,0 +1,3 @@ +description: "A repository for building initrd.img in docker " +private: false +default_delete_branch_after_merge: false diff --git a/config/git.unkin.net/unkin/repository/node-lookup.yaml b/config/git.unkin.net/unkin/repository/node-lookup.yaml new file mode 100644 index 0000000..f5cf984 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/node-lookup.yaml @@ -0,0 +1,15 @@ +description: "A CLI tool written in Go that queries the PuppetDB API to look up and filter node facts." +private: false +default_branch: "master" +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "master" + enable_push: false + status_check_contexts: + - "ci/woodpecker/pr/lint" + - "ci/woodpecker/pr/pre-commit" + - "ci/woodpecker/pr/unit-tests" + approval_whitelist_users: + - "unkinben" +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/packer-images.yaml b/config/git.unkin.net/unkin/repository/packer-images.yaml new file mode 100644 index 0000000..74b5c73 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/packer-images.yaml @@ -0,0 +1,15 @@ +description: "Packer images for docker, incus and other systems." +private: false +default_branch: "master" +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_teams: + - "docker" + status_check_contexts: + - "Build / build (pull_request)" + approval_whitelist_teams: + - "docker" + block_on_rejected_reviews: true diff --git a/config/git.unkin.net/unkin/repository/puppet-prod.yaml b/config/git.unkin.net/unkin/repository/puppet-prod.yaml new file mode 100644 index 0000000..f081992 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/puppet-prod.yaml @@ -0,0 +1,38 @@ +description: "production puppet-control repository" +private: false +default_branch: "develop" +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "develop" + enable_push: false + merge_whitelist_teams: + - "puppet" + status_check_contexts: + - "ci/woodpecker/pr/bolt-validate" + - "ci/woodpecker/pr/epp-validate" + - "ci/woodpecker/pr/erb-validate" + - "ci/woodpecker/pr/puppet-lint" + - "ci/woodpecker/pr/puppet-validate" + - "ci/woodpecker/pr/ruby-check" + - "ci/woodpecker/pr/ruby-validate" + - "ci/woodpecker/pr/yamllint" + approval_whitelist_teams: + - "puppet" + block_on_rejected_reviews: true + - rule_name: "master" + enable_push: false + merge_whitelist_teams: + - "puppet" + status_check_contexts: + - "ci/woodpecker/pr/bolt-validate" + - "ci/woodpecker/pr/epp-validate" + - "ci/woodpecker/pr/erb-validate" + - "ci/woodpecker/pr/puppet-lint" + - "ci/woodpecker/pr/puppet-validate" + - "ci/woodpecker/pr/ruby-check" + - "ci/woodpecker/pr/ruby-validate" + - "ci/woodpecker/pr/yamllint" + approval_whitelist_teams: + - "puppet" +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/puppet-r10k.yaml b/config/git.unkin.net/unkin/repository/puppet-r10k.yaml new file mode 100644 index 0000000..7087627 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/puppet-r10k.yaml @@ -0,0 +1,16 @@ +description: "Stores the puppet r10k yaml file" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_teams: + - "puppet" + status_check_contexts: + - "ci/woodpecker/pr/pre-commit" + - "ci/woodpecker/pr/g10k-validate" + approval_whitelist_teams: + - "puppet" + block_on_rejected_reviews: true +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/puppetapi.yaml b/config/git.unkin.net/unkin/repository/puppetapi.yaml new file mode 100644 index 0000000..be50ccc --- /dev/null +++ b/config/git.unkin.net/unkin/repository/puppetapi.yaml @@ -0,0 +1,4 @@ +description: "A fastapi service to present puppet services via an api" +private: false +default_branch: "master" +default_delete_branch_after_merge: true diff --git a/config/git.unkin.net/unkin/repository/rpmbuild-gonic.yaml b/config/git.unkin.net/unkin/repository/rpmbuild-gonic.yaml new file mode 100644 index 0000000..7633325 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/rpmbuild-gonic.yaml @@ -0,0 +1,5 @@ +description: "rpmbuild repo for gonic" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true diff --git a/config/git.unkin.net/unkin/repository/rpmbuild-internal-ca-certificates.yaml b/config/git.unkin.net/unkin/repository/rpmbuild-internal-ca-certificates.yaml new file mode 100644 index 0000000..f36b569 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/rpmbuild-internal-ca-certificates.yaml @@ -0,0 +1,4 @@ +description: "Package the internal ca-certificates" +private: false +default_branch: "master" +default_delete_branch_after_merge: false diff --git a/config/git.unkin.net/unkin/repository/rpmbuild-jellyfin-web.yaml b/config/git.unkin.net/unkin/repository/rpmbuild-jellyfin-web.yaml new file mode 100644 index 0000000..0c6cd8e --- /dev/null +++ b/config/git.unkin.net/unkin/repository/rpmbuild-jellyfin-web.yaml @@ -0,0 +1,5 @@ +description: "Build rpms for jellyfin-web" +private: false +default_branch: "master" +default_delete_branch_after_merge: false +archived: true diff --git a/config/git.unkin.net/unkin/repository/rpmbuild-proxlb.yaml b/config/git.unkin.net/unkin/repository/rpmbuild-proxlb.yaml new file mode 100644 index 0000000..7a9e978 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/rpmbuild-proxlb.yaml @@ -0,0 +1,5 @@ +description: "build the proxlb package" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true diff --git a/config/git.unkin.net/unkin/repository/rpmbuild-template.yaml b/config/git.unkin.net/unkin/repository/rpmbuild-template.yaml new file mode 100644 index 0000000..1db083f --- /dev/null +++ b/config/git.unkin.net/unkin/repository/rpmbuild-template.yaml @@ -0,0 +1,6 @@ +description: "Template rpmbuld repo" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +archived: true +repo_template: true diff --git a/config/git.unkin.net/unkin/repository/rpmbuilder.yaml b/config/git.unkin.net/unkin/repository/rpmbuilder.yaml new file mode 100644 index 0000000..af5ebcb --- /dev/null +++ b/config/git.unkin.net/unkin/repository/rpmbuilder.yaml @@ -0,0 +1,21 @@ +description: "A repository for building RPMs in docker" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_teams: + - "rpmbuild" + status_check_contexts: + - "ci/woodpecker/pr/build-almalinux9" + - "ci/woodpecker/pr/build-almalinux8" + - "ci/woodpecker/pr/build-fedora42" + - "ci/woodpecker/pr/build-fedora43" + - "ci/woodpecker/pr/build-fedora44" + - "ci/woodpecker/pr/pre-commit" + approval_whitelist_teams: + - "rpmbuild" + block_on_rejected_reviews: true +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/streamstack.yaml b/config/git.unkin.net/unkin/repository/streamstack.yaml new file mode 100644 index 0000000..cc7a4bd --- /dev/null +++ b/config/git.unkin.net/unkin/repository/streamstack.yaml @@ -0,0 +1,3 @@ +description: "A stack of microservices that aim to offer a distributed streaming service." +private: false +default_delete_branch_after_merge: false diff --git a/config/git.unkin.net/unkin/repository/terraform-incus.yaml b/config/git.unkin.net/unkin/repository/terraform-incus.yaml new file mode 100644 index 0000000..5b5e8a1 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/terraform-incus.yaml @@ -0,0 +1,15 @@ +description: "Repository containing the terragrunt/terraform IaC to deploy Incus instances" +private: false +default_branch: "master" +default_delete_branch_after_merge: true +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_teams: + - "terraform" + status_check_contexts: + - "Build / build (pull_request)" + approval_whitelist_teams: + - "terraform" + block_on_rejected_reviews: true + dismiss_stale_approvals: true diff --git a/config/git.unkin.net/unkin/repository/terraform-nomad.yaml b/config/git.unkin.net/unkin/repository/terraform-nomad.yaml new file mode 100644 index 0000000..f2ad7b9 --- /dev/null +++ b/config/git.unkin.net/unkin/repository/terraform-nomad.yaml @@ -0,0 +1,14 @@ +description: "Manage nomad with Terraform" +private: false +default_branch: "master" +default_delete_branch_after_merge: false +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_teams: + - "terraform" + status_check_contexts: + - "Build / build (pull_request)" + approval_whitelist_teams: + - "terraform" + dismiss_stale_approvals: true diff --git a/config/git.unkin.net/unkin/repository/terraform-provider-artifactapi.yaml b/config/git.unkin.net/unkin/repository/terraform-provider-artifactapi.yaml new file mode 100644 index 0000000..205406f --- /dev/null +++ b/config/git.unkin.net/unkin/repository/terraform-provider-artifactapi.yaml @@ -0,0 +1,9 @@ +description: "Terraform provider for managing ArtifactAPI" +private: false +default_delete_branch_after_merge: false +branch_protection: + - rule_name: "main" + enable_push: false + approval_whitelist_teams: + - "Owners" +woodpecker: true diff --git a/config/git.unkin.net/unkin/repository/terraform-vault.yaml b/config/git.unkin.net/unkin/repository/terraform-vault.yaml new file mode 100644 index 0000000..840d32f --- /dev/null +++ b/config/git.unkin.net/unkin/repository/terraform-vault.yaml @@ -0,0 +1,19 @@ +description: "A repository to manage the configuration of Vault secret engines, authentication modes and policies." +private: false +default_branch: "master" +default_delete_branch_after_merge: true +default_merge_style: "squash" +branch_protection: + - rule_name: "master" + enable_push: false + merge_whitelist_users: + - "benvin" + - "unkinben" + status_check_contexts: + - "ci/woodpecker/pr/pre-commit" + - "ci/woodpecker/pr/plan" + approval_whitelist_users: + - "unkinben" + approval_whitelist_teams: + - "Owners" +woodpecker: true diff --git a/config/git.unkin.net/unkin/team/Owners.yaml b/config/git.unkin.net/unkin/team/Owners.yaml new file mode 100644 index 0000000..785094a --- /dev/null +++ b/config/git.unkin.net/unkin/team/Owners.yaml @@ -0,0 +1,7 @@ +description: "" +permission: owner +include_all_repositories: true +can_create_repos: true +members: + - unkinben + - benvin diff --git a/config/git.unkin.net/unkin/team/docker.yaml b/config/git.unkin.net/unkin/team/docker.yaml new file mode 100644 index 0000000..3438f91 --- /dev/null +++ b/config/git.unkin.net/unkin/team/docker.yaml @@ -0,0 +1,15 @@ +description: "manage docker related repositories" +permission: write +include_all_repositories: false +can_create_repos: false +repositories: + - docker-almalinux-base + - docker-almalinux-buildrunner + - docker-almalinux-jupyterinstance + - docker-almalinux-runnerdnd + - docker-template + - packer-images +members: + - droneci + - unkinben + - benvin diff --git a/config/git.unkin.net/unkin/team/puppet.yaml b/config/git.unkin.net/unkin/team/puppet.yaml new file mode 100644 index 0000000..78dc0ff --- /dev/null +++ b/config/git.unkin.net/unkin/team/puppet.yaml @@ -0,0 +1,10 @@ +description: "owners of the puppet system" +permission: write +include_all_repositories: false +can_create_repos: false +repositories: + - puppet-prod + - puppet-r10k +members: + - unkinben + - benvin diff --git a/config/git.unkin.net/unkin/team/pybuild.yaml b/config/git.unkin.net/unkin/team/pybuild.yaml new file mode 100644 index 0000000..8190795 --- /dev/null +++ b/config/git.unkin.net/unkin/team/pybuild.yaml @@ -0,0 +1,9 @@ +description: "Python package builers" +permission: write +include_all_repositories: false +can_create_repos: false +repositories: + - puppetapi +members: + - unkinben + - benvin diff --git a/config/git.unkin.net/unkin/team/rpmbuild.yaml b/config/git.unkin.net/unkin/team/rpmbuild.yaml new file mode 100644 index 0000000..40ec10f --- /dev/null +++ b/config/git.unkin.net/unkin/team/rpmbuild.yaml @@ -0,0 +1,15 @@ +description: "manage rpmbuild repos" +permission: write +include_all_repositories: false +can_create_repos: false +repositories: + - rpmbuilder + - rpmbuild-gonic + - rpmbuild-internal-ca-certificates + - rpmbuild-jellyfin-web + - rpmbuild-proxlb + - rpmbuild-template +members: + - droneci + - unkinben + - benvin diff --git a/config/git.unkin.net/unkin/team/terraform.yaml b/config/git.unkin.net/unkin/team/terraform.yaml new file mode 100644 index 0000000..dfb44ab --- /dev/null +++ b/config/git.unkin.net/unkin/team/terraform.yaml @@ -0,0 +1,10 @@ +description: "terraform job maintainers" +permission: write +include_all_repositories: false +can_create_repos: false +repositories: + - terraform-incus + - terraform-nomad +members: + - unkinben + - benvin diff --git a/environments/au/syd1/terragrunt.hcl b/environments/au/syd1/terragrunt.hcl new file mode 100644 index 0000000..4c92627 --- /dev/null +++ b/environments/au/syd1/terragrunt.hcl @@ -0,0 +1,25 @@ +include "root" { + path = find_in_parent_folders("root.hcl") + expose = true +} + +include "config" { + path = "${get_repo_root()}/config/config.hcl" + expose = true +} + +locals { + config = include.config.locals.config +} + +terraform { + source = "../../../modules/gitea_instance" +} + +inputs = { + organisation = local.config.organisation + repository = local.config.repository + branch_protection = local.config.branch_protection + deploy_key = local.config.deploy_key + team = local.config.team +} diff --git a/environments/root.hcl b/environments/root.hcl new file mode 100644 index 0000000..14abeb5 --- /dev/null +++ b/environments/root.hcl @@ -0,0 +1,39 @@ +generate "backend" { + path = "backend.tf" + if_exists = "overwrite" + contents = < 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" { ... } diff --git a/modules/gitea_instance/modules/branch_protection/main.tf b/modules/gitea_instance/modules/branch_protection/main.tf new file mode 100644 index 0000000..d4e4282 --- /dev/null +++ b/modules/gitea_instance/modules/branch_protection/main.tf @@ -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 +} diff --git a/modules/gitea_instance/modules/branch_protection/terraform.tf b/modules/gitea_instance/modules/branch_protection/terraform.tf new file mode 100644 index 0000000..0d5ef69 --- /dev/null +++ b/modules/gitea_instance/modules/branch_protection/terraform.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.10" + required_providers { + gitea = { + source = "go-gitea/gitea" + version = "0.7.0" + } + } +} diff --git a/modules/gitea_instance/modules/branch_protection/variables.tf b/modules/gitea_instance/modules/branch_protection/variables.tf new file mode 100644 index 0000000..25da9c9 --- /dev/null +++ b/modules/gitea_instance/modules/branch_protection/variables.tf @@ -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 = "" +} diff --git a/modules/gitea_instance/modules/deploy_key/main.tf b/modules/gitea_instance/modules/deploy_key/main.tf new file mode 100644 index 0000000..5f0258b --- /dev/null +++ b/modules/gitea_instance/modules/deploy_key/main.tf @@ -0,0 +1,6 @@ +resource "gitea_repository_key" "this" { + repository = var.repository_id + title = var.title + key = var.key + read_only = var.read_only +} diff --git a/modules/gitea_instance/modules/deploy_key/terraform.tf b/modules/gitea_instance/modules/deploy_key/terraform.tf new file mode 100644 index 0000000..0d5ef69 --- /dev/null +++ b/modules/gitea_instance/modules/deploy_key/terraform.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.10" + required_providers { + gitea = { + source = "go-gitea/gitea" + version = "0.7.0" + } + } +} diff --git a/modules/gitea_instance/modules/deploy_key/variables.tf b/modules/gitea_instance/modules/deploy_key/variables.tf new file mode 100644 index 0000000..3a5b793 --- /dev/null +++ b/modules/gitea_instance/modules/deploy_key/variables.tf @@ -0,0 +1,16 @@ +variable "repository_id" { + type = number +} + +variable "title" { + type = string +} + +variable "key" { + type = string +} + +variable "read_only" { + type = bool + default = true +} diff --git a/modules/gitea_instance/modules/organisation/main.tf b/modules/gitea_instance/modules/organisation/main.tf new file mode 100644 index 0000000..3713bfe --- /dev/null +++ b/modules/gitea_instance/modules/organisation/main.tf @@ -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 +} diff --git a/modules/gitea_instance/modules/organisation/terraform.tf b/modules/gitea_instance/modules/organisation/terraform.tf new file mode 100644 index 0000000..0d5ef69 --- /dev/null +++ b/modules/gitea_instance/modules/organisation/terraform.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.10" + required_providers { + gitea = { + source = "go-gitea/gitea" + version = "0.7.0" + } + } +} diff --git a/modules/gitea_instance/modules/organisation/variables.tf b/modules/gitea_instance/modules/organisation/variables.tf new file mode 100644 index 0000000..26d61f9 --- /dev/null +++ b/modules/gitea_instance/modules/organisation/variables.tf @@ -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 +} diff --git a/modules/gitea_instance/modules/repository/main.tf b/modules/gitea_instance/modules/repository/main.tf new file mode 100644 index 0000000..b714315 --- /dev/null +++ b/modules/gitea_instance/modules/repository/main.tf @@ -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 +} diff --git a/modules/gitea_instance/modules/repository/outputs.tf b/modules/gitea_instance/modules/repository/outputs.tf new file mode 100644 index 0000000..c344e7f --- /dev/null +++ b/modules/gitea_instance/modules/repository/outputs.tf @@ -0,0 +1,3 @@ +output "id" { + value = gitea_repository.this.id +} diff --git a/modules/gitea_instance/modules/repository/terraform.tf b/modules/gitea_instance/modules/repository/terraform.tf new file mode 100644 index 0000000..0d5ef69 --- /dev/null +++ b/modules/gitea_instance/modules/repository/terraform.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.10" + required_providers { + gitea = { + source = "go-gitea/gitea" + version = "0.7.0" + } + } +} diff --git a/modules/gitea_instance/modules/repository/variables.tf b/modules/gitea_instance/modules/repository/variables.tf new file mode 100644 index 0000000..d6479e1 --- /dev/null +++ b/modules/gitea_instance/modules/repository/variables.tf @@ -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 +} diff --git a/modules/gitea_instance/modules/team/main.tf b/modules/gitea_instance/modules/team/main.tf new file mode 100644 index 0000000..cb55295 --- /dev/null +++ b/modules/gitea_instance/modules/team/main.tf @@ -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) +} diff --git a/modules/gitea_instance/modules/team/terraform.tf b/modules/gitea_instance/modules/team/terraform.tf new file mode 100644 index 0000000..0d5ef69 --- /dev/null +++ b/modules/gitea_instance/modules/team/terraform.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.10" + required_providers { + gitea = { + source = "go-gitea/gitea" + version = "0.7.0" + } + } +} diff --git a/modules/gitea_instance/modules/team/variables.tf b/modules/gitea_instance/modules/team/variables.tf new file mode 100644 index 0000000..c9b1722 --- /dev/null +++ b/modules/gitea_instance/modules/team/variables.tf @@ -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 = [] +} diff --git a/modules/gitea_instance/modules/woodpecker_repository/main.tf b/modules/gitea_instance/modules/woodpecker_repository/main.tf new file mode 100644 index 0000000..df929d5 --- /dev/null +++ b/modules/gitea_instance/modules/woodpecker_repository/main.tf @@ -0,0 +1,4 @@ +resource "woodpecker_repository" "this" { + full_name = var.full_name + visibility = var.visibility +} diff --git a/modules/gitea_instance/modules/woodpecker_repository/terraform.tf b/modules/gitea_instance/modules/woodpecker_repository/terraform.tf new file mode 100644 index 0000000..25c5cdb --- /dev/null +++ b/modules/gitea_instance/modules/woodpecker_repository/terraform.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.10" + required_providers { + woodpecker = { + source = "Kichiyaki/woodpecker" + version = "0.5.0" + } + } +} diff --git a/modules/gitea_instance/modules/woodpecker_repository/variables.tf b/modules/gitea_instance/modules/woodpecker_repository/variables.tf new file mode 100644 index 0000000..4c10ed3 --- /dev/null +++ b/modules/gitea_instance/modules/woodpecker_repository/variables.tf @@ -0,0 +1,8 @@ +variable "full_name" { + type = string +} + +variable "visibility" { + type = string + default = "internal" +} diff --git a/modules/gitea_instance/variables.tf b/modules/gitea_instance/variables.tf new file mode 100644 index 0000000..8e78559 --- /dev/null +++ b/modules/gitea_instance/variables.tf @@ -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 = {} +}