mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-29 08:07:48 +09:00
The unsupported-browser warning now uses the Builder’s regular modal dialog. Users can acknowledge it and continue, while the small-window state remains a blocking alert. Closes #6331 ## Implementation - Render the existing browser guidance and links inside the design-system `Dialog`. - Set the dialog to modal and keep close, Escape, outside-click, and **Continue** dismissal behavior. - Keep unsupported-browser detection, preview-mode suppression, and Builder-ready timing unchanged. - Keep the small-window alert blocking and remove its now-unused dismissible-alert path. - Add a focused Storybook state and browser regression test. - Increase Main and Visual Regression workflow job timeouts from five to seven minutes after otherwise healthy jobs were cancelled during slow dependency setup. - Run failed E2E diagnostics and service teardown through the backend shell script so cleanup does not depend on pnpm remaining on `PATH` after cancellation. - Route `safe-to-deploy` label events through one reusable gate that starts both Main and Vercel workflows. - Remove revision-specific deployment and visual approval labels in one synchronization-only cleanup job. - Reduce expected skipped jobs from 6 to 1 on a normal branch update and from 17 to 3 on a visual approval label event. No migrations or compatibility changes are required. ## Todo - [x] Replace the unsupported-browser alert with a modal dialog. - [x] Preserve the small-window blocking alert. - [x] Add regression coverage. - [x] Give CI enough time for slower dependency setup. - [x] Keep E2E cleanup available after runner cancellation. - [x] Simplify label-triggered Main and Vercel workflows. - [x] Consolidate revision-specific approval cleanup. - [x] Review documentation impact: no product documentation changes are needed for this presentation-only warning update. - [x] Review fixture impact: no fixture inputs or generated fixture outputs are affected. ## Verification - [x] Focused browser regression test - [x] Builder typecheck - [x] Oxlint on changed files - [x] Oxfmt and `git diff --check` - [x] Workflow YAML parsing and backend shell syntax checks - [x] Workflow trigger, nesting, permissions, and reusable-job contract assertions - [x] Prettier checks for changed workflows - [ ] Storybook screenshot review was not run locally because explicit visual-test approval was not available. ## Known limitations - The visual state was not screenshot-tested locally. - The reduced `pull_request_target` fan-out takes effect after these workflow files reach the default branch, because GitHub loads those workflow definitions from that branch.
149 lines
5.8 KiB
YAML
149 lines
5.8 KiB
YAML
name: Visual regression
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, labeled]
|
|
pull_request_target:
|
|
types: [opened, synchronize, reopened, labeled]
|
|
|
|
concurrency:
|
|
# A synchronize action emits both pull_request and pull_request_target. Keep
|
|
# same-repository and fork runs separate.
|
|
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.number || github.sha }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
visual-regression:
|
|
# Same-repository PRs use the unprivileged event. Organization members'
|
|
# fork PRs are trusted; every other fork revision needs safe-to-deploy.
|
|
if: |
|
|
github.event_name == 'push' ||
|
|
(
|
|
github.event_name == 'pull_request' &&
|
|
github.event.pull_request.head.repo.full_name == github.repository &&
|
|
(
|
|
github.event.action != 'labeled' ||
|
|
github.event.label.name == 'visual-change-approved'
|
|
)
|
|
) ||
|
|
(
|
|
github.event_name == 'pull_request_target' &&
|
|
github.event.pull_request.head.repo.full_name != github.repository &&
|
|
(
|
|
(
|
|
contains(fromJSON('["OWNER", "MEMBER"]'), github.event.pull_request.author_association) &&
|
|
(
|
|
github.event.action != 'labeled' ||
|
|
github.event.label.name == 'visual-change-approved'
|
|
)
|
|
) ||
|
|
(
|
|
github.event.action == 'labeled' &&
|
|
(
|
|
github.event.label.name == 'safe-to-deploy' ||
|
|
(
|
|
github.event.label.name == 'visual-change-approved' &&
|
|
contains(github.event.pull_request.labels.*.name, 'safe-to-deploy')
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
timeout-minutes: 7
|
|
runs-on: ubuntu-latest
|
|
environment:
|
|
name: development
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
allow-unsafe-pr-checkout: ${{ github.event_name == 'pull_request_target' }}
|
|
|
|
- uses: ./.github/actions/submodules-checkout
|
|
with:
|
|
submodules-ssh-key: ${{ secrets.PRIVATE_GITHUB_DEPLOY_TOKEN }}
|
|
|
|
- uses: ./.github/actions/ci-setup
|
|
|
|
- name: Cache Playwright Chromium
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.cache/ms-playwright/chromium-*
|
|
~/.cache/ms-playwright/chromium_headless_shell-*
|
|
~/.cache/ms-playwright/ffmpeg-*
|
|
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
|
|
|
|
- name: Install pinned visual browser
|
|
run: |
|
|
pnpm --filter @webstudio-is/sdk-components-animation exec playwright install chromium
|
|
playwright_browser_path="$(pnpm --filter @webstudio-is/sdk-components-animation exec node -e 'console.log(require("playwright").chromium.executablePath())')"
|
|
test -x "$playwright_browser_path"
|
|
echo "WEBSTUDIO_BROWSER_PATH=$playwright_browser_path" >> "$GITHUB_ENV"
|
|
|
|
- name: Install visual comparison dependencies
|
|
run: sudo apt-get install --no-install-recommends -y tesseract-ocr
|
|
|
|
- name: Resolve visual baseline cache key
|
|
id: visual-cache
|
|
env:
|
|
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
|
|
run: |
|
|
echo "base=$(git merge-base HEAD "$BASE_REF")" >> "$GITHUB_OUTPUT"
|
|
echo "head=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
|
browser_version="$("$WEBSTUDIO_BROWSER_PATH" --version)"
|
|
runtime_hash="$(pnpm tsx scripts/visual-regression/runtime-hash.ts "$browser_version")"
|
|
echo "runtime=$runtime_hash" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Restore visual baseline screenshots
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: .visual-regression-cache/${{ steps.visual-cache.outputs.base }}-*
|
|
key: visual-baseline-v1-${{ runner.os }}-${{ steps.visual-cache.outputs.base }}-${{ steps.visual-cache.outputs.runtime }}
|
|
|
|
- name: Compare portable stories
|
|
env:
|
|
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
|
|
APPROVE_VISUAL_CHANGES: ${{ github.event_name == 'push' || (github.event.action == 'labeled' && github.event.label.name == 'visual-change-approved') }}
|
|
run: |
|
|
args=(--base "$BASE_REF")
|
|
if [[ "$APPROVE_VISUAL_CHANGES" == "true" ]]; then
|
|
args+=(--approve-visual-changes)
|
|
fi
|
|
pnpm visual-regression -- "${args[@]}"
|
|
|
|
- name: Validate visual comparison report
|
|
if: always()
|
|
run: |
|
|
node -e 'const report = require("./.visual-regression/report/report.json"); if (!Array.isArray(report.comparisons) || report.comparisons.length === 0) throw new Error("Visual comparison report has no story results.")'
|
|
|
|
- name: Upload visual comparison report
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: visual-regression-report
|
|
path: .visual-regression/report
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
|
|
- name: Cache visual screenshots for dependent revisions
|
|
if: always()
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: .visual-regression-cache/${{ steps.visual-cache.outputs.head }}-*
|
|
key: visual-baseline-v1-${{ runner.os }}-${{ steps.visual-cache.outputs.head }}-${{ steps.visual-cache.outputs.runtime }}
|
|
|
|
- name: Cache generated baseline screenshots
|
|
if: always()
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: .visual-regression-cache/${{ steps.visual-cache.outputs.base }}-*
|
|
key: visual-baseline-v1-${{ runner.os }}-${{ steps.visual-cache.outputs.base }}-${{ steps.visual-cache.outputs.runtime }}
|