bottler: share a tap across many homebrew.yml sources
Make it work to host homebrew.yml files in several repos all releasing
to the same shared tap (e.g. samoht/brew). Previously, each release
overwrote the tap's README with only its own packages, and a concurrent
push was rejected non-fast-forward with no retry, so multi-source use
was not viable.
Library changes
---------------
ocaml-homebrew:
- Formula.desc_of_file reads an existing .rb's [desc "..."] line so a
README can describe formulas written by a different source.
- Tap.reset_to_remote fetches + hard-resets + cleans the local tap
checkout, for retry-after-race flows.
bottler: split release.ml per feature, add helpers:
- Formula (of_package, ensure, update_checksums, bottle_checksums,
regen) -- formula file construction and in-place updates.
- Readme (entries, generate) -- union scan of all Formula/*.rb so
each release produces the same README content regardless of which
source is pushing. Description for a locally-owned formula comes
from the config; for others it's parsed from the .rb.
- Retry.with_tap_push -- reset-to-remote + retry loop used by both
release and remove. Shared tap state makes concurrent pushes safe:
on non-ff rejection we fetch the competing commit and regenerate
our changes on top.
- Release.run now drives the pipeline and delegates to Formula,
Readme, and Retry; scopes the commit message with config.handle
("user/pkg: update bottles YYYYMMDD-SHA") so the tap log shows
which source pushed.
- Stdlib.List.* used where stdlib's List is needed, since Bottler.List
shadows inside the wrapped library.
New commands
------------
- bottler list -- enumerate every formula in the tap, flagging rows
owned by the current homebrew.yml. Lets a maintainer see sibling
sources' formulas.
- bottler remove NAME -- delete Formula/NAME.rb, regenerate the
union README, commit+push with retry. Orphan bottle blobs on S3
are left for bottler gc.
Fixes
-----
- install (both run and --local) now upgrades in place when the
target is already installed: brew upgrade tap/name for tap
installs, brew reinstall <path> for local-bottle reinstalls.
- New No_formula variant + err_no_formula helper instead of an
inline Error (Fmt.str ...) in remove.
Tests
-----
Eight new tests across Formula, Readme, Retry, List, Remove; Release
test reduced to a signature check since the pipeline is exercised by
bottler release end-to-end.