- Let JSON API post-install data express PostgreSQL's versioned public links.
- Fix PostgreSQL post-install ordering by running link steps before initdb.
- Add `link_dir` for recursive links that keep real extension dirs, skip
`.DS_Store` and replace conflicting files or symlinks.
- Keep existing real target directories even when a source file or symlink
maps to the same path.
- Add `link_children` for versioned executable links with implicit target
paths and `HOMEBREW_PREFIX` as the link target default.
- Expand `#{name}`, `#{version}`, `#{version.major}` and
`#{version.major_minor}` tokens from serialised step data.
- Normalise `path`, `source` and `target` values into typed path specs so
runner methods do not need string-or-hash checks.
- Replace loose `T.untyped` install-step payloads with narrower aliases for
raw steps, path specs, system arguments and template values.
- Keep Formula and Cask source or target base defaults intact while giving
public link steps the prefix default needed by versioned formulae.
- Prefer the installed metadata path version over a stale tab when
loading saved JSON cask metadata.
- This lets upgrades recover from receipts left behind by earlier
successful cask upgrades.
- Add a regression covering two consecutive `local-caffeine` upgrades.
- Keep bottled SBOMs reproducible by moving pour-specific runtime,
compiler and bottle package data into package manifests.
- Preserve per-platform supplements for `:all` bottles so installs merge
only the current platform data.
- Avoid validating full SBOMs during pour by appending manifest
supplement arrays.
Show the top-level formula header whenever any dependency was
installed first, not only when there were two or more, so the
transition from a dependency build to the formula build is clear.
Also format long build durations as hours and minutes and drop the
seconds once past five minutes, showing at most two units.
Motivation:
`brew bundle dump` could not round-trip a uv tool installed from a
non-PyPI source. Installing with:
uv tool install git+https://github.com/astral-sh/ruff.git
records the git source in uv's receipt, but `dump` emitted only
`uv "ruff"`, so restoring the Brewfile would reinstall from PyPI (or
fail) rather than the git repo. The source was lost because
`Uv.parse_tool_list` ran `uv tool list --show-with --show-extras`, which
never reports the source; uv only surfaces it via
`--show-version-specifiers` as `ruff v0.14 [required: git+https://...]`.
Changes:
- Request `--show-version-specifiers` and parse the `[required: ...]`
segment, keeping it only when it is a real source (git/URL/path)
rather than a bare version constraint like `>=0.1`.
- Add a `source:` Brewfile option to the uv DSL, validated as a String
and stored only when present (mirrors flatpak's `url:` handling).
- Thread `source` through install (installs from the source instead of
the name), dump (emits `, source: "..."`), and the already-installed
check so `brew bundle check`/`cleanup` treat two tools with the same
name but different sources as distinct.
- Extract the package-manager bootstrap in the base extension into
`Extension.ensure_package_manager_installed!` so uv's overridden
`preinstall!` reuses it instead of duplicating the logic.
Example Brewfile line now dumped and restored faithfully:
uv "ruff", source: "git+https://github.com/astral-sh/ruff.git"
Methods added:
- Uv.parse_source / Uv.source_requirement? (extract + classify source)
- Uv.dump_source (read source from a package record)
- Uv#preinstall!, Uv#install!, Uv#package_installed?, Uv#dump_entry,
Uv#installed_and_up_to_date? (source-aware overrides)
- Extension.ensure_package_manager_installed! (shared bootstrap)
Documented the option with an example in Brew-Bundle-and-Brewfile.md.
---------------------------------------------------
Address review feedback on the uv `source:` option:
- Extract the source-detection regex into a `SOURCE_REQUIREMENT_REGEX`
constant and inline the single-use `source_requirement?` helper.
- Replace hard-to-read ternaries in `parse_source` and `dump_source`
with early returns and a typed `package_source` helper, mirroring the
`dump_with`/`package_with` pattern and dropping an unnecessary cast.
- Simplify `format_checkable` to pass entry options straight through (as
Flatpak does), letting the typed extractors handle with/source.
--------------------------------------------------
Address uv bundle review feedback
- parse_source: fold the .git suffix into SOURCE_REQUIREMENT_REGEX so
the check is a single match?.
- entry/package_source: use .fetch(:source, nil) to distinguish an
unset source from an explicit nil.
- package_source: replace the silent is_a?(String) guard with a T.cast
on the :source value, matching Flatpak (uv casts only the value since
its options mix :with arrays with the :source string).
- Consolidate source stripping into a single normalize_source helper,
used by entry, parse_source and normalized_options.
Without the annotation the frozen array constant infers as a
fixed-size tuple, which changed how Sorbet resolved the curl_output
calls that splat it, leaving stdout/status typed as NilClass and
SystemCommand::Result. Destructure via an explicit .to_a so the
multiple assignment typechecks against the tuple inference.
Sorbet infers constant types from simple literals, literal arrays and
constructor calls (since 0.6.13304, including .freeze-d values), and
infers instance variable types assigned from signature parameters in
initialize. Remove T.let annotations that just restate those inferred
types, as found by the Sorbet/RedundantTLet and
Sorbet/RedundantTLetForLiteral cops proposed in
https://github.com/Shopify/rubocop-sorbet/pull/381.
dev-cmd/bump.rb's DEFAULT_CURL_ARGS is deliberately left annotated:
without T.let it infers as a fixed-size tuple, which changes how
Sorbet resolves the curl_output calls that splat it and fails brew
typecheck.
- Model the existing PostgreSQL, MySQL and MariaDB initialisation patterns as
named `init_data_dir` actions with their fixed marker and CI guards.
- Preserve API installs for database formulae that need post-install data
bootstrapping without reopening arbitrary Ruby execution.
- Validate specialised `using:` values before creating data directories so
invalid steps do not leave filesystem state behind.
- Keep permissions, ownership, PostgreSQL link maintenance and MySQL config
warnings for separate named steps instead of overloading this DSL.
- Record that specialised `using:` variants require at least three current
tap usages before they are added.
`brew info <formula>` marked installed dependencies with a green ✔ but
left uninstalled ones (e.g. `llvm`) as plain text, so they were easy to
miss among the ticks.
Give `pretty_install_status` a nilable `bold:` flag:
- `true` bold every entry, installed or not
- `false` never bold any entry
- `nil` (default) bold installed entries only
`brew info`'s header and its dependency/requirement listings pass
`bold: true`, so the formula name and all of its deps render bold whether
or not they are installed. A dependency's red ✘ still only appears when
the formula itself is installed and the dependency is missing. Add a
`pretty_unmarked` helper for the bold-but-unmarked case.
Previous calculation of dependencies would use default handler, which
omits optional and recommends but keeps build and test. This is
different from how `Formula#declared_runtime_dependencies` behaves which
omits build/test/optional but keeps recommends.
`uninstall_preflight { set_ownership ... }` runs `sudo chown -R` on the
app bundle. On macOS 13 or later, modifying the contents of an app
bundle requires the App Management TCC permission even when running as
root, and `chown` does not trigger the system permission prompt:
without the permission, the uninstall aborts with one
`Operation not permitted` line per file in the bundle and no hint at
the actual cause.
Reuse Quarantine.app_management_permissions_granted? (already used for
upgrades in Cask::Artifact::Moved) before chowning, so that:
- the system permission prompt is triggered, letting the user grant
access on the spot, and
- a clear, actionable CaskError is raised if the permission is missing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Treat missing cask cache targets as stale before timestamp checks
so broken legacy symlinks cannot abort cleanup.
- Reuse one `Cask/` cache listing during legacy cask pruning to
avoid repeated directory scans.
- Let formula steps run before a remaining `post_install` so tap migrations
can move repeated statements without blocking on unrelated Ruby work.
- Cover the main installer finish path so steps continue to run before
`post_install` outside the explicit `brew postinstall` command.
- Keep the legacy conflict check as a TODO because side-by-side use is only
a temporary conversion bridge.
- Make formula and cask DSL ordering reflect runtime order while the bridge
exists.
- Record the final deprecation target and local tap verification workflow so
follow-up tap PRs have a concrete success condition.
- Include `bounds` in `UsesFromMacOSDependency#eql?` so Hash
caches do not reuse formulae across different macOS bounds.
- Cover the equality case that made generic dependency expansion
order-dependent.