From a0a987d561e386e200ef7cd3ed9bbb8828f9f15e Mon Sep 17 00:00:00 2001 From: Masahiko AMANO Date: Fri, 3 Jul 2026 10:57:16 +0300 Subject: [PATCH] ci(project): run the test suite before deploying Gate the deploy on tests. After pulling master and before building the image, run the full Go suite (incl. integration tests, against an ephemeral Postgres) and the frontend type-check + build. Everything runs inside throwaway toolchain containers, so the host still needs only docker and a red build never reaches production. Update DEPLOY.md to reflect the gate. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/deploy.yml | 45 ++++++++++++++++++++++++++++++++++++- docs/DEPLOY.md | 9 +++++--- 2 files changed, 50 insertions(+), 4 deletions(-) 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**