Skip to content

docs: clarify reload password CLI/HTTP threat model (#2477) (#2516) #263

docs: clarify reload password CLI/HTTP threat model (#2477) (#2516)

docs: clarify reload password CLI/HTTP threat model (#2477) (#2516) #263

Workflow file for this run

name: Deploy static sites
on:
push:
branches: [develop]
paths:
- 'web/**'
- '.github/workflows/web-deploy.yml'
pull_request:
branches: [develop]
paths:
- 'web/**'
- '.github/workflows/web-deploy.yml'
repository_dispatch:
types: [registry-updated]
jobs:
deploy:
name: Deploy ${{ matrix.site }}
runs-on: ubuntu-latest
concurrency:
group: web-deploy-${{ matrix.site }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
deployments: write
pull-requests: write
strategy:
fail-fast: false
matrix:
site: ${{ github.event_name == 'repository_dispatch' && fromJSON('["packages"]') || fromJSON('["landing", "blog", "guides", "api", "packages"]') }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up pnpm
uses: pnpm/action-setup@v5
with:
version: 10.23.0
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Install dependencies
working-directory: web
run: pnpm install --frozen-lockfile
- name: Build ${{ matrix.site }}
working-directory: web
run: pnpm --filter @wheels-dev/site-${{ matrix.site }} build
- name: Deploy to Cloudflare Pages
working-directory: web
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# Routed via env to keep untrusted branch names out of the command string.
DEPLOY_BRANCH: ${{ github.head_ref || github.ref_name }}
SITE: ${{ matrix.site }}
run: |
pnpm dlx wrangler@4.78.0 pages deploy "sites/${SITE}/dist" \
--project-name="wheels-${SITE}" \
--branch="${DEPLOY_BRANCH}" \
--commit-hash="${GITHUB_SHA}"
visual-regression:
name: Visual regression
# Hard gate for non-content changes (layout/styling/structure). For
# content-only pushes (markdown under web/content/blog/posts/), the
# job auto-regenerates the affected baseline, commits it back to
# develop with [skip ci], and exits success — so adding a new blog
# article doesn't trip a stale-baseline failure.
#
# If this job fails on a non-content PR and the change is intentional,
# download the `visual-regression-diffs` artifact and copy each
# *.actual.png over the matching web/tests/visual-baselines/*.png,
# commit, and push.
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up pnpm
uses: pnpm/action-setup@v5
with:
version: 10.23.0
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Install dependencies
working-directory: web
run: pnpm install --frozen-lockfile
- name: Cache Playwright browsers
uses: actions/cache@v5
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('web/pnpm-lock.yaml') }}
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: web
run: pnpm exec playwright install --with-deps chromium
- name: Build all sites
working-directory: web
run: pnpm build
- name: Run visual regression
id: regress
continue-on-error: true
working-directory: web
run: pnpm visual:test
- name: Detect content-only push
id: scope
if: steps.regress.outcome == 'failure' && github.event_name == 'push'
run: |
set -euo pipefail
# `changed` comes from git itself (file paths under version
# control), not from user-controlled event metadata, so it's
# safe to grep over without further sanitization.
changed=$(git diff --name-only HEAD~1..HEAD)
non_content=$(echo "$changed" | grep -v -E '^web/content/blog/posts/' || true)
if [ -z "$non_content" ]; then
echo "content_only=true" >> "$GITHUB_OUTPUT"
echo "All changes under web/content/blog/posts/ — will auto-refresh baseline."
else
echo "content_only=false" >> "$GITHUB_OUTPUT"
echo "Non-content files changed; treating regression failure as real."
echo "$non_content"
fi
- name: Auto-refresh blog baseline
if: steps.regress.outcome == 'failure' && steps.scope.outputs.content_only == 'true'
working-directory: web
run: node scripts/visual-regression.mjs --update --site blog
- name: Commit and push refreshed baseline
if: steps.regress.outcome == 'failure' && steps.scope.outputs.content_only == 'true'
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add web/tests/visual-baselines/blog.png
if git diff --cached --quiet; then
echo "Baseline did not actually change after refresh — exiting clean."
exit 0
fi
# [skip ci] keeps this push from re-triggering the deploy
# workflow and creating a publish loop.
git commit -m "chore(docs): auto-refresh blog visual baseline after content change [skip ci]"
git push origin HEAD:develop
- name: Fail job on real regression
if: steps.regress.outcome == 'failure' && steps.scope.outputs.content_only != 'true'
run: |
echo "Visual regression failed on changes that include non-content files."
echo "Download the visual-regression-diffs artifact and update baselines as needed."
exit 1
- name: Upload diffs on failure
if: steps.regress.outcome == 'failure'
uses: actions/upload-artifact@v7
with:
name: visual-regression-diffs
path: |
web/tests/visual-diffs/*.png
web/tests/visual-baselines/*.png
if-no-files-found: ignore
retention-days: 14
# Note: the packages-index baseline auto-refresh that fires on
# repository_dispatch: registry-updated lives in its own workflow file
# at .github/workflows/refresh-packages-baseline.yml so it doesn't
# appear as a "skipped" check on regular PRs.