ci: release #10
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: Visual Regression | |
| # Untrusted measure job. Runs the visual-regression Playwright suite against the | |
| # committed baselines and, when snapshots differ, produces an artifact with the | |
| # candidate (accepted-state) baselines plus diff images. This workflow runs with | |
| # a read-only token and never comments or pushes. The companion workflows handle | |
| # that with elevated permissions, isolated from PR-authored code: | |
| # - "Visual Regression — Report" (workflow_run) posts the sticky comment. | |
| # - "Visual Regression — Apply" (schedule) commits accepted baselines when a | |
| # maintainer reacts 👍 to the sticky comment. | |
| # | |
| # Baselines are Linux/chromium PNGs regenerated here on the CI runner, so they | |
| # always match the environment they're diffed in. Contributors do NOT commit | |
| # baselines by hand (macOS PNGs would never match); the 👍-accept flow commits | |
| # the correct Linux baselines for them. | |
| on: | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - "packages/admin/**" | |
| - "packages/core/**" | |
| - "e2e/**" | |
| - "playwright.config.ts" | |
| - ".github/workflows/visual.yml" | |
| - "pnpm-lock.yaml" | |
| - "package.json" | |
| permissions: | |
| contents: read | |
| jobs: | |
| measure: | |
| name: Measure | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm run --filter emdash... build | |
| - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| id: playwright-cache | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-${{ hashFiles('pnpm-lock.yaml') }} | |
| - run: pnpm exec playwright install --with-deps chromium | |
| if: steps.playwright-cache.outputs.cache-hit != 'true' | |
| - name: Run visual regression | |
| id: run | |
| env: | |
| EMDASH_VISUAL: "1" | |
| run: | | |
| set +e | |
| pnpm exec playwright test visual-regression --reporter=line | |
| code=$? | |
| set -e | |
| echo "exit=$code" >> "$GITHUB_OUTPUT" | |
| echo "Playwright exited with $code" | |
| # Always record PR metadata so the Report workflow can upsert *or* clear | |
| # the sticky comment. Candidate baselines + diff images are added only | |
| # when the suite actually diffed (exit != 0 AND regeneration produced | |
| # snapshots) -- an infra failure exits non-zero but yields no candidates, | |
| # so we don't mislabel it as a visual change. | |
| - name: Collect result | |
| id: collect | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| RUN_EXIT: ${{ steps.run.outputs.exit }} | |
| run: | | |
| mkdir -p visual-out | |
| echo "$PR_NUMBER" > visual-out/pr-number | |
| echo "$HEAD_SHA" > visual-out/head-sha | |
| if [ "$RUN_EXIT" = "0" ]; then | |
| echo "false" > visual-out/has-drift | |
| echo "has_drift=false" >> "$GITHUB_OUTPUT" | |
| echo "No visual drift." | |
| exit 0 | |
| fi | |
| # Copy the diff/actual/expected triptychs Playwright writes on failure. | |
| mkdir -p visual-out/diff | |
| if [ -d test-results ]; then | |
| find test-results -type f \ | |
| \( -name '*-diff.png' -o -name '*-actual.png' -o -name '*-expected.png' \) \ | |
| -exec cp {} visual-out/diff/ \; | |
| fi | |
| # Regenerate the accepted-state baselines (the candidates the Apply | |
| # workflow will commit verbatim on 👍). These are Linux/chromium PNGs. | |
| pnpm exec playwright test visual-regression --update-snapshots --reporter=line || true | |
| mkdir -p visual-out/candidates | |
| if [ -d e2e/tests/visual-regression.spec.ts-snapshots ]; then | |
| cp -r e2e/tests/visual-regression.spec.ts-snapshots/. visual-out/candidates/ | |
| fi | |
| if [ -z "$(ls -A visual-out/candidates 2>/dev/null)" ]; then | |
| # Non-zero exit but nothing regenerated -> infra failure, not drift. | |
| echo "false" > visual-out/has-drift | |
| echo "has_drift=false" >> "$GITHUB_OUTPUT" | |
| echo "::warning::Visual suite failed without producing candidate baselines (likely an infra failure, not a UI diff)." | |
| else | |
| echo "true" > visual-out/has-drift | |
| echo "has_drift=true" >> "$GITHUB_OUTPUT" | |
| echo "Visual drift detected — candidates and diff images staged for the Report workflow." | |
| fi | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: visual-regression | |
| path: visual-out/ | |
| retention-days: 7 | |
| if-no-files-found: error | |
| # Make the check red so a UI PR gets a visible signal. This does not, by | |
| # itself, block merge -- keep it out of required checks until the false- | |
| # diff rate is near zero, then promote it. | |
| - name: Fail on drift | |
| if: steps.collect.outputs.has_drift == 'true' | |
| run: | | |
| echo "::error::Visual snapshots changed. Review the diff in the sticky comment; a maintainer 👍 on that comment accepts the new baselines." | |
| exit 1 |