Local development
How to work on the apistock repository: services, tests, checks and CI.
Prerequisites#
| Tool | Version | Why |
|---|---|---|
| Go | 1.26 or later (use the latest patch: Go 1.26.0 has known standard library vulnerabilities) | Build and test |
| Docker with Compose v2 | Any recent Docker Desktop or Engine | PostgreSQL for tests and examples |
| git | Any | Version control |
PostgreSQL is never installed locally or downloaded as a binary: it always runs in Docker (ADR-0028).
Installing the CLI#
cd cli && go install ./cmd/aps
aps version
If aps isn't found, add $(go env GOPATH)/bin to your PATH. Reinstall after pulling changes. See the CLI guide.
Repository layout#
Each directory with a go.mod is its own Go module: the core library at the root, modules/*, cli, and each examples/* app. Run Go commands inside the module you are working on; replace directives point modules at the local checkout.
PostgreSQL#
The root compose.yaml runs postgres:18 for module tests on 127.0.0.1:55432 (not 5432, so it can run next to other projects).
docker compose up -d --wait # start
docker compose ps # check it is healthy
docker compose down # stop, keep data
docker compose down -v # stop and delete data (also removes old pgtest templates)
Set APISTOCK_POSTGRES_PORT to use another host port.
Mailpit#
The same compose.yaml runs Mailpit, a local email inbox, for the SMTP module and example tests: SMTP on 127.0.0.1:51025, web inbox on http://127.0.0.1:58025 (APISTOCK_MAILPIT_SMTP_PORT and APISTOCK_MAILPIT_WEB_PORT to change). Apps have their own Mailpit in their compose.yaml, on 1025 and 8025 (email guide).
Running tests#
Database tests use pgtest, which reads the server URL from an environment variable; email delivery tests read Mailpit's addresses:
export APISTOCK_TEST_DATABASE_URL='postgres://apistock:apistock@127.0.0.1:55432/apistock?sslmode=disable'
export APISTOCK_TEST_MAILPIT_SMTP=127.0.0.1:51025
export APISTOCK_TEST_MAILPIT_URL=http://127.0.0.1:58025
| Module | Command |
|---|---|
| Core | go test ./... (repository root) |
| A module | cd modules/jobs && go test -race ./... |
| An example app | cd examples/full-single && go test -race ./... |
| The CLI | cd cli && go test -race ./... |
| The website | cd site && go test ./... (builds both sites, fails on broken links) |
What each kind of test covers, the helpers, and drift checks: testing.
- Without
APISTOCK_TEST_DATABASE_URL, database tests are skipped with instructions. - With
APISTOCK_REQUIRE_DB=1(as in CI), a missing database fails the tests instead. - Without the Mailpit variables,
modules/mail/smtp's Mailpit test is skipped and the example checks that email is queued but not delivered;APISTOCK_REQUIRE_MAILPIT=1(as in CI) makes the missing Mailpit a failure. APS_E2E_DOCKER=1inclirunsaps devin a new Full app against real Docker on free ports, signs in as the seeded administrator and checks that a registration email reaches Mailpit; its containers and volume are removed afterwards.APS_E2E=1incliruns the end-to-end tests: a generated Minimal app passes its tests, and a copy ofexamples/full-singlebuilds afteraps add mailswitches it to SMTP and back.- Every test gets its own database, cloned from a migrated template, and dropped afterwards; tests are isolated and can run in parallel across packages.
Checks#
Run these in the module you changed before committing:
gofmt -l .
go vet ./...
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.2 run --config ../../.golangci.yml ./...
GOTOOLCHAIN=go1.26.8 go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
Adjust the --config path to reach the repository root's .golangci.yml. For an example app, also regenerate its API contract after changing endpoints:
go run ./cmd/api openapi --dir api
Running the Full preset example#
cd examples/full-single
aps dev # .env, its own PostgreSQL on 127.0.0.1:5432 and Mailpit on http://127.0.0.1:8025, migrations, seed data
The first run prints the seeded administrator's password (admin@example.com) once. Without the CLI:
cp .env.example .env
docker compose up -d --wait
go run ./cmd/migrate && go run ./cmd/seed
go run ./cmd/api
The app reads .env only through aps dev; with plain go run, export the variables first (for example set -a; . ./.env; set +a).
CI#
.github/workflows/ci.yml runs, per module: gofmt, go vet, go test -race against PostgreSQL and Mailpit service containers, golangci-lint, govulncheck, OpenAPI drift checks for the example apps, recipe drift for the Minimal preset, an end-to-end generated-app test, and a gitleaks secret scan.
The workflows are currently disabled on GitHub during active development. Re-enable them with:
gh workflow enable CI -R apistockhq/apistock
gh workflow enable "Release aps" -R apistockhq/apistock
Commits#
Commit messages use a short imperative subject and a bullet body describing what changed. Don't add co-author trailers.