skills/dev-cloudflare-pages-ci-setup/SKILL.md
Set up Cloudflare Pages deployment with GitHub Actions workflows. Use when: (1) Deploying a static site to Cloudflare Pages, (2) User says 'cloudflare pages', 'deploy to cloudflare', 'cf pages setup', (3) User wants CI/CD workflows for Cloudflare Pages with PR previews, (4) Setting up wrangler deployment pipelines.
npx skillsauth add takazudo/claude-resources dev-cloudflare-pages-ci-setupInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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 notificationsdevelopment
Link Claude Code skill names mentioned in a CodeGrid article (data/{series}/{n}.md) to the author's public claude-resources repo, pinned to the latest commit hash so links don't rot. Use when: (1) user says 'linkify cc resources', 'link the skills', 'link skill names', or invokes /dev-linkify-cc-resources; (2) editing a CodeGrid article that mentions `/commits`, `/pr-complete`, `/skill-creator` or other Claude Code skills and they should point to claude-resources. Only links skills that actually exist in the public repo; skips hypothetical examples and code blocks.
development
Second opinion from Claude Opus on a plan or approach. Use when: (1) Planning phase of /big-plan needs a higher-quality review than /codex-2nd / /gco-2nd, (2) User says 'opus 2nd' or 'opus opinion', (3) Wanting Anthropic's larger model to critique a plan. Spawns a general-purpose Agent with model: opus that reads the plan file and returns structured feedback. Anthropic quota — not free.
tools
AI-based testing via subagent + a per-task test-flow skill. Use when the user wants to verify something that mechanical assertions can't fully capture — image recognition, visual size/position comparison, animation smoothness, multi-step manual flows that need AI judgment. Triggers: 'AI-based test', 'AI test', 'visual verify', 'image recognition test', 'manual operation test', 'human-eye check', 'verify visually', 'compare screenshots', 'looks the same', 'looks correct'. The skill's job is to (1) author a focused test-flow skill that captures the exact procedure + verdict criteria, then (2) dispatch a verification subagent via the Agent tool that loads BOTH the test-flow skill AND a browser-driving skill (/verify-ui primary, /headless-browser fallback) so the subagent has clear context and consistent verdicts. NEVER uses `claude -p` — subagent dispatch goes through the Agent tool exclusively.
development
End-of-workflow audit of touched GitHub issues, PRs, and branches via a Sonnet subagent. Use when: (1) /big-plan, /x-as-pr, or /x-wt-teams finishes its main work and needs to verify every touched resource is in the right state (closed when done, kept when ongoing, deleted when dead), (2) User says 'cleanup resources', 'audit cleanup', or 'check what should be closed', (3) A long workflow ends and the manager wants a structured paper trail of what it closed/kept/deleted. Auto-execute by default — the Sonnet agent proposes, the manager (you) executes safe actions and prints a final report.