Files
railiance-infra/Makefile

165 lines
8.0 KiB
Makefile
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -------- RailianceHosts Make Utilities --------
SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help
# Set this to your Gitea host if you want 'remote-set' helper
GITEA ?= gitea.example.com
OWNER ?= coulomb
REPO ?= railiance-hosts
# New-host defaults (can be overridden: make new-host NAME=... TYPE=...)
TYPE ?= cpx11
REGION ?= nbg1
ROLE ?= core
IMG ?= ubuntu-24.04
USER ?= admin
# Decrypt Hetzner token at runtime (requires SOPS_AGE_KEY or keys.txt locally)
HCLOUD_TOKEN := $(shell sops -d --extract '["hetzner"]["token"]' secrets/hetzner-token.yaml 2>/dev/null)
# ---- Help ----
help: ## Show this help
@echo "RailianceHosts Commands"; \
grep -E '^[a-zA-Z0-9_-]+:.*?## ' $(MAKEFILE_LIST) | sort | sed 's/:.*##/: /'
# ---- Git hooks ----
hooks: ## Configure git to use repo-local hooks (.githooks) and ensure executables
@mkdir -p .githooks
git config core.hooksPath .githooks
@test -f .githooks/pre-commit || (echo "❌ Missing .githooks/pre-commit"; exit 1)
chmod +x .githooks/pre-commit
@echo "✔ hooks enabled and pre-commit is executable"
hooks-test: ## Test secrets hook blocks plaintext in secrets/
@mkdir -p secrets && echo 'PLAINTEXT_TEST=true' > secrets/_hook_test.yaml
@git add secrets/_hook_test.yaml || true
@if git commit -m "TEST: should be blocked" 2>/dev/null; then \
echo "❌ Hook did NOT block plaintext (check .githooks/pre-commit)"; \
git reset --soft HEAD~1; \
else \
echo "✔ Hook blocked plaintext as expected"; \
fi
@git restore --staged secrets/_hook_test.yaml || true
@rm -f secrets/_hook_test.yaml
# ---- SOPS / Age helpers ----
sops-setup: ## Copy age key to SOPS default path (~/.config/sops/age/keys.txt)
mkdir -p ~/.config/sops/age
cp -n ~/.config/age/key.txt ~/.config/sops/age/keys.txt || true
chmod 600 ~/.config/sops/age/keys.txt
@echo "✔ SOPS key path set (~/.config/sops/age/keys.txt). Alternatively export SOPS_AGE_KEY."
sops-edit: ## Edit the global secrets with SOPS
sops secrets/hetzner-token.yaml
sops-encrypt: ## Encrypt a file in place: make sops-encrypt FILE=secrets/foo.yaml
@[ -n "$(FILE)" ] || (echo "Usage: make sops-encrypt FILE=secrets/xxx.yaml" && exit 1)
sops --encrypt --in-place $(FILE)
@echo "✔ Encrypted $(FILE)"
sops-decrypt: ## Print decrypted file to stdout (for inspection) FILE=secrets/foo.sops.yaml
@[ -n "$(FILE)" ] || (echo "Usage: make sops-decrypt FILE=secrets/xxx.sops.yaml" && exit 1)
sops -d $(FILE)
sops-rotate: ## Rotate recipients on a SOPS file (after updating .sops.yaml)
@[ -n "$(FILE)" ] || (echo "Usage: make sops-rotate FILE=secrets/xxx.sops.yaml" && exit 1)
sops --rotate --in-place $(FILE)
check-secrets: ## Fail if any file in secrets/ is not SOPS-encrypted
@! (git ls-files secrets | xargs -r grep -L -E '(^sops:$$|\"sops\"[[:space:]]*:)' | tee /dev/stderr | read) \
|| (echo "❌ Unencrypted secrets detected above. Encrypt with: sops --encrypt --in-place <file>"; exit 1)
@echo "✔ All files in secrets/ appear SOPS-encrypted"
# ---- Terraform (Hetzner) ----
tf-fmt: ## Terraform fmt
@[ -n "$(HCLOUD_TOKEN)" ] || (echo "HCLOUD_TOKEN empty; export SOPS_AGE_KEY or set keys.txt & fill secrets.sops.yaml" && exit 1)
@export HCLOUD_TOKEN=$(HCLOUD_TOKEN); @terraform -chdir=terraform/hetzner fmt -recursive || true
tf-init: ## Terraform init
@[ -n "$(HCLOUD_TOKEN)" ] || (echo "HCLOUD_TOKEN empty; export SOPS_AGE_KEY or set keys.txt & fill secrets.sops.yaml" && exit 1)
@export HCLOUD_TOKEN=$(HCLOUD_TOKEN); terraform -chdir=terraform/hetzner init
tf-plan: tf-init ## Terraform plan (requires decrypted HCLOUD_TOKEN)
@echo "🔍 Running terraform plan..."
@[ -n "$(HCLOUD_TOKEN)" ] || (echo "HCLOUD_TOKEN empty; export SOPS_AGE_KEY or set keys.txt & fill secrets.sops.yaml" && exit 1)
@export HCLOUD_TOKEN=$(HCLOUD_TOKEN); terraform -chdir=terraform/hetzner plan -var="hcloud_token=$(HCLOUD_TOKEN)"
tf-apply: tf-init ## Terraform apply (provision)
@[ -n "$(HCLOUD_TOKEN)" ] || (echo "HCLOUD_TOKEN empty; export SOPS_AGE_KEY or set keys.txt & fill secrets.sops.yaml" && exit 1)
@export HCLOUD_TOKEN=$(HCLOUD_TOKEN); terraform -chdir=terraform/hetzner apply -auto-approve -var="hcloud_token=$(HCLOUD_TOKEN)"
tf-destroy: tf-init ## Terraform destroy (tear down)
@[ -n "$(HCLOUD_TOKEN)" ] || (echo "HCLOUD_TOKEN empty; export SOPS_AGE_KEY or set keys.txt & fill secrets.sops.yaml" && exit 1)
@export HCLOUD_TOKEN=$(HCLOUD_TOKEN); terraform -chdir=terraform/hetzner destroy -auto-approve -var="hcloud_token=$(HCLOUD_TOKEN)"
# --- Terraform provider/lockfile helpers ---
TF_DIR := terraform/hetzner
TF_TOKEN := $(HCLOUD_TOKEN)
LOCKFILE := $(TF_DIR)/.terraform.lock.hcl
tf-lock-commit: ## Commit the current provider lockfile
@test -f $(LOCKFILE) || (echo "$(LOCKFILE) not found. Run 'make tf-init' first."; exit 1)
@git add $(LOCKFILE)
@git commit -m "chore(terraform): lock providers" || echo " No lockfile changes to commit."
tf-providers-check: ## Check if newer provider versions are available (non-destructive)
@echo "🔎 Checking for provider upgrades (lockfile readonly)…"
@if terraform -chdir=$(TF_DIR) init -upgrade -lockfile=readonly >/dev/null 2>&1; then \
echo "✔ Providers up to date (no upgrades available)."; \
else \
echo "↗ Provider upgrades likely available (readonly lockfile blocked changes)."; \
echo " Run: make tf-providers-upgrade"; \
fi
tf-providers-upgrade: ## Upgrade providers (updates .terraform.lock.hcl)
@echo "⬆️ Upgrading providers…"
@terraform -chdir=$(TF_DIR) init -upgrade
@echo "— Diff for $(LOCKFILE):"
@git --no-pager diff -- $(LOCKFILE) || true
@echo "💡 If changes look good: make tf-lock-commit"
tf-providers-upgrade-commit: tf-providers-upgrade tf-lock-commit ## Upgrade providers and commit the lockfile
tf-providers-plan: ## Plan after an upgrade (uses HCLOUD_TOKEN if set)
@echo "🧪 Planning with upgraded providers…"
@terraform -chdir=$(TF_DIR) plan $(if $(TF_TOKEN),-var="hcloud_token=$(TF_TOKEN)")
# ---- Ansible ----
ansible-bootstrap: ## Run base bootstrap play (users, ssh, ufw, sops-agent)
cd ansible && ansible-playbook playbooks/bootstrap.yaml -u admin
converge: ansible-bootstrap ## Alias for current bootstrap converge
@true
# ---- Orchestration ----
apply: tf-fmt tf-apply ansible-bootstrap ## Provision via Terraform then converge via Ansible
# ---- Utilities ----
doctor: ## Check tools and basic repo setup
@bash -ceu ' \
ok(){ printf "✔ %s\n" "$$1"; }; fail(){ printf "❌ %s\n" "$$1"; exit 1; }; \
command -v git >/dev/null && ok "git: $$(git --version)" || fail "git missing"; \
command -v ansible >/dev/null && ok "ansible: $$(ansible --version | head -1)"; \
command -v sops >/dev/null && ok "sops: $$(sops --version --check-for-updates)"; \
command -v age >/dev/null && ok "age: $$(age --version)"; \
command -v terraform >/dev/null && ok "terraform: $$(terraform -version | head -1)"; \
test -f keys/admin_ssh.pub && ok "keys/admin_ssh.pub present" || echo " add your SSH pubkey to keys/admin_ssh.pub"; \
test -f inventory/group_vars/secrets.sops.yaml && ok "secrets.sops.yaml present" || echo " create inventory/group_vars/secrets.sops.yaml"; \
grep -q "age1" .sops.yaml && ok ".sops.yaml has an age recipient" || echo " add your age public key to .sops.yaml"; \
git config --get core.hooksPath >/dev/null && ok "git hooksPath: $$(git config --get core.hooksPath)" || echo " run: make hooks"; \
'
# ---- Inventory convenience ----
new-host: ## Add a new host quickly: make new-host NAME=core1 TYPE=cpx11 REGION=nbg1 ROLE=core
@[ -n "$(NAME)" ] || (echo "Usage: make new-host NAME=... [TYPE=...] [REGION=...] [ROLE=...] [IMG=...] [USER=...]" && exit 1)
@python3 scripts/new_host.py --name "$(NAME)" --type "$(TYPE)" --region "$(REGION)" --role "$(ROLE)" --image "$(IMG)" --user "$(USER)"
@echo "✔ Added host $(NAME) to inventory/servers.yaml"
remote-set: ## Set origin to your Gitea repo (GITEA/OWNER/REPO vars)
git remote remove origin 2>/dev/null || true
git remote add origin https://$(GITEA)/$(OWNER)/$(REPO).git
git branch -M main
git push -u origin main
@echo "✔ Remote set to https://$(GITEA)/$(OWNER)/$(REPO).git"