Skip to content

Commit

Permalink
Merge pull request #16779 from reitermarkus/fix-load-from-path
Browse files Browse the repository at this point in the history
Fix loading casks/formulae from relative paths.
  • Loading branch information
MikeMcQuaid authored Mar 1, 2024
2 parents 0e1c1e3 + 3da0f8c commit f1eea64
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 24 deletions.
15 changes: 10 additions & 5 deletions Library/Homebrew/cask/cask_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ class FromTapLoader < FromPathLoader
def self.try_new(ref, warn: false)
ref = ref.to_s

return unless ref.match?(HOMEBREW_TAP_CASK_REGEX)
return unless (token_tap_type = CaskLoader.tap_cask_token_type(ref, warn: warn))

token, tap, = CaskLoader.tap_cask_token_type(ref, warn: warn)
token, tap, = token_tap_type
new("#{tap}/#{token}")
end

Expand Down Expand Up @@ -529,9 +529,12 @@ def self.load(ref, config: nil, warn: true)
self.for(ref, warn: warn).load(config: config)
end

sig { params(tapped_token: String, warn: T::Boolean).returns(T.nilable([String, Tap, T.nilable(Symbol)])) }
def self.tap_cask_token_type(tapped_token, warn:)
user, repo, token = tapped_token.split("/", 3).map(&:downcase)
tap = Tap.fetch(user, repo)
return unless (tap_with_token = Tap.with_cask_token(tapped_token))

tap, token = tap_with_token

type = nil

if (new_token = tap.cask_renames[token].presence)
Expand All @@ -550,7 +553,9 @@ def self.tap_cask_token_type(tapped_token, warn:)
opoo "Tap migration for #{tapped_token} points to itself, stopping recursion."
else
old_token = tap.core_cask_tap? ? token : tapped_token
token, tap, = tap_cask_token_type(new_tapped_token, warn: false)
return unless (token_tap_type = tap_cask_token_type(new_tapped_token, warn: false))

token, tap, = token_tap_type
new_token = new_tap.core_cask_tap? ? token : "#{tap}/#{token}"
type = :migration
end
Expand Down
4 changes: 2 additions & 2 deletions Library/Homebrew/cask/installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ def check_conflicts
return unless @cask.conflicts_with

@cask.conflicts_with[:cask].each do |conflicting_cask|
if (match = conflicting_cask.match(HOMEBREW_TAP_CASK_REGEX))
conflicting_cask_tap = Tap.fetch(match[1], match[2])
if (conflicting_cask_tap_with_token = Tap.with_cask_token(conflicting_cask))
conflicting_cask_tap, = conflicting_cask_tap_with_token
next unless conflicting_cask_tap.installed?
end

Expand Down
10 changes: 6 additions & 4 deletions Library/Homebrew/cmd/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,13 @@ def self.install
end

args.named.each do |name|
next if File.exist?(name)
next unless name =~ HOMEBREW_TAP_FORMULA_REGEX
if (tap_with_name = Tap.with_formula_name(name))
tap, = tap_with_name
elsif (tap_with_token = Tap.with_cask_token(name))
tap, = tap_with_token
end

tap = Tap.fetch(Regexp.last_match(1), Regexp.last_match(2))
tap.ensure_installed!
tap&.ensure_installed!
end

if args.ignore_dependencies?
Expand Down
4 changes: 3 additions & 1 deletion Library/Homebrew/dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ def initialize(name, tags = [])
@name = name
@tags = tags

@tap = Tap.fetch(Regexp.last_match(1), Regexp.last_match(2)) if name =~ HOMEBREW_TAP_FORMULA_REGEX
return unless (tap_with_name = Tap.with_formula_name(name))

@tap, = tap_with_name
end

def to_s
Expand Down
5 changes: 2 additions & 3 deletions Library/Homebrew/dev-cmd/extract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ def self.extract_args
def self.extract
args = extract_args.parse

if (match = args.named.first.match(HOMEBREW_TAP_FORMULA_REGEX))
name = match[3].downcase
source_tap = Tap.fetch(match[1], match[2])
if (tap_with_name = args.named.first&.then { Tap.with_formula_name(_1) })
source_tap, name = tap_with_name
else
name = args.named.first.downcase
source_tap = CoreTap.instance
Expand Down
26 changes: 17 additions & 9 deletions Library/Homebrew/formulary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -721,15 +721,15 @@ class FromTapLoader < FormulaLoader
}
def self.try_new(ref, from: T.unsafe(nil), warn: false)
ref = ref.to_s
return unless (name = ref[HOMEBREW_TAP_FORMULA_REGEX, :name])

alias_name = name
return unless (name_tap_type = Formulary.tap_formula_name_type(ref, warn: warn))

name, tap, type = Formulary.tap_formula_name_type(ref, warn: warn)
name, tap, type = name_tap_type
path = Formulary.find_formula_in_tap(name, tap)

options = if type == :alias
{ alias_name: alias_name.downcase }
# TODO: Simplify this by making `tap_formula_name_type` return the alias name.
{ alias_name: T.must(ref[HOMEBREW_TAP_FORMULA_REGEX, :name]).downcase }
else
{}
end
Expand Down Expand Up @@ -893,7 +893,9 @@ def self.try_new(ref, from: T.unsafe(nil), warn: false)

ref = "#{CoreTap.instance}/#{name}"

name, tap, type = Formulary.tap_formula_name_type(ref, warn: warn)
return unless (name_tap_type = Formulary.tap_formula_name_type(ref, warn: warn))

name, tap, type = name_tap_type

options = if type == :alias
{ alias_name: alias_name.downcase }
Expand Down Expand Up @@ -1127,9 +1129,12 @@ def self.path(ref)
loader_for(ref).path
end

sig { params(tapped_name: String, warn: T::Boolean).returns(T.nilable([String, Tap, T.nilable(Symbol)])) }
def self.tap_formula_name_type(tapped_name, warn:)
user, repo, name = tapped_name.split("/", 3).map(&:downcase)
tap = Tap.fetch(user, repo)
return unless (tap_with_name = Tap.with_formula_name(tapped_name))

tap, name = tap_with_name

type = nil

# FIXME: Remove the need to do this here.
Expand All @@ -1138,7 +1143,7 @@ def self.tap_formula_name_type(tapped_name, warn:)
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
name = T.must(possible_alias.split("/").last)
type = :alias
elsif (new_name = tap.formula_renames[name].presence)
old_name = tap.core_tap? ? name : tapped_name
Expand All @@ -1156,7 +1161,10 @@ def self.tap_formula_name_type(tapped_name, warn:)
opoo "Tap migration for #{tapped_name} points to itself, stopping recursion."
else
old_name = tap.core_tap? ? name : tapped_name
name, tap, = tap_formula_name_type(new_tapped_name, warn: false)
return unless (name_tap_type = tap_formula_name_type(new_tapped_name, warn: false))

name, tap, = name_tap_type

new_name = new_tap.core_tap? ? name : "#{tap}/#{name}"
type = :migration
end
Expand Down
32 changes: 32 additions & 0 deletions Library/Homebrew/tap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,38 @@ def self.from_path(path)
fetch(match[:user], match[:repo])
end

# @private
sig { params(name: String).returns(T.nilable([T.attached_class, String])) }
def self.with_formula_name(name)
return unless (match = name.match(HOMEBREW_TAP_FORMULA_REGEX))

user = T.must(match[:user])
repo = T.must(match[:repo])
name = T.must(match[:name])

# Relative paths are not taps.
return if [user, repo].intersect?([".", ".."])

tap = fetch(user, repo)
[tap, name.downcase]
end

# @private
sig { params(token: String).returns(T.nilable([T.attached_class, String])) }
def self.with_cask_token(token)
return unless (match = token.match(HOMEBREW_TAP_CASK_REGEX))

user = T.must(match[:user])
repo = T.must(match[:repo])
token = T.must(match[:token])

# Relative paths are not taps.
return if [user, repo].intersect?([".", ".."])

tap = fetch(user, repo)
[tap, token.downcase]
end

sig { returns(CoreCaskTap) }
def self.default_cask_tap
odisabled "`Tap.default_cask_tap`", "`CoreCaskTap.instance`"
Expand Down
16 changes: 16 additions & 0 deletions Library/Homebrew/test/formulary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,22 @@ def formula_json_contents(extra_items = {})
end

describe "::loader_for" do
context "when given a relative path with two slashes" do
it "returns a `FromPathLoader`" do
mktmpdir.cd do
FileUtils.mkdir "Formula"
FileUtils.touch "Formula/gcc.rb"
expect(described_class.loader_for("./Formula/gcc.rb")).to be_a Formulary::FromPathLoader
end
end
end

context "when given a tapped name" do
it "returns a `FromTapLoader`" do
expect(described_class.loader_for("homebrew/core/gcc")).to be_a Formulary::FromTapLoader
end
end

context "when not using the API" do
before do
ENV["HOMEBREW_NO_INSTALL_FROM_API"] = "1"
Expand Down
20 changes: 20 additions & 0 deletions Library/Homebrew/test/tap_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -599,4 +599,24 @@ class Foo < Formula
expect(described_class.fetch("my", "tap-with-@-symbol").repo_var_suffix).to eq "_MY_HOMEBREW_TAP_WITH___SYMBOL"
end
end

describe "::with_formula_name" do
it "returns the tap and formula name when given a full name" do
expect(described_class.with_formula_name("homebrew/core/gcc")).to eq [CoreTap.instance, "gcc"]
end

it "returns nil when given a relative path" do
expect(described_class.with_formula_name("./Formula/gcc.rb")).to be_nil
end
end

describe "::with_cask_token" do
it "returns the tap and cask token when given a full token" do
expect(described_class.with_cask_token("homebrew/cask/alfred")).to eq [CoreCaskTap.instance, "alfred"]
end

it "returns nil when given a relative path" do
expect(described_class.with_cask_token("./Casks/alfred.rb")).to be_nil
end
end
end

0 comments on commit f1eea64

Please sign in to comment.