Loading files
4bc857bacThis file is pinned to the selected release. Download the original to open it in your design tool.
name: Deploy Cloudflare PR Preview
on:
workflow_run:
workflows:
- Bundle Stack-chan Firmware
types:
- completed
pull_request_target:
types:
- closed
permissions: {}
concurrency:
group: cloudflare-preview-${{ github.event.workflow_run.head_repository.id || github.event.pull_request.head.repo.id || github.repository_id }}-${{ github.event.workflow_run.head_branch || github.event.pull_request.head.ref || github.run_id }}
cancel-in-progress: false
jobs:
deploy:
if: github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
issues: write
pull-requests: write
steps:
- name: Checkout trusted preview scripts
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.repository.default_branch }}
path: trusted-source
persist-credentials: false
- name: Resolve current pull request
id: pull_request
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
const { pathToFileURL } = require('node:url')
const helperUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/trusted-source/.github/scripts/resolve-preview-pull-request.mjs`,
).href
const { resolvePreviewPullRequest } = await import(helperUrl)
const run = context.payload.workflow_run
const resolution = await resolvePreviewPullRequest({
github,
owner: context.repo.owner,
repo: context.repo.repo,
run,
})
if (!resolution.number) {
core.notice('No pull request is associated with this workflow run')
core.setOutput('eligible', 'false')
return
}
core.setOutput('number', String(resolution.number))
core.setOutput('head_sha', resolution.headSha)
core.setOutput('eligible', String(resolution.eligible))
if (!resolution.eligible) {
core.notice(`Skipping stale or ineligible preview run for PR #${resolution.number}`)
}
- name: Report failed preview build
if: steps.pull_request.outputs.eligible == 'true' && github.event.workflow_run.conclusion != 'success'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
PR_NUMBER: ${{ steps.pull_request.outputs.number }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
RUN_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
with:
script: |
const { pathToFileURL } = require('node:url')
const helperUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/trusted-source/.github/scripts/upsert-preview-comment.mjs`,
).href
const { upsertPreviewComment } = await import(helperUrl)
const content = `## Cloudflare PR preview
The preview build **${process.env.RUN_CONCLUSION}**. No new deployment was published.
[Open the failed workflow run](${process.env.RUN_URL}). Any URL from an earlier comment is stale.`
await upsertPreviewComment({
github,
owner: context.repo.owner,
repo: context.repo.repo,
issueNumber: Number(process.env.PR_NUMBER),
content,
})
- name: Download preview artifact
if: steps.pull_request.outputs.eligible == 'true' && github.event.workflow_run.conclusion == 'success'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: cloudflare-pages-preview
path: preview-upload/site
github-token: ${{ github.token }}
run-id: ${{ github.event.workflow_run.id }}
- name: Validate preview artifact
if: steps.pull_request.outputs.eligible == 'true' && github.event.workflow_run.conclusion == 'success'
run: node ./trusted-source/.github/scripts/validate-pages-preview.mjs ./preview-upload/site
- name: Validate Cloudflare configuration
if: steps.pull_request.outputs.eligible == 'true' && github.event.workflow_run.conclusion == 'success'
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_PAGES_PROJECT: ${{ vars.CLOUDFLARE_PAGES_PROJECT }}
run: node ./trusted-source/.github/scripts/validate-cloudflare-config.mjs
- name: Deploy preview to Cloudflare Pages
if: steps.pull_request.outputs.eligible == 'true' && github.event.workflow_run.conclusion == 'success'
id: cloudflare
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: preview-upload
packageManager: npm
command: >-
pages deploy site
--project-name=${{ vars.CLOUDFLARE_PAGES_PROJECT }}
--branch=pr-${{ steps.pull_request.outputs.number }}
--commit-hash=${{ steps.pull_request.outputs.head_sha }}
- name: Publish preview link
if: steps.pull_request.outputs.eligible == 'true' && github.event.workflow_run.conclusion == 'success'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
PR_NUMBER: ${{ steps.pull_request.outputs.number }}
HEAD_SHA: ${{ steps.pull_request.outputs.head_sha }}
PREVIEW_URL: ${{ steps.cloudflare.outputs.pages-deployment-alias-url }}
IMMUTABLE_URL: ${{ steps.cloudflare.outputs.deployment-url }}
with:
script: |
const { pathToFileURL } = require('node:url')
const helperUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/trusted-source/.github/scripts/upsert-preview-comment.mjs`,
).href
const { upsertPreviewComment } = await import(helperUrl)
const previewUrl = process.env.PREVIEW_URL || process.env.IMMUTABLE_URL
if (!previewUrl) throw new Error('Cloudflare did not return a preview URL')
const content = `## Cloudflare PR preview
[Open the latest preview](${previewUrl}) for commit \`${process.env.HEAD_SHA.slice(0, 12)}\`.
Immutable deployment: ${process.env.IMMUTABLE_URL || previewUrl}
> [!WARNING]
> Pull request previews contain untrusted web and firmware code. Review the changes before granting WebSerial/Bluetooth permissions or flashing a device.`
await upsertPreviewComment({
github,
owner: context.repo.owner,
repo: context.repo.repo,
issueNumber: Number(process.env.PR_NUMBER),
content,
})
close:
if: github.event_name == 'pull_request_target' && github.event.action == 'closed'
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Find preview comment
id: preview
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
const marker = '<!-- stackchan-cloudflare-preview -->'
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
})
const exists = comments.some(
(comment) => comment.user?.login === 'github-actions[bot]' && comment.body?.includes(marker),
)
core.setOutput('exists', String(exists))
- name: Checkout trusted preview scripts
if: steps.preview.outputs.exists == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.repository.default_branch }}
path: trusted-source
persist-credentials: false
- name: Validate Cloudflare configuration
if: steps.preview.outputs.exists == 'true'
env:
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_PAGES_PROJECT: ${{ vars.CLOUDFLARE_PAGES_PROJECT }}
run: node ./trusted-source/.github/scripts/validate-cloudflare-config.mjs
- name: Create closed preview page
if: steps.preview.outputs.exists == 'true'
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
mkdir -p closed-upload/site
printf '%s\n' \
'<!doctype html>' \
'<html lang="en"><head><meta charset="utf-8"><meta name="robots" content="noindex">' \
'<meta name="viewport" content="width=device-width,initial-scale=1">' \
"<title>PR #${PR_NUMBER} preview closed</title></head>" \
"<body><main><h1>PR #${PR_NUMBER} preview closed</h1>" \
'<p>This pull request is closed, so its preview is no longer available.</p></main></body></html>' \
> closed-upload/site/index.html
- name: Replace preview with closed page
if: steps.preview.outputs.exists == 'true'
id: cloudflare
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: closed-upload
packageManager: npm
command: >-
pages deploy site
--project-name=${{ vars.CLOUDFLARE_PAGES_PROJECT }}
--branch=pr-${{ github.event.pull_request.number }}
--commit-hash=${{ github.event.pull_request.head.sha }}
- name: Delete superseded preview deployments
if: steps.preview.outputs.exists == 'true'
env:
BRANCH_NAME: pr-${{ github.event.pull_request.number }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_PAGES_PROJECT: ${{ vars.CLOUDFLARE_PAGES_PROJECT }}
KEEP_DEPLOYMENT_ID: ${{ steps.cloudflare.outputs.pages-deployment-id }}
run: |
set -euo pipefail
test -n "$KEEP_DEPLOYMENT_ID"
page=1
while true; do
response=$(curl -fsS \
--get \
--header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
--header 'Content-Type: application/json' \
--data-urlencode 'env=preview' \
--data-urlencode 'per_page=100' \
--data-urlencode "page=${page}" \
"https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${CLOUDFLARE_PAGES_PROJECT}/deployments")
echo "$response" | jq -e '.success == true' >/dev/null
echo "$response" | jq -r \
--arg branch "$BRANCH_NAME" \
--arg keep "$KEEP_DEPLOYMENT_ID" \
'.result[] | select(.deployment_trigger.metadata.branch == $branch and .id != $keep) | .id' \
>> deployments-to-delete.txt
total_pages=$(echo "$response" | jq -r '.result_info.total_pages // 1')
if [ "$page" -ge "$total_pages" ]; then
break
fi
page=$((page + 1))
done
sort -u deployments-to-delete.txt | while IFS= read -r deployment_id; do
if [ -z "$deployment_id" ]; then
continue
fi
curl -fsS \
--request DELETE \
--header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
"https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${CLOUDFLARE_PAGES_PROJECT}/deployments/${deployment_id}?force=true" \
>/dev/null
done
- name: Mark preview as closed
if: steps.preview.outputs.exists == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
PREVIEW_URL: ${{ steps.cloudflare.outputs.pages-deployment-alias-url }}
with:
script: |
const { pathToFileURL } = require('node:url')
const helperUrl = pathToFileURL(
`${process.env.GITHUB_WORKSPACE}/trusted-source/.github/scripts/upsert-preview-comment.mjs`,
).href
const { upsertPreviewComment } = await import(helperUrl)
const content = `## Cloudflare PR preview
This pull request is closed. Its preview has been replaced with a closed page${process.env.PREVIEW_URL ? ` at ${process.env.PREVIEW_URL}` : ''}.`
await upsertPreviewComment({
github,
owner: context.repo.owner,
repo: context.repo.repo,
issueNumber: Number(process.env.PR_NUMBER),
content,
})