diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 664c166..3c55bab 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -19,7 +19,9 @@ jobs: # existing clone in /opt/tanabata. See docs/DEPLOY.md for runner setup. # # Only shell steps here (no `uses:` actions), so the host needs git + docker - # and nothing else — no node, no rsync. + # and nothing else — no node, no go, no rsync. The test step below runs the + # Go and Node toolchains inside throwaway containers, so nothing has to be + # installed on the host. runs-on: host env: @@ -35,6 +37,47 @@ jobs: git fetch --prune origin git reset --hard origin/master + - name: Run tests + working-directory: /opt/tanabata + # Everything runs INSIDE throwaway toolchain containers — the host only + # needs docker (which it already uses for `docker compose`). Nothing (Go, + # Node, Postgres, vips/ffmpeg/exiftool) has to be installed on the host. + # The orchestration below uses only bash builtins + docker. A failure here + # fails the job, so a red build never reaches production. Module/npm caches + # persist in named volumes for speed. + shell: bash + run: | + set -euo pipefail + + docker rm -f tfm-ci-db >/dev/null 2>&1 || true + docker network rm tfm-ci-net >/dev/null 2>&1 || true + docker network create tfm-ci-net >/dev/null + trap 'docker rm -f tfm-ci-db >/dev/null 2>&1 || true; docker network rm tfm-ci-net >/dev/null 2>&1 || true' EXIT + + docker run -d --name tfm-ci-db --network tfm-ci-net \ + -e POSTGRES_PASSWORD=postgres postgres:14-alpine >/dev/null + # Wait for Postgres, using pg_isready inside the DB container (no host tools). + for ((i = 0; i < 30; i++)); do + docker exec tfm-ci-db pg_isready -U postgres >/dev/null 2>&1 && break + sleep 1 + done + + # Backend: full suite, including the integration tests, against the + # ephemeral Postgres. -buildvcs=false since the .git dir isn't mounted. + docker run --rm --network tfm-ci-net \ + -v /opt/tanabata/backend:/src -w /src \ + -v tfm-ci-gomod:/go/pkg/mod -v tfm-ci-gocache:/root/.cache/go-build \ + -e CGO_ENABLED=0 \ + -e TANABATA_TEST_ADMIN_DSN="postgres://postgres:postgres@tfm-ci-db:5432/postgres?sslmode=disable" \ + golang:1.26-alpine go test -buildvcs=false -count=1 ./... + + # Frontend: type-check + production build (also validates openapi via + # the generate:types prestep). + docker run --rm \ + -v /opt/tanabata:/repo -w /repo/frontend \ + -v tfm-ci-npm:/root/.npm \ + node:22-alpine sh -c "npm ci && npm run check && npm run build" + - name: Build image and start the stack working-directory: /opt/tanabata # .env must already exist in DEPLOY_DIR on the host (secrets + DB mode). diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 1b9319f..e5b0b79 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -3,13 +3,16 @@ Tanabata is deployed by a [Gitea Actions](https://docs.gitea.com/usage/actions/overview) workflow ([`.gitea/workflows/deploy.yml`](../.gitea/workflows/deploy.yml)) that runs on the **production host itself**. On every push to `master` it updates the -git clone in `/opt/tanabata` and runs `docker compose up -d --build` there, so the -image is built from the freshly-pushed code and the stack is restarted. +git clone in `/opt/tanabata`, runs the test suite (backend + frontend, in +throwaway toolchain containers), and — only if it passes — runs +`docker compose up -d --build` there, so the image is built from the +freshly-pushed code and the stack is restarted. ``` push master ──> Gitea (container) ──> act_runner (host, "host" label) │ git fetch + reset --hard (in /opt/tanabata) - └ docker compose up -d --build + │ run tests (go + node in containers; ephemeral Postgres) + └ docker compose up -d --build (only if tests pass) ``` The Gitea server runs in a container, but the **runner runs directly on the host**