diff options
Diffstat (limited to 'Library')
38 files changed, 943 insertions, 248 deletions
diff --git a/Library/Homebrew/cask/lib/hbc/cli/search.rb b/Library/Homebrew/cask/lib/hbc/cli/search.rb index e89dced92..d56d0c81f 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/search.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/search.rb @@ -2,8 +2,12 @@ module Hbc class CLI class Search < AbstractCommand def run - results = self.class.search(*args) - self.class.render_results(*results) + if args.empty? + puts Formatter.columns(CLI.nice_listing(Hbc.all_tokens)) + else + results = self.class.search(*args) + self.class.render_results(*results) + end end def self.extract_regexp(string) @@ -15,8 +19,17 @@ module Hbc end def self.search_remote(query) - matches = GitHub.search_code(user: "caskroom", path: "Casks", - filename: query, extension: "rb") + matches = begin + GitHub.search_code( + user: "caskroom", + path: "Casks", + filename: query, + extension: "rb", + ) + rescue GitHub::Error => error + opoo "Error searching on GitHub: #{error}\n" + [] + end matches.map do |match| tap = Tap.fetch(match["repository"]["full_name"]) next if tap.installed? diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index d1f0b2516..340161204 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -6,6 +6,10 @@ module Homebrew module Cleanup @disk_cleanup_size = 0 + class << self + attr_reader :disk_cleanup_size + end + module_function def cleanup @@ -21,10 +25,6 @@ module Homebrew @disk_cleanup_size += path_size end - def disk_cleanup_size - @disk_cleanup_size - end - def unremovable_kegs @unremovable_kegs ||= [] end diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index 0718a3af4..acee9817f 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -36,7 +36,7 @@ module Homebrew elsif ARGV.include? "--fedora" exec_browser "https://apps.fedoraproject.org/packages/s/#{ARGV.next}" elsif ARGV.include? "--ubuntu" - exec_browser "http://packages.ubuntu.com/search?keywords=#{ARGV.next}&searchon=names&suite=all§ion=all" + exec_browser "https://packages.ubuntu.com/search?keywords=#{ARGV.next}&searchon=names&suite=all§ion=all" elsif ARGV.include? "--desc" query = ARGV.next regex = query_regexp(query) @@ -102,11 +102,13 @@ module Homebrew odie "#{query} is not a valid regex" end - def search_taps(query) + def search_taps(query, silent: false) return [] if ENV["HOMEBREW_NO_GITHUB_API"] # Use stderr to avoid breaking parsed output - $stderr.puts Formatter.headline("Searching taps on GitHub...", color: :blue) + unless silent + $stderr.puts Formatter.headline("Searching taps on GitHub...", color: :blue) + end valid_dirnames = ["Formula", "HomebrewFormula", "Casks", "."].freeze matches = GitHub.search_code(user: ["Homebrew", "caskroom"], filename: query, extension: "rb") diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index ea915f99c..164413cd1 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -203,6 +203,7 @@ module Homebrew end new_homebrew_repository = Pathname.new "/usr/local/Homebrew" + new_homebrew_repository.rmdir_if_possible if new_homebrew_repository.exist? ofail <<-EOS.undent #{new_homebrew_repository} already exists. diff --git a/Library/Homebrew/compat/ENV/shared.rb b/Library/Homebrew/compat/ENV/shared.rb index 200e7b132..c700b1e00 100644 --- a/Library/Homebrew/compat/ENV/shared.rb +++ b/Library/Homebrew/compat/ENV/shared.rb @@ -3,4 +3,8 @@ module SharedEnvExtension odeprecated "ENV.j1", "ENV.deparallelize" deparallelize end + + def java_cache + # odeprecated "ENV.java_cache" + end end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 170fb6d5f..d089f308d 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -201,7 +201,7 @@ class FormulaAuditor @specs = %w[stable devel head].map { |s| formula.send(s) }.compact end - def self.check_http_content(url, name, user_agents: [:default]) + def self.check_http_content(url, name, user_agents: [:default], check_content: false, strict: false) return unless url.start_with? "http" details = nil @@ -236,8 +236,32 @@ class FormulaAuditor details[:content_length] == secure_details[:content_length] file_match = details[:file_hash] == secure_details[:file_hash] - return if !etag_match && !content_length_match && !file_match - "The URL #{url} could use HTTPS rather than HTTP" + if etag_match || content_length_match || file_match + return "The URL #{url} should use HTTPS rather than HTTP" + end + + return unless check_content + + no_protocol_file_contents = %r{https?:\\?/\\?/} + details[:file] = details[:file].gsub(no_protocol_file_contents, "/") + secure_details[:file] = secure_details[:file].gsub(no_protocol_file_contents, "/") + + # Check for the same content after removing all protocols + if details[:file] == secure_details[:file] + return "The URL #{url} should use HTTPS rather than HTTP" + end + + return unless strict + + # Same size, different content after normalization + # (typical causes: Generated ID, Timestamp, Unix time) + if details[:file].length == secure_details[:file].length + return "The URL #{url} may be able to use HTTPS rather than HTTP. Please verify it in a browser." + end + + lenratio = (100 * secure_details[:file].length / details[:file].length).to_i + return unless (90..110).cover?(lenratio) + "The URL #{url} may be able to use HTTPS rather than HTTP. Please verify it in a browser." end def self.http_content_headers_and_checksum(url, hash_needed: false, user_agent: :default) @@ -260,6 +284,7 @@ class FormulaAuditor etag: headers[%r{ETag: ([wW]\/)?"(([^"]|\\")*)"}, 2], content_length: headers[/Content-Length: (\d+)/, 1], file_hash: output_hash, + file: output, } end @@ -412,7 +437,7 @@ class FormulaAuditor same_name_tap_formulae = @@local_official_taps_name_map[name] || [] if @online - Homebrew.search_taps(name).each do |tap_formula_full_name| + Homebrew.search_taps(name, silent: true).each do |tap_formula_full_name| tap_formula_name = tap_formula_full_name.split("/").last next if tap_formula_name != name same_name_tap_formulae << tap_formula_full_name @@ -566,7 +591,9 @@ class FormulaAuditor return unless DevelopmentTools.curl_handles_most_https_homepages? if http_content_problem = FormulaAuditor.check_http_content(homepage, formula.name, - user_agents: [:browser, :default]) + user_agents: [:browser, :default], + check_content: true, + strict: @strict) problem http_content_problem end end @@ -808,39 +835,6 @@ class FormulaAuditor end def line_problems(line, _lineno) - if line =~ /<(Formula|AmazonWebServicesFormula|ScriptFileFormula|GithubGistFormula)/ - problem "Use a space in class inheritance: class Foo < #{Regexp.last_match(1)}" - end - - # Commented-out cmake support from default template - problem "Commented cmake call found" if line.include?('# system "cmake') - - # Comments from default template - [ - "# PLEASE REMOVE", - "# Documentation:", - "# if this fails, try separate make/make install steps", - "# The URL of the archive", - "## Naming --", - "# if your formula requires any X11/XQuartz components", - "# if your formula fails when building in parallel", - "# Remove unrecognized options if warned by configure", - ].each do |comment| - next unless line.include?(comment) - problem "Please remove default template comments" - end - - # FileUtils is included in Formula - # encfs modifies a file with this name, so check for some leading characters - if line =~ %r{[^'"/]FileUtils\.(\w+)} - problem "Don't need 'FileUtils.' before #{Regexp.last_match(1)}." - end - - # Check for long inreplace block vars - if line =~ /inreplace .* do \|(.{2,})\|/ - problem "\"inreplace <filenames> do |s|\" is preferred over \"|#{Regexp.last_match(1)}|\"." - end - # Check for string interpolation of single values. if line =~ /(system|inreplace|gsub!|change_make_var!).*[ ,]"#\{([\w.]+)\}"/ problem "Don't need to interpolate \"#{Regexp.last_match(2)}\" with #{Regexp.last_match(1)}" @@ -890,9 +884,6 @@ class FormulaAuditor end end - # Commented-out depends_on - problem "Commented-out dep #{Regexp.last_match(1)}" if line =~ /#\s*depends_on\s+(.+)\s*$/ - if line =~ /if\s+ARGV\.include\?\s+'--(HEAD|devel)'/ problem "Use \"if build.#{Regexp.last_match(1).downcase}?\" instead" end @@ -905,6 +896,10 @@ class FormulaAuditor problem "Use \"depends_on :x11\" instead of \"ENV.x11\"" end + if line.include?("ENV.java_cache") + problem "In-formula ENV.java_cache usage has been deprecated & should be removed." + end + # Avoid hard-coding compilers if line =~ %r{(system|ENV\[.+\]\s?=)\s?['"](/usr/bin/)?(gcc|llvm-gcc|clang)['" ]} problem "Use \"\#{ENV.cc}\" instead of hard-coding \"#{Regexp.last_match(3)}\"" @@ -1264,6 +1259,7 @@ class ResourceAuditor end elsif strategy <= SubversionDownloadStrategy next unless DevelopmentTools.subversion_handles_most_https_certificates? + next unless Utils.svn_available? unless Utils.svn_remote_exists url problem "The URL #{url} is not a valid svn URL" end diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index dd2bc6270..a8f35531f 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -587,7 +587,7 @@ module Homebrew # We're in the cache; make sure to force re-download loop do begin - curl_download url, to: filename + curl_download url, continue_at: 0, to: filename break rescue if retry_count >= max_curl_retries diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index 2a8b6e585..adeb0a02a 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -381,7 +381,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy # Curl options to be always passed to curl, # with raw head calls (`curl --head`) or with actual `fetch`. def _curl_opts - return ["--user" << meta.fetch(:user)] if meta.key?(:user) + return ["--user", meta.fetch(:user)] if meta.key?(:user) [] end diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb index b51ade48b..15488ee19 100644 --- a/Library/Homebrew/extend/ENV/shared.rb +++ b/Library/Homebrew/extend/ENV/shared.rb @@ -260,10 +260,6 @@ module SharedEnvExtension set_cpu_flags(flags) end - def java_cache - append "_JAVA_OPTIONS", "-Duser.home=#{HOMEBREW_CACHE}/java_cache" - end - # ld64 is a newer linker provided for Xcode 2.5 # @private def ld64 diff --git a/Library/Homebrew/extend/fileutils.rb b/Library/Homebrew/extend/fileutils.rb index 52d4cbf51..ed5bfe6c3 100644 --- a/Library/Homebrew/extend/fileutils.rb +++ b/Library/Homebrew/extend/fileutils.rb @@ -3,7 +3,7 @@ require "tmpdir" require "etc" # Homebrew extends Ruby's `FileUtils` to make our code more readable. -# @see http://ruby-doc.org/stdlib-2.0.0/libdoc/fileutils/rdoc/FileUtils.html Ruby's FileUtils API +# @see https://ruby-doc.org/stdlib-2.0.0/libdoc/fileutils/rdoc/FileUtils.html Ruby's FileUtils API module FileUtils # Create a temporary directory then yield. When the block returns, # recursively delete the temporary directory. Passing opts[:retain] diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index 767d83ff9..e1acd1f77 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -59,7 +59,7 @@ module DiskUsageExtension end # Homebrew extends Ruby's `Pathname` to make our code more readable. -# @see http://ruby-doc.org/stdlib-1.8.7/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API +# @see https://ruby-doc.org/stdlib-1.8.7/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API class Pathname include DiskUsageExtension diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 8cea85a99..2f0c913c2 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1613,6 +1613,7 @@ class Formula def run_test @prefix_returns_versioned_prefix = true old_home = ENV["HOME"] + old_java_opts = ENV["_JAVA_OPTIONS"] old_curl_home = ENV["CURL_HOME"] old_tmpdir = ENV["TMPDIR"] old_temp = ENV["TEMP"] @@ -1626,6 +1627,7 @@ class Formula ENV["TERM"] = "dumb" ENV["PATH"] = PATH.new(old_path).append(HOMEBREW_PREFIX/"bin") ENV["HOMEBREW_PATH"] = nil + ENV["_JAVA_OPTIONS"] = "#{old_java_opts} -Duser.home=#{HOMEBREW_CACHE}/java_cache" ENV.clear_sensitive_environment! @@ -1646,6 +1648,7 @@ class Formula ensure @testpath = nil ENV["HOME"] = old_home + ENV["_JAVA_OPTIONS"] = old_java_opts ENV["CURL_HOME"] = old_curl_home ENV["TMPDIR"] = old_tmpdir ENV["TEMP"] = old_temp @@ -1888,11 +1891,13 @@ class Formula mkdir_p env_home old_home = ENV["HOME"] + old_java_opts = ENV["_JAVA_OPTIONS"] old_curl_home = ENV["CURL_HOME"] old_path = ENV["HOMEBREW_PATH"] unless ARGV.interactive? ENV["HOME"] = env_home + ENV["_JAVA_OPTIONS"] = "#{old_java_opts} -Duser.home=#{HOMEBREW_CACHE}/java_cache" ENV["CURL_HOME"] = old_curl_home || old_home end ENV["HOMEBREW_PATH"] = nil @@ -1907,6 +1912,7 @@ class Formula @buildpath = nil unless ARGV.interactive? ENV["HOME"] = old_home + ENV["_JAVA_OPTIONS"] = old_java_opts ENV["CURL_HOME"] = old_curl_home end ENV["HOMEBREW_PATH"] = old_path diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 6c5b8bdab..216a375ce 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -85,13 +85,12 @@ class FormulaInstaller return false if @pour_failed bottle = formula.bottle - return false unless bottle + return false if !bottle && !formula.local_bottle_path return true if force_bottle? return false if build_from_source? || build_bottle? || interactive? return false if ARGV.cc return false unless options.empty? return false if formula.bottle_disabled? - return true if formula.local_bottle_path unless formula.pour_bottle? if install_bottle_options[:warn] && formula.pour_bottle_check_unsatisfied_reason opoo <<-EOS.undent diff --git a/Library/Homebrew/gpg.rb b/Library/Homebrew/gpg.rb index f56473df3..de2089dda 100644 --- a/Library/Homebrew/gpg.rb +++ b/Library/Homebrew/gpg.rb @@ -7,8 +7,7 @@ class Gpg next unless gpg_short_version gpg_version = Version.create(gpg_short_version.to_s) @version = gpg_version - gpg_version == Version.create("2.1") || - gpg_version == Version.create("2.0") + gpg_version >= Version.create("2.0") end end @@ -38,12 +37,30 @@ class Gpg Key-Length: 2048 Subkey-Type: RSA Subkey-Length: 2048 - Passphrase: '' Name-Real: Testing Name-Email: testing@foo.bar Expire-Date: 1d + %no-protection %commit EOS system GPG_EXECUTABLE, "--batch", "--gen-key", "batch.gpg" end + + def self.cleanup_test_processes! + odie "No GPG present to test against!" unless available? + gpgconf = Pathname.new(GPG_EXECUTABLE).parent/"gpgconf" + + system gpgconf, "--kill", "gpg-agent" + system gpgconf, "--homedir", "keyrings/live", "--kill", + "gpg-agent" + end + + def self.test(path) + create_test_key(path) + begin + yield + ensure + cleanup_test_processes! + end + end end diff --git a/Library/Homebrew/manpages/brew-cask.1.md b/Library/Homebrew/manpages/brew-cask.1.md index bfb9cd7a5..715d8fd77 100644 --- a/Library/Homebrew/manpages/brew-cask.1.md +++ b/Library/Homebrew/manpages/brew-cask.1.md @@ -1,5 +1,5 @@ brew-cask(1) - a friendly binary installer for macOS -======================================================== +==================================================== ## SYNOPSIS @@ -85,7 +85,7 @@ names, and other aspects of this manual are still subject to change. If <token> is given, summarize the staged files associated with the given Cask. - + * `outdated` [--greedy] [--verbose|--quiet] [ <token> ...]: Without token arguments, display all the installed Casks that have newer versions available in the tap; otherwise check only the tokens given @@ -101,9 +101,10 @@ names, and other aspects of this manual are still subject to change. Reinstall the given Cask. * `search` or `-S` [<text> | /<regexp>/]: - Without an argument, display all Casks available for install; otherwise - perform a substring search of known Cask tokens for <text> or, if the - text is delimited by slashes (/<regexp>/), it is interpreted as a + Without an argument, display all locally available Casks for install; no + online search is performed. + Otherwise perform a substring search of known Cask tokens for <text> or, + if the text is delimited by slashes (/<regexp>/), it is interpreted as a Ruby regular expression. * `style` [--fix] [ <token> ... ]: @@ -255,7 +256,7 @@ Environment variables specific to Homebrew-Cask: export HOMEBREW_CASK_OPTS='--appdir=~/Applications --fontdir=/Library/Fonts' Other environment variables: - + * `SUDO_ASKPASS`: When this variable is set, Homebrew-Cask will call `sudo`(8) with the `-A` option. diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb index 4d90ddb61..a3d182a2b 100644 --- a/Library/Homebrew/missing_formula.rb +++ b/Library/Homebrew/missing_formula.rb @@ -31,7 +31,7 @@ module Homebrew #{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/science/pillow`. + Instead of PIL, consider `pip2 install pillow`. EOS when "macruby" then <<-EOS.undent MacRuby is not packaged and is on an indefinite development hiatus. @@ -53,16 +53,7 @@ module Homebrew 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")} + Install gsutil with `pip2 install gsutil` EOS when "gfortran" then <<-EOS.undent GNU Fortran is now provided as part of GCC, and can be installed with: diff --git a/Library/Homebrew/requirements/gpg2_requirement.rb b/Library/Homebrew/requirements/gpg2_requirement.rb index d570983eb..ebdd71f6e 100644 --- a/Library/Homebrew/requirements/gpg2_requirement.rb +++ b/Library/Homebrew/requirements/gpg2_requirement.rb @@ -5,8 +5,8 @@ class GPG2Requirement < Requirement fatal true default_formula "gnupg" - # GPGTools installs GnuPG 2.0.x as a vanilla `gpg` symlink - # pointing to `gpg2`. Homebrew install 2.1.x as a non-symlink `gpg`. - # We support both the 2.0.x "stable" and 2.1.x "modern" series here. + # GPGTools installs GnuPG 2.0.x as a `gpg` symlink pointing + # to `gpg2`. Our `gnupg` installs only a non-symlink `gpg`. + # The aim is to retain support for any version above 2.0. satisfy(build_env: false) { Gpg.gpg || Gpg.gpg2 } end diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb index 862664010..7844f7bf2 100644 --- a/Library/Homebrew/rubocops/extend/formula_cop.rb +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -86,9 +86,13 @@ module RuboCop end # Returns an array of method call nodes matching method_name in every descendant of node - def find_every_method_call_by_name(node, method_name) + # Returns every method call if no method_name is passed + def find_every_method_call_by_name(node, method_name = nil) return if node.nil? - node.each_descendant(:send).select { |method_node| method_name == method_node.method_name } + node.each_descendant(:send).select do |method_node| + method_name.nil? || + method_name == method_node.method_name + end end # Given a method_name and arguments, yields to a block with @@ -108,7 +112,7 @@ module RuboCop def find_instance_method_call(node, instance, method_name) methods = find_every_method_call_by_name(node, method_name) methods.each do |method| - next unless method.receiver.const_name == instance + next unless method.receiver && method.receiver.const_name == instance @offense_source_range = method.source_range @offensive_node = method yield method @@ -202,9 +206,15 @@ module RuboCop end # Returns an array of block nodes of any depth below node in AST + # If a block is given then yields matching block node to the block! def find_all_blocks(node, block_name) return if node.nil? - node.each_descendant(:block).select { |block_node| block_name == block_node.method_name } + blocks = node.each_descendant(:block).select { |block_node| block_name == block_node.method_name } + return blocks unless block_given? + blocks.each do |block_node| + offending_node(block_node) + yield block_node + end end # Returns a method definition node with method_name @@ -316,6 +326,15 @@ module RuboCop end end + # Yields to a block with comment text as parameter + def audit_comments + @processed_source.comments.each do |comment_node| + @offensive_node = comment_node + @offense_source_range = :expression + yield comment_node.text + end + end + # Returns the begin position of the node's line in source code def line_start_column(node) node.source_range.source_buffer.line_range(node.loc.line).begin_pos @@ -326,6 +345,11 @@ module RuboCop node.source_range.begin_pos end + # Returns the ending position of the node in source code + def end_column(node) + node.source_range.end_pos + end + # Returns the line number of the node def line_number(node) node.loc.line diff --git a/Library/Homebrew/rubocops/lines_cop.rb b/Library/Homebrew/rubocops/lines_cop.rb index 22039869b..ed50ba49c 100644 --- a/Library/Homebrew/rubocops/lines_cop.rb +++ b/Library/Homebrew/rubocops/lines_cop.rb @@ -15,6 +15,60 @@ module RuboCop problem ":tex is deprecated" if depends_on?(:tex) end end + + class ClassInheritance < FormulaCop + def audit_formula(_node, class_node, parent_class_node, _body_node) + begin_pos = start_column(parent_class_node) + end_pos = end_column(class_node) + return unless begin_pos-end_pos != 3 + problem "Use a space in class inheritance: class #{@formula_name} < #{class_name(parent_class_node)}" + end + end + + class Comments < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, _body_node) + audit_comments do |comment| + [ + "# PLEASE REMOVE", + "# Documentation:", + "# if this fails, try separate make/make install steps", + "# The URL of the archive", + "## Naming --", + "# if your formula requires any X11/XQuartz components", + "# if your formula fails when building in parallel", + "# Remove unrecognized options if warned by configure", + '# system "cmake', + ].each do |template_comment| + next unless comment.include?(template_comment) + problem "Please remove default template comments" + break + end + end + + audit_comments do |comment| + # Commented-out depends_on + next unless comment =~ /#\s*depends_on\s+(.+)\s*$/ + problem "Commented-out dependency #{Regexp.last_match(1)}" + end + end + end + + class Miscellaneous < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, body_node) + # FileUtils is included in Formula + # encfs modifies a file with this name, so check for some leading characters + find_instance_method_call(body_node, "FileUtils", nil) do |method_node| + problem "Don't need 'FileUtils.' before #{method_node.method_name}" + end + + # Check for long inreplace block vars + find_all_blocks(body_node, :inreplace) do |node| + block_arg = node.arguments.children.first + next unless block_arg.source.size>1 + problem "\"inreplace <filenames> do |s|\" is preferred over \"|#{block_arg.source}|\"." + end + end + end end end end diff --git a/Library/Homebrew/rubocops/patches_cop.rb b/Library/Homebrew/rubocops/patches_cop.rb index b27283382..7ee1a127a 100644 --- a/Library/Homebrew/rubocops/patches_cop.rb +++ b/Library/Homebrew/rubocops/patches_cop.rb @@ -25,12 +25,22 @@ module RuboCop def patch_problems(patch) patch_url = string_content(patch) + gh_patch_param_pattern = %r{https?://github\.com/.+/.+/(?:commit|pull)/[a-fA-F0-9]*.(?:patch|diff)} + if regex_match_group(patch, gh_patch_param_pattern) + if patch_url !~ /\?full_index=\w+$/ + problem <<-EOS.undent + GitHub patches should use the full_index parameter: + #{patch_url}?full_index=1 + EOS + end + end + gh_patch_patterns = Regexp.union([%r{/raw\.github\.com/}, %r{gist\.github\.com/raw}, %r{gist\.github\.com/.+/raw}, %r{gist\.githubusercontent\.com/.+/raw}]) if regex_match_group(patch, gh_patch_patterns) - unless patch_url =~ /[a-fA-F0-9]{40}/ + if patch_url !~ /[a-fA-F0-9]{40}/ problem <<-EOS.undent.chomp GitHub/Gist patches should specify a revision: #{patch_url} diff --git a/Library/Homebrew/test/cask/cli/search_spec.rb b/Library/Homebrew/test/cask/cli/search_spec.rb index e237ad464..6dc980590 100644 --- a/Library/Homebrew/test/cask/cli/search_spec.rb +++ b/Library/Homebrew/test/cask/cli/search_spec.rb @@ -22,16 +22,36 @@ describe Hbc::CLI::Search, :cask do EOS end + it "returns matches even when online search failed" do + allow(GitHub).to receive(:search_code).and_raise(GitHub::Error.new("reason")) + expect { + Hbc::CLI::Search.run("local") + }.to output(<<-EOS.undent).to_stdout + local-caffeine + local-transmission + EOS + .and output(/^Warning: Error searching on GitHub: reason/).to_stderr + end + it "shows that there are no Casks matching a search term that did not result in anything" do expect { Hbc::CLI::Search.run("foo-bar-baz") - }.to output("No Cask found for \"foo-bar-baz\".\n").to_stdout.as_tty + }.to output(<<-EOS.undent).to_stdout.as_tty + No Cask found for "foo-bar-baz". + EOS end - it "lists all available Casks with no search term" do - expect { - Hbc::CLI::Search.run - }.to output(/local-caffeine/).to_stdout.as_tty + it "doesn't output anything to non-TTY stdout when there are no matches" do + expect { Hbc::CLI::Search.run("foo-bar-baz") } + .to not_to_output.to_stdout + .and not_to_output.to_stderr + end + + it "lists all Casks available offline with no search term" do + allow(GitHub).to receive(:search_code).and_raise(GitHub::Error.new("reason")) + expect { Hbc::CLI::Search.run } + .to output(/local-caffeine/).to_stdout.as_tty + .and not_to_output.to_stderr end it "ignores hyphens in search terms" do @@ -55,19 +75,29 @@ describe Hbc::CLI::Search, :cask do it "accepts a regexp argument" do expect { Hbc::CLI::Search.run("/^local-c[a-z]ffeine$/") - }.to output("==> Regexp Matches\nlocal-caffeine\n").to_stdout.as_tty + }.to output(<<-EOS.undent).to_stdout.as_tty + ==> Regexp Matches + local-caffeine + EOS end - it "Returns both exact and partial matches" do + it "returns both exact and partial matches" do expect { Hbc::CLI::Search.run("test-opera") - }.to output(/^==> Exact Match\ntest-opera\n==> Partial Matches\ntest-opera-mail/).to_stdout.as_tty + }.to output(<<-EOS.undent).to_stdout.as_tty + ==> Exact Match + test-opera + ==> Partial Matches + test-opera-mail + EOS end it "does not search the Tap name" do expect { Hbc::CLI::Search.run("caskroom") - }.to output(/^No Cask found for "caskroom"\.\n/).to_stdout.as_tty + }.to output(<<-EOS.undent).to_stdout.as_tty + No Cask found for "caskroom". + EOS end it "doesn't highlight packages that aren't installed" do diff --git a/Library/Homebrew/test/cleanup_spec.rb b/Library/Homebrew/test/cleanup_spec.rb index 4e5e42efa..da262c5ca 100644 --- a/Library/Homebrew/test/cleanup_spec.rb +++ b/Library/Homebrew/test/cleanup_spec.rb @@ -5,6 +5,7 @@ require "pathname" describe Homebrew::Cleanup do let(:ds_store) { Pathname.new("#{HOMEBREW_PREFIX}/Library/.DS_Store") } + let(:sec_in_a_day) { 60 * 60 * 24 } around(:each) do |example| begin @@ -104,14 +105,30 @@ describe Homebrew::Cleanup do expect(f4).to be_installed end - specify "::cleanup_logs" do - path = (HOMEBREW_LOGS/"delete_me") - path.mkpath - ARGV << "--prune=all" + describe "::cleanup_logs" do + let(:path) { (HOMEBREW_LOGS/"delete_me") } - described_class.cleanup_logs + before do + path.mkpath + end + + it "cleans all logs if prune all" do + ARGV << "--prune=all" + described_class.cleanup_logs + expect(path).not_to exist + end - expect(path).not_to exist + it "cleans up logs if older than 14 days" do + allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - sec_in_a_day * 15) + described_class.cleanup_logs + expect(path).not_to exist + end + + it "does not clean up logs less than 14 days old" do + allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - sec_in_a_day * 2) + described_class.cleanup_logs + expect(path).to exist + end end describe "::cleanup_cache" do @@ -124,6 +141,15 @@ describe Homebrew::Cleanup do expect(incomplete).not_to exist end + it "cleans up 'glide_home'" do + glide_home = (HOMEBREW_CACHE/"glide_home") + glide_home.mkpath + + described_class.cleanup_cache + + expect(glide_home).not_to exist + end + it "cleans up 'java_cache'" do java_cache = (HOMEBREW_CACHE/"java_cache") java_cache.mkpath @@ -141,5 +167,99 @@ describe Homebrew::Cleanup do expect(npm_cache).not_to exist end + + it "cleans up all files and directories" do + git = (HOMEBREW_CACHE/"gist--git") + gist = (HOMEBREW_CACHE/"gist") + svn = (HOMEBREW_CACHE/"gist--svn") + + git.mkpath + gist.mkpath + FileUtils.touch svn + + allow(ARGV).to receive(:value).with("prune").and_return("all") + + described_class.cleanup_cache + + expect(git).not_to exist + expect(gist).to exist + expect(svn).not_to exist + end + + it "does not clean up directories that are not VCS checkouts" do + git = (HOMEBREW_CACHE/"git") + git.mkpath + allow(ARGV).to receive(:value).with("prune").and_return("all") + + described_class.cleanup_cache + + expect(git).to exist + end + + it "cleans up VCS checkout directories with modified time < prune time" do + foo = (HOMEBREW_CACHE/"--foo") + foo.mkpath + allow(ARGV).to receive(:value).with("prune").and_return("1") + allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - sec_in_a_day * 2) + described_class.cleanup_cache + expect(foo).not_to exist + end + + it "does not clean up VCS checkout directories with modified time >= prune time" do + foo = (HOMEBREW_CACHE/"--foo") + foo.mkpath + allow(ARGV).to receive(:value).with("prune").and_return("1") + described_class.cleanup_cache + expect(foo).to exist + end + + context "cleans old files in HOMEBREW_CACHE" do + let(:bottle) { (HOMEBREW_CACHE/"testball-0.0.1.bottle.tar.gz") } + let(:testball) { (HOMEBREW_CACHE/"testball-0.0.1") } + + before(:each) do + FileUtils.touch(bottle) + FileUtils.touch(testball) + (HOMEBREW_CELLAR/"testball"/"0.0.1").mkpath + FileUtils.touch(CoreTap.instance.formula_dir/"testball.rb") + end + + it "cleans up file if outdated" do + allow(Utils::Bottles).to receive(:file_outdated?).with(any_args).and_return(true) + described_class.cleanup_cache + expect(bottle).not_to exist + expect(testball).not_to exist + end + + it "cleans up file if ARGV has -s and formula not installed" do + ARGV << "-s" + described_class.cleanup_cache + expect(bottle).not_to exist + expect(testball).not_to exist + end + + it "cleans up file if stale" do + described_class.cleanup_cache + expect(bottle).not_to exist + expect(testball).not_to exist + end + end + end + + describe "::prune?" do + before do + foo.mkpath + end + + let(:foo) { HOMEBREW_CACHE/"foo" } + + it "returns true when path_modified_time < days_default" do + allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - sec_in_a_day * 2) + expect(described_class.prune?(foo, days_default: "1")).to be_truthy + end + + it "returns false when path_modified_time >= days_default" do + expect(described_class.prune?(foo, days_default: "2")).to be_falsey + end end end diff --git a/Library/Homebrew/test/cmd/search_remote_tap_spec.rb b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb index b0beb122c..eb256b924 100644 --- a/Library/Homebrew/test/cmd/search_remote_tap_spec.rb +++ b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb @@ -2,6 +2,9 @@ require "cmd/search" describe Homebrew do specify "#search_taps" do + # Otherwise the tested method returns [], regardless of our stub + ENV.delete("HOMEBREW_NO_GITHUB_API") + json_response = { "items" => [ { diff --git a/Library/Homebrew/test/cmd/search_spec.rb b/Library/Homebrew/test/cmd/search_spec.rb index 77c2c6352..36ddde3e1 100644 --- a/Library/Homebrew/test/cmd/search_spec.rb +++ b/Library/Homebrew/test/cmd/search_spec.rb @@ -53,7 +53,7 @@ describe "brew search", :integration_test do "debian" => "https://packages.debian.org/search?keywords=testball&searchon=names&suite=all§ion=all", "opensuse" => "https://software.opensuse.org/search?q=testball", "fedora" => "https://apps.fedoraproject.org/packages/s/testball", - "ubuntu" => "http://packages.ubuntu.com/search?keywords=testball&searchon=names&suite=all§ion=all", + "ubuntu" => "https://packages.ubuntu.com/search?keywords=testball&searchon=names&suite=all§ion=all", }.each do |flag, url| specify "--#{flag}" do expect { brew "search", "--#{flag}", "testball", "HOMEBREW_BROWSER" => "echo" } diff --git a/Library/Homebrew/test/dev-cmd/audit_spec.rb b/Library/Homebrew/test/dev-cmd/audit_spec.rb index f2d8a8e7c..037865fdf 100644 --- a/Library/Homebrew/test/dev-cmd/audit_spec.rb +++ b/Library/Homebrew/test/dev-cmd/audit_spec.rb @@ -263,28 +263,6 @@ describe FormulaAuditor do expect(fa.problems.shift) .to eq('Use pkgshare instead of (share/"foolibc++")') end - - specify "no space in class inheritance" do - fa = formula_auditor "foo", <<-EOS.undent - class Foo<Formula - url '/foo-1.0.tgz' - end - EOS - - fa.line_problems "class Foo<Formula", 1 - expect(fa.problems.shift) - .to eq("Use a space in class inheritance: class Foo < Formula") - end - - specify "default template" do - fa = formula_auditor "foo", "class Foo < Formula; url '/foo-1.0.tgz'; end" - - fa.line_problems '# system "cmake", ".", *std_cmake_args', 3 - expect(fa.problems.shift).to eq("Commented cmake call found") - - fa.line_problems "# PLEASE REMOVE", 3 - expect(fa.problems.shift).to eq("Please remove default template comments") - end end describe "#audit_github_repository" do diff --git a/Library/Homebrew/test/download_strategies_spec.rb b/Library/Homebrew/test/download_strategies_spec.rb index 8c376a649..06d6fa855 100644 --- a/Library/Homebrew/test/download_strategies_spec.rb +++ b/Library/Homebrew/test/download_strategies_spec.rb @@ -200,6 +200,17 @@ describe GitDownloadStrategy do end end +describe CurlDownloadStrategy do + subject { described_class.new(name, resource) } + let(:name) { "foo" } + let(:url) { "http://example.com/foo.tar.gz" } + let(:resource) { double(Resource, url: url, mirrors: [], specs: { user: "download:123456" }, version: nil) } + + it "parses the opts and sets the corresponding args" do + expect(subject.send(:_curl_opts)).to eq(["--user", "download:123456"]) + end +end + describe DownloadStrategyDetector do describe "::detect" do subject { described_class.detect(url) } diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb index a48f12ecd..0a905004b 100644 --- a/Library/Homebrew/test/missing_formula_spec.rb +++ b/Library/Homebrew/test/missing_formula_spec.rb @@ -82,12 +82,6 @@ describe Homebrew::MissingFormula do it { is_expected.to be_blacklisted } end - context "clojure" do - subject { "clojure" } - - it { is_expected.to be_blacklisted } - end - context "gfortran" do subject { "gfortran" } diff --git a/Library/Homebrew/test/rubocops/lines_cop_spec.rb b/Library/Homebrew/test/rubocops/lines_cop_spec.rb index c865e1480..b0ed8f4d1 100644 --- a/Library/Homebrew/test/rubocops/lines_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/lines_cop_spec.rb @@ -51,3 +51,154 @@ describe RuboCop::Cop::FormulaAudit::Lines do end end end + +describe RuboCop::Cop::FormulaAudit::ClassInheritance do + subject(:cop) { described_class.new } + + context "When auditing lines" do + it "with no space in class inheritance" do + source = <<-EOS.undent + class Foo<Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + end + EOS + + expected_offenses = [{ message: "Use a space in class inheritance: class Foo < Formula", + severity: :convention, + line: 1, + column: 10, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + end +end + +describe RuboCop::Cop::FormulaAudit::Comments do + subject(:cop) { described_class.new } + + context "When auditing formula" do + it "with commented cmake call" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + # system "cmake", ".", *std_cmake_args + end + EOS + + expected_offenses = [{ message: "Please remove default template comments", + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with default template comments" do + source = <<-EOS.undent + class Foo < Formula + # PLEASE REMOVE + desc "foo" + url 'http://example.com/foo-1.0.tgz' + end + EOS + + expected_offenses = [{ message: "Please remove default template comments", + severity: :convention, + line: 2, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with commented out depends_on" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + # depends_on "foo" + end + EOS + + expected_offenses = [{ message: 'Commented-out dependency "foo"', + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + end +end + +describe RuboCop::Cop::FormulaAudit::Miscellaneous do + subject(:cop) { described_class.new } + + context "When auditing formula" do + it "with FileUtils" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + FileUtils.mv "hello" + end + EOS + + expected_offenses = [{ message: "Don't need 'FileUtils.' before mv", + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with long inreplace block vars" do + source = <<-EOS.undent + class Foo < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + inreplace "foo" do |longvar| + somerandomCall(longvar) + end + end + EOS + + expected_offenses = [{ message: "\"inreplace <filenames> do |s|\" is preferred over \"|longvar|\".", + severity: :convention, + line: 4, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + end +end diff --git a/Library/Homebrew/test/rubocops/patches_cop_spec.rb b/Library/Homebrew/test/rubocops/patches_cop_spec.rb index 092782bfb..28915a1ec 100644 --- a/Library/Homebrew/test/rubocops/patches_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/patches_cop_spec.rb @@ -28,11 +28,11 @@ describe RuboCop::Cop::FormulaAudit::Patches do end EOS - expected_offenses = [{ message: "Use the patch DSL instead of defining a 'patches' method", - severity: :convention, - line: 4, - column: 2, - source: source }] + expected_offenses = [{ message: "Use the patch DSL instead of defining a 'patches' method", + severity: :convention, + line: 4, + column: 2, + source: source }] inspect_source(cop, source) @@ -48,6 +48,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do "http://trac.macports.org/export/102865/trunk/dports/mail/uudeview/files/inews.c.patch", "http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=5;filename=patch-libunac1.txt;att=1;bug=623340", "https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch", + "https://github.com/dlang/dub/pull/1221.patch", ] patch_urls.each do |patch_url| source = <<-EOS.undent @@ -61,55 +62,64 @@ describe RuboCop::Cop::FormulaAudit::Patches do EOS inspect_source(cop, source) - if patch_url =~ %r{/raw\.github\.com/} - expected_offenses = [{ message: <<-EOS.undent.chomp, - GitHub/Gist patches should specify a revision: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 12, - source: source }] + expected_offense = if patch_url =~ %r{/raw\.github\.com/} + [{ message: <<-EOS.undent.chomp, + GitHub/Gist patches should specify a revision: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 12, + source: source }] elsif patch_url =~ %r{macports/trunk} - expected_offenses = [{ message: <<-EOS.undent.chomp, - MacPorts patches should specify a revision instead of trunk: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 33, - source: source }] + [{ message: <<-EOS.undent.chomp, + MacPorts patches should specify a revision instead of trunk: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 33, + source: source }] elsif patch_url =~ %r{^http://trac\.macports\.org} - expected_offenses = [{ message: <<-EOS.undent.chomp, - Patches from MacPorts Trac should be https://, not http: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 5, - source: source }] + [{ message: <<-EOS.undent.chomp, + Patches from MacPorts Trac should be https://, not http: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 5, + source: source }] elsif patch_url =~ %r{^http://bugs\.debian\.org} - expected_offenses = [{ message: <<-EOS.undent.chomp, - Patches from Debian should be https://, not http: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 5, - source: source }] + [{ message: <<-EOS.undent.chomp, + Patches from Debian should be https://, not http: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 5, + source: source }] elsif patch_url =~ %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)} - expected_offenses = [{ message: <<-EOS.undent, - use GitHub pull request URLs: - https://github.com/foo/foo-bar/pull/100.patch - Rather than patch-diff: - https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch - EOS - severity: :convention, - line: 5, - column: 5, - source: source }] + [{ message: <<-EOS.undent, + use GitHub pull request URLs: + https://github.com/foo/foo-bar/pull/100.patch + Rather than patch-diff: + https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch + EOS + severity: :convention, + line: 5, + column: 5, + source: source }] + elsif patch_url =~ %r{https?://github\.com/.+/.+/(?:commit|pull)/[a-fA-F0-9]*.(?:patch|diff)} + [{ message: <<-EOS.undent, + GitHub patches should use the full_index parameter: + #{patch_url}?full_index=1 + EOS + severity: :convention, + line: 5, + column: 5, + source: source }] end - expected_offenses.zip([cop.offenses.last]).each do |expected, actual| + expected_offense.zip([cop.offenses.last]).each do |expected, actual| expect_offense(expected, actual) end end @@ -130,19 +140,19 @@ describe RuboCop::Cop::FormulaAudit::Patches do end EOS - expected_offenses = [{ message: "Use the patch DSL instead of defining a 'patches' method", - severity: :convention, - line: 4, - column: 2, - source: source }, - { message: <<-EOS.undent.chomp, - Patches from MacPorts Trac should be https://, not http: - http://trac.macports.org/export/68507/trunk/dports/net/trafshow/files/ - EOS - severity: :convention, - line: 8, - column: 26, - source: source }] + expected_offenses = [{ message: "Use the patch DSL instead of defining a 'patches' method", + severity: :convention, + line: 4, + column: 2, + source: source }, + { message: <<-EOS.undent.chomp, + Patches from MacPorts Trac should be https://, not http: + http://trac.macports.org/export/68507/trunk/dports/net/trafshow/files/ + EOS + severity: :convention, + line: 8, + column: 26, + source: source }] inspect_source(cop, source) @@ -174,55 +184,55 @@ describe RuboCop::Cop::FormulaAudit::Patches do EOS inspect_source(cop, source) - if patch_url =~ %r{/raw\.github\.com/} - expected_offenses = [{ message: <<-EOS.undent.chomp, - GitHub/Gist patches should specify a revision: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 16, - source: source }] + expected_offense = if patch_url =~ %r{/raw\.github\.com/} + [{ message: <<-EOS.undent.chomp, + GitHub/Gist patches should specify a revision: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 16, + source: source }] elsif patch_url =~ %r{macports/trunk} - expected_offenses = [{ message: <<-EOS.undent.chomp, - MacPorts patches should specify a revision instead of trunk: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 37, - source: source }] + [{ message: <<-EOS.undent.chomp, + MacPorts patches should specify a revision instead of trunk: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 37, + source: source }] elsif patch_url =~ %r{^http://trac\.macports\.org} - expected_offenses = [{ message: <<-EOS.undent.chomp, - Patches from MacPorts Trac should be https://, not http: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 9, - source: source }] + [{ message: <<-EOS.undent.chomp, + Patches from MacPorts Trac should be https://, not http: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 9, + source: source }] elsif patch_url =~ %r{^http://bugs\.debian\.org} - expected_offenses = [{ message: <<-EOS.undent.chomp, - Patches from Debian should be https://, not http: - #{patch_url} - EOS - severity: :convention, - line: 5, - column: 9, - source: source }] + [{ message: <<-EOS.undent.chomp, + Patches from Debian should be https://, not http: + #{patch_url} + EOS + severity: :convention, + line: 5, + column: 9, + source: source }] elsif patch_url =~ %r{https?://patch-diff\.githubusercontent\.com/raw/(.+)/(.+)/pull/(.+)\.(?:diff|patch)} - expected_offenses = [{ message: <<-EOS.undent, - use GitHub pull request URLs: - https://github.com/foo/foo-bar/pull/100.patch - Rather than patch-diff: - https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch - EOS - severity: :convention, - line: 5, - column: 9, - source: source }] + [{ message: <<-EOS.undent, + use GitHub pull request URLs: + https://github.com/foo/foo-bar/pull/100.patch + Rather than patch-diff: + https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch + EOS + severity: :convention, + line: 5, + column: 9, + source: source }] end - expected_offenses.zip([cop.offenses.last]).each do |expected, actual| + expected_offense.zip([cop.offenses.last]).each do |expected, actual| expect_offense(expected, actual) end end diff --git a/Library/Homebrew/test/utils/analytics_spec.rb b/Library/Homebrew/test/utils/analytics_spec.rb new file mode 100644 index 000000000..bb6cda0b1 --- /dev/null +++ b/Library/Homebrew/test/utils/analytics_spec.rb @@ -0,0 +1,88 @@ +require "utils/analytics" +require "formula_installer" + +describe Utils::Analytics do + describe "::os_prefix_ci" do + context "when anonymous_os_prefix_ci is not set" do + before(:each) do + described_class.clear_anonymous_os_prefix_ci_cache + end + + it "returns OS_VERSION and prefix when HOMEBREW_PREFIX is not /usr/local" do + stub_const("HOMEBREW_PREFIX", "blah") + expect(described_class.os_prefix_ci).to include("#{OS_VERSION}, non-/usr/local") + end + + it "includes CI when ENV['CI'] is set" do + ENV["CI"] = "true" + expect(described_class.os_prefix_ci).to include("CI") + end + + it "does not include prefix when HOMEBREW_PREFIX is /usr/local" do + stub_const("HOMEBREW_PREFIX", "/usr/local") + expect(described_class.os_prefix_ci).not_to include("non-/usr/local") + end + end + end + + describe "::report_event" do + let(:f) { formula { url "foo-1.0" } } + let(:options) { FormulaInstaller.new(f).display_options(f) } + let(:action) { "#{f.full_name} #{options}".strip } + + context "when ENV vars is set" do + it "returns nil when HOMEBREW_NO_ANALYTICS is true" do + ENV["HOMEBREW_NO_ANALYTICS"] = "true" + expect(described_class.report_event("install", action)).to be_nil + end + + it "returns nil when HOMEBREW_NO_ANALYTICS_THIS_RUN is true" do + ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "true" + expect(described_class.report_event("install", action)).to be_nil + end + + it "returns nil when HOMEBREW_ANALYTICS_DEBUG is true" do + ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN") + ENV.delete("HOMEBREW_NO_ANALYTICS") + ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true" + expect(described_class.report_event("install", action)).to be_nil + end + end + end + + describe "::report_build_error" do + context "when tap is installed" do + let(:err) { BuildError.new(f, "badprg", %w[arg1 arg2], {}) } + let(:f) { formula { url "foo-1.0" } } + + it "reports event if BuildError raised for a formula with a public remote repository" do + allow_any_instance_of(Tap).to receive(:custom_remote?).and_return(false) + expect(described_class).to respond_to(:report_event) + described_class.report_build_error(err) + end + + it "does not report event if BuildError raised for a formula with a private remote repository" do + expect(described_class.report_build_error(err)).to be_nil + end + end + + context "when formula does not have a tap" do + let(:err) { BuildError.new(f, "badprg", %w[arg1 arg2], {}) } + let(:f) { double(Formula, name: "foo", path: "blah", tap: nil) } + + it "does not report event if BuildError is raised" do + expect(described_class.report_build_error(err)).to be_nil + end + end + + context "when tap for a formula is not installed" do + let(:err) { BuildError.new(f, "badprg", %w[arg1 arg2], {}) } + let(:f) { double(Formula, name: "foo", path: "blah", tap: CoreTap.instance) } + + it "does not report event if BuildError is raised" do + allow_any_instance_of(Pathname).to receive(:directory?).and_return(false) + expect(described_class.report_build_error(err)).to be_nil + end + end + end +end diff --git a/Library/Homebrew/test/utils/git_spec.rb b/Library/Homebrew/test/utils/git_spec.rb new file mode 100644 index 000000000..e511212f4 --- /dev/null +++ b/Library/Homebrew/test/utils/git_spec.rb @@ -0,0 +1,150 @@ +require "utils/git" + +describe Git do + before(:each) do + git = HOMEBREW_SHIMS_PATH/"scm/git" + + HOMEBREW_CACHE.cd do + system git, "init" + + File.open(file, "w") { |f| f.write("blah") } + system git, "add", HOMEBREW_CACHE/file + system git, "commit", "-m", "'File added'" + @h1 = `git rev-parse HEAD` + + File.open(file, "w") { |f| f.write("brew") } + system git, "add", HOMEBREW_CACHE/file + system git, "commit", "-m", "'written to File'" + @h2 = `git rev-parse HEAD` + end + end + + let(:file) { "blah.rb" } + let(:hash1) { @h1[0..6] } + let(:hash2) { @h2[0..6] } + + describe "#last_revision_commit_of_file" do + it "gives last revision commit when before_commit is nil" do + expect( + described_class.last_revision_commit_of_file(HOMEBREW_CACHE, file), + ).to eq(hash1) + end + + it "gives revision commit based on before_commit when it is not nil" do + expect( + described_class.last_revision_commit_of_file(HOMEBREW_CACHE, + file, + before_commit: hash2), + ).to eq(hash2) + end + end + + describe "#last_revision_of_file" do + it "returns last revision of file" do + expect( + described_class.last_revision_of_file(HOMEBREW_CACHE, + HOMEBREW_CACHE/file), + ).to eq("blah") + end + + it "returns last revision of file based on before_commit" do + expect( + described_class.last_revision_of_file(HOMEBREW_CACHE, HOMEBREW_CACHE/file, + before_commit: "0..3"), + ).to eq("brew") + end + end +end + +describe Utils do + before(:each) do + described_class.clear_git_available_cache + end + + describe "::git_available?" do + it "returns true if git --version command succeeds" do + expect(described_class.git_available?).to be_truthy + end + + it "returns false if git --version command does not succeed" do + stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") + expect(described_class.git_available?).to be_falsey + end + end + + describe "::git_path" do + it "returns nil when git is not available" do + stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") + expect(described_class.git_path).to eq(nil) + end + + it "returns path of git when git is available" do + expect(described_class.git_path).to end_with("git") + end + end + + describe "::git_version" do + it "returns nil when git is not available" do + stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") + expect(described_class.git_path).to eq(nil) + end + + it "returns version of git when git is available" do + expect(described_class.git_version).not_to be_nil + end + end + + describe "::ensure_git_installed!" do + it "returns nil if git already available" do + expect(described_class.ensure_git_installed!).to be_nil + end + + context "when git is not already available" do + before do + stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") + end + + it "can't install brewed git if homebrew/core is unavailable" do + allow_any_instance_of(Pathname).to receive(:directory?).and_return(false) + expect { described_class.ensure_git_installed! }.to raise_error("Git is unavailable") + end + + it "raises error if can't install git" do + stub_const("HOMEBREW_BREW_FILE", HOMEBREW_PREFIX/"bin/brew") + expect { described_class.ensure_git_installed! }.to raise_error("Git is unavailable") + end + + it "installs git" do + allow(Homebrew).to receive(:_system).with(any_args).and_return(true) + described_class.ensure_git_installed! + end + end + end + + describe "::git_remote_exists" do + it "returns true when git is not available" do + stub_const("HOMEBREW_SHIMS_PATH", HOMEBREW_PREFIX/"bin/shim") + expect(described_class.git_remote_exists("blah")).to be_truthy + end + + context "when git is available" do + it "returns true when git remote exists", :needs_network do + git = HOMEBREW_SHIMS_PATH/"scm/git" + url = "http://github.com/Homebrew/homebrew.github.io" + repo = HOMEBREW_CACHE/"hey" + repo.mkpath + + repo.cd do + system git, "init" + system git, "remote", "add", "origin", url + end + + expect(described_class.git_remote_exists(url)).to be_truthy + end + + it "returns false when git remote does not exist" do + expect(described_class.git_remote_exists("blah")).to be_falsey + end + end + end +end diff --git a/Library/Homebrew/test/utils/svn_spec.rb b/Library/Homebrew/test/utils/svn_spec.rb new file mode 100644 index 000000000..4edb365a0 --- /dev/null +++ b/Library/Homebrew/test/utils/svn_spec.rb @@ -0,0 +1,39 @@ +require "utils/svn" + +describe Utils do + describe "#self.svn_available?" do + before(:each) do + described_class.clear_svn_version_cache + end + + it "returns svn version if svn available" do + expect(described_class.svn_available?).to be_truthy + end + end + + describe "#self.svn_remote_exists" do + it "returns true when svn is not available" do + allow(Utils).to receive(:svn_available?).and_return(false) + expect(described_class.svn_remote_exists("blah")).to be_truthy + end + + context "when svn is available" do + before do + allow(Utils).to receive(:svn_available?).and_return(true) + end + + it "returns false when remote does not exist" do + expect(described_class.svn_remote_exists(HOMEBREW_CACHE/"install")).to be_falsey + end + + it "returns true when remote exists", :needs_network do + remote = "http://github.com/Homebrew/install" + svn = HOMEBREW_SHIMS_PATH/"scm/svn" + + HOMEBREW_CACHE.cd { system svn, "checkout", remote } + + expect(described_class.svn_remote_exists(HOMEBREW_CACHE/"install")).to be_truthy + end + end + end +end diff --git a/Library/Homebrew/utils/analytics.rb b/Library/Homebrew/utils/analytics.rb index a89995ba9..9766c14db 100644 --- a/Library/Homebrew/utils/analytics.rb +++ b/Library/Homebrew/utils/analytics.rb @@ -3,6 +3,11 @@ require "erb" module Utils module Analytics class << self + def clear_anonymous_os_prefix_ci_cache + return unless instance_variable_defined?(:@anonymous_os_prefix_ci) + remove_instance_variable(:@anonymous_os_prefix_ci) + end + def os_prefix_ci @anonymous_os_prefix_ci ||= begin os = OS_VERSION diff --git a/Library/Homebrew/utils/curl.rb b/Library/Homebrew/utils/curl.rb index 52d03c93e..bc7055c0c 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -37,13 +37,12 @@ def curl(*args) safe_system(*curl_args(*args)) end -def curl_download(*args, to: nil, **options) - continue_at ||= "-" - curl("--location", "--remote-time", "--continue-at", continue_at, "--output", to, *args, **options) +def curl_download(*args, to: nil, continue_at: "-", **options) + curl("--location", "--remote-time", "--continue-at", continue_at.to_s, "--output", to, *args, **options) rescue ErrorDuringExecution # `curl` error 33: HTTP server doesn't seem to support byte ranges. Cannot resume. if $CHILD_STATUS.exitstatus == 33 && continue_at == "-" - continue_at = "0" + continue_at = 0 retry end diff --git a/Library/Homebrew/utils/fork.rb b/Library/Homebrew/utils/fork.rb index 92f5bf899..2f2a403e2 100644 --- a/Library/Homebrew/utils/fork.rb +++ b/Library/Homebrew/utils/fork.rb @@ -38,7 +38,7 @@ module Utils Process.wait(pid) unless socket.nil? raise Marshal.load(data) unless data.nil? || data.empty? raise Interrupt if $CHILD_STATUS.exitstatus == 130 - raise "Suspicious failure" unless $CHILD_STATUS.success? + raise "Forked child process failed: #{$CHILD_STATUS}" unless $CHILD_STATUS.success? end end end diff --git a/Library/Homebrew/utils/git.rb b/Library/Homebrew/utils/git.rb index 43d93b64e..1fc01188c 100644 --- a/Library/Homebrew/utils/git.rb +++ b/Library/Homebrew/utils/git.rb @@ -16,8 +16,7 @@ module Git def last_revision_of_file(repo, file, before_commit: nil) relative_file = Pathname(file).relative_path_from(repo) - commit_hash = last_revision_commit_of_file(repo, file, before_commit: before_commit) - + commit_hash = last_revision_commit_of_file(repo, relative_file, before_commit: before_commit) out, = Open3.capture3( HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo, "show", "#{commit_hash}:#{relative_file}" diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index a1cf5fbba..a50d6d8e5 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -245,7 +245,7 @@ module GitHub end def print_pull_requests_matching(query) - open_or_closed_prs = search_issues(query, type: "pr") + open_or_closed_prs = search_issues(query, type: "pr", user: "Homebrew") open_prs = open_or_closed_prs.select { |i| i["state"] == "open" } prs = if !open_prs.empty? diff --git a/Library/Homebrew/utils/svn.rb b/Library/Homebrew/utils/svn.rb index fb49ac2e9..150b7eee7 100644 --- a/Library/Homebrew/utils/svn.rb +++ b/Library/Homebrew/utils/svn.rb @@ -1,4 +1,8 @@ module Utils + def self.clear_svn_version_cache + remove_instance_variable(:@svn) if instance_variable_defined?(:@svn) + end + def self.svn_available? return @svn if instance_variable_defined?(:@svn) @svn = quiet_system HOMEBREW_SHIMS_PATH/"scm/svn", "--version" |
