Release v0.4.1 shipped with zero artifacts, so brew installs 0.3.0 on GA day #232
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
brew install fjon GA day installs 0.3.0, two releases behind, and the tap cannot be bumped because the newest release has no binaries.Evidence
pushonrefs/tags/v0.4.1, failed at2026-07-22T20:19:49Z. It has never been retried.v0.4.1is a published, non-draft release with 0 assets and an empty body. Comparev0.3.0, which has 5:SHA256SUMS, the three platform tarballs, and a generatedfj.rb.rasterstate/homebrew-tapFormula/fj.rbstill pinsversion "0.3.0"withv0.3.0download URLs, which is correct behaviour given there is nothing newer to point at.v0.4.0tag at all; the tag sequence isv0.3.0thenv0.4.1.Why this matters now
GA is announced and live.
FJ_INSTALL_COMMANDin fjord-platform (src/lib/clients.ts:41) is the public install path, it is rendered on the CLI page, and the GA blog post reuses the same one-liner. Every person who follows the announcement to install the CLI today gets 0.3.0.The compounding part: the GA blog post's
fjcapability list was rewritten to match the 0.4.x verb set. So the documented commands and the installable binary are from different releases. Whichever verbs landed after 0.3.0 are documented but absent for every new user.Scope
v0.4.1artifacts. Note the release job also generatesfj.rb, so the tap bump is meant to fall out of a successful release rather than being hand-edited.rasterstate/homebrew-tapto the shipped version with correctsha256values, and verify a cleanbrew tap ... && brew install fjproduces a binary whosefj --versionmatches.Also noticed, minor
FJ_INSTALL_COMMANDtaps asrasterandstate/tapwhile the repo israsterstate/homebrew-tap. It works because the explicit URL wins, but the alias is a typo and it is public-facing.Item 1: which documented verbs are missing from 0.3.0
None. No verb named in any GA post is absent from 0.3.0, so the blog needed neither a hold nor a copy change on verb grounds.
Method: built 0.3.0 from its tag and 0.4.x from
main, walked the full--helptree of each (every subcommand and every nested subcommand), and diffed the command surfaces.Top-level surface is byte-identical between the two. The only two differences anywhere in the tree:
fj run logfj auth sessionThat is the whole diff. Every verb the posts actually instruct a reader to type (
fj auth login,fj repo view,fj pr create,fj issue list,fj run list,fj api, and the rest) exists in 0.3.0 with the same flags.The caveat that does matter, which is not a missing verb
meet-fjhas a bullet promising you can watch Actions runs and "read their logs". On 0.3.0 that half-works, and the reason is #103 rather than a missing command:Forgejo's Actions log and rerun routes are cookie-gated and reject a PAT.
fj auth sessionexists in 0.4.x precisely to work around it, by importing ani_like_giteacookie. Without it:So the exposure on GA day is narrower than "the post documents verbs that do not exist", but it is not nothing: the one bullet that does not hold on the installable version is a bullet aimed at exactly the customers who pay.
Two ways to close it
fj auth sessionexists, logs work on private repos. This is the one I would take.Nothing else in the launch copy is affected either way.
Item 2: does #233 republish the actual v0.4.1 artifacts, or only protect future tags?
It republishes the real ones. v0.4.1 gets its five assets, the tag does not move, and it keeps its 2026-07-22 date. Not a future-tags-only fix. The chain, with the part I verified at each link:
1. A dispatch is a new run, and that alone unwedges it. The collision that killed run 391 is keyed on the run id. From
upload-artifact-actionsrc/lib/config.js:162:and the throw at
src/lib/upload-impl.js:94:Run 391's stale objects live under
.../artifacts/391/. A dispatch gets a freshGITHUB_RUN_ID, soexists()is false and every leg uploads clean. #230'soverwrite: trueis a second layer under it, not the thing doing the work. This is also why re-running 391 could never go green no matter how many times it was retried.2. It builds the tag's code, not
main's. Bothbuildandpublishcheck outref: v0.4.1. The dispatching branch supplies only the workflow file, which is the whole point: a tag push reads its workflow from the tag, so the fix is unreachable from 391 but reachable from a dispatch.3. The action attaches to the release that already exists. This was the part worth checking rather than assuming, since v0.4.1 is already a published release (id
214,draft: false,assets: 0, empty body).forgejo-release-action/src/release.js:87:So it takes the update branch: PATCHes release 214 with the CHANGELOG body, then uploads. No new tag, no moved tag, no second release. A useful side effect: if
guardhas demoted a failed release to draft, a later successful dispatch promotes it back, becausedraftdefaults to false.4. Everything the job needs is present at the tag. Checked against
v0.4.1(62d4d7f) rather than the working tree:CHANGELOG.mdhas## [0.4.1] - 2026-07-22; the awk extracts 12 lines and stops at## [0.4.0]. The release body will not be empty.README.md,LICENSE,CHANGELOG.md,Cargo.lockall present, which thepackageand--lockedsteps need.packagestep producesfj-v0.4.1-<target>.tar.gzwith a single top-levelfj-v0.4.1-<target>/, matching what the corrected formula expects. Re-verified after this round's changes.5. Then the tap bump is a copy, not an edit, because the formula generator no longer emits the
cdthat made every generatedfj.rbuninstallable.What to run
Dispatch
releasefrommainwithtag: v0.4.1. Expected: 5 assets on v0.4.1 (three tarballs,SHA256SUMS,fj.rb), a non-empty body, unchanged tag and date.What I cannot confirm without CI
The macOS signing and notarization steps and the artifact round-trip. If notarization is flaky the build fails again, but recoverably now: each dispatch is a fresh run id, so a retry is a retry rather than a permanent wedge, and
guardfiles an issue instead of leaving a zero-asset release looking installable.target_commitishon release 214 currently readsmain, andupdateReleasedoes not send that field, so the stale value stays. Cosmetic: the tag itself points at62d4d7fand that is what the assets are built from. Not worth moving the tag to tidy.After the dispatch, the tap bump is the last piece of this issue. Per the operator's gate I will open that PR and stop rather than push to the tap.
Tap bump: verified and pushed, but the PR cannot be opened
v0.4.1 now has its five assets (recovery dispatch, run #410, after #233 and #234). The tap bump is ready on
rasterstate/homebrew-tapbranchfj-0.4.1atf705bc3. It is not merged and I have not pushed tomain.rasterstate/homebrew-taphashas_pull_requests: false, so there is nowhere to open a pull request.fj pr createreportsnot found: rasterstate/homebrew-tap, and the repository also hashas_actions: false, so no Forseti review can run there either. The repo has no branch protection and I have push access, which is exactly why I stopped at the branch rather than pushingmain. Two ways forward, operator's call: enable the pull requests unit and I open it normally, or reviewmain...fj-0.4.1and merge it directly.The formula is byte-identical to the published asset
Formula/fj.rbwas replaced wholesale with thefj.rbpublished on the v0.4.1 release rather than hand-edited from 0.3.0.cmpreports no difference; both hash to7d62eeb32052730187626c2bb53e7de6cc0c3c41673cba80bc06e32087aa5602. The diff against 0.3.0 is the version, three URLs, three hashes, and a longer comment on theinstallblock, which is the generator's wording.Verified rather than trusted
shasum -a 256. All three match the publishedSHA256SUMSand the hashes infj.rb.shasum -cpasses on all three.brew fetchpasses, so Homebrew's own checksum verification agrees.installblock references, which is what makes the droppedcdcorrect.Mach-O arm64, hardened runtime, andspctl -a -t installreportsaccepted, source=Notarized Developer ID, origin=Developer ID Application: Raster & State LLC (62Y3FRM8PD). Notarization was the last thing I had listed as unverifiable without CI; it is now verified from the published artifact.rasterstate/tapcheckout atfj-0.4.1, ranbrew upgrade fj, and got0.3.0 -> 0.4.1withfj --versionreportingfj 0.4.1andREADME.md,LICENSE,CHANGELOG.mdlanding inshare/fj. Then restored the checkout to2b263a2and reinstalled 0.3.0, so the machine is as it was.brew install fjon a machine with the tap already tapped served 0.3.0.Flagging, not fixing:
brew test fjfails, and did before thisThe formula's test block asserts a string that
fj --helpdoes not contain:The help text reads
fj is a command-line tool for Forgejo, lowercasec, so the match fails:Three things worth separating:
--help.brew testhas been broken here since at least 0.3.0.brew install. Test blocks only run underbrew testand strict audits, which is why nobody noticed. The--versionassertion in the same block passes..forgejo/workflows/release.yml, not in the tap. Fixing it in the tap would break the byte-identity with the published asset that makes this bump a copy rather than an edit, and it would be undone by the next release. I have not changed it in either place. Happy to open a one-line fj PR against the generator (assert the lowercase string, or thedesctext that actually appears) on your word.Remaining on this issue
Only the merge. Once
fj-0.4.1lands on the tap'smain,brew tap rasterstate/tap && brew install fjserves 0.4.1, and with #665 having fixed therasterandstate/taptypo inFJ_INSTALL_COMMAND, the documented one-liner and the tap agree.Closing the loop on the last half of this, since the issue was marked closed while
brew install fjwas still serving 0.3.0.The tap was the remaining gap.
rasterstate/homebrew-taphas no pull requests unit, somainfast-forwarded to thefj-0.4.1branch directly:2b263a2..f705bc3, one commit,Formula/fj.rbonly.brew info rasterstate/tap/fjnow reports stable 0.4.1.Verified independently of the release job before pushing, rather than trusting the published
SHA256SUMS(same job produces both, so it is not a check):fj-v0.4.1-darwin-aarch64.tar.gzand hashed it withshasum -a 256. It matches the formula's99ee1c6a...exactly, and all three formula hashes match the published sums.fj-v0.4.1-darwin-aarch64/containing exactly the four files the install block references, which confirms the no-cdinstall and the comment explaining it.fj.rbasset on the v0.4.1 release, both 1423 bytes, so the tap is the generator's output and not a hand-edit.One defect stays open and is not this issue's: the formula's
test doassertsassert_match "fj #{version}". That belongs in the generator in this repo rather than in the tap, so it should be filed here if it is not already.