feat: add a reusable release workflow for Foundry packages - #16
Merged
Conversation
Six repositories carried near-identical copies of the changesets release sequence — sohl, thalorna, kethira, harn-adventures, harn-ensemble and hm3 — differing in a package name, one npm script and one word in two filenames. That duplication had already produced its own failure twice. `changesets/action` renamed every multi-word output to kebab-case at v2; a stale v1 name resolves to the empty string, so `'' == 'false'` is always false, the gated step is skipped and the job reports green while releasing nothing. One dependency bump introduced that into every copy at once, and it was found in three only because someone went looking after noticing it in one. Separately, harn-ensemble's job still announces itself as `sohl-kethira-basic`. The sequence now lives here once. Only two axes genuinely differ, so only two are inputs: `package-kind` (`system`/`module`, which names both release assets) and `build-script`. `post-release-script` is the escape hatch for the one caller — sohl — that does something no other caller does, and it runs after the Release exists so a package's follow-on work can never be why the Release was not cut. `build:pack-release` is fixed: an input for an axis nothing varies on is a knob that invites divergence. Two guards that were missing or wrong: - The already-released check now branches on `git ls-remote`'s exit code rather than on truthiness (#13). The obvious `if` conflates "no matching ref" (2) with "could not reach the remote" (128), so a transient failure read as *this version is untagged* and proceeded to release. Every other guard in the file fails closed; that one failed open and did the irreversible thing. - The two release assets are checked non-empty before the Release is created. A Release is published the instant it exists and is what every installed copy updates from, so a pack script that failed quietly would produce a tag, a Release, and nothing to install. The README records the standing rule the rename defect calls for — re-check every `steps.<id>.outputs.*` against the pinned major's `action.yml` whenever that pin moves — the adoption order, and the two-run success signal, since "green while doing nothing" is the shape being guarded against and a single green run does not disprove it. No repository is migrated here. This adds the workflow; adoption is one repository at a time, each verified by a real release. #13 stays open until then: the six repositories still carry the fail-open guard in their own copies, and centralising the fix does not reach them until they call this. Closes #12 Refs #13
3 tasks
toastygm
added a commit
that referenced
this pull request
Sep 1, 2026
#18) #16 said the `build-script` input existed because "five of the six callers use `build:noci` and sohl, whose `build` does not reinstall, passes `build`". The second half is false. In all six repositories `build` is literally `npm ci && npm run build:noci`, sohl included — so it does reinstall, and sohl's release has been running a redundant `npm ci` on top of the install two steps earlier. Nothing varies on this axis at all: every caller can take the `build:noci` default, sohl included. The input stays anyway, but on honest grounds — it is the same named-script seam deploy-package-site.yml makes with `build:site`, so a package whose script is named differently can call this without editing it. An input every caller leaves alone is a seam; the one worth refusing is an input that invites callers to differ where they currently agree, which is why `build:pack-release` is still fixed. The sohl caller example loses its `build-script: build` line accordingly. Left in, it would have instructed the sixth adoption to carry the redundant install across deliberately. No behaviour change: the default was already `build:noci` and the only adopter so far does not pass this input. Co-authored-by: Tom Rodriguez <tmrodrig@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: add a reusable release workflow for Foundry packages
Six repositories carried near-identical copies of the changesets release
sequence — sohl, thalorna, kethira, harn-adventures, harn-ensemble and hm3 —
differing in a package name, one npm script and one word in two filenames.
That duplication had already produced its own failure twice.
changesets/actionrenamed every multi-word output to kebab-case at v2; a stale v1 name resolves to
the empty string, so
'' == 'false'is always false, the gated step is skippedand the job reports green while releasing nothing. One dependency bump
introduced that into every copy at once, and it was found in three only because
someone went looking after noticing it in one. Separately, harn-ensemble's job
still announces itself as
sohl-kethira-basic.The sequence now lives here once. Only two axes genuinely differ, so only two
are inputs:
package-kind(system/module, which names both release assets)and
build-script.post-release-scriptis the escape hatch for the onecaller — sohl — that does something no other caller does, and it runs after the
Release exists so a package's follow-on work can never be why the Release was
not cut.
build:pack-releaseis fixed: an input for an axis nothing varies onis a knob that invites divergence.
Two guards that were missing or wrong:
git ls-remote's exit code ratherthan on truthiness (The duplicate-release guard fails open: an unreachable remote reads as "this version is untagged" #13). The obvious
ifconflates "no matching ref" (2)with "could not reach the remote" (128), so a transient failure read as this
version is untagged and proceeded to release. Every other guard in the file
fails closed; that one failed open and did the irreversible thing.
Release is published the instant it exists and is what every installed copy
updates from, so a pack script that failed quietly would produce a tag, a
Release, and nothing to install.
The README records the standing rule the rename defect calls for — re-check
every
steps.<id>.outputs.*against the pinned major'saction.ymlwheneverthat pin moves — the adoption order, and the two-run success signal, since
"green while doing nothing" is the shape being guarded against and a single
green run does not disprove it.
No repository is migrated here. This adds the workflow; adoption is one
repository at a time, each verified by a real release. #13 stays open until
then: the six repositories still carry the fail-open guard in their own copies,
and centralising the fix does not reach them until they call this.
Closes #12
Refs #13