missing_formula: subsume historic logic.

These methods belong together so combine them in a single class to
provide a simpler API.
This commit is contained in:
Mike McQuaid 2017-03-20 20:37:12 +01:00
parent 80e95b684e
commit f59eb358c2
8 changed files with 116 additions and 150 deletions

View File

@ -23,7 +23,6 @@ require "formula"
require "keg"
require "tab"
require "json"
require "historic"
module Homebrew
module_function
@ -57,22 +56,10 @@ module Homebrew
end
rescue FormulaUnavailableError => e
# No formula with this name, try a missing formula lookup
if (missing_formula = Homebrew::MissingFormula.missing_formula(f))
ofail "#{e.message}\n#{missing_formula}"
if (reason = Homebrew::MissingFormula.reason(f))
ofail "#{e.message}\n#{reason}"
else
ofail e.message
# No point in searching if the specified tap isn't tapped yet
next if e.instance_of?(TapFormulaUnavailableError) && !e.tap.installed?
migrations = search_for_migrated_formula(f)
next unless migrations.empty?
ohai "Searching among deleted formulae..."
begin
search_for_deleted_formula(f)
rescue
nil
end
end
end
end

View File

@ -62,7 +62,6 @@ require "formula_installer"
require "tap"
require "hardware"
require "development_tools"
require "historic"
module Homebrew
module_function
@ -207,24 +206,13 @@ module Homebrew
# formula was found, but there's a problem with its implementation).
ofail e.message
rescue FormulaUnavailableError => e
if (missing_formula = Homebrew::MissingFormula.missing_formula(e.name))
ofail "#{e.message}\n#{missing_formula}"
if (reason = Homebrew::MissingFormula.reason(e.name))
ofail "#{e.message}\n#{reason}"
elsif e.name == "updog"
ofail "What's updog?"
else
ofail e.message
migrations = search_for_migrated_formula(e.name)
return unless migrations.empty?
ohai "Searching among deleted formulae..."
begin
search_for_deleted_formula(e.name)
return
rescue
nil
end
query = query_regexp(e.name)
ohai "Searching for similarly named formulae..."

View File

@ -67,12 +67,12 @@ module Homebrew
if $stdout.tty?
count = local_results.length + tap_results.length
if msg = Homebrew::MissingFormula.missing_formula(query)
if reason = Homebrew::MissingFormula.reason(query)
if count > 0
puts
puts "If you meant #{query.inspect} specifically:"
end
puts msg
puts reason
elsif count.zero?
puts "No formula found for #{query.inspect}."
begin

View File

@ -94,19 +94,6 @@ class TapFormulaUnavailableError < FormulaUnavailableError
end
end
class FormulaExistsError < RuntimeError
attr_reader :name, :path
def initialize(name, path)
@name = name
@path = path
end
def to_s
"Formula #{name} exists in #{path}"
end
end
class FormulaClassUnavailableError < FormulaUnavailableError
attr_reader :path
attr_reader :class_name

View File

@ -1,57 +0,0 @@
require "formulary"
require "tap"
module Homebrew
module_function
# name should not be qualified, since migration of qualified names is already
# handled in Formulary::TapLoader.formula_name_path.
def search_for_migrated_formula(name, options = {})
print_messages = options.fetch(:print_messages, true)
migrations = []
Tap.each do |old_tap|
new_tap_name = old_tap.tap_migrations[name]
next unless new_tap_name
migrations << [old_tap, new_tap_name]
next unless print_messages
deprecation = (new_tap_name == "homebrew/boneyard") ? "deprecated " : ""
puts "A #{deprecation}formula named \"#{name}\" has been migrated from #{old_tap} to #{new_tap_name}."
end
migrations
end
# name may be qualified.
def search_for_deleted_formula(name, options = {})
print_messages = options.fetch(:print_messages, true)
warn_shallow = options.fetch(:warn_shallow, false)
path = Formulary.path name
raise FormulaExistsError.new(name, path) if File.exist? path
path.to_s =~ HOMEBREW_TAP_PATH_REGEX
tap = Tap.new ($1 == "Homebrew" ? "homebrew" : $1), $2.strip_prefix("homebrew-")
raise TapUnavailableError, tap.name unless File.exist? tap.path
relpath = path.relative_path_from tap.path
cd tap.path
if warn_shallow && File.exist?(".git/shallow")
opoo <<-EOS.undend
The git repository is a shallow clone therefore the output may be incomplete.
Use `git fetch -C #{tap.path} --unshallow` to get the full repository.
EOS
end
log_cmd = "git log --name-only --max-count=1 --format=$'format:%H\\n%h' -- #{relpath}"
hash, hash_abbrev, relpath = Utils.popen_read(log_cmd).lines.map(&:chomp)
if hash.to_s.empty? || hash_abbrev.to_s.empty? || relpath.to_s.empty?
raise FormulaUnavailableError, name
end
if print_messages
puts "#{name} was deleted from #{tap.name} in commit #{hash_abbrev}."
puts "Run `brew boneyard #{name}` to show the formula's content prior to its removal."
end
[tap, relpath, hash, hash_abbrev]
end
end

View File

@ -6,7 +6,7 @@ module Homebrew
module MissingFormula
class << self
def reason(name)
blacklisted_reason(name)
blacklisted_reason(name) || tap_migration_reason(name) || deleted_reason(name)
end
def blacklisted_reason(name)
@ -100,6 +100,59 @@ module Homebrew
end
alias generic_blacklisted_reason blacklisted_reason
def tap_migration_reason(name)
message = nil
Tap.each do |old_tap|
new_tap_name = old_tap.tap_migrations[name]
next unless new_tap_name
message = <<-EOS.undent
It was migrated from #{old_tap} to #{new_tap_name}.
You can access it again by running:
brew tap #{new_tap_name}
EOS
break
end
message
end
def deleted_reason(name)
path = Formulary.path name
return if File.exist? path
tap = Tap.from_path(path)
return unless File.exist? tap.path
relative_path = path.relative_path_from tap.path
tap.path.cd do
# We know this may return incomplete results for shallow clones but
# we don't want to nag everyone with a shallow clone to unshallow it.
log_command = "git log --name-only --max-count=1 --format=%H\\\\n%h\\\\n%B -- #{relative_path}"
hash, short_hash, *commit_message, relative_path =
Utils.popen_read(log_command).gsub("\\n", "\n").lines.map(&:chomp)
if hash.to_s.empty? || short_hash.to_s.empty? ||
relative_path.to_s.empty?
return
end
commit_message = commit_message.reject(&:empty?).join("\n ")
commit_message.sub!(/ \(#(\d+)\)$/, " (#{tap.issues_url}/\\1)")
commit_message.gsub!(/(Closes|Fixes) #(\d+)/, "\\1 #{tap.issues_url}/\\2")
<<-EOS.undent
#{name} was deleted from #{tap.name} in commit #{short_hash}:
#{commit_message}
To show the formula before removal run:
git -C "$(brew --repo #{tap})" show #{short_hash}^:#{relative_path}
If you still use this formula consider creating your own tap:
http://docs.brew.sh/How-to-Create-and-Maintain-a-Tap.html
EOS
end
end
require "extend/os/missing_formula"
end
end

View File

@ -1,46 +0,0 @@
require "testing_env"
require "historic"
class HistoricTest < Homebrew::TestCase
def setup
super
@path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
@path.mkpath
@tap = Tap.new("Homebrew", "foo")
(@path/"tap_migrations.json").write <<-EOS.undent
{ "migrated-formula": "homebrew/bar" }
EOS
(@path/"Formula/to-delete.rb").write "placeholder"
@path.cd do
shutup do
system "git", "init"
system "git", "add", "--all"
system "git", "commit", "-m", "initial state"
system "git", "rm", "Formula/to-delete.rb"
system "git", "commit", "-m", "delete formula 'to-delete'"
end
end
end
def teardown
@path.rmtree
super
end
def test_search_for_migrated_formula
migrations = Homebrew.search_for_migrated_formula("migrated-formula", print_messages: false)
assert_equal [[@tap, "homebrew/bar"]], migrations
end
def test_search_for_deleted_formula
tap, relpath, hash, = Homebrew.search_for_deleted_formula("homebrew/foo/to-delete",
print_messages: false)
assert_equal tap, @tap
assert_equal relpath, "Formula/to-delete.rb"
assert_equal `git rev-parse HEAD`.chomp, hash
end
end

View File

@ -1,13 +1,13 @@
require "missing_formula"
describe Homebrew::MissingFormula do
context ".reason" do
context "::reason" do
subject { described_class.reason("gem") }
it { is_expected.to_not be_nil }
end
context ".blacklisted_reason" do
context "::blacklisted_reason" do
matcher(:be_blacklisted) do
match(&Homebrew::MissingFormula.method(:blacklisted_reason))
end
@ -122,4 +122,58 @@ describe Homebrew::MissingFormula do
end
end
end
context "::tap_migration_reason" do
subject { described_class.tap_migration_reason(formula) }
before do
Tap.clear_cache
tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
tap_path.mkpath
(tap_path/"tap_migrations.json").write <<-EOS.undent
{ "migrated-formula": "homebrew/bar" }
EOS
end
context "with a migrated formula" do
let(:formula) { "migrated-formula" }
it { is_expected.to_not be_nil }
end
context "with a missing formula" do
let(:formula) { "missing-formula" }
it { is_expected.to be_nil }
end
end
context "::deleted_reason" do
subject { described_class.deleted_reason(formula) }
before do
Tap.clear_cache
tap_path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
tap_path.mkpath
(tap_path/"deleted-formula.rb").write "placeholder"
tap_path.cd do
shutup do
system "git", "init"
system "git", "add", "--all"
system "git", "commit", "-m", "initial state"
system "git", "rm", "deleted-formula.rb"
system "git", "commit", "-m", "delete formula 'deleted-formula'"
end
end
end
context "with a deleted formula" do
let(:formula) { "homebrew/foo/deleted-formula" }
it { is_expected.to_not be_nil }
end
context "with a formula that never existed" do
let(:formula) { "homebrew/foo/missing-formula" }
it { is_expected.to be_nil }
end
end
end