Files
impuestospy/.github/workflows/ci.yml
T
MichilisandClaude Opus 5 37370dd079
CI / types, lint and rules coverage (push) Canceled after 0s
CI / tests (postgres) (push) Canceled after 0s
CI / tests (sqlite) (push) Canceled after 0s
CI / playwright (push) Canceled after 0s
phase-8: run it on Postgres, and find out what that was hiding
Six phases claimed the product runs on SQLite and on Postgres. Nothing
had ever run it on Postgres. TEST_DATABASE_URL now points the whole
suite at a real server and CI runs both arms.

The first run found a bug that would have shipped. better-auth's banned
flag is integer 0/1 on SQLite and a real boolean on Postgres, and the
code read it as `banned === 1`, so on Postgres an account someone asked
us to freeze went on receiving email. Both of those flags are now typed
for either dialect and read through isFlagSet.

It also found the migration advisory lock being taken on a pool. An
advisory lock belongs to the session that took it, so a lock on one
pooled connection and an unlock on another leaves it held. Two replicas
migrating at once is the ordinary case in k8s and is precisely what it
was there to protect.

New for the scaled mode: k8s manifests with migrations as an
initContainer, one poller in its own worker Deployment rather than one
per API replica, and an Ingress that exposes the web app only. The
storage drivers finally have tests, S3 included, since scaled mode
requires it and it had never been exercised.

Two acceptance tests, both checked against a deliberately broken build
first: two workers claiming a hundred jobs report 188 claims with SKIP
LOCKED removed, and the in-flight request is cut off with the drain wait
removed.

340 tests on SQLite, 341 on Postgres, 70 Playwright, rules coverage 100%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 22:03:13 +00:00

132 lines
4.3 KiB
YAML

name: CI
on:
push:
branches: [main, master]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Everything that does not need a database, once.
static:
name: types, lint and rules coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm lint
# RULES.md section 10: the tax logic is the product, so its coverage is a gate.
- run: pnpm test:coverage
# The same suite against both dialects. SPEC.md section 13: the product claims to run on
# SQLite and on Postgres, so both are run rather than one being assumed from the other.
test:
name: tests (${{ matrix.dialect }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dialect: [sqlite, postgres]
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: impuestos
POSTGRES_PASSWORD: impuestos
POSTGRES_DB: impuestos
ports: ['5432:5432']
options: >-
--health-cmd "pg_isready -U impuestos"
--health-interval 5s
--health-timeout 5s
--health-retries 10
# Scaled mode requires S3, so the driver is exercised against a real object store
# rather than assumed from the local one.
minio:
image: bitnami/minio:latest
env:
MINIO_ROOT_USER: impuestos
MINIO_ROOT_PASSWORD: impuestos-secret
MINIO_DEFAULT_BUCKETS: comprobantes
ports: ['9000:9000']
options: >-
--health-cmd "curl -f http://localhost:9000/minio/health/live"
--health-interval 5s
--health-timeout 5s
--health-retries 20
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
# TEST_DATABASE_URL unset means SQLite in memory, which is what the harness falls
# back to. Set, every harness builds itself a database of its own inside this server,
# and the two-worker claim test in scale.test.ts stops skipping.
- name: Run the suite
env:
TEST_DATABASE_URL: ${{ matrix.dialect == 'postgres' && 'postgres://impuestos:impuestos@localhost:5432/impuestos' || '' }}
TEST_S3_ENDPOINT: http://localhost:9000
TEST_S3_BUCKET: comprobantes
TEST_S3_REGION: us-east-1
TEST_S3_ACCESS_KEY_ID: impuestos
TEST_S3_SECRET_ACCESS_KEY: impuestos-secret
run: pnpm test
# The golden paths, against a built stack rather than the dev server. SQLite and local
# files: that is the combined mode compose ships, and it is the one the helpers in
# e2e/db.ts can read, since they open the database file to check what a flow wrote.
e2e:
name: playwright
runs-on: ubuntu-latest
env:
DATABASE_URL: sqlite:./apps/api/data/app.db
STORAGE_DRIVER: local
STORAGE_LOCAL_PATH: ./apps/api/data/files
BETTER_AUTH_SECRET: ci-secret-that-is-long-enough-32-chars
BETTER_AUTH_URL: http://localhost:3005
APP_PUBLIC_URL: http://localhost:3005
PORT: '4000'
API_INTERNAL_URL: http://localhost:4000
E2E_BASE_URL: http://localhost:3005
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm db:migrate
- run: pnpm db:seed
- run: pnpm build
- name: Start the stack
run: |
node --import tsx apps/api/src/index.ts &
pnpm --filter @impuestos/web start &
npx --yes wait-on http://localhost:4000/readyz http://localhost:3005/healthz -t 120000
- run: pnpm test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 7