Repository navigation
Expand file tree
/
Copy pathTaskfile.yml
More file actions
465 lines (413 loc) · 20.1 KB
/
Copy pathTaskfile.yml
File metadata and controls
465 lines (413 loc) · 20.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
version: '3'
tasks:
# ── FORMATTING ───────────────────────────────────────────────────────────────
# Internal helper: runs gci import ordering (called by fmt:be).
imports:be:
internal: true
dir: backend
cmd: go tool gci write ./ --section standard --section default --section "Prefix(github.com/ArionMiles)" --skip-generated --skip-vendor
fmt:be:
summary: Format Go source files (gci import ordering + gofumpt).
desc: Runs import ordering and gofumpt over the backend source tree.
dir: backend
deps: [imports:be]
cmd: go tool gofumpt -l -w -extra .
fmt:fe:
summary: Format frontend TypeScript/TSX files with Prettier.
desc: Formats frontend source files with Prettier using the repo config.
dir: frontend
deps: [frontend:install]
cmd: npm run format
fmt:fe:check:
summary: Check frontend TypeScript/TSX files are formatted with Prettier (non-mutating, for CI).
desc: Verifies frontend formatting without modifying files. Intended for CI and pre-commit checks.
dir: frontend
deps: [frontend:install]
cmd: npm run format:check
fmt:
summary: Format all source files (Go + frontend).
desc: Runs backend and frontend formatting targets in sequence.
cmds:
- task: fmt:be
- task: fmt:fe
# ── LINTING ──────────────────────────────────────────────────────────────────
lint:be:
summary: Lint Go source files with golangci-lint (local config).
desc: >-
Lints Go source files using .golangci.toml (lighter local configuration
for development).
dir: backend
cmd: go tool golangci-lint --config=".golangci.toml" run ./...
lint:be:new:
summary: Lint only new/changed Go source files (compared to main).
desc: >-
Lints only Go files that are new or changed compared to origin/main.
Uses --new-from-rev for incremental adoption.
dir: backend
cmd: go tool golangci-lint --config=".golangci-prod.toml" --new-from-rev=origin/main --max-same-issues=0 --max-issues-per-linter=0 run ./...
lint:be:prod:
summary: Lint all Go source files with golangci-lint (strict production config).
desc: >-
Lints all Go source files using .golangci-prod.toml (strict CI rules).
Must report 0 issues before committing.
dir: backend
cmd: go tool golangci-lint --config=".golangci-prod.toml" --max-same-issues=0 --max-issues-per-linter=0 run ./...
lint:fe:
summary: Type-check and lint frontend source files.
desc: Runs the TypeScript compiler and ESLint over the frontend source tree.
dir: frontend
deps: [frontend:install]
cmd: npm run lint
lint:
summary: Lint all backend and frontend source files.
desc: Runs local backend linting plus frontend type checking and ESLint.
cmds:
- task: lint:be
- task: lint:fe
check:go-version:
summary: Verify runtime Go version pins are synchronized.
desc: >-
Checks that the Dockerfile and component/contract Compose images use the
same Go version as backend/go.mod.
cmd: scripts/versions/check-go.sh
# ── TESTING ──────────────────────────────────────────────────────────────────
test:be:
summary: Run Go unit tests.
desc: Run all Go unit tests with verbose output. Integration tests require Docker; use -short to skip them.
dir: backend
cmd: go test -v ./...
test:be:cover:
summary: Run Go unit tests with coverage report.
desc: >-
Executes the backend Go test suite with coverage profiling enabled and
prints the function-level coverage summary for CI and local inspection.
dir: backend
cmd: |
go test -v -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
test:be:cover:check:
summary: Verify backend unit coverage against the configured floor.
desc: >-
Parses backend/coverage.out and fails if the total unit-test statements
coverage is below BACKEND_COVERAGE_MIN.
dir: backend
cmd: ../scripts/coverage/check-backend.sh coverage.out "${BACKEND_COVERAGE_MIN:-45.0}"
test:be:component:
summary: Run Docker Compose-backed backend component tests.
desc: >-
Starts PostgreSQL and a coverage-instrumented backend under
tests/component, applies deterministic seed data, runs build-tagged Go
black-box API tests, and post-processes runtime backend coverage from
GOCOVERDIR into a text profile.
cmds:
- rm -rf tests/component/artifacts
- mkdir -p tests/component/artifacts/backend-coverage
- docker compose -f tests/component/docker-compose.yml down -v --remove-orphans
- docker compose -f tests/component/docker-compose.yml up --quiet-pull -d postgres backend
- docker compose -f tests/component/docker-compose.yml run --rm seed
- docker compose -f tests/component/docker-compose.yml run --rm runner
- docker compose -f tests/component/docker-compose.yml exec -T backend sh -lc "pkill -INT expensor || true"
- docker compose -f tests/component/docker-compose.yml down -v --remove-orphans
- cd backend && go tool covdata percent -i=../tests/component/artifacts/backend-coverage
- cd backend && go tool covdata textfmt -i=../tests/component/artifacts/backend-coverage -o=../tests/component/artifacts/backend-component.coverage.out
test:be:contract:
summary: Run Docker Compose-backed backend contract tests.
desc: >-
Starts PostgreSQL and the backend under tests/contract, applies the
deterministic component seed fixture, and runs Schemathesis against
the generated OpenAPI artifact for the Phase 4 read-only allowlist.
cmds:
- rm -rf tests/contract/artifacts
- mkdir -p tests/contract/artifacts
- docker compose -f tests/contract/docker-compose.yml down -v --remove-orphans
- docker compose -f tests/contract/docker-compose.yml up --quiet-pull -d postgres backend
- docker compose -f tests/contract/docker-compose.yml run --quiet-pull --rm seed
- docker compose -f tests/contract/docker-compose.yml run --quiet-pull --rm runner
- docker compose -f tests/contract/docker-compose.yml down -v --remove-orphans
test:fe:
summary: Run frontend unit and component tests.
desc: >-
Executes the Vitest suite in non-watch mode for the frontend app.
Intended for local verification and CI.
dir: frontend
deps: [frontend:install]
cmds:
- npm run test
- npm run test:scripts
test:fe:watch:
summary: Run frontend tests in watch mode.
desc: >-
Starts Vitest in watch mode for iterative frontend test development.
Intended for local development only.
dir: frontend
deps: [frontend:install]
cmd: npm run test:watch
test:fe:cover:
summary: Run frontend tests with coverage output.
desc: >-
Executes the frontend Vitest suite with V8 coverage enabled and writes
coverage artifacts under the frontend coverage directory.
dir: frontend
deps: [frontend:install]
cmd: npm run test:cover
test:fe:cover:check:
summary: Verify frontend app coverage against the configured floor.
desc: >-
Parses frontend/coverage/coverage-summary.json and fails if the total
app statements coverage is below FRONTEND_COVERAGE_MIN.
dir: frontend
deps: [frontend:install]
cmd: node ../scripts/coverage/check-frontend.mjs coverage/coverage-summary.json "${FRONTEND_COVERAGE_MIN:-22.5}"
test:fe:e2e:
summary: Run mocked Playwright browser tests against the built preview app.
desc: >-
Builds the frontend, serves it with Vite preview, enables browser-side
MSW mocks at runtime, and exercises the initial high-value browser flows.
dir: frontend
deps: [frontend:install]
cmd: npm run test:e2e
test:fe:e2e:smoke:
summary: Run real-stack Playwright smoke tests against the shared seeded backend.
desc: >-
Starts PostgreSQL and the backend with the existing shared seed fixture,
then runs the thin Playwright smoke slice against the built preview app.
cmds:
- docker compose -f tests/component/docker-compose.yml down -v --remove-orphans
- docker compose -f tests/component/docker-compose.yml up --quiet-pull -d postgres backend
- docker compose -f tests/component/docker-compose.yml run --quiet-pull --rm seed
- cd frontend && VITE_API_PROXY_TARGET=http://127.0.0.1:18080 npm run test:e2e:smoke
- docker compose -f tests/component/docker-compose.yml down -v --remove-orphans
screenshots:review:
summary: Start the seeded screenshot review stack.
desc: >-
Recreates the component backend with deterministic seed data, builds the
frontend, and serves a Vite preview at http://127.0.0.1:4173 for manual
review before running the screenshot capture.
deps: [frontend:install]
cmds:
- docker compose -f tests/component/docker-compose.yml down -v --remove-orphans
- docker compose -f tests/component/docker-compose.yml up --quiet-pull -d postgres backend
- docker compose -f tests/component/docker-compose.yml run --quiet-pull --rm seed
- cd frontend && VITE_API_PROXY_TARGET=http://127.0.0.1:18080 npm run build
- cd frontend && VITE_API_PROXY_TARGET=http://127.0.0.1:18080 npm run preview -- --host 127.0.0.1 --port 4173
screenshots:live:
summary: Capture live high-resolution dashboard and transactions screenshots.
desc: >-
Recreates the seeded component stack, serves the built frontend through
Vite preview, signs in with the component admin fixture, and captures
rounded, high-DPI dashboard and transactions screenshots in both light
and dark themes.
deps: [frontend:install]
cmds:
- docker compose -f tests/component/docker-compose.yml down -v --remove-orphans
- docker compose -f tests/component/docker-compose.yml up --quiet-pull -d postgres backend
- docker compose -f tests/component/docker-compose.yml run --quiet-pull --rm seed
- cd frontend && VITE_API_PROXY_TARGET=http://127.0.0.1:18080 npm run build
- |
cd frontend
VITE_API_PROXY_TARGET=http://127.0.0.1:18080 npm run preview -- --host 127.0.0.1 --port 4173 &
PREVIEW_PID=$!
cleanup() {
kill "$PREVIEW_PID" 2>/dev/null || true
docker compose -f ../tests/component/docker-compose.yml down -v --remove-orphans
}
trap cleanup EXIT
until curl -sf http://127.0.0.1:4173 >/dev/null; do
sleep 0.5
done
EXPENSOR_SCREENSHOT_BASE_URL=http://127.0.0.1:4173 EXPENSOR_SCREENSHOT_API_URL=http://127.0.0.1:18080 npm run screenshot:live
test:
summary: Run all backend and frontend unit/component tests.
desc: >-
Executes the default backend Go test suite and the frontend Vitest suite.
Excludes later-phase component, contract, and browser test harnesses.
cmds:
- task: test:be
- task: test:fe
# ── SECURITY / AUDIT ─────────────────────────────────────────────────────────
audit:be:
summary: Run govulncheck vulnerability scanner on Go source.
desc: Scans the backend Go packages for known vulnerabilities using govulncheck.
dir: backend
cmd: go tool govulncheck -show verbose ./...
audit:fe:
summary: Audit frontend npm dependencies for known vulnerabilities.
desc: Audits production frontend dependencies with npm audit.
dir: frontend
deps: [frontend:install]
cmd: npm audit --audit-level=moderate --omit=dev
audit:
summary: Run all security audits (Go + frontend npm).
desc: Runs backend govulncheck and frontend npm audit in sequence.
cmds:
- task: audit:be
- task: audit:fe
secrets:generate:
summary: Generate an encryption secret key.
desc: Generate a base64-encoded 32-byte secret key for credential encryption.
cmd: scripts/secrets/generate-key.sh
# ── BUILD ────────────────────────────────────────────────────────────────────
build:
summary: Build the Go application.
desc: Compiles all backend Go packages to catch build-time regressions.
dir: backend
cmd: go build -v ./...
build:binary:
summary: Build optimized binary for current platform.
desc: Build a production-ready Go binary with version information embedded.
dir: backend
vars:
VERSION:
sh: |
RAW=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
if echo "$RAW" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+'; then
echo "$RAW"
else
SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
if echo "$RAW" | grep -q dirty; then echo "dirty-${SHA}"; else echo "dev-${SHA}"; fi
fi
cmd: |
CGO_ENABLED=0 go build \
-trimpath \
-ldflags="-s -w -X github.com/ArionMiles/expensor/backend/pkg/config.Version={{.VERSION}}" \
-o ../bin/expensor \
./cmd/server
openapi:generate:
summary: Generate the committed OpenAPI baseline for the backend.
desc: >-
Runs the pinned Swaggo v1.16.4 generator from backend/ against the HTTP
package, writes YAML into api/openapi, and renames
the generated swagger.yaml artifact to api/openapi/expensor.openapi.yaml.
Re-run whenever OpenAPI annotations or route shapes change.
dir: backend
cmd: |
mkdir -p ../api/openapi
go run github.com/swaggo/swag/cmd/swag@v1.16.4 init -g openapi.go -d ./internal/httpapi --output ../api/openapi --outputTypes yaml
mv -f ../api/openapi/swagger.yaml ../api/openapi/expensor.openapi.yaml
openapi:check:
summary: Regenerate the OpenAPI baseline and fail if the committed artifact drifts.
desc: >-
Rebuilds api/openapi/expensor.openapi.yaml with the pinned Swaggo v1.16.4
workflow, verifies the artifact is tracked by git, and then exits
non-zero if git diff reports any drift. Intended for contributor
verification and CI guardrails.
cmds:
- task: openapi:generate
- git ls-files --error-unmatch api/openapi/expensor.openapi.yaml
- git diff --exit-code -- api/openapi/expensor.openapi.yaml
build:docker:
summary: Build Docker image locally.
desc: Builds the local expensor Docker image with the current repo version metadata.
vars:
VERSION:
sh: |
RAW=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
if echo "$RAW" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+'; then
echo "$RAW"
else
SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
if echo "$RAW" | grep -q dirty; then echo "dirty-${SHA}"; else echo "dev-${SHA}"; fi
fi
IMAGE_NAME: "expensor:{{.VERSION}}"
cmd: |
docker buildx build \
--build-arg VERSION={{.VERSION}} \
-t {{.IMAGE_NAME}} \
--load \
.
# ── DATABASE ─────────────────────────────────────────────────────────────────
db:start:
summary: Start the local development PostgreSQL container.
desc: >-
Starts the local development PostgreSQL container defined in tests/docker-compose.local.yml.
Idempotent — safe to run when the containers are already running.
Pass RECREATE=1 to destroy the postgres volume and recreate from scratch.
dir: tests
cmd: |
if [ "${RECREATE:-0}" = "1" ]; then
echo "Recreating dev postgres (RECREATE=1)..."
docker compose -f docker-compose.local.yml down -v
fi
docker compose -f docker-compose.local.yml up -d postgres
echo "Waiting for postgres to be healthy..."
until docker inspect --format '{{ "{{" }}.State.Health.Status{{ "}}" }}' expensor-dev-postgres 2>/dev/null | grep -qx "healthy"; do
sleep 1
done
echo "Postgres is healthy."
observability:start:
summary: Start local observability collectors and visualizers.
desc: >-
Starts the local OpenTelemetry Collector, Jaeger, and Prometheus services.
This is used by task dev and kept separate from db:start so database-only
workflows do not require observability ports to be available.
dir: tests
cmd: |
docker compose -f docker-compose.local.yml --profile observability up -d jaeger otel-collector prometheus
echo "Jaeger traces: http://localhost:16686"
echo "Prometheus metrics: http://localhost:9090"
db:stop:
summary: Stop the local development PostgreSQL container.
desc: Stops the local dev postgres container without removing its volume.
dir: tests
cmd: docker compose -f docker-compose.local.yml stop
# ── RUN ──────────────────────────────────────────────────────────────────────
run:
summary: Start the backend server with local dev environment.
desc: Starts the backend server locally with tests/config.dev.toml. Local tasks default to DB_BACKEND=postgres until SQLite support lands.
cmds:
- task: run:backend
run:backend:
dir: backend
vars:
DEV_DB_BACKEND: '{{default "postgres" .DB_BACKEND}}'
VERSION: '{{default "dev" .VERSION}}'
env:
EXPENSOR_CONFIG_FILE: "{{.ROOT_DIR}}/tests/config.dev.toml"
EXPENSOR_DB_BACKEND: "{{.DEV_DB_BACKEND}}"
EXPENSOR_SECRET_KEY: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
cmd: go run -ldflags="-X github.com/ArionMiles/expensor/backend/pkg/config.Version={{.VERSION}}" ./cmd/server
frontend:install:
summary: Install frontend npm dependencies (skipped if node_modules already exists).
desc: Runs npm install in the frontend directory. Idempotent — skipped automatically when node_modules is present.
dir: frontend
status:
- test -d node_modules
cmd: npm install
run:frontend:
summary: Start the frontend Vite dev server.
desc: Installs frontend dependencies if needed and starts the local Vite dev server.
dir: frontend
deps: [frontend:install]
cmd: npm run dev
dev:
summary: Start local dev containers, backend, and frontend.
desc: >-
Ensures the local dev containers are running (idempotent), waits for
postgres to be healthy, starts the backend with tests/config.dev.toml, waits
for /api/health, then starts the Vite dev server. Observability UIs are
available at Jaeger traces: http://localhost:16686 and Prometheus metrics:
http://localhost:9090. Kills the backend when the frontend exits. Pass
RECREATE=1 to wipe and recreate the postgres volume.
cmd: |
# Start local dev containers (idempotent)
RECREATE=${RECREATE:-0} task db:start
task observability:start
# Compute version string (same logic as build:binary)
RAW=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
if echo "$RAW" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+'; then
DEV_VERSION="$RAW"
else
SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
if echo "$RAW" | grep -q dirty; then DEV_VERSION="dirty-${SHA}"; else DEV_VERSION="dev-${SHA}"; fi
fi
# Start backend in background
task run:backend VERSION="${DEV_VERSION}" DB_BACKEND="{{default "postgres" .DB_BACKEND}}" &
BACKEND_PID=$!
echo "Backend started (pid $BACKEND_PID), waiting for /api/health..."
until curl -sf http://localhost:8080/api/health > /dev/null 2>&1; do
sleep 0.5
done
echo "Backend is up — starting frontend"
[ -d frontend/node_modules ] || (cd frontend && npm install)
(cd frontend && npm run dev) || true
kill "$BACKEND_PID" 2>/dev/null || true