Files
supabase/.github/workflows/docs-e2e.yml
Miranda Limonczenko d77e7a25dd ci(docs): decide which suites run in one step
The same boolean was repeated across eight step conditions. Compute pages,
global_elements, and any once, matching www.

Also gates the report upload on what actually ran rather than on the page
scope, which could have tried to upload directories that were never created.
2026-08-17 11:48:21 -07:00

315 lines
13 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Docs E2E Tests
# "Docs E2E" is a required status check on master, so this workflow must
# produce a check run on every PR — a `paths` trigger filter would leave
# non-docs PRs waiting on a check that never reports. Path scoping happens
# in the "Detect changed paths" step instead; when nothing docs-related
# changed, the remaining steps are skipped and the check reports green.
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
branches: ['master']
workflow_dispatch:
inputs:
base_url:
description: 'Base URL to test against'
required: false
default: 'https://supabase.com'
type: string
page_paths:
description: 'Comma-separated /docs/... paths to test (required for manual runs)'
required: false
default: ''
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
statuses: read
pull-requests: read
env:
CI: true
jobs:
e2e:
name: Docs E2E
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.draft == false
timeout-minutes: 30
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
# Runs before checkout — reads the PR file list from the API. `docs`
# mirrors the path scope this workflow used to have as a trigger filter;
# `docs_components` scopes the global element scan to the markup it reads;
# `docs_app` decides whether a Vercel docs preview exists to test against.
- name: Detect changed paths
id: changes
if: github.event_name == 'pull_request'
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
docs:
- 'apps/docs/content/guides/**/*.mdx'
- 'apps/docs/content/troubleshooting/**/*.mdx'
- 'apps/docs/content/_partials/**'
- 'e2e/docs/**'
- 'e2e/shared/**'
- 'pnpm-lock.yaml'
- '.github/workflows/docs-e2e.yml'
docs_components:
- 'apps/docs/app/**'
- 'apps/docs/components/**'
- 'apps/docs/features/ui/**'
- 'apps/docs/layouts/**'
- 'e2e/docs/**'
- 'e2e/shared/**'
- 'pnpm-lock.yaml'
- '.github/workflows/docs-e2e.yml'
docs_app:
- 'apps/docs/**'
- name: Decide which suites run
id: gate
env:
EVENT_NAME: ${{ github.event_name }}
DOCS: ${{ steps.changes.outputs.docs }}
DOCS_COMPONENTS: ${{ steps.changes.outputs.docs_components }}
run: |
set -euo pipefail
pages=false
global_elements=false
any=false
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
pages=true
global_elements=true
else
if [ "$DOCS" = "true" ]; then pages=true; fi
if [ "$DOCS_COMPONENTS" = "true" ]; then global_elements=true; fi
fi
if [ "$pages" = "true" ] || [ "$global_elements" = "true" ]; then any=true; fi
{
echo "pages=$pages"
echo "global_elements=$global_elements"
echo "any=$any"
} >> "$GITHUB_OUTPUT"
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
if: steps.gate.outputs.any == 'true'
with:
persist-credentials: false
# Need full history on PRs so we can diff against the base branch.
# Use string '0' — numeric 0 is falsy in GitHub Actions expressions.
fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
sparse-checkout: |
e2e/docs
e2e/shared
scripts
patches
apps/docs/content/guides
apps/docs/content/troubleshooting
apps/docs/content/_partials
apps/docs/scripts/federated-content/sources
- name: Use Node.js
if: steps.gate.outputs.any == 'true'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
# Map changed owned content (guides, troubleshooting, partials) to page
# URLs. Harness-only PRs resolve to skip=true and exit before Playwright.
# This scope covers the page suite only; the global element suite scans a
# fixed page list.
- name: Resolve docs E2E scope
id: scope
if: steps.gate.outputs.any == 'true'
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
PAGE_PATHS_INPUT: ${{ inputs.page_paths }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
if [ -z "$PAGE_PATHS_INPUT" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "paths=" >> "$GITHUB_OUTPUT"
echo "Manual run requires the page_paths input."
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
printf 'paths=%s\n' "$PAGE_PATHS_INPUT" >> "$GITHUB_OUTPUT"
exit 0
fi
git diff --name-only --diff-filter=ACMR "origin/$BASE_REF"...HEAD \
| node --experimental-strip-types e2e/docs/scripts/resolve-docs-scope.ts
- name: Skip Playwright (no in-scope pages)
if: steps.scope.outputs.skip == 'true'
run: echo "No in-scope docs pages changed; skipping the page suite."
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
if: steps.scope.outputs.skip == 'false' || steps.gate.outputs.global_elements == 'true'
name: Install pnpm
with:
run_install: false
- name: Enable pnpm store cache
if: steps.scope.outputs.skip == 'false' || steps.gate.outputs.global_elements == 'true'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
# Vercel skips the docs preview when a PR only changes the harness
# (e2e/docs, workflow), so wait for a preview only when apps/docs changed.
#
# Vercel's GitHub App stopped writing GitHub Deployment objects on
# 2026-02-17 (broken app auth), so vercel/wait-for-deployment-action
# times out polling that API even though the preview builds fine.
# Poll the "Vercel – docs" commit status instead — Vercel keeps posting
# those — then resolve the deployment it points to via Vercel's own API
# to get the actual preview URL. See scripts/waitForVercelPreview.js.
# A Vercel failure or timeout is not the author's problem, and the required
# "Vercel – docs" check already reports it. Resolve no URL and skip below.
- name: Wait for Vercel docs preview
if: (steps.scope.outputs.skip == 'false' || steps.gate.outputs.global_elements == 'true') && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && steps.changes.outputs.docs_app == 'true'
id: deployment
continue-on-error: true
run: node scripts/waitForVercelPreview.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
VERCEL_STATUS_CONTEXT: 'Vercel – docs'
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
# The two suites disagree on one case: with no preview the page suite has
# nothing to test, while the global element suite can fall back to production.
- name: Resolve base URL
if: steps.scope.outputs.skip == 'false' || steps.gate.outputs.global_elements == 'true'
id: base-url
env:
EVENT_NAME: ${{ github.event_name }}
BASE_URL_INPUT: ${{ inputs.base_url }}
DEPLOYMENT_URL: ${{ steps.deployment.outputs.deployment-url }}
PAGE_PATHS: ${{ steps.scope.outputs.paths }}
PAGES_SKIP: ${{ steps.scope.outputs.skip }}
DOCS_APP_CHANGED: ${{ steps.changes.outputs.docs_app }}
RUN_GLOBAL: ${{ steps.gate.outputs.global_elements }}
run: |
set -euo pipefail
PAGES=false
if [ "$PAGES_SKIP" = "false" ]; then
PAGES=true
fi
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
printf 'url=%s\n' "$BASE_URL_INPUT" >> "$GITHUB_OUTPUT"
# Non-production targets are previews, which may need the bypass.
if [ "$BASE_URL_INPUT" = "https://supabase.com" ]; then
echo "use_bypass=false" >> "$GITHUB_OUTPUT"
else
echo "use_bypass=true" >> "$GITHUB_OUTPUT"
fi
echo "should_test=$PAGES" >> "$GITHUB_OUTPUT"
echo "global_should_test=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ -n "$DEPLOYMENT_URL" ]; then
printf 'url=%s\n' "$DEPLOYMENT_URL" >> "$GITHUB_OUTPUT"
echo "use_bypass=true" >> "$GITHUB_OUTPUT"
echo "should_test=$PAGES" >> "$GITHUB_OUTPUT"
echo "global_should_test=$RUN_GLOBAL" >> "$GITHUB_OUTPUT"
exit 0
fi
# A change that ships no markup leaves production serving the same
# global elements this would test.
GLOBAL=false
if [ "$RUN_GLOBAL" = "true" ] && [ "$DOCS_APP_CHANGED" != "true" ]; then
GLOBAL=true
fi
if [ "$GLOBAL" = "true" ]; then
echo "url=https://supabase.com" >> "$GITHUB_OUTPUT"
else
echo "url=" >> "$GITHUB_OUTPUT"
fi
echo "use_bypass=false" >> "$GITHUB_OUTPUT"
# Production is not a substitute for the page suite: it lacks pages
# this pull request adds, so testing it fails a required check for a
# valid change.
echo "should_test=false" >> "$GITHUB_OUTPUT"
echo "global_should_test=$GLOBAL" >> "$GITHUB_OUTPUT"
if [ "$RUN_GLOBAL" = "true" ] && [ "$GLOBAL" != "true" ]; then
echo "::warning::No Vercel docs preview URL for this pull request, so nothing is serving the global elements it changes. Skipping that scan rather than scanning production, which still has the old markup."
fi
if [ "$PAGES" != "true" ]; then
exit 0
fi
echo "::warning::No Vercel docs preview URL for this pull request, so there is nothing serving its content to test. Skipping Playwright rather than testing production, which does not have pages this pull request adds."
{
echo "### Docs E2E skipped: no preview to test against"
echo
echo "Nothing is serving this pull request's content, and production is not a"
echo "substitute — pages it adds do not exist there yet."
echo
echo "Fork pull requests reach this path because they run without repository"
echo "secrets. A maintainer can run the suite against the preview manually:"
echo
echo '```'
echo "gh workflow run docs-e2e.yml \\"
echo " -f base_url=<preview-url> \\"
echo " -f page_paths=$PAGE_PATHS"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Install dependencies
if: steps.base-url.outputs.should_test == 'true' || steps.base-url.outputs.global_should_test == 'true'
run: pnpm install --frozen-lockfile --filter=e2e-docs...
- name: Install Playwright Chromium
if: steps.base-url.outputs.should_test == 'true' || steps.base-url.outputs.global_should_test == 'true'
run: pnpm -C e2e/docs exec playwright install chromium --with-deps --only-shell
- name: Run docs E2E
if: steps.base-url.outputs.should_test == 'true'
working-directory: e2e/docs
run: pnpm run e2e:docs
env:
PLAYWRIGHT_BASE_URL: ${{ steps.base-url.outputs.url }}
DOCS_E2E_PAGE_PATHS: ${{ steps.scope.outputs.paths }}
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ steps.base-url.outputs.use_bypass == 'true' && secrets.VERCEL_AUTOMATION_BYPASS_DOCS || '' }}
- name: Run docs global elements E2E
if: steps.base-url.outputs.global_should_test == 'true'
working-directory: e2e/docs
run: pnpm run e2e:docs:global-elements
env:
PLAYWRIGHT_BASE_URL: ${{ steps.base-url.outputs.url }}
VERCEL_AUTOMATION_BYPASS_SECRET: ${{ steps.base-url.outputs.use_bypass == 'true' && secrets.VERCEL_AUTOMATION_BYPASS_DOCS || '' }}
- name: Upload Playwright report
if: failure() && (steps.base-url.outputs.should_test == 'true' || steps.base-url.outputs.global_should_test == 'true')
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: docs-playwright-report
path: |
e2e/docs/playwright-report/
e2e/docs/playwright-report-global-elements/
e2e/docs/test-results/
retention-days: 7