Skip to content

Commit

Permalink
Revert "Merge pull request Homebrew#16608 from bevanjkay/revert-16596…
Browse files Browse the repository at this point in the history
…-cask-rename-migrations"

This reverts commit 5799e85, reversing
changes made to 6775171.
  • Loading branch information
reitermarkus committed Feb 7, 2024
1 parent 51ec419 commit e5a6f76
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 23 deletions.
99 changes: 83 additions & 16 deletions Library/Homebrew/cask/cask_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ module ILoader
extend T::Helpers
interface!

sig { overridable.params(_ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(_ref)
false
end

sig { abstract.params(config: Config).returns(Cask) }
def load(config:); end
end
Expand Down Expand Up @@ -50,10 +55,11 @@ def cask(header_token, **options, &block)

# Loads a cask from a string.
class FromContentLoader < AbstractContentLoader
sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
return false unless ref.respond_to?(:to_str)

content = ref.to_str
content = T.unsafe(ref).to_str

# Cache compiled regex
@regex ||= begin
Expand All @@ -66,7 +72,8 @@ def self.can_load?(ref)
content.match?(@regex)
end

def initialize(content, tap: nil)
sig { params(content: String, tap: Tap).void }
def initialize(content, tap: T.unsafe(nil))
super()

@content = content.force_encoding("UTF-8")
Expand All @@ -82,14 +89,19 @@ def load(config:)

# Loads a cask from a path.
class FromPathLoader < AbstractContentLoader
sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
path = Pathname(ref)
ref = Pathname(ref) if ref.is_a?(String)
return false unless ref.is_a?(Pathname)

path = ref
%w[.rb .json].include?(path.extname) && path.expand_path.exist?
end

attr_reader :token, :path

def initialize(path, token: nil)
sig { params(path: T.any(Pathname, String), token: String).void }
def initialize(path, token: T.unsafe(nil))
super()

path = Pathname(path).expand_path
Expand Down Expand Up @@ -134,16 +146,18 @@ def cask(header_token, **options, &block)

# Loads a cask from a URI.
class FromURILoader < FromPathLoader
sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
# Cache compiled regex
@uri_regex ||= begin
uri_regex = ::URI::DEFAULT_PARSER.make_regexp
Regexp.new("\\A#{uri_regex.source}\\Z", uri_regex.options)
end

return false unless ref.to_s.match?(@uri_regex)
uri = ref.to_s
return false unless uri.match?(@uri_regex)

uri = URI(ref)
uri = URI(uri)
return false unless uri.path

true
Expand Down Expand Up @@ -173,10 +187,12 @@ def load(config:)

# Loads a cask from a tap path.
class FromTapPathLoader < FromPathLoader
sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
super && !Tap.from_path(ref).nil?
end

sig { params(path: T.any(Pathname, String)).void }
def initialize(path)
super(path)
@tap = Tap.from_path(path)
Expand All @@ -185,10 +201,12 @@ def initialize(path)

# Loads a cask from a specific tap.
class FromTapLoader < FromTapPathLoader
sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
ref.to_s.match?(HOMEBREW_TAP_CASK_REGEX)
end

sig { params(tapped_name: String).void }
def initialize(tapped_name)
user, repo, token = tapped_name.split("/", 3)
tap = Tap.fetch(user, repo)
Expand All @@ -205,10 +223,12 @@ def load(config:)

# Loads a cask from the default tap path.
class FromDefaultTapPathLoader < FromTapPathLoader
sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
super CaskLoader.default_path(ref)
end

sig { params(ref: String).void }
def initialize(ref)
super CaskLoader.default_path(ref)
end
Expand All @@ -217,10 +237,13 @@ def initialize(ref)
# Loads a cask from an existing {Cask} instance.
class FromInstanceLoader
include ILoader

sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
ref.is_a?(Cask)
end

sig { params(cask: Cask).void }
def initialize(cask)
@cask = cask
end
Expand All @@ -233,25 +256,32 @@ def load(config:)
# Loads a cask from the JSON API.
class FromAPILoader
include ILoader

attr_reader :token, :path

sig { returns(T.nilable(Hash)) }
attr_reader :from_json

sig { params(ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(ref)
return false if Homebrew::EnvConfig.no_install_from_api?
return false unless ref.is_a?(String)
return false unless ref.match?(HOMEBREW_MAIN_TAP_CASK_REGEX)

token = ref.sub(%r{^homebrew/(?:homebrew-)?cask/}i, "")
return false unless (match = ref.match(HOMEBREW_MAIN_TAP_CASK_REGEX))

token = match[:token]
Homebrew::API::Cask.all_casks.key?(token)
end

def initialize(token, from_json: nil)
sig { params(token: String, from_json: Hash).void }
def initialize(token, from_json: T.unsafe(nil))
@token = token.sub(%r{^homebrew/(?:homebrew-)?cask/}i, "")
@path = CaskLoader.default_path(@token)
@from_json = from_json
end

def load(config:)
json_cask = @from_json || Homebrew::API::Cask.all_casks[token]
json_cask = from_json || Homebrew::API::Cask.all_casks[token]

cask_options = {
loaded_from_api: true,
Expand Down Expand Up @@ -401,7 +431,8 @@ def from_h_gsubs(value, appdir)

# Pseudo-loader which raises an error when trying to load the corresponding cask.
class NullLoader < FromPathLoader
def self.can_load?(*)
sig { params(_ref: T.any(String, Pathname, Cask, URI::Generic)).returns(T::Boolean) }
def self.can_load?(_ref)
true
end

Expand All @@ -424,6 +455,33 @@ def self.load(ref, config: nil, warn: true)
self.for(ref, warn: warn).load(config: config)
end

def self.tap_cask_token_type(tapped_token, warn:)
user, repo, token = tapped_token.split("/", 3).map(&:downcase)
tap = Tap.fetch(user, repo)
type = nil

if (new_token = tap.cask_renames[token].presence)
old_token = token
token = new_token
new_token = tap.core_cask_tap? ? token : "#{tap}/#{token}"
type = :rename
elsif (new_tap_name = tap.tap_migrations[token].presence)
new_tap_user, new_tap_repo, = new_tap_name.split("/")
new_tap_name = "#{new_tap_user}/#{new_tap_repo}"
new_tap = Tap.fetch(new_tap_name)
new_tap.ensure_installed!
new_tapped_token = "#{new_tap_name}/#{token}"
token, tap, = tap_cask_token_type(new_tapped_token, warn: false)
old_token = tapped_token
new_token = new_tap.core_cask_tap? ? token : new_tapped_token
type = :migration
end

opoo "Cask #{old_token} was renamed to #{new_token}." if warn && old_token && new_token

[token, tap, type]
end

def self.for(ref, need_path: false, warn: true)
[
FromInstanceLoader,
Expand All @@ -435,23 +493,32 @@ def self.for(ref, need_path: false, warn: true)
FromPathLoader,
FromDefaultTapPathLoader,
].each do |loader_class|
if loader_class.can_load?(ref)
$stderr.puts "#{$PROGRAM_NAME} (#{loader_class}): loading #{ref}" if debug?
return loader_class.new(ref)
next unless loader_class.can_load?(ref)

$stderr.puts "#{$PROGRAM_NAME} (#{loader_class}): loading #{ref}" if debug?

if [FromAPILoader, FromTapLoader].include?(loader_class)
ref = "#{CoreCaskTap.instance}/#{ref}" if CoreCaskTap.instance.cask_renames.key?(ref)
token, tap, = tap_cask_token_type(ref, warn: warn)
loader_class = T.cast(loader_class, T.any(T.class_of(FromAPILoader), T.class_of(FromTapLoader)))
return loader_class.new("#{tap}/#{token}")
end

return loader_class.new(ref)
end

case (possible_tap_casks = tap_paths(ref, warn: warn)).count
when 1
return FromTapPathLoader.new(possible_tap_casks.first)
when 2..Float::INFINITY
loaders = possible_tap_casks.map(&FromTapPathLoader.method(:new))

raise TapCaskAmbiguityError.new(ref, loaders)
end

possible_installed_cask = Cask.new(ref)
return FromPathLoader.new(possible_installed_cask.installed_caskfile) if possible_installed_cask.installed?
if (installed_caskfile = possible_installed_cask.installed_caskfile)
return FromPathLoader.new(installed_caskfile)
end

NullLoader.new(ref)
end
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/tap_constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# 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)
HOMEBREW_MAIN_TAP_CASK_REGEX = T.let(%r{^(?<tap>[Hh]omebrew/(?:homebrew-)?cask/)?(?<token>[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/(?<user>[\w-]+)/(?<repo>[\w-]+)}, Regexp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@

context "when not using the API" do
before do
allow(Homebrew::EnvConfig)
.to receive(:no_install_from_api?)
.and_return(true)
ENV["HOMEBREW_NO_INSTALL_FROM_API"] = "1"
end

it "returns false" do
Expand All @@ -38,9 +36,7 @@

context "when using the API" do
before do
allow(Homebrew::EnvConfig)
.to receive(:no_install_from_api?)
.and_return(false)
ENV.delete("HOMEBREW_NO_INSTALL_FROM_API")
end

it "returns true for valid token" do
Expand Down
70 changes: 70 additions & 0 deletions Library/Homebrew/test/cask/cask_loader_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

describe Cask::CaskLoader, :cask do
describe "::for" do
let(:tap) { CoreCaskTap.instance }

context "when a cask is renamed" do
let(:old_token) { "version-newest" }
let(:new_token) { "version-latest" }

let(:api_casks) do
[old_token, new_token].to_h do |token|
hash = described_class.load(new_token).to_hash_with_variations
json = JSON.pretty_generate(hash)
cask_json = JSON.parse(json)

[token, cask_json.except("token")]
end
end
let(:cask_renames) do
{ old_token => new_token }
end

before do
allow(Homebrew::API::Cask)
.to receive(:all_casks)
.and_return(api_casks)

allow(tap).to receive(:cask_renames)
.and_return(cask_renames)
end

context "when not using the API" do
before do
ENV["HOMEBREW_NO_INSTALL_FROM_API"] = "1"
end

it "warns when using the short token" do
expect do
expect(described_class.for("version-newest")).to be_a Cask::CaskLoader::FromTapPathLoader
end.to output(/version-newest was renamed to version-latest/).to_stderr
end

it "warns when using the full token" do
expect do
expect(described_class.for("homebrew/cask/version-newest")).to be_a Cask::CaskLoader::FromTapPathLoader
end.to output(/version-newest was renamed to version-latest/).to_stderr
end
end

context "when using the API" do
before do
ENV.delete("HOMEBREW_NO_INSTALL_FROM_API")
end

it "warns when using the short token" do
expect do
expect(described_class.for("version-newest")).to be_a Cask::CaskLoader::FromAPILoader
end.to output(/version-newest was renamed to version-latest/).to_stderr
end

it "warns when using the full token" do
expect do
expect(described_class.for("homebrew/cask/version-newest")).to be_a Cask::CaskLoader::FromAPILoader
end.to output(/version-newest was renamed to version-latest/).to_stderr
end
end
end
end
end

0 comments on commit e5a6f76

Please sign in to comment.