fix(ci): pass migration options via CLI flags for node-pg-migrate v7 #404
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| # ---------------------------------------------------------------- | |
| # Per-workspace lint + typecheck + test, run in parallel. Each | |
| # matrix entry runs on its own runner so the wall time drops from | |
| # the previous ~15 min (6 sequential lints + 6 sequential | |
| # typechecks + 4 sequential test:cov + lockfile checks) to roughly | |
| # the slowest single workspace. The actions/setup-node `cache: npm` | |
| # hint means each matrix entry's `npm ci` is near-instant on cache | |
| # hits. | |
| # | |
| # The matrix drives WHICH scripts run (lint, typecheck, test:cov) | |
| # AND whether the workspace needs the test Docker database. The | |
| # postgres service in `services:` is therefore started on every | |
| # matrix entry — a few seconds of unused-postgres overhead per entry | |
| # in exchange for a single simplified workflow shape. | |
| # ---------------------------------------------------------------- | |
| quality: | |
| name: Quality (${{ matrix.workspace.label }}) | |
| runs-on: ubuntu-latest | |
| # Cancel any in-flight job for the same branch / workspace on a new | |
| # commit so force-pushes don't queue up. | |
| concurrency: | |
| group: ci-quality-${{ matrix.workspace.label }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| workspace: | |
| # id-only workspaces first so they fail fast; the integration | |
| # suites are last so they get the longest wall-clock budget. | |
| # `needs_tests: false` skips the `npm run test:cov` step and | |
| # the Codecov upload — these workspaces don't ship Jest suites | |
| # (types is compile-only; contracts is shared schema/zod | |
| # definitions whose provider/consumer verification lives in | |
| # the api and sdk matrices respectively). | |
| - { | |
| label: types, | |
| path: packages/types, | |
| needs_db: false, | |
| needs_tests: false, | |
| } | |
| - { | |
| label: contracts, | |
| path: tests/contracts, | |
| needs_db: false, | |
| needs_tests: false, | |
| } | |
| - { | |
| label: sdk, | |
| path: xstreamroll-sdk, | |
| needs_db: false, | |
| needs_tests: true, | |
| } | |
| - { | |
| label: processing, | |
| path: xstreamroll-processing, | |
| needs_db: false, | |
| needs_tests: true, | |
| } | |
| - { label: app, path: app, needs_db: false, needs_tests: true } | |
| - { label: api, path: api, needs_db: true, needs_tests: true } | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_DB: test | |
| POSTGRES_USER: test | |
| POSTGRES_PASSWORD: test | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 2s | |
| --health-timeout 2s | |
| --health-retries 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| # Install ALL workspace dependencies in one shot via the root | |
| # `npm ci`. npm itself does not support `npm ci` inside an | |
| # individual workspace (the lockfile lives at the repo root for | |
| # workspaces in this repo), so a single root install is the | |
| # documented path (#375). | |
| - name: Install workspace dependencies | |
| run: npm ci | |
| - name: Build types package | |
| run: npm run build --workspace=packages/types | |
| - name: Build contract tests package | |
| run: npm run build --workspace=tests/contracts | |
| - name: Lint — ${{ matrix.workspace.label }} | |
| run: cd ${{ matrix.workspace.path }} && npm run lint | |
| env: | |
| # Database-bound lint config (api) reads DATABASE_URL; give | |
| # it the same postgres service that the test step will use. | |
| DATABASE_URL: postgresql://test:test@localhost:5432/test | |
| JWT_SECRET: test-secret | |
| STREAM_API_KEY: test-api-key | |
| - name: Typecheck — ${{ matrix.workspace.label }} | |
| run: cd ${{ matrix.workspace.path }} && npm run typecheck | |
| - name: Start test database | |
| if: matrix.workspace.needs_db | |
| run: docker compose -f docker-compose.test.yml up -d --wait | |
| # Issue #340 — exercise the migration runner against the freshly | |
| # provisioned test DB before the api test suite runs. The test DB | |
| # starts empty (no schema.sql autoload), so this also confirms the | |
| # migration files together reproduce the schema end-to-end. Runs | |
| # the idempotent "up" migration; the runner tracks applied | |
| # migrations in `pgmigrations` so reruns against a polluted DB | |
| # stay safe. | |
| - name: Run database migrations (Issue #340) | |
| if: matrix.workspace.needs_db | |
| run: cd api && npm run migrate | |
| env: | |
| DATABASE_URL: postgresql://xstreamroll:xstreamroll@localhost:5433/xstreamroll_test | |
| # Provider verification for tests/contracts/ runs as part of this | |
| # step (api/src/contract-provider.spec.ts matches the api Jest | |
| # config's `*.spec.ts` pattern) — a contract violation fails the | |
| # build the same way any other api test failure would. Consumer | |
| # contract verification (xstreamroll-sdk/__tests__/contract.consumer.test.ts) | |
| # runs in the sdk matrix entry. | |
| - name: Test — ${{ matrix.workspace.label }} (with coverage) | |
| if: matrix.workspace.needs_tests | |
| run: cd ${{ matrix.workspace.path }} && npm run test:cov | |
| env: | |
| DATABASE_URL: postgresql://xstreamroll:xstreamroll@localhost:5433/xstreamroll_test | |
| JWT_SECRET: test-secret | |
| STREAM_API_KEY: test-api-key | |
| - name: Stop test database | |
| if: always() && matrix.workspace.needs_db | |
| run: docker compose -f docker-compose.test.yml down -v | |
| - name: Upload ${{ matrix.workspace.label }} coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| if: always() && matrix.workspace.needs_tests | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ${{ matrix.workspace.path }}/coverage/lcov.info | |
| flags: ${{ matrix.workspace.label }} | |
| name: ${{ matrix.workspace.label }}-coverage | |
| fail_ci_if_error: false | |
| # ---------------------------------------------------------------- | |
| # Lockfile-drift guard (issue #375). Runs `npm ci` once at the repo | |
| # root and confirms no `package-lock.json` was mutated by the | |
| # install. Lives in its own job so it can run in parallel with the | |
| # quality matrix — readers get the result faster — and so a failure | |
| # is reported independently of any specific workspace's lint/test | |
| # results. The samePaths list keeps the guard future-proof: any | |
| # workspace that gains a workspace-local lockfile later MUST be | |
| # added here. | |
| # ---------------------------------------------------------------- | |
| lockfile: | |
| name: Verify lockfile integrity | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| - name: Install workspace dependencies | |
| run: npm ci | |
| - name: Verify lockfile integrity | |
| run: bash .github/scripts/verify-lockfiles.sh | |
| # ---------------------------------------------------------------- | |
| # k8s/70-network-policies.yaml validator (issue #357). Runs WITHOUT | |
| # a cluster: parses the YAML against documented invariants (see | |
| # scripts/validate-network-policies.js for the full list). The | |
| # validator fails CI on a regression that would otherwise break | |
| # production traffic — e.g., a deleted allow rule or a typo'd | |
| # podSelector label — instead of waiting for the rollout. | |
| # ---------------------------------------------------------------- | |
| validate-k8s: | |
| name: Validate k8s NetworkPolicies | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| - name: Install workspace dependencies | |
| run: npm ci | |
| - name: Validate NetworkPolicies | |
| run: npm run validate:network-policies | |
| bundle-analysis: | |
| runs-on: ubuntu-latest | |
| needs: quality | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| - name: Install workspace dependencies | |
| run: npm ci | |
| - name: Build types package | |
| run: npm run build --workspace=packages/types | |
| - name: Build app with analysis | |
| run: cd app && npm run analyze | |
| - name: List analyzer output | |
| run: ls -la app/.next || true; ls -la app/.next/analyze || true | |
| - name: Check bundle sizes | |
| run: node .github/scripts/check-bundle-size.js | |
| env: | |
| BUNDLE_BUDGET_TOTAL: 5000000 | |
| BUNDLE_BUDGET_PER_LARGEST: 500000 | |
| - name: Upload analyzer artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: app-bundle-analysis | |
| path: app/.next/analyze | |
| - name: Upload bundle summary | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: app-bundle-summary | |
| path: app/.next/analyze/summary.json | |
| # Same lockfile-drift invariant as the `lockfile` job (#375). The | |
| # bundle-analysis install path runs again here, so any accidental | |
| # mutation to a package-lock.json must also fail this job rather | |
| # than silently propagate. | |
| - name: Verify lockfile integrity | |
| run: bash .github/scripts/verify-lockfiles.sh | |
| - name: Comment PR with summary | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: actions/github-script@v6 | |
| continue-on-error: true | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| const path = 'app/.next/analyze/summary.json'; | |
| if (fs.existsSync(path)) { | |
| const summary = JSON.parse(fs.readFileSync(path, 'utf8')); | |
| const body = `Bundle analysis summary:\n\n- Total JS size: ${summary.totalBytes} bytes\n- Largest file: ${summary.largestFile} (${summary.largestBytes} bytes)\n\nArtifacts: app-bundle-analysis`; | |
| github.rest.issues.createComment({ | |
| issue_number: context.payload.pull_request.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body | |
| }); | |
| } else { | |
| console.log('No summary available') | |
| } |