fix: restore worker.ts in cloudflare templates and add template sync#478
fix: restore worker.ts in cloudflare templates and add template sync#478
Conversation
Cloudflare templates were missing src/worker.ts and the "main" field in wrangler.jsonc, so the custom worker entrypoint (Astro handler + PluginBridge re-export) was never used. Also adds worker-configuration.d.ts to the three templates that were missing it. Adds scripts/sync-templates-repo.mjs to sync templates to the standalone emdash-cms/templates repo with resolved dependency versions. A new sync-templates workflow runs after publish or on manual dispatch. Also fixes sync-cloudflare-templates.sh to use rsync with --exclude so it preserves worker.ts when copying src/ from base templates.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 48954b5 | Apr 12 2026, 10:36 AM |
|
Scope checkThis PR changes 36,495 lines across 15 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Pull request overview
Restores Cloudflare templates’ custom worker entrypoint usage and introduces automation to sync templates into the standalone emdash-cms/templates repository after releases.
Changes:
- Add
main: ./src/worker.tsto Cloudflare templatewrangler.jsoncfiles and restoresrc/worker.tsentrypoints that re-exportPluginBridge. - Add
scripts/sync-templates-repo.mjsplus a reusable workflow to sync templates toemdash-cms/templatesand open a PR. - Update
sync-cloudflare-templates.shto rsync directories while preserving Cloudflare-specificworker.ts.
Reviewed changes
Copilot reviewed 12 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| templates/starter-cloudflare/wrangler.jsonc | Points Wrangler to the intended Cloudflare worker entrypoint. |
| templates/starter-cloudflare/src/worker.ts | Restores the worker entrypoint (Astro handler + PluginBridge re-export). |
| templates/portfolio-cloudflare/wrangler.jsonc | Points Wrangler to the intended Cloudflare worker entrypoint. |
| templates/portfolio-cloudflare/src/worker.ts | Restores the worker entrypoint (Astro handler + PluginBridge re-export). |
| templates/marketing-cloudflare/wrangler.jsonc | Points Wrangler to the intended Cloudflare worker entrypoint. |
| templates/marketing-cloudflare/src/worker.ts | Restores the worker entrypoint (Astro handler + PluginBridge re-export). |
| templates/blog-cloudflare/wrangler.jsonc | Points Wrangler to the intended Cloudflare worker entrypoint. |
| templates/blog-cloudflare/src/worker.ts | Restores the worker entrypoint (Astro handler + PluginBridge re-export). |
| scripts/sync-templates-repo.mjs | New script to clone/sync templates repo, resolve dependency protocols, and open a PR. |
| scripts/sync-cloudflare-templates.sh | Switches to rsync for directories with an exclude to preserve Cloudflare-specific files. |
| .github/workflows/sync-templates.yml | Adds a reusable workflow to run the sync script with an app token. |
| .github/workflows/release.yml | Triggers template sync workflow after a successful publish. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use gh repo clone instead of embedding token in clone URL - Use execFileSync with arg array for gh pr create (no shell injection) - Use -B and --force-with-lease for re-runnable branch push - Guard against non-directory dest in sync-cloudflare-templates.sh
- Replace all execSync/run() calls with execFileSync arg arrays so interpolated values (version strings from package.json) never pass through a shell - Add ref guard (github.ref == refs/heads/main) to sync-templates workflow so it can't be dispatched from arbitrary branches - Wrap post-clone logic in try/finally to clean up temp dir on error
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 15 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| git(["checkout", "-B", branch], targetDir); | ||
| git(["add", "-A"], targetDir); | ||
| git(["commit", "-m", `chore: sync templates from emdash v${emdashVersion}`], targetDir); | ||
| git(["push", "--force-with-lease", "-u", "origin", branch], targetDir); | ||
|
|
There was a problem hiding this comment.
git push will likely fail in CI because the repo is cloned via gh repo clone but git isn’t configured with credentials for the generated app token. Other workflows in this repo set up authenticated pushes via GIT_ASKPASS/x-access-token (see .github/workflows/auto-format.yml:96-107). Consider configuring git auth before pushing (e.g., gh auth setup-git for the provided GH_TOKEN, or an askpass/remote approach that doesn’t leak the token).
|
|
||
| - name: Checkout | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
|
|
There was a problem hiding this comment.
This workflow runs a Node script but doesn’t set up/pin a Node version (unlike other workflows such as .github/workflows/ci.yml:25-30). To avoid runner-image drift breaking the sync (and to match repo conventions), add an explicit actions/setup-node step (and optionally cache/lock the version).
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' |
| if [[ -L "$dest" ]]; then | ||
| rm "$dest" | ||
| elif [[ -d "$dest" ]]; then | ||
| rm -rf "$dest" | ||
| elif [[ -f "$dest" ]]; then | ||
| rm "$dest" | ||
| fi | ||
|
|
||
| # Copy the item | ||
| if [[ -d "$src" ]]; then | ||
| cp -r "$src" "$dest" | ||
| echo " Copied directory: $item" | ||
| else | ||
| cp "$src" "$dest" | ||
| echo " Copied file: $item" | ||
| fi | ||
| cp "$src" "$dest" | ||
| echo " Copied file: $item" |
There was a problem hiding this comment.
In the file-copy branch, if $dest exists as a directory (e.g., from a previous run or template structure change), it won’t be removed and cp will fail because it can’t overwrite a directory. Consider handling the -d "$dest" case here (e.g., remove it recursively) before copying the file to make the sync script robust.
- Run gh auth setup-git after clone so git push works with GH_TOKEN - Pin Node version in sync-templates workflow - Handle dest-is-directory in file copy branch of sync-cloudflare-templates.sh
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 15 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| on: | ||
| workflow_dispatch: | ||
| workflow_call: | ||
|
|
||
| jobs: | ||
| sync: | ||
| name: Sync templates to emdash-cms/templates | ||
| if: github.ref == 'refs/heads/main' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Generate token | ||
| id: app-token | ||
| uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 | ||
| with: | ||
| app-id: ${{ secrets.APP_ID }} | ||
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | ||
| repositories: templates | ||
| owner: emdash-cms | ||
|
|
There was a problem hiding this comment.
sync-templates.yml is used as a reusable workflow (workflow_call) from release.yml, but it doesn’t declare the secrets it expects. In reusable workflows, secrets.* are only available if they’re declared under on.workflow_call.secrets, so secrets.APP_ID / secrets.APP_PRIVATE_KEY will be empty and token generation will fail. Declare these secrets (and mark required) under workflow_call.
| const args = process.argv.slice(2); | ||
| const dryRun = args.includes("--dry-run"); | ||
| const localIdx = args.indexOf("--local"); | ||
| const localPath = localIdx !== -1 ? args[localIdx + 1] : null; | ||
|
|
||
| const catalog = parseCatalog(); | ||
| const workspace = collectWorkspaceVersions(); | ||
|
|
||
| console.log("Workspace packages:"); | ||
| for (const [name, version] of Object.entries(workspace)) { | ||
| console.log(` ${name} = ${String(version)}`); | ||
| } | ||
| console.log(""); | ||
|
|
||
| let targetDir; | ||
| let tempDir; | ||
|
|
||
| if (localPath) { | ||
| targetDir = resolve(localPath); | ||
| if (!existsSync(join(targetDir, ".git"))) { | ||
| console.error(`Error: ${targetDir} is not a git repository`); | ||
| process.exit(1); | ||
| } | ||
| } else { |
There was a problem hiding this comment.
--local parsing doesn’t validate that a path argument is actually provided. If someone runs node scripts/sync-templates-repo.mjs --local (or passes another flag next), localPath becomes undefined and the script falls back to cloning/pushing unexpectedly. Add an explicit check when --local is present to require a non-empty path (and exit with a helpful error).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 15 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| git(["checkout", "-B", branch], targetDir); | ||
| git(["add", "-A"], targetDir); | ||
| git(["commit", "-m", `chore: sync templates from emdash v${emdashVersion}`], targetDir); |
There was a problem hiding this comment.
git push --force-with-lease will fail if the sync branch already exists on the remote (e.g., rerunning the workflow before the previous PR is merged), because the clone doesn’t have an up-to-date remote-tracking ref for that branch. Consider either fetching the remote branch first (so the lease can be evaluated), using a unique branch name per run, or explicitly handling the “branch already exists” case so reruns update the existing branch reliably.
| git(["commit", "-m", `chore: sync templates from emdash v${emdashVersion}`], targetDir); | |
| git(["commit", "-m", `chore: sync templates from emdash v${emdashVersion}`], targetDir); | |
| let remoteBranchExists = false; | |
| try { | |
| git(["ls-remote", "--exit-code", "--heads", "origin", branch], targetDir); | |
| remoteBranchExists = true; | |
| } catch { | |
| remoteBranchExists = false; | |
| } | |
| if (remoteBranchExists) { | |
| git( | |
| [ | |
| "fetch", | |
| "origin", | |
| `refs/heads/${branch}:refs/remotes/origin/${branch}`, | |
| ], | |
| targetDir, | |
| ); | |
| } |
scripts/sync-templates-repo.mjs
Outdated
| const prUrl = execFileSync( | ||
| "gh", | ||
| [ | ||
| "pr", | ||
| "create", | ||
| "--repo", | ||
| REPO, | ||
| "--title", | ||
| `chore: sync templates from emdash v${emdashVersion}`, | ||
| "--body", | ||
| prBody, | ||
| ], | ||
| { encoding: "utf8", stdio: "pipe", cwd: targetDir }, | ||
| ).trim(); | ||
|
|
||
| console.log(`PR: ${prUrl}`); |
There was a problem hiding this comment.
gh pr create will error if a PR already exists for this branch (common when the sync job is rerun). Consider detecting an existing PR for the head branch (e.g., gh pr view / gh pr list --head) and reusing its URL instead of failing the workflow.
| const prUrl = execFileSync( | |
| "gh", | |
| [ | |
| "pr", | |
| "create", | |
| "--repo", | |
| REPO, | |
| "--title", | |
| `chore: sync templates from emdash v${emdashVersion}`, | |
| "--body", | |
| prBody, | |
| ], | |
| { encoding: "utf8", stdio: "pipe", cwd: targetDir }, | |
| ).trim(); | |
| console.log(`PR: ${prUrl}`); | |
| const existingPrs = JSON.parse( | |
| execFileSync( | |
| "gh", | |
| [ | |
| "pr", | |
| "list", | |
| "--repo", | |
| REPO, | |
| "--head", | |
| branch, | |
| "--state", | |
| "open", | |
| "--json", | |
| "url", | |
| ], | |
| { encoding: "utf8", stdio: "pipe", cwd: targetDir }, | |
| ), | |
| ); | |
| const prUrl = | |
| existingPrs[0]?.url ?? | |
| execFileSync( | |
| "gh", | |
| [ | |
| "pr", | |
| "create", | |
| "--repo", | |
| REPO, | |
| "--title", | |
| `chore: sync templates from emdash v${emdashVersion}`, | |
| "--body", | |
| prBody, | |
| ], | |
| { encoding: "utf8", stdio: "pipe", cwd: targetDir }, | |
| ).trim(); | |
| console.log(existingPrs[0] ? `PR already exists: ${prUrl}` : `PR: ${prUrl}`); |
- Fetch remote branch before force-with-lease so the lease has a ref - Check for existing open PR before creating a new one
What does this PR do?
Cloudflare templates were missing
src/worker.tsand the"main"field inwrangler.jsonc, so the custom worker entrypoint (Astro handler + PluginBridge re-export) was never used. Also addsworker-configuration.d.tsto the three cloudflare templates that were missing it.Adds
scripts/sync-templates-repo.mjsto automatically sync templates to the standaloneemdash-cms/templatesrepo with resolved dependency versions (workspace:*→^0.1.1,catalog:→ real versions). A newsync-templatesworkflow runs after changesets publishes or on manual dispatch.Also fixes
sync-cloudflare-templates.shto use rsync with--exclude=worker.tsso it preserves cloudflare-specific files when copyingsrc/from base templates.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runpnpm locale:extracthas been run (if applicable)AI-generated code disclosure
Screenshots / test output
Dry-run output of
sync-templates-repo.mjs --dry-run --local ../emdash-templates: