From d884a8c2abee816650de034ab14b50caee1b6371 Mon Sep 17 00:00:00 2001 From: julano Date: Mon, 6 Jul 2026 12:05:17 -0300 Subject: [PATCH] ci: unit + build job and a real integration job (e2e vs production server) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements docs/07 stages 1-2 as one workflow: - unit job: pnpm install (frozen lockfile), prisma generate, vitest, build - integration job: postgres:16 service container -> migrate deploy -> pnpm build -> node server.js (ORIGIN set) -> healthcheck -> pnpm e2e (auth + rooms + match over real HTTP/WS) - auth e2e: the Secure cookie assertion is now mode-aware (E2E_SECURE=1 for production servers, which set the attribute; scripts replay the cookie header so both modes work) - retire the legacy gh-pages workflow (pre-auth static demo) Rehearsed locally with the exact integration steps: build, node server.js, healthcheck, full e2e suite — all green. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 79 ++++++++++++++++++++++++++++++++++++ .github/workflows/static.yml | 57 -------------------------- e2e/auth.e2e.mjs | 6 ++- 3 files changed, 84 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/static.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7f6490e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + unit: + name: unit tests + build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 8 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: npx prisma generate + - run: pnpm test + - run: pnpm build + + integration: + name: e2e against the production server + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_USER: hyperchessuser + POSTGRES_PASSWORD: hyperchess + POSTGRES_DB: hyperchess + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U hyperchessuser -d hyperchess" + --health-interval 5s + --health-timeout 3s + --health-retries 10 + env: + DATABASE_URL: postgresql://hyperchessuser:hyperchess@localhost:5432/hyperchess + ORIGIN: http://localhost:3000 + BASE: http://localhost:3000 + E2E_SECURE: '1' # production build sets the Secure cookie attribute + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 8 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: npx prisma generate + - run: npx prisma migrate deploy + - run: pnpm build + + - name: start server + run: | + node server.js & + echo $! > server.pid + curl --retry 20 --retry-delay 1 --retry-connrefused -sf http://localhost:3000/healthcheck + + - name: run e2e suite (auth + rooms + match over HTTP/WS) + run: pnpm e2e + + - name: stop server + if: always() + run: kill "$(cat server.pid)" || true diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml deleted file mode 100644 index 6f3d140..0000000 --- a/.github/workflows/static.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Deploy to GitHub Pages - -on: - push: - branches: 'gh-pages' - -jobs: - build_site: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - # If you're using pnpm, add this step then change the commands and cache key below to use `pnpm` - # - name: Install pnpm - # uses: pnpm/action-setup@v2 - # with: - # version: 8 - - - name: Install Node.js - uses: actions/setup-node@v3 - with: - node-version: 19 - cache: npm - - - name: Install dependencies - run: npm install - - - name: build - env: - BASE_PATH: '/hyperchess' - run: | - npm run build - touch build/.nojekyll - - - name: Upload Artifacts - uses: actions/upload-pages-artifact@v1 - with: - # this should match the `pages` option in your adapter-static options - path: 'build/' - - deploy: - needs: build_site - runs-on: ubuntu-latest - - permissions: - pages: write - id-token: write - - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: Deploy - id: deployment - uses: actions/deploy-pages@v1 diff --git a/e2e/auth.e2e.mjs b/e2e/auth.e2e.mjs index 4acc474..2875743 100644 --- a/e2e/auth.e2e.mjs +++ b/e2e/auth.e2e.mjs @@ -18,7 +18,11 @@ assert(!!cookie, `signup set ${COOKIE} cookie (status ${signup.status})`); const attrs = cookieAttrs(signup); assert(/HttpOnly/i.test(attrs), 'cookie is HttpOnly'); assert(/SameSite=Lax/i.test(attrs), 'cookie is SameSite=Lax'); -assert(!/Secure/i.test(attrs), 'cookie has no Secure on localhost'); +// dev server omits Secure (http localhost); the production build sets it. +// Scripts replay the cookie header directly, so both modes work — set +// E2E_SECURE=1 when targeting a production server. +const expectSecure = process.env.E2E_SECURE === '1'; +assert(/;\s*Secure/i.test(attrs) === expectSecure, `cookie Secure attribute matches mode (expect ${expectSecure})`); const authed = (c) => (path, opts = {}) => fetch(`${BASE}${path}`, { ...opts, headers: { ...(opts.headers || {}), cookie: c, origin: BASE } });