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.
157 lines
5.7 KiB
YAML
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
|
|
});
|
|
}
|