publish-npm.yml is the right design — OIDC trusted publishing, provenance, an environment gate, per-package preview/latest tagging. It is also, as written, unable to succeed on its first real tag push, and if that first failure is fixed the obvious way it ships a 75-line shell over a real package.
Both come from the same three lines:
set -euo pipefail
for dir in packages/*/; do
...
( cd "$dir" && pnpm publish --access public --provenance --no-git-checks --tag "$tag" )
done
Bug 1 — the loop aborts on the first package, every time
The glob expands alphabetically, so packages/adk/ is always first. adk is at 1.0.14 locally and 1.0.14 on npm (published from elsewhere — see #42). npm rejects a republish of an existing version, pnpm publish exits non-zero, and under set -e a failing subshell in command position kills the whole job.
Three of the four packages that exist on npm are exact version collisions:
| Package |
local |
npm |
on pnpm publish |
adk |
1.0.14 |
1.0.14 |
collision → job dies here |
mcp-server |
0.1.8 |
0.1.8 |
collision |
workflow-sdk |
1.0.6 |
1.0.6 |
collision |
sdk |
3.0.0 |
2.0.14 |
no collision |
So git tag sdks-v… && git push --tags publishes nothing and reports failure — including the 45 packages that have never been published and genuinely should be.
I have not been able to run this: Actions is billing-locked org-wide (wave-rig#174), so the reasoning above is from the source and the registry, not from a run. The version numbers and the glob order are measured; the set -e abort is read from the script.
Bug 2 — the fix that suggests itself is worse than the bug
The natural repair is "skip a version that already exists." Do only that, and sdk becomes the first package to get through — and sdk here is 2 files, 75 lines, versioned 3.0.0, against a real @wave-av/sdk@2.0.14 with 16 versions behind it.
case "$ver" in 0.0.*) tag=preview sends only 0.0.x to preview. 3.0.0 is not 0.0.x, so it goes to latest. Every consumer of @wave-av/sdk would resolve to the shell on their next install, and semver would present it as the newest stable release.
This is the same failure wave-av/adk#64 was opened to prevent, in a second location and with a wider blast radius — adk#64 guards a fork nobody depends on; this guards the package with the most published history in the scope.
Suggested fix — skip collisions, but refuse regressions
Two independent conditions, because they fail differently. Skipping an already-published version is routine; publishing behind the registry is never intended and should stop the job.
set -euo pipefail
for dir in packages/*/; do
name=$(node -p "require('./${dir}package.json').name" 2>/dev/null) || continue
case "$name" in @wave-av/*) ;; *) continue ;; esac
ver=$(node -p "require('./${dir}package.json').version")
published=$(npm view "$name" version 2>/dev/null || echo "0.0.0")
# Already on the registry at this exact version: nothing to do. Not an error —
# a monorepo-wide tag will always include packages that did not change.
if [ "$ver" = "$published" ]; then
echo "skip $name@$ver — already published"
continue
fi
# Behind the registry: the local copy is not the source of truth for this package.
# Never let semver present it as newer. Hard fail — this needs a human.
newest=$(printf '%s\n%s\n' "$ver" "$published" | sort -V | tail -1)
if [ "$newest" != "$ver" ]; then
echo "::error::$name is $ver locally but npm serves $published — refusing to publish backwards (see #42)"
exit 1
fi
tag=latest; case "$ver" in 0.0.*) tag=preview ;; esac
echo "publishing $name@$ver --tag $tag"
( cd "$dir" && pnpm publish --access public --provenance --no-git-checks --tag "$tag" )
done
Note this deliberately still fails on sdk, because sdk@3.0.0 being a 75-line shell is a real problem that a workflow should not paper over. The guard turns a silent bad release into a loud stop.
One thing I got wrong in #42
I recommended "fix the repository fields first." For this repo that is already done — all 49 packages point at wave-av/sdks, which is exactly what provenance requires, and the workflow's (satisfied) comment is accurate. The wrong repository metadata is in the published artifacts, because they were built from a different source copy. Nothing to fix here.
Also worth confirming before arming this
The header says the workflow is inert until a Trusted Publisher is registered on npmjs.com for this repo + this workflow + the npm-publish environment. Worth confirming that is still unregistered — if it were registered, a tag push would already be live, and Bug 1 is the only thing that has been preventing a bad release.
Related: #42 (which repo publishes what), wave-av/adk#64 (same guard, adk fork).
publish-npm.ymlis the right design — OIDC trusted publishing, provenance, an environment gate, per-packagepreview/latesttagging. It is also, as written, unable to succeed on its first real tag push, and if that first failure is fixed the obvious way it ships a 75-line shell over a real package.Both come from the same three lines:
Bug 1 — the loop aborts on the first package, every time
The glob expands alphabetically, so
packages/adk/is always first.adkis at 1.0.14 locally and 1.0.14 on npm (published from elsewhere — see #42). npm rejects a republish of an existing version,pnpm publishexits non-zero, and underset -ea failing subshell in command position kills the whole job.Three of the four packages that exist on npm are exact version collisions:
pnpm publishadkmcp-serverworkflow-sdksdkSo
git tag sdks-v… && git push --tagspublishes nothing and reports failure — including the 45 packages that have never been published and genuinely should be.I have not been able to run this: Actions is billing-locked org-wide (wave-rig#174), so the reasoning above is from the source and the registry, not from a run. The version numbers and the glob order are measured; the
set -eabort is read from the script.Bug 2 — the fix that suggests itself is worse than the bug
The natural repair is "skip a version that already exists." Do only that, and
sdkbecomes the first package to get through — andsdkhere is 2 files, 75 lines, versioned 3.0.0, against a real@wave-av/sdk@2.0.14with 16 versions behind it.case "$ver" in 0.0.*) tag=previewsends only0.0.xtopreview.3.0.0is not0.0.x, so it goes tolatest. Every consumer of@wave-av/sdkwould resolve to the shell on their next install, and semver would present it as the newest stable release.This is the same failure
wave-av/adk#64was opened to prevent, in a second location and with a wider blast radius —adk#64guards a fork nobody depends on; this guards the package with the most published history in the scope.Suggested fix — skip collisions, but refuse regressions
Two independent conditions, because they fail differently. Skipping an already-published version is routine; publishing behind the registry is never intended and should stop the job.
Note this deliberately still fails on
sdk, becausesdk@3.0.0being a 75-line shell is a real problem that a workflow should not paper over. The guard turns a silent bad release into a loud stop.One thing I got wrong in #42
I recommended "fix the
repositoryfields first." For this repo that is already done — all 49 packages point atwave-av/sdks, which is exactly what provenance requires, and the workflow's(satisfied)comment is accurate. The wrongrepositorymetadata is in the published artifacts, because they were built from a different source copy. Nothing to fix here.Also worth confirming before arming this
The header says the workflow is inert until a Trusted Publisher is registered on npmjs.com for this repo + this workflow + the
npm-publishenvironment. Worth confirming that is still unregistered — if it were registered, a tag push would already be live, and Bug 1 is the only thing that has been preventing a bad release.Related: #42 (which repo publishes what), wave-av/adk#64 (same guard, adk fork).