b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
174 lines
7.5 KiB
YAML
174 lines
7.5 KiB
YAML
name: Playground Preview
|
|
|
|
# Workers Builds runs `wrangler preview` for the playground demo (it can't use
|
|
# the standard preview-URL flow because the playground has a Durable Object).
|
|
# These are a private beta feature, and don't currently support automatic PR
|
|
# comments with the preview URL.
|
|
# The cloudflare-workers-and-pages bot posts a "build successful" comment when
|
|
# the build finishes, but it doesn't include the preview URL (preview URLs are
|
|
# a `wrangler versions upload` concept, not `wrangler preview`).
|
|
# This workflow is a workaround to post the preview URL in the PR description.
|
|
#
|
|
# The playground is the primary "try this PR" surface for emdash: each visit
|
|
# gets its own session-scoped Durable Object, so reviewers can poke at a full
|
|
# working admin without signup, login, or shared state. That makes the preview
|
|
# link the single most useful thing in the PR -- but a sticky comment posted
|
|
# after the build would land below the fold (CF bot, pkg-pr-new, changeset-bot,
|
|
# etc.). So instead we edit the PR description to insert a managed block.
|
|
#
|
|
# Trigger: the CF bot's "Deployment successful" edit, scoped to emdash-playground.
|
|
# This means we comment when the deploy is genuinely live, not just when the
|
|
# commit was pushed.
|
|
#
|
|
# The branch preview URL is fully deterministic from the branch name and the
|
|
# worker/account names, so no Cloudflare API token is required -- only the
|
|
# default GITHUB_TOKEN.
|
|
#
|
|
# Caveats:
|
|
# - Branch slugs longer than the (private-beta, undocumented) max length get
|
|
# truncated server-side; collisions get a random 6-char suffix appended.
|
|
# In practice this is fine for emdash branch names. If the URL 404s, check
|
|
# the dash.
|
|
# - issue_comment workflows run from `main`, not the PR branch. Changes to
|
|
# this file only take effect once merged.
|
|
# - This won't fire for PRs from forks where the fork doesn't have the
|
|
# workflow file. That's fine -- the playground builds only run for the
|
|
# internal repo, not forks.
|
|
|
|
on:
|
|
issue_comment:
|
|
types: [created, edited]
|
|
|
|
permissions: {}
|
|
|
|
# The Cloudflare bot edits its comment 4-5 times during a build (queued ->
|
|
# initializing -> running -> ... -> successful). The "successful" edit is
|
|
# usually the terminal state, but a subsequent edit could race a still-running
|
|
# workflow that's mid-fetch. Serialize per PR; cancel in-flight runs so only
|
|
# the latest comment state is processed.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
update-body:
|
|
name: Update PR body
|
|
runs-on: ubuntu-latest
|
|
# Only react to:
|
|
# - PR comments (not issue comments)
|
|
# - the CF bot's comment
|
|
# - that mentions the playground worker
|
|
# - and contains the deployment-success marker
|
|
if: >-
|
|
github.event.issue.pull_request != null &&
|
|
github.event.comment.user.login == 'cloudflare-workers-and-pages[bot]' &&
|
|
contains(github.event.comment.body, 'emdash-playground') &&
|
|
contains(github.event.comment.body, 'Deployment successful!')
|
|
permissions:
|
|
pull-requests: write # read PR body and update it with the playground block; no PR code is checked out
|
|
steps:
|
|
- name: Update PR description with playground link
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
env:
|
|
BOT_COMMENT_BODY: ${{ github.event.comment.body }}
|
|
PR_NUMBER: ${{ github.event.issue.number }}
|
|
with:
|
|
script: |
|
|
const { BOT_COMMENT_BODY, PR_NUMBER } = process.env;
|
|
const prNumber = Number(PR_NUMBER);
|
|
|
|
// Confirm the playground row itself is in the successful state.
|
|
// Each row in the bot's comment looks like:
|
|
// | ✅ Deployment successful! ... | emdash-playground | <sha> | ... |
|
|
const playgroundRow = BOT_COMMENT_BODY.split("\n").find((line) =>
|
|
line.includes("| emdash-playground |"),
|
|
);
|
|
if (!playgroundRow || !playgroundRow.includes("✅ Deployment successful!")) {
|
|
core.info("Playground row not in successful state; skipping.");
|
|
return;
|
|
}
|
|
|
|
const { data: pr } = await github.rest.pulls.get({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: prNumber,
|
|
});
|
|
const branch = pr.head.ref;
|
|
|
|
// Slug rules (from SPEC: Worker Previews): lowercase, with /, .,
|
|
// +, =, _ replaced by -. We widen this to any non-DNS-safe char
|
|
// (handles Renovate-style `@`, unusual community branches, etc.):
|
|
// anything outside [a-z0-9-] becomes -, repeated -- collapsed,
|
|
// leading/trailing - trimmed. If we end up with an empty slug
|
|
// (e.g. branch was all special chars), bail rather than emit a
|
|
// broken URL.
|
|
const slug = branch
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9-]+/g, "-")
|
|
.replace(/-+/g, "-")
|
|
.replace(/^-+|-+$/g, "");
|
|
if (!slug) {
|
|
core.warning(`Branch "${branch}" produced an empty slug; skipping.`);
|
|
return;
|
|
}
|
|
|
|
const url = `https://${slug}-emdash-playground.emdash-cms.workers.dev`;
|
|
|
|
const START = "<!-- emdash:playground-preview:start -->";
|
|
const END = "<!-- emdash:playground-preview:end -->";
|
|
|
|
// The managed block. Kept short and inviting -- the link is the
|
|
// point. Each visit to the playground gets its own session-scoped
|
|
// Durable Object, so reviewers can play freely.
|
|
const block = [
|
|
START,
|
|
"",
|
|
"---",
|
|
"",
|
|
`### Try this PR`,
|
|
"",
|
|
`**[Open a fresh playground →](${url})**`,
|
|
"",
|
|
`A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.`,
|
|
"",
|
|
`<sub>Tracks \`${branch}\`. Updated automatically when the playground redeploys.</sub>`,
|
|
"",
|
|
END,
|
|
].join("\n");
|
|
|
|
const existingBody = pr.body ?? "";
|
|
|
|
// If a block already exists, replace it in place (preserves
|
|
// whatever position the author or a previous run put it in).
|
|
// Otherwise append it to the end of the description.
|
|
const blockRegex = new RegExp(
|
|
`\\n*${escapeRegex(START)}[\\s\\S]*?${escapeRegex(END)}\\n*`,
|
|
);
|
|
|
|
let newBody;
|
|
if (blockRegex.test(existingBody)) {
|
|
newBody = existingBody.replace(blockRegex, `\n\n${block}\n`);
|
|
core.info("Replaced existing playground block in PR body.");
|
|
} else {
|
|
// Append, with a blank line separator.
|
|
const trimmed = existingBody.replace(/\s+$/, "");
|
|
newBody = trimmed.length > 0 ? `${trimmed}\n\n${block}\n` : `${block}\n`;
|
|
core.info("Appended playground block to PR body.");
|
|
}
|
|
|
|
if (newBody === existingBody) {
|
|
core.info("PR body unchanged; skipping update.");
|
|
return;
|
|
}
|
|
|
|
await github.rest.pulls.update({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: prNumber,
|
|
body: newBody,
|
|
});
|
|
|
|
function escapeRegex(s) {
|
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|