Skip to content

Merge branch 'main' into auto-update-github-stars #118

Merge branch 'main' into auto-update-github-stars

Merge branch 'main' into auto-update-github-stars #118

Workflow file for this run

name: Deploy Preview
on:
pull_request:
push:
branches-ignore: [main, develop]
concurrency:
group: preview-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy Preview to Cloudflare Workers
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
deployments: write
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Determine whether preview deploy should run
id: deploy_guard
shell: bash
env:
CF_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CF_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
BASE_REPO: ${{ github.repository }}
EVENT_NAME: ${{ github.event_name }}
ACTOR: ${{ github.actor }}
run: |
# Decide whether we can deploy a preview safely.
# Fork PRs do not receive repository secrets, so deployment must be skipped.
SHOULD_DEPLOY="true"
REASON=""
if [[ "$ACTOR" == "dependabot[bot]" ]]; then
SHOULD_DEPLOY="false"
REASON="Dependabot-triggered workflows do not have access to repository secrets in this workflow."
fi
if [[ "$EVENT_NAME" == "pull_request" ]]; then
if [[ -n "$PR_HEAD_REPO" && "$PR_HEAD_REPO" != "$BASE_REPO" ]]; then
SHOULD_DEPLOY="false"
REASON="Fork pull requests do not have access to repository secrets in GitHub Actions."
fi
fi
if [[ "$SHOULD_DEPLOY" == "true" ]]; then
if [[ -z "$CF_API_TOKEN" || -z "$CF_ACCOUNT_ID" ]]; then
SHOULD_DEPLOY="false"
REASON="Missing required Cloudflare secrets (CLOUDFLARE_API_TOKEN/CLOUDFLARE_ACCOUNT_ID)."
fi
fi
{
echo "should_deploy=$SHOULD_DEPLOY"
echo "reason=$REASON"
} >> "$GITHUB_OUTPUT"
- name: Compute preview identifiers
id: preview_meta
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
CF_BRANCH="pr-${{ github.event.pull_request.number }}"
CONTEXT_LABEL="PR #${{ github.event.pull_request.number }}"
else
RAW_BRANCH="${{ github.ref_name }}"
SLUG="$(echo "$RAW_BRANCH" | tr '[:upper:]' '[:lower:]' | sed -E 's#[^a-z0-9]+#-#g; s#(^-+|-+$)##g' | cut -c1-40)"
if [[ -z "$SLUG" ]]; then
SLUG="branch"
fi
CF_BRANCH="br-$SLUG"
CONTEXT_LABEL="Branch $RAW_BRANCH"
fi
# Cloudflare Preview URLs (aliased) format:
# <ALIAS>-<WORKER_NAME>.<SUBDOMAIN>.workers.dev
# See: https://developers.cloudflare.com/workers/configuration/previews/
PREVIEW_ALIAS="$CF_BRANCH"
WORKER_NAME="aicodingstack"
PREVIEW_URL="https://${PREVIEW_ALIAS}-${WORKER_NAME}.pr-preview.workers.dev"
{
echo "cf_branch=$CF_BRANCH"
echo "worker_name=$WORKER_NAME"
echo "preview_alias=$PREVIEW_ALIAS"
echo "preview_url=$PREVIEW_URL"
echo "context_label=$CONTEXT_LABEL"
} >> "$GITHUB_OUTPUT"
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run CI tests
run: npm run test:ci
- name: Build with OpenNext
run: npm run build
env:
BUILD_TIME: ${{ github.event.pull_request.updated_at || github.event.head_commit.timestamp }}
- name: Deploy Preview
if: steps.deploy_guard.outputs.should_deploy == 'true'
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: versions upload --preview-alias ${{ steps.preview_meta.outputs.preview_alias }}
- name: Comment Preview URL
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
env:
PREVIEW_URL: ${{ steps.preview_meta.outputs.preview_url }}
SHOULD_DEPLOY: ${{ steps.deploy_guard.outputs.should_deploy }}
SKIP_REASON: ${{ steps.deploy_guard.outputs.reason }}
with:
script: |
const prNumber = context.payload.pull_request.number;
const previewUrl = process.env.PREVIEW_URL;
const shouldDeploy = process.env.SHOULD_DEPLOY === 'true';
const skipReason = process.env.SKIP_REASON || 'Preview deployment was skipped.';
const commitSha = context.sha.substring(0, 7);
// Find existing bot comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('Preview deployment')
);
const status = shouldDeploy ? 'Ready' : 'Skipped';
const urlCell = shouldDeploy ? `[${previewUrl}](${previewUrl})` : '-';
const reasonBlock = shouldDeploy ? '' : `\n\n**Reason:** ${skipReason}\n\nIf you want a preview deployment, please ask a maintainer to run this from a branch within the main repository.`;
const body = `### Preview deployment
| Status | URL |
|--------|-----|
| ${status} | ${urlCell} |
**Commit:** \`${commitSha}\`
**Updated:** ${new Date().toISOString()}${reasonBlock}`;
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body
});
}
- name: Deployment summary
run: |
echo "### Preview Deployment" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Context:** ${{ steps.preview_meta.outputs.context_label }}" >> $GITHUB_STEP_SUMMARY
echo "- **URL:** ${{ steps.preview_meta.outputs.preview_url }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Deploy:** ${{ steps.deploy_guard.outputs.should_deploy == 'true' && 'ran' || 'skipped' }}" >> $GITHUB_STEP_SUMMARY
if [[ "${{ steps.deploy_guard.outputs.should_deploy }}" != "true" && -n "${{ steps.deploy_guard.outputs.reason }}" ]]; then
echo "- **Skip reason:** ${{ steps.deploy_guard.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
fi