From 49c03d1d25b63e0d2e1ec198c4fffbf123c43eba Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Thu, 8 Feb 2024 12:14:26 +0100 Subject: [PATCH] Refactor `Formulary::loader_for`. --- Library/Homebrew/api/formula.rb | 4 +- Library/Homebrew/cli/named_args.rb | 2 +- Library/Homebrew/diagnostic.rb | 9 +- Library/Homebrew/exceptions.rb | 16 +- Library/Homebrew/formula.rb | 19 +- Library/Homebrew/formulary.rb | 389 ++++++++++++++++-------- Library/Homebrew/tap_constants.rb | 14 +- Library/Homebrew/test/formula_spec.rb | 30 +- Library/Homebrew/test/formulary_spec.rb | 23 +- 9 files changed, 324 insertions(+), 182 deletions(-) diff --git a/Library/Homebrew/api/formula.rb b/Library/Homebrew/api/formula.rb index a8d2215295f722..8704ec6ad4342a 100644 --- a/Library/Homebrew/api/formula.rb +++ b/Library/Homebrew/api/formula.rb @@ -59,7 +59,7 @@ def download_and_cache_data! end private :download_and_cache_data! - sig { returns(Hash) } + sig { returns(T::Hash[String, Hash]) } def all_formulae unless cache.key?("formulae") json_updated = download_and_cache_data! @@ -69,7 +69,7 @@ def all_formulae cache["formulae"] end - sig { returns(Hash) } + sig { returns(T::Hash[String, String]) } def all_aliases unless cache.key?("aliases") json_updated = download_and_cache_data! diff --git a/Library/Homebrew/cli/named_args.rb b/Library/Homebrew/cli/named_args.rb index 04f81687ba9708..a1494035151e54 100644 --- a/Library/Homebrew/cli/named_args.rb +++ b/Library/Homebrew/cli/named_args.rb @@ -256,7 +256,7 @@ def to_paths(only: parent&.only_formula_or_cask, recurse_tap: false) paths = [] if formula_path.exist? || - (!CoreTap.instance.installed? && Homebrew::API::Formula.all_formulae.key?(path.basename)) + (!CoreTap.instance.installed? && Homebrew::API::Formula.all_formulae.key?(path.basename.to_s)) paths << formula_path end if cask_path.exist? || diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index b039069da96d0f..c14bbaeb744f8c 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -835,14 +835,7 @@ def check_deleted_formula kegs = Keg.all deleted_formulae = kegs.map do |keg| - next if Formulary.tap_paths(keg.name).any? - - unless EnvConfig.no_install_from_api? - # Formulae installed from the API should not count as deleted formulae - # but may not have a tap listed in their tab - tap = Tab.for_keg(keg).tap - next if (tap.blank? || tap.core_tap?) && Homebrew::API::Formula.all_formulae.key?(keg.name) - end + next if Formulary::FromNameLoader.try_new(keg.name, warn: false).nil? keg.name end.compact.uniq diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index e0861796c52462..e402a3b8280a6f 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -259,19 +259,19 @@ def initialize(tap, name, error) # Raised when a formula with the same name is found in multiple taps. class TapFormulaAmbiguityError < RuntimeError - attr_reader :name, :paths, :formulae + attr_reader :name, :taps - def initialize(name, paths) + def initialize(name, taps) @name = name - @paths = paths - @formulae = paths.map do |path| - "#{Tap.from_path(path).name}/#{path.basename(".rb")}" - end + @taps = taps + + formulae = taps.map { |tap| "#{tap}/#{name}" } + formula_list = formulae.map { |f| "\n * #{f}" }.join super <<~EOS - Formulae found in multiple taps: #{formulae.map { |f| "\n * #{f}" }.join} + Formulae found in multiple taps:#{formula_list} - Please use the fully-qualified name (e.g. #{formulae.first}) to refer to the formula. + Please use the fully-qualified name (e.g. #{formulae.first}) to refer to a specific formula. EOS end end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 5533a1cd6d2d81..75e12642ed6ec9 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -77,7 +77,7 @@ class Formula # The path to the alias that was used to identify this {Formula}. # e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Aliases/another-name-for-this-formula` - sig { returns(T.any(NilClass, Pathname, String)) } + sig { returns(T.nilable(Pathname)) } attr_reader :alias_path # The name of the alias that was used to identify this {Formula}. @@ -199,7 +199,7 @@ class Formula # @private sig { - params(name: String, path: Pathname, spec: Symbol, alias_path: T.any(NilClass, Pathname, String), + params(name: String, path: Pathname, spec: Symbol, alias_path: T.nilable(Pathname), tap: T.nilable(Tap), force_bottle: T::Boolean).void } def initialize(name, path, spec, alias_path: nil, tap: nil, force_bottle: false) @@ -326,18 +326,22 @@ def validate_attributes! # The alias path that was used to install this formula, if it exists. # Can differ from {#alias_path}, which is the alias used to find the formula, # and is specified to this instance. + sig { returns(T.nilable(Pathname)) } def installed_alias_path build_tab = build path = build_tab.source["path"] if build_tab.is_a?(Tab) + return unless path&.match?(%r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases}o) - return unless File.symlink?(path) + + path = Pathname(path) + return unless path.symlink? path end sig { returns(T.nilable(String)) } def installed_alias_name - File.basename(installed_alias_path) if installed_alias_path + installed_alias_path&.basename&.to_s end def full_installed_alias_name @@ -346,14 +350,13 @@ def full_installed_alias_name # The path that was specified to find this formula. def specified_path - alias_pathname = Pathname(T.must(alias_path)) if alias_path.present? - return alias_pathname if alias_pathname&.exist? + return alias_path if alias_path&.exist? return @unresolved_path if @unresolved_path.exist? return local_bottle_path if local_bottle_path.presence&.exist? - alias_pathname || @unresolved_path + alias_path || @unresolved_path end # The name specified to find this formula. @@ -2379,7 +2382,7 @@ def to_hash_with_variations(hash_method: :to_hash) # Take from API, merging in local install status. if loaded_from_api? && !Homebrew::EnvConfig.no_install_from_api? - json_formula = Homebrew::API::Formula.all_formulae[name].dup + json_formula = Homebrew::API::Formula.all_formulae.fetch(name).dup return json_formula.merge( hash.slice("name", "installed", "linked_keg", "pinned", "outdated"), ) diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index e12eb93962cfd9..a23af198187b26 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -16,6 +16,7 @@ # # @api private module Formulary + extend Context extend Cachable URL_START_REGEX = %r{(https?|ftp|file)://} @@ -480,17 +481,26 @@ class FormulaLoader include Context # The formula's name + sig { returns(String) } attr_reader :name + # The formula's ruby file's path or filename + sig { returns(Pathname) } attr_reader :path + # The name used to install the formula + sig { returns(T.nilable(Pathname)) } attr_reader :alias_path + # The formula's tap (nil if it should be implicitly determined) + sig { returns(T.nilable(Tap)) } attr_reader :tap - def initialize(name, path, tap: nil) + sig { params(name: String, path: Pathname, alias_path: Pathname, tap: Tap).void } + def initialize(name, path, alias_path: T.unsafe(nil), tap: T.unsafe(nil)) @name = name @path = path + @alias_path = alias_path if alias_path @tap = tap end @@ -519,7 +529,17 @@ def load_file(flags:, ignore_errors:) end # Loads a formula from a bottle. - class BottleLoader < FormulaLoader + class FromBottleLoader < FormulaLoader + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + ref = ref.to_s + + new(ref) if HOMEBREW_BOTTLES_EXTNAME_REGEX.match?(ref) + end + def initialize(bottle_name) case bottle_name when URL_START_REGEX @@ -562,27 +582,68 @@ def get_formula(spec, force_bottle: false, flags: [], ignore_errors: false, **) end end - # Loads a formula from a path to an alias. - class AliasLoader < FormulaLoader - def initialize(alias_path) - path = alias_path.resolved_path - name = path.basename(".rb").to_s - super name, path - @alias_path = alias_path.to_s - end - end - # Loads formulae from disk using a path. class FromPathLoader < FormulaLoader - def initialize(path) - path = Pathname.new(path).expand_path + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + path = case ref + when String + Pathname(ref) + when Pathname + ref + else + return + end + + return unless path.expand_path.exist? + + options = if path.symlink? + alias_path = path + path = alias_path.resolved_path + { alias_path: alias_path } + else + {} + end + + return if path.extname != ".rb" + + new(path, **options) + end + + sig { params(path: T.any(Pathname, String), alias_path: Pathname).void } + def initialize(path, alias_path: T.unsafe(nil)) + path = Pathname(path).expand_path name = path.basename(".rb").to_s - super name, path, tap: Homebrew::API.tap_from_source_download(path) + alias_path = alias_path&.expand_path + alias_dir = alias_path&.dirname + + tap = Tap.from_path(path) || Homebrew::API.tap_from_source_download(path) + + options = { + alias_path: (alias_path if alias_dir == tap&.alias_dir), + tap: tap, + + }.compact + + super(name, path, **options) end end - # Loads formulae from URLs. - class FromUrlLoader < FormulaLoader + # Loads formula from a URI. + class FromURILoader < FormulaLoader + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + ref = ref.to_s + + new(ref, from: from) if URL_START_REGEX.match?(ref) + end + attr_reader :url sig { params(url: T.any(URI::Generic, String), from: T.nilable(Symbol)).void } @@ -621,7 +682,45 @@ def load_file(flags:, ignore_errors:) end # Loads tapped formulae. - class TapLoader < FormulaLoader + class FromTapLoader < FormulaLoader + sig { returns(Tap) } + attr_reader :tap + + sig { returns(Pathname) } + attr_reader :path + + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + ref = ref.to_s + return unless (match = ref.match(HOMEBREW_TAP_FORMULA_REGEX)) + + alias_name = T.cast(match[:name], String) + + name, tap, type = Formulary.tap_formula_name_type(ref, warn: warn) + path = Formulary.find_formula_in_tap(name, tap) + + options = if type == :alias + { alias_name: alias_name.downcase } + else + {} + end + + new(name, path, tap: tap, **options) + end + + sig { params(name: String, path: Pathname, tap: Tap, alias_name: String).void } + def initialize(name, path, tap:, alias_name: T.unsafe(nil)) + options = { + alias_path: (tap.alias_dir/alias_name if alias_name), + tap: tap, + }.compact + + super(name, path, **options) + end + def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false) super rescue FormulaUnreadableError => e @@ -638,17 +737,90 @@ def load_file(flags:, ignore_errors:) e.issues_url = tap.issues_url || tap.to_s raise end + end + + # Loads a formula from a name, as long as it exists only in a single tap. + module FromNameLoader + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(FromTapLoader)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + return unless ref.is_a?(String) + return if ref.include?("/") + + name = ref + + loaders = Tap.map { |tap| FromTapLoader.try_new("#{tap}/#{name}") } + .compact + .select { _1.path.exist? } + + case loaders.count + when 1 + loaders.first + when 2..Float::INFINITY + default_tap_loaders, other_loaders = *loaders.partition { _1.tap.core_tap? } + default_tap_loader = default_tap_loaders.first if default_tap_loaders.count + + # Put default tap last so that the error message always recommends + # using the fully-qualified name for non-default taps. + taps = other_loaders.map(&:tap) + default_tap_loaders.map(&:tap) + + error = TapFormulaAmbiguityError.new(name, taps) + + raise error unless default_tap_loader + + opoo error if warn + default_tap_loader + end + end + end - private + # Loads a formula from a formula file in a keg. + class FromKegLoader < FormulaLoader + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + ref = ref.to_s + + return unless (keg_formula = HOMEBREW_PREFIX/"opt/#{ref}/.brew/#{ref}.rb").file? - def find_formula_from_name(name, tap) - Formulary.find_formula_in_tap(name, tap) + new(ref, keg_formula) + end + end + + # Loads a formula from a cached formula file. + class FromCacheLoader < FormulaLoader + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + ref = ref.to_s + + return unless (cached_formula = HOMEBREW_CACHE_FORMULA/"#{ref}.rb").file? + + new(ref, cached_formula) end end # Pseudo-loader which will raise a {FormulaUnavailableError} when trying to load the corresponding formula. class NullLoader < FormulaLoader - def initialize(name) + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + return if ref.is_a?(URI::Generic) + + new(ref) + end + + sig { params(ref: T.any(String, Pathname)).void } + def initialize(ref) + name = File.basename(ref, ".rb") super name, Formulary.core_path(name) end @@ -674,10 +846,44 @@ def klass(flags:, ignore_errors:) end end - # Load formulae from the API. - class FormulaAPILoader < FormulaLoader - def initialize(name) - super name, Formulary.core_path(name) + # Load a formula from the API. + class FromAPILoader < FormulaLoader + sig { + params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean) + .returns(T.nilable(T.attached_class)) + } + def self.try_new(ref, from: T.unsafe(nil), warn: false) + return if Homebrew::EnvConfig.no_install_from_api? + return unless ref.is_a?(String) + + return unless (match = ref.match(HOMEBREW_DEFAULT_TAP_FORMULA_REGEX)) + + name = alias_name = T.cast(match[:name], String) + unless Homebrew::API::Formula.all_formulae.key?(name) || Homebrew::API::Formula.all_aliases.key?(name) || Homebrew::API::Formula.all_renames.key?(name) + return + end + + ref = "#{CoreTap.instance}/#{name}" + + name, tap, type = Formulary.tap_formula_name_type(ref, warn: warn) + + options = if type == :alias + { alias_name: alias_name.downcase } + else + {} + end + + new(name, tap: tap, **options) + end + + sig { params(name: String, tap: Tap, alias_name: String).void } + def initialize(name, tap: T.unsafe(nil), alias_name: T.unsafe(nil)) + options = { + alias_path: (CoreTap.instance.alias_dir/alias_name if alias_name), + tap: tap, + }.compact + + super(name, Formulary.core_path(name), **options) end def klass(flags:, ignore_errors:) @@ -694,14 +900,6 @@ def load_from_api(flags:) end end - # Load aliases from the API. - class AliasAPILoader < FormulaAPILoader - def initialize(alias_name) - super Homebrew::API::Formula.all_aliases[alias_name] - @alias_path = Formulary.core_alias_path(alias_name).to_s - end - end - # Return a {Formula} instance for the given reference. # `ref` is a string containing: # @@ -741,6 +939,7 @@ def self.factory( force_bottle: force_bottle, flags: flags, ignore_errors: ignore_errors }.compact + formula = loader_for(ref, **loader_options) .get_formula(spec, **formula_options) @@ -904,11 +1103,20 @@ def self.path(ref) def self.tap_formula_name_type(tapped_name, warn:) user, repo, name = tapped_name.split("/", 3).map(&:downcase) - tap = Tap.fetch user, repo + tap = Tap.fetch(user, repo) type = nil - if (possible_alias = tap.alias_table[name].presence) - name = possible_alias + # FIXME: Remove the need to do this here. + alias_table_key = if tap.core_tap? + name + else + "#{tap}/#{name}" + end + + if (possible_alias = tap.alias_table[alias_table_key].presence) + # FIXME: Remove the need to split the name and instead make + # the alias table only contain short names. + name = possible_alias.split("/").last type = :alias elsif (new_name = tap.formula_renames[name].presence) old_name = name @@ -932,104 +1140,31 @@ def self.tap_formula_name_type(tapped_name, warn:) [name, tap, type] end - def self.tap_loader_for(tapped_name, warn:) - name, tap, type = Formulary.tap_formula_name_type(tapped_name, warn: warn) - - if tap.core_tap? && !Homebrew::EnvConfig.no_install_from_api? - if type == :alias - alias_name = tapped_name[HOMEBREW_TAP_FORMULA_REGEX, 3] - return AliasAPILoader.new(alias_name) - elsif Homebrew::API::Formula.all_formulae.key?(name) - return FormulaAPILoader.new(name) + def self.loader_for(ref, from: T.unsafe(nil), warn: true) + options = { from: from, warn: warn }.compact + + [ + FromBottleLoader, + FromURILoader, + FromAPILoader, + FromTapLoader, + FromPathLoader, + FromNameLoader, + FromKegLoader, + FromCacheLoader, + NullLoader, + ].each do |loader_class| + if (loader = loader_class.try_new(ref, **options)) + $stderr.puts "#{$PROGRAM_NAME} (#{loader_class}): loading #{ref}" if debug? + return loader end end - - path = find_formula_in_tap(name, tap) - TapLoader.new(name, path, tap: tap) - end - - def self.loader_for(ref, from: nil, warn: true) - case ref - when HOMEBREW_BOTTLES_EXTNAME_REGEX - return BottleLoader.new(ref) - when URL_START_REGEX - return FromUrlLoader.new(ref, from: from) - when HOMEBREW_TAP_FORMULA_REGEX - return Formulary.tap_loader_for(ref, warn: warn) - end - - pathname_ref = Pathname.new(ref) - return FromPathLoader.new(ref) if File.extname(ref) == ".rb" && pathname_ref.expand_path.exist? - - unless Homebrew::EnvConfig.no_install_from_api? - return FormulaAPILoader.new(ref) if Homebrew::API::Formula.all_formulae.key?(ref) - return AliasAPILoader.new(ref) if Homebrew::API::Formula.all_aliases.key?(ref) - end - - formula_with_that_name = core_path(ref) - return FormulaLoader.new(ref, formula_with_that_name) if formula_with_that_name.file? - - possible_alias = if pathname_ref.absolute? - pathname_ref - else - core_alias_path(ref) - end - return AliasLoader.new(possible_alias) if possible_alias.symlink? - - case (possible_tap_formulae = tap_paths(ref)).count - when 1 - path = possible_tap_formulae.first.resolved_path - name = path.basename(".rb").to_s - return FormulaLoader.new(name, path) - when 2..Float::INFINITY - raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae) - end - - if CoreTap.instance.formula_renames.key?(ref) - return Formulary.tap_loader_for("#{CoreTap.instance}/#{ref}", warn: warn) - end - - possible_taps = Tap.select { |tap| tap.formula_renames.key?(ref) } - - case possible_taps.count - when 1 - return Formulary.tap_loader_for("#{possible_taps.first}/#{ref}", warn: warn) - when 2..Float::INFINITY - possible_tap_newname_formulae = possible_taps.map { |tap| "#{tap}/#{tap.formula_renames[ref]}" } - raise TapFormulaWithOldnameAmbiguityError.new(ref, possible_tap_newname_formulae) - end - - if (keg_formula = HOMEBREW_PREFIX/"opt/#{ref}/.brew/#{ref}.rb").file? - return FormulaLoader.new(ref, keg_formula) - end - - if (cached_formula = HOMEBREW_CACHE_FORMULA/"#{ref}.rb").file? - return FormulaLoader.new(ref, cached_formula) - end - - NullLoader.new(ref) end def self.core_path(name) find_formula_in_tap(name.to_s.downcase, CoreTap.instance) end - def self.core_alias_path(name) - CoreTap.instance.alias_dir/name.to_s.downcase - end - - def self.tap_paths(name) - name = name.to_s.downcase - Tap.map do |tap| - formula_path = find_formula_in_tap(name, tap) - - alias_path = tap.alias_dir/name - next alias_path if !formula_path.exist? && alias_path.exist? - - formula_path - end.select(&:file?) - end - sig { params(name: String, tap: Tap).returns(Pathname) } def self.find_formula_in_tap(name, tap) filename = if name.end_with?(".rb") diff --git a/Library/Homebrew/tap_constants.rb b/Library/Homebrew/tap_constants.rb index 670821f660d379..9bc5ecd2bb44d7 100644 --- a/Library/Homebrew/tap_constants.rb +++ b/Library/Homebrew/tap_constants.rb @@ -2,19 +2,23 @@ # frozen_string_literal: true # Match taps' formulae, e.g. `someuser/sometap/someformula` -HOMEBREW_TAP_FORMULA_REGEX = T.let(%r{^([\w-]+)/([\w-]+)/([\w+-.@]+)$}, Regexp) +HOMEBREW_TAP_FORMULA_REGEX = T.let(%r{^(?[\w-]+)/(?[\w-]+)/(?[\w+-.@]+)$}, Regexp) +# Match default formula taps' formulae, e.g. `homebrew/core/someformula` or `someformula` +HOMEBREW_DEFAULT_TAP_FORMULA_REGEX = T.let(%r{^(?:[Hh]omebrew/(?:homebrew-)?core/)?(?[a-z0-9\-_]+)$}, Regexp) # Match taps' casks, e.g. `someuser/sometap/somecask` HOMEBREW_TAP_CASK_REGEX = T.let(%r{^([\w-]+)/([\w-]+)/([a-z0-9\-_]+)$}, Regexp) # Match main cask taps' casks, e.g. `homebrew/cask/somecask` or `somecask` HOMEBREW_MAIN_TAP_CASK_REGEX = T.let(%r{^(?:[Hh]omebrew/(?:homebrew-)?cask/)?(?[a-z0-9\-_]+)$}, Regexp) # Match taps' directory paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap` HOMEBREW_TAP_DIR_REGEX = T.let( - %r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/(?[\w-]+)/(?[\w-]+)}, Regexp + %r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/(?[\w-]+)/(?[\w-]+)}, + Regexp, ) # Match taps' formula paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula` HOMEBREW_TAP_PATH_REGEX = T.let(Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{(?:/.*)?$}.source).freeze, Regexp) # Match official taps' casks, e.g. `homebrew/cask/somecask or homebrew/cask-versions/somecask` -HOMEBREW_CASK_TAP_CASK_REGEX = - T.let(%r{^(?:([Cc]askroom)/(cask|versions)|([Hh]omebrew)/(?:homebrew-)?(cask|cask-[\w-]+))/([\w+-.]+)$}, - Regexp) +HOMEBREW_CASK_TAP_CASK_REGEX = T.let( + %r{^(?:([Cc]askroom)/(cask|versions)|([Hh]omebrew)/(?:homebrew-)?(cask|cask-[\w-]+))/([\w+-.]+)$}, + Regexp, +) HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX = T.let(/^(home|linux)brew-/, Regexp) diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index 414b6004dd7386..de9eba13577965 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -28,7 +28,7 @@ let(:path) { Formulary.core_path(name) } let(:spec) { :stable } let(:alias_name) { "baz@1" } - let(:alias_path) { (CoreTap.instance.alias_dir/alias_name).to_s } + let(:alias_path) { CoreTap.instance.alias_dir/alias_name } let(:f) { klass.new(name, path, spec) } let(:f_alias) { klass.new(name, path, spec, alias_path: alias_path) } @@ -190,11 +190,11 @@ end alias_name = "bar" - alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}" + alias_path = CoreTap.instance.alias_dir/alias_name CoreTap.instance.alias_dir.mkpath FileUtils.ln_sf f.path, alias_path - f.build = Tab.new(source: { "path" => alias_path }) + f.build = Tab.new(source: { "path" => alias_path.to_s }) expect(f.installed_alias_path).to eq(alias_path) expect(f.installed_alias_name).to eq(alias_name) @@ -225,12 +225,12 @@ end alias_name = "bar" + alias_path = tap.alias_dir/alias_name full_alias_name = "#{tap.user}/#{tap.repo}/#{alias_name}" - alias_path = "#{tap.alias_dir}/#{alias_name}" tap.alias_dir.mkpath FileUtils.ln_sf f.path, alias_path - f.build = Tab.new(source: { "path" => alias_path }) + f.build = Tab.new(source: { "path" => alias_path.to_s }) expect(f.installed_alias_path).to eq(alias_path) expect(f.installed_alias_name).to eq(alias_name) @@ -451,7 +451,7 @@ FileUtils.ln_sf f.path, source_path expect(f.alias_path).to eq(alias_path) - expect(f.installed_alias_path).to eq(source_path.to_s) + expect(f.installed_alias_path).to eq(source_path) end end @@ -491,14 +491,14 @@ end specify "with alias path with a path" do - alias_path = "#{CoreTap.instance.alias_dir}/alias" - different_alias_path = "#{CoreTap.instance.alias_dir}/another_alias" + alias_path = CoreTap.instance.alias_dir/"alias" + different_alias_path = CoreTap.instance.alias_dir/"another_alias" formula_with_alias = formula "foo" do url "foo-1.0" end formula_with_alias.build = Tab.empty - formula_with_alias.build.source["path"] = alias_path + formula_with_alias.build.source["path"] = alias_path.to_s formula_without_alias = formula "bar" do url "bar-1.0" @@ -510,7 +510,7 @@ url "baz-1.0" end formula_with_different_alias.build = Tab.empty - formula_with_different_alias.build.source["path"] = different_alias_path + formula_with_different_alias.build.source["path"] = different_alias_path.to_s formulae = [ formula_with_alias, @@ -1239,8 +1239,8 @@ def pour_bottle? end let(:tab) { Tab.empty } - let(:alias_path) { "#{CoreTap.instance.alias_dir}/bar" } let(:alias_name) { "bar" } + let(:alias_path) { CoreTap.instance.alias_dir/alias_name } before do allow(described_class).to receive(:installed).and_return([f]) @@ -1261,7 +1261,7 @@ def pour_bottle? end specify "alias changes when not changed" do - tab.source["path"] = alias_path + tab.source["path"] = alias_path.to_s stub_formula_loader(f, alias_name) CoreTap.instance.alias_dir.mkpath @@ -1276,7 +1276,7 @@ def pour_bottle? end specify "alias changes when new alias target" do - tab.source["path"] = alias_path + tab.source["path"] = alias_path.to_s stub_formula_loader(new_formula, alias_name) CoreTap.instance.alias_dir.mkpath @@ -1291,7 +1291,7 @@ def pour_bottle? end specify "alias changes when old formulae installed" do - tab.source["path"] = alias_path + tab.source["path"] = alias_path.to_s stub_formula_loader(new_formula, alias_name) CoreTap.instance.alias_dir.mkpath @@ -1332,8 +1332,8 @@ def pour_bottle? end end - let(:alias_path) { "#{f.tap.alias_dir}/bar" } let(:alias_name) { "bar" } + let(:alias_path) { f.tap.alias_dir/alias_name } def setup_tab_for_prefix(prefix, options = {}) prefix.mkpath diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb index 9cb4b64b803240..e1c3ed7b26a288 100644 --- a/Library/Homebrew/test/formulary_spec.rb +++ b/Library/Homebrew/test/formulary_spec.rb @@ -111,7 +111,7 @@ class Wrong#{described_class.class_s(formula_name)} < Formula it "raises an error" do expect do described_class.factory(formula_name) - end.to raise_error(FormulaClassUnavailableError) + end.to raise_error(TapFormulaClassUnavailableError) end end @@ -139,12 +139,13 @@ class Wrong#{described_class.class_s(formula_name)} < Formula context "when given an alias" do subject(:formula) { described_class.factory("foo") } - let(:alias_dir) { CoreTap.instance.alias_dir.tap(&:mkpath) } + let(:alias_dir) { CoreTap.instance.alias_dir } let(:alias_path) { alias_dir/"foo" } before do alias_dir.mkpath FileUtils.ln_s formula_path, alias_path + CoreTap.instance.clear_cache end it "returns a Formula" do @@ -152,7 +153,7 @@ class Wrong#{described_class.class_s(formula_name)} < Formula end it "calling #alias_path on the returned Formula returns the alias path" do - expect(formula.alias_path).to eq(alias_path.to_s) + expect(formula.alias_path).to eq(alias_path) end end @@ -231,16 +232,22 @@ class Wrong#{described_class.class_s(formula_name)} < Formula let(:tap) { Tap.new("homebrew", "foo") } let(:another_tap) { Tap.new("homebrew", "bar") } let(:formula_path) { tap.path/"Formula/#{formula_name}.rb" } + let(:alias_name) { "bar" } + let(:alias_dir) { tap.alias_dir } + let(:alias_path) { alias_dir/alias_name } + + before do + alias_dir.mkpath + FileUtils.ln_s formula_path, alias_path + tap.clear_cache + end it "returns a Formula when given a name" do expect(described_class.factory(formula_name)).to be_a(Formula) end it "returns a Formula from an Alias path" do - alias_dir = tap.path/"Aliases" - alias_dir.mkpath - FileUtils.ln_s formula_path, alias_dir/"bar" - expect(described_class.factory("bar")).to be_a(Formula) + expect(described_class.factory(alias_name)).to be_a(Formula) end it "raises an error when the Formula cannot be found" do @@ -371,7 +378,7 @@ def formula_json_contents(extra_items = {}) end before do - allow(described_class).to receive(:loader_for).and_return(described_class::FormulaAPILoader.new(formula_name)) + allow(described_class).to receive(:loader_for).and_return(described_class::FromAPILoader.new(formula_name)) # don't try to load/fetch gcc/glibc allow(DevelopmentTools).to receive_messages(needs_libc_formula?: false, needs_compiler_formula?: false)