diff --git a/Library/Homebrew/dev-cmd/bump.rb b/Library/Homebrew/dev-cmd/bump.rb index d52400fc85..17655e4b33 100644 --- a/Library/Homebrew/dev-cmd/bump.rb +++ b/Library/Homebrew/dev-cmd/bump.rb @@ -16,6 +16,8 @@ module Homebrew Display out-of-date brew formulae and the latest version available. Also displays whether a pull request has been opened with the URL. EOS + switch "--full-name", + description: "Print formulae/casks with fully-qualified names." switch "--no-pull-requests", description: "Do not retrieve pull requests from GitHub." switch "--formula", "--formulae", @@ -39,11 +41,14 @@ module Homebrew end formulae_and_casks = if args.formula? - args.named.to_formulae.presence + args.named.to_formulae elsif args.cask? - args.named.to_casks.presence + args.named.to_casks else - args.named.to_formulae_and_casks.presence + args.named.to_formulae_and_casks + end + formulae_and_casks = formulae_and_casks&.sort_by do |formula_or_cask| + formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name end limit = args.limit.to_i if args.limit.present? @@ -51,10 +56,29 @@ module Homebrew if formulae_and_casks Livecheck.load_other_tap_strategies(formulae_and_casks) + ambiguous_casks = [] + if !args.formula? && !args.cask? + ambiguous_casks = formulae_and_casks.group_by { |item| Livecheck.formula_or_cask_name(item, full_name: true) } + .values + .select { |items| items.length > 1 } + .flatten + .select { |item| item.is_a?(Cask::Cask) } + end + + ambiguous_names = [] + unless args.full_name? + ambiguous_names = + (formulae_and_casks - ambiguous_casks).group_by { |item| Livecheck.formula_or_cask_name(item) } + .values + .select { |items| items.length > 1 } + .flatten + end + formulae_and_casks.each_with_index do |formula_or_cask, i| puts if i.positive? - name = Livecheck.formula_or_cask_name(formula_or_cask) + use_full_name = args.full_name? || ambiguous_names.include?(formula_or_cask) + name = Livecheck.formula_or_cask_name(formula_or_cask, full_name: use_full_name) repository = if formula_or_cask.is_a?(Formula) if formula_or_cask.head_only? ohai name @@ -68,7 +92,13 @@ module Homebrew end package_data = Repology.single_package_query(name, repository: repository) - retrieve_and_display_info(formula_or_cask, name, package_data&.values&.first, args: args) + retrieve_and_display_info( + formula_or_cask, + name, + package_data&.values&.first, + args: args, + ambiguous_cask: ambiguous_casks.include?(formula_or_cask), + ) end else api_response = {} @@ -105,9 +135,14 @@ module Homebrew next end name = Livecheck.formula_or_cask_name(formula_or_cask) + ambiguous_cask = begin + formula_or_cask.is_a?(Cask::Cask) && !args.cask? && Formula[name] + rescue FormulaUnavailableError + false + end puts if i.positive? - retrieve_and_display_info(formula_or_cask, name, repositories, args: args) + retrieve_and_display_info(formula_or_cask, name, repositories, args: args, ambiguous_cask: ambiguous_cask) break if limit && i >= limit end @@ -145,7 +180,7 @@ module Homebrew pull_requests end - def retrieve_and_display_info(formula_or_cask, name, repositories, args:) + def retrieve_and_display_info(formula_or_cask, name, repositories, args:, ambiguous_cask: false) current_version = if formula_or_cask.is_a?(Formula) formula_or_cask.stable.version else @@ -161,6 +196,7 @@ module Homebrew livecheck_latest = livecheck_result(formula_or_cask) pull_requests = retrieve_pull_requests(formula_or_cask, name) unless args.no_pull_requests? + name += " (cask)" if ambiguous_cask title = if current_version == repology_latest && current_version == livecheck_latest "#{name} is up to date!" diff --git a/Library/Homebrew/dev-cmd/livecheck.rb b/Library/Homebrew/dev-cmd/livecheck.rb index 13f05f09f4..c702bc4fc3 100644 --- a/Library/Homebrew/dev-cmd/livecheck.rb +++ b/Library/Homebrew/dev-cmd/livecheck.rb @@ -61,54 +61,55 @@ module Homebrew puts ENV["HOMEBREW_LIVECHECK_WATCHLIST"] if ENV["HOMEBREW_LIVECHECK_WATCHLIST"].present? end - formulae_and_casks_to_check = - if args.tap - tap = Tap.fetch(args.tap) - formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) } - casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) } - formulae + casks - elsif args.installed? - formulae = args.cask? ? [] : Formula.installed - casks = args.formula? ? [] : Cask::Caskroom.casks - formulae + casks - elsif args.all? - formulae = args.cask? ? [] : Formula.to_a - casks = args.formula? ? [] : Cask::Cask.to_a - formulae + casks - elsif args.named.present? - if args.formula? - args.named.to_formulae - elsif args.cask? - args.named.to_casks - else - args.named.to_formulae_and_casks - end - elsif File.exist?(WATCHLIST_PATH) - begin - names = Pathname.new(WATCHLIST_PATH).read.lines - .reject { |line| line.start_with?("#") || line.blank? } - .map(&:strip) - - named_args = T.unsafe(CLI::NamedArgs).new(*names, parent: args) - named_args.to_formulae_and_casks(ignore_unavailable: true) - rescue Errno::ENOENT => e - onoe e - end + formulae_and_casks_to_check = if args.tap + tap = Tap.fetch(args.tap) + formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) } + casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) } + formulae + casks + elsif args.installed? + formulae = args.cask? ? [] : Formula.installed + casks = args.formula? ? [] : Cask::Caskroom.casks + formulae + casks + elsif args.all? + formulae = args.cask? ? [] : Formula.to_a + casks = args.formula? ? [] : Cask::Cask.to_a + formulae + casks + elsif args.named.present? + if args.formula? + args.named.to_formulae + elsif args.cask? + args.named.to_casks else - raise UsageError, "A watchlist file is required when no arguments are given." - end&.sort_by do |formula_or_cask| - formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name + args.named.to_formulae_and_casks end + elsif File.exist?(WATCHLIST_PATH) + begin + names = Pathname.new(WATCHLIST_PATH).read.lines + .reject { |line| line.start_with?("#") || line.blank? } + .map(&:strip) + + named_args = T.unsafe(CLI::NamedArgs).new(*names, parent: args) + named_args.to_formulae_and_casks(ignore_unavailable: true) + rescue Errno::ENOENT => e + onoe e + end + else + raise UsageError, "A watchlist file is required when no arguments are given." + end + formulae_and_casks_to_check = formulae_and_casks_to_check.sort_by do |formula_or_cask| + formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name + end raise UsageError, "No formulae or casks to check." if formulae_and_casks_to_check.blank? options = { - json: args.json?, - full_name: args.full_name?, - newer_only: args.newer_only?, - quiet: args.quiet?, - debug: args.debug?, - verbose: args.verbose?, + json: args.json?, + full_name: args.full_name?, + handle_name_conflict: !args.formula? && !args.cask?, + newer_only: args.newer_only?, + quiet: args.quiet?, + debug: args.debug?, + verbose: args.verbose?, }.compact Livecheck.run_checks(formulae_and_casks_to_check, **options) diff --git a/Library/Homebrew/livecheck/livecheck.rb b/Library/Homebrew/livecheck/livecheck.rb index 4ff07d864b..92097bb711 100644 --- a/Library/Homebrew/livecheck/livecheck.rb +++ b/Library/Homebrew/livecheck/livecheck.rb @@ -63,7 +63,7 @@ module Homebrew # Uses `formulae_and_casks_to_check` to identify taps in use other than # homebrew/core and homebrew/cask and loads strategies from them. - sig { params(formulae_and_casks_to_check: T::Enumerable[T.any(Formula, Cask::Cask)]).void } + sig { params(formulae_and_casks_to_check: T::Array[T.any(Formula, Cask::Cask)]).void } def load_other_tap_strategies(formulae_and_casks_to_check) other_taps = {} formulae_and_casks_to_check.each do |formula_or_cask| @@ -86,8 +86,9 @@ module Homebrew # `formulae_and_casks_to_check` array and prints the results. sig { params( - formulae_and_casks_to_check: T::Enumerable[T.any(Formula, Cask::Cask)], + formulae_and_casks_to_check: T::Array[T.any(Formula, Cask::Cask)], full_name: T::Boolean, + handle_name_conflict: T::Boolean, json: T::Boolean, newer_only: T::Boolean, debug: T::Boolean, @@ -97,10 +98,29 @@ module Homebrew } def run_checks( formulae_and_casks_to_check, - full_name: false, json: false, newer_only: false, debug: false, quiet: false, verbose: false + full_name: false, handle_name_conflict: false, json: false, newer_only: false, + debug: false, quiet: false, verbose: false ) load_other_tap_strategies(formulae_and_casks_to_check) + ambiguous_casks = [] + if handle_name_conflict + ambiguous_casks = formulae_and_casks_to_check.group_by { |item| formula_or_cask_name(item, full_name: true) } + .values + .select { |items| items.length > 1 } + .flatten + .select { |item| item.is_a?(Cask::Cask) } + end + + ambiguous_names = [] + unless full_name + ambiguous_names = + (formulae_and_casks_to_check - ambiguous_casks).group_by { |item| formula_or_cask_name(item) } + .values + .select { |items| items.length > 1 } + .flatten + end + has_a_newer_upstream_version = T.let(false, T::Boolean) if json && !quiet && $stderr.tty? @@ -122,7 +142,9 @@ module Homebrew formulae_checked = formulae_and_casks_to_check.map.with_index do |formula_or_cask, i| formula = formula_or_cask if formula_or_cask.is_a?(Formula) cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask) - name = formula_or_cask_name(formula_or_cask, full_name: full_name) + + use_full_name = full_name || ambiguous_names.include?(formula_or_cask) + name = formula_or_cask_name(formula_or_cask, full_name: use_full_name) if debug && i.positive? puts <<~EOS @@ -130,9 +152,11 @@ module Homebrew ---------- EOS + elsif debug + puts end - skip_info = SkipConditions.skip_information(formula_or_cask, full_name: full_name, verbose: verbose) + skip_info = SkipConditions.skip_information(formula_or_cask, full_name: use_full_name, verbose: verbose) if skip_info.present? next skip_info if json @@ -164,7 +188,7 @@ module Homebrew else version_info = latest_version( formula_or_cask, - json: json, full_name: full_name, verbose: verbose, debug: debug, + json: json, full_name: use_full_name, verbose: verbose, debug: debug, ) version_info[:latest] if version_info.present? end @@ -175,7 +199,7 @@ module Homebrew next version_info if version_info.is_a?(Hash) && version_info[:status] && version_info[:messages] - next status_hash(formula_or_cask, "error", [no_versions_msg], full_name: full_name, verbose: verbose) + next status_hash(formula_or_cask, "error", [no_versions_msg], full_name: use_full_name, verbose: verbose) end if (m = latest.to_s.match(/(.*)-release$/)) && !current.to_s.match(/.*-release$/) @@ -220,15 +244,19 @@ module Homebrew next info end - print_latest_version(info, verbose: verbose) + print_latest_version(info, verbose: verbose, ambiguous_cask: ambiguous_casks.include?(formula_or_cask)) nil rescue => e Homebrew.failed = true + use_full_name = full_name || ambiguous_names.include?(formula_or_cask) if json progress&.increment - status_hash(formula_or_cask, "error", [e.to_s], full_name: full_name, verbose: verbose) + status_hash(formula_or_cask, "error", [e.to_s], full_name: use_full_name, verbose: verbose) elsif !quiet + name = formula_or_cask_name(formula_or_cask, full_name: use_full_name) + name += " (cask)" if ambiguous_casks.include?(formula_or_cask) + onoe "#{Tty.blue}#{name}#{Tty.reset}: #{e}" $stderr.puts e.backtrace if debug && !e.is_a?(Livecheck::Error) nil @@ -306,9 +334,10 @@ module Homebrew end # Formats and prints the livecheck result for a formula. - sig { params(info: Hash, verbose: T::Boolean).void } - def print_latest_version(info, verbose:) + sig { params(info: Hash, verbose: T::Boolean, ambiguous_cask: T::Boolean).void } + def print_latest_version(info, verbose:, ambiguous_cask: false) formula_or_cask_s = "#{Tty.blue}#{info[:formula] || info[:cask]}#{Tty.reset}" + formula_or_cask_s += " (cask)" if ambiguous_cask formula_or_cask_s += " (guessed)" if !info[:meta][:livecheckable] && verbose current_s = if info[:version][:newer_than_upstream] @@ -438,7 +467,6 @@ module Homebrew urls ||= checkable_urls(formula_or_cask) if debug - puts if formula puts "Formula: #{formula_name(formula, full_name: full_name)}" puts "Head only?: true" if formula.head_only?