mirror of
https://github.com/cole-maxwell1/todo-hetzner.git
synced 2026-10-27 19:02: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.
38 lines
4.1 KiB
Markdown
38 lines
4.1 KiB
Markdown
# Repository Guide
|
|
|
|
## Boundaries
|
|
|
|
- `app/` is the Go 1.22 module. The server entrypoint is `app/cmd/server/main.go`; handlers own HTTP behavior, `internal/db` owns xorm persistence, and `app/web` embeds templates plus compiled CSS into the binary.
|
|
- `.github/workflows/go-tests.yml` gates app changes with PostgreSQL-backed tests. Published semantic-version GitHub Releases run `.github/workflows/publish-image.yml` and 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.yml` configures both hosts; `deploy-app.yml` pulls and restarts the rootless Podman/Quadlet app. Environment-specific behavior is selected by generated inventory groups and `deployment_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$'` or `go test ./internal/db -run '^TestStoreLifecycle$'`.
|
|
- `internal/db` integration tests silently skip unless `TEST_DATABASE_URL` points to PostgreSQL. Normal `make test` needs no database.
|
|
- GitHub CI supplies PostgreSQL so the `internal/db` integration test must run rather than skip.
|
|
- Format Go and OpenTofu files with `make format`; it runs `gofmt` and recursive `tofu 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 writes `app/web/static/app.css`; `make css` performs 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 local `app/tailwindcss` binary.
|
|
|
|
## Generated And Runtime Behavior
|
|
|
|
- Edit Tailwind source in `app/web/input.css` and templates under `app/web/templates/`; regenerate the tracked `app/web/static/app.css` with `make css` after class or style changes.
|
|
- The standalone Tailwind binary is downloaded at pinned version `v3.4.17` to ignored `app/tailwindcss`.
|
|
- The server requires `DATABASE_URL`, defaults `APP_ADDR` to `:8080`, and runs xorm `Sync2` for the `todos` table during startup. Keep `xorm.io/xorm` paired with `github.com/lib/pq` in this stack.
|
|
- `make generate-inventory` reads applied OpenTofu output and overwrites ignored `ansible/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`, and `TF_VAR_s3_secret_key`; values in `*.tfvars` override 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 set `ANSIBLE_CONFIG=ansible/ansible.cfg`; otherwise the repository role path is not loaded.
|
|
- `make deploy-ansible` is 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.yml` intentionally pulls `app_image` before restarting `app-todo.service`.
|
|
- The example Ansible configuration defaults to the public `ghcr.io/cole-maxwell1/todo-hetzner:latest` image. Prefer a version or digest override when reproducibility matters.
|
|
- Production WAL-G archives under a prefix ending in `/prod`. Staging's scheduled `db-sync` destructively replaces `/var/lib/pgsql/data` from `LATEST` and leaves the app stopped on failure; do not weaken its path/prefix guards or use it as a routine test.
|