Align SSR #1897
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: Semiotic | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| # Run freshness, size, and packed-consumer gates independently from coverage | |
| # and the broader contract suite so regressions appear within the first CI | |
| # job instead of after the long-running test path completes. | |
| fast-contracts: | |
| name: Fast feedback gates (Node 22) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout source code | |
| uses: actions/checkout@v7 | |
| - name: Use Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.x | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Run freshness and size gates | |
| run: npm run check:fast | |
| # Full gate suite once on the primary LTS. Cross-Node compatibility is | |
| # covered by the smoke matrix below — re-running docs/AI/size checks on | |
| # every Node version was pure wall-clock cost with no signal. | |
| testing-full: | |
| name: Full checks (Node 22) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout source code | |
| uses: actions/checkout@v7 | |
| - name: Use Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.x | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Build library | |
| run: npm run dist | |
| - name: Build MCP server | |
| run: npm run build:mcp | |
| - name: Check Cloud Run deployment manifest | |
| run: npm run check:cloud-run-manifest | |
| - name: Check repository-built nightly Cloud Run deployment | |
| run: npm run check:cloud-run-nightly | |
| - name: Test nightly deployment config and hosted smoke runner | |
| run: | | |
| npm run test:cloud-run-nightly | |
| npm run test:smoke-hosted-mcp | |
| - name: Run tests with coverage | |
| run: npm run test:coverage | |
| - name: Run type check | |
| run: npm run typescript | |
| - name: Run test type check | |
| run: npm run typescript:tests | |
| - name: Run MCP type check | |
| run: npm run typescript:mcp | |
| - name: Check chart spec registry round-trip | |
| run: npm run check:chart-specs | |
| - name: Check capability matrix freshness | |
| run: npm run check:capabilities | |
| - name: Check visual baseline capability coverage | |
| run: npm run check:visual-baseline-capabilities | |
| - name: Check docs coverage and per-page quality | |
| run: npm run check:docs-coverage | |
| - name: Check docs example manifest and source integrity | |
| run: npm run check:docs-example-integrity | |
| - name: Check prop-table drift vs chartSpecs | |
| run: npm run check:docs-prop-tables | |
| - name: Check playground control drift vs chartSpecs | |
| run: npm run check:docs-playground-controls | |
| - name: Check llms.txt freshness | |
| run: npm run check:llms | |
| - name: Check blog metadata registry freshness | |
| run: npm run check:blog-entries | |
| - name: Check CLAUDE.md component coverage | |
| run: npm run check:claude-md-coverage | |
| - name: Check context7.json freshness | |
| run: npm run check:context7 | |
| - name: Check MCP Registry submission cross-references | |
| run: npm run check:mcp-registry | |
| - name: Check AI/MCP surface parity | |
| run: npm run check:surface | |
| - name: Check generated AI surface manifest freshness | |
| run: npm run check:ai-surface | |
| - name: Check SSR alignment | |
| run: npm run check:ssr | |
| - name: Check public API surface (no breaking changes) | |
| run: npm run check:api-surface | |
| - name: Check HOC JSDoc coverage (≥2 @example blocks per HOC) | |
| run: npm run check:jsdoc-coverage | |
| - name: Check ai/examples.md coverage + drift | |
| run: npm run check:ai-examples-coverage | |
| - name: Build docs site and smoke-check generated routes | |
| run: npm run check:website-build | |
| - name: Pack smoke test (verify all entry points resolve) | |
| run: npm run check:pack | |
| - name: Verify TypeScript declarations | |
| run: | | |
| for f in dist/semiotic.d.ts dist/semiotic-xy.d.ts dist/semiotic-ordinal.d.ts dist/semiotic-network.d.ts dist/semiotic-geo.d.ts dist/semiotic-realtime.d.ts dist/semiotic-ai.d.ts dist/semiotic-data.d.ts dist/semiotic-server.d.ts dist/semiotic-themes.d.ts; do | |
| if [ ! -f "$f" ]; then | |
| echo "MISSING: $f" | |
| exit 1 | |
| fi | |
| done | |
| echo "All declaration files present" | |
| - name: Validate canvas stub completeness | |
| run: | | |
| # Extract canvas method names used in production renderers | |
| USED=$(grep -roh 'ctx\.\([a-zA-Z]*\)' src/components/stream/renderers/ | \ | |
| sed 's/ctx\.//' | sort -u | grep -v '^$') | |
| # Extract stubbed method names from setupTests.ts | |
| STUBBED=$(grep -oP '"\K[a-zA-Z]+(?=")' src/setupTests.ts | sort -u) | |
| # Find methods used in renderers but not stubbed | |
| MISSING="" | |
| for method in $USED; do | |
| if ! echo "$STUBBED" | grep -qx "$method"; then | |
| # Skip property assignments (not methods) | |
| case "$method" in | |
| fillStyle|strokeStyle|lineWidth|lineCap|lineJoin|globalAlpha|\ | |
| globalCompositeOperation|font|textAlign|textBaseline|shadowColor|\ | |
| shadowBlur|shadowOffsetX|shadowOffsetY|lineDashOffset|miterLimit|\ | |
| canvas|imageSmoothingEnabled|direction|filter|letterSpacing|\ | |
| fontKerning|fontStretch|fontVariantCaps|textRendering|wordSpacing) | |
| ;; # property, not a method — skip | |
| *) | |
| MISSING="$MISSING $method" | |
| ;; | |
| esac | |
| fi | |
| done | |
| if [ -n "$MISSING" ]; then | |
| echo "Canvas methods used in renderers but not stubbed in setupTests.ts:" | |
| echo "$MISSING" | |
| echo "" | |
| echo "Add these to the canvasMethods array in src/setupTests.ts" | |
| exit 1 | |
| fi | |
| echo "All canvas methods are stubbed" | |
| # Smoke matrix: install, build, typecheck, and unit tests on each | |
| # supported Node. Catches runtime/engine issues without re-running the | |
| # full docs/AI contract suite four times. | |
| testing-matrix: | |
| name: Smoke (Node ${{ matrix.node-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # 22.x is covered by testing-full; matrix is the other engines only. | |
| node-version: ["20.19.0", "24.x", "26.x"] | |
| steps: | |
| - name: Checkout source code | |
| uses: actions/checkout@v7 | |
| - name: Use Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Build library | |
| run: npm run dist | |
| - name: Build MCP server | |
| run: npm run build:mcp | |
| - name: Run tests | |
| run: npx vitest run | |
| - name: Run type check | |
| run: npm run typescript | |
| - name: Run MCP type check | |
| run: npm run typescript:mcp | |
| testing: | |
| name: testing | |
| runs-on: ubuntu-latest | |
| needs: [fast-contracts, testing-full, testing-matrix] | |
| if: always() | |
| steps: | |
| - name: Aggregate matrix result | |
| run: | | |
| if [[ "${{ needs['fast-contracts'].result }}" != "success" || "${{ needs['testing-full'].result }}" != "success" || "${{ needs['testing-matrix'].result }}" != "success" ]]; then | |
| { | |
| echo "## Testing failed" | |
| echo "" | |
| echo "Fast feedback gates result: \`${{ needs['fast-contracts'].result }}\`" | |
| echo "Full checks result: \`${{ needs['testing-full'].result }}\`" | |
| echo "Smoke matrix result: \`${{ needs['testing-matrix'].result }}\`" | |
| echo "" | |
| echo "Open the failed job above for the concrete error." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| echo "::error title=Testing failed::Fast feedback gates: ${{ needs['fast-contracts'].result }}; Full checks: ${{ needs['testing-full'].result }}; Smoke matrix: ${{ needs['testing-matrix'].result }}" | |
| exit 1 | |
| fi | |
| bench: | |
| name: Performance regression | |
| runs-on: ubuntu-latest | |
| # PR-only: captures main's bench numbers in the same job on the same | |
| # hardware, then compares the PR's results against them. Avoids the | |
| # cross-architecture noise that plagues a committed-baseline approach | |
| # (M-series Mac local capture vs. x86 Linux CI runner produces uniform | |
| # 60–90% "slowdowns" across every benchmark). On `push: main` there's | |
| # nothing to compare main against, so the job is skipped. | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout source code | |
| uses: actions/checkout@v7 | |
| with: | |
| # `bench:pr-vs-main` worktrees `origin/main` to capture a | |
| # CI-native baseline. Default checkout is shallow to the | |
| # triggering ref only; full history makes worktree-add reliable | |
| # without needing extra fetch plumbing. | |
| fetch-depth: 0 | |
| - name: Use Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.x | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Compare PR vs main on identical hardware | |
| run: npm run bench:pr-vs-main | |
| e2e: | |
| runs-on: ubuntu-latest | |
| needs: testing | |
| # Pin the rendering environment. `ubuntu-latest` is a moving image whose | |
| # font/freetype stack drifts from committed baselines, causing sub-pixel | |
| # text-AA diffs across the whole visual suite. Running inside the same | |
| # Playwright image the baselines are generated in (scripts/run-playwright- | |
| # linux-bootstrap.mjs) makes CI renders match the baselines exactly and | |
| # survives future `ubuntu-latest` bumps. | |
| container: mcr.microsoft.com/playwright:v1.61.1-noble | |
| # In a container job GitHub sets $HOME=/github/home (owned by the image's | |
| # pwuser) while steps run as root. Firefox refuses to launch unless $HOME | |
| # is owned by the current user, so point it at root's own home. Chromium | |
| # and WebKit are unaffected; without this only Firefox fails to start. | |
| env: | |
| HOME: /root | |
| steps: | |
| - name: Checkout source code | |
| uses: actions/checkout@v7 | |
| - name: Use Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.x | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Install Playwright browsers | |
| run: npx playwright install --with-deps chromium firefox webkit | |
| - name: Build library | |
| run: npm run dist | |
| - name: Run E2E tests | |
| # CI is the visual regression gate: a missing baseline is an | |
| # unreviewed visual contract, not a reason to create one during a | |
| # pull request. Maintainers can use `test:visual:bootstrap` locally | |
| # to generate a proposed baseline, then commit it with review. | |
| run: npx playwright test --update-snapshots=none | |
| - name: Capture docs example source and local Chromium contract evidence | |
| run: npm run test:examples:source | |
| # The contract test validates this complete local-only capture before it | |
| # writes the JSON. Retain it for review, but do not compare it against a | |
| # committed baseline: capture time and runner geometry are per-run facts. | |
| # It records reduced-motion and browser-emitted visibility behavior without | |
| # claiming policy-specific animation, performance, or cross-browser parity. | |
| - name: Upload local docs example contract evidence | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docs-example-local-contract-evidence | |
| path: test-results/**/docs-example-local-contract-evidence.v2.json | |
| if-no-files-found: warn | |
| retention-days: 14 | |
| - name: Upload snapshot baselines | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-snapshots | |
| path: integration-tests/*.spec.ts-snapshots/ | |
| retention-days: 30 | |
| - name: Upload test artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: | | |
| test-results/ | |
| integration-tests/test-results/ | |
| retention-days: 7 |