Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions .github/workflows/deploy-cloudflare.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Deploy to Cloudflare Workers

on:
push:
branches: [main]
pull_request:
branches: [main]
types: [opened, synchronize, reopened, closed]

jobs:
test:
runs-on: ubuntu-latest
if: github.event.action != 'closed'
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"

- run: npm ci

- name: Run tests
run: npm test

deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"

- run: npm ci

- name: Build and deploy to Cloudflare Workers
run: npx @opennextjs/cloudflare build && npx @opennextjs/cloudflare deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}
NEXT_PUBLIC_SANITY_DATASET: ${{ vars.NEXT_PUBLIC_SANITY_DATASET }}
NEXT_PUBLIC_SANITY_PERSPECTIVE: ${{ vars.NEXT_PUBLIC_SANITY_PERSPECTIVE }}
SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}

preview:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action != 'closed'
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"

- run: npm ci

- name: Build for preview
run: npx @opennextjs/cloudflare build
env:
NEXT_PUBLIC_SANITY_PROJECT_ID: ${{ vars.NEXT_PUBLIC_SANITY_PROJECT_ID }}
NEXT_PUBLIC_SANITY_DATASET: ${{ vars.NEXT_PUBLIC_SANITY_DATASET }}
NEXT_PUBLIC_SANITY_PERSPECTIVE: ${{ vars.NEXT_PUBLIC_SANITY_PERSPECTIVE }}
SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}

- name: Deploy preview to Cloudflare Workers
id: deploy
run: |
set -e
PREVIEW_NAME="bayphillips-blog-pr-${{ github.event.pull_request.number }}"
# Deploy and capture output to extract URL
DEPLOY_OUTPUT=$(npx wrangler deploy --name "$PREVIEW_NAME" 2>&1 | tee /dev/stderr)
# Extract the workers.dev URL from output (wrangler outputs the URL after deployment)
PREVIEW_URL=$(echo "$DEPLOY_OUTPUT" | grep -oE 'https://[^ ]+\.workers\.dev' | head -1)
if [ -z "$PREVIEW_URL" ]; then
echo "Error: Could not extract preview URL from wrangler output"
exit 1
fi
# Set secrets for the preview worker
echo "$SANITY_API_READ_TOKEN" | npx wrangler secret put SANITY_API_READ_TOKEN --name "$PREVIEW_NAME"
echo "preview_url=$PREVIEW_URL" >> $GITHUB_OUTPUT
echo "preview_name=$PREVIEW_NAME" >> $GITHUB_OUTPUT
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
SANITY_API_READ_TOKEN: ${{ secrets.SANITY_API_READ_TOKEN }}

- name: Comment preview URL on PR
uses: actions/github-script@v7
with:
script: |
const previewUrl = '${{ steps.deploy.outputs.preview_url }}';
const previewName = '${{ steps.deploy.outputs.preview_name }}';
const body = `## 🚀 Preview Deployment

| Status | URL |
|--------|-----|
| ✅ Deployed | [${previewUrl}](${previewUrl}) |

**Worker:** \`${previewName}\`
**Commit:** \`${{ github.event.pull_request.head.sha }}\`

> This preview will be automatically cleaned up when the PR is closed.`;

// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('## 🚀 Preview Deployment')
);

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}

cleanup-preview:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.action == 'closed'
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: "npm"

- run: npm ci

- name: Delete preview worker
run: |
PREVIEW_NAME="bayphillips-blog-pr-${{ github.event.pull_request.number }}"
npx wrangler delete --name "$PREVIEW_NAME" --force || true
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ yarn-error.log*
# vercel
.vercel

# cloudflare
.open-next

# typescript
*.tsbuildinfo
next-env.d.ts
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

Next.js 15 blog application with Sanity.io CMS, built for bayphillips.com. Uses App Router, TypeScript, and Tailwind CSS. Hosted on Vercel.
Next.js 15 blog application with Sanity.io CMS, built for bayphillips.com. Uses App Router, TypeScript, and Tailwind CSS. Hosted on Cloudflare Workers via `@opennextjs/cloudflare`.

## Commands

Expand Down Expand Up @@ -102,7 +102,7 @@ All schemas auto-imported via schema index file.

- **Development**: Uses Turbopack (`next dev --turbopack`)
- **Production**: Uses webpack (`next build --webpack`)
- **Output**: Standalone mode (`output: 'standalone'` in next.config.js)
- **Output**: Cloudflare Workers via `@opennextjs/cloudflare` (no `standalone` mode; use `npx opennextjs-cloudflare build` for deployment builds)
- **Image Optimization**: Configured for Sanity CDN (`cdn.sanity.io`)
- **Security Headers**: CORS, XSS protection, frame options, CSP configured

Expand Down
Loading