feat(release): ship the Homebrew formula from native release tarballs - #356
Merged
Merged
Conversation
The release manifest and the CDN latest.json name a bare binary per platform, but the release only uploaded zip/zst/tar.gz, so every native update from 2.1.0 hit HTTP 404. Clients since #323 also fetched from a CDN /binaries/ route that only serves the site HTML. - produce-manifest uploads the bare binary + sha256 sidecar it names - client resolves manifest and binaries on the GitHub release again - release gate HEADs every advertised download, not only the version - brew bump drops the one-shot npm view that raced publish propagation (red X on the 2.4.0 and 2.4.1 release runs); the tarball poll gates it
Port the CodexBar Homebrew channel: the formula installs the native per-platform tarball (macOS/Linux x arm64/x64) with a --version test instead of the npm tarball plus a node dependency. - update-brew-formula renders the whole formula, hashes downloaded tarballs (404 poll), pushes with rebase retry, reads it back - update-brew-tap waits for publish-native-assets - verify-brew-install installs from the public tap on macOS + Linux and is the Homebrew lane result in the release summary - a native binary under a Homebrew Cellar reports source homebrew and never stages or swaps itself - release skill documents the lane
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 18 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configurationConfiguration used: Repository: PyModel/pythinker-code/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
# Conflicts: # scripts/release/update-brew-formula.mjs # scripts/release/update-brew-formula.test.mjs
elkaix
enabled auto-merge (squash)
October 2, 2026 01:24
Merged
elkaix
pushed a commit
that referenced
this pull request
Oct 2, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @pymodel/pythinker-code@2.5.0 ### Minor Changes - [#356](#356) [`d1b4e58`](d1b4e58) Thanks [@elkaix](https://github.com/elkaix)! - Homebrew now installs the native `pythinker` binary on macOS and Linux, without Node.js. ### Patch Changes - [#355](#355) [`34b854a`](34b854a) Thanks [@elkaix](https://github.com/elkaix)! - Native `pythinker update` downloads the new binary from the GitHub release again; native installs on 2.2.0–2.4.1 need one reinstall to receive it (see [#354](#354)). ## @pymodel/pythinker-desktop@1.6.0 ### Minor Changes - [#356](#356) [`d1b4e58`](d1b4e58) Thanks [@elkaix](https://github.com/elkaix)! - Homebrew now installs the native `pythinker` binary on macOS and Linux, without Node.js. ### Patch Changes - [#355](#355) [`34b854a`](34b854a) Thanks [@elkaix](https://github.com/elkaix)! - Native `pythinker update` downloads the new binary from the GitHub release again; native installs on 2.2.0–2.4.1 need one reinstall to receive it (see [#354](#354)). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
6 tasks done
elkaix
added a commit
that referenced
this pull request
Oct 2, 2026
…on stanza (#358) ## Requirement or Bug The nightly workflow has failed on `main` every scheduled run since 2026-09-28 (runs 36405927270, 36551889134, 36698004633, 36814079854, 36847459152). After PR #356 shipped the Homebrew formula from native release tarballs, the release-reconciliation script can no longer read the formula's version, so the Homebrew lane fails on every nightly. ## Bug Reproduction Steps 1. Let a release publish with the #356 formula shape (done: `@pymodel/pythinker-code@2.5.0`). 2. Run `node scripts/release/release-status.mjs` on `main` (commit before this PR). 3. The Homebrew lane reports `FAIL | Formula/pythinker-code.rb (no version)` — the step exits 1 and the nightly run goes red (drift issue "Release lane drift detected"). ## Root Cause `brewFormulaVersion` extracted the version from the old npm-tarball formula shape via the regex `/pythinker-code-(\d+\.\d+\.\d+)\.tgz/`. PR #356 rewrote the generated formula to pin native per-platform tarballs (`pythinker-code-<target>.tar.gz`, no version in the filename) and to declare an explicit `version "X.Y.Z"` stanza instead. The lane checker — a consumer of the formula shape — was not updated, so it now always reports the formula as unreadable. This is a fundamental fix: the parser now reads the stanza the generator actually writes (`renderFormula` in `scripts/release/update-brew-formula.mjs`). ## Code Changes - `scripts/release/release-status.mjs` — `brewFormulaVersion` now extracts the version from the formula's `version "…"` stanza. - `scripts/release/release-status.test.mjs` — the fixture formula now renders the native-tarball shape (version stanza + native tarball URL), and a new test proves a formula without a version stanza fails the lane with "no pythinker-code tarball version". ## Behavior Changes and Affected Users | Behavior | Before | After | Who relies on the old behavior | Escape hatch | |---|---|---|---|---| | Homebrew lane version detection in nightly reconciliation | version parsed from a `pythinker-code-<version>.tgz` npm-tarball URL | version parsed from the formula's `version "…"` stanza | maintainers reading the drift issue's "Observed" column for a tap still serving the pre-#356 npm-tarball formula: they now see "unavailable" instead of the old version number | none — the pre-#356 formula shape is no longer produced by any pipeline | Nothing else observable changes: the CLI, desktop app, extension, installers, and updaters do not consume `brewFormulaVersion`; it is internal to the release reconciliation script and the nightly workflow. Test coverage: `scripts/release/release-status.test.mjs` — lane-aligned test (native-shape fixture), lagging-formula test, and the new missing-stanza test (`pnpm run test:release`, 59/59). ## Checklist - [x] I have read the [CONTRIBUTING](https://github.com/PyModel/pythinker-code/blob/main/CONTRIBUTING.md) document. - [x] I have linked a related issue (external PRs: issue must have a maintainer's `/approve`). - [x] I have added tests that prove my feature works. - [x] The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR. - [x] Ran `gen-changesets` skill, or this PR needs no changeset. - [x] Ran `gen-docs` skill, or this PR needs no doc update. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Homebrew release status now reflects the version declared in the formula, rather than inferring it from the release archive’s filename. This keeps the reported version accurate when archive naming differs from the formula. If the formula does not contain a valid version declaration, the version is reported as unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.
Requirement or Bug
Ship the Homebrew formula as the native binary, the way CodexBar's tap does: per-platform release tarballs, bumped by the release run, verified by a real install.
Stacked on #355 (shares
update-brew-formula.mjs).Bug Reproduction Steps
N/A (feature).
Root Cause
N/A (feature).
Code Changes
update-brew-formula.mjs:renderFormulawrites the whole formula from a template instead of patching oneurlline with a regex. The template hason_macos/on_linux×Hardware::CPU.arm?, one nativepythinker-code-<target>.tar.gzurl + sha256 per target,bin.install "pythinker", and atest dothat assertspythinker --version. The sha256 comes from the downloaded bytes, not from the sidecars. All four downloads share one 600 s poll that retries on 404. After the push, the script readsFormula/pythinker-code.rbback from the tap'smainand fails if it differs. A rejected push gets up to 3 attempts withpull --rebasebetween them.release.yml:update-brew-tapnow also needspublish-native-assets. On 2.4.1 the tarballs appeared about 13 min afterreleasefinished.verify-brew-installjob (macos-latest + ubuntu-latest) runsbrew tap,brew install,brew testand compares--versionwithpackage.json.Release lane summaryreadsBREW_RESULTfromverify-brew-install.cli/update/source.ts: a native binary under a Homebrew Cellar counts as ahomebrewinstall.detectNativeInstall()returns false there, so the binary never stages or swaps itself inside the Cellar, andbrew upgradeis its update path. Every caller ofdetectNativeInstall()(update download, startup swap, source detection) goes through this one function..agents/skills/release/SKILL.md: the brew section now describes this flow.How this differs from CodexBar: CodexBar dispatches a tap-side workflow with a PAT. This PR keeps the existing GitHub App token, which pushes the formula directly. That needs no new secret and no workflow in the second repo. The substance is the same: a native per-platform formula, a 404 retry on assets, and a content check after the bump.
Behavior Changes and Affected Users
brew install pymodel/tap/pythinker-codeinstallsdepends_on "node"npm i -g @pymodel/pythinker-codenodeformula afterbrew upgradebrew autoremovemay delete it)nodeonly through this formulabrew install node/opt/homebrew/Cellar/…or/home/linuxbrew/.linuxbrew/Cellar/…native(would stage and swap itself)homebrew(shows thebrew upgradehint)RELEASE_LANE_BREW=disabled(unchanged)Inventory of the old npm-formula path and where each item went:
depends_on "node"--versiontestTest coverage:
source.test.ts: Cellar paths on macOS and Linux →homebrew, anddetectNativeInstallreturns false. Both failed before the change.update-brew-formula.test.mjs: formula pairs in order, a missing target throws, an invalid sha256 throws.release-workflows.test.mjs: the brew needs andBREW_RESULTwiring.brew install+brew test:homebrew/brewcontainer):--versionprints 2.4.1.pnpm test:release58/58; app update suites 329/329;tsc, lint, actionlint clean.Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.