Skip to content

feat(plugin-cli): integrate delegated releases with Changesets - #3093

Merged
ascorbic merged 3 commits into
mainfrom
codex/changesets-plugin-releases
Sep 13, 2026
Merged

feat(plugin-cli): integrate delegated releases with Changesets#3093
ascorbic merged 3 commits into
mainfrom
codex/changesets-plugin-releases

Conversation

@ascorbic

@ascorbic ascorbic commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds an optional native Changesets handoff to the repository-level delegated plugin release workflow introduced in #3081.

  • emdash-plugin release setup detects a root .changeset/config.json and asks how EmDash plugins should be released: follow Changesets releases, follow <slug>@<version> tags, or run manually. --trigger auto|changesets|tags|manual makes non-interactive behavior deterministic.
  • The Changesets variant is a reusable workflow that accepts the official Changesets Action published-package JSON after its publish step succeeds. It does not infer releases from Git diffs or change how Changesets versions and publishes packages.
  • emdash-plugin release plan --published-packages <json> maps npm package names to emdash-plugin.jsonc slugs, ignores ordinary npm packages, validates reported versions, and emits the plugin matrix. This supports mixed monorepos and package names that differ from plugin IDs.
  • Private EmDash-only packages produce a setup warning unless Changesets versions and tags them. Unrelated private packages can remain ignored.
  • Generated workflows retain manual releases, use per-job permissions, disable checkout credential persistence, queue overlapping releases, and pass zizmor with the configurable first-party Action reference documented.
  • The CLI docs, delegated-release guide, technical specification, repository and generated skills, Action README, dashboard setup copy, and changeset explain the automatic and manual Changesets Action v1/v2 setup.

Discussion: #1590

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • New features link to an approved Discussion: RFC: Attested Automated Publishing #1590
  • I have included screenshots below if this PR changes the UI

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenAI Codex (GPT-5)

Screenshots / test output

Publisher dashboard explaining Changesets, package-tag, and manual release sources

Verified locally:

  • root package build and typecheck;
  • type-aware lint and formatting;
  • 464 plugin CLI tests;
  • release-service application/UI typechecks and 14 UI tests;
  • documentation build;
  • four Playwright browser journeys, including Arabic mobile RTL;
  • generated reusable Changesets workflow with zizmor pedantic mode: no findings, one documented configurable first-party reference;
  • black-box mixed-monorepo planning from Changesets published-package JSON;
  • git diff --check.

Publisher dashboard explaining Changesets, package-tag, and manual release sources

@changeset-bot

changeset-bot Bot commented Sep 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecc2373

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@emdash-cms/plugin-cli Minor
@emdash-cms/plugin-test Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added area/docs size/XL review/needs-review No maintainer or bot review yet labels Sep 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 683 lines across 17 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.

@pkg-pr-new

pkg-pr-new Bot commented Sep 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@3093

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@3093

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@3093

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@3093

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@3093

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@3093

emdash

npm i https://pkg.pr.new/emdash@3093

create-emdash

npm i https://pkg.pr.new/create-emdash@3093

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@3093

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@3093

@emdash-cms/plugin-test

npm i https://pkg.pr.new/@emdash-cms/plugin-test@3093

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@3093

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@3093

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@3093

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@3093

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@3093

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@3093

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@3093

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@3093

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@3093

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@3093

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@3093

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@3093

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@3093

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@3093

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@3093

commit: ecc2373

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
🔵 In progress
View logs
docs 69f9cb0 Sep 13 2026, 07:46 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 13, 2026

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://codex-changesets-plugin-releases.try.emdashcms.com, https://codex-changesets-plugin-releases-emdash-playground.emdash-cms.workers.dev (commit ecc2373)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://4faf246b.try.emdashcms.com, https://4faf246b-emdash-playground.emdash-cms.workers.dev ecc2373 2026-09-13T07:55:00.358Z Visit the dashboard ↗
  • Build: In progress 🔵

View logs ↗
69f9cb0 2026-09-13T07:46:33.817Z View logs ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://873214c8.try.emdashcms.com, https://873214c8-emdash-playground.emdash-cms.workers.dev 55c3341 2026-09-13T07:13:57.511Z Visit the dashboard ↗

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a well-scoped, additive feature that wires an optional Changesets trigger into the delegated plugin-release workflow. The approach is sensible: release setup detects a root .changeset/config.json, resolves an explicit/auto trigger, and emits either a Changesets-aware matrix workflow, a tag workflow, or a manual-only workflow. The new release plan --since logic uses plain git diff to detect plugin package.json version changes, which keeps the CLI from depending on Changesets internals and naturally maps changed npm package directories to EmDash plugin slugs. Generated workflows follow the project’s security conventions—top-level or per-job permissions, persist-credentials: false, pinned third-party Actions, a documented first-party Action ref, and workflow-level concurrency. The UI string in PublisherPage.tsx is correctly wrapped for Lingui.

I found two documentation gaps where the updated READMEs still describe the tag trigger as the only way to start releases, contradicting the new Changesets and manual options. The implementation itself looks clean, and the added test coverage for Changesets detection, the matrix workflow, and the planning logic is appropriate.

Headline: merge after updating the two stale README sentences.


Findings

  • [needs fixing] packages/plugin-cli/README.md:144

    This paragraph still tells the reader to start a release by pushing a <slug>@<version> tag, but the same README now documents Changesets and manual triggers. The sentence is a false general claim after the feature change.

    Start the release using the trigger selected during setup: merge a Changesets version pull request, push a package tag such as `gallery@1.2.3`, or run the workflow from GitHub Actions. The service verifies that the signed `gallery` profile names the GitHub repository, then the first run for that ref scope waits and adds a repository-approval link to the GitHub job summary. Open that link, check the repository, workflow file, branch or tag, and environment, then confirm the connection. The same run continues after confirmation.
    
  • [needs fixing] apps/release-action/README.md:20

    The preceding paragraph was updated to say the workflow can also start from Changesets version updates or manual selections, but this paragraph still opens with tag-only instructions. Update it to match the three trigger modes.

    Start the workflow by merging a Changesets version pull request, pushing a package tag such as `gallery@1.2.3`, or running the workflow manually from GitHub Actions. The service checks that the signed package profile names the GitHub repository before creating a connection request. The Action writes an approval link to the job summary and waits. Open the link, sign in to the release service, and check the repository, workflow file, branch or tag, and environment reported by GitHub. After confirmation, the same Action run requests a fresh OIDC token and submits the release. Later packages reuse approved tag and branch scopes when their signed profiles name the same repository.
    

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Sep 13, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Sep 13, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Sep 13, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a re-review of the Changesets-aware delegated plugin release feature. The two stale README sentences from the prior review have been updated, and the PR now consistently documents the three release triggers across the Action README, plugin CLI README, release-service UI, user docs, technical spec, and skills.

The approach remains sound: release setup detects .changeset/config.json, resolves or prompts for a trigger, and emits either a reusable Changesets-aware workflow or the existing tag/manual workflow. The new release plan --published-packages path validates the Changesets JSON output, maps npm package names to EmDash plugin slugs, rejects version mismatches, and emits a matrix selector. Generated workflows continue to pin third-party Actions, disable checkout credential persistence, queue releases with concurrency, and scope permissions per job for the Changesets variant.

However, the new documentation and generated template contain a recurring false claim about the upstream Changesets Action output name. The project itself consumes steps.changesets.outputs.publishedPackages in .github/workflows/release.yml (which uses changesets/action@v1.9.0), and @changesets/action exposes the published-package list as publishedPackages in both v1 and v2. The new READMEs, docs, and generated plugin README claim that v2 renamed this output to published-packages. If a publisher copies those snippets, the published-packages input to the generated reusable workflow will be undefined, and no plugin matrix will be produced. The correct pattern is to keep the published-packages job output name for normalization, but read from steps.changesets.outputs.publishedPackages.

Fix the output name in the docs/template, and this is ready to merge.

# Keep the existing runner, permissions, and steps.
outputs:
published: ${{ steps.changesets.outputs.published }}
published-packages: ${{ steps.changesets.outputs['published-packages'] }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The v2 caller example reads the Changesets step output as steps.changesets.outputs['published-packages'], but @changesets/action exposes the published-package list as publishedPackages (camelCase) — the same output this repository already uses in .github/workflows/release.yml. Copying this snippet will pass an undefined value into the EmDash reusable workflow. Keep the published-packages job output name, but read from the upstream camelCase output.

Suggested change
published-packages: ${{ steps.changesets.outputs['published-packages'] }}
published-packages: ${{ steps.changesets.outputs.publishedPackages }}

The generated workflow resolves `<slug>@<version>` package tags to a unique plugin manifest, builds that package, creates signed GitHub build provenance, and publishes it. Manual runs accept a plugin ID and use its manifest version. Private and internal GitHub repositories are not supported because their attestations use a private Sigstore trust root that the release verifier does not trust.
With the default `--trigger auto`, setup detects a valid `.changeset/config.json` at the Git repository root. Interactive setup asks whether EmDash should follow Changesets releases, package tags, or manual runs. Non-interactive setup selects Changesets when detected and package tags otherwise.

The Changesets variant is a reusable workflow. Pass the existing Changesets Action `published-packages` output to it from a dependent job. It maps npm package names to `emdash-plugin.jsonc` slugs, ignores ordinary packages, verifies reported versions, and publishes matching plugins as a matrix. Changesets Action v1 names the step output `publishedPackages`; v2 names it `published-packages`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This sentence claims that Changesets Action v2 renamed the step output to published-packages. The upstream action uses publishedPackages in both v1 and v2; the kebab-case name does not exist. Reword so publishers map the existing camelCase step output to a published-packages job output.

Suggested change
The Changesets variant is a reusable workflow. Pass the existing Changesets Action `published-packages` output to it from a dependent job. It maps npm package names to `emdash-plugin.jsonc` slugs, ignores ordinary packages, verifies reported versions, and publishes matching plugins as a matrix. Changesets Action v1 names the step output `publishedPackages`; v2 names it `published-packages`.
The Changesets variant is a reusable workflow. Pass the existing Changesets Action `publishedPackages` step output to it from a dependent job (expose it as a `published-packages` job output). It maps npm package names to `emdash-plugin.jsonc` slugs, ignores ordinary packages, verifies reported versions, and publishes matching plugins as a matrix.

# Keep the existing runner, permissions, and steps.
outputs:
published: ${{ steps.changesets.outputs.published }}
published-packages: ${{ steps.changesets.outputs['published-packages'] }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The canonical docs repeat the incorrect v2 step-output name. The Changesets Action output is publishedPackages in both v1 and v2, so this expression evaluates to undefined. Normalize it to a published-packages job output, but read from publishedPackages.

Suggested change
published-packages: ${{ steps.changesets.outputs['published-packages'] }}
published-packages: ${{ steps.changesets.outputs.publishedPackages }}

# Keep the existing runner, permissions, and steps.
outputs:
published: ${{ steps.changesets.outputs.published }}
published-packages: ${{ steps.changesets.outputs['published-packages'] }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This v2 example reads a non-existent published-packages step output from changesets/action. The actual output is publishedPackages; use that as the source for the normalized job output.

Suggested change
published-packages: ${{ steps.changesets.outputs['published-packages'] }}
published-packages: ${{ steps.changesets.outputs.publishedPackages }}

For GitHub Actions, run `emdash-plugin release setup` from one plugin package. It prepares that package profile and creates one shared `.github/workflows/emdash-release.yml` at the Git repository root. The first `<slug>@<version>` tag requests approval for the repository workflow through GitHub OpenID Connect. A manual run requests approval the first time its branch is used; confirmation adds that scope without replacing approved tags. Later packages reuse approved scopes when their signed profiles name the same repository. Prepare each package with `emdash-plugin profile setup --dir <package-directory>`.
For GitHub Actions, run `emdash-plugin release setup` from one plugin package. It prepares that package profile and creates one shared `.github/workflows/emdash-release.yml` at the Git repository root. When `.changeset/config.json` exists, setup offers **Follow Changesets releases**. The generated reusable workflow accepts the Changesets Action published-package JSON, maps package names to plugin slugs, and publishes matching plugins at the same versions. Otherwise, package tags use `<slug>@<version>`. Select explicitly with `--trigger changesets|tags|manual`.

To connect Changesets manually, expose its `published` and published-package step outputs from the existing release job, then call `./.github/workflows/emdash-release.yml` from a dependent job when `published == 'true'`. Changesets Action v1 uses `publishedPackages`; v2 uses `published-packages`. Private EmDash-only packages require `privatePackages.version: true` and `privatePackages.tag: true`. Read [Publishing](./references/publishing.md) for the complete caller blocks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The prose misidentifies the Changesets Action output name for v2. Both v1 and v2 expose publishedPackages. Rephrase to point publishers at the correct upstream output.

Suggested change
To connect Changesets manually, expose its `published` and published-package step outputs from the existing release job, then call `./.github/workflows/emdash-release.yml` from a dependent job when `published == 'true'`. Changesets Action v1 uses `publishedPackages`; v2 uses `published-packages`. Private EmDash-only packages require `privatePackages.version: true` and `privatePackages.tag: true`. Read [Publishing](./references/publishing.md) for the complete caller blocks.
To connect Changesets manually, expose its `published` and `publishedPackages` step outputs from the existing release job, then call `./.github/workflows/emdash-release.yml` from a dependent job when `published == 'true'`. Private EmDash-only packages require `privatePackages.version: true` and `privatePackages.tag: true`. Read [Publishing](./references/publishing.md) for the complete caller blocks.

## Publishing

Use the local publish script for a release started from this computer. Use the release-setup script for GitHub Actions. The first automated release connects the repository workflow; later packages reuse it only when their signed profiles name the same repository.
Use the local publish script for a release started from this computer. Use the release-setup script for GitHub Actions. Setup detects a root Changesets configuration and offers to follow packages released by Changesets; otherwise it uses package tags. Connect the generated reusable workflow to the existing Changesets publish job by passing its published-package output. Changesets Action v1 names the step output \`publishedPackages\`; v2 names it \`published-packages\`. Expose it as a \`published-packages\` job output and pass it to the generated workflow from a dependent job when Changesets reports \`published == 'true'\`. The first automated release connects the repository workflow; later packages reuse it only when their signed profiles name the same repository.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The generated plugin README template also tells users that Changesets Action v2 uses a published-packages step output. Because every newly scaffolded plugin ships this README, this propagates the same broken caller snippet. Correct it to the upstream publishedPackages output.

Suggested change
Use the local publish script for a release started from this computer. Use the release-setup script for GitHub Actions. Setup detects a root Changesets configuration and offers to follow packages released by Changesets; otherwise it uses package tags. Connect the generated reusable workflow to the existing Changesets publish job by passing its published-package output. Changesets Action v1 names the step output \`publishedPackages\`; v2 names it \`published-packages\`. Expose it as a \`published-packages\` job output and pass it to the generated workflow from a dependent job when Changesets reports \`published == 'true'\`. The first automated release connects the repository workflow; later packages reuse it only when their signed profiles name the same repository.
... Changesets Action exposes the step output as `publishedPackages`; expose it as a `published-packages` job output and pass it to the generated workflow from a dependent job when Changesets reports `published == 'true'`. The first automated release connects ...

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Sep 13, 2026
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-rereview Author pushed changes since the last review labels Sep 13, 2026
@ascorbic

Copy link
Copy Markdown
Collaborator Author

The v1/v2 distinction in the docs is intentional and matches the current upstream contracts:

  • this repository's pinned changesets/action@v1.9.0 exposes publishedPackages, as used by .github/workflows/release.yml;
  • Changesets Action v2.1.2 defines the output as published-packages in action.yml, and its current README documents the same kebab-case name.

Both examples normalize that step output to the generated reusable workflow's published-packages job input. No change is needed for this finding.

~ 🤖 Codex

@ascorbic
ascorbic merged commit ea2ccd5 into main Sep 13, 2026
68 checks passed
@ascorbic
ascorbic deleted the codex/changesets-plugin-releases branch September 13, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs cla: signed review/awaiting-author Reviewed; waiting on the author to respond size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant