Commit d1b4e58
authored
feat(release): ship the Homebrew formula from native release tarballs (#356)
## 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`: `renderFormula` writes the whole formula
from a template instead of patching one `url` line with a regex. The
template has `on_macos`/`on_linux` × `Hardware::CPU.arm?`, one native
`pythinker-code-<target>.tar.gz` url + sha256 per target, `bin.install
"pythinker"`, and a `test do` that asserts `pythinker --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 reads `Formula/pythinker-code.rb` back from the tap's `main` and
fails if it differs. A rejected push gets up to 3 attempts with `pull
--rebase` between them.
- `release.yml`:
- `update-brew-tap` now also needs `publish-native-assets`. On 2.4.1 the
tarballs appeared about 13 min after `release` finished.
- New `verify-brew-install` job (macos-latest + ubuntu-latest) runs
`brew tap`, `brew install`, `brew test` and compares `--version` with
`package.json`.
- `Release lane summary` reads `BREW_RESULT` from `verify-brew-install`.
- `cli/update/source.ts`: a native binary under a Homebrew Cellar counts
as a `homebrew` install. `detectNativeInstall()` returns false there, so
the binary never stages or swaps itself inside the Cellar, and `brew
upgrade` is its update path. Every caller of `detectNativeInstall()`
(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
| Behavior | Before | After | Who relies on the old behavior | Escape
hatch |
|---|---|---|---|---|
| What `brew install pymodel/tap/pythinker-code` installs | npm tarball
+ `depends_on "node"` | native binary, no Node.js | Homebrew users on
macOS and Linux | `npm i -g @pymodel/pythinker-code` |
| `node` formula after `brew upgrade` | a dependency | orphaned (`brew
autoremove` may delete it) | users who rely on brew-managed `node` only
through this formula | `brew install node` |
| opentui caveat in the formula | printed | removed (the native binary
bundles it) | nobody | n/a |
| Install source of a native binary in `/opt/homebrew/Cellar/…` or
`/home/linuxbrew/.linuxbrew/Cellar/…` | `native` (would stage and swap
itself) | `homebrew` (shows the `brew upgrade` hint) | nobody; only this
PR puts native binaries into the Cellar | n/a |
| Release lane brew result | the tap push job | the real install on
macOS + Ubuntu | release operators | `RELEASE_LANE_BREW=disabled`
(unchanged) |
Inventory of the old npm-formula path and where each item went:
| Old item | New place |
|---|---|
| `depends_on "node"` | dropped; the binary bundles Node |
| opentui caveat | dropped |
| npm tarball sha256 from the registry | sha256 of each release tarball
from the downloaded bytes |
| npm 404 poll (600 s) | the same poll, shared across the 4 release
tarballs |
| `--version` test | kept, now on the native binary |
Test coverage:
- `source.test.ts`: Cellar paths on macOS and Linux → `homebrew`, and
`detectNativeInstall` returns 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 and `BREW_RESULT` wiring.
- Manual check with real Homebrew on the 2.4.1 tarballs, done with `brew
install` + `brew test`:
- macOS arm64: the Developer ID signature stays intact.
- Linux x64 (`homebrew/brew` container): `--version` prints 2.4.1.
- `pnpm test:release` 58/58; app update suites 329/329; `tsc`, lint,
actionlint clean.
## 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.1 parent 34b854a commit d1b4e58
8 files changed
Lines changed: 315 additions & 83 deletions
File tree
- .agents/skills/release
- .changeset
- .github/workflows
- apps/pythinker-code
- src/cli/update
- test/cli/update
- scripts/release
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
| 47 | + | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| |||
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
73 | 85 | | |
74 | 86 | | |
75 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
494 | 494 | | |
495 | 495 | | |
496 | 496 | | |
497 | | - | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
498 | 502 | | |
499 | 503 | | |
500 | 504 | | |
| |||
526 | 530 | | |
527 | 531 | | |
528 | 532 | | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
529 | 578 | | |
530 | 579 | | |
531 | 580 | | |
| |||
663 | 712 | | |
664 | 713 | | |
665 | 714 | | |
| 715 | + | |
666 | 716 | | |
667 | 717 | | |
668 | 718 | | |
| |||
687 | 737 | | |
688 | 738 | | |
689 | 739 | | |
690 | | - | |
| 740 | + | |
691 | 741 | | |
692 | 742 | | |
693 | 743 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
30 | | - | |
| 29 | + | |
31 | 30 | | |
32 | 31 | | |
33 | 32 | | |
| |||
37 | 36 | | |
38 | 37 | | |
39 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
40 | 52 | | |
41 | 53 | | |
42 | 54 | | |
| |||
70 | 82 | | |
71 | 83 | | |
72 | 84 | | |
| 85 | + | |
73 | 86 | | |
74 | 87 | | |
75 | 88 | | |
| |||
153 | 166 | | |
154 | 167 | | |
155 | 168 | | |
156 | | - | |
| 169 | + | |
| 170 | + | |
157 | 171 | | |
158 | 172 | | |
159 | 173 | | |
160 | | - | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
161 | 177 | | |
162 | 178 | | |
163 | 179 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
158 | 159 | | |
159 | 160 | | |
160 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
161 | 174 | | |
162 | 175 | | |
163 | 176 | | |
| |||
197 | 210 | | |
198 | 211 | | |
199 | 212 | | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
35 | 39 | | |
36 | 40 | | |
37 | 41 | | |
| |||
0 commit comments