diff --git a/Library/Homebrew/cask/cask.rb b/Library/Homebrew/cask/cask.rb index ead2b69214..b6a80e667e 100644 --- a/Library/Homebrew/cask/cask.rb +++ b/Library/Homebrew/cask/cask.rb @@ -19,10 +19,48 @@ module Cask extend Searchable include Metadata - attr_reader :token, :sourcefile_path, :source, :config, :default_config, :loaded_from_api + HOME_PLACEHOLDER = "$HOME" + HOMEBREW_PREFIX_PLACEHOLDER = "$HOMEBREW_PREFIX" + APPDIR_PLACEHOLDER = "$APPDIR" + + # TODO: can be removed when API JSON is regenerated with HOMEBREW_PREFIX_PLACEHOLDER. + HOMEBREW_OLD_PREFIX_PLACEHOLDER = "$(brew --prefix)" + + attr_reader :token, :sourcefile_path, :source, :config, :default_config, :loaded_from_api, :loader attr_accessor :download, :allow_reassignment + class << self + def generating_hash! + return if generating_hash? + + # Apply monkeypatches for API generation + @old_homebrew_prefix = HOMEBREW_PREFIX + @old_home = Dir.home + Object.send(:remove_const, :HOMEBREW_PREFIX) + Object.const_set(:HOMEBREW_PREFIX, Pathname(HOMEBREW_PREFIX_PLACEHOLDER)) + ENV["HOME"] = HOME_PLACEHOLDER + + @generating_hash = true + end + + def generated_hash! + return unless generating_hash? + + # Revert monkeypatches for API generation + Object.send(:remove_const, :HOMEBREW_PREFIX) + Object.const_set(:HOMEBREW_PREFIX, @old_homebrew_prefix) + ENV["HOME"] = @old_home + + @generating_hash = false + end + + def generating_hash? + @generating_hash ||= false + @generating_hash == true + end + end + def self.all # TODO: ideally avoid using ARGV by moving to e.g. CLI::Parser if ARGV.exclude?("--eval-all") && !Homebrew::EnvConfig.eval_all? @@ -45,13 +83,14 @@ module Cask end def initialize(token, sourcefile_path: nil, source: nil, tap: nil, config: nil, - allow_reassignment: false, loaded_from_api: false, &block) + allow_reassignment: false, loaded_from_api: false, loader: nil, &block) @token = token @sourcefile_path = sourcefile_path @source = source @tap = tap @allow_reassignment = allow_reassignment @loaded_from_api = loaded_from_api + @loader = loader @block = block @default_config = config || Config.new @@ -251,7 +290,7 @@ module Cask "outdated" => outdated?, "sha256" => sha256, "artifacts" => artifacts_list, - "caveats" => (to_h_string_gsubs(caveats) unless caveats.empty?), + "caveats" => (caveats unless caveats.empty?), "depends_on" => depends_on, "conflicts_with" => conflicts_with, "container" => container&.pairs, @@ -315,52 +354,10 @@ module Cask when Artifact::AbstractFlightBlock # Only indicate whether this block is used as we don't load it from the API { artifact.summarize => nil } - when Artifact::Relocated - # Don't replace the Homebrew prefix in the source path since the source could include /usr/local - source, *args = artifact.to_args - { artifact.class.dsl_key => [to_h_string_gsubs(source, replace_prefix: false), *to_h_gsubs(args)] } else - { artifact.class.dsl_key => to_h_gsubs(artifact.to_args) } + { artifact.class.dsl_key => artifact.to_args } end end end - - def to_h_string_gsubs(string, replace_prefix: true) - string = string.to_s.gsub(Dir.home, "$HOME") - - if replace_prefix - string.gsub(HOMEBREW_PREFIX, "$(brew --prefix)") - else - string.gsub(Caskroom.path, "$(brew --prefix)/Caskroom") - end - end - - def to_h_array_gsubs(array) - array.to_a.map do |value| - to_h_gsubs(value) - end - end - - def to_h_hash_gsubs(hash) - hash.to_h.transform_values do |value| - to_h_gsubs(value) - end - rescue TypeError - to_h_array_gsubs(hash) - end - - def to_h_gsubs(value) - return value if value.blank? - - if value.respond_to? :to_h - to_h_hash_gsubs(value) - elsif value.respond_to? :to_a - to_h_array_gsubs(value) - elsif [true, false].include? value - value - else - to_h_string_gsubs(value) - end - end end end diff --git a/Library/Homebrew/cask/cask_loader.rb b/Library/Homebrew/cask/cask_loader.rb index 39240df11a..7fa4689acc 100644 --- a/Library/Homebrew/cask/cask_loader.rb +++ b/Library/Homebrew/cask/cask_loader.rb @@ -237,13 +237,9 @@ module Cask return FromContentLoader.new(cask_source).load(config: config) end - # convert generic string replacements into actual ones - json_cask[:artifacts] = json_cask[:artifacts].map(&method(:from_h_hash_gsubs)) - json_cask[:caveats] = from_h_string_gsubs(json_cask[:caveats]) - tap = Tap.fetch(json_cask[:tap]) if json_cask[:tap].to_s.include?("/") - Cask.new(token, tap: tap, source: cask_source, config: config, loaded_from_api: true) do + Cask.new(token, tap: tap, source: cask_source, config: config, loaded_from_api: true, loader: self) do version json_cask[:version] if json_cask[:sha256] == "no_check" @@ -300,45 +296,53 @@ module Cask end json_cask[:artifacts].each do |artifact| + # convert generic string replacements into actual ones + artifact = cask.loader.from_h_hash_gsubs(artifact, appdir) key = artifact.keys.first send(key, *artifact[key]) end - caveats json_cask[:caveats] if json_cask[:caveats].present? + if json_cask[:caveats].present? + # convert generic string replacements into actual ones + json_cask[:caveats] = cask.loader.from_h_string_gsubs(json_cask[:caveats], appdir) + caveats json_cask[:caveats] + end end end - private - - def from_h_string_gsubs(string) + def from_h_string_gsubs(string, appdir) + # TODO: HOMEBREW_OLD_PREFIX_PLACEHOLDER can be removed when API JSON is + # regenerated with HOMEBREW_PREFIX_PLACEHOLDER. string.to_s - .gsub("$HOME", Dir.home) - .gsub("$(brew --prefix)", HOMEBREW_PREFIX) + .gsub(Cask::HOME_PLACEHOLDER, Dir.home) + .gsub(Cask::HOMEBREW_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) + .gsub(Cask::APPDIR_PLACEHOLDER, appdir) + .gsub(Cask::HOMEBREW_OLD_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) end - def from_h_array_gsubs(array) + def from_h_array_gsubs(array, appdir) array.to_a.map do |value| - from_h_gsubs(value) + from_h_gsubs(value, appdir) end end - def from_h_hash_gsubs(hash) + def from_h_hash_gsubs(hash, appdir) hash.to_h.transform_values do |value| - from_h_gsubs(value) + from_h_gsubs(value, appdir) end rescue TypeError - from_h_array_gsubs(hash) + from_h_array_gsubs(hash, appdir) end - def from_h_gsubs(value) + def from_h_gsubs(value, appdir) return value if value.blank? if value.respond_to? :to_h - from_h_hash_gsubs(value) + from_h_hash_gsubs(value, appdir) elsif value.respond_to? :to_a - from_h_array_gsubs(value) + from_h_array_gsubs(value, appdir) elsif value.is_a? String - from_h_string_gsubs(value) + from_h_string_gsubs(value, appdir) else value end diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index e51ee42094..9617e849d0 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -376,6 +376,8 @@ module Cask # @api public def appdir + return Cask::APPDIR_PLACEHOLDER if Cask.generating_hash? + cask.config.appdir end end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 4251a1267d..4e6b179cde 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2119,7 +2119,7 @@ class Formula "uses_from_macos" => uses_from_macos_elements.uniq, "requirements" => [], "conflicts_with" => conflicts.map(&:name), - "caveats" => caveats&.gsub(HOMEBREW_PREFIX, "$(brew --prefix)"), + "caveats" => caveats&.gsub(HOMEBREW_PREFIX, HOMEBREW_PREFIX_PLACEHOLDER), "installed" => [], "linked_keg" => linked_version&.to_s, "pinned" => pinned?, diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 0e9bed96dc..301ed9a1fa 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -251,6 +251,7 @@ module Formulary @caveats_string = json_formula["caveats"] def caveats self.class.instance_variable_get(:@caveats_string) + &.gsub(HOMEBREW_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) end @tap_git_head_string = json_formula["tap_git_head"] diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 5013b4e895..011ec9def1 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -64,6 +64,7 @@ HOMEBREW_MACOS_ARM_DEFAULT_PREFIX = "/opt/homebrew" HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY = HOMEBREW_MACOS_ARM_DEFAULT_PREFIX HOMEBREW_LINUX_DEFAULT_PREFIX = "/home/linuxbrew/.linuxbrew" HOMEBREW_LINUX_DEFAULT_REPOSITORY = "#{HOMEBREW_LINUX_DEFAULT_PREFIX}/Homebrew" +HOMEBREW_PREFIX_PLACEHOLDER = "$HOMEBREW_PREFIX" HOMEBREW_PULL_API_REGEX = %r{https://api\.github\.com/repos/([\w-]+)/([\w-]+)?/pulls/(\d+)}.freeze diff --git a/Library/Homebrew/test/cask/cask_spec.rb b/Library/Homebrew/test/cask/cask_spec.rb index a6c563d012..d9c85e0069 100644 --- a/Library/Homebrew/test/cask/cask_spec.rb +++ b/Library/Homebrew/test/cask/cask_spec.rb @@ -300,5 +300,18 @@ describe Cask::Cask, :cask do expect(h).to be_a(Hash) expect(JSON.pretty_generate(h["variations"])).to eq expected_sha256_variations.strip end + + it "returns the correct hash placeholders" do + described_class.generating_hash! + expect(described_class).to be_generating_hash + c = Cask::CaskLoader.load("placeholders") + h = c.to_hash_with_variations + described_class.generated_hash! + expect(described_class).not_to be_generating_hash + + expect(h).to be_a(Hash) + expect(h["artifacts"].first[:binary].first).to eq "$APPDIR/some/path" + expect(h["caveats"]).to eq "$HOMEBREW_PREFIX and $HOME\n" + end end end diff --git a/Library/Homebrew/test/cask/cmd/list_spec.rb b/Library/Homebrew/test/cask/cmd/list_spec.rb index 3c1518cafc..9dab951c1d 100644 --- a/Library/Homebrew/test/cask/cmd/list_spec.rb +++ b/Library/Homebrew/test/cask/cmd/list_spec.rb @@ -117,7 +117,7 @@ describe Cask::Cmd::List, :cask do { "zap": [ { - "trash": "$HOME/support/fixtures/cask/caffeine/org.example.caffeine.plist" + "trash": "#{TEST_FIXTURE_DIR}/cask/caffeine/org.example.caffeine.plist" } ] } diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/placeholders.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/placeholders.rb new file mode 100644 index 0000000000..81b4548b9b --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/placeholders.rb @@ -0,0 +1,11 @@ +cask "placeholders" do + version "2.61" + sha256 "e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68" + + url "file://#{TEST_FIXTURE_DIR}/cask/transmission-2.61.dmg" + homepage "https://brew.sh/placeholders" + + binary "#{appdir}/some/path" + + caveats "#{HOMEBREW_PREFIX} and #{Dir.home}" +end