agentsclimarketplace

Cut a release

Skill strapi-community/plugin-rest-cache/.claude/skills/cut-a-release

Publish this plugin to npm - a production release, a pre-release, or an experimental build from a branch. Use when preparing or shipping a release, updating the changelog, or debugging the publish workflow.From its SKILL.md

Install
npx -y skills add strapi-community/plugin-rest-cache --skill cut-a-release

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

8.6 KB, as published. Nobody here has run it

Cutting a release

Publishing is always manual. There is no trigger that publishes on merge, and there must never be one. Two humans gates stand in front of npm: pressing "Publish release" on a draft, and approving the deployment environment.

The pieces

release-please.ymlWatches main, maintains one rolling release PR, and creates draft GitHub Releases. Publishes nothing.
publish.ymlThe only thing that talks to npm. Never fires on merge.
release-please-config.jsonChangelog sections, package list, draft: true.
.release-please-manifest.jsonCurrent version per package. release-please owns this; do not hand-edit.

Production release

  1. Merge work to main using conventional commits. release-please keeps a chore: release X.Y.Z PR up to date, with the changelog grouped by change type.
  2. Review that PR. The version comes from the commit types since the last release — feat gives a minor, fix a patch, ! or a BREAKING CHANGE: footer a major. If the proposed version is wrong, the commit messages are wrong; fix them rather than editing the version.
  3. Merge it. release-please tags and creates draft Releases.
  4. Go to Releases, review, press Publish release.
  5. publish.yml starts and waits on the npm-latest environment. A maintainer approves. It packs and publishes to the latest tag.

The draft step is not ceremony: a Release created by GITHUB_TOKEN does not trigger other workflows, so an auto-published one would silently never reach npm.

Pre-release

release-please only ever proposes stable versions. A beta is not something it produces, and there is deliberately no prerelease mode in its config - that made every ordinary release a beta and left the repository unable to reach a stable version without a Release-As: override.

So a beta is a manual dispatch. Run the publish workflow from the Actions tab, with the branch selector on main:

  • mode: next
  • version: the exact version, e.g. 5.2.0-beta.0
  • target_branch: the branch to build, defaults to main

It stamps that version across all three packages, builds, and publishes to the next dist-tag through the npm-prerelease environment. Users install it with npm install @strapi-community/plugin-rest-cache@next.

version must carry a prerelease suffix. scripts/stamp-version.mjs refuses a stable version, so no dispatch can put one on npm - stable versions come only from release-please and the version already committed to package.json.

Nothing is tagged and no GitHub Release is created: a dispatched beta is a build of a branch, not a point in the release history. The run summary records the commit.

If a GitHub Release is marked as a pre-release and published, that also routes to next - publish.yml reads github.event.release.prerelease. That path publishes the version release-please committed, and never stamps over it.

Moving off a prerelease version

If the last release was a prerelease, release-please will not graduate on its own. The default versioning strategy keeps the suffix, so 5.1.0-beta plus a fix: becomes 5.1.1-beta, not 5.1.0.

Land a commit on main whose message carries the version as a footer:

chore: graduate to a stable release

Release-As: 5.1.0

It applies to that one release and leaves nothing behind in the config.

Note where the footer has to be. GitHub builds a squash commit from the pull request title and body only when the branch has more than one commit; with a single commit it uses that commit's own message. A footer written in the pull request description is silently dropped in that case, and the next release comes out as another prerelease.

Experimental build from a pull request

The easy path. Add the publish-experimental label to a pull request. It publishes 0.0.0-experimental.<pr head sha> to the experimental dist-tag and comments on the PR with the exact install command.

The workflow then removes the label again, including when the publish failed. The label is a one-shot request, not a mode: to cut another build after pushing more commits, add it back. It deliberately no longer fires on synchronize, which used to republish silently on every push for as long as the label happened to still be attached.

Same-repo pull requests only; a fork PR never reaches the publish job.

Experimental build from a branch

When there is no pull request, or you want a specific branch.

Run the publish workflow from the Actions tab:

  • keep the branch selector on main — this matters, see below;
  • mode: experimental;
  • target_branch: the branch to build.

It publishes 0.0.0-experimental.<sha> to the experimental dist-tag. Install by exact version, not by tag — the tag is clobbered by the next experimental build:

npm install @strapi-community/[email protected].<sha>

Why the branch selector must stay on main, and why the PR trigger is pull_request_target

npm trusted publishing is not branch-scoped. It checks the repository, the workflow filename and the environment, and discards the ref. Since workflow_dispatch runs the workflow file from the branch you pick, choosing a feature branch would hand that branch's own YAML a publish-capable token — and npm publish defaults to --tag latest.

The label trigger has the same problem in a different shape: plain pull_request would run the PR branch's copy of the workflow, with publish credentials in scope. That is what Strapi's own experimental workflow does, and it is a real accepted risk on their side. This one uses pull_request_target, which runs the workflow file as it exists on the default branch, so neither the environment gate nor the version assertion can be edited by the branch being published.

The usual pull_request_target trap - checking out untrusted code in a job holding secrets - does not apply here, because the build job holds neither secrets nor an id-token.

So the workflow takes the branch as an input instead. The build job checks it out but has no id-token and no environment; the publish job runs from main's YAML and asserts the version matches 0.0.0-experimental.<40 hex> before publishing. Both halves are needed: without the assertion, a poisoned build could stamp 5.99.0, which every ^5.0.0 range would resolve regardless of dist-tag.

Things that will break a release

Everything publishing lives in publish.yml and must stay there. npm validates the workflow filename and allows only one trusted-publisher config per package, so a second publishing workflow file would fail authentication.

Never npm publish from a package directory. npm does not understand the workspace: protocol and would publish workspace:* literally, producing a manifest nobody can install. The workflow runs pnpm pack first and publishes the tarball. The plugin depends on the memory provider this way, so this affects the primary package.

The build must run before packing, and in order. The providers typecheck against the plugin's emitted declarations, so pnpm run build builds the plugin first, then the providers. files is ["dist"] — an unbuilt package publishes an empty directory.

Node 24 or newer in the publish job. Trusted publishing needs npm ≥ 11.5.1 and no Node 22 release ships one.

If a publish fails

  • Waiting for approval — expected. Someone with reviewer rights approves the deployment.
  • 401/403 from npm — the trusted publisher config on npmjs.com does not match this workflow's filename or environment name. Both are exact, case-sensitive strings.
  • ERESOLVE installing an experimental build — the internal peer ranges were not rewritten. scripts/stamp-experimental-version.mjs pins them to the exact experimental version because a ^5.x peer cannot satisfy 0.0.0-*.
  • Version assertion failed — the build produced a version that does not match the mode. Treat this as a real signal, not a check to relax.

Never

  • Add a trigger that publishes on a push to a branch, or on a merge. The labelled-PR trigger is deliberate and only ever produces 0.0.0-experimental builds; nothing may reach the latest tag without a human pressing publish.
  • Change the PR trigger from pull_request_target to pull_request.
  • Give the build job id-token: write, an environment, or any secret.
  • Remove the version-shape assertion.
  • Add a second workflow file that publishes.

Keep looking

Skills are one crate of 628,853. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.