Skip to content

brew bundle drops trusted: true on tap entries without a custom remote (option parsed into clone_target) #22668

Description

@mserajnik

brew doctor output

Your system is ready to brew.

Verification

  • I ran brew update twice and am still able to reproduce my issue.
  • My "brew doctor output" above says Your system is ready to brew or a definitely unrelated Tier message.
  • This issue's title and/or description do not reference a single formula e.g. brew install wget. If they do, open an issue at https://github.com/Homebrew/homebrew-core/issues/new/choose instead.

brew config output

HOMEBREW_VERSION: 6.0.0-2-g1cd9e81
ORIGIN: https://github.com/Homebrew/brew
HEAD: 1cd9e81cc250a12f586b590ebbff64bb07771d83
Last commit: 2 hours ago
Branch: main
Core tap: N/A
Core cask tap: N/A
HOMEBREW_PREFIX: /opt/homebrew
HOMEBREW_BAT_THEME: base16
HOMEBREW_CASK_OPTS: []
HOMEBREW_DOWNLOAD_CONCURRENCY: 16
HOMEBREW_EDITOR: nvim
HOMEBREW_FORBID_PACKAGES_FROM_PATHS: set
HOMEBREW_MAKE_JOBS: 8
HOMEBREW_REQUIRE_TAP_TRUST: set
Homebrew Ruby: 4.0.5 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/4.0.5_1/bin/ruby
CPU: octa-core 64-bit arm_blizzard_avalanche
Clang: 21.0.0 build 2100
Git: 2.50.1 => /Library/Developer/CommandLineTools/usr/bin/git
Curl: 8.7.1 => /usr/bin/curl
macOS: 26.5-arm64
CLT: 26.5.0.0.1777544298
Xcode: N/A
Rosetta 2: false

What were you trying to do (and why)?

I wanted to use the new tap trust mechanism declaratively from my Brewfile, so that machines provisioned from it do not need per-machine brew trust --tap calls:

tap "oven-sh/bun", trusted: true
brew "oven-sh/bun/bun"

What happened (include all command output)?

The trusted: true option on tap entries without a custom remote is silently dropped: the tap is not added to trust.json, and every formula from it produces a trust warning during the brew bundle upgrade check:

Warning: Cannot check whether oven-sh/bun/bun is outdated because its tap is not trusted. Run `brew trust --formula oven-sh/bun/bun` to trust it.

The cause is in the Brewfile DSL; Dsl#tap accepts only positional parameters:

sig { params(name: String, clone_target: T.nilable(String), options: Homebrew::Bundle::EntryOptions).void }
def tap(name, clone_target = nil, options = {})
options[:clone_target] = clone_target
name = Homebrew::Bundle::Dsl.sanitize_tap_name(name)
@entries << Entry.new(:tap, name, options)
end

Since the method declares no keyword parameters, Ruby converts trusted: true into a positional Hash, which fills the clone_target slot while options stays empty:

tap "foo/bar", trusted: true
# entry options: {clone_target: {trusted: true}}

tap "baz/qux", "https://example.com/qux", trusted: true
# entry options: {trusted: true, clone_target: "https://example.com/qux"}

So Installer.apply_trust! never sees options[:trusted] for "by name" taps, and the option only survives when a remote URL happens to occupy the clone_target position (which is also why the misplaced Hash goes unnoticed: for already-installed taps clone_target is never used).

This also breaks dump round-tripping, since the dumper emits exactly this form for any explicitly trusted tap, with or without a remote:

tapline = "tap \"#{tap.name}\"#{remote}"
tapline += ", trusted: true" if Homebrew::Trust.explicitly_trusted_tap?(tap)

brew trust --tap foo/bar followed by brew bundle dump therefore produces a Brewfile whose trust information is lost when parsed back.

The "by name" case is clearly intended to work; there is a dedicated test for it, but it constructs the Dsl::Entry by hand and thus bypasses the parser:

it "trusts `trusted: true` taps by name" do
tap_entry = Homebrew::Bundle::Dsl::Entry.new(:tap, "thirdparty/tap", { trusted: true })
expect(Homebrew::Trust).to receive(:trust!).with(:tap, "thirdparty/tap").and_return(true)
described_class.install!([tap_entry], quiet: true)
end

dsl_spec.rb has no trusted: coverage at all, and the one dump test asserting trusted: true output happens to use a tap with a custom remote, so the gap is invisible to CI. Making Dsl#tap accept keyword arguments (plus a parser-level or dump round-trip test) should cover it.

What did you expect to happen?

I expected tap "oven-sh/bun", trusted: true to behave like brew trust --tap oven-sh/bun: the tap gets recorded in trust.json and formulae from it produce no trust warnings during brew bundle.

Step-by-step reproduction instructions (by running brew commands)

printf 'tap "oven-sh/bun", trusted: true\nbrew "oven-sh/bun/bun"\n' > Brewfile
brew bundle install --file Brewfile
brew trust --json v1

brew bundle install prints the trust warning quoted above during its upgrade check, and the brew trust --json v1 output shows that oven-sh/bun was not added to the trusted taps. Adding a custom remote as the second argument (tap "oven-sh/bun", "https://github.com/oven-sh/homebrew-bun", trusted: true) makes the same option work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions