fix(reporting): split "1 payment" into the three things it was confla… #187
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] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| quality: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| - name: Install root dependencies | |
| run: npm ci | |
| - name: Install server dependencies | |
| run: npm ci --prefix server | |
| # Type-check the full Next.js project. Fast (~15s), catches API | |
| # regressions that tests would miss. | |
| - name: Typecheck | |
| run: npm run typecheck | |
| # Lint: error baseline is 0 as of Week 3 (2026-04-24). Warnings still | |
| # permitted — ESLint exits 0 on warning-only runs, so this step passes | |
| # until a real error regression slips in, at which point it blocks. | |
| - name: Lint | |
| run: npm run lint | |
| # Full test suite: 508 vitest + 11 node:test = 519 assertions as of | |
| # 2026-04-24. Covers color data, color utilities, collection prose lint, | |
| # and the StoreKit JWS contract. | |
| - name: Tests | |
| run: npm test | |
| # Smoke: make sure the production build still succeeds (catches breakage | |
| # that only shows up at build time — static page generation, sitemap, | |
| # Sentry source-map upload, etc.). | |
| - name: Production build | |
| run: npm run build | |
| env: | |
| NEXT_PUBLIC_SITE_URL: https://colorarchive.org | |
| NEXT_PUBLIC_API_URL: https://api.colorarchive.org | |
| # Sentry auth token is intentionally omitted in CI — source maps | |
| # still upload from Vercel deploys where the token is set. Omitting | |
| # here keeps CI from needing an org-level secret. | |
| # The Figma plugin ships from this repo (figma-plugin/). All three bugs in | |
| # the 2026-05-12 review rejection were of the "one CI command would have | |
| # caught it" variety — this job is that command. | |
| figma-plugin: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| defaults: | |
| run: | |
| working-directory: figma-plugin | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: npm | |
| cache-dependency-path: figma-plugin/package-lock.json | |
| - name: Install plugin dependencies | |
| run: npm ci | |
| # Type-check the main-thread source (src/code.ts) without emitting. | |
| - name: Typecheck plugin | |
| run: npx tsc --noEmit | |
| # ui.html is not compiled — a syntax error in its inline <script> only | |
| # surfaces at runtime inside Figma. Extract the script and parse it. | |
| - name: Syntax-check ui.html script | |
| run: | | |
| node -e ' | |
| const fs = require("fs"); | |
| const html = fs.readFileSync("ui.html", "utf8"); | |
| const blocks = [...html.matchAll(/<script>([\s\S]*?)<\/script>/g)]; | |
| if (blocks.length === 0) { console.error("No <script> block found in ui.html"); process.exit(1); } | |
| fs.writeFileSync("/tmp/ui-script.js", blocks.map(b => b[1]).join("\n;\n")); | |
| ' | |
| node --check /tmp/ui-script.js | |
| # The plugin UI iframe is a data: URL where localStorage ALWAYS throws | |
| # SecurityError, and one unguarded touch at load kills the whole script | |
| # (root cause of the 2026-05-12 rejection). Only the safe* wrapper | |
| # definitions may reference it. | |
| - name: Forbid bare localStorage in ui.html | |
| run: | | |
| BARE=$(grep -nE 'localStorage\s*\.' ui.html | grep -vE 'function safe(Get|Set|Remove)Item' || true) | |
| if [ -n "$BARE" ]; then | |
| echo "::error::Bare localStorage usage in ui.html — route it through the safe* wrappers (see figma-plugin/README.md):" | |
| echo "$BARE" | |
| exit 1 | |
| fi | |
| echo "OK: no bare localStorage usage outside safe* wrappers" |