feat: Formula writes rook-keg-marker.json, the keg's own identity record - #16
Merged
Conversation
`rook update` (LambdatestIncPrivate/rook#576) must establish that the keg its running copy sits in is rook's own before it prints `brew upgrade` for it. Homebrew's INSTALL_RECEIPT.json cannot carry that fact: it has no formula-name field, and for an API-loaded formula source.path is the shared formula.jws.json cache — so without a record of our own, the only implementation is inferring from the Cellar path, which the reader refuses to do. def install now writes {v: 1, formula: "rook", version} to <keg>/rook-keg-marker.json, unconditionally (the opoo fallback still installs rook), with no absolute paths inside so bottles pour and relocate cleanly under :any_skip_relocation. test do asserts all three fields, and brew-smoke inherits the assertion through the brew test call it already makes. Verified by a real from-source install via a throwaway local tap: the marker lands at the keg root beside INSTALL_RECEIPT.json with exactly {"v":1,"formula":"rook","version":"0.1.0"}, and all three test-do assertions pass executed against the installed keg. Without this change the file does not exist (the shipped 0.1.0 bottle demonstrates it) and the test's read raises ENOENT. brew style reports only the pre-existing, deliberate ComponentsOrder offense; the update-formula.yml sed anchors (url/sha256/version) are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- guard the test do marker assertions on existence: bottle do's sha256 and version are unchanged by this PR, so brew install keeps pouring the pre-marker 0.1.0 bottle until a rebuild lands one that has it — unguarded, any brew-smoke run dispatched against 0.1.0 in that window hits an uncaught Errno::ENOENT instead of a clean pass - write the marker via temp-file-then-rename so a crash mid-write can't leave a truncated file on disk (same reasoning as install.sh's manifest write, #15) - derive formula: name instead of hardcoding "rook" at both the write and test sites - test do independently re-derives the "marker sits five levels above pkg_dir" layout claim instead of only reading back through prefix, which def install also wrote through Two findings from the same review are out of scope for this PR and tracked separately, since the fix lives in the private CLI repo: readKegRecord() never validates the marker's version field (LambdatestIncPrivate/rook#635), and there's no contract test between this Formula's output and that reader (LambdatestIncPrivate/rook#636). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Ran a fresh
Two findings from the same pass are real but out of scope here (the fix lives in the private CLI repo, not this Formula) — filed and linked rather than touched:
Verified: |
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.
What
def installwrites the keg's own identity record —rook-keg-marker.json,{v: 1, formula: "rook", version}— at the keg root beside Homebrew'sINSTALL_RECEIPT.json.test doasserts all three fields, and brew-smoke inherits the assertion through thebrew testcall it already makes.One file, no absolute paths inside the record, written unconditionally (the
opoofallback still installs rook, and the keg is rook's either way).Why
rook update(LambdatestIncPrivate/rook#576) must establish that the keg its running copy sits in is rook's own before it printsbrew upgrade <tap>/rook— the tap is read from the receipt, and a receipt that isn't rook's must never supply it.The receipt cannot carry that fact. Read against a real one: its keys are
aliases, arch, built_as_bottle, …, source, andsourceholdspath, spec, tap, tap_git_head, versions— no field names the formula. For an API-loaded formula,source.pathis the sharedformula.jws.jsoncache, not a per-formula path. Without a record of our own, "is this keg rook's?" is only answerable by inferring from theCellar/rook/<v>path shape, which the reader's design forbids — and a receipt alone, found above a package root, could be planted by an untrusted repository to get itssource.tapprinted into a pasteable command (#576, review round 6, #35).No absolute paths inside the record: bottles are poured into whatever prefix the host uses, and
cellar: :any_skip_relocationmeans nothing would rewrite one.The shipped
0.1.0bottle predates the marker. Those installs degrade on the private side to printing candidates rather than the fully-qualified upgrade line, and heal on the nextbrew upgrade— the same migration-by-omission as pre-manifest curl installs (#15).Evidence
Verified by a real from-source install, staged through a throwaway local tap (
brew tap-new+brew install --build-from-source):INSTALL_RECEIPT.json, byte-exact:{"v":1,"formula":"rook","version":"0.1.0"}test doassertions pass, executed against the installed kegbin/rook --version→0.1.0Without the change the file does not exist — the shipped
0.1.0bottle demonstrates the absence — so the test'sJSON.parse(read)raises rather than passing vacuously.Two honest caveats:
brew testitself could not run on the dev machine (the keg would not link past a developer symlink already occupyingHOMEBREW_PREFIX/bin/rook; the assertions were executed manually against the keg instead — CI runners link cleanly), and the bottled path is verified by the next release's brew-smoke run, not by this PR, since bottles are cut bybuild-bottles.ymlafter merge.ruby -cclean.brew stylereports only the pre-existing, deliberateFormulaAudit/ComponentsOrderoffense documented in the formula. Theupdate-formula.ymlsed anchors (url/sha256/version) are untouched — the write sits insidedef install, far from the patched lines.Not in this PR
rook updateitself — the reader of this record lives in the private CLI repository (LambdatestIncPrivate/rook#576).install.sh's manifest, the curl channel's equivalent record — feat: record how and where install.sh installed, forrook update#15.🤖 Generated with Claude Code