From claude-resources
Sets up Cloudflare Pages deployments with GitHub Actions workflows for static sites, including production deploys, PR previews, named previews, wrangler config, and retry logic.
npx claudepluginhub takazudo/claude-resourcesThis skill uses the workspace's default tool permissions.
Set up Cloudflare Pages deployment with GitHub Actions workflows for static sites. Supports production deploys, PR preview deploys, and named preview branches.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Share bugs, ideas, or general feedback.
Set up Cloudflare Pages deployment with GitHub Actions workflows for static sites. Supports production deploys, PR preview deploys, and named preview branches.
Identify the project's build setup:
cat package.json
ls .github/workflows/ 2>/dev/null
cat wrangler.toml 2>/dev/null
Determine: package manager (pnpm/npm/yarn), build command, output directory (dist/, build/, out/), base path (root / or subpath like /pj/project-name/).
--project-name)/ or specific subpath# Cloudflare Pages project configuration
compatibility_date = "2024-12-01"
pnpm add -D wrangler # or npm
For pnpm: add esbuild and workerd to pnpm.onlyBuiltDependencies in package.json.
If the site has a base path (e.g., /pj/project-name/), create public/_redirects:
/ /pj/project-name/ 302
Most static site generators (Astro, Next.js, etc.) copy public/ to output, eliminating CI-time redirect generation.
permissions blocks (least privilege)${{ }} values via env: blocks, never inline in github-script JavaScript (prevents script injection)"${GITHUB_SHA}"npm install -g wrangler@4 (or pnpm exec wrangler when node_modules available)timeout-minutes to all jobs (build: 15, deploy: 20, notify: 5)curl -sSf --max-time 10 for external HTTP callsCloudflare Pages API occasionally returns transient errors (504 Gateway Timeout on /upload-token). Wrap all wrangler pages deploy commands in a bash retry loop:
- name: Deploy to Cloudflare Pages
run: |
for attempt in 1 2 3; do
echo "Deploy attempt $attempt/3..."
if wrangler pages deploy deploy \
--project-name=PROJECT_NAME \
--branch=main \
--commit-hash="${GITHUB_SHA}" \
--commit-message="Production deploy: ${GITHUB_SHA}"; then
echo "Deploy succeeded on attempt $attempt"
exit 0
fi
if [ "$attempt" -lt 3 ]; then
echo "Deploy failed, retrying in 150 seconds..."
sleep 150
fi
done
echo "Deploy failed after 3 attempts"
exit 1
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
timeout-minutes on deploy jobs to 20 (from 10) to accommodate retriesGITHUB_OUTPUT (preview URLs), move the output logic inside the success branch of the if blocknpx wrangler@4 and pnpm exec wrangler variantsTrigger: push to main. Concurrency: production-deploy, cancel-in-progress: false.
permissions:
contents: read
jobs:
build:
# Heavy job — candidate for self-hosted runner via /dev-actions-self-runner
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
# fetch-depth: 0 if project needs git history (e.g., doc history, changelogs)
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm build
- uses: actions/upload-artifact@v4
with: { name: dist-out, path: dist/, retention-days: 1 }
deploy:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/download-artifact@v4
with: { name: dist-out, path: deploy/ }
- run: npm install -g wrangler@4
- name: Deploy to Cloudflare Pages (production)
run: |
for attempt in 1 2 3; do
echo "Deploy attempt $attempt/3..."
if wrangler pages deploy deploy \
--project-name=PROJECT_NAME \
--branch=main \
--commit-hash="${GITHUB_SHA}" \
--commit-message="Production deploy: ${GITHUB_SHA}"; then
echo "Deploy succeeded on attempt $attempt"
exit 0
fi
if [ "$attempt" -lt 3 ]; then
echo "Deploy failed, retrying in 150 seconds..."
sleep 150
fi
done
echo "Deploy failed after 3 attempts"
exit 1
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
notify: # Optional IFTTT notification
needs: [build, deploy]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Notify via IFTTT
if: env.IFTTT_PROD_NOTIFY != ''
env:
IFTTT_PROD_NOTIFY: ${{ secrets.IFTTT_PROD_NOTIFY }}
RAW_COMMIT_MSG: ${{ github.event.head_commit.message }}
BUILD_RESULT: ${{ needs.build.result }}
DEPLOY_RESULT: ${{ needs.deploy.result }}
GITHUB_SHA_VAL: ${{ github.sha }}
SERVER_URL: ${{ github.server_url }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
run: |
if [ "$DEPLOY_RESULT" = "success" ]; then STATUS="succeeded"
elif [ "$BUILD_RESULT" = "failure" ]; then STATUS="failed (build)"
elif [ "$DEPLOY_RESULT" = "failure" ]; then STATUS="failed (deploy)"
else STATUS="cancelled"; fi
COMMIT_MSG=$(echo "$RAW_COMMIT_MSG" | head -1 | sed 's/"/\\"/g')
SHORT_SHA=$(echo "$GITHUB_SHA_VAL" | cut -c1-7)
RUN_URL="${SERVER_URL}/${REPO}/actions/runs/${RUN_ID}"
curl -sSf --max-time 10 -X POST "$IFTTT_PROD_NOTIFY" \
-H 'Content-Type: application/json' \
-d "{
\"value1\": \"Cloudflare Pages deploy ${STATUS}\",
\"value2\": \"${SHORT_SHA} ${COMMIT_MSG}\",
\"value3\": \"${RUN_URL}\"
}" || echo "::warning::IFTTT notification failed"
Trigger: pull_request to main. Concurrency: per-PR, cancel-in-progress: true.
permissions:
contents: read
pull-requests: write
Build job identical to production. Preview job:
deploy/--branch="pr-${PR_NUMBER}"https://pr-${PR_NUMBER}.PROJECT_NAME.pages.devactions/github-script@v8 with marker <!-- cf-preview-pr -->env:: const deployUrl = process.env.DEPLOY_URL;Trigger: push to preview and expreview/**. Concurrency: per-branch, cancel-in-progress: true.
permissions:
contents: read
pull-requests: write
statuses: write
Single-job workflow (build + deploy in one job):
pnpm exec wrangler (node_modules available in same job)createCommitStatus API<!-- cf-preview-branch -->| Secret | Required | Purpose |
|---|---|---|
CLOUDFLARE_API_TOKEN | Yes | Wrangler authentication |
CLOUDFLARE_ACCOUNT_ID | Yes | Cloudflare account identifier |
IFTTT_PROD_NOTIFY | No | IFTTT webhook URL (skipped if not set) |
The Cloudflare Pages project is auto-created on first deploy via wrangler pages deploy.
pnpm build # Verify build works locally
/dev-actions-self-runner — Add self-hosted runner with fallback for build jobs/dev-ci-ifttt-notify — Add IFTTT webhook notifications