From 1c10a6260fb4f4036d68b4c34acc6135b26b93d6 Mon Sep 17 00:00:00 2001 From: Zhiming Wang <zmwangx@gmail.com> Date: Sun, 25 Dec 2016 18:54:08 -0500 Subject: [PATCH] Hint at new location of migrated formulae Partial implementation of https://github.com/Homebrew/brew-evolution/pull/15, along with the ability to search for deleted formulae in git history (inspired by #1996) which is not described in the proposal. See also: #1371. --- Library/Homebrew/cmd/info.rb | 22 ++++++++-- Library/Homebrew/cmd/install.rb | 13 ++++++ Library/Homebrew/exceptions.rb | 13 ++++++ Library/Homebrew/historic.rb | 57 ++++++++++++++++++++++++++ Library/Homebrew/test/historic_test.rb | 46 +++++++++++++++++++++ 5 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 Library/Homebrew/historic.rb create mode 100644 Library/Homebrew/test/historic_test.rb diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index b7de0005ca..2d5e34ce2c 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -23,6 +23,7 @@ require "formula" require "keg" require "tab" require "json" +require "historic" module Homebrew module_function @@ -54,10 +55,25 @@ module Homebrew else info_formula Formulary.find_with_priority(f) end - rescue FormulaUnavailableError + rescue FormulaUnavailableError => e # No formula with this name, try a blacklist lookup - raise unless (blacklist = blacklisted?(f)) - puts blacklist + if (blacklist = blacklisted?(f)) + ofail "#{e.message}\n#{blacklist}" + 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 end diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index c825e27960..8a99bd3978 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -62,6 +62,7 @@ require "formula_installer" require "tap" require "hardware" require "development_tools" +require "historic" module Homebrew module_function @@ -212,6 +213,18 @@ module Homebrew 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..." diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 77da4489e8..e9885890bb 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -94,6 +94,19 @@ 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 diff --git a/Library/Homebrew/historic.rb b/Library/Homebrew/historic.rb new file mode 100644 index 0000000000..0afb9646ab --- /dev/null +++ b/Library/Homebrew/historic.rb @@ -0,0 +1,57 @@ +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 diff --git a/Library/Homebrew/test/historic_test.rb b/Library/Homebrew/test/historic_test.rb new file mode 100644 index 0000000000..d09656fe0b --- /dev/null +++ b/Library/Homebrew/test/historic_test.rb @@ -0,0 +1,46 @@ +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 -- GitLab