Commit graph

7 commits

Author SHA1 Message Date
45b2449b14 Add phased nginx setup for bilhej.se TLS on srvr.nu.
First-time host nginx setup needs HTTP-only vhost before certbot can
issue certs; the full bilhej.nginx.conf 443 block fails nginx -t until
those files exist.

- Add docker/bilhej.nginx.http.conf for ACME phase
- Reorder README one-time setup: HTTP vhost, certbot, then full config
2026-05-21 17:06:21 +02:00
5eb49c05a8 fix: correct COPY paths in backend.prod.Dockerfile
All checks were successful
CI / Lint, type check, unit tests, coverage (push) Successful in 1m47s
CI / E2E browser tests (push) Successful in 44s
The production backend Dockerfile was looking for Gradle files in a
backend/ subdirectory that doesn't exist in the repo structure:

- gradlew lives at repo root, not backend/gradlew
- gradle/ wrapper dir lives at repo root, not backend/gradle/
- settings.gradle lives at repo root, not backend/settings.gradle

Fixed by copying root-level Gradle files and placing backend-specific
files in the backend/ subdirectory. Also added :backend: subproject
prefix to Gradle tasks and corrected the output JAR path.

This fixes the deploy pipeline failure:
failed to calculate checksum: /backend/settings.gradle: not found
2026-05-20 11:48:29 +02:00
0137a5005b feat: add production deploy pipeline and nginx config for bilhej.se
All checks were successful
CI / Lint, type check, unit tests, coverage (push) Successful in 1m51s
CI / E2E browser tests (push) Successful in 1m18s
Add a manually-triggered deploy workflow that builds production Docker
images and starts the stack on the srvr.nu server.

- : workflow_dispatch with version input,
  writes production .env from Forgejo secrets, builds and starts the
  docker-compose.prod.yml stack, runs health checks via temporary curl
  containers on the bilhej_default Docker network, tags the git commit.

- : nginx server block for bilhej.se.
  Handles HTTP→HTTPS redirect, SSL termination with Let's Encrypt certs,
  and proxies all traffic to the bilhej-frontend-prod container on the
  Docker 'web' network. The frontend container handles /api/ proxying
  to the backend internally.

To deploy:
1. Add production secrets to Forgejo (Settings → Actions → Secrets)
2. Trigger deploy from Actions → Deploy to Production
3. Run certbot for bilhej.se SSL (one-time setup)
4. Add docker/bilhej.nginx.conf to srvr.nu nginx container
5. Point bilhej.se DNS A record to srvr.nu IP
2026-05-19 21:21:36 +02:00
e8530b8d95 fix: E2E pipeline — vite preview instead of nginx, ts build fixes
Some checks failed
CI / Lint, type check, unit tests, coverage (push) Successful in 11m12s
CI / E2E browser tests (push) Failing after 8m0s
Three problems caused E2E browser tests to fail in Forgejo CI:

1. TypeScript build errors in  (frontend.e2e.Dockerfile):
   -  used parameter property  which violates
     . Replaced with explicit property declaration.
   -  included  in type-checking, causing
     mock Response type mismatches. Added .
   -  mock Order was missing  field.

2. Nginx SSL crash:
   -  copied production
     which references SSL certs that don't exist in the e2e image.
   - Replaced nginx entirely with  (simpler, no SSL needed).
   - Added  to  so  routes to backend.

3. Docker context hygiene:
   -  excludes  so test files don't
     bloat the build context or trigger type errors in the container.

All other files untouched.
2026-05-19 18:53:52 +02:00
1f1016a775 feat: add isolated E2E browser test pipeline for Forgejo Actions
Some checks failed
CI / Lint, type check, unit tests, coverage (push) Successful in 1m53s
CI / E2E browser tests (push) Failing after 11s
Implement per-job Docker-in-Docker (DinD) for E2E tests, giving each
job a completely isolated Docker daemon and network. This prevents
leakage to the host Docker or other containers.

The previous E2E approach failed because:
1. The Forgejo runner's container.docker_host was not set, causing
   the runner itself to try unix:///var/run/docker.sock and crash-loop.
2. The host DinD daemon had isolated networking — job containers
   running docker compose could not resolve 'dind' hostname or access
   host filesystem bind mounts (e.g. .:/app).

New approach — zero bind mounts, all COPY-based images:

- docker/backend.e2e.Dockerfile: multi-stage build from repo root.
  Copies gradlew + settings.gradle + backend/build.gradle to download
  dependencies in a cacheable layer, then copies backend/src and builds
  the bootJar. Runs the JAR directly on startup.

- docker/frontend.e2e.Dockerfile: multi-stage Node build → nginx.
  Reuses existing docker/nginx.conf for /api proxy to backend service.
  No volume mounts, fully self-contained.

- docker/playwright.e2e.Dockerfile: extends official Playwright image.
  Installs deps from package-lock.json, copies e2e tests + config.

- docker-compose.e2e.yml: zero bind mounts. Services depend on each
  other in order: postgres (healthy) → backend → frontend → playwright.
  Playwright waits for backend and frontend via curl loops before
  running tests.

- .forgejo/workflows/ci.yml: E2E job adds a 'dind' service container
  (docker:28-dind, privileged, no TLS). The job sets DOCKER_HOST to
  tcp://dind:2375 so the docker CLI inside the job talks to the
  per-job DinD daemon. The compose file is docker-compose.e2e.yml.

- Runner fix on tocke: added container.docker_host: 'tcp://dind:2375'
  to runner-config.yaml so the runner's own Docker client connects to
  the host DinD container, stopping the crash loop.

Key properties:
- Network isolation: each E2E job gets its own DinD with its own
  container network. No host container visibility.
- No bind mount leakage: all images use COPY instead of volume mounts.
  The per-job DinD has its own filesystem and can't see host paths.
- Deterministic: builds start from clean state every time. Image cache
  exists only within the per-job DinD lifetime.
- Lint-and-test job is untouched and remains green.
2026-05-19 18:07:12 +02:00
d70196112d refactor: move Gradle wrapper to repo root, add convenience tasks
Move gradlew, gradle/wrapper, and settings.gradle from backend/ to
the repo root so build commands run from the top-level directory.
This follows the standard multi-project Gradle layout where the build
tool lives alongside docker-compose.yml and all submodules.

- Move gradlew + gradle/wrapper/* from backend/ to repo root
- Move settings.gradle to root with rootProject.name and include 'backend'
- Create root build.gradle with convenience tasks: check, up, down, reset
- check task chains frontend lint → frontend test → backend check
- Update docker-compose.yml backend volume from ./backend:/app to .:/app
- Update backend.Dockerfile entrypoint to ./gradlew :backend:bootRun
- Update AGENTS.md: document ./gradlew check, up, down, reset
- Delete backend/settings.gradle (now at root)
- Add .gradle/ and build/ to .gitignore
- Add !gradle/wrapper/gradle-wrapper.jar exception (blocked by *.jar rule)

All 38 frontend tests and 33 backend tests pass via ./gradlew check.
2026-05-01 18:40:18 +02:00
4d449d54d0 feat: add Docker Compose setup with dev and prod configurations
- docker-compose.yml (dev): 3 services — postgres:16, backend (gradle
  bootRun with JDK 21, spring-boot-devtools), frontend (Vite HMR on
  node:24-alpine). Source volume mounts for live editing, Gradle cache
  volume for fast rebuilds, pg_isready healthcheck on postgres.

- docker-compose.prod.yml (prod): same 3 services but with multi-stage
  Dockerfiles. Backend: Gradle bootJar → JRE Alpine, non-root user.
  Frontend: npm ci + vite build → nginx:alpine serving static dist/.
  SSL termination via self-signed cert (auto-generated in entrypoint).
  No source mounts, restart: unless-stopped, separate volumes.

- application-docker.yml: Spring profile overriding H2 with PostgreSQL
  via env vars. Hostname "postgres" resolved by Docker Compose DNS.

- Vite proxy /api → backend:8080 for dev. nginx nginx.conf handles
  /api proxy + SPA fallback + gzip + SSL in prod.

- AGENTS.md, README.md: architecture diagram, dev vs prod comparison
  table, Spring profiles docs, file reference updates.
2026-05-01 01:45:07 +02:00