# Server-side mirror of the local `siftlode check` gate (E1 S3 / DX-1), so `main` is always green # independent of any one machine. Two parallel jobs match the two local lanes: # frontend — tsc (3 projects) + eslint + prettier --check + knip + vitest # backend — ruff + the full pytest suite (pure-logic + the DB-backed lane) against a real Postgres # # Runner: the lab act_runner (LXC 115) mounts the Docker socket, so service containers work. The backend # runs pytest NATIVELY against a `postgres` service (not the local docker-compose.test.yml, whose # bind-mount is unreliable under a mounted-socket runner); fidelity is kept by installing the SAME pinned # requirements, Postgres 16, and ffmpeg, and by setting SIFTLODE_TEST_REQUIRE_DB=1 so a DB/ffmpeg that # failed to come up is a hard error, never a silent skip. # # The frontend container's node major is NOT free: it must equal the prod build image's and the dev # machine's, because vitest/tsc read the engine's own Intl and web-API implementations and those split # between majors. `.nvmrc` is the single source of truth and # `frontend/src/lib/nodeEngine.contract.test.ts` fails this gate if any of them drift apart. name: gate # Branch pushes only. A release pushes `main` and then the `v` tag, and a bare `on: [push]` # treated the tag as a second push of the IDENTICAL commit — every release ran this whole gate # twice, to the same verdict, for nothing (runs #81-#90 are five such pairs). `branches: ['**']` # keeps every branch covered and drops tag refs. # # It has to be `branches`, NOT the `tags-ignore` this obviously wants to be. Gitea's matchPushEvent # requires EVERY named filter to match, and each one bails on the wrong ref kind: `tags-ignore` # starts with `if !refName.IsTag() { break }`, so a filter meant to skip tags rejects every BRANCH # push and the gate stops running entirely. `branches` bails the same way on a tag, which is the # direction we want. (Measured in go-gitea v1.27.3 modules/actions/workflows.go, not assumed — it # differs from GitHub, where `tags-ignore` alone leaves branches unfiltered.) # # If that filter were ever wrong the gate would not run at all — which is why `siftlode release` # now ends by reading the verdict back for the sha it pushed, and says so out loud when there is # no run for it. A gate that silently stops running is the failure this repo already lived through. on: push: branches: ["**"] pull_request: jobs: frontend: runs-on: ubuntu-latest container: node:24-bookworm defaults: run: working-directory: frontend steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run typecheck - run: npm run lint - run: npm run format:check - run: npm run knip - run: npm run test backend: runs-on: ubuntu-latest # A combined python+node image: python 3.12 (matches the prod Dockerfile) for the tests, and node # so the JS actions (checkout) run. actions/setup-python is NOT usable here — the runner can't reach # GitHub's python-versions download, so it must be baked into the image. # # This node runs the checkout action, never our code, so it is deliberately OUTSIDE the engine # contract the frontend job is held to — it tracks `.nvmrc` as hygiene (node 20 went EOL in April # 2026), but a missing upstream tag here must never be able to block the gate. container: nikolaik/python-nodejs:python3.12-nodejs24 services: postgres: image: postgres:16-alpine env: POSTGRES_USER: siftlode POSTGRES_PASSWORD: siftlode POSTGRES_DB: siftlode_test options: >- --health-cmd "pg_isready -U siftlode -d siftlode_test" --health-interval 2s --health-timeout 3s --health-retries 30 env: DATABASE_URL: postgresql+psycopg://siftlode:siftlode@postgres:5432/siftlode_test SIFTLODE_TEST_REQUIRE_DB: "1" steps: - uses: actions/checkout@v4 # ffmpeg for the poster/thumbnail tests; postgresql-client for the readiness wait below. - run: apt-get update && apt-get install -y --no-install-recommends ffmpeg postgresql-client - name: Wait for Postgres run: until pg_isready -h postgres -U siftlode -d siftlode_test; do sleep 1; done - run: pip install --no-cache-dir -r backend/requirements.lock -r backend/requirements-dev.txt - name: ruff run: python -m ruff check --config backend/ruff.toml backend scripts - name: ruff format --check run: python -m ruff format --check --config backend/ruff.toml backend scripts - name: pytest run: python -m pytest working-directory: backend # Dependency CVE audit over the pinned lock. Non-blocking for now (informational) — a real CVE # shows up in the run without failing the gate; tighten to a hard fail once the noise is known. - name: pip-audit (advisory) run: pip install --no-cache-dir pip-audit && pip-audit -r backend/requirements.lock continue-on-error: true