You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: harden publish for trusted publishing + stranded tags (#101)
## Summary
Two related fixes for publish failures hit when releasing brand-new
packages from GitHub Actions with npm trusted publishing (OIDC).
Reported by a downstream project (varlock) where publishing
`@varlock/kubernetes-plugin@0.1.0` failed because the package didn't
exist on npm yet, leaving a stranded GH draft release + remote tag that
then broke the retry's `git push --tags`.
### 1. Detect new-package + OIDC-only auth before any side effects
Trusted publishing can't bootstrap a package that doesn't exist on npm —
the trusted publisher config has to be created on npmjs.com first, which
requires the package to already exist. We now check the registry up
front and emit a specific, actionable error directing the user to
publish a `0.0.0` placeholder first.
The check is gated on `willUseOidcExclusively` (OIDC env detected AND no
`NPM_TOKEN`/`NODE_AUTH_TOKEN` AND no `.npmrc` auth) to avoid false
positives for users who enable `id-token: write` for provenance
attestations alongside token auth. Runs before `gh release create` and
`npm publish`, so a failing check leaves no stranded state. On
`--dry-run` it warns instead of erroring.
### 2. Per-tag force push replaces blanket `git push --tags`
`gh release create --draft --target SHA` creates the tag on the remote
at draft-creation time. If a prior publish failed and HEAD has since
moved, the remote tag is stale and `git push --tags` rejects with
"already exists". The fix iterates `releasePlan.releases` minus
`result.failed` and force-pushes each tag individually.
Preserves the existing anySucceeded-aware semantics used for local tag
movement: packages whose targets all succeeded in a prior run are
stripped upstream by the `alreadyPublished` filter, so their tags stay
at the SHA the artifact was actually published from — the GitHub release
continues to point at the right commit. Mixed-success scenarios (e.g.
package A published from SHA1 in run 1, package B retried from SHA2 in
run 2) end up correct: A's tag is left at SHA1, only B's tag is
force-pushed to SHA2.
## Test plan
- [x] `bun run check` passes (lint + format + typecheck)
- [x] `bun run test` — 258/258 pass
- [ ] Verify in downstream project (varlock) that the new error fires on
the OIDC + new-package case
- [ ] Verify a clean retry after publishing a `0.0.0` placeholder
succeeds and pushes tags without rejection
---------
Co-authored-by: CI <ci@example.com>
Harden the publish flow for two failure modes hit when releasing brand-new packages via GitHub Actions + npm trusted publishing (OIDC).
6
+
7
+
- Detect the new-package case before any side effects. When OIDC is the only available auth path (no `NPM_TOKEN`/`NODE_AUTH_TOKEN`, no `.npmrc` auth), bumpy now checks the npm registry up front and emits a clear error directing the user to publish a `0.0.0` placeholder before merging — instead of failing partway through with stranded GitHub draft releases and remote tags. The check is skipped when a token fallback is present, so users who enable `id-token: write` for provenance attestations alongside token auth are unaffected.
8
+
- Replace blanket `git push --tags` after publish with per-tag force push. `gh release create --draft --target SHA` creates the tag on the remote at draft-creation time; if a prior publish failed and HEAD has since moved, the remote tag is stale and `git push --tags` rejects with "already exists". The new logic iterates `releasePlan.releases` minus failed packages and force-pushes each tag individually, preserving the anySucceeded-aware semantics already used for local tag movement — packages whose targets all succeeded in a prior run are stripped upstream and their tags stay at the SHA the artifact was actually published from.
0 commit comments