name: Tests # backend-integration lives in its own manual workflow # (.gitea/workflows/backend-integration.yml) rather than running here. It held # the runner for 3h12m on 2026-08-18 — 87 seconds of tests followed by a hang # after the run completed — and blocked frontend-e2e behind it for the same # three hours. Run it from Actions before merging anything that touches the API # or the database. on: push: branches: [main] pull_request: types: [opened, synchronize, reopened] workflow_dispatch: jobs: # Fails only on the rules with real defect-catching value — unhandled # promises, hook dependencies, missing alt text. Everything else is a warning # and does not block, which is why no --max-warnings flag appears here: # ESLint exits non-zero on errors and zero on warnings on its own. The split, # and the measurements behind it, are in # docs/superpowers/specs/2026-08-19-eslint-design.md. lint: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - name: Install backend deps run: npm install working-directory: backend - name: Lint backend run: npm run lint working-directory: backend - name: Install frontend deps run: npm install working-directory: frontend - name: Lint frontend run: npm run lint working-directory: frontend backend-unit: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - name: Install deps run: npm install working-directory: backend - name: Run unit tests id: unit continue-on-error: true run: npm run test:unit:json working-directory: backend - name: Summarize if: always() run: node scripts/summarize-jest.js backend/unit-results.json "Backend Unit Test" - name: Fail job if tests failed if: steps.unit.outcome == 'failure' run: exit 1 frontend-e2e: runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_USER: redefined_test POSTGRES_PASSWORD: redefined_test POSTGRES_DB: redefined_test options: >- --health-cmd "pg_isready -U redefined_test" --health-interval 5s --health-timeout 5s --health-retries 10 env: PGHOST: postgres PGPORT: 5432 PGUSER: redefined_test PGPASSWORD: redefined_test PGDATABASE: redefined_test PORT: 3000 DEMO_MODE: 'true' UPLOADS_DIR: /tmp/redefined-uploads # NODE_ENV is deliberately unset: `npm install` omits devDependencies when # NODE_ENV=production, which strips tsc/vite/@playwright/test and breaks the # build. It would also flip the session cookie to Secure, which the e2e run # serves over plain http. steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v4 with: node-version: '20' - name: Install backend deps run: npm install working-directory: backend - name: Run migrations run: node migrate.js up working-directory: backend - name: Build backend run: npm run build working-directory: backend - name: Start backend run: | mkdir -p /tmp/redefined-uploads node dist/server.js > /tmp/backend.log 2>&1 & for i in $(seq 1 30); do if node -e "require('http').get('http://localhost:3000/api/config', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"; then echo "Backend ready after ${i}s" exit 0 fi sleep 1 done echo "Backend did not become ready within 30s:" cat /tmp/backend.log exit 1 working-directory: backend - name: Install frontend deps run: npm install working-directory: frontend - name: Install Playwright browsers run: npx playwright install --with-deps chromium working-directory: frontend - name: Run Playwright tests id: e2e continue-on-error: true env: PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-results.json run: npx playwright test --reporter=json working-directory: frontend - name: Backend log if: steps.e2e.outcome == 'failure' run: cat /tmp/backend.log - name: Summarize if: always() run: node scripts/summarize-playwright.js frontend/playwright-results.json - name: Fail job if tests failed if: steps.e2e.outcome == 'failure' run: exit 1