ci(project): run the test suite before deploying
deploy / deploy (push) Failing after 1m35s

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 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 10:57:16 +03:00
parent 3e04da3926
commit a0a987d561
2 changed files with 50 additions and 4 deletions
+44 -1
View File
@@ -19,7 +19,9 @@ jobs:
# existing clone in /opt/tanabata. See docs/DEPLOY.md for runner setup. # 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 # 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 runs-on: host
env: env:
@@ -35,6 +37,47 @@ jobs:
git fetch --prune origin git fetch --prune origin
git reset --hard origin/master 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 - name: Build image and start the stack
working-directory: /opt/tanabata working-directory: /opt/tanabata
# .env must already exist in DEPLOY_DIR on the host (secrets + DB mode). # .env must already exist in DEPLOY_DIR on the host (secrets + DB mode).
+6 -3
View File
@@ -3,13 +3,16 @@
Tanabata is deployed by a [Gitea Actions](https://docs.gitea.com/usage/actions/overview) Tanabata is deployed by a [Gitea Actions](https://docs.gitea.com/usage/actions/overview)
workflow ([`.gitea/workflows/deploy.yml`](../.gitea/workflows/deploy.yml)) that workflow ([`.gitea/workflows/deploy.yml`](../.gitea/workflows/deploy.yml)) that
runs on the **production host itself**. On every push to `master` it updates the 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 git clone in `/opt/tanabata`, runs the test suite (backend + frontend, in
image is built from the freshly-pushed code and the stack is restarted. 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) push master ──> Gitea (container) ──> act_runner (host, "host" label)
│ git fetch + reset --hard (in /opt/tanabata) │ 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** The Gitea server runs in a container, but the **runner runs directly on the host**