From 1c10a6260fb4f4036d68b4c34acc6135b26b93d6 Mon Sep 17 00:00:00 2001 From: Zhiming Wang Date: Sun, 25 Dec 2016 18:54:08 -0500 Subject: 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 (limited to 'Library') diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index b7de0005c..2d5e34ce2 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 c825e2796..8a99bd397 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 77da4489e..e9885890b 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 000000000..0afb9646a --- /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 000000000..d09656fe0 --- /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 -- cgit v1.2.3 From ac10b2ab50e987858b9fa5514785b12b7600787f Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 18 Mar 2017 16:56:59 +0200 Subject: Tap: add from_path helper method. This makes it easier to turn an arbitrary path into a tap path. --- Library/Homebrew/tap.rb | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Library') diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index ba55e3cf6..99138330b 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -41,6 +41,15 @@ class Tap CACHE.fetch(cache_key) { |key| CACHE[key] = Tap.new(user, repo) } end + def self.from_path(path) + path.to_s =~ HOMEBREW_TAP_PATH_REGEX + raise "Invalid tap path '#{path}'" unless $1 + fetch($1, $2) + rescue + # No need to error as a nil tap is sufficient to show failure. + nil + end + extend Enumerable # The user name of this {Tap}. Usually, it's the Github username of -- cgit v1.2.3 From 8cedd62750403d7f0c66cbeb714f6da50f50384f Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 18 Mar 2017 17:04:44 +0200 Subject: search: tweak specific formula match formatting. --- Library/Homebrew/cmd/search.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Library') diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index e834a00b5..4c8bbc77c 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -70,8 +70,7 @@ module Homebrew if msg = blacklisted?(query) if count > 0 puts - puts "If you meant #{query.inspect} precisely:" - puts + puts "If you meant #{query.inspect} specifically:" end puts msg elsif count.zero? -- cgit v1.2.3 From 623c95b3f8660d5c77936483ec9b9a4db16aff00 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 18 Mar 2017 16:57:40 +0200 Subject: cmd/log: improve output messaging. This wasn’t adapted to the new, multiple repository world. --- Library/Homebrew/cmd/log.rb | 28 ++++++++++++++++++++-------- Library/Homebrew/test/cmd/log_spec.rb | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'Library') diff --git a/Library/Homebrew/cmd/log.rb b/Library/Homebrew/cmd/log.rb index 22a3ee11d..9323c762d 100644 --- a/Library/Homebrew/cmd/log.rb +++ b/Library/Homebrew/cmd/log.rb @@ -9,20 +9,32 @@ module Homebrew def log if ARGV.named.empty? - cd HOMEBREW_REPOSITORY - git_log + git_log HOMEBREW_REPOSITORY else path = Formulary.path(ARGV.named.first) - cd path.dirname # supports taps - git_log path + tap = Tap.from_path(path) + git_log path.dirname, path, tap end end - def git_log(path = nil) - if File.exist? "#{`git rev-parse --show-toplevel`.chomp}/.git/shallow" + def git_log(cd_dir, path = nil, tap = nil) + cd cd_dir + repo = Utils.popen_read("git rev-parse --show-toplevel").chomp + if tap + name = tap.to_s + git_cd = "$(brew --repo #{tap})" + elsif cd_dir == HOMEBREW_REPOSITORY + name = "Homebrew/brew" + git_cd = "$(brew --repo)" + else + name, git_cd = cd_dir + end + + if File.exist? "#{repo}/.git/shallow" opoo <<-EOS.undent - The git repository is a shallow clone therefore the filtering may be incorrect. - Use `git fetch --unshallow` to get the full repository. + #{name} is a shallow clone so only partial output will be shown. + To get a full clone run: + git -C "#{git_cd}" fetch --unshallow EOS end args = ARGV.options_only diff --git a/Library/Homebrew/test/cmd/log_spec.rb b/Library/Homebrew/test/cmd/log_spec.rb index bdbca8912..b9e3e8d3e 100644 --- a/Library/Homebrew/test/cmd/log_spec.rb +++ b/Library/Homebrew/test/cmd/log_spec.rb @@ -33,7 +33,7 @@ describe "brew log", :integration_test do expect { brew "log", "#{shallow_tap}/testball" } .to output(/This is a test commit for Testball/).to_stdout - .and output(/Warning: The git repository is a shallow clone/).to_stderr + .and output(%r{Warning: homebrew/shallow is a shallow clone}).to_stderr .and be_a_success expect(shallow_tap.path/".git/shallow").to exist, "A shallow clone should have been created." -- cgit v1.2.3 From 80e95b684e7485b5c5b7f7209dd95b0bdc9e3406 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sat, 18 Mar 2017 17:02:08 +0200 Subject: blacklist: move to missing_formula class instead. This will allow extending this class so it can be used by more than just blacklisting. --- Library/Homebrew/blacklist.rb | 92 ---------------- Library/Homebrew/cmd/info.rb | 8 +- Library/Homebrew/cmd/install.rb | 6 +- Library/Homebrew/cmd/search.rb | 4 +- Library/Homebrew/dev-cmd/audit.rb | 4 +- Library/Homebrew/dev-cmd/create.rb | 6 +- Library/Homebrew/extend/os/blacklist.rb | 2 - Library/Homebrew/extend/os/mac/blacklist.rb | 16 --- Library/Homebrew/extend/os/mac/missing_formula.rb | 22 ++++ Library/Homebrew/extend/os/missing_formula.rb | 2 + Library/Homebrew/missing_formula.rb | 106 ++++++++++++++++++ Library/Homebrew/test/blacklist_spec.rb | 115 -------------------- Library/Homebrew/test/missing_formula_spec.rb | 125 ++++++++++++++++++++++ 13 files changed, 269 insertions(+), 239 deletions(-) delete mode 100644 Library/Homebrew/blacklist.rb delete mode 100644 Library/Homebrew/extend/os/blacklist.rb delete mode 100644 Library/Homebrew/extend/os/mac/blacklist.rb create mode 100644 Library/Homebrew/extend/os/mac/missing_formula.rb create mode 100644 Library/Homebrew/extend/os/missing_formula.rb create mode 100644 Library/Homebrew/missing_formula.rb delete mode 100644 Library/Homebrew/test/blacklist_spec.rb create mode 100644 Library/Homebrew/test/missing_formula_spec.rb (limited to 'Library') diff --git a/Library/Homebrew/blacklist.rb b/Library/Homebrew/blacklist.rb deleted file mode 100644 index 93c8f81a0..000000000 --- a/Library/Homebrew/blacklist.rb +++ /dev/null @@ -1,92 +0,0 @@ -def blacklisted?(name) - case name.downcase - when "gem", /^rubygems?$/ then <<-EOS.undent - Homebrew provides gem via: `brew install ruby`. - EOS - when "tex", "tex-live", "texlive", "latex" then <<-EOS.undent - Installing TeX from source is weird and gross, requires a lot of patches, - and only builds 32-bit (and thus can't use Homebrew dependencies) - - We recommend using a MacTeX distribution: https://www.tug.org/mactex/ - - You can install it with Homebrew-Cask: - brew cask install mactex - EOS - when "pip" then <<-EOS.undent - Homebrew provides pip via: `brew install python`. However you will then - have two Pythons installed on your Mac, so alternatively you can install - pip via the instructions at: - #{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")} - EOS - when "pil" then <<-EOS.undent - Instead of PIL, consider `pip install pillow` or `brew install Homebrew/python/pillow`. - EOS - when "macruby" then <<-EOS.undent - MacRuby is not packaged and is on an indefinite development hiatus. - You can read more about it at: - #{Formatter.url("https://github.com/MacRuby/MacRuby")} - EOS - when /(lib)?lzma/ - "lzma is now part of the xz formula." - when "gtest", "googletest", "google-test" then <<-EOS.undent - Installing gtest system-wide is not recommended; it should be vendored - in your projects that use it. - EOS - when "gmock", "googlemock", "google-mock" then <<-EOS.undent - Installing gmock system-wide is not recommended; it should be vendored - in your projects that use it. - EOS - when "sshpass" then <<-EOS.undent - We won't add sshpass because it makes it too easy for novice SSH users to - ruin SSH's security. - EOS - when "gsutil" then <<-EOS.undent - Install gsutil with `pip install gsutil` - EOS - when "clojure" then <<-EOS.undent - Clojure isn't really a program but a library managed as part of a - project and Leiningen is the user interface to that library. - - To install Clojure you should install Leiningen: - brew install leiningen - and then follow the tutorial: - #{Formatter.url("https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md")} - EOS - when "osmium" then <<-EOS.undent - The creator of Osmium requests that it not be packaged and that people - use the GitHub master branch instead. - EOS - when "gfortran" then <<-EOS.undent - GNU Fortran is now provided as part of GCC, and can be installed with: - brew install gcc - EOS - when "play" then <<-EOS.undent - Play 2.3 replaces the play command with activator: - brew install typesafe-activator - - You can read more about this change at: - #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Migration23")} - #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Highlights23")} - EOS - when "haskell-platform" then <<-EOS.undent - We no longer package haskell-platform. Consider installing ghc - and cabal-install instead: - brew install ghc cabal-install - - You can install with Homebrew-Cask: - brew cask install haskell-platform - EOS - when "mysqldump-secure" then <<-EOS.undent - The creator of mysqldump-secure tried to game our popularity metrics. - EOS - when "ngrok" then <<-EOS.undent - Upstream sunsetted 1.x in March 2016 and 2.x is not open-source. - - If you wish to use the 2.x release you can install with Homebrew-Cask: - brew cask install ngrok - EOS - end -end -alias generic_blacklisted? blacklisted? - -require "extend/os/blacklist" diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index 2d5e34ce2..5c96e5c50 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -16,7 +16,7 @@ #: See the docs for examples of using the JSON output: #: -require "blacklist" +require "missing_formula" require "caveats" require "options" require "formula" @@ -56,9 +56,9 @@ module Homebrew info_formula Formulary.find_with_priority(f) end rescue FormulaUnavailableError => e - # No formula with this name, try a blacklist lookup - if (blacklist = blacklisted?(f)) - ofail "#{e.message}\n#{blacklist}" + # No formula with this name, try a missing formula lookup + if (missing_formula = Homebrew::MissingFormula.missing_formula(f)) + ofail "#{e.message}\n#{missing_formula}" else ofail e.message diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 8a99bd397..a0f95887b 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -55,7 +55,7 @@ #: If `--git` is passed, Homebrew will create a Git repository, useful for #: creating patches to the software. -require "blacklist" +require "missing_formula" require "diagnostic" require "cmd/search" require "formula_installer" @@ -207,8 +207,8 @@ module Homebrew # formula was found, but there's a problem with its implementation). ofail e.message rescue FormulaUnavailableError => e - if (blacklist = blacklisted?(e.name)) - ofail "#{e.message}\n#{blacklist}" + if (missing_formula = Homebrew::MissingFormula.missing_formula(e.name)) + ofail "#{e.message}\n#{missing_formula}" elsif e.name == "updog" ofail "What's updog?" else diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index 4c8bbc77c..6887805d6 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -14,7 +14,7 @@ #: Search for in the given package manager's list. require "formula" -require "blacklist" +require "missing_formula" require "utils" require "thread" require "official_taps" @@ -67,7 +67,7 @@ module Homebrew if $stdout.tty? count = local_results.length + tap_results.length - if msg = blacklisted?(query) + if msg = Homebrew::MissingFormula.missing_formula(query) if count > 0 puts puts "If you meant #{query.inspect} specifically:" diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 677a52447..70104e0cb 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -38,7 +38,7 @@ require "official_taps" require "cmd/search" require "cmd/style" require "date" -require "blacklist" +require "missing_formula" require "digest" module Homebrew @@ -399,7 +399,7 @@ class FormulaAuditor name = formula.name full_name = formula.full_name - if blacklisted?(name) + if Homebrew::MissingFormula.blacklisted_reason(name) problem "'#{name}' is blacklisted." end diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb index 9c58dc71a..6855d6f37 100644 --- a/Library/Homebrew/dev-cmd/create.rb +++ b/Library/Homebrew/dev-cmd/create.rb @@ -19,7 +19,7 @@ #: the specified tap. require "formula" -require "blacklist" +require "missing_formula" require "digest" require "erb" @@ -73,8 +73,8 @@ module Homebrew # Don't allow blacklisted formula, or names that shadow aliases, # unless --force is specified. unless ARGV.force? - if msg = blacklisted?(fc.name) - raise "#{fc.name} is blacklisted for creation.\n#{msg}\nIf you really want to create this formula use --force." + if reason = Homebrew::MissingFormula.blacklisted_reason(fc.name) + raise "#{fc.name} is blacklisted for creation.\n#{reason}\nIf you really want to create this formula use --force." end if Formula.aliases.include? fc.name diff --git a/Library/Homebrew/extend/os/blacklist.rb b/Library/Homebrew/extend/os/blacklist.rb deleted file mode 100644 index 932040f82..000000000 --- a/Library/Homebrew/extend/os/blacklist.rb +++ /dev/null @@ -1,2 +0,0 @@ -require "blacklist" -require "extend/os/mac/blacklist" if OS.mac? diff --git a/Library/Homebrew/extend/os/mac/blacklist.rb b/Library/Homebrew/extend/os/mac/blacklist.rb deleted file mode 100644 index edff4697e..000000000 --- a/Library/Homebrew/extend/os/mac/blacklist.rb +++ /dev/null @@ -1,16 +0,0 @@ -def blacklisted?(name) - case name.downcase - when "xcode" - if MacOS.version >= :lion - <<-EOS.undent - Xcode can be installed from the App Store. - EOS - else - <<-EOS.undent - Xcode can be installed from https://developer.apple.com/xcode/downloads/ - EOS - end - else - generic_blacklisted?(name) - end -end diff --git a/Library/Homebrew/extend/os/mac/missing_formula.rb b/Library/Homebrew/extend/os/mac/missing_formula.rb new file mode 100644 index 000000000..48dfd2865 --- /dev/null +++ b/Library/Homebrew/extend/os/mac/missing_formula.rb @@ -0,0 +1,22 @@ +module Homebrew + module MissingFormula + class << self + def blacklisted_reason(name) + case name.downcase + when "xcode" + if MacOS.version >= :lion + <<-EOS.undent + Xcode can be installed from the App Store. + EOS + else + <<-EOS.undent + Xcode can be installed from #{Formatter.url("https://developer.apple.com/xcode/downloads/")}. + EOS + end + else + generic_blacklisted_reason(name) + end + end + end + end +end diff --git a/Library/Homebrew/extend/os/missing_formula.rb b/Library/Homebrew/extend/os/missing_formula.rb new file mode 100644 index 000000000..02c59f4e4 --- /dev/null +++ b/Library/Homebrew/extend/os/missing_formula.rb @@ -0,0 +1,2 @@ +require "missing_formula" +require "extend/os/mac/missing_formula" if OS.mac? diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb new file mode 100644 index 000000000..28738a0dc --- /dev/null +++ b/Library/Homebrew/missing_formula.rb @@ -0,0 +1,106 @@ +require "formulary" +require "tap" +require "utils" + +module Homebrew + module MissingFormula + class << self + def reason(name) + blacklisted_reason(name) + end + + def blacklisted_reason(name) + case name.downcase + when "gem", /^rubygems?$/ then <<-EOS.undent + Homebrew provides gem via: `brew install ruby`. + EOS + when "tex", "tex-live", "texlive", "latex" then <<-EOS.undent + Installing TeX from source is weird and gross, requires a lot of patches, + and only builds 32-bit (and thus can't use Homebrew dependencies) + + We recommend using a MacTeX distribution: https://www.tug.org/mactex/ + + You can install it with Homebrew-Cask: + brew cask install mactex + EOS + when "pip" then <<-EOS.undent + Homebrew provides pip via: `brew install python`. However you will then + have two Pythons installed on your Mac, so alternatively you can install + pip via the instructions at: + #{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")} + EOS + when "pil" then <<-EOS.undent + Instead of PIL, consider `pip install pillow` or `brew install Homebrew/python/pillow`. + EOS + when "macruby" then <<-EOS.undent + MacRuby is not packaged and is on an indefinite development hiatus. + You can read more about it at: + #{Formatter.url("https://github.com/MacRuby/MacRuby")} + EOS + when /(lib)?lzma/ + "lzma is now part of the xz formula." + when "gtest", "googletest", "google-test" then <<-EOS.undent + Installing gtest system-wide is not recommended; it should be vendored + in your projects that use it. + EOS + when "gmock", "googlemock", "google-mock" then <<-EOS.undent + Installing gmock system-wide is not recommended; it should be vendored + in your projects that use it. + EOS + when "sshpass" then <<-EOS.undent + We won't add sshpass because it makes it too easy for novice SSH users to + ruin SSH's security. + EOS + when "gsutil" then <<-EOS.undent + Install gsutil with `pip install gsutil` + EOS + when "clojure" then <<-EOS.undent + Clojure isn't really a program but a library managed as part of a + project and Leiningen is the user interface to that library. + + To install Clojure you should install Leiningen: + brew install leiningen + and then follow the tutorial: + #{Formatter.url("https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md")} + EOS + when "osmium" then <<-EOS.undent + The creator of Osmium requests that it not be packaged and that people + use the GitHub master branch instead. + EOS + when "gfortran" then <<-EOS.undent + GNU Fortran is now provided as part of GCC, and can be installed with: + brew install gcc + EOS + when "play" then <<-EOS.undent + Play 2.3 replaces the play command with activator: + brew install typesafe-activator + + You can read more about this change at: + #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Migration23")} + #{Formatter.url("https://www.playframework.com/documentation/2.3.x/Highlights23")} + EOS + when "haskell-platform" then <<-EOS.undent + We no longer package haskell-platform. Consider installing ghc + and cabal-install instead: + brew install ghc cabal-install + + You can install with Homebrew-Cask: + brew cask install haskell-platform + EOS + when "mysqldump-secure" then <<-EOS.undent + The creator of mysqldump-secure tried to game our popularity metrics. + EOS + when "ngrok" then <<-EOS.undent + Upstream sunsetted 1.x in March 2016 and 2.x is not open-source. + + If you wish to use the 2.x release you can install with Homebrew-Cask: + brew cask install ngrok + EOS + end + end + alias generic_blacklisted_reason blacklisted_reason + + require "extend/os/missing_formula" + end + end +end diff --git a/Library/Homebrew/test/blacklist_spec.rb b/Library/Homebrew/test/blacklist_spec.rb deleted file mode 100644 index 01882167d..000000000 --- a/Library/Homebrew/test/blacklist_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -require "blacklist" - -describe "Blacklist" do - matcher(:be_blacklisted) { match(&method(:blacklisted?)) } - - context "rubygems" do - %w[gem rubygem rubygems].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "latex" do - %w[latex tex tex-live texlive TexLive].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "pip" do - subject { "pip" } - - it { is_expected.to be_blacklisted } - end - - context "pil" do - subject { "pil" } - - it { is_expected.to be_blacklisted } - end - - context "macruby" do - subject { "MacRuby" } - - it { is_expected.to be_blacklisted } - end - - context "lzma" do - %w[lzma liblzma].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "gtest" do - %w[gtest googletest google-test].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "gmock" do - %w[gmock googlemock google-mock].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "sshpass" do - subject { "sshpass" } - - it { is_expected.to be_blacklisted } - end - - context "gsutil" do - subject { "gsutil" } - - it { is_expected.to be_blacklisted } - end - - context "clojure" do - subject { "clojure" } - - it { is_expected.to be_blacklisted } - end - - context "osmium" do - %w[osmium Osmium].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end - - context "gfortran" do - subject { "gfortran" } - - it { is_expected.to be_blacklisted } - end - - context "play" do - subject { "play" } - - it { is_expected.to be_blacklisted } - end - - context "haskell-platform" do - subject { "haskell-platform" } - - it { is_expected.to be_blacklisted } - end - - context "xcode", :needs_macos do - %w[xcode Xcode].each do |s| - subject { s } - - it { is_expected.to be_blacklisted } - end - end -end diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb new file mode 100644 index 000000000..11b93316f --- /dev/null +++ b/Library/Homebrew/test/missing_formula_spec.rb @@ -0,0 +1,125 @@ +require "missing_formula" + +describe Homebrew::MissingFormula do + context ".reason" do + subject { described_class.reason("gem") } + + it { is_expected.to_not be_nil } + end + + context ".blacklisted_reason" do + matcher(:be_blacklisted) do + match(&Homebrew::MissingFormula.method(:blacklisted_reason)) + end + + context "rubygems" do + %w[gem rubygem rubygems].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "latex" do + %w[latex tex tex-live texlive TexLive].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "pip" do + subject { "pip" } + + it { is_expected.to be_blacklisted } + end + + context "pil" do + subject { "pil" } + + it { is_expected.to be_blacklisted } + end + + context "macruby" do + subject { "MacRuby" } + + it { is_expected.to be_blacklisted } + end + + context "lzma" do + %w[lzma liblzma].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gtest" do + %w[gtest googletest google-test].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gmock" do + %w[gmock googlemock google-mock].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "sshpass" do + subject { "sshpass" } + + it { is_expected.to be_blacklisted } + end + + context "gsutil" do + subject { "gsutil" } + + it { is_expected.to be_blacklisted } + end + + context "clojure" do + subject { "clojure" } + + it { is_expected.to be_blacklisted } + end + + context "osmium" do + %w[osmium Osmium].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + + context "gfortran" do + subject { "gfortran" } + + it { is_expected.to be_blacklisted } + end + + context "play" do + subject { "play" } + + it { is_expected.to be_blacklisted } + end + + context "haskell-platform" do + subject { "haskell-platform" } + + it { is_expected.to be_blacklisted } + end + + context "xcode", :needs_macos do + %w[xcode Xcode].each do |s| + subject { s } + + it { is_expected.to be_blacklisted } + end + end + end +end -- cgit v1.2.3 From f59eb358c29c5f40601a99e3f1bf7e8e891f10ba Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Mon, 20 Mar 2017 20:37:12 +0100 Subject: missing_formula: subsume historic logic. These methods belong together so combine them in a single class to provide a simpler API. --- Library/Homebrew/cmd/info.rb | 17 +------- Library/Homebrew/cmd/install.rb | 16 +------- Library/Homebrew/cmd/search.rb | 4 +- Library/Homebrew/exceptions.rb | 13 ------ Library/Homebrew/historic.rb | 57 -------------------------- Library/Homebrew/missing_formula.rb | 55 ++++++++++++++++++++++++- Library/Homebrew/test/historic_test.rb | 46 --------------------- Library/Homebrew/test/missing_formula_spec.rb | 58 ++++++++++++++++++++++++++- 8 files changed, 116 insertions(+), 150 deletions(-) delete mode 100644 Library/Homebrew/historic.rb delete mode 100644 Library/Homebrew/test/historic_test.rb (limited to 'Library') diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index 5c96e5c50..7e1815556 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -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 diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index a0f95887b..bd7897171 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -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..." diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index 6887805d6..db5898872 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -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 diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index e9885890b..77da4489e 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -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 diff --git a/Library/Homebrew/historic.rb b/Library/Homebrew/historic.rb deleted file mode 100644 index 0afb9646a..000000000 --- a/Library/Homebrew/historic.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb index 28738a0dc..ba09f7426 100644 --- a/Library/Homebrew/missing_formula.rb +++ b/Library/Homebrew/missing_formula.rb @@ -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 diff --git a/Library/Homebrew/test/historic_test.rb b/Library/Homebrew/test/historic_test.rb deleted file mode 100644 index d09656fe0..000000000 --- a/Library/Homebrew/test/historic_test.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb index 11b93316f..f395965a6 100644 --- a/Library/Homebrew/test/missing_formula_spec.rb +++ b/Library/Homebrew/test/missing_formula_spec.rb @@ -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 -- cgit v1.2.3