Skip to content

fix(ci): publish workspaces one at a time, skipping what is already out - #8

Merged
qwlong merged 1 commit into
mainfrom
fix/publish-skip-existing
Aug 18, 2026
Merged

qwlong merged 1 commit into
mainfrom
fix/publish-skip-existing

Conversation

@barney-ws

Copy link
Copy Markdown
Collaborator

Problem

Every release since v0.10.3 has reported failure while landing about half of itself.

v0.10.3 v0.10.4 v0.10.5
git tag
GitHub release
CHANGELOG entry
@dorval/core, dio, custom on npm
dorval (the CLI) on npm

dorval on npm is still 0.10.2, published 2026-01-21. Anyone running npm i dorval today gets the January build — none of the enum fixes from #6, none of the date-format fixes from #7.

The workflow run says failure with no indication of which half went out. From the log of the v0.10.5 run:

npm notice filename: dorval-core-0.10.5.tgz          <- @dorval/* went out fine
npm error code E403
npm error 403 403 Forbidden - PUT https://registry.npmjs.org/dorval
          - You may not perform that action with these credentials.
[semantic-release] › ✘  Failed step "publish" of plugin "@semantic-release/exec"

Root cause, in two parts.

The 403 itself is a credentials problem, not a code problem. The same secrets.NPM_TOKEN publishes all three @dorval/* packages successfully and is refused on the unscoped dorval. Both dorval and @dorval/core list the same owner (wildstorm), so this is not a missing maintainer — it is a granular access token whose granted packages cover the @dorval scope but not the separate, unscoped dorval package name. That part cannot be fixed from this repo, see the checklist below.

Everything else about the failure is this repo's doing. publishCmd was:

npm publish --workspaces --access public

One command for all four packages, and it stops at the first one it cannot publish. So a single package the token has no rights to:

  • takes down whatever would have been published after it — this time dorval happened to sort last, which is the only reason dio and custom got out at all;
  • leaves a tag and a GitHub release pointing at a version npm does not have;
  • reports one opaque failure, with the published/not-published split buried in the log;
  • and cannot be recovered by re-running, because the packages that did publish now make the same command fail on "you cannot publish over an existing version".

Change

scripts/publish-workspaces.sh, called from both publish paths. Per package:

  • skip it if that exact name@version is already on the registry;
  • otherwise publish it, and record the outcome rather than aborting the loop;
  • print published / skipped / failed at the end, and exit non-zero if anything failed, so a real problem still fails the build.

@dorval/core is published first explicitly — the CLI depends on it, and the old code relied on how the glob happened to sort. private: true packages are skipped.

Skipping what is already out is what makes a re-run useful: once the token covers dorval, re-running picks up only the package that was missed.

manual-publish.yml now calls the same script. Its per-package steps had drifted from the semantic-release path and from each other — a failure from dio or custom was tolerated with || echo, the same failure from core or the CLI was fatal.

Tests

Registry reads are real here, so the skip logic was verified against the live registry (nothing was published — npm publish only ran under --dry-run):

$ DRY_RUN=1 bash scripts/publish-workspaces.sh
-- @dorval/core@0.10.5 is already on the registry, skipping
-- @dorval/custom@0.10.5 is already on the registry, skipping
-- @dorval/dio@0.10.5 is already on the registry, skipping
-- publishing dorval@0.10.5 (tag: latest)

published: dorval@0.10.5
skipped:   @dorval/core@0.10.5 @dorval/custom@0.10.5 @dorval/dio@0.10.5
failed:    none

That is exactly the recovery shape wanted: the three packages already out are left alone, and the one missing package is the only thing attempted.

packages/core: 450 tests pass. .releaserc.json and both workflow files validated as JSON/YAML.

What is not verified: real publishing. I have no credentials for this registry, so the npm publish call itself was only exercised with --dry-run. The ordering, the skip decision, the failure collection and the exit code are verified; the authenticated upload is not.

To actually get the CLI published

This PR stops the failure from being silent and makes recovery possible. It does not grant permissions — that step is manual:

  • npmjs.com → Access Tokens → the token behind secrets.NPM_TOKENPackages and scopes → add the dorval package (a token scoped to @dorval does not carry it). Alternatively use a token with write access to both.
  • Then publish the missing CLI build, either by re-running Manual Publish (Backup) with version 0.10.5, or locally with npm login && bash scripts/publish-workspaces.sh — the three @dorval/* packages will be skipped and only dorval@0.10.5 goes out.

Notes

v0.10.3 has a tag and a CHANGELOG entry but nothing on npm at all — the @dorval/* packages jump straight from 0.10.2 to 0.10.4. Worth deciding whether to leave that gap or re-tag; this PR does not touch it.

The GitHub release for v0.10.3 and v0.10.4 is also missing while v0.10.5 has one, which fits the publish step dying before @semantic-release/github on those two runs. Nothing here changes plugin order, so that resolves on its own once publishing stops failing.

`npm publish --workspaces` stops at the first package it cannot publish, so
a token whose granted packages do not cover the unscoped `dorval` took the
whole release down with it: v0.10.3, v0.10.4 and v0.10.5 each got a tag, a
GitHub release and their @dorval/* packages, while the CLI stayed at 0.10.2
on npm and the workflow reported failure with no indication of which half
had landed.

Publish each package on its own, skip versions already on the registry, and
report published/skipped/failed at the end. A real problem still fails the
build, but a re-run now picks up whatever was missed instead of tripping over
the packages that did go out.

Also points manual-publish at the same script - its per-package steps had
drifted, tolerating a failure from dio and custom but not from core or the CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@barney-ws barney-ws added the bug Something isn't working label Aug 17, 2026
@qwlong
qwlong merged commit 39f42fd into main Aug 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants