feat(plugin-cli): integrate delegated releases with Changesets - #3093
Conversation
🦋 Changeset detectedLatest commit: ecc2373 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
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 |
Scope checkThis 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. |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-test
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@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-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| 🔵 In progress View logs |
docs | 69f9cb0 | Sep 13 2026, 07:46 AM |
🚀 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 deploymentPreview Deployments by commit
|
There was a problem hiding this comment.
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:144This 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:20The 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.
There was a problem hiding this comment.
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'] }} |
There was a problem hiding this comment.
[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.
| 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`. |
There was a problem hiding this comment.
[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.
| 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'] }} |
There was a problem hiding this comment.
[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.
| 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'] }} |
There was a problem hiding this comment.
[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.
| 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. |
There was a problem hiding this comment.
[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.
| 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. |
There was a problem hiding this comment.
[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.
| 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 ... |
|
The v1/v2 distinction in the docs is intentional and matches the current upstream contracts:
Both examples normalize that step output to the generated reusable workflow's ~ 🤖 Codex |
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 setupdetects a root.changeset/config.jsonand asks how EmDash plugins should be released: follow Changesets releases, follow<slug>@<version>tags, or run manually.--trigger auto|changesets|tags|manualmakes non-interactive behavior deterministic.emdash-plugin release plan --published-packages <json>maps npm package names toemdash-plugin.jsoncslugs, ignores ordinary npm packages, validates reported versions, and emits the plugin matrix. This supports mixed monorepos and package names that differ from plugin IDs.Discussion: #1590
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output
Verified locally:
git diff --check.