actions/cloudflare-pages-preview/deploy/action.yml
Den Delimarsky a4bb78216a
Add pr-number input to cloudflare-pages-preview actions
Both deploy and cleanup previously read context.payload.pull_request.number
directly, which throws on workflow_dispatch and other non-PR triggers.

New optional inputs:
- deploy: pr-number (default: event PR number), commit-sha (default: github.sha)
- cleanup: pr-number (default: event PR number)

If pr-number resolves to empty/0, the comment step is skipped gracefully
instead of crashing.

This lets /stageblog's companion workflow_dispatch receiver call the
composite deploy action directly instead of inlining wrangler + noindex
injection. The noindex logic staying in the actions repo (rather than in
a script file inside the caller repo that gets checked out at the fork's
SHA) is also the secure path — fork authors can't tamper with it.

The slash-commands README example is updated to use the deploy action
with the new inputs. The earlier commit on this branch (which rewrote
the example to call wrangler directly) is effectively superseded.
2026-03-11 20:25:04 +00:00

157 lines
5.7 KiB
YAML

name: Cloudflare Pages PR Preview — Deploy
description: >-
Deploys a pre-built static HTML directory to Cloudflare Pages under a per-PR
branch alias, injects noindex headers/meta tags, and posts/updates a sticky
PR comment with the preview URLs.
inputs:
directory:
description: Path to the built static HTML directory (e.g. blog/public)
required: true
project-name:
description: Cloudflare Pages project name (must already exist in the CF dashboard)
required: true
api-token:
description: Cloudflare API token with Pages:Edit permission on the project
required: true
account-id:
description: Cloudflare account ID
required: true
branch:
description: Cloudflare Pages branch alias for this deployment
required: false
default: pr-${{ github.event.pull_request.number }}
inject-noindex:
description: >-
Whether to inject X-Robots-Tag headers (via _headers file) and a
<meta name="robots"> tag into all HTML pages to prevent search engine
indexing of the preview. Set to 'false' to skip.
required: false
default: 'true'
comment-marker:
description: HTML comment marker used to identify and update the sticky PR comment
required: false
default: '<!-- cf-pages-preview -->'
comment-title:
description: Heading shown in the sticky PR comment
required: false
default: 'Preview Deployed'
github-token:
description: GitHub token for posting/updating the PR comment
required: false
default: ${{ github.token }}
pr-number:
description: >-
PR number to comment on. Only needed on triggers where
context.payload.pull_request is unavailable (e.g. workflow_dispatch).
Defaults to the event payload's PR number.
required: false
default: ${{ github.event.pull_request.number }}
commit-sha:
description: >-
Commit SHA shown in the sticky comment. Useful when the checked-out
ref differs from github.sha (e.g. building a pinned head SHA on
workflow_dispatch). Defaults to github.sha.
required: false
default: ${{ github.sha }}
outputs:
deployment-url:
description: URL of the specific deployment (changes per push)
value: ${{ steps.deploy.outputs.deployment-url }}
alias-url:
description: Stable branch-alias URL (same across pushes to this PR)
value: ${{ steps.deploy.outputs.pages-deployment-alias-url }}
runs:
using: composite
steps:
# Prevent preview deployments from being indexed by search engines.
# Cloudflare Pages serves _headers as HTTP headers for matching paths.
# X-Robots-Tag covers all content types (HTML, RSS, sitemaps, etc.)
- name: Add noindex headers
if: inputs.inject-noindex == 'true'
shell: bash
env:
DIR: ${{ inputs.directory }}
run: |
cat > "${DIR}/_headers" <<'EOF'
/*
X-Robots-Tag: noindex, nofollow, noarchive, nosnippet
EOF
# Also inject a <meta> tag into every HTML page as belt-and-suspenders
# in case the site is later served from somewhere that ignores _headers.
- name: Inject noindex meta tag
if: inputs.inject-noindex == 'true'
shell: bash
env:
DIR: ${{ inputs.directory }}
run: |
find "${DIR}" -name '*.html' -print0 | \
xargs -0 -r sed -i 's|<head>|<head><meta name="robots" content="noindex, nofollow, noarchive, nosnippet">|'
- name: Deploy to Cloudflare Pages
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ inputs.api-token }}
accountId: ${{ inputs.account-id }}
command: pages deploy ${{ inputs.directory }} --project-name=${{ inputs.project-name }} --branch=${{ inputs.branch }}
- name: Comment with preview URL
uses: actions/github-script@v8
env:
DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }}
ALIAS_URL: ${{ steps.deploy.outputs.pages-deployment-alias-url }}
COMMENT_MARKER: ${{ inputs.comment-marker }}
COMMENT_TITLE: ${{ inputs.comment-title }}
INJECT_NOINDEX: ${{ inputs.inject-noindex }}
PR_NUMBER: ${{ inputs.pr-number }}
COMMIT_SHA: ${{ inputs.commit-sha }}
with:
github-token: ${{ inputs.github-token }}
script: |
const { owner, repo } = context.repo;
const prNumber = Number(process.env.PR_NUMBER);
if (!prNumber) {
core.info('No PR number available — skipping comment.');
return;
}
const deployUrl = process.env.DEPLOY_URL;
const aliasUrl = process.env.ALIAS_URL || deployUrl;
const marker = process.env.COMMENT_MARKER;
const title = process.env.COMMENT_TITLE;
const noindex = process.env.INJECT_NOINDEX === 'true';
const noindexNote = noindex
? ' All pages served with `noindex, nofollow` — search engines will not crawl this preview.'
: '';
const body = `${marker}
## ${title}
| | |
|---|---|
| **Preview (stable)** | ${aliasUrl} |
| **This commit** | ${deployUrl} |
| **Commit** | \`${process.env.COMMIT_SHA.slice(0, 7)}\` |
_Includes drafts and future-dated posts.${noindexNote}_`;
const { data: comments } = await github.rest.issues.listComments({
owner, repo, issue_number: prNumber
});
const existing = comments.find(c =>
c.user.type === 'Bot' && c.body.includes(marker)
);
if (existing) {
await github.rest.issues.updateComment({
owner, repo, comment_id: existing.id, body
});
} else {
await github.rest.issues.createComment({
owner, repo, issue_number: prNumber, body
});
}