Skip to content

learn: resize the four payoff tasks so the better algorithm actually … #45

learn: resize the four payoff tasks so the better algorithm actually …

learn: resize the four payoff tasks so the better algorithm actually … #45

Workflow file for this run

name: CI
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: yarn
- name: Install packages
run: yarn install --frozen-lockfile
- name: Build
run: yarn build
- name: Check the static files survived the build
# CNAME keeps the custom domain, 404.html is the SPA shell that serves
# unprerendered deep paths, and api/ is the gpu.js API reference served
# at https://gpu.rocks/api/
run: |
for file in index.html CNAME 404.html api/index.html manifest.json service-worker.js; do
if [ ! -f "dist/$file" ]; then
echo "::error::dist/$file is missing from the build"
exit 1
fi
echo "ok dist/$file"
done
- name: Serve the build
run: |
yarn preview --port 4173 &
for _ in $(seq 1 30); do
curl -sf http://localhost:4173/ >/dev/null && exit 0
sleep 1
done
echo "::error::preview server never came up"
exit 1
- name: Smoke test every route in a browser
run: yarn test:smoke http://localhost:4173
- name: Keep the tested build for the deploy job
uses: actions/upload-artifact@v4
with:
name: dist
path: dist
retention-days: 7
deploy:
# publishes the exact build the smoke test passed against, rather than
# rebuilding, so what ships is what was verified
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
concurrency:
group: deploy-gh-pages
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Publish to the gh-pages branch
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
# gh-pages publishes from its own clone, which does not inherit the
# credentials actions/checkout writes into this working copy
npx --yes gh-pages@6 --dist dist --dotfiles \
--repo "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
--message "Deploy ${GITHUB_SHA::7}: $(git log -1 --pretty=%s)"
- name: Wait for the deploy to go live
# compares the served page against what was just published rather than
# looking for an asset name, so an HTML-only change is verified too
run: |
for _ in $(seq 1 30); do
if curl -sf "https://gpu.rocks/?cachebust=$RANDOM" -o /tmp/live.html \
&& diff -q dist/index.html /tmp/live.html >/dev/null; then
curl -sf -o /dev/null "https://gpu.rocks/api/" \
&& echo "live, and /api/ is still served" && exit 0
echo "::error::the site is live but /api/ is not being served"
exit 1
fi
sleep 10
done
echo "::error::gpu.rocks is not serving the page that was just published"
diff dist/index.html /tmp/live.html || true
exit 1
- name: Purge the Cloudflare cache (HTML only)
# HTML must be purged: it names the new content-hashed chunks, and a
# stale page would ask for assets this deploy replaced. The assets
# themselves must NOT be — their names change whenever their bytes do,
# so they are immutable, and purge_everything just forces every visitor
# to re-pull ~2 MB from GitHub Pages cold. Doing that on back-to-back
# deploys is what turned this site into 8-17 s origin fetches and edge
# 520s. Cloudflare takes at most 30 URLs per call, hence the batching.
env:
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
if [ -z "$CLOUDFLARE_ZONE_ID" ] || [ -z "$CLOUDFLARE_API_TOKEN" ]; then
echo "Cloudflare secrets not configured — skipping purge (stale HTML expires within ~10 min)"
exit 0
fi
# Every prerendered page, in both the slashless and trailing-slash
# forms GitHub Pages serves — Cloudflare keys the cache on the exact
# URL, so both need purging. Only index.html files name real URLs:
# prerender also writes a <route>.html twin so GitHub Pages serves the
# slashless path without a redirect, but nobody requests that name —
# purging it would be 95 wasted origin fetches. dist/api is the
# vendored gpu.js reference: it changes only on a version bump, is not
# coupled to our hashed chunks, and can expire on its own max-age.
find dist -name 'index.html' -not -path 'dist/api/*' | while read -r file; do
rel=${file#dist/}
if [ "$rel" = "index.html" ]; then
echo "https://gpu.rocks/"
else
dir=${rel%/index.html}
echo "https://gpu.rocks/$dir"
echo "https://gpu.rocks/$dir/"
fi
done | sort -u > /tmp/purge-urls.txt
printf '%s\n' \
"https://gpu.rocks/404.html" \
"https://gpu.rocks/sitemap.xml" \
"https://gpu.rocks/robots.txt" >> /tmp/purge-urls.txt
echo "purging $(wc -l < /tmp/purge-urls.txt) HTML URLs (assets left warm)"
split -l 30 /tmp/purge-urls.txt /tmp/purge-batch-
failed=0
for batch in /tmp/purge-batch-*; do
payload=$(jq -R -s -c 'split("\n") | map(select(length > 0)) | {files: .}' < "$batch")
response=$(curl -s -X POST \
"https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "$payload")
if echo "$response" | grep -q '"success": *true'; then
echo " purged $(wc -l < "$batch") URLs"
else
echo "::error::Cloudflare purge failed: $response"
failed=1
fi
done
[ "$failed" -eq 0 ] || exit 1
echo "Cloudflare HTML cache purged"