Skip to content
1 change: 1 addition & 0 deletions Library/Homebrew/cmd/tab.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def update_tab(formula_or_cask, installed_on_request:)
end

tab.installed_on_request = installed_on_request
tab.installed_as_dependency = !installed_on_request if formula_or_cask.is_a?(Formula)
tab.write
ohai "#{name} is now marked as #{installed_on_request_str}."
end
Expand Down
83 changes: 83 additions & 0 deletions Library/Homebrew/dev-cmd/which-entry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# typed: strict
# frozen_string_literal: true

require "abstract_command"
require "formula"
require "formulary"
require "json"

module Homebrew
module DevCmd
class WhichEntry < AbstractCommand
cmd_args do
description <<~EOS
Generate an `executables.txt` entry for <formula> using bottle manifest metadata.
EOS
flag "--output-db=",
description: "Append or update the entry in the given `executables.txt` database file."
named_args :formula, min: 1
end

sig { override.void }
def run
db_path = Pathname(T.unsafe(args).output_db) if T.unsafe(args).output_db
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary, you may need to run brew typecheck --update to avoid the need for T.unsafe.

args.named.each { |name| process(name, db_path:) }
end

private

sig { params(name: String, db_path: T.nilable(Pathname)).void }
def process(name, db_path:)
formula = Formulary.factory(name)
line = db_line(formula)
if db_path
write_db(db_path, formula.full_name, line)
elsif line
puts line
end
rescue FormulaUnavailableError
write_db(db_path, name, nil) if db_path&.exist?
end

sig { params(db_path: Pathname, name: String, line: T.nilable(String)).void }
def write_db(db_path, name, line)
lines = db_path.readlines(chomp: true).compact_blank if db_path.exist?
lines = (lines || []).reject { |l| l.start_with?("#{name}(") }
Comment thread
ooye-sanket marked this conversation as resolved.
Outdated
lines << line if line
db_path.write("#{lines.sort.join("\n")}\n")
end

sig { params(formula: Formula).returns(T.nilable(String)) }
def db_line(formula)
return if formula.disabled? || formula.deprecated?

exes = executables_from_manifest(formula)
"#{formula.full_name}(#{formula.pkg_version}):#{exes.join(" ")}"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason we added pkg_version before was to track formula file changes over time (we only checked the formulae with changed versions for updated exes).

Now that we're going to run this on every merge where we can detect which formulae are updated, I don't think we need the pkg_version anymore

end

sig { params(formula: Formula).returns(T::Array[String]) }
def executables_from_manifest(formula)
return [] unless formula.bottled?

manifest_path = HOMEBREW_CACHE.glob("#{formula.name}_bottle_manifest--*").first
if manifest_path.blank?
bottle = formula.bottle
return [] unless bottle

bottle.fetch
manifest_path = HOMEBREW_CACHE.glob("#{formula.name}_bottle_manifest--*").first
end
return [] if manifest_path.blank?

manifest = JSON.parse(manifest_path.read)
exec_files = manifest.dig("manifests", 0, "annotations", "sh.brew.path_exec_files")
return [] if exec_files.blank?

exec_files.split.map { |f| File.basename(f) }.sort
rescue JSON::ParserError => e
opoo "Failed to parse bottle manifest for #{formula.name}: #{e.message}"
[]
end
Comment on lines +59 to +80
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this share any logic or constants with the relevant bottle.rb or resource.rb code?

end
end
end
8 changes: 8 additions & 0 deletions Library/Homebrew/test/dev-cmd/which-entry_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

require "cmd/shared_examples/args_parse"
require "dev-cmd/which-entry"

RSpec.describe Homebrew::DevCmd::WhichEntry do
it_behaves_like "parseable arguments"
end
Loading