# Ansible + Terraform homelab monorepo — Makefile
# All operations go through these targets. Never run ansible-playbook or terraform directly.

VENV        := .venv
PYTHON      := $(VENV)/bin/python
PIP         := $(VENV)/bin/pip
ANSIBLE     := $(VENV)/bin/ansible
PLAYBOOK    := $(VENV)/bin/ansible-playbook
GALAXY      := $(VENV)/bin/ansible-galaxy
LINT        := $(VENV)/bin/ansible-lint
MOLECULE    := $(VENV)/bin/molecule
# Vault password is resolved via ansible.cfg (vault_password_file); no flag needed.
VAULT_ARGS  :=
INVENTORY   := -i inventories/production/hosts.yml

TF              := terraform
TF_ENV          ?= staging
MOLECULE_IMAGE  := forgejo.nyumbani.baobab.band/sjat/molecule-debian13:latest
MOLECULE_DOCKERFILE := .docker/molecule-debian13/Dockerfile

.DEFAULT_GOAL := help

.PHONY: help setup collections lint test test-all check deploy encrypt decrypt new-role \
        tf-init tf-plan tf-apply tf-output tf-inventory \
        molecule-image molecule-image-push

help:
	@echo ""
	@echo "Ansible homelab — available targets:"
	@echo ""
	@echo "  make setup                         Create venv and install Python deps"
	@echo "  make collections                   Install Ansible collections"
	@echo "  make lint                          Run yamllint + ansible-lint"
	@echo "  make test ROLE=<name>              Run Molecule tests for a role"
	@echo "  make test-all                      Run Molecule tests for all roles"
	@echo "  make check PLAYBOOK=<name>         Dry-run a playbook (check mode)"
	@echo "  make deploy PLAYBOOK=<name>        Run a playbook against production"
	@echo "  make encrypt FILE=<path>           Encrypt a vault file"
	@echo "  make decrypt FILE=<path>           Decrypt a vault file"
	@echo "  make new-role NAME=<name>          Scaffold a new role"
	@echo ""
	@echo "  make tf-init [TF_ENV=staging]      Initialise Terraform providers"
	@echo "  make tf-plan [TF_ENV=staging]      Show Terraform plan"
	@echo "  make tf-apply [TF_ENV=staging]     Apply Terraform changes"
	@echo "  make tf-output [TF_ENV=staging]    Print Terraform outputs as JSON"
	@echo "  make tf-inventory [TF_ENV=staging] Regenerate Ansible inventory from Terraform outputs"
	@echo ""
	@echo "  TF_ENV defaults to 'staging'. Use TF_ENV=production for production."
	@echo ""
	@echo "  make molecule-image           Build the Molecule test image locally"
	@echo "  make molecule-image-push      Push the test image to the Forgejo registry"
	@echo ""

# ── Environment setup ─────────────────────────────────────────────────────────

setup:
	python3 -m venv $(VENV)
	$(PIP) install --upgrade pip
	$(PIP) install -r requirements.txt
	@echo "Venv ready. Activate with: source $(VENV)/bin/activate"

collections:
	$(GALAXY) collection install -r requirements.yml --upgrade

# ── Linting ───────────────────────────────────────────────────────────────────

lint:
	$(VENV)/bin/yamllint .
	$(LINT)

# ── Testing ───────────────────────────────────────────────────────────────────

test:
ifndef ROLE
	$(error ROLE is required: make test ROLE=<rolename>)
endif
	cd roles/$(ROLE) && ../../$(MOLECULE) test

test-all:
	@for role in roles/*/; do \
	  echo "── Testing $$role ──"; \
	  cd $$role && ../../$(MOLECULE) test; cd ../..; \
	done

# ── Playbook execution ────────────────────────────────────────────────────────

check:
ifndef PLAYBOOK
	$(error PLAYBOOK is required: make check PLAYBOOK=<name>)
endif
	$(PLAYBOOK) $(INVENTORY) $(VAULT_ARGS) --check --diff playbooks/$(PLAYBOOK).yml

deploy:
ifndef PLAYBOOK
	$(error PLAYBOOK is required: make deploy PLAYBOOK=<name>)
endif
	$(PLAYBOOK) $(INVENTORY) $(VAULT_ARGS) playbooks/$(PLAYBOOK).yml

# ── Vault ─────────────────────────────────────────────────────────────────────

encrypt:
ifndef FILE
	$(error FILE is required: make encrypt FILE=<path>)
endif
	$(ANSIBLE)-vault encrypt $(FILE)

decrypt:
ifndef FILE
	$(error FILE is required: make decrypt FILE=<path>)
endif
	$(ANSIBLE)-vault decrypt $(FILE)

# ── Molecule test image ───────────────────────────────────────────────────────

molecule-image:
	docker build -t $(MOLECULE_IMAGE) -f $(MOLECULE_DOCKERFILE) .

molecule-image-push: molecule-image
	docker push $(MOLECULE_IMAGE)

# ── Terraform ─────────────────────────────────────────────────────────────────

tf-init:
	$(TF) -chdir=terraform/environments/$(TF_ENV) init

tf-plan:
	$(TF) -chdir=terraform/environments/$(TF_ENV) plan

tf-apply:
	$(TF) -chdir=terraform/environments/$(TF_ENV) apply

tf-output:
	$(TF) -chdir=terraform/environments/$(TF_ENV) output -json

tf-inventory:
ifndef TF_ENV
	$(error TF_ENV is required: make tf-inventory TF_ENV=<staging|production>)
endif
	$(TF) -chdir=terraform/environments/$(TF_ENV) output -json \
	  | $(PYTHON) scripts/tf_to_inventory.py > inventories/$(TF_ENV)/hosts.yml
	@echo "Inventory written to inventories/$(TF_ENV)/hosts.yml"

# ── Role scaffolding ──────────────────────────────────────────────────────────

new-role:
ifndef NAME
	$(error NAME is required: make new-role NAME=<rolename>)
endif
	mkdir -p roles/$(NAME)/tasks roles/$(NAME)/handlers roles/$(NAME)/defaults \
	         roles/$(NAME)/templates roles/$(NAME)/files roles/$(NAME)/meta \
	         roles/$(NAME)/molecule/default
	echo "---" > roles/$(NAME)/tasks/main.yml
	echo "---" > roles/$(NAME)/handlers/main.yml
	echo "---" > roles/$(NAME)/defaults/main.yml
	echo "---" > roles/$(NAME)/meta/main.yml
	printf '# %s\n\nRole description here.\n' "$(NAME)" > roles/$(NAME)/README.md
	cp .scaffold/molecule.yml roles/$(NAME)/molecule/default/molecule.yml
	sed 's/ROLE_NAME_PLACEHOLDER/$(NAME)/g' .scaffold/converge.yml > roles/$(NAME)/molecule/default/converge.yml
	cp .scaffold/verify.yml   roles/$(NAME)/molecule/default/verify.yml
	@echo "Role $(NAME) scaffolded at roles/$(NAME)/"
	@echo "Next: fill in meta/main.yml, defaults/main.yml, tasks/main.yml, README.md"
