cloudflare / cloudflare/workers-sdk
Release recovery: retain pending non-npm deployments after tags are created
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 186
Description
### What versions & operating system are you using?
Current `main` release tooling at commit `6bd7b6cae44d441e415130991e3f181694bd3b6d`, reviewed September 10, 2026. This concerns repository CI rather than an end-user Wrangler installation.
### Please provide a link to a minimal reproduction
The relevant handoff is in [changesets.yml](https://github.com/cloudflare/workers-sdk/blob/6bd7b6cae44d441e415130991e3f181694bd3b6d/.github/workflows/changesets.yml#L65-L105) and [deploy-non-npm-packages.ts](https://github.com/cloudflare/workers-sdk/blob/6bd7b6cae44d441e415130991e3f181694bd3b6d/tools/deployments/deploy-non-npm-packages.ts#L20-L53).
The repository's [automated triage reproduction](https://github.com/cloudflare/workers-sdk/issues/15582#issuecomment-5609837853) modeled a first run that created tags and a rerun of the same commit. On the rerun, the custom publisher found nothing unpublished, `changeset tag` emitted no `New tag:` lines, Changesets Action supplied `publishedPackages=[]`, and the non-npm deployment loop did nothing and exited successfully. I could not inspect the Access-protected archive, so I treat the fixture results as the triage system's evidence rather than an independently rerun artifact.
I did independently verify an adjacent source-level failure in the pinned file: [`deployPackage()` ignores the return value from `spawnSync`](https://github.com/cloudflare/workers-sdk/blob/6bd7b6cae44d441e415130991e3f181694bd3b6d/tools/deployments/deploy-non-npm-packages.ts#L118-L134). A normal nonzero `pnpm ... deploy` exit is returned in `status`; it is not thrown into this `catch`. The function can therefore increment the deployed count and leave `deploymentErrors` empty after a child deployment failed. Combined with the attempt-local `publishedPackages` handoff, the next rerun has no package identity from which to resume that deployment.
### Describe the Bug
Please retain pending non-npm deployments across reruns and propagate a failed child deployment:
1. npm publication and `changeset tag` complete.
2. A non-npm deployment exits nonzero, or the job stops before it completes.
3. The same release commit is rerun after npm versions and tags exist.
4. The current attempt emits no new tags, so `publishedPackages` is empty and the pending deployment is skipped.
Expected: validate `spawnSync`'s `error`, `signal`, and nonzero `status`; record completion separately for each non-npm destination; and let a retry select incomplete deployments without minting another npm version or tag. A documented recovery command with an explicit package/version input would also make the operation recoverable.
### Please provide any relevant error logs
Two production histories show that the custom npm publisher already handles a different, pre-tag partial-publication case correctly:
- [August 20 attempt 1](https://github.com/cloudflare/workers-sdk/actions/runs/32397005665/attempts/1) tried five tier-0 versions. It published `@cloudflare/config@0.7.0`, `@cloudflare/workers-utils@0.34.0`, `create-cloudflare@2.72.2`, and `miniflare@5.20260820.0-alpha`; `@cloudflare/codemods@0.1.0` failed with npm E404. The script emitted `No tags were created; re-run this job to retry.` [Attempt 2](https://github.com/cloudflare/workers-sdk/actions/runs/32397005665/attempts/2), recorded with `edmundhung` as `triggering_actor`, published the missing package, continued through later tiers, and then created the full tag set. It repeated a 93-second full build after the first attempt's 89-second build; the second publish/tag step ran for 520 seconds.
- [August 13 attempt 1](https://github.com/cloudflare/workers-sdk/actions/runs/31697065220/attempts/1) published `create-cloudflare@2.72.0` and `miniflare@5.20260811.1-alpha`, then stopped after waiting 600 seconds for the latter to become registry-resolvable. [Attempt 2](https://github.com/cloudflare/workers-sdk/actions/runs/31697065220/attempts/2), recorded with `petebacondarwin` as `triggering_actor`, recognized that tier 0 had nothing left to publish, published later tiers, and created tags for the whole release. It repeated a 109-second build after the first attempt's 92-second build.
These runs demonstrate deliberate, idempotent recovery before tags. They do not demonstrate the reported post-tag non-npm failure. The post-tag case is supported by the automated fixture and the independently checked source behavior above.
I reviewed all 2,738 runs returned for this workflow from 2024-09-10 through 2026-09-10 using monthly API windows. Twenty-eight run IDs had more than one attempt. A repeated attempt alone does not prove a manual publication recovery, and retained step/log detail was not available for every older run.
### Responsibility boundary
The repository calls Changesets Action v1.9.0 with a custom tiered publisher that ends in `changeset tag`. Returning only packages tagged in the current attempt is the action's handoff behavior; it is useful for a fresh release. The unrecoverable state arises when the workflow treats that attempt-local list as the only record of downstream work, and when the custom deploy wrapper does not inspect the child's nonzero exit. The observed npm E404 and registry-propagation timeout are external/custom-publisher failures, not Changesets defects.
Contributor guide
Research direction
Start with .github/workflows/changesets.yml lines 65-105 and tools/deployments/deploy-non-npm-packages.ts lines 20-53 and 118-134. Trace how publishedPackages reaches the non-npm deployment loop, then inspect spawnSync handling. Done means failed or interrupted destinations remain selectable on a rerun, child failures are propagated, and npm versions or tags are not reminted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions, typescript
- Domain
- ci-cd, release
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100