Skip to content

5minutes_rule

5minutes_rule #1014

Workflow file for this run

# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch. Homepage/UI-only pushes are
# ignored here so the fast UI workflow can deploy them without waiting behind
# this full Worker/release/static validation stack. Mixed or risky pushes
# still run this workflow because paths-ignore only skips when every changed
# file matches the safe UI list.
push:
branches: ["main"]
paths-ignore:
- "index.html"
- "de/index.html"
- "css/pages/index.css"
- "css/base/**"
- "css/components/**"
- "js/pages/index/**"
- "assets/images/**"
- "assets/favicons/**"
- "fonts/**"
- "tests/smoke.spec.js"
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
inputs:
release_plan_base_ref:
description: "Git base ref/SHA for release-plan comparison. Defaults to HEAD^ for manual reruns."
required: false
default: "HEAD^"
release_plan_dependency_acknowledgement:
description: "Optional manual static deploy acknowledgement after release-plan Worker/schema dependencies are handled. Must equal I_CONFIRM_RELEASE_PLAN_DEPENDENCIES_HANDLED."
required: false
default: ""
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
release-compatibility:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Audit root dependencies
run: npm audit --audit-level=low
- name: Validate worker package dependencies
run: |
for worker in workers/auth workers/contact workers/ai; do
npm --prefix "$worker" ci
npm --prefix "$worker" ls --depth=0
done
npm run check:worker-dependency-audits
- name: Check toolchain pinning
run: npm run check:toolchain
- name: Run quality gate tests
run: npm run test:quality-gates
- name: Check committed secrets
run: npm run check:secrets
- name: Check DOM sink baseline
run: npm run check:dom-sinks
- name: Check auth route policy coverage
run: npm run check:route-policies
- name: Test operational readiness checks
run: npm run test:operational-readiness
- name: Check operational readiness docs
run: npm run check:operational-readiness
- name: Run live health check in skipped mode
run: npm run check:live-health
- name: Run live security header check in skipped mode
run: npm run check:live-security-headers
- name: Test live runtime canary skipped/safe mode
run: npm run test:live-canary
- name: Test release cutover evidence generator
run: npm run test:release-cutover-evidence
- name: Check targeted JavaScript syntax
run: npm run check:js
- name: Run release compatibility tests
run: npm run test:release-compat
- name: Run release planner tests
run: npm run test:release-plan
- name: Run static deploy safety tests
run: npm run test:static-deploy-safety
- name: Run Cloudflare deploy prerequisite tests
run: npm run test:cloudflare-prereqs
- name: Run Cloudflare resource model tests
run: npm run test:cloudflare-resource-model
- name: Run readiness dossier tests
run: npm run test:readiness-dossier
- name: Run rollback drill tests
run: npm run test:rollback-drill
- name: Run Release Candidate manifest tests
run: npm run test:release-rc
- name: Run Release Candidate matrix tests
run: npm run test:rc-check
- name: Check Worker body parser guardrails
run: npm run check:worker-body-parsers
- name: Check admin activity query shape
run: npm run check:admin-activity-query-shape
- name: Check data lifecycle guardrails
run: npm run check:data-lifecycle
- name: Run doc currentness tests
run: npm run test:doc-currentness
- name: Check documentation currentness
run: npm run check:doc-currentness
- name: Run asset version tests
run: npm run test:asset-version
- name: Validate release compatibility
run: npm run validate:release
- name: Validate Cloudflare deploy prerequisites
run: npm run validate:cloudflare-prereqs
- name: Validate asset version sources
run: npm run validate:asset-version
worker-validation:
needs: release-compatibility
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run worker route tests
run: npm run test:workers
# Single deploy job since we're just deploying
deploy:
needs: [release-compatibility, worker-validation]
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Check static deploy release-plan safety
id: static_safety
env:
STATIC_DEPLOY_EVENT: ${{ github.event_name }}
STATIC_DEPLOY_ACK: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_plan_dependency_acknowledgement || '' }}
STATIC_DEPLOY_BASE_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_plan_base_ref || github.event.before }}
STATIC_DEPLOY_HEAD_REF: ${{ github.sha }}
run: |
set -euo pipefail
npm run check:static-deploy-safety:github -- --base "${STATIC_DEPLOY_BASE_REF:-}" --head "${STATIC_DEPLOY_HEAD_REF:-}" --event-name "$STATIC_DEPLOY_EVENT" --acknowledgement "$STATIC_DEPLOY_ACK"
- name: Report skipped static deploy
if: steps.static_safety.outputs.static_deploy_skipped == 'true'
run: |
echo "Static deploy skipped because release plan requires non-static deploy steps first."
echo "Workers, D1 migrations, and processor services are not deployed by the GitHub Pages workflow."
- name: Setup Pages
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
uses: actions/configure-pages@v6
- name: Validate JS import paths
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: |
errors=0
grep -rn "from ['\"]\.\.*/.*['\"]" --include="*.js" --include="*.html" --exclude-dir=node_modules --exclude-dir=workers --exclude-dir=.wrangler . | while IFS= read -r line; do
file=$(echo "$line" | cut -d: -f1)
# Extract the path between quotes after "from"
import_path=$(echo "$line" | sed "s/.*from ['\"]//;s/['\"].*//" )
# Strip query params (?v=...)
clean_path=$(echo "$import_path" | sed 's/?.*//')
# Resolve relative to the importing file's directory
dir=$(dirname "$file")
resolved=$(cd "$dir" && realpath -m "$clean_path" 2>/dev/null || echo "$dir/$clean_path")
if [ ! -f "$resolved" ]; then
echo "ERROR: Broken import in $file"
echo " import '$import_path' -> $resolved (not found)"
echo "FAIL" >> /tmp/import_errors
fi
done
if [ -f /tmp/import_errors ]; then
count=$(wc -l < /tmp/import_errors)
rm /tmp/import_errors
echo ""
echo "FAILED: $count broken import path(s) found"
exit 1
fi
echo "All JS import paths validated successfully"
- name: Check target="_blank" links have rel="noopener"
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: |
violations=$(grep -rn 'target="_blank"' --include="*.html" --include="*.js" --exclude-dir=node_modules --exclude-dir=workers --exclude-dir=_site --exclude-dir=.wrangler --exclude="*.min.js" . | grep -v 'noopener' || true)
if [ -n "$violations" ]; then
echo "ERROR: Found target=\"_blank\" without rel=\"noopener noreferrer\":"
echo "$violations"
echo ""
echo "FAILED: Add rel=\"noopener noreferrer\" to all target=\"_blank\" links"
exit 1
fi
echo "All target=\"_blank\" links include rel=\"noopener\" — OK"
- name: Validate local CSS/JS references
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: |
# Check that CSS stylesheet and JS script references point to existing files
# (Complements the JS import path validator above)
grep -rn 'href="[^"]*\.css' --include="*.html" --exclude-dir=node_modules --exclude-dir=workers --exclude-dir=_site --exclude-dir=.wrangler . | while IFS= read -r line; do
file=$(echo "$line" | cut -d: -f1)
echo "$line" | grep -oE 'href="[^"]*\.css[^"]*"' | sed 's/href="//;s/"//' | while IFS= read -r ref; do
case "$ref" in http://*|https://*|//*) continue ;; esac
[ -z "$ref" ] && continue
clean=$(echo "$ref" | sed 's/[?#].*//')
dir=$(dirname "$file")
resolved=$(cd "$dir" && realpath -m "$clean" 2>/dev/null || echo "$dir/$clean")
if [ ! -f "$resolved" ]; then
echo "ERROR: Missing CSS in $file"
echo " href='$ref' -> $resolved (not found)"
echo "FAIL" >> /tmp/asset_errors
fi
done
done
grep -rn 'src="[^"]*\.js' --include="*.html" --exclude-dir=node_modules --exclude-dir=workers --exclude-dir=_site --exclude-dir=.wrangler . | while IFS= read -r line; do
file=$(echo "$line" | cut -d: -f1)
echo "$line" | grep -oE 'src="[^"]*\.js[^"]*"' | sed 's/src="//;s/"//' | while IFS= read -r ref; do
case "$ref" in http://*|https://*|//*) continue ;; esac
[ -z "$ref" ] && continue
clean=$(echo "$ref" | sed 's/[?#].*//')
dir=$(dirname "$file")
resolved=$(cd "$dir" && realpath -m "$clean" 2>/dev/null || echo "$dir/$clean")
if [ ! -f "$resolved" ]; then
echo "ERROR: Missing JS in $file"
echo " src='$ref' -> $resolved (not found)"
echo "FAIL" >> /tmp/asset_errors
fi
done
done
if [ -f /tmp/asset_errors ]; then
count=$(wc -l < /tmp/asset_errors)
rm /tmp/asset_errors
echo ""
echo "FAILED: $count broken CSS/JS reference(s) found"
exit 1
fi
echo "All local CSS/JS references validated successfully"
- name: Validate page metadata
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: |
errors=0
for f in index.html; do
[ ! -f "$f" ] && continue
if ! grep -q '<meta name="description"' "$f"; then
echo "ERROR: Missing meta description in $f"
errors=$((errors + 1))
fi
if ! grep -q '<link rel="canonical"' "$f"; then
echo "ERROR: Missing canonical link in $f"
errors=$((errors + 1))
fi
if ! grep -q 'og:title' "$f"; then
echo "ERROR: Missing og:title in $f"
errors=$((errors + 1))
fi
done
if [ $errors -gt 0 ]; then
echo "FAILED: $errors metadata error(s) found"
exit 1
fi
echo "Page metadata validated successfully"
- name: Install Playwright Chromium
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: npx playwright install --with-deps chromium
- name: Run smoke tests
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: npm run test:static
- name: Upload Playwright report
if: steps.static_safety.outputs.static_deploy_skipped != 'true' && failure()
uses: actions/upload-artifact@v6
with:
name: playwright-report
path: playwright-report/
retention-days: 7
- name: Prepare deployment
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
run: npm run build:static
- name: Upload artifact
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
uses: actions/upload-pages-artifact@v4
with:
path: '_site'
- name: Deploy to GitHub Pages
if: steps.static_safety.outputs.static_deploy_skipped != 'true'
id: deployment
uses: actions/deploy-pages@v5