mirror of
https://github.com/cole-maxwell1/todo-hetzner.git
synced 2026-10-27 10:52:49 -05:00
Add PostgreSQL-backed Go validation for application changes and semantic-version release publishing to GHCR. Default deployments to the public image and prevent host setup from starting the application before deployment.
4.1 KiB
4.1 KiB
Repository Guide
Boundaries
app/is the Go 1.22 module. The server entrypoint isapp/cmd/server/main.go; handlers own HTTP behavior,internal/dbowns xorm persistence, andapp/webembeds templates plus compiled CSS into the binary..github/workflows/go-tests.ymlgates app changes with PostgreSQL-backed tests. Published semantic-version GitHub Releases run.github/workflows/publish-image.ymland publish the multi-architecture image to GHCR.infra/is one OpenTofu root module and one shared state for both production and staging.infra/environments/*contain input files, not independent stacks.ansible/playbooks/setup-node.ymlconfigures both hosts;deploy-app.ymlpulls and restarts the rootless Podman/Quadlet app. Environment-specific behavior is selected by generated inventory groups anddeployment_environment.- Staging restores unchanged production data. Treat staging, backups, logs, real tfvars, Vault files, and state as production-sensitive; inspect examples instead of secret-bearing files.
Commands
- Run repository checks with
make validate: Go tests,go vet, OpenTofu validation, then both Ansible syntax checks. - Run one Go test from
app/:go test ./internal/handler -run '^TestName$'orgo test ./internal/db -run '^TestStoreLifecycle$'. internal/dbintegration tests silently skip unlessTEST_DATABASE_URLpoints to PostgreSQL. Normalmake testneeds no database.- GitHub CI supplies PostgreSQL so the
internal/dbintegration test must run rather than skip. - Format Go and OpenTofu files with
make format; it runsgofmtand recursivetofu fmt(including real ignored tfvars), but does not format YAML or templates. - Build the embedded app with
make build-app. This first runs Tailwind and writesapp/web/static/app.css;make cssperforms only that asset step. - Build the deployment image with
docker build -t todo:local app. Its CSS stage is self-contained and does not use the localapp/tailwindcssbinary.
Generated And Runtime Behavior
- Edit Tailwind source in
app/web/input.cssand templates underapp/web/templates/; regenerate the trackedapp/web/static/app.csswithmake cssafter class or style changes. - The standalone Tailwind binary is downloaded at pinned version
v3.4.17to ignoredapp/tailwindcss. - The server requires
DATABASE_URL, defaultsAPP_ADDRto:8080, and runs xormSync2for thetodostable during startup. Keepxorm.io/xormpaired withgithub.com/lib/pqin this stack. make generate-inventoryreads applied OpenTofu output and overwrites ignoredansible/inventory/hosts.yml; rerun it after server replacement or address changes.
Infrastructure And Deployment
- OpenTofu commands must use both var files against the root stack, for example
tofu -chdir=infra plan -var-file=environments/prod/prod.tfvars -var-file=environments/staging/staging.tfvars. The staging file only supplies staging-specific overrides. - Prefer
TF_VAR_hcloud_token,TF_VAR_s3_access_key, andTF_VAR_s3_secret_key; values in*.tfvarsoverride those environment variables. - Keep the AWS provider on
~> 5.69.0: newer releases wait for an AWS-only response header absent from Hetzner Object Storage. - Run Ansible from
ansible/, as the Make targets do, or setANSIBLE_CONFIG=ansible/ansible.cfg; otherwise the repository role path is not loaded. make deploy-ansibleis an operational deployment, not a verification command. It regenerates inventory, runs host setup before app deployment, requires decryptable Vault files, and acts on both production and staging.- The Quadlet uses
Pull=never;deploy-app.ymlintentionally pullsapp_imagebefore restartingapp-todo.service. - The example Ansible configuration defaults to the public
ghcr.io/cole-maxwell1/todo-hetzner:latestimage. Prefer a version or digest override when reproducibility matters. - Production WAL-G archives under a prefix ending in
/prod. Staging's scheduleddb-syncdestructively replaces/var/lib/pgsql/datafromLATESTand leaves the app stopped on failure; do not weaken its path/prefix guards or use it as a routine test.