diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f4e56604e5..287e95b03a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,17 +1,22 @@ +**Please note we will close your issue without comment if you delete, do not read or do not fill out the issue checklist below and provide ALL the requested information. If you repeatedly fail to use the issue template, we will block you from ever submitting issues to Homebrew again.** + # Please always follow these steps: -- [ ] Confirmed this is a problem with running a `brew` command and not `brew install`ing or the post-install behaviour of one or more formulae? If it's a formulae-specific problem please file this issue at https://github.com/Homebrew/homebrew-core/issues/new +- [ ] Confirmed this is a problem with running a `brew` command and not `brew install`ing or the post-install behaviour of one or more formulae? If it's a formulae-specific problem please file this issue at the relevant tap e.g. for Homebrew/homebrew-core https://github.com/Homebrew/homebrew-core/issues/new - [ ] Ran `brew update` and retried your prior step? - [ ] Ran `brew doctor`, fixed all issues and retried your prior step? - [ ] Ran `brew config` and `brew doctor` and included their output with your issue? -**Please note we will close your issue without comment if you delete or do not fill out the issue checklist and provide ALL the requested information.** - To help us debug your issue please explain: - What you were trying to do (and why) - What happened (include command output) - What you expected to happen - Step-by-step reproduction instructions (by running `brew` commands) -# Or propose a feature: -Please replace this section with a detailed description of your proposed feature, the motivation for it, how it would be relevant to at least 90% of Homebrew users and alternatives considered. -Please note we will close this issue or ask you to create a pull-request if it's something we're not actively planning to work on. +# Features +Please replace this section with: +- a detailed description of your proposed feature +- the motivation for the feature +- how the feature would be relevant to at least 90% of Homebrew users +- what alternatives to the feature you have considered + +We will close this issue or ask you to create a pull-request if it's something we're not actively planning to work on. diff --git a/.travis.yml b/.travis.yml index 9cdebda832..670e69ad09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,30 +11,31 @@ matrix: fast_finish: true include: - os: osx - osx_image: xcode8.3 + osx_image: xcode9 rvm: system - os: linux sudo: false - rvm: 2.0.0 + rvm: 2.3.3 before_install: - export HOMEBREW_NO_AUTO_UPDATE=1 - export HOMEBREW_DEVELOPER=1 - - git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then HOMEBREW_REPOSITORY="$(brew --repo)"; sudo chown -R "$USER" "$HOMEBREW_REPOSITORY/Library/Taps"; mv "$HOMEBREW_REPOSITORY/Library/Taps" "$PWD/Library"; sudo rm -rf "$HOMEBREW_REPOSITORY"; sudo ln -s "$PWD" "$HOMEBREW_REPOSITORY"; + git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot; else + umask 022; git fetch --unshallow; export PATH="$PWD/bin:$PATH"; HOMEBREW_CORE_TAP_DIR="$(brew --repo "homebrew/core")"; mkdir -p "$HOMEBREW_CORE_TAP_DIR"; + git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot; HOMEBREW_TEST_BOT_TAP_DIR="$(brew --repo "homebrew/test-bot")"; ln -s "$HOMEBREW_TEST_BOT_TAP_DIR/.git" "$HOMEBREW_TEST_BOT_TAP_DIR/Formula" "$HOMEBREW_CORE_TAP_DIR"; - umask 022; fi script: diff --git a/CODEOFCONDUCT.md b/CODE_OF_CONDUCT.md similarity index 100% rename from CODEOFCONDUCT.md rename to CODE_OF_CONDUCT.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e5272c6826..4729d6ca33 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ # Contributing to Homebrew -First time contributing to Homebrew? Read our [Code of Conduct](https://github.com/Homebrew/brew/blob/master/CODEOFCONDUCT.md#code-of-conduct). +First time contributing to Homebrew? Read our [Code of Conduct](https://github.com/Homebrew/brew/blob/master/CODE_OF_CONDUCT.md#code-of-conduct). ### Report a bug diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml index a2e3cc9c95..ed31de5954 100644 --- a/Library/.rubocop.yml +++ b/Library/.rubocop.yml @@ -1,5 +1,5 @@ AllCops: - TargetRubyVersion: 2.0 + TargetRubyVersion: 2.3 Exclude: - '**/Casks/**/*' - '**/vendor/**/*' @@ -123,7 +123,7 @@ Style/Tab: Enabled: true # dashes in filenames are typical -Style/FileName: +Naming/FileName: Regex: !ruby/regexp /^[\w\@\-\+\.]+(\.rb)?$/ # falsely flags e.g. curl formatting arguments as format strings @@ -193,8 +193,25 @@ Style/TrailingCommaInArguments: EnforcedStyleForMultiline: comma # we have too many variables like sha256 where this harms readability -Style/VariableNumber: +Naming/VariableNumber: Enabled: false Style/WordArray: MinSize: 4 + +# we want to add this slowly and manually +Style/FrozenStringLiteralComment: + Enabled: false + +# generally rescuing StandardError is fine +Lint/RescueWithoutErrorClass: + Enabled: false + +# implicitly allow EOS as we use it everywhere +Naming/HeredocDelimiterNaming: + Blacklist: + - END, EOD, EOF + +# we output how to use interpolated strings too often +Lint/InterpolationCheck: + Enabled: false diff --git a/Library/Homebrew/.rubocop.yml b/Library/Homebrew/.rubocop.yml index 26c9445294..1434686432 100644 --- a/Library/Homebrew/.rubocop.yml +++ b/Library/Homebrew/.rubocop.yml @@ -1,6 +1,5 @@ inherit_from: - ../.rubocop.yml - - .rubocop_todo.yml AllCops: Include: @@ -26,12 +25,30 @@ Lint/NestedMethodDefinition: Lint/ParenthesesAsGroupedExpression: Enabled: true +Metrics/BlockNesting: + Max: 5 + +Metrics/ModuleLength: + Max: 360 + Metrics/ParameterLists: CountKeywordArgs: false +# we won't change backward compatible method names +Naming/MethodName: + Exclude: + - 'compat/**/*' + +# we won't change backward compatible predicate names +Naming/PredicateName: + Exclude: + - 'compat/**/*' + NameWhitelist: is_32_bit?, is_64_bit? + Style/BlockDelimiters: Exclude: - '**/*_spec.rb' + - '**/shared_examples/**/*.rb' # so many of these in formulae but none in here Style/GuardClause: @@ -40,14 +57,3 @@ Style/GuardClause: # hash-rockets preferred for formulae, a: 1 preferred elsewhere Style/HashSyntax: EnforcedStyle: ruby19_no_mixed_keys - -# we won't change backward compatible method names -Style/MethodName: - Exclude: - - 'compat/**/*' - -# we won't change backward compatible predicate names -Style/PredicateName: - Exclude: - - 'compat/**/*' - NameWhitelist: is_32_bit?, is_64_bit? diff --git a/Library/Homebrew/.rubocop_todo.yml b/Library/Homebrew/.rubocop_todo.yml deleted file mode 100644 index 37518a5f0a..0000000000 --- a/Library/Homebrew/.rubocop_todo.yml +++ /dev/null @@ -1,145 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config --exclude-limit 100` -# on 2017-01-27 21:44:55 +0000 using RuboCop version 0.47.1. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. - -# Offense count: 17 -Lint/HandleExceptions: - Exclude: - - 'cmd/install.rb' - - 'cmd/reinstall.rb' - - 'cmd/tap.rb' - - 'cmd/update-report.rb' - - 'cmd/upgrade.rb' - - 'cmd/uses.rb' - - 'descriptions.rb' - - 'diagnostic.rb' - - 'extend/ENV/super.rb' - - 'extend/pathname.rb' - - 'formula.rb' - - 'formula_versions.rb' - - 'test/ENV_test.rb' - -# Offense count: 3 -Lint/IneffectiveAccessModifier: - Exclude: - - 'formula.rb' - - 'version.rb' - -# Offense count: 1 -Lint/Loop: - Exclude: - - 'patch.rb' - -# Offense count: 28 -Lint/RescueException: - Exclude: - - 'brew.rb' - - 'build.rb' - - 'cmd/fetch.rb' - - 'cmd/reinstall.rb' - - 'cmd/update-report.rb' - - 'debrew.rb' - - 'dev-cmd/pull.rb' - - 'dev-cmd/test.rb' - - 'formula.rb' - - 'formula_installer.rb' - - 'migrator.rb' - - 'postinstall.rb' - - 'readall.rb' - - 'test.rb' - - 'test/ENV_test.rb' - - 'utils/fork.rb' - -# Offense count: 1 -Lint/ShadowedException: - Exclude: - - 'utils/fork.rb' - -# Offense count: 13 -# Configuration parameters: CountBlocks. -Metrics/BlockNesting: - Max: 5 - -# Offense count: 19 -# Configuration parameters: CountComments. -Metrics/ModuleLength: - Max: 400 - -# Offense count: 1 -# Configuration parameters: CountKeywordArgs. -Metrics/ParameterLists: - Max: 6 - -# Offense count: 2 -Security/MarshalLoad: - Exclude: - - 'dependency.rb' - - 'utils/fork.rb' - -# Offense count: 1 -Style/AccessorMethodName: - Exclude: - - 'extend/ENV/super.rb' - -# Offense count: 6 -Style/ClassVars: - Exclude: - - 'dev-cmd/audit.rb' - - 'formula_installer.rb' - - 'test/support/helper/fs_leak_logger.rb' - -# Offense count: 13 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: compact, expanded -Style/EmptyMethod: - Exclude: - - 'debrew/irb.rb' - - 'download_strategy.rb' - - 'extend/ENV/super.rb' - - 'formula.rb' - - 'patch.rb' - -# Offense count: 13 -# Configuration parameters: AllowedVariables. -Style/GlobalVars: - Exclude: - - 'diagnostic.rb' - - 'utils.rb' - -# Offense count: 1 -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: module_function, extend_self -Style/ModuleFunction: - Exclude: - - 'os/mac/xcode.rb' - -# Offense count: 8 -Style/MultilineBlockChain: - Exclude: - - 'cmd/search.rb' - - 'dev-cmd/aspell-dictionaries.rb' - - 'dev-cmd/audit.rb' - - 'dev-cmd/man.rb' - - 'diagnostic.rb' - - 'test/patching_test.rb' - -# Offense count: 4 -# Cop supports --auto-correct. -Style/MutableConstant: - Exclude: - - 'dependency_collector.rb' - - 'formulary.rb' - - 'tab.rb' - - 'tap.rb' - -# Offense count: 8 -Style/OpMethod: - Exclude: - - 'dependencies.rb' - - 'install_renamed.rb' - - 'options.rb' diff --git a/Library/Homebrew/.simplecov b/Library/Homebrew/.simplecov index 23e60faeb0..e0d6d7601a 100755 --- a/Library/Homebrew/.simplecov +++ b/Library/Homebrew/.simplecov @@ -11,11 +11,6 @@ SimpleCov.start do # tests to be dropped. This causes random fluctuations in test coverage. merge_timeout 86400 - add_filter "/Homebrew/compat/" - add_filter "/Homebrew/dev-cmd/tests.rb" - add_filter "/Homebrew/test/" - add_filter "/Homebrew/vendor/" - if ENV["HOMEBREW_INTEGRATION_TEST"] command_name "#{ENV["HOMEBREW_INTEGRATION_TEST"]} (#{$PROCESS_ID})" @@ -33,22 +28,32 @@ SimpleCov.start do end else command_name "#{command_name} (#{$PROCESS_ID})" + + subdirs = Dir.chdir(SimpleCov.root) { Dir.glob("*") } + .reject { |d| d.end_with?(".rb") || ["test", "vendor"].include?(d) } + .map { |d| "#{d}/**/*.rb" }.join(",") + # Not using this during integration tests makes the tests 4x times faster # without changing the coverage. - track_files "#{SimpleCov.root}/**/*.rb" + track_files "#{SimpleCov.root}/{#{subdirs},*.rb}" end + add_filter %r{^/compat/} + add_filter %r{^/dev-cmd/tests.rb$} + add_filter %r{^/test/} + add_filter %r{^/vendor/} + # Add groups and the proper project name to the output. project_name "Homebrew" - add_group "Cask", "/Homebrew/cask/" - add_group "Commands", %w[/Homebrew/cmd/ /Homebrew/dev-cmd/] - add_group "Extensions", "/Homebrew/extend/" - add_group "OS", %w[/Homebrew/extend/os/ /Homebrew/os/] - add_group "Requirements", "/Homebrew/requirements/" - add_group "Scripts", %w[ - /Homebrew/brew.rb - /Homebrew/build.rb - /Homebrew/postinstall.rb - /Homebrew/test.rb + add_group "Cask", %r{^/cask/} + add_group "Commands", [%r{/cmd/}, %r{^/dev-cmd/}] + add_group "Extensions", %r{^/extend/} + add_group "OS", [%r{^/extend/os/}, %r{^/os/}] + add_group "Requirements", %r{^/requirements/} + add_group "Scripts", [ + %r{^/brew.rb$}, + %r{^/build.rb$}, + %r{^/postinstall.rb$}, + %r{^/test.rb$}, ] end diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index ec86bd7941..86b40a79da 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -5,8 +5,12 @@ end std_trap = trap("INT") { exit! 130 } # no backtrace thanks # check ruby version before requiring any modules. -RUBY_TWO = RUBY_VERSION.split(".").first.to_i >= 2 -raise "Homebrew must be run under Ruby 2!" unless RUBY_TWO +RUBY_VERSION_SPLIT = RUBY_VERSION.split "." +RUBY_X = RUBY_VERSION_SPLIT[0].to_i +RUBY_Y = RUBY_VERSION_SPLIT[1].to_i +if RUBY_X < 2 || (RUBY_X == 2 && RUBY_Y < 3) + raise "Homebrew must be run under Ruby 2.3!" +end require "pathname" HOMEBREW_LIBRARY_PATH = Pathname.new(__FILE__).realpath.parent @@ -105,18 +109,16 @@ begin possible_tap = OFFICIAL_CMD_TAPS.find { |_, cmds| cmds.include?(cmd) } possible_tap = Tap.fetch(possible_tap.first) if possible_tap - if possible_tap && !possible_tap.installed? - brew_uid = HOMEBREW_BREW_FILE.stat.uid - tap_commands = [] - if Process.uid.zero? && !brew_uid.zero? - tap_commands += %W[/usr/bin/sudo -u ##{brew_uid}] - end - tap_commands += %W[#{HOMEBREW_BREW_FILE} tap #{possible_tap}] - safe_system(*tap_commands) - exec HOMEBREW_BREW_FILE, cmd, *ARGV - else - odie "Unknown command: #{cmd}" + odie "Unknown command: #{cmd}" if !possible_tap || possible_tap.installed? + + brew_uid = HOMEBREW_BREW_FILE.stat.uid + tap_commands = [] + if Process.uid.zero? && !brew_uid.zero? + tap_commands += %W[/usr/bin/sudo -u ##{brew_uid}] end + tap_commands += %W[#{HOMEBREW_BREW_FILE} tap #{possible_tap}] + safe_system(*tap_commands) + exec HOMEBREW_BREW_FILE, cmd, *ARGV end rescue UsageError => e require "cmd/help" @@ -144,7 +146,7 @@ rescue MethodDeprecatedError => e $stderr.puts " #{Formatter.url(e.issues_url)}" end exit 1 -rescue Exception => e +rescue Exception => e # rubocop:disable Lint/RescueException onoe e if internal_cmd && defined?(OS::ISSUES_URL) && !ENV["HOMEBREW_NO_AUTO_UPDATE"] diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index c40ce8bf72..b2859c9277 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -23,7 +23,7 @@ HOMEBREW_VERSION="$(git -C "$HOMEBREW_REPOSITORY" describe --tags --dirty --abbr HOMEBREW_USER_AGENT_VERSION="$HOMEBREW_VERSION" if [[ -z "$HOMEBREW_VERSION" ]] then - HOMEBREW_VERSION=">1.2.0 (no git repository)" + HOMEBREW_VERSION=">1.2.0 (shallow or no git repository)" HOMEBREW_USER_AGENT_VERSION="1.X.Y" fi @@ -105,7 +105,14 @@ then HOMEBREW_OS_USER_AGENT_VERSION="Mac OS X $HOMEBREW_MACOS_VERSION" printf -v HOMEBREW_MACOS_VERSION_NUMERIC "%02d%02d%02d" ${HOMEBREW_MACOS_VERSION//./ } - if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "100900" && + if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "101000" ]] + then + HOMEBREW_SYSTEM_CURL_TOO_OLD="1" + fi + + # The system Curl is too old for some modern HTTPS certificates on + # older macOS versions. + if [[ -n "$HOMEBREW_SYSTEM_CURL_TOO_OLD" && -x "$HOMEBREW_PREFIX/opt/curl/bin/curl" ]] then HOMEBREW_CURL="$HOMEBREW_PREFIX/opt/curl/bin/curl" diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb index 8dd4fb245f..836b360daa 100644 --- a/Library/Homebrew/build.rb +++ b/Library/Homebrew/build.rb @@ -112,6 +112,10 @@ class Build formula.extend(Debrew::Formula) if ARGV.debug? formula.brew do |_formula, staging| + # For head builds, HOMEBREW_FORMULA_PREFIX should include the commit, + # which is not known until after the formula has been staged. + ENV["HOMEBREW_FORMULA_PREFIX"] = formula.prefix + staging.retain! if ARGV.keep_tmp? formula.patch @@ -186,7 +190,7 @@ begin options = Options.create(ARGV.flags_only) build = Build.new(formula, options) build.install -rescue Exception => e +rescue Exception => e # rubocop:disable Lint/RescueException Marshal.dump(e, error_pipe) error_pipe.close exit! 1 diff --git a/Library/Homebrew/cask/lib/hbc.rb b/Library/Homebrew/cask/lib/hbc.rb index 780acedf57..01a0850192 100644 --- a/Library/Homebrew/cask/lib/hbc.rb +++ b/Library/Homebrew/cask/lib/hbc.rb @@ -25,7 +25,6 @@ require "hbc/scopes" require "hbc/staged" require "hbc/system_command" require "hbc/topological_hash" -require "hbc/underscore_supporting_uri" require "hbc/url" require "hbc/utils" require "hbc/verify" diff --git a/Library/Homebrew/cask/lib/hbc/artifact.rb b/Library/Homebrew/cask/lib/hbc/artifact.rb index 074d15017a..1cbe49cf27 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact.rb @@ -25,47 +25,5 @@ require "hbc/artifact/zap" module Hbc module Artifact - # NOTE: Order is important here! - # - # The `uninstall` stanza should be run first, as it may - # depend on other artifacts still being installed. - # - # We want to extract nested containers before we - # handle any other artifacts. - # - TYPES = [ - PreflightBlock, - Uninstall, - NestedContainer, - Installer, - App, - Suite, - Artifact, # generic 'artifact' stanza - Colorpicker, - Pkg, - Prefpane, - Qlplugin, - Dictionary, - Font, - Service, - StageOnly, - Binary, - InputMethod, - InternetPlugin, - AudioUnitPlugin, - VstPlugin, - Vst3Plugin, - ScreenSaver, - PostflightBlock, - Zap, - ].freeze - - def self.for_cask(cask, options = {}) - odebug "Determining which artifacts are present in Cask #{cask}" - - TYPES - .select { |klass| klass.me?(cask) } - .map { |klass| klass.new(cask, options) } - end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/abstract_artifact.rb b/Library/Homebrew/cask/lib/hbc/artifact/abstract_artifact.rb new file mode 100644 index 0000000000..f9f736662f --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/artifact/abstract_artifact.rb @@ -0,0 +1,109 @@ +module Hbc + module Artifact + class AbstractArtifact + include Comparable + extend Predicable + + def self.english_name + @english_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2') + end + + def self.english_article + @english_article ||= (english_name =~ /^[aeiou]/i) ? "an" : "a" + end + + def self.dsl_key + @dsl_key ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym + end + + def self.dirmethod + @dirmethod ||= "#{dsl_key}dir".to_sym + end + + def <=>(other) + return unless other.class < AbstractArtifact + return 0 if self.class == other.class + + @@sort_order ||= [ # rubocop:disable Style/ClassVars + PreflightBlock, + # The `uninstall` stanza should be run first, as it may + # depend on other artifacts still being installed. + Uninstall, + # We want to extract nested containers before we + # handle any other artifacts. + NestedContainer, + Installer, + [ + App, + Suite, + Artifact, + Colorpicker, + Prefpane, + Qlplugin, + Dictionary, + Font, + Service, + InputMethod, + InternetPlugin, + AudioUnitPlugin, + VstPlugin, + Vst3Plugin, + ScreenSaver, + ], + # `pkg` should be run before `binary`, so + # targets are created prior to linking. + Pkg, + Binary, + PostflightBlock, + Zap, + ].each_with_index.flat_map { |classes, i| [*classes].map { |c| [c, i] } }.to_h + + (@@sort_order[self.class] <=> @@sort_order[other.class]).to_i + end + + # TODO: this sort of logic would make more sense in dsl.rb, or a + # constructor called from dsl.rb, so long as that isn't slow. + def self.read_script_arguments(arguments, stanza, default_arguments = {}, override_arguments = {}, key = nil) + # TODO: when stanza names are harmonized with class names, + # stanza may not be needed as an explicit argument + description = key ? "#{stanza} #{key.inspect}" : stanza.to_s + + # backward-compatible string value + arguments = { executable: arguments } if arguments.is_a?(String) + + # key sanity + permitted_keys = [:args, :input, :executable, :must_succeed, :sudo, :print_stdout, :print_stderr] + unknown_keys = arguments.keys - permitted_keys + unless unknown_keys.empty? + opoo %Q{Unknown arguments to #{description} -- #{unknown_keys.inspect} (ignored). Running "brew update; brew cleanup; brew cask cleanup" will likely fix it.} + end + arguments.select! { |k| permitted_keys.include?(k) } + + # key warnings + override_keys = override_arguments.keys + ignored_keys = arguments.keys & override_keys + unless ignored_keys.empty? + onoe "Some arguments to #{description} will be ignored -- :#{unknown_keys.inspect} (overridden)." + end + + # extract executable + executable = arguments.key?(:executable) ? arguments.delete(:executable) : nil + + arguments = default_arguments.merge arguments + arguments.merge! override_arguments + + [executable, arguments] + end + + attr_reader :cask + + def initialize(cask) + @cask = cask + end + + def to_s + "#{summarize} (#{self.class.english_name})" + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/abstract_flight_block.rb b/Library/Homebrew/cask/lib/hbc/artifact/abstract_flight_block.rb index be3050acb7..a3075ff409 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/abstract_flight_block.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/abstract_flight_block.rb @@ -1,39 +1,45 @@ -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" module Hbc module Artifact - class AbstractFlightBlock < Base - def self.artifact_dsl_key + class AbstractFlightBlock < AbstractArtifact + def self.dsl_key super.to_s.sub(/_block$/, "").to_sym end - def self.uninstall_artifact_dsl_key - artifact_dsl_key.to_s.prepend("uninstall_").to_sym + def self.uninstall_dsl_key + dsl_key.to_s.prepend("uninstall_").to_sym end - def self.class_for_dsl_key(dsl_key) - Object.const_get("Hbc::DSL::#{dsl_key.to_s.split("_").collect(&:capitalize).join}") + attr_reader :directives + + def initialize(cask, **directives) + super(cask) + @directives = directives end - def self.me?(cask) - cask.artifacts[artifact_dsl_key].any? || - cask.artifacts[uninstall_artifact_dsl_key].any? + def install_phase(**) + abstract_phase(self.class.dsl_key) end - def install_phase - abstract_phase(self.class.artifact_dsl_key) - end - - def uninstall_phase - abstract_phase(self.class.uninstall_artifact_dsl_key) + def uninstall_phase(**) + abstract_phase(self.class.uninstall_dsl_key) end private + def class_for_dsl_key(dsl_key) + namespace = self.class.name.to_s.sub(/::.*::.*$/, "") + self.class.const_get("#{namespace}::DSL::#{dsl_key.to_s.split("_").collect(&:capitalize).join}") + end + def abstract_phase(dsl_key) - @cask.artifacts[dsl_key].each do |block| - self.class.class_for_dsl_key(dsl_key).new(@cask).instance_eval(&block) - end + return if (block = directives[dsl_key]).nil? + class_for_dsl_key(dsl_key).new(cask).instance_eval(&block) + end + + def summarize + directives.keys.map(&:to_s).join(", ") end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb b/Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb similarity index 60% rename from Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb rename to Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb index d926441502..3f63dae8fe 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/abstract_uninstall.rb @@ -1,11 +1,11 @@ require "pathname" require "timeout" -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" module Hbc module Artifact - class UninstallBase < Base + class AbstractUninstall < AbstractArtifact ORDERED_DIRECTIVES = [ :early_script, :launchctl, @@ -20,26 +20,42 @@ module Hbc :rmdir, ].freeze - def dispatch_uninstall_directives - directives_set = @cask.artifacts[stanza] - ohai "Running #{stanza} process for #{@cask}; your password may be necessary" + def self.from_args(cask, **directives) + new(cask, directives) + end - directives_set.each do |directives| - warn_for_unknown_directives(directives) - end + attr_reader :directives - ORDERED_DIRECTIVES.each do |directive_sym| - directives_set.select { |h| h.key?(directive_sym) }.each do |directives| - args = directives[directive_sym] - send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args)) - end - end + def initialize(cask, directives) + super(cask) + directives[:signal] = [*directives[:signal]].flatten.each_slice(2).to_a + @directives = directives + end + + def to_h + directives.to_h + end + + def summarize + to_h.flat_map { |key, val| [*val].map { |v| "#{key.inspect} => #{v.inspect}" } }.join(", ") end private + def dispatch_uninstall_directives(**options) + ohai "Running #{stanza} process for #{@cask}; your password may be necessary" + + warn_for_unknown_directives(directives) + + ORDERED_DIRECTIVES.each do |directive_sym| + next unless directives.key?(directive_sym) + args = directives[directive_sym] + send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args), **options) + end + end + def stanza - self.class.artifact_dsl_key + self.class.dsl_key end def warn_for_unknown_directives(directives) @@ -51,18 +67,18 @@ module Hbc # Preserve prior functionality of script which runs first. Should rarely be needed. # :early_script should not delete files, better defer that to :script. # If Cask writers never need :early_script it may be removed in the future. - def uninstall_early_script(directives) - uninstall_script(directives, directive_name: :early_script) + def uninstall_early_script(directives, **options) + uninstall_script(directives, directive_name: :early_script, **options) end # :launchctl must come before :quit/:signal for cases where app would instantly re-launch - def uninstall_launchctl(*services) + def uninstall_launchctl(*services, command: nil, **_) services.each do |service| ohai "Removing launchctl service #{service}" [false, true].each do |with_sudo| - plist_status = @command.run("/bin/launchctl", args: ["list", service], sudo: with_sudo, print_stderr: false).stdout + plist_status = command.run("/bin/launchctl", args: ["list", service], sudo: with_sudo, print_stderr: false).stdout if plist_status =~ /^\{/ - @command.run!("/bin/launchctl", args: ["remove", service], sudo: with_sudo) + command.run!("/bin/launchctl", args: ["remove", service], sudo: with_sudo) sleep 1 end paths = ["/Library/LaunchAgents/#{service}.plist", @@ -70,38 +86,38 @@ module Hbc paths.each { |elt| elt.prepend(ENV["HOME"]) } unless with_sudo paths = paths.map { |elt| Pathname(elt) }.select(&:exist?) paths.each do |path| - @command.run!("/bin/rm", args: ["-f", "--", path], sudo: with_sudo) + command.run!("/bin/rm", args: ["-f", "--", path], sudo: with_sudo) end # undocumented and untested: pass a path to uninstall :launchctl next unless Pathname(service).exist? - @command.run!("/bin/launchctl", args: ["unload", "-w", "--", service], sudo: with_sudo) - @command.run!("/bin/rm", args: ["-f", "--", service], sudo: with_sudo) + command.run!("/bin/launchctl", args: ["unload", "-w", "--", service], sudo: with_sudo) + command.run!("/bin/rm", args: ["-f", "--", service], sudo: with_sudo) sleep 1 end end end - def running_processes(bundle_id) - @command.run!("/bin/launchctl", args: ["list"]).stdout.lines - .map { |line| line.chomp.split("\t") } - .map { |pid, state, id| [pid.to_i, state.to_i, id] } - .select do |fields| - next if fields[0].zero? - fields[2] =~ /^#{Regexp.escape(bundle_id)}($|\.\d+)/ - end + def running_processes(bundle_id, command: nil) + command.run!("/bin/launchctl", args: ["list"]).stdout.lines + .map { |line| line.chomp.split("\t") } + .map { |pid, state, id| [pid.to_i, state.to_i, id] } + .select do |fields| + next if fields[0].zero? + fields[2] =~ /^#{Regexp.escape(bundle_id)}($|\.\d+)/ + end end # :quit/:signal must come before :kext so the kext will not be in use by a running process - def uninstall_quit(*bundle_ids) + def uninstall_quit(*bundle_ids, command: nil, **_) bundle_ids.each do |bundle_id| ohai "Quitting application ID #{bundle_id}" - next if running_processes(bundle_id).empty? - @command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{bundle_id}" to quit)], sudo: true) + next if running_processes(bundle_id, command: command).empty? + command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{bundle_id}" to quit)], sudo: true) begin Timeout.timeout(3) do Kernel.loop do - break if running_processes(bundle_id).empty? + break if running_processes(bundle_id, command: command).empty? end end rescue Timeout::Error @@ -111,15 +127,15 @@ module Hbc end # :signal should come after :quit so it can be used as a backup when :quit fails - def uninstall_signal(*signals) - signals.flatten.each_slice(2) do |pair| + def uninstall_signal(*signals, command: nil, **_) + signals.each do |pair| unless pair.size == 2 - raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must consist of 2 elements.") + raise CaskInvalidError.new(cask, "Each #{stanza} :signal must consist of 2 elements.") end signal, bundle_id = pair ohai "Signalling '#{signal}' to application ID '#{bundle_id}'" - pids = running_processes(bundle_id).map(&:first) + pids = running_processes(bundle_id, command: command).map(&:first) next unless pids.any? # Note that unlike :quit, signals are sent from the current user (not # upgraded to the superuser). This is a todo item for the future, but @@ -133,10 +149,10 @@ module Hbc end end - def uninstall_login_item(*login_items) + def uninstall_login_item(*login_items, command: nil, **_) login_items.each do |name| ohai "Removing login item #{name}" - @command.run!("/usr/bin/osascript", + command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application "System Events" to delete every login item whose name is "#{name}")], sudo: false) sleep 1 @@ -144,23 +160,24 @@ module Hbc end # :kext should be unloaded before attempting to delete the relevant file - def uninstall_kext(*kexts) + def uninstall_kext(*kexts, command: nil, **_) kexts.each do |kext| ohai "Unloading kernel extension #{kext}" - is_loaded = @command.run!("/usr/sbin/kextstat", args: ["-l", "-b", kext], sudo: true).stdout + is_loaded = command.run!("/usr/sbin/kextstat", args: ["-l", "-b", kext], sudo: true).stdout if is_loaded.length > 1 - @command.run!("/sbin/kextunload", args: ["-b", kext], sudo: true) + command.run!("/sbin/kextunload", args: ["-b", kext], sudo: true) sleep 1 end - @command.run!("/usr/sbin/kextfind", args: ["-b", kext], sudo: true).stdout.chomp.lines.each do |kext_path| + command.run!("/usr/sbin/kextfind", args: ["-b", kext], sudo: true).stdout.chomp.lines.each do |kext_path| ohai "Removing kernel extension #{kext_path}" - @command.run!("/bin/rm", args: ["-rf", kext_path], sudo: true) + command.run!("/bin/rm", args: ["-rf", kext_path], sudo: true) end end end # :script must come before :pkgutil, :delete, or :trash so that the script file is not already deleted - def uninstall_script(directives, directive_name: :script) + def uninstall_script(directives, directive_name: :script, force: false, command: nil, **_) + # TODO: Create a common `Script` class to run this and Artifact::Installer. executable, script_arguments = self.class.read_script_arguments(directives, "uninstall", { must_succeed: true, sudo: false }, @@ -168,25 +185,25 @@ module Hbc directive_name) ohai "Running uninstall script #{executable}" - raise CaskInvalidError.new(@cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil? - executable_path = @cask.staged_path.join(executable) + raise CaskInvalidError.new(cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil? + executable_path = cask.staged_path.join(executable) unless executable_path.exist? message = "uninstall script #{executable} does not exist" - raise CaskError, "#{message}." unless force? + raise CaskError, "#{message}." unless force opoo "#{message}, skipping." return end - @command.run("/bin/chmod", args: ["--", "+x", executable_path]) - @command.run(executable_path, script_arguments) + command.run("/bin/chmod", args: ["--", "+x", executable_path]) + command.run(executable_path, script_arguments) sleep 1 end - def uninstall_pkgutil(*pkgs) + def uninstall_pkgutil(*pkgs, command: nil, **_) ohai "Uninstalling packages:" pkgs.each do |regex| - Hbc::Pkg.all_matching(regex, @command).each do |pkg| + Hbc::Pkg.all_matching(regex, command).each do |pkg| puts pkg.package_id pkg.uninstall end @@ -215,28 +232,28 @@ module Hbc end end - def uninstall_delete(*paths) + def uninstall_delete(*paths, command: nil, **_) return if paths.empty? ohai "Removing files:" each_resolved_path(:delete, paths) do |path, resolved_paths| puts path - @command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "-r", "-f", "--"], input: resolved_paths.join("\0"), sudo: true) + command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "-r", "-f", "--"], input: resolved_paths.join("\0"), sudo: true) end end - def uninstall_trash(*paths) + def uninstall_trash(*paths, **options) return if paths.empty? resolved_paths = each_resolved_path(:trash, paths).to_a ohai "Trashing files:" puts resolved_paths.map(&:first) - trash_paths(*resolved_paths.flat_map(&:last)) + trash_paths(*resolved_paths.flat_map(&:last), **options) end - def trash_paths(*paths) - @command.run!("/usr/bin/osascript", args: ["-e", <<-'EOS'.undent, *paths]) + def trash_paths(*paths, command: nil, **_) + result = command.run!("/usr/bin/osascript", args: ["-e", <<-'EOS'.undent, *paths]) on run argv repeat with i from 1 to (count argv) set item i of argv to (item i of argv as POSIX file) @@ -250,7 +267,7 @@ module Hbc set trashedItem to POSIX path of (item i of trashedItems as string) set output to output & trashedItem if i < count trashedItems then - set output to output & (do shell script "printf \"\\0\"") + set output to output & character id 0 end if end repeat @@ -258,9 +275,12 @@ module Hbc end tell end run EOS + + # Remove AppleScript's automatic newline. + result.tap { |r| r.stdout.sub!(/\n$/, "") } end - def uninstall_rmdir(*directories) + def uninstall_rmdir(*directories, command: nil, **_) return if directories.empty? ohai "Removing directories if empty:" @@ -268,10 +288,10 @@ module Hbc puts path resolved_paths.select(&:directory?).each do |resolved_path| if (ds_store = resolved_path.join(".DS_Store")).exist? - @command.run!("/bin/rm", args: ["-f", "--", ds_store], sudo: true, print_stderr: false) + command.run!("/bin/rm", args: ["-f", "--", ds_store], sudo: true, print_stderr: false) end - @command.run("/bin/rmdir", args: ["--", resolved_path], sudo: true, print_stderr: false) + command.run("/bin/rmdir", args: ["--", resolved_path], sudo: true, print_stderr: false) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/artifact.rb b/Library/Homebrew/cask/lib/hbc/artifact/artifact.rb index b42db877d0..0f37afaded 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/artifact.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/artifact.rb @@ -5,21 +5,32 @@ require "hbc/utils/hash_validator" module Hbc module Artifact class Artifact < Moved - def self.artifact_english_name + def self.english_name "Generic Artifact" end - def self.artifact_dirmethod - :appdir + def self.from_args(cask, *args) + source_string, target_hash = args + + if source_string.nil? + raise CaskInvalidError.new(cask.token, "no source given for #{english_name}") + end + + unless target_hash.is_a?(Hash) + raise CaskInvalidError.new(cask.token, "target required for #{english_name} '#{source_string}'") + end + + target_hash.extend(HashValidator).assert_valid_keys(:target) + + new(cask, source_string, **target_hash) end - def load_specification(artifact_spec) - source_string, target_hash = artifact_spec - raise CaskInvalidError.new(@cask.token, "no source given for artifact") if source_string.nil? - @source = @cask.staged_path.join(source_string) - raise CaskInvalidError.new(@cask.token, "target required for generic artifact #{source_string}") unless target_hash.is_a?(Hash) - target_hash.extend(HashValidator).assert_valid_keys(:target) - @target = Pathname.new(target_hash[:target]) + def self.resolve_target(target) + Pathname(target) + end + + def initialize(cask, source, target: nil) + super(cask, source, target: target) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/base.rb b/Library/Homebrew/cask/lib/hbc/artifact/base.rb deleted file mode 100644 index ae15552a4e..0000000000 --- a/Library/Homebrew/cask/lib/hbc/artifact/base.rb +++ /dev/null @@ -1,80 +0,0 @@ -module Hbc - module Artifact - class Base - extend Predicable - - def self.artifact_name - @artifact_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase - end - - def self.artifact_english_name - @artifact_english_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2') - end - - def self.artifact_english_article - @artifact_english_article ||= (artifact_english_name =~ /^[aeiou]/i) ? "an" : "a" - end - - def self.artifact_dsl_key - @artifact_dsl_key ||= artifact_name.to_sym - end - - def self.artifact_dirmethod - @artifact_dirmethod ||= "#{artifact_name}dir".to_sym - end - - def self.me?(cask) - cask.artifacts[artifact_dsl_key].any? - end - - attr_reader :force - - # TODO: this sort of logic would make more sense in dsl.rb, or a - # constructor called from dsl.rb, so long as that isn't slow. - def self.read_script_arguments(arguments, stanza, default_arguments = {}, override_arguments = {}, key = nil) - # TODO: when stanza names are harmonized with class names, - # stanza may not be needed as an explicit argument - description = key ? "#{stanza} #{key.inspect}" : stanza.to_s - - # backward-compatible string value - arguments = { executable: arguments } if arguments.is_a?(String) - - # key sanity - permitted_keys = [:args, :input, :executable, :must_succeed, :sudo, :print_stdout, :print_stderr] - unknown_keys = arguments.keys - permitted_keys - unless unknown_keys.empty? - opoo %Q{Unknown arguments to #{description} -- #{unknown_keys.inspect} (ignored). Running "brew update; brew cleanup; brew cask cleanup" will likely fix it.} - end - arguments.select! { |k| permitted_keys.include?(k) } - - # key warnings - override_keys = override_arguments.keys - ignored_keys = arguments.keys & override_keys - unless ignored_keys.empty? - onoe "Some arguments to #{description} will be ignored -- :#{unknown_keys.inspect} (overridden)." - end - - # extract executable - executable = arguments.key?(:executable) ? arguments.delete(:executable) : nil - - arguments = default_arguments.merge arguments - arguments.merge! override_arguments - - [executable, arguments] - end - - def summary - {} - end - - attr_predicate :force?, :verbose? - - def initialize(cask, command: SystemCommand, force: false, verbose: false) - @cask = cask - @command = command - @force = force - @verbose = verbose - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb index 7178c2af69..68f4b074da 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb @@ -3,13 +3,13 @@ require "hbc/artifact/symlinked" module Hbc module Artifact class Binary < Symlinked - def link - super + def link(command: nil, **options) + super(command: command, **options) return if source.executable? if source.writable? FileUtils.chmod "+x", source else - @command.run!("/bin/chmod", args: ["+x", source], sudo: true) + command.run!("/bin/chmod", args: ["+x", source], sudo: true) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/installer.rb b/Library/Homebrew/cask/lib/hbc/artifact/installer.rb index be857696ec..588bcabd54 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/installer.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/installer.rb @@ -1,29 +1,82 @@ -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" module Hbc module Artifact - class Installer < Base - def install_phase - @cask.artifacts[self.class.artifact_dsl_key].each do |artifact| - if artifact.manual - puts <<-EOS.undent - To complete the installation of Cask #{@cask}, you must also - run the installer at + class Installer < AbstractArtifact + VALID_KEYS = Set.new [ + :manual, + :script, + ] - '#{@cask.staged_path.join(artifact.manual)}' + module ManualInstaller + def install_phase(**) + puts <<-EOS.undent + To complete the installation of Cask #{cask}, you must also + run the installer at - EOS - else - executable, script_arguments = self.class.read_script_arguments(artifact.script, - self.class.artifact_dsl_key.to_s, - { must_succeed: true, sudo: false }, - print_stdout: true) - ohai "Running #{self.class.artifact_dsl_key} script #{executable}" - raise CaskInvalidError.new(@cask, "#{self.class.artifact_dsl_key} missing executable") if executable.nil? - executable_path = @cask.staged_path.join(executable) - @command.run("/bin/chmod", args: ["--", "+x", executable_path]) if File.exist?(executable_path) - @command.run(executable_path, script_arguments) + '#{path}' + EOS + end + end + + module ScriptInstaller + def install_phase(command: nil, **_) + ohai "Running #{self.class.dsl_key} script '#{path.relative_path_from(cask.staged_path)}'" + FileUtils.chmod "+x", path unless path.executable? + command.run(path, **args) + end + end + + def self.from_args(cask, **args) + raise CaskInvalidError.new(cask, "'installer' stanza requires an argument.") if args.empty? + + if args.key?(:script) && !args[:script].respond_to?(:key?) + if args.key?(:executable) + raise CaskInvalidError.new(cask, "'installer' stanza gave arguments for both :script and :executable.") end + + args[:executable] = args[:script] + args.delete(:script) + args = { script: args } + end + + unless args.keys.count == 1 + raise CaskInvalidError.new(cask, "invalid 'installer' stanza: Only one of #{VALID_KEYS.inspect} is permitted.") + end + + args.extend(HashValidator).assert_valid_keys(*VALID_KEYS) + new(cask, **args) + end + + attr_reader :path, :args + + def initialize(cask, **args) + super(cask) + + if args.key?(:manual) + @path = cask.staged_path.join(args[:manual]) + @args = [] + extend(ManualInstaller) + return + end + + path, @args = self.class.read_script_arguments( + args[:script], self.class.dsl_key.to_s, { must_succeed: true, sudo: false }, print_stdout: true + ) + raise CaskInvalidError.new(cask, "#{self.class.dsl_key} missing executable") if path.nil? + + path = Pathname(path) + @path = path.absolute? ? path : cask.staged_path.join(path) + extend(ScriptInstaller) + end + + def summarize + path.relative_path_from(cask.staged_path).to_s + end + + def to_h + { path: path.relative_path_from(cask.staged_path).to_s }.tap do |h| + h[:args] = args unless is_a?(ManualInstaller) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb index 3fe969c0c2..ba1c8e9072 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb @@ -4,63 +4,61 @@ module Hbc module Artifact class Moved < Relocated def self.english_description - "#{artifact_english_name}s" + "#{english_name}s" end - def install_phase - each_artifact(&method(:move)) + def install_phase(**options) + move(**options) end - def uninstall_phase - each_artifact(&method(:delete)) + def uninstall_phase(**options) + delete(**options) + end + + def summarize_installed + if target.exist? + "#{printable_target} (#{target.abv})" + else + Formatter.error(printable_target, label: "Missing #{self.class.english_name}") + end end private - def move + def move(force: false, command: nil, **options) if Utils.path_occupied?(target) - message = "It seems there is already #{self.class.artifact_english_article} #{self.class.artifact_english_name} at '#{target}'" - raise CaskError, "#{message}." unless force? + message = "It seems there is already #{self.class.english_article} #{self.class.english_name} at '#{target}'" + raise CaskError, "#{message}." unless force opoo "#{message}; overwriting." - delete + delete(force: force, command: command, **options) end unless source.exist? - raise CaskError, "It seems the #{self.class.artifact_english_name} source '#{source}' is not there." + raise CaskError, "It seems the #{self.class.english_name} source '#{source}' is not there." end - ohai "Moving #{self.class.artifact_english_name} '#{source.basename}' to '#{target}'." + ohai "Moving #{self.class.english_name} '#{source.basename}' to '#{target}'." target.dirname.mkpath if target.parent.writable? FileUtils.move(source, target) else - SystemCommand.run("/bin/mv", args: [source, target], sudo: true) + command.run("/bin/mv", args: [source, target], sudo: true) end - add_altname_metadata target, source.basename.to_s + add_altname_metadata(target, source.basename, command: command) end - def delete - ohai "Removing #{self.class.artifact_english_name} '#{target}'." - raise CaskError, "Cannot remove undeletable #{self.class.artifact_english_name}." if MacOS.undeletable?(target) + def delete(force: false, command: nil, **_) + ohai "Removing #{self.class.english_name} '#{target}'." + raise CaskError, "Cannot remove undeletable #{self.class.english_name}." if MacOS.undeletable?(target) return unless Utils.path_occupied?(target) if target.parent.writable? && !force target.rmtree else - Utils.gain_permissions_remove(target, command: @command) - end - end - - def summarize_artifact(artifact_spec) - load_specification artifact_spec - - if target.exist? - "#{printable_target} (#{target.abv})" - else - Formatter.error(printable_target, label: "Missing #{self.class.artifact_english_name}") + Utils.gain_permissions_remove(target, command: command) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/nested_container.rb b/Library/Homebrew/cask/lib/hbc/artifact/nested_container.rb index 84253ea308..c9fd3dc271 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/nested_container.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/nested_container.rb @@ -1,23 +1,35 @@ -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" module Hbc module Artifact - class NestedContainer < Base - def install_phase - @cask.artifacts[:nested_container].each { |container| extract(container) } + class NestedContainer < AbstractArtifact + attr_reader :path + + def initialize(cask, path) + super(cask) + @path = cask.staged_path.join(path) end - def extract(container_relative_path) - source = @cask.staged_path.join(container_relative_path) - container = Container.for_path(source, @command) + def install_phase(**options) + extract(**options) + end + + private + + def summarize + path.relative_path_from(cask.staged_path).to_s + end + + def extract(command: nil, verbose: nil, **_) + container = Container.for_path(path, command) unless container raise CaskError, "Aw dang, could not identify nested container at '#{source}'" end - ohai "Extracting nested container #{source.basename}" - container.new(@cask, source, @command, verbose: verbose?).extract - FileUtils.remove_entry_secure(source) + ohai "Extracting nested container #{path.relative_path_from(cask.staged_path)}" + container.new(cask, path, command, verbose: verbose).extract + FileUtils.remove_entry_secure(path) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb b/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb index be0a6be717..b4bdf3de66 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb @@ -1,4 +1,4 @@ -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" require "hbc/utils/hash_validator" @@ -6,62 +6,61 @@ require "vendor/plist/plist" module Hbc module Artifact - class Pkg < Base + class Pkg < AbstractArtifact attr_reader :pkg_relative_path - def self.artifact_dsl_key - :pkg + def self.from_args(cask, path, **stanza_options) + stanza_options.extend(HashValidator).assert_valid_keys( + :allow_untrusted, :choices + ) + new(cask, path, **stanza_options) end - def load_pkg_description(pkg_description) - @pkg_relative_path = pkg_description.shift - @pkg_install_opts = pkg_description.shift - begin - if @pkg_install_opts.respond_to?(:keys) - @pkg_install_opts.extend(HashValidator).assert_valid_keys(:allow_untrusted, :choices) - elsif @pkg_install_opts - raise - end - raise if pkg_description.nil? - rescue StandardError - raise CaskInvalidError.new(@cask, "Bad pkg stanza") - end + attr_reader :path, :stanza_options + + def initialize(cask, path, **stanza_options) + super(cask) + @path = cask.staged_path.join(path) + @stanza_options = stanza_options end - def pkg_install_opts(opt) - @pkg_install_opts[opt] if @pkg_install_opts.respond_to?(:keys) + def summarize + path.relative_path_from(cask.staged_path).to_s end - def install_phase - @cask.artifacts[:pkg].each { |pkg_description| run_installer(pkg_description) } + def install_phase(**options) + run_installer(**options) end - def run_installer(pkg_description) - load_pkg_description pkg_description - ohai "Running installer for #{@cask}; your password may be necessary." + private + + def run_installer(command: nil, verbose: false, **_options) + ohai "Running installer for #{cask}; your password may be necessary." ohai "Package installers may write to any location; options such as --appdir are ignored." - source = @cask.staged_path.join(pkg_relative_path) - unless source.exist? - raise CaskError, "pkg source file not found: '#{source}'" + unless path.exist? + raise CaskError, "pkg source file not found: '#{path.relative_path_from(cask.staged_path)}'" end args = [ - "-pkg", source, + "-pkg", path, "-target", "/" ] - args << "-verboseR" if verbose? - args << "-allowUntrusted" if pkg_install_opts :allow_untrusted + args << "-verboseR" if verbose + if stanza_options.fetch(:allow_untrusted, false) + args << "-allowUntrusted" + end with_choices_file do |choices_path| args << "-applyChoiceChangesXML" << choices_path if choices_path - @command.run!("/usr/sbin/installer", sudo: true, args: args, print_stdout: true) + command.run!("/usr/sbin/installer", sudo: true, args: args, print_stdout: true) end end def with_choices_file - return yield nil unless pkg_install_opts(:choices) + choices = stanza_options.fetch(:choices, {}) + return yield nil if choices.empty? Tempfile.open(["choices", ".xml"]) do |file| begin - file.write Plist::Emit.dump(pkg_install_opts(:choices)) + file.write Plist::Emit.dump(choices) file.close yield file.path ensure diff --git a/Library/Homebrew/cask/lib/hbc/artifact/prefpane.rb b/Library/Homebrew/cask/lib/hbc/artifact/prefpane.rb index a44f8ae3a3..87f120934d 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/prefpane.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/prefpane.rb @@ -3,7 +3,7 @@ require "hbc/artifact/moved" module Hbc module Artifact class Prefpane < Moved - def self.artifact_english_name + def self.english_name "Preference Pane" end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/qlplugin.rb b/Library/Homebrew/cask/lib/hbc/artifact/qlplugin.rb index ee41de2fe2..298714d894 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/qlplugin.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/qlplugin.rb @@ -3,22 +3,24 @@ require "hbc/artifact/moved" module Hbc module Artifact class Qlplugin < Moved - def self.artifact_english_name + def self.english_name "QuickLook Plugin" end - def install_phase - super - reload_quicklook + def install_phase(**options) + super(**options) + reload_quicklook(**options) end - def uninstall_phase - super - reload_quicklook + def uninstall_phase(**options) + super(**options) + reload_quicklook(**options) end - def reload_quicklook - @command.run!("/usr/bin/qlmanage", args: ["-r"]) + private + + def reload_quicklook(command: nil, **_) + command.run!("/usr/bin/qlmanage", args: ["-r"]) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb b/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb index 4dba46c9d8..540699630e 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb @@ -1,65 +1,79 @@ -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" require "hbc/utils/hash_validator" module Hbc module Artifact - class Relocated < Base - def summary - { - english_description: self.class.english_description, - contents: @cask.artifacts[self.class.artifact_dsl_key].map(&method(:summarize_artifact)).compact, - } + class Relocated < AbstractArtifact + def self.from_args(cask, *args) + source_string, target_hash = args + + if target_hash + raise CaskInvalidError unless target_hash.respond_to?(:keys) + target_hash.extend(HashValidator).assert_valid_keys(:target) + end + + target_hash ||= {} + + new(cask, source_string, **target_hash) + end + + def self.resolve_target(target) + Hbc.public_send(dirmethod).join(target) end attr_reader :source, :target - def printable_target - target.to_s.sub(/^#{ENV['HOME']}(#{File::SEPARATOR}|$)/, "~/") + def initialize(cask, source, target: nil) + super(cask) + + @source_string = source.to_s + @target_string = target.to_s + source = cask.staged_path.join(source) + @source = source + target ||= source.basename + @target = self.class.resolve_target(target) end + def to_a + [@source_string].tap do |ary| + ary << { target: @target_string } unless @target_string.empty? + end + end + + def summarize + target_string = @target_string.empty? ? "" : " -> #{@target_string}" + "#{@source_string}#{target_string}" + end + + private + ALT_NAME_ATTRIBUTE = "com.apple.metadata:kMDItemAlternateNames".freeze # Try to make the asset searchable under the target name. Spotlight # respects this attribute for many filetypes, but ignores it for App # bundles. Alfred 2.2 respects it even for App bundles. - def add_altname_metadata(file, altname) - return if altname.casecmp(file.basename).zero? + def add_altname_metadata(file, altname, command: nil) + return if altname.to_s.casecmp(file.basename.to_s).zero? odebug "Adding #{ALT_NAME_ATTRIBUTE} metadata" - altnames = @command.run("/usr/bin/xattr", - args: ["-p", ALT_NAME_ATTRIBUTE, file.to_s], + altnames = command.run("/usr/bin/xattr", + args: ["-p", ALT_NAME_ATTRIBUTE, file], print_stderr: false).stdout.sub(/\A\((.*)\)\Z/, '\1') odebug "Existing metadata is: '#{altnames}'" altnames.concat(", ") unless altnames.empty? altnames.concat(%Q("#{altname}")) altnames = "(#{altnames})" - # Some packges are shipped as u=rx (e.g. Bitcoin Core) - @command.run!("/bin/chmod", args: ["--", "u+rw", file, file.realpath]) + # Some packages are shipped as u=rx (e.g. Bitcoin Core) + command.run!("/bin/chmod", args: ["--", "u+rw", file, file.realpath]) - @command.run!("/usr/bin/xattr", + command.run!("/usr/bin/xattr", args: ["-w", ALT_NAME_ATTRIBUTE, altnames, file], print_stderr: false) end - def each_artifact - @cask.artifacts[self.class.artifact_dsl_key].each do |artifact| - load_specification(artifact) - yield - end - end - - def load_specification(artifact_spec) - source_string, target_hash = artifact_spec - raise CaskInvalidError if source_string.nil? - @source = @cask.staged_path.join(source_string) - if target_hash - raise CaskInvalidError unless target_hash.respond_to?(:keys) - target_hash.extend(HashValidator).assert_valid_keys(:target) - @target = Hbc.send(self.class.artifact_dirmethod).join(target_hash[:target]) - else - @target = Hbc.send(self.class.artifact_dirmethod).join(source.basename) - end + def printable_target + target.to_s.sub(/^#{ENV['HOME']}(#{File::SEPARATOR}|$)/, "~/") end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/stage_only.rb b/Library/Homebrew/cask/lib/hbc/artifact/stage_only.rb index 1122c1d02d..8c32a52d08 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/stage_only.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/stage_only.rb @@ -1,10 +1,22 @@ -require "hbc/artifact/base" +require "hbc/artifact/abstract_artifact" module Hbc module Artifact - class StageOnly < Base - def self.artifact_dsl_key - :stage_only + class StageOnly < AbstractArtifact + def self.from_args(cask, *args) + if args != [true] + raise CaskInvalidError.new(cask.token, "'stage_only' takes only a single argument: true") + end + + new(cask) + end + + def initialize(cask) + super(cask) + end + + def to_a + [true] end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/suite.rb b/Library/Homebrew/cask/lib/hbc/artifact/suite.rb index 35251f70c1..59ae58cf13 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/suite.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/suite.rb @@ -3,11 +3,11 @@ require "hbc/artifact/moved" module Hbc module Artifact class Suite < Moved - def self.artifact_english_name + def self.english_name "App Suite" end - def self.artifact_dirmethod + def self.dirmethod :appdir end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb b/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb index 16715fe81a..3726ebb5c5 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/symlinked.rb @@ -8,47 +8,18 @@ module Hbc end def self.english_description - "#{artifact_english_name} #{link_type_english_name}s" + "#{english_name} #{link_type_english_name}s" end - def install_phase - each_artifact(&method(:link)) + def install_phase(**options) + link(**options) end - def uninstall_phase - each_artifact(&method(:unlink)) + def uninstall_phase(**options) + unlink(**options) end - private - - def link - unless source.exist? - raise CaskError, "It seems the #{self.class.link_type_english_name.downcase} source '#{source}' is not there." - end - - if target.exist? && !target.symlink? - raise CaskError, "It seems there is already #{self.class.artifact_english_article} #{self.class.artifact_english_name} at '#{target}'; not linking." - end - - ohai "Linking #{self.class.artifact_english_name} '#{source.basename}' to '#{target}'." - create_filesystem_link(source, target) - end - - def unlink - return unless target.symlink? - ohai "Unlinking #{self.class.artifact_english_name} '#{target}'." - target.delete - end - - def create_filesystem_link(source, target) - target.dirname.mkpath - @command.run!("/bin/ln", args: ["-h", "-f", "-s", "--", source, target]) - add_altname_metadata source, target.basename.to_s - end - - def summarize_artifact(artifact_spec) - load_specification artifact_spec - + def summarize_installed if target.symlink? && target.exist? && target.readlink.exist? "#{printable_target} -> #{target.readlink} (#{target.readlink.abv})" else @@ -61,6 +32,33 @@ module Hbc Formatter.error(string, label: "Broken Link") end end + + private + + def link(**options) + unless source.exist? + raise CaskError, "It seems the #{self.class.link_type_english_name.downcase} source '#{source}' is not there." + end + + if target.exist? && !target.symlink? + raise CaskError, "It seems there is already #{self.class.english_article} #{self.class.english_name} at '#{target}'; not linking." + end + + ohai "Linking #{self.class.english_name} '#{source.basename}' to '#{target}'." + create_filesystem_link(**options) + end + + def unlink(**) + return unless target.symlink? + ohai "Unlinking #{self.class.english_name} '#{target}'." + target.delete + end + + def create_filesystem_link(command: nil, **_) + target.dirname.mkpath + command.run!("/bin/ln", args: ["-h", "-f", "-s", "--", source, target]) + add_altname_metadata(source, target.basename, command: command) + end end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/uninstall.rb b/Library/Homebrew/cask/lib/hbc/artifact/uninstall.rb index 5a3dc098d9..2bbf82862a 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/uninstall.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/uninstall.rb @@ -1,10 +1,10 @@ -require "hbc/artifact/uninstall_base" +require "hbc/artifact/abstract_uninstall" module Hbc module Artifact - class Uninstall < UninstallBase - def uninstall_phase - dispatch_uninstall_directives + class Uninstall < AbstractUninstall + def uninstall_phase(**options) + dispatch_uninstall_directives(**options) end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/zap.rb b/Library/Homebrew/cask/lib/hbc/artifact/zap.rb index cdfe2531da..31ff54d201 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/zap.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/zap.rb @@ -1,10 +1,10 @@ -require "hbc/artifact/uninstall_base" +require "hbc/artifact/abstract_uninstall" module Hbc module Artifact - class Zap < UninstallBase - def zap_phase - dispatch_uninstall_directives + class Zap < AbstractUninstall + def zap_phase(**options) + dispatch_uninstall_directives(**options) end end end diff --git a/Library/Homebrew/cask/lib/hbc/audit.rb b/Library/Homebrew/cask/lib/hbc/audit.rb index cee1fe8070..bd25477aca 100644 --- a/Library/Homebrew/cask/lib/hbc/audit.rb +++ b/Library/Homebrew/cask/lib/hbc/audit.rb @@ -70,12 +70,16 @@ module Hbc previous_cask_contents = Git.last_revision_of_file(tap.path, @cask.sourcefile_path, before_commit: commit_range) return if previous_cask_contents.empty? - previous_cask = CaskLoader.load_from_string(previous_cask_contents) + begin + previous_cask = CaskLoader.load(previous_cask_contents) - return unless previous_cask.version == cask.version - return if previous_cask.sha256 == cask.sha256 + return unless previous_cask.version == cask.version + return if previous_cask.sha256 == cask.sha256 - add_error "only sha256 changed (see: https://github.com/caskroom/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/sha256.md)" + add_error "only sha256 changed (see: https://github.com/caskroom/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/sha256.md)" + rescue CaskError => e + add_warning "Skipped version and checksum comparison. Reading previous version failed: #{e}" + end end def check_version @@ -143,7 +147,15 @@ module Hbc def check_appcast_http_code odebug "Verifying appcast returns 200 HTTP response code" - result = @command.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--output", "/dev/null", "--write-out", "%{http_code}", cask.appcast], print_stderr: false) + + curl_executable, *args = curl_args( + "--compressed", "--location", "--fail", + "--write-out", "%{http_code}", + "--output", "/dev/null", + cask.appcast, + user_agent: :fake + ) + result = @command.run(curl_executable, args: args, print_stderr: false) if result.success? http_code = result.stdout.chomp add_warning "unexpected HTTP response code retrieving appcast: #{http_code}" unless http_code == "200" @@ -206,12 +218,10 @@ module Hbc end def check_generic_artifacts - cask.artifacts[:artifact].each do |source, target_hash| - unless target_hash.is_a?(Hash) && target_hash[:target] - add_error "target required for generic artifact #{source}" - next + cask.artifacts.select { |a| a.is_a?(Hbc::Artifact::Artifact) }.each do |artifact| + unless artifact.target.absolute? + add_error "target must be absolute path for #{artifact.class.english_name} #{artifact.source}" end - add_error "target must be absolute path for generic artifact #{source}" unless Pathname.new(target_hash[:target]).absolute? end end diff --git a/Library/Homebrew/cask/lib/hbc/auditor.rb b/Library/Homebrew/cask/lib/hbc/auditor.rb index 48f36a54d5..231c005f9b 100644 --- a/Library/Homebrew/cask/lib/hbc/auditor.rb +++ b/Library/Homebrew/cask/lib/hbc/auditor.rb @@ -43,7 +43,7 @@ module Hbc def audit_languages(languages) ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.join(", ")}" MacOS.instance_variable_set(:@languages, languages) - audit_cask_instance(CaskLoader.load_from_file(cask.sourcefile_path)) + audit_cask_instance(CaskLoader.load(cask.sourcefile_path)) ensure CLI::Cleanup.run(cask.token) if audit_download? end diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index 6d89a997cc..72a23066f8 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -17,7 +17,7 @@ module Hbc @token = token @sourcefile_path = sourcefile_path @tap = tap - @dsl = DSL.new(@token) + @dsl = DSL.new(self) return unless block_given? @dsl.instance_eval(&block) @dsl.language_eval @@ -41,6 +41,14 @@ module Hbc .reverse end + def full_name + if @tap.nil? || @tap == Hbc.default_tap + token + else + "#{@tap}/#{token}" + end + end + def installed? !versions.empty? end diff --git a/Library/Homebrew/cask/lib/hbc/cask_loader.rb b/Library/Homebrew/cask/lib/hbc/cask_loader.rb index 5003146719..08d4576439 100644 --- a/Library/Homebrew/cask/lib/hbc/cask_loader.rb +++ b/Library/Homebrew/cask/lib/hbc/cask_loader.rb @@ -3,6 +3,18 @@ module Hbc class FromContentLoader attr_reader :content + def self.can_load?(ref) + return false unless ref.respond_to?(:to_str) + content = ref.to_str + + token = /(?:"[^"]*"|'[^']*')/ + curly = /\(\s*#{token}\s*\)\s*\{.*\}/ + do_end = /\s+#{token}\s+do(?:\s*;\s*|\s+).*end/ + regex = /\A\s*cask(?:#{curly.source}|#{do_end.source})\s*\Z/m + + content.match?(regex) + end + def initialize(content) @content = content end @@ -56,7 +68,8 @@ module Hbc class FromURILoader < FromPathLoader def self.can_load?(ref) - ref.to_s.match?(::URI.regexp) + uri_regex = ::URI::DEFAULT_PARSER.make_regexp + ref.to_s.match?(Regexp.new('\A' + uri_regex.source + '\Z', uri_regex.options)) end attr_reader :url @@ -71,7 +84,7 @@ module Hbc begin ohai "Downloading #{url}." - curl url, "-o", path + curl_download url, to: path rescue ErrorDuringExecution raise CaskUnavailableError.new(token, "Failed to download #{Formatter.url(url)}.") end @@ -116,6 +129,22 @@ module Hbc end end + class FromInstanceLoader + attr_reader :cask + + def self.can_load?(ref) + ref.is_a?(Cask) + end + + def initialize(cask) + @cask = cask + end + + def load + cask + end + end + class NullLoader < FromPathLoader def self.can_load?(*) true @@ -131,14 +160,6 @@ module Hbc end end - def self.load_from_file(path) - FromPathLoader.new(path).load - end - - def self.load_from_string(content) - FromContentLoader.new(content).load - end - def self.path(ref) self.for(ref).path end @@ -149,6 +170,8 @@ module Hbc def self.for(ref) [ + FromInstanceLoader, + FromContentLoader, FromURILoader, FromTapLoader, FromTapPathLoader, diff --git a/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb b/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb index 77f85301eb..001a9623b2 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb @@ -42,41 +42,32 @@ module Hbc @args = process_arguments(*args) end - def self.warn_unavailable_with_suggestion(cask_token, e) - exact_match, partial_matches = Search.search(cask_token) - error_message = e.message - if exact_match - error_message.concat(" Did you mean:\n#{exact_match}") - elsif !partial_matches.empty? - error_message.concat(" Did you mean one of:\n") - .concat(Formatter.columns(partial_matches.take(20))) - end - onoe error_message - end - private def casks(alternative: -> { [] }) - return to_enum(:casks, alternative: alternative) unless block_given? - - count = 0 - + return @casks if defined?(@casks) casks = args.empty? ? alternative.call : args + @casks = casks.map { |cask| CaskLoader.load(cask) } + rescue CaskUnavailableError => e + reason = [e.reason, suggestion_message(e.token)].join(" ") + raise e.class.new(e.token, reason) + end - casks.each do |cask_or_token| - begin - yield cask_or_token.respond_to?(:token) ? cask_or_token : CaskLoader.load(cask_or_token) - count += 1 - rescue CaskUnavailableError => e - cask_token = cask_or_token - self.class.warn_unavailable_with_suggestion cask_token, e - rescue CaskError => e - onoe e.message - end + def suggestion_message(cask_token) + exact_match, partial_matches = Search.search(cask_token) + + if exact_match.nil? && partial_matches.count == 1 + exact_match = partial_matches.first end - return :empty if casks.length.zero? - (count == casks.length) ? :complete : :incomplete + if exact_match + "Did you mean “#{exact_match}”?" + elsif !partial_matches.empty? + "Did you mean one of these?\n" + .concat(Formatter.columns(partial_matches.take(20))) + else + "" + end end end end diff --git a/Library/Homebrew/cask/lib/hbc/cli/cat.rb b/Library/Homebrew/cask/lib/hbc/cli/cat.rb index d08c87bea2..0430805561 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/cat.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/cat.rb @@ -7,10 +7,6 @@ module Hbc end def run - raise CaskError, "Cat incomplete." if cat_casks == :incomplete - end - - def cat_casks casks.each do |cask| puts File.open(cask.sourcefile_path, &:read) end diff --git a/Library/Homebrew/cask/lib/hbc/cli/edit.rb b/Library/Homebrew/cask/lib/hbc/cli/edit.rb index b9485886c9..8bce81c523 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/edit.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/edit.rb @@ -4,21 +4,18 @@ module Hbc def initialize(*) super raise CaskUnspecifiedError if args.empty? - raise ArgumentError, "Only one Cask can be created at a time." if args.count > 1 + raise ArgumentError, "Only one Cask can be edited at a time." if args.count > 1 end def run - cask_token = args.first - cask_path = begin - CaskLoader.load(cask_token).sourcefile_path - rescue CaskUnavailableError => e - reason = e.reason.empty? ? "" : "#{e.reason} " - reason.concat("Run #{Formatter.identifier("brew cask create #{e.token}")} to create a new Cask.") - raise e.class.new(e.token, reason) - end - - odebug "Opening editor for Cask #{cask_token}" + cask = casks.first + cask_path = cask.sourcefile_path + odebug "Opening editor for Cask #{cask.token}" exec_editor cask_path + rescue CaskUnavailableError => e + reason = e.reason.empty? ? "" : "#{e.reason} " + reason.concat("Run #{Formatter.identifier("brew cask create #{e.token}")} to create a new Cask.") + raise e.class.new(e.token, reason) end def self.help diff --git a/Library/Homebrew/cask/lib/hbc/cli/fetch.rb b/Library/Homebrew/cask/lib/hbc/cli/fetch.rb index e31b1a17c1..12c794f5f6 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/fetch.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/fetch.rb @@ -9,10 +9,6 @@ module Hbc end def run - raise CaskError, "Fetch incomplete." if fetch_casks == :incomplete - end - - def fetch_casks casks.each do |cask| ohai "Downloading external files for Cask #{cask}" downloaded_path = Download.new(cask, force: force?).perform diff --git a/Library/Homebrew/cask/lib/hbc/cli/info.rb b/Library/Homebrew/cask/lib/hbc/cli/info.rb index d26747e176..f12fe55648 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/info.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/info.rb @@ -23,6 +23,7 @@ module Hbc installation_info(cask) repo_info(cask) name_info(cask) + language_info(cask) artifact_info(cask) Installer.print_caveats(cask) end @@ -51,6 +52,13 @@ module Hbc puts cask.name.empty? ? Formatter.error("None") : cask.name end + def self.language_info(cask) + return if cask.languages.empty? + + ohai "Languages" + puts cask.languages.join(", ") + end + def self.repo_info(cask) user, repo, token = QualifiedToken.parse(Hbc.all_tokens.detect { |t| t.split("/").last == cask.token }) @@ -69,12 +77,10 @@ module Hbc def self.artifact_info(cask) ohai "Artifacts" - DSL::ORDINARY_ARTIFACT_TYPES.each do |type| - next if cask.artifacts[type].empty? - cask.artifacts[type].each do |artifact| - activatable_item = (type == :stage_only) ? "" : artifact.first - puts "#{activatable_item} (#{type})" - end + cask.artifacts.each do |artifact| + next unless artifact.respond_to?(:install_phase) + next unless DSL::ORDINARY_ARTIFACT_CLASSES.include?(artifact.class) + puts artifact.to_s end end end diff --git a/Library/Homebrew/cask/lib/hbc/cli/install.rb b/Library/Homebrew/cask/lib/hbc/cli/install.rb index 0f1a5dd34c..9a2116e6ac 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/install.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/install.rb @@ -10,10 +10,6 @@ module Hbc end def run - raise CaskError, "Install incomplete." if install_casks == :incomplete - end - - def install_casks casks.each do |cask| begin Installer.new(cask, binaries: binaries?, diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_appcast_checkpoint.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_appcast_checkpoint.rb index cd26797821..a538ffd8c3 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_appcast_checkpoint.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_appcast_checkpoint.rb @@ -12,7 +12,7 @@ module Hbc if args.all? { |t| t =~ %r{^https?://} && t !~ /\.rb$/ } self.class.appcask_checkpoint_for_url(args) else - self.class.appcask_checkpoint(args, calculate?) + self.class.appcask_checkpoint(casks, calculate?) end end @@ -23,33 +23,27 @@ module Hbc end end - def self.appcask_checkpoint(cask_tokens, calculate) - count = 0 - - cask_tokens.each do |cask_token| - cask = CaskLoader.load(cask_token) - + def self.appcask_checkpoint(casks, calculate) + casks.each do |cask| if cask.appcast.nil? opoo "Cask '#{cask}' is missing an `appcast` stanza." else - if calculate + checkpoint = if calculate result = cask.appcast.calculate_checkpoint - - checkpoint = result[:checkpoint] + result[:checkpoint] else - checkpoint = cask.appcast.checkpoint + cask.appcast.checkpoint end - if checkpoint.nil? + if calculate && checkpoint.nil? onoe "Could not retrieve `appcast` checkpoint for cask '#{cask}': #{result[:command_result].stderr}" + elsif casks.count > 1 + puts "#{checkpoint} #{cask}" else - puts((cask_tokens.count > 1) ? "#{checkpoint} #{cask}" : checkpoint) - count += 1 + puts checkpoint end end end - - count == cask_tokens.count end def self.help diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb index 3673a5391a..b83224fb1e 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_audit_modified_casks.rb @@ -59,7 +59,7 @@ module Hbc end def modified_cask_files - @modified_cask_files ||= git_filter_cask_files("AM") + @modified_cask_files ||= git_filter_cask_files("AMR") end def added_cask_files diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb index e21ce86b6c..8a38ce1bed 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb @@ -7,10 +7,6 @@ module Hbc end def run - raise CaskError, "Dump incomplete." if dump_casks == :incomplet - end - - def dump_casks casks.each(&:dumpcask) end diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb index 4515fe9319..c046197989 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb @@ -3,7 +3,7 @@ module Hbc class InternalStanza < AbstractInternalCommand # Syntax # - # brew cask _stanza [ --table | --yaml | --inspect | --quiet ] [ ... ] + # brew cask _stanza [ --quiet ] [ --table | --yaml ] [ ... ] # # If no tokens are given, then data for all Casks is returned. # @@ -14,41 +14,16 @@ module Hbc # Examples # # brew cask _stanza appcast --table - # brew cask _stanza app --table alfred google-chrome adium voicemac logisim vagrant - # brew cask _stanza url --table alfred google-chrome adium voicemac logisim vagrant - # brew cask _stanza version --table alfred google-chrome adium voicemac logisim vagrant - # brew cask _stanza artifacts --table --inspect alfred google-chrome adium voicemac logisim vagrant - # brew cask _stanza artifacts --table --yaml alfred google-chrome adium voicemac logisim vagrant + # brew cask _stanza app --table alfred google-chrome adium vagrant + # brew cask _stanza url --table alfred google-chrome adium vagrant + # brew cask _stanza version --table alfred google-chrome adium vagrant + # brew cask _stanza artifacts --table alfred google-chrome adium vagrant + # brew cask _stanza artifacts --table --yaml alfred google-chrome adium vagrant # - # TODO: this should be retrievable from Hbc::DSL - ARTIFACTS = Set.new [ - :app, - :suite, - :artifact, - :prefpane, - :qlplugin, - :dictionary, - :font, - :service, - :colorpicker, - :binary, - :input_method, - :internet_plugin, - :audio_unit_plugin, - :vst_plugin, - :vst3_plugin, - :screen_saver, - :pkg, - :installer, - :stage_only, - :nested_container, - :uninstall, - :preflight, - :postflight, - :uninstall_preflight, - :uninstall_postflight, - ] + ARTIFACTS = + DSL::ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key) + + DSL::ARTIFACT_BLOCK_CLASSES.map(&:dsl_key) option "--table", :table, false option "--quiet", :quiet, false @@ -68,16 +43,9 @@ module Hbc @stanza = args.shift.to_sym @format = :to_yaml if yaml? - @format = :inspect if inspect? end def run - return unless print_stanzas == :incomplete - exit 1 if quiet? - raise CaskError, "Print incomplete." - end - - def print_stanzas if ARTIFACTS.include?(stanza) artifact_name = stanza @stanza = :artifacts @@ -93,7 +61,7 @@ module Hbc end begin - value = cask.send(@stanza) + value = cask.send(stanza) rescue StandardError opoo "failure calling '#{stanza}' on Cask '#{cask}'" unless quiet? puts "" @@ -106,11 +74,25 @@ module Hbc next end - value = value.fetch(artifact_name).to_a.flatten if artifact_name + if stanza == :artifacts + value = Hash[ + value.map do |k, v| + v = v.map do |a| + next a.to_a if a.respond_to?(:to_a) + next a.to_h if a.respond_to?(:to_h) + a + end - if @format - puts value.send(@format) - elsif artifact_name || value.is_a?(Symbol) + [k, v] + end + ] + + value = value.fetch(artifact_name) if artifact_name + end + + if format + puts value.send(format) + elsif value.is_a?(Symbol) puts value.inspect else puts value.to_s diff --git a/Library/Homebrew/cask/lib/hbc/cli/list.rb b/Library/Homebrew/cask/lib/hbc/cli/list.rb index 9d978360eb..4a50ae74ad 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/list.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/list.rb @@ -3,6 +3,7 @@ module Hbc class List < AbstractCommand option "-1", :one, false option "--versions", :versions, false + option "--full-name", :full_name, false option "-l", (lambda do |*| one = true # rubocop:disable Lint/UselessAssignment @@ -10,8 +11,7 @@ module Hbc end) def run - retval = args.any? ? list : list_installed - raise CaskError, "Listing incomplete." if retval == :incomplete + args.any? ? list : list_installed end def list @@ -23,16 +23,16 @@ module Hbc elsif versions? puts self.class.format_versioned(cask) else - cask = CaskLoader.load_from_file(cask.installed_caskfile) + cask = CaskLoader.load(cask.installed_caskfile) self.class.list_artifacts(cask) end end end def self.list_artifacts(cask) - Artifact.for_cask(cask).each do |artifact| - summary = artifact.summary - ohai summary[:english_description], summary[:contents] unless summary.empty? + cask.artifacts.group_by(&:class).each do |klass, artifacts| + next unless klass.respond_to?(:english_description) + ohai klass.english_description, artifacts.map(&:summarize_installed) end end @@ -43,11 +43,11 @@ module Hbc puts installed_casks.map(&:to_s) elsif versions? puts installed_casks.map(&self.class.method(:format_versioned)) + elsif full_name? + puts installed_casks.map(&:full_name).sort &tap_and_name_comparison elsif !installed_casks.empty? puts Formatter.columns(installed_casks.map(&:to_s)) end - - installed_casks.empty? ? :empty : :complete end def self.format_versioned(cask) diff --git a/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb b/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb index 337a2eb9d6..408be134da 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb @@ -1,7 +1,7 @@ module Hbc class CLI class Reinstall < Install - def install_casks + def run casks.each do |cask| Installer.new(cask, binaries: binaries?, verbose: verbose?, diff --git a/Library/Homebrew/cask/lib/hbc/cli/search.rb b/Library/Homebrew/cask/lib/hbc/cli/search.rb index 643d18d554..d56d0c81fe 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,18 @@ module Hbc end def self.search_remote(query) - matches = GitHub.search_code("user:caskroom", "path:Casks", "filename:#{query}", "extension:rb") - [*matches].map do |match| + 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? "#{tap.name}/#{File.basename(match["path"], ".rb")}" diff --git a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb index c0697c8080..f2059605ca 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb @@ -9,10 +9,6 @@ module Hbc end def run - raise CaskError, "Uninstall incomplete." if uninstall_casks == :incomplete - end - - def uninstall_casks casks.each do |cask| odebug "Uninstalling Cask #{cask}" @@ -20,7 +16,7 @@ module Hbc if cask.installed? && !cask.installed_caskfile.nil? # use the same cask file that was used for installation, if possible - cask = CaskLoader.load_from_file(cask.installed_caskfile) if cask.installed_caskfile.exist? + cask = CaskLoader.load(cask.installed_caskfile) if cask.installed_caskfile.exist? end Installer.new(cask, binaries: binaries?, verbose: verbose?, force: force?).uninstall diff --git a/Library/Homebrew/cask/lib/hbc/cli/zap.rb b/Library/Homebrew/cask/lib/hbc/cli/zap.rb index e709f41914..7f5e6785dc 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/zap.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/zap.rb @@ -9,10 +9,6 @@ module Hbc end def run - raise CaskError, "Zap incomplete." if zap_casks == :incomplete - end - - def zap_casks casks.each do |cask| odebug "Zapping Cask #{cask}" Installer.new(cask, verbose: verbose?, force: force?).zap diff --git a/Library/Homebrew/cask/lib/hbc/container.rb b/Library/Homebrew/cask/lib/hbc/container.rb index 93e825e038..fab3a3c1c3 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -4,6 +4,7 @@ require "hbc/container/bzip2" require "hbc/container/cab" require "hbc/container/criteria" require "hbc/container/dmg" +require "hbc/container/directory" require "hbc/container/executable" require "hbc/container/generic_unar" require "hbc/container/gpg" @@ -14,6 +15,7 @@ require "hbc/container/otf" require "hbc/container/pkg" require "hbc/container/seven_zip" require "hbc/container/sit" +require "hbc/container/svn_repository" require "hbc/container/tar" require "hbc/container/ttf" require "hbc/container/rar" @@ -43,6 +45,7 @@ module Hbc Xz, # pure xz Gpg, # GnuPG signed data Executable, + SvnRepository, ] # for explicit use only (never autodetected): # Hbc::Container::Naked diff --git a/Library/Homebrew/cask/lib/hbc/container/base.rb b/Library/Homebrew/cask/lib/hbc/container/base.rb index 52eb5aab17..78610a1ab5 100644 --- a/Library/Homebrew/cask/lib/hbc/container/base.rb +++ b/Library/Homebrew/cask/lib/hbc/container/base.rb @@ -20,7 +20,7 @@ module Hbc unless children.count == 1 && !nested_container.directory? && - @cask.artifacts[:nested_container].empty? && + @cask.artifacts.none? { |a| a.is_a?(Artifact::NestedContainer) } && extract_nested_container(nested_container) children.each do |src| diff --git a/Library/Homebrew/cask/lib/hbc/container/criteria.rb b/Library/Homebrew/cask/lib/hbc/container/criteria.rb index 66ecb8c874..52f171d6a1 100644 --- a/Library/Homebrew/cask/lib/hbc/container/criteria.rb +++ b/Library/Homebrew/cask/lib/hbc/container/criteria.rb @@ -13,9 +13,11 @@ module Hbc end def magic_number(regex) + return false if path.directory? + # 262: length of the longest regex (currently: Hbc::Container::Tar) - @magic_number ||= File.open(@path, "rb") { |f| f.read(262) } - @magic_number =~ regex + @magic_number ||= File.open(path, "rb") { |f| f.read(262) } + @magic_number.match?(regex) end end end diff --git a/Library/Homebrew/cask/lib/hbc/container/directory.rb b/Library/Homebrew/cask/lib/hbc/container/directory.rb new file mode 100644 index 0000000000..e4bb1095b2 --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/directory.rb @@ -0,0 +1,24 @@ +require "hbc/container/base" + +module Hbc + class Container + class Directory < Base + def self.me?(*) + false + end + + def extract + @path.children.each do |child| + next if skip_path?(child) + FileUtils.cp child, @cask.staged_path + end + end + + private + + def skip_path?(*) + false + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/container/executable.rb b/Library/Homebrew/cask/lib/hbc/container/executable.rb index 848f6d4be2..af3b36fd10 100644 --- a/Library/Homebrew/cask/lib/hbc/container/executable.rb +++ b/Library/Homebrew/cask/lib/hbc/container/executable.rb @@ -8,7 +8,7 @@ module Hbc return true if criteria.magic_number(/^#!\s*\S+/) begin - MachO.open(criteria.path).header.executable? + criteria.path.file? && MachO.open(criteria.path).header.executable? rescue MachO::MagicError false end diff --git a/Library/Homebrew/cask/lib/hbc/container/naked.rb b/Library/Homebrew/cask/lib/hbc/container/naked.rb index 375d62f7ae..dc265c4025 100644 --- a/Library/Homebrew/cask/lib/hbc/container/naked.rb +++ b/Library/Homebrew/cask/lib/hbc/container/naked.rb @@ -16,7 +16,7 @@ module Hbc def target_file return @path.basename if @nested - URI.decode(File.basename(@cask.url.path)) + CGI.unescape(File.basename(@cask.url.path)) end end end diff --git a/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb b/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb new file mode 100644 index 0000000000..cae613b2db --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/svn_repository.rb @@ -0,0 +1,15 @@ +require "hbc/container/directory" + +module Hbc + class Container + class SvnRepository < Directory + def self.me?(criteria) + criteria.path.join(".svn").directory? + end + + def skip_path?(path) + path.basename.to_s == ".svn" + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/download_strategy.rb b/Library/Homebrew/cask/lib/hbc/download_strategy.rb index 016bb66e6e..245ad4adeb 100644 --- a/Library/Homebrew/cask/lib/hbc/download_strategy.rb +++ b/Library/Homebrew/cask/lib/hbc/download_strategy.rb @@ -10,7 +10,7 @@ module Hbc class AbstractDownloadStrategy attr_reader :cask, :name, :url, :uri_object, :version - def initialize(cask, command = SystemCommand) + def initialize(cask, command: SystemCommand) @cask = cask @command = command # TODO: this excess of attributes is a function of integrating @@ -33,8 +33,8 @@ module Hbc class HbVCSDownloadStrategy < AbstractDownloadStrategy REF_TYPES = [:branch, :revision, :revisions, :tag].freeze - def initialize(cask, command = SystemCommand) - super + def initialize(*args, **options) + super(*args, **options) @ref_type, @ref = extract_ref @clone = Hbc.cache.join(cache_filename) end @@ -64,11 +64,6 @@ module Hbc end class CurlDownloadStrategy < AbstractDownloadStrategy - # TODO: should be part of url object - def mirrors - @mirrors ||= [] - end - def tarball_path @tarball_path ||= Hbc.cache.join("#{name}--#{version}#{ext}") end @@ -95,13 +90,8 @@ module Hbc end end - def downloaded_size - temporary_path.size? || 0 - end - def _fetch - odebug "Calling curl with args #{cask_curl_args}" - curl(*cask_curl_args) + curl_download url, *cask_curl_args, to: temporary_path, user_agent: uri_object.user_agent end def fetch @@ -131,33 +121,12 @@ module Hbc ignore_interrupts { temporary_path.rename(tarball_path) } end tarball_path - rescue CurlDownloadStrategyError - raise if mirrors.empty? - puts "Trying a mirror..." - @url = mirrors.shift - retry end private def cask_curl_args - default_curl_args.tap do |args| - args.concat(user_agent_args) - args.concat(cookies_args) - args.concat(referer_args) - end - end - - def default_curl_args - [url, "-C", downloaded_size, "-o", temporary_path] - end - - def user_agent_args - if uri_object.user_agent - ["-A", uri_object.user_agent] - else - [] - end + cookies_args + referer_args end def cookies_args @@ -191,8 +160,7 @@ module Hbc class CurlPostDownloadStrategy < CurlDownloadStrategy def cask_curl_args - super - default_curl_args.concat(post_args) + super.concat(post_args) end def post_args @@ -225,8 +193,8 @@ module Hbc # super does not provide checks for already-existing downloads def fetch - if tarball_path.exist? - puts "Already downloaded: #{tarball_path}" + if cached_location.directory? + puts "Already downloaded: #{cached_location}" else @url = @url.sub(/^svn\+/, "") if @url =~ %r{^svn\+http://} ohai "Checking out #{@url}" @@ -252,9 +220,8 @@ module Hbc else fetch_repo @clone, @url end - compress end - tarball_path + cached_location end # This primary reason for redefining this method is the trust_cert @@ -288,10 +255,6 @@ module Hbc print_stderr: false) end - def tarball_path - @tarball_path ||= cached_location.dirname.join(cached_location.basename.to_s + "-#{@cask.version}.tar") - end - def shell_quote(str) # Oh god escaping shell args. # See http://notetoself.vrensk.com/2008/08/escaping-single-quotes-in-ruby-harder-than-expected/ @@ -304,35 +267,5 @@ module Hbc yield name, url end end - - private - - # TODO/UPDATE: the tar approach explained below is fragile - # against challenges such as case-sensitive filesystems, - # and must be re-implemented. - # - # Seems nutty: we "download" the contents into a tape archive. - # Why? - # * A single file is tractable to the rest of the Cask toolchain, - # * An alternative would be to create a Directory container type. - # However, some type of file-serialization trick would still be - # needed in order to enable calculating a single checksum over - # a directory. So, in that alternative implementation, the - # special cases would propagate outside this class, including - # the use of tar or equivalent. - # * SubversionDownloadStrategy.cached_location is not versioned - # * tarball_path provides a needed return value for our overridden - # fetch method. - # * We can also take this private opportunity to strip files from - # the download which are protocol-specific. - - def compress - Dir.chdir(cached_location) do - @command.run!("/usr/bin/tar", - args: ['-s/^\.//', "--exclude", ".svn", "-cf", Pathname.new(tarball_path), "--", "."], - print_stderr: false) - end - clear_cache - end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 8ad206c2f2..2db2c66a98 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -1,6 +1,8 @@ require "set" require "locale" +require "hbc/artifact" + require "hbc/dsl/appcast" require "hbc/dsl/base" require "hbc/dsl/caveats" @@ -8,7 +10,6 @@ require "hbc/dsl/conflicts_with" require "hbc/dsl/container" require "hbc/dsl/depends_on" require "hbc/dsl/gpg" -require "hbc/dsl/installer" require "hbc/dsl/postflight" require "hbc/dsl/preflight" require "hbc/dsl/stanza_proxy" @@ -18,39 +19,35 @@ require "hbc/dsl/version" module Hbc class DSL - ORDINARY_ARTIFACT_TYPES = [ - :app, - :artifact, - :audio_unit_plugin, - :binary, - :colorpicker, - :dictionary, - :font, - :input_method, - :internet_plugin, - :pkg, - :prefpane, - :qlplugin, - :screen_saver, - :service, - :stage_only, - :suite, - :vst_plugin, - :vst3_plugin, + ORDINARY_ARTIFACT_CLASSES = [ + Artifact::Installer, + Artifact::App, + Artifact::Artifact, + Artifact::AudioUnitPlugin, + Artifact::Binary, + Artifact::Colorpicker, + Artifact::Dictionary, + Artifact::Font, + Artifact::InputMethod, + Artifact::InternetPlugin, + Artifact::Pkg, + Artifact::Prefpane, + Artifact::Qlplugin, + Artifact::ScreenSaver, + Artifact::Service, + Artifact::StageOnly, + Artifact::Suite, + Artifact::VstPlugin, + Artifact::Vst3Plugin, + Artifact::Uninstall, + Artifact::Zap, ].freeze - ACTIVATABLE_ARTIFACT_TYPES = ([:installer, *ORDINARY_ARTIFACT_TYPES] - [:stage_only]).freeze + ACTIVATABLE_ARTIFACT_CLASSES = ORDINARY_ARTIFACT_CLASSES - [Artifact::StageOnly] - SPECIAL_ARTIFACT_TYPES = [ - :uninstall, - :zap, - ].freeze - - ARTIFACT_BLOCK_TYPES = [ - :preflight, - :postflight, - :uninstall_preflight, - :uninstall_postflight, + ARTIFACT_BLOCK_CLASSES = [ + Artifact::PreflightBlock, + Artifact::PostflightBlock, ].freeze DSL_METHODS = Set.new [ @@ -66,21 +63,23 @@ module Hbc :gpg, :homepage, :language, + :languages, :name, :sha256, :staged_path, :url, :version, :appdir, - *ORDINARY_ARTIFACT_TYPES, - *ACTIVATABLE_ARTIFACT_TYPES, - *SPECIAL_ARTIFACT_TYPES, - *ARTIFACT_BLOCK_TYPES, + *ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key), + *ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key), + *ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] }, ].freeze - attr_reader :token - def initialize(token) - @token = token + attr_reader :cask, :token + + def initialize(cask) + @cask = cask + @token = cask.token end def name(*args) @@ -93,12 +92,14 @@ module Hbc return instance_variable_get("@#{stanza}") if should_return if instance_variable_defined?("@#{stanza}") - raise CaskInvalidError.new(token, "'#{stanza}' stanza may only appear once") + raise CaskInvalidError.new(cask, "'#{stanza}' stanza may only appear once.") end instance_variable_set("@#{stanza}", yield) + rescue CaskInvalidError + raise rescue StandardError => e - raise CaskInvalidError.new(token, "'#{stanza}' stanza failed with: #{e}") + raise CaskInvalidError.new(cask, "'#{stanza}' stanza failed with: #{e}") end def homepage(homepage = nil) @@ -106,19 +107,21 @@ module Hbc end def language(*args, default: false, &block) - if !args.empty? && block_given? + if args.empty? + language_eval + elsif block_given? @language_blocks ||= {} @language_blocks[args] = block return unless default unless @language_blocks.default.nil? - raise CaskInvalidError.new(token, "Only one default language may be defined") + raise CaskInvalidError.new(cask, "Only one default language may be defined.") end @language_blocks.default = block else - language_eval + raise CaskInvalidError.new(cask, "No block given to language stanza.") end end @@ -127,6 +130,10 @@ module Hbc return @language = nil if @language_blocks.nil? || @language_blocks.empty? + if @language_blocks.default.nil? + raise CaskInvalidError.new(cask, "No default language specified.") + end + MacOS.languages.map(&Locale.method(:parse)).each do |locale| key = @language_blocks.keys.detect do |strings| strings.any? { |string| locale.include?(string) } @@ -140,6 +147,12 @@ module Hbc @language = @language_blocks.default.call end + def languages + return [] if @language_blocks.nil? + + @language_blocks.keys.flatten + end + def url(*args, &block) set_unique_stanza(:url, args.empty? && !block_given?) do begin @@ -162,8 +175,8 @@ module Hbc begin DSL::Container.new(*args).tap do |container| # TODO: remove this backward-compatibility section after removing nested_container - if container && container.nested - artifacts[:nested_container] << container.nested + if container&.nested + artifacts.add(Artifact::NestedContainer.new(cask, container.nested)) end end end @@ -173,7 +186,7 @@ module Hbc def version(arg = nil) set_unique_stanza(:version, arg.nil?) do if !arg.is_a?(String) && arg != :latest - raise CaskInvalidError.new(token, "invalid 'version' value: '#{arg.inspect}'") + raise CaskInvalidError.new(cask, "invalid 'version' value: '#{arg.inspect}'") end DSL::Version.new(arg) end @@ -182,7 +195,7 @@ module Hbc def sha256(arg = nil) set_unique_stanza(:sha256, arg.nil?) do if !arg.is_a?(String) && arg != :no_check - raise CaskInvalidError.new(token, "invalid 'sha256' value: '#{arg.inspect}'") + raise CaskInvalidError.new(cask, "invalid 'sha256' value: '#{arg.inspect}'") end arg end @@ -195,7 +208,7 @@ module Hbc begin @depends_on.load(*args) rescue RuntimeError => e - raise CaskInvalidError.new(token, e) + raise CaskInvalidError.new(cask, e) end @depends_on end @@ -206,7 +219,7 @@ module Hbc end def artifacts - @artifacts ||= Hash.new { |hash, key| hash[key] = Set.new } + @artifacts ||= SortedSet.new end def caskroom_path @@ -237,39 +250,27 @@ module Hbc set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates } end - ORDINARY_ARTIFACT_TYPES.each do |type| - define_method(type) do |*args| - if type == :stage_only - if args != [true] - raise CaskInvalidError.new(token, "'stage_only' takes a single argument: true") + ORDINARY_ARTIFACT_CLASSES.each do |klass| + define_method(klass.dsl_key) do |*args| + begin + if [*artifacts.map(&:class), klass].include?(Artifact::StageOnly) && (artifacts.map(&:class) & ACTIVATABLE_ARTIFACT_CLASSES).any? + raise CaskInvalidError.new(cask, "'stage_only' must be the only activatable artifact.") end - unless (artifacts.keys & ACTIVATABLE_ARTIFACT_TYPES).empty? - raise CaskInvalidError.new(token, "'stage_only' must be the only activatable artifact") - end + artifacts.add(klass.from_args(cask, *args)) + rescue CaskInvalidError + raise + rescue StandardError => e + raise CaskInvalidError.new(cask, "invalid '#{klass.dsl_key}' stanza: #{e}") end - - artifacts[type].add(args) end end - def installer(*args) - return artifacts[:installer] if args.empty? - artifacts[:installer] << DSL::Installer.new(*args) - raise "'stage_only' must be the only activatable artifact" if artifacts.key?(:stage_only) - rescue StandardError => e - raise CaskInvalidError.new(token, e) - end - - SPECIAL_ARTIFACT_TYPES.each do |type| - define_method(type) do |*args| - artifacts[type].merge(args) - end - end - - ARTIFACT_BLOCK_TYPES.each do |type| - define_method(type) do |&block| - artifacts[type] << block + ARTIFACT_BLOCK_CLASSES.each do |klass| + [klass.dsl_key, klass.uninstall_dsl_key].each do |dsl_key| + define_method(dsl_key) do |&block| + artifacts.add(klass.new(cask, dsl_key => block)) + end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb index d302d09460..f3994b81f2 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb @@ -3,16 +3,20 @@ require "hbc/system_command" module Hbc class DSL class Appcast - attr_reader :parameters, :checkpoint + attr_reader :uri, :checkpoint, :parameters - def initialize(uri, parameters = {}) - @parameters = parameters - @uri = UnderscoreSupportingURI.parse(uri) - @checkpoint = @parameters[:checkpoint] + def initialize(uri, **parameters) + @uri = URI(uri) + @parameters = parameters + @checkpoint = parameters[:checkpoint] end def calculate_checkpoint - result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--fail", @uri], print_stderr: false) + curl_executable, *args = curl_args( + "--compressed", "--location", "--fail", uri, + user_agent: :fake + ) + result = SystemCommand.run(curl_executable, args: args, print_stderr: false) checkpoint = if result.success? processed_appcast_text = result.stdout.gsub(%r{[^<]*}m, "") @@ -26,11 +30,11 @@ module Hbc end def to_yaml - [@uri, @parameters].to_yaml + [uri, parameters].to_yaml end def to_s - @uri.to_s + uri.to_s end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/base.rb b/Library/Homebrew/cask/lib/hbc/dsl/base.rb index 63df847e95..b97c4e104e 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/base.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/base.rb @@ -10,14 +10,14 @@ module Hbc def_delegators :@cask, :token, :version, :caskroom_path, :staged_path, :appdir, :language - def system_command(executable, options = {}) - @command.run!(executable, options) + def system_command(executable, **options) + @command.run!(executable, **options) end def method_missing(method, *) if method underscored_class = self.class.name.gsub(/([[:lower:]])([[:upper:]][[:lower:]])/, '\1_\2').downcase - section = underscored_class.downcase.split("::").last + section = underscored_class.split("::").last Utils.method_missing_message(method, @cask.to_s, section) nil else diff --git a/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb b/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb index 402574456a..7d373b5f33 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/caveats.rb @@ -48,7 +48,7 @@ module Hbc brew cask install java EOS - elsif java_version.include?("8") || java_version.include?("+") + elsif java_version.include?("9") || java_version.include?("+") puts <<-EOS.undent #{@cask} requires Java #{java_version}. You can install the latest version with diff --git a/Library/Homebrew/cask/lib/hbc/dsl/conflicts_with.rb b/Library/Homebrew/cask/lib/hbc/dsl/conflicts_with.rb index 9483482392..dfaa55a5c2 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/conflicts_with.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/conflicts_with.rb @@ -10,25 +10,20 @@ module Hbc :java, ] - attr_accessor(*VALID_KEYS) - attr_accessor :pairs + attr_reader *VALID_KEYS def initialize(pairs = {}) @pairs = pairs + + VALID_KEYS.each do |key| + instance_variable_set("@#{key}", Set.new) + end + pairs.each do |key, value| raise "invalid conflicts_with key: '#{key.inspect}'" unless VALID_KEYS.include?(key) - writer_method = "#{key}=".to_sym - send(writer_method, value) + instance_variable_set("@#{key}", instance_variable_get("@#{key}").merge([*value])) end end - - def to_yaml - @pairs.to_yaml - end - - def to_s - @pairs.inspect - end end end end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/gpg.rb b/Library/Homebrew/cask/lib/hbc/dsl/gpg.rb index b49ebb97cd..99b39d43fd 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/gpg.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/gpg.rb @@ -14,11 +14,11 @@ module Hbc def initialize(signature, parameters = {}) @parameters = parameters - @signature = UnderscoreSupportingURI.parse(signature) + @signature = URI(signature) parameters.each do |hkey, hvalue| raise "invalid 'gpg' parameter: '#{hkey.inspect}'" unless VALID_PARAMETERS.include?(hkey) writer_method = "#{hkey}=".to_sym - hvalue = UnderscoreSupportingURI.parse(hvalue) if hkey == :key_url + hvalue = URI(hvalue) if hkey == :key_url valid_id?(hvalue) if hkey == :key_id send(writer_method, hvalue) end @@ -35,7 +35,7 @@ module Hbc end def to_yaml - # bug, :key_url value is not represented as an instance of Hbc::UnderscoreSupportingURI + # bug, :key_url value is not represented as an instance of URI [@signature, @parameters].to_yaml end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/installer.rb b/Library/Homebrew/cask/lib/hbc/dsl/installer.rb deleted file mode 100644 index b01b28d762..0000000000 --- a/Library/Homebrew/cask/lib/hbc/dsl/installer.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Hbc - class DSL - class Installer - VALID_KEYS = Set.new [ - :manual, - :script, - ] - - attr_accessor(*VALID_KEYS) - - def initialize(*parameters) - raise CaskInvalidError.new(token, "'installer' stanza requires an argument") if parameters.empty? - parameters = {}.merge(*parameters) - if parameters.key?(:script) && !parameters[:script].respond_to?(:key?) - if parameters.key?(:executable) - raise CaskInvalidError.new(token, "'installer' stanza gave arguments for both :script and :executable") - end - parameters[:executable] = parameters[:script] - parameters.delete(:script) - parameters = { script: parameters } - end - unless parameters.keys.length == 1 - raise "invalid 'installer' stanza: only one of #{VALID_KEYS.inspect} is permitted" - end - key = parameters.keys.first - raise "invalid 'installer' stanza key: '#{key.inspect}'" unless VALID_KEYS.include?(key) - writer_method = "#{key}=".to_sym - send(writer_method, parameters[key]) - end - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/dsl/version.rb b/Library/Homebrew/cask/lib/hbc/dsl/version.rb index d73205f528..9605feb579 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/version.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/version.rb @@ -49,7 +49,7 @@ module Hbc end end - DIVIDERS.keys.each do |divider| + DIVIDERS.each_key do |divider| define_divider_methods(divider) end diff --git a/Library/Homebrew/cask/lib/hbc/exceptions.rb b/Library/Homebrew/cask/lib/hbc/exceptions.rb index 1a246be65a..f7f9e43b66 100644 --- a/Library/Homebrew/cask/lib/hbc/exceptions.rb +++ b/Library/Homebrew/cask/lib/hbc/exceptions.rb @@ -17,6 +17,19 @@ module Hbc end end + class CaskConflictError < AbstractCaskErrorWithToken + attr_reader :conflicting_cask + + def initialize(token, conflicting_cask) + super(token) + @conflicting_cask = conflicting_cask + end + + def to_s + "Cask '#{token}' conflicts with '#{conflicting_cask}'." + end + end + class CaskUnavailableError < AbstractCaskErrorWithToken def to_s "Cask '#{token}' is unavailable" << (reason.empty? ? "." : ": #{reason}") diff --git a/Library/Homebrew/cask/lib/hbc/installer.rb b/Library/Homebrew/cask/lib/hbc/installer.rb index 53210ed4b4..68b9595e10 100644 --- a/Library/Homebrew/cask/lib/hbc/installer.rb +++ b/Library/Homebrew/cask/lib/hbc/installer.rb @@ -86,6 +86,8 @@ module Hbc raise CaskAlreadyInstalledError, @cask end + check_conflicts + print_caveats fetch uninstall_existing_cask if @reinstall @@ -98,6 +100,21 @@ module Hbc puts summary end + def check_conflicts + return unless @cask.conflicts_with + + @cask.conflicts_with.cask.each do |conflicting_cask| + begin + conflicting_cask = CaskLoader.load(conflicting_cask) + if conflicting_cask.installed? + raise CaskConflictError.new(@cask, conflicting_cask) + end + rescue CaskUnavailableError + next # Ignore conflicting Casks that do not exist. + end + end + end + def reinstall odebug "Hbc::Installer#reinstall" @reinstall = true @@ -109,7 +126,7 @@ module Hbc # use the same cask file that was used for installation, if possible installed_caskfile = @cask.installed_caskfile - installed_cask = installed_caskfile.exist? ? CaskLoader.load_from_file(installed_caskfile) : @cask + installed_cask = installed_caskfile.exist? ? CaskLoader.load(installed_caskfile) : @cask # Always force uninstallation, ignore method parameter Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true).uninstall @@ -142,7 +159,7 @@ module Hbc odebug "Extracting primary container" FileUtils.mkdir_p @cask.staged_path - container = if @cask.container && @cask.container.type + container = if @cask.container&.type Container.from_type(@cask.container.type) else Container.for_path(@downloaded_path, @command) @@ -160,7 +177,7 @@ module Hbc already_installed_artifacts = [] odebug "Installing artifacts" - artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?) + artifacts = @cask.artifacts odebug "#{artifacts.length} artifact/s defined", artifacts artifacts.each do |artifact| @@ -171,7 +188,7 @@ module Hbc next unless binaries? end - artifact.install_phase + artifact.install_phase(command: @command, verbose: verbose?, force: force?) already_installed_artifacts.unshift(artifact) end rescue StandardError => e @@ -179,7 +196,7 @@ module Hbc already_installed_artifacts.each do |artifact| next unless artifact.respond_to?(:uninstall_phase) odebug "Reverting installation of artifact of class #{artifact.class}" - artifact.uninstall_phase + artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?) end ensure purge_versioned_files @@ -344,7 +361,7 @@ module Hbc savedir = @cask.metadata_subdir("Casks", timestamp: :now, create: true) FileUtils.copy @cask.sourcefile_path, savedir - old_savedir.rmtree unless old_savedir.nil? + old_savedir&.rmtree end def uninstall @@ -357,25 +374,27 @@ module Hbc def uninstall_artifacts odebug "Un-installing artifacts" - artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?) + artifacts = @cask.artifacts odebug "#{artifacts.length} artifact/s defined", artifacts artifacts.each do |artifact| next unless artifact.respond_to?(:uninstall_phase) odebug "Un-installing artifact of class #{artifact.class}" - artifact.uninstall_phase + artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?) end end def zap ohai %Q(Implied "brew cask uninstall #{@cask}") uninstall_artifacts - if Artifact::Zap.me?(@cask) - ohai "Dispatching zap stanza" - Artifact::Zap.new(@cask, command: @command).zap_phase - else + if (zap_stanzas = @cask.artifacts.select { |a| a.is_a?(Artifact::Zap) }).empty? opoo "No zap stanza present for Cask '#{@cask}'" + else + ohai "Dispatching zap stanza" + zap_stanzas.each do |stanza| + stanza.zap_phase(command: @command, verbose: verbose?, force: force?) + end end ohai "Removing all staged versions of Cask '#{@cask}'" purge_caskroom_path diff --git a/Library/Homebrew/cask/lib/hbc/staged.rb b/Library/Homebrew/cask/lib/hbc/staged.rb index c1aa01b295..da097e0cf9 100644 --- a/Library/Homebrew/cask/lib/hbc/staged.rb +++ b/Library/Homebrew/cask/lib/hbc/staged.rb @@ -4,7 +4,7 @@ module Hbc index = 0 if index == :first index = 1 if index == :second index = -1 if index == :last - Hbc.appdir.join(@cask.artifacts[:app].to_a.at(index).first, "Contents", "Info.plist") + @cask.artifacts.select { |a| a.is_a?(Artifact::App) }.at(index).target.join("Contents", "Info.plist") end def plist_exec(cmd) diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb index 901617b71b..be083c29ef 100644 --- a/Library/Homebrew/cask/lib/hbc/system_command.rb +++ b/Library/Homebrew/cask/lib/hbc/system_command.rb @@ -61,7 +61,7 @@ module Hbc end def assert_success - return if processed_status && processed_status.success? + return if processed_status&.success? raise CaskCommandFailedError.new(command, processed_output[:stdout], processed_output[:stderr], processed_status) end @@ -112,11 +112,7 @@ module Hbc processed_output[:stderr], processed_status.exitstatus) end - end -end -module Hbc - class SystemCommand class Result attr_accessor :command, :stdout, :stderr, :exit_status diff --git a/Library/Homebrew/cask/lib/hbc/underscore_supporting_uri.rb b/Library/Homebrew/cask/lib/hbc/underscore_supporting_uri.rb deleted file mode 100644 index 8f8f66f43e..0000000000 --- a/Library/Homebrew/cask/lib/hbc/underscore_supporting_uri.rb +++ /dev/null @@ -1,28 +0,0 @@ -require "uri" - -module Hbc - module UnderscoreSupportingURI - def self.parse(maybe_uri) - return nil if maybe_uri.nil? - URI.parse(maybe_uri) - rescue URI::InvalidURIError => e - scheme, host, path = simple_parse(maybe_uri) - raise e unless path && host.include?("_") - URI.parse(without_host_underscores(scheme, host, path)).tap do |uri| - uri.instance_variable_set("@host", host) - end - end - - def self.simple_parse(maybe_uri) - scheme, host_and_path = maybe_uri.split("://") - host, path = host_and_path.split("/", 2) - [scheme, host, path] - rescue StandardError - nil - end - - def self.without_host_underscores(scheme, host, path) - ["#{scheme}:/", host.tr("_", "-"), path].join("/") - end - end -end diff --git a/Library/Homebrew/cask/lib/hbc/url.rb b/Library/Homebrew/cask/lib/hbc/url.rb index 15da2ced21..35020c5db3 100644 --- a/Library/Homebrew/cask/lib/hbc/url.rb +++ b/Library/Homebrew/cask/lib/hbc/url.rb @@ -1,8 +1,6 @@ module Hbc class URL - FAKE_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io".freeze - - attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data + attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data, :user_agent extend Forwardable def_delegators :uri, :path, :scheme, :to_s @@ -16,8 +14,8 @@ module Hbc end def initialize(uri, options = {}) - @uri = Hbc::UnderscoreSupportingURI.parse(uri) - @user_agent = options[:user_agent] + @uri = URI(uri) + @user_agent = options.fetch(:user_agent, :default) @cookies = options[:cookies] @referer = options[:referer] @using = options[:using] @@ -25,10 +23,5 @@ module Hbc @trust_cert = options[:trust_cert] @data = options[:data] end - - def user_agent - return FAKE_USER_AGENT if @user_agent == :fake - @user_agent - end end end diff --git a/Library/Homebrew/cask/lib/hbc/verify/gpg.rb b/Library/Homebrew/cask/lib/hbc/verify/gpg.rb index dbb537756d..f4996a5b5e 100644 --- a/Library/Homebrew/cask/lib/hbc/verify/gpg.rb +++ b/Library/Homebrew/cask/lib/hbc/verify/gpg.rb @@ -33,7 +33,7 @@ module Hbc meta_dir = cached || cask.metadata_subdir("gpg", :now, true) sig_path = meta_dir.join("signature.asc") - curl(cask.gpg.signature, "-o", sig_path.to_s) unless cached || force + curl_download cask.gpg.signature, to: sig_path unless cached || force sig_path end diff --git a/Library/Homebrew/caveats.rb b/Library/Homebrew/caveats.rb index 578b292fab..1849ea79b3 100644 --- a/Library/Homebrew/caveats.rb +++ b/Library/Homebrew/caveats.rb @@ -163,7 +163,7 @@ class Caveats def plist_caveats s = [] - if f.plist || (keg && keg.plist_installed?) + if f.plist || (keg&.plist_installed?) plist_domain = f.plist_path.basename(".plist") # we readlink because this path probably doesn't exist since caveats diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index d1f0b25163..3401612043 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/deps.rb b/Library/Homebrew/cmd/deps.rb index bbf0c1b0b4..de7aa4a512 100644 --- a/Library/Homebrew/cmd/deps.rb +++ b/Library/Homebrew/cmd/deps.rb @@ -1,4 +1,4 @@ -#: * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] : +#: * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] [`--include-requirements`] : #: Show dependencies for . When given multiple formula arguments, #: show the intersection of dependencies for . #: @@ -19,15 +19,22 @@ #: . To include the `:build` type dependencies, pass `--include-build`. #: Similarly, pass `--include-optional` to include `:optional` dependencies. #: To skip `:recommended` type dependencies, pass `--skip-recommended`. +#: To include requirements in addition to dependencies, pass `--include-requirements`. #: -#: * `deps` `--tree` [] (|`--installed`): +#: * `deps` `--tree` [`--1`] [] [`--annotate`] (|`--installed`): #: Show dependencies as a tree. When given multiple formula arguments, output #: individual trees for every formula. #: +#: If `--1` is passed, only one level of children is displayed. +#: #: If `--installed` is passed, output a tree for every installed formula. #: #: The placeholder is any combination of options `--include-build`, -#: `--include-optional`, and `--skip-recommended` as documented above. +#: `--include-optional`, `--skip-recommended`, and `--include-requirements` as +#: documented above. +#: +#: If `--annotate` is passed, the build, optional, and recommended dependencies +#: are marked as such in the output. #: #: * `deps` [] (`--installed`|`--all`): #: Show dependencies for installed or all available formulae. Every line of @@ -37,6 +44,10 @@ #: The placeholder is any combination of options `--include-build`, #: `--include-optional`, and `--skip-recommended` as documented above. +# The undocumented `--for-each` option will switch into the mode used by `deps --all`, +# but only list dependencies for specified formula, one specified formula per line. +# This is used for debugging the `--installed`/`--all` display mode. + # encoding: UTF-8 require "formula" @@ -52,20 +63,26 @@ module Homebrew all?: ARGV.include?("--all"), topo_order?: ARGV.include?("-n"), union?: ARGV.include?("--union"), + for_each?: ARGV.include?("--for-each"), ) - if mode.installed? && mode.tree? - puts_deps_tree Formula.installed + if mode.tree? + if mode.installed? + puts_deps_tree Formula.installed, !ARGV.one? + else + raise FormulaUnspecifiedError if ARGV.named.empty? + puts_deps_tree ARGV.formulae, !ARGV.one? + end elsif mode.all? puts_deps Formula - elsif mode.tree? - raise FormulaUnspecifiedError if ARGV.named.empty? - puts_deps_tree ARGV.formulae elsif ARGV.named.empty? raise FormulaUnspecifiedError unless mode.installed? puts_deps Formula.installed + elsif mode.for_each? + puts_deps ARGV.formulae else all_deps = deps_for_formulae(ARGV.formulae, !ARGV.one?, &(mode.union? ? :| : :&)) + all_deps = condense_requirements(all_deps) all_deps = all_deps.select(&:installed?) if mode.installed? all_deps = all_deps.map(&method(:dep_display_name)).uniq all_deps.sort! unless mode.topo_order? @@ -73,24 +90,59 @@ module Homebrew end end - def dep_display_name(d) - ARGV.include?("--full-name") ? d.to_formula.full_name : d.name + def condense_requirements(deps) + if ARGV.include?("--include-requirements") + deps + else + deps.map do |dep| + if dep.is_a? Dependency + dep + elsif dep.default_formula? + dep.to_dependency + end + end.compact + end + end + + def dep_display_name(dep) + str = if dep.is_a? Requirement + if ARGV.include?("--include-requirements") + if dep.default_formula? + ":#{dep.display_s} (#{dep_display_name(dep.to_dependency)})" + else + ":#{dep.display_s}" + end + elsif dep.default_formula? + dep_display_name(dep.to_dependency) + else + # This shouldn't happen, but we'll put something here to help debugging + "::#{dep.name}" + end + else + ARGV.include?("--full-name") ? dep.to_formula.full_name : dep.name + end + if ARGV.include?("--annotate") + str = "#{str} [build]" if dep.build? + str = "#{str} [optional" if dep.optional? + str = "#{str} [recommended]" if dep.recommended? + end + str end def deps_for_formula(f, recursive = false) includes = [] ignores = [] - if ARGV.include? "--include-build" + if ARGV.include?("--include-build") includes << "build?" else ignores << "build?" end - if ARGV.include? "--include-optional" + if ARGV.include?("--include-optional") includes << "optional?" else ignores << "optional?" end - ignores << "recommended?" if ARGV.include? "--skip-recommended" + ignores << "recommended?" if ARGV.include?("--skip-recommended") if recursive deps = f.recursive_dependencies do |dependent, dep| @@ -120,7 +172,7 @@ module Homebrew end end - deps + reqs.select(&:default_formula?).map(&:to_dependency) + deps + reqs.to_a end def deps_for_formulae(formulae, recursive = false, &block) @@ -129,41 +181,55 @@ module Homebrew def puts_deps(formulae) formulae.each do |f| - deps = deps_for_formula(f).sort_by(&:name).map(&method(:dep_display_name)) + deps = deps_for_formula(f) + deps = condense_requirements(deps) + deps = deps.sort_by(&:name).map(&method(:dep_display_name)) puts "#{f.full_name}: #{deps.join(" ")}" end end - def puts_deps_tree(formulae) + def puts_deps_tree(formulae, recursive = false) formulae.each do |f| - puts "#{f.full_name} (required dependencies)" - recursive_deps_tree(f, "") + puts f.full_name + @dep_stack = [] + recursive_deps_tree(f, "", recursive) puts end end - def recursive_deps_tree(f, prefix) - reqs = f.requirements.select(&:default_formula?) - deps = f.deps.default - max = reqs.length - 1 - reqs.each_with_index do |req, i| - chr = if i == max && deps.empty? + def recursive_deps_tree(f, prefix, recursive) + reqs = f.requirements + deps = f.deps + dependables = reqs + deps + dependables = dependables.reject(&:optional?) unless ARGV.include?("--include-optional") + dependables = dependables.reject(&:build?) unless ARGV.include?("--include-build") + dependables = dependables.reject(&:recommended?) if ARGV.include?("--skip-recommended") + max = dependables.length - 1 + @dep_stack.push f.name + dependables.each_with_index do |dep, i| + next if !ARGV.include?("--include-requirements") && dep.is_a?(Requirement) && !dep.default_formula? + tree_lines = if i == max "└──" else "├──" end - puts prefix + "#{chr} :#{dep_display_name(req.to_dependency)}" - end - max = deps.length - 1 - deps.each_with_index do |dep, i| - chr = if i == max - "└──" + display_s = "#{tree_lines} #{dep_display_name(dep)}" + is_circular = @dep_stack.include?(dep.name) + display_s = "#{display_s} (CIRCULAR DEPENDENCY)" if is_circular + puts "#{prefix}#{display_s}" + next if !recursive || is_circular + prefix_addition = if i == max + " " else - "├──" + "│ " + end + if dep.is_a?(Requirement) && dep.default_formula? + recursive_deps_tree(Formulary.factory(dep.to_dependency.name), prefix + prefix_addition, true) + end + if dep.is_a? Dependency + recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_addition, true) end - prefix_ext = (i == max) ? " " : "│ " - puts prefix + "#{chr} #{dep_display_name(dep)}" - recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_ext) end + @dep_stack.pop end end diff --git a/Library/Homebrew/cmd/fetch.rb b/Library/Homebrew/cmd/fetch.rb index 006c637466..4117539927 100644 --- a/Library/Homebrew/cmd/fetch.rb +++ b/Library/Homebrew/cmd/fetch.rb @@ -49,8 +49,10 @@ module Homebrew if fetch_bottle?(f) begin fetch_formula(f.bottle) - rescue Exception => e - raise if ARGV.homebrew_developer? || e.is_a?(Interrupt) + rescue Interrupt + raise + rescue => e + raise if ARGV.homebrew_developer? fetched_bottle = false onoe e.message opoo "Bottle fetch failed: fetching the source." diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index c000877058..ca8f294772 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -219,6 +219,7 @@ module Homebrew end end + return if formulae.empty? perform_preinstall_checks formulae.each do |f| @@ -338,6 +339,7 @@ module Homebrew rescue FormulaInstallationAlreadyAttemptedError # We already attempted to install f as part of the dependency tree of # another formula. In that case, don't generate an error, just move on. + return rescue CannotInstallFormulaError => e ofail e.message end diff --git a/Library/Homebrew/cmd/irb.rb b/Library/Homebrew/cmd/irb.rb index cba8a5b82b..4cd3d4c9ec 100644 --- a/Library/Homebrew/cmd/irb.rb +++ b/Library/Homebrew/cmd/irb.rb @@ -19,15 +19,13 @@ class String end end -def cask - $LOAD_PATH.unshift("#{HOMEBREW_LIBRARY_PATH}/cask/lib") - require "hbc" -end - module Homebrew module_function def irb + $LOAD_PATH.unshift("#{HOMEBREW_LIBRARY_PATH}/cask/lib") + require "hbc" + if ARGV.include? "--examples" puts "'v8'.f # => instance of the v8 formula" puts ":hub.f.installed?" diff --git a/Library/Homebrew/cmd/list.rb b/Library/Homebrew/cmd/list.rb index f5c4e68acf..436fc1f976 100644 --- a/Library/Homebrew/cmd/list.rb +++ b/Library/Homebrew/cmd/list.rb @@ -39,15 +39,7 @@ module Homebrew filtered_list elsif ARGV.named.empty? if ARGV.include? "--full-name" - full_names = Formula.installed.map(&:full_name).sort do |a, b| - if a.include?("/") && !b.include?("/") - 1 - elsif !a.include?("/") && b.include?("/") - -1 - else - a <=> b - end - end + full_names = Formula.installed.map(&:full_name).sort &tap_and_name_comparison return if full_names.empty? puts Formatter.columns(full_names) else diff --git a/Library/Homebrew/cmd/pin.rb b/Library/Homebrew/cmd/pin.rb index c5087f6d4c..5a14f853ce 100644 --- a/Library/Homebrew/cmd/pin.rb +++ b/Library/Homebrew/cmd/pin.rb @@ -1,6 +1,7 @@ #: * `pin` : #: Pin the specified , preventing them from being upgraded when -#: issuing the `brew upgrade` command. See also `unpin`. +#: issuing the `brew upgrade ` command (but can still be upgraded +#: as dependencies for other formulae). See also `unpin`. require "formula" diff --git a/Library/Homebrew/cmd/postinstall.rb b/Library/Homebrew/cmd/postinstall.rb index f5d091227e..02fd8a5f64 100644 --- a/Library/Homebrew/cmd/postinstall.rb +++ b/Library/Homebrew/cmd/postinstall.rb @@ -29,8 +29,6 @@ module Homebrew args << "--devel" end - Sandbox.print_sandbox_message if Sandbox.formula?(formula) - Utils.safe_fork do if Sandbox.formula?(formula) sandbox = Sandbox.new diff --git a/Library/Homebrew/cmd/prune.rb b/Library/Homebrew/cmd/prune.rb index 9fc6dbcd97..7ec2838bae 100644 --- a/Library/Homebrew/cmd/prune.rb +++ b/Library/Homebrew/cmd/prune.rb @@ -55,7 +55,7 @@ module Homebrew else n, d = ObserverPathnameExtension.counts print "Pruned #{n} symbolic links " - print "and #{d} directories " if d > 0 + print "and #{d} directories " if d.positive? puts "from #{HOMEBREW_PREFIX}" end end diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index 94096d2dd4..6727c0b6bb 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -47,8 +47,8 @@ module Homebrew fi.install fi.finish rescue FormulaInstallationAlreadyAttemptedError - # next - rescue Exception + return + rescue Exception # rubocop:disable Lint/RescueException ignore_interrupts { restore_backup(keg, keg_was_linked) } raise else diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index b2d0697445..c01a11c104 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -34,38 +34,40 @@ module Homebrew elsif ARGV.include? "--opensuse" exec_browser "https://software.opensuse.org/search?q=#{ARGV.next}" elsif ARGV.include? "--fedora" - exec_browser "https://admin.fedoraproject.org/pkgdb/packages/%2A#{ARGV.next}%2A/" + 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) Descriptions.search(regex, :desc).print elsif ARGV.first =~ HOMEBREW_TAP_FORMULA_REGEX query = ARGV.first - user, repo, name = query.split("/", 3) begin result = Formulary.factory(query).name + results = Array(result) rescue FormulaUnavailableError - result = search_tap(user, repo, name) + _, _, name = query.split("/", 3) + results = search_taps(name) end - results = Array(result) puts Formatter.columns(results) unless results.empty? else query = ARGV.first regex = query_regexp(query) local_results = search_formulae(regex) puts Formatter.columns(local_results) unless local_results.empty? + tap_results = search_taps(query) puts Formatter.columns(tap_results) unless tap_results.empty? if $stdout.tty? count = local_results.length + tap_results.length + ohai "Searching blacklisted, migrated and deleted formulae..." if reason = Homebrew::MissingFormula.reason(query, silent: true) - if count > 0 + if count.positive? puts puts "If you meant #{query.inspect} specifically:" end @@ -100,10 +102,18 @@ 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 + 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", "user:caskroom", "filename:#{query}", "extension:rb") - [*matches].map do |match| + matches = GitHub.search_code(user: ["Homebrew", "caskroom"], filename: query, extension: "rb") + + matches.map do |match| dirname, filename = File.split(match["path"]) next unless valid_dirnames.include?(dirname) tap = Tap.fetch(match["repository"]["full_name"]) @@ -113,6 +123,9 @@ module Homebrew end def search_formulae(regex) + # Use stderr to avoid breaking parsed output + $stderr.puts Formatter.headline("Searching local taps...", color: :blue) + aliases = Formula.alias_full_names results = (Formula.full_names + aliases).grep(regex).sort diff --git a/Library/Homebrew/cmd/style.rb b/Library/Homebrew/cmd/style.rb index b0f46fadc4..58f430a27d 100644 --- a/Library/Homebrew/cmd/style.rb +++ b/Library/Homebrew/cmd/style.rb @@ -117,12 +117,13 @@ module Homebrew case output_type when :print + args << "--debug" if ARGV.debug? args << "--display-cop-names" if ARGV.include? "--display-cop-names" args << "--format" << "simple" if files - system(cache_env, "rubocop", *args) + system(cache_env, "rubocop", "_#{HOMEBREW_RUBOCOP_VERSION}_", *args) !$CHILD_STATUS.success? when :json - json, _, status = Open3.capture3(cache_env, "rubocop", "--format", "json", *args) + json, _, status = Open3.capture3(cache_env, "rubocop", "_#{HOMEBREW_RUBOCOP_VERSION}_", "--format", "json", *args) # exit status of 1 just means violations were found; other numbers mean # execution errors. # exitstatus can also be nil if RuboCop process crashes, e.g. due to diff --git a/Library/Homebrew/cmd/tap-info.rb b/Library/Homebrew/cmd/tap-info.rb index af087645d2..cb0e0b387e 100644 --- a/Library/Homebrew/cmd/tap-info.rb +++ b/Library/Homebrew/cmd/tap-info.rb @@ -64,10 +64,10 @@ module Homebrew if tap.installed? info += tap.pinned? ? "pinned" : "unpinned" info += ", private" if tap.private? - if (formula_count = tap.formula_files.size) > 0 + if (formula_count = tap.formula_files.size).positive? info += ", #{Formatter.pluralize(formula_count, "formula")}" end - if (command_count = tap.command_files.size) > 0 + if (command_count = tap.command_files.size).positive? info += ", #{Formatter.pluralize(command_count, "command")}" end info += ", no formulae/commands" if (formula_count + command_count).zero? diff --git a/Library/Homebrew/cmd/tap.rb b/Library/Homebrew/cmd/tap.rb index 2a07c1b2f3..fa520e2c56 100644 --- a/Library/Homebrew/cmd/tap.rb +++ b/Library/Homebrew/cmd/tap.rb @@ -54,8 +54,7 @@ module Homebrew quiet: ARGV.quieter? rescue TapRemoteMismatchError => e odie e - rescue TapAlreadyTappedError, TapAlreadyUnshallowError - # Do nothing. + rescue TapAlreadyTappedError, TapAlreadyUnshallowError # rubocop:disable Lint/HandleExceptions end end end diff --git a/Library/Homebrew/cmd/unlinkapps.rb b/Library/Homebrew/cmd/unlinkapps.rb index 7cae97e27c..56dba3603c 100644 --- a/Library/Homebrew/cmd/unlinkapps.rb +++ b/Library/Homebrew/cmd/unlinkapps.rb @@ -77,7 +77,7 @@ module Homebrew def unlinkapps_unlink?(target_app, opts = {}) # Skip non-symlinks and symlinks that don't point into the Homebrew prefix. app = target_app.readlink.to_s if target_app.symlink? - return false unless app && app.start_with?(*UNLINKAPPS_PREFIXES) + return false unless app&.start_with?(*UNLINKAPPS_PREFIXES) if opts.fetch(:prune, false) !File.exist?(app) # Remove only broken symlinks in prune mode. diff --git a/Library/Homebrew/cmd/unpin.rb b/Library/Homebrew/cmd/unpin.rb index a669df1ec4..e15a156ead 100644 --- a/Library/Homebrew/cmd/unpin.rb +++ b/Library/Homebrew/cmd/unpin.rb @@ -1,6 +1,6 @@ #: * `unpin` : -#: Unpin , allowing them to be upgraded by `brew upgrade`. See also -#: `pin`. +#: Unpin , allowing them to be upgraded by `brew upgrade `. +#: See also `pin`. require "formula" diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index ea915f99cf..0974df0b41 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -124,7 +124,7 @@ module Homebrew return if ENV["HOMEBREW_UPDATE_TEST"] core_tap = CoreTap.instance return if core_tap.installed? - CoreTap.ensure_installed! quiet: false + CoreTap.ensure_installed! revision = core_tap.git_head ENV["HOMEBREW_UPDATE_BEFORE_HOMEBREW_HOMEBREW_CORE"] = revision ENV["HOMEBREW_UPDATE_AFTER_HOMEBREW_HOMEBREW_CORE"] = revision @@ -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. @@ -371,7 +372,7 @@ class Reporter new_version = formula.pkg_version old_version = FormulaVersions.new(formula).formula_at_revision(@initial_revision, &:pkg_version) next if new_version == old_version - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "#{e.message}\n#{e.backtrace.join "\n"}" if ARGV.homebrew_developer? end @report[:M] << tap.formula_file_to_name(src) @@ -459,7 +460,7 @@ class Reporter unless Formulary.factory(new_full_name).keg_only? system HOMEBREW_BREW_FILE, "link", new_full_name, "--overwrite" end - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "#{e.message}\n#{e.backtrace.join "\n"}" if ARGV.homebrew_developer? end next @@ -520,7 +521,7 @@ class Reporter begin f = Formulary.factory(new_full_name) - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "#{e.message}\n#{e.backtrace.join "\n"}" if ARGV.homebrew_developer? next end diff --git a/Library/Homebrew/cmd/update.sh b/Library/Homebrew/cmd/update.sh index fb6a3459c1..3507fa92b5 100644 --- a/Library/Homebrew/cmd/update.sh +++ b/Library/Homebrew/cmd/update.sh @@ -385,6 +385,12 @@ EOS if ! git --version >/dev/null 2>&1 then + # we need a new enough curl to install git + if [[ -n "$HOMEBREW_SYSTEM_CURL_TOO_OLD" && + ! -x "$HOMEBREW_PREFIX/opt/curl/bin/curl" ]] + then + brew install curl + fi # we cannot install brewed git if homebrew/core is unavailable. [[ -d "$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-core" ]] && brew install git unset GIT_EXECUTABLE @@ -564,6 +570,7 @@ EOS -d "$HOMEBREW_LIBRARY/LinkedKegs" || (-n "$HOMEBREW_DEVELOPER" && -z "$HOMEBREW_UPDATE_PREINSTALL") ]] then + unset HOMEBREW_RUBY_PATH brew update-report "$@" return $? elif [[ -z "$HOMEBREW_UPDATE_PREINSTALL" ]] diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index 1cdb497cf6..f1ce3c7da4 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -150,6 +150,7 @@ module Homebrew rescue FormulaInstallationAlreadyAttemptedError # We already attempted to upgrade f as part of the dependency tree of # another formula. In that case, don't generate an error, just move on. + return rescue CannotInstallFormulaError => e ofail e rescue BuildError => e diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb index 24684c3b6c..0b09e1bf19 100644 --- a/Library/Homebrew/cmd/uses.rb +++ b/Library/Homebrew/cmd/uses.rb @@ -74,12 +74,13 @@ module Homebrew end end - dep_formulae = deps.map do |dep| + dep_formulae = deps.flat_map do |dep| begin dep.to_formula rescue + [] end - end.compact + end reqs_by_formula = ([f] + dep_formulae).flat_map do |formula| formula.requirements.map { |req| [formula, req] } @@ -118,6 +119,7 @@ module Homebrew rescue FormulaUnavailableError # Silently ignore this case as we don't care about things used in # taps that aren't currently tapped. + next end end end diff --git a/Library/Homebrew/cmd/vendor-install.sh b/Library/Homebrew/cmd/vendor-install.sh index fe7e26dd44..6d16a297d5 100644 --- a/Library/Homebrew/cmd/vendor-install.sh +++ b/Library/Homebrew/cmd/vendor-install.sh @@ -13,16 +13,16 @@ if [[ -n "$HOMEBREW_MACOS" ]] then if [[ "$HOMEBREW_PROCESSOR" = "Intel" ]] then - ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.0.0-p648.leopard_64.bottle.tar.gz" - ruby_SHA="5c1240abe4be91c9774a0089c2a38a8ccfff87c009e8e5786730c659d5e633f7" + ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz" + ruby_SHA="34ce9e4c9c1be28db564d744165aa29291426f8a3d2ef806ba4f0b9175aedb2b" else ruby_URL="" ruby_SHA="" fi elif [[ -n "$HOMEBREW_LINUX" ]] then - ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.0.0-p648.x86_64_linux.bottle.tar.gz" - ruby_SHA="dbb5118a22a6a75cc77e62544a3d8786d383fab1bdaf8c154951268807357bf0" + ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.x86_64_linux.bottle.tar.gz" + ruby_SHA="543c18bd33a300e6c16671437b1e0f17b03bb64e6a485fc15ff7de1eb1a0bc2a" fi fetch() { @@ -45,20 +45,25 @@ fetch() { curl_args[${#curl_args[*]}]="--progress-bar" fi + if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "100600" ]] + then + curl_args[${#curl_args[*]}]="--insecure" + fi + temporary_path="$CACHED_LOCATION.incomplete" mkdir -p "$HOMEBREW_CACHE" - [[ -n "$HOMEBREW_QUIET" ]] || echo "==> Downloading $VENDOR_URL" + [[ -n "$HOMEBREW_QUIET" ]] || echo "==> Downloading $VENDOR_URL" >&2 if [[ -f "$CACHED_LOCATION" ]] then - [[ -n "$HOMEBREW_QUIET" ]] || echo "Already downloaded: $CACHED_LOCATION" + [[ -n "$HOMEBREW_QUIET" ]] || echo "Already downloaded: $CACHED_LOCATION" >&2 else if [[ -f "$temporary_path" ]] then "$HOMEBREW_CURL" "${curl_args[@]}" -C - "$VENDOR_URL" -o "$temporary_path" if [[ $? -eq 33 ]] then - [[ -n "$HOMEBREW_QUIET" ]] || echo "Trying a full download" + [[ -n "$HOMEBREW_QUIET" ]] || echo "Trying a full download" >&2 rm -f "$temporary_path" "$HOMEBREW_CURL" "${curl_args[@]}" "$VENDOR_URL" -o "$temporary_path" fi @@ -135,7 +140,7 @@ install() { fi safe_cd "$VENDOR_DIR" - [[ -n "$HOMEBREW_QUIET" ]] || echo "==> Unpacking $(basename "$VENDOR_URL")" + [[ -n "$HOMEBREW_QUIET" ]] || echo "==> Pouring $(basename "$VENDOR_URL")" >&2 tar "$tar_args" "$CACHED_LOCATION" safe_cd "$VENDOR_DIR/portable-$VENDOR_NAME" diff --git a/Library/Homebrew/compat/ENV/shared.rb b/Library/Homebrew/compat/ENV/shared.rb index 200e7b132b..c700b1e00a 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/compat/formula_specialties.rb b/Library/Homebrew/compat/formula_specialties.rb index ec5e91ce8a..78966625ee 100644 --- a/Library/Homebrew/compat/formula_specialties.rb +++ b/Library/Homebrew/compat/formula_specialties.rb @@ -16,7 +16,7 @@ end # This formula serves as the base class for several very similar # formulae for Amazon Web Services related tools. class AmazonWebServicesFormula < Formula - # Use this method to peform a standard install for Java-based tools, + # Use this method to perform a standard install for Java-based tools, # keeping the .jars out of HOMEBREW_PREFIX/lib def install odeprecated "AmazonWebServicesFormula#install", "Formula#install" diff --git a/Library/Homebrew/constants.rb b/Library/Homebrew/constants.rb index 23be70bccf..b122946c8f 100644 --- a/Library/Homebrew/constants.rb +++ b/Library/Homebrew/constants.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # RuboCop version used for `brew style` and `brew cask style` -HOMEBREW_RUBOCOP_VERSION = "0.49.1".freeze -HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.13.1".freeze # has to be updated when RuboCop version changes +HOMEBREW_RUBOCOP_VERSION = "0.50.0" +HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.14.2" # has to be updated when RuboCop version changes diff --git a/Library/Homebrew/debrew.rb b/Library/Homebrew/debrew.rb index c2662c9eee..6206eb8a27 100644 --- a/Library/Homebrew/debrew.rb +++ b/Library/Homebrew/debrew.rb @@ -9,7 +9,7 @@ module Debrew module Raise def raise(*) super - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException e.extend(Ignorable) super(e) unless Debrew.debug(e) == :ignore end @@ -57,7 +57,7 @@ module Debrew input.chomp! i = input.to_i - if i > 0 + if i.positive? choice = menu.entries[i - 1] else possible = menu.entries.find_all { |e| e.name.start_with?(input) } @@ -92,7 +92,7 @@ module Debrew yield rescue SystemExit original_raise - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException debug(e) ensure @active = false @@ -119,7 +119,7 @@ module Debrew if e.is_a?(Ignorable) menu.choice(:irb) do puts "When you exit this IRB session, execution will continue." - set_trace_func proc { |event, _, _, id, binding, klass| + set_trace_func proc { |event, _, _, id, binding, klass| # rubocop:disable Metrics/ParameterLists if klass == Raise && id == :raise && event == "return" set_trace_func(nil) synchronize { IRB.start_within(binding) } diff --git a/Library/Homebrew/debrew/irb.rb b/Library/Homebrew/debrew/irb.rb index f974037820..eeb3758a98 100644 --- a/Library/Homebrew/debrew/irb.rb +++ b/Library/Homebrew/debrew/irb.rb @@ -4,8 +4,7 @@ module IRB @setup_done = false extend Module.new { - def parse_opts - end + def parse_opts; end def start_within(binding) unless @setup_done @@ -16,7 +15,7 @@ module IRB workspace = WorkSpace.new(binding) irb = Irb.new(workspace) - @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC] + @CONF[:IRB_RC]&.call(irb.context) @CONF[:MAIN_CONTEXT] = irb.context trap("SIGINT") do diff --git a/Library/Homebrew/dependency.rb b/Library/Homebrew/dependency.rb index d7d5ec59c0..7f0e7fbffe 100644 --- a/Library/Homebrew/dependency.rb +++ b/Library/Homebrew/dependency.rb @@ -51,7 +51,7 @@ class Dependency end def modify_build_environment - env_proc.call unless env_proc.nil? + env_proc&.call end def inspect @@ -64,7 +64,7 @@ class Dependency end def self._load(marshaled) - new(*Marshal.load(marshaled)) + new(*Marshal.load(marshaled)) # rubocop:disable Security/MarshalLoad end class << self diff --git a/Library/Homebrew/dependency_collector.rb b/Library/Homebrew/dependency_collector.rb index fff80a28cf..f855669e0a 100644 --- a/Library/Homebrew/dependency_collector.rb +++ b/Library/Homebrew/dependency_collector.rb @@ -4,6 +4,7 @@ require "ld64_dependency" require "requirement" require "requirements" require "set" +require "extend/cachable" ## A dependency is a formula that another formula needs to install. ## A requirement is something other than a formula that another formula @@ -16,17 +17,13 @@ require "set" # This class is used by `depends_on` in the formula DSL to turn dependency # specifications into the proper kinds of dependencies and requirements. class DependencyCollector + extend Cachable + # Define the languages that we can handle as external dependencies. LANGUAGE_MODULES = Set[ :lua, :lua51, :perl, :python, :python3, :ruby ].freeze - CACHE = {} - - def self.clear_cache - CACHE.clear - end - attr_reader :deps, :requirements def initialize @@ -45,7 +42,7 @@ class DependencyCollector end def fetch(spec) - CACHE.fetch(cache_key(spec)) { |key| CACHE[key] = build(spec) } + self.class.cache.fetch(cache_key(spec)) { |key| self.class.cache[key] = build(spec) } end def cache_key(spec) diff --git a/Library/Homebrew/descriptions.rb b/Library/Homebrew/descriptions.rb index ac1d68216a..bc19826731 100644 --- a/Library/Homebrew/descriptions.rb +++ b/Library/Homebrew/descriptions.rb @@ -78,10 +78,10 @@ class Descriptions formula_names.each do |name| begin - desc = Formulary.factory(name).desc + @cache[name] = Formulary.factory(name).desc rescue FormulaUnavailableError, *FormulaVersions::IGNORED_EXCEPTIONS + @cache.delete(name) end - @cache[name] = desc end save_cache if options[:save] end diff --git a/Library/Homebrew/dev-cmd/aspell-dictionaries.rb b/Library/Homebrew/dev-cmd/aspell-dictionaries.rb index 955e41b01f..ab0e66d2bf 100644 --- a/Library/Homebrew/dev-cmd/aspell-dictionaries.rb +++ b/Library/Homebrew/dev-cmd/aspell-dictionaries.rb @@ -27,13 +27,12 @@ module Homebrew end end - languages.inject([]) do |resources, (lang, path)| + languages.each do |lang, path| r = Resource.new(lang) r.owner = Formulary.factory("aspell") r.url "#{dict_url}/#{path}" r.mirror "#{dict_mirror}/#{path}" - resources << r - end.each(&:fetch).each do |r| + r.fetch puts <<-EOS option "with-lang-#{r.name}", "Install #{r.name} dictionary" resource "#{r.name}" do diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index f8236e6a60..b58672043a 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -53,7 +53,8 @@ module Homebrew module_function def audit - Homebrew.inject_dump_stats!(FormulaAuditor, /^audit_/) if ARGV.switch? "D" + inject_dump_stats!(FormulaAuditor, /^audit_/) if ARGV.switch? "D" + Homebrew.auditing = true formula_count = 0 problem_count = 0 @@ -201,19 +202,24 @@ 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, user_agents: [:default], check_content: false, strict: false, require_http: false) return unless url.start_with? "http" details = nil user_agent = nil - hash_needed = url.start_with?("http:") && name != "curl" + hash_needed = url.start_with?("http:") && !require_http user_agents.each do |ua| details = http_content_headers_and_checksum(url, hash_needed: hash_needed, user_agent: ua) user_agent = ua break if details[:status].to_s.start_with?("2") end - return "The URL #{url} is not reachable" unless details[:status] + unless details[:status] + # Hack around https://github.com/Homebrew/brew/issues/3199 + return if MacOS.version == :el_capitan + return "The URL #{url} is not reachable" + end + unless details[:status].start_with? "2" return "The URL #{url} is not reachable (HTTP status code #{details[:status]})" end @@ -236,18 +242,40 @@ 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) max_time = hash_needed ? "600" : "25" - args = curl_args( - extra_args: ["--connect-timeout", "15", "--include", "--max-time", max_time, url], - show_output: true, - user_agent: user_agent, + output, = curl_output( + "--connect-timeout", "15", "--include", "--max-time", max_time, "--location", url, + user_agent: user_agent ) - output = Open3.popen3(*args) { |_, stdout, _, _| stdout.read } status_code = :unknown while status_code == :unknown || status_code.to_s.start_with?("3") @@ -262,6 +290,7 @@ class FormulaAuditor etag: headers[%r{ETag: ([wW]\/)?"(([^"]|\\")*)"}, 2], content_length: headers[/Content-Length: (\d+)/, 1], file_hash: output_hash, + file: output, } end @@ -329,7 +358,8 @@ class FormulaAuditor end valid_alias_names = [alias_name_major, alias_name_major_minor] - if formula.tap && !formula.tap.core_tap? + unless formula.tap&.core_tap? + versioned_aliases.map! { |a| "#{formula.tap}/#{a}" } valid_alias_names.map! { |a| "#{formula.tap}/#{a}" } end @@ -357,31 +387,17 @@ class FormulaAuditor end end - def audit_class - if @strict - unless formula.test_defined? - problem "A `test do` test block should be added" - end - end - - classes = %w[GithubGistFormula ScriptFileFormula AmazonWebServicesFormula] - klass = classes.find do |c| - Object.const_defined?(c) && formula.class < Object.const_get(c) - end - - problem "#{klass} is deprecated, use Formula instead" if klass + def self.aliases + # core aliases + tap alias names + tap alias full name + @aliases ||= Formula.aliases + Formula.tap_aliases end - # core aliases + tap alias names + tap alias full name - @@aliases ||= Formula.aliases + Formula.tap_aliases - def audit_formula_name return unless @strict # skip for non-official taps return if formula.tap.nil? || !formula.tap.official? name = formula.name - full_name = formula.full_name if Homebrew::MissingFormula.blacklisted_reason(name) problem "'#{name}' is blacklisted." @@ -397,33 +413,10 @@ class FormulaAuditor return end - if !formula.core_formula? && Formula.core_names.include?(name) - problem "Formula name conflicts with existing core formula." - return - end + return if formula.core_formula? + return unless Formula.core_names.include?(name) - @@local_official_taps_name_map ||= Tap.select(&:official?).flat_map(&:formula_names) - .each_with_object({}) do |tap_formula_full_name, name_map| - tap_formula_name = tap_formula_full_name.split("/").last - name_map[tap_formula_name] ||= [] - name_map[tap_formula_name] << tap_formula_full_name - name_map - end - - same_name_tap_formulae = @@local_official_taps_name_map[name] || [] - - if @online - Homebrew.search_taps(name).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 - end - end - - same_name_tap_formulae.delete(full_name) - - return if same_name_tap_formulae.empty? - problem "Formula name conflicts with #{same_name_tap_formulae.join ", "}" + problem "Formula name conflicts with existing core formula." end def audit_deps @@ -451,7 +444,7 @@ class FormulaAuditor problem "Dependency '#{dep.name}' was renamed; use new name '#{dep_f.name}'." end - if @@aliases.include?(dep.name) && + if self.class.aliases.include?(dep.name) && (dep_f.core_formula? || !dep_f.versioned_formula?) problem "Dependency '#{dep.name}' is an alias; use the canonical name '#{dep.to_formula.full_name}'." end @@ -462,16 +455,16 @@ class FormulaAuditor problem "Dependency '#{dep.name}' may be unnecessary as it is provided by macOS; try to build this formula without it." end - dep.options.reject do |opt| - next true if dep_f.option_defined?(opt) - dep_f.requirements.detect do |r| + dep.options.each do |opt| + next if dep_f.option_defined?(opt) + next if dep_f.requirements.detect do |r| if r.recommended? opt.name == "with-#{r.name}" elsif r.optional? opt.name == "without-#{r.name}" end end - end.each do |opt| + problem "Dependency #{dep} does not define option #{opt.name.inspect}" end @@ -564,10 +557,11 @@ class FormulaAuditor return unless @online - return unless DevelopmentTools.curl_handles_most_https_homepages? + return unless DevelopmentTools.curl_handles_most_https_certificates? 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 @@ -598,7 +592,8 @@ class FormulaAuditor return if metadata.nil? problem "GitHub fork (not canonical repository)" if metadata["fork"] - if (metadata["forks_count"] < 20) && (metadata["subscribers_count"] < 20) && + if formula&.tap&.core_tap? && + (metadata["forks_count"] < 20) && (metadata["subscribers_count"] < 20) && (metadata["stargazers_count"] < 50) problem "GitHub repository not notable enough (<20 forks, <20 watchers and <50 stars)" end @@ -617,13 +612,14 @@ class FormulaAuditor end %w[Stable Devel HEAD].each do |name| - next unless spec = formula.send(name.downcase) + spec_name = name.downcase.to_sym + next unless spec = formula.send(spec_name) - ra = ResourceAuditor.new(spec, online: @online, strict: @strict).audit + ra = ResourceAuditor.new(spec, spec_name, online: @online, strict: @strict).audit problems.concat ra.problems.map { |problem| "#{name}: #{problem}" } spec.resources.each_value do |resource| - ra = ResourceAuditor.new(resource, online: @online, strict: @strict).audit + ra = ResourceAuditor.new(resource, spec_name, online: @online, strict: @strict).audit problems.concat ra.problems.map { |problem| "#{name} resource #{resource.name.inspect}: #{problem}" } @@ -688,7 +684,7 @@ class FormulaAuditor end stable = formula.stable - case stable && stable.url + case stable&.url when /[\d\._-](alpha|beta|rc\d)/ matched = Regexp.last_match(1) version_prefix = stable.version.to_s.sub(/\d+$/, "") @@ -855,7 +851,7 @@ class FormulaAuditor def audit_reverse_migration # Only enforce for new formula being re-added to core and official taps return unless @strict - return unless formula.tap && formula.tap.official? + return unless formula.tap&.official? return unless formula.tap.tap_migrations.key?(formula.name) problem <<-EOS.undent @@ -918,10 +914,10 @@ class FormulaAuditor end class ResourceAuditor - attr_reader :problems - attr_reader :version, :checksum, :using, :specs, :url, :mirrors, :name + attr_reader :name, :version, :checksum, :url, :mirrors, :using, :specs, :owner + attr_reader :spec_name, :problems - def initialize(resource, options = {}) + def initialize(resource, spec_name, options = {}) @name = resource.name @version = resource.version @checksum = resource.checksum @@ -929,9 +925,11 @@ class ResourceAuditor @mirrors = resource.mirrors @using = resource.using @specs = resource.specs - @online = options[:online] - @strict = options[:strict] - @problems = [] + @owner = resource.owner + @spec_name = spec_name + @online = options[:online] + @strict = options[:strict] + @problems = [] end def audit @@ -1005,11 +1003,29 @@ class ResourceAuditor problem "Redundant :using value in URL" end + def self.curl_openssl_and_deps + @curl_openssl_and_deps ||= begin + formulae_names = ["curl", "openssl"] + formulae_names += formulae_names.flat_map do |f| + Formula[f].recursive_dependencies.map(&:name) + end + formulae_names.uniq + rescue FormulaUnavailableError + [] + end + end + def audit_urls urls = [url] + mirrors - if name == "curl" && !urls.find { |u| u.start_with?("http://") } - problem "should always include at least one HTTP url" + curl_openssl_or_deps = ResourceAuditor.curl_openssl_and_deps.include?(owner.name) + + if spec_name == :stable && curl_openssl_or_deps + problem "should not use xz tarballs" if url.end_with?(".xz") + + unless urls.find { |u| u.start_with?("http://") } + problem "should always include at least one HTTP mirror" + end end return unless @online @@ -1021,7 +1037,7 @@ class ResourceAuditor # A `brew mirror`'ed URL is usually not yet reachable at the time of # pull request. next if url =~ %r{^https://dl.bintray.com/homebrew/mirror/} - if http_content_problem = FormulaAuditor.check_http_content(url, name) + if http_content_problem = FormulaAuditor.check_http_content(url, require_http: curl_openssl_or_deps) problem http_content_problem end elsif strategy <= GitDownloadStrategy @@ -1030,6 +1046,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/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb index d8aefc4c0d..fb862c7733 100644 --- a/Library/Homebrew/dev-cmd/bottle.rb +++ b/Library/Homebrew/dev-cmd/bottle.rb @@ -47,7 +47,7 @@ BOTTLE_ERB = <<-EOS.freeze <% elsif cellar != BottleSpecification::DEFAULT_CELLAR %> cellar "<%= cellar %>" <% end %> - <% if rebuild > 0 %> + <% if rebuild.positive? %> rebuild <%= rebuild %> <% end %> <% checksums.each do |checksum_type, checksum_values| %> @@ -75,7 +75,7 @@ module Homebrew @put_filenames ||= [] - return if @put_filenames.include? filename + return if @put_filenames.include?(filename) puts Formatter.error(filename.to_s) @put_filenames << filename @@ -84,8 +84,7 @@ module Homebrew result = false keg.each_unique_file_matching(string) do |file| - # skip document file. - next if Metafiles::EXTENSIONS.include? file.extname + next if Metafiles::EXTENSIONS.include?(file.extname) # Skip document files. linked_libraries = Keg.file_linked_libraries(file, string) result ||= !linked_libraries.empty? @@ -156,9 +155,7 @@ module Homebrew return ofail "Formula not installed or up-to-date: #{f.full_name}" end - tap = f.tap - - unless tap + unless tap = f.tap unless ARGV.include?("--force-core-tap") return ofail "Formula not from core or any taps: #{f.full_name}" end @@ -186,7 +183,7 @@ module Homebrew ohai "Determining #{f.full_name} bottle rebuild..." versions = FormulaVersions.new(f) rebuilds = versions.bottle_version_map("origin/master")[f.pkg_version] - rebuilds.pop if rebuilds.last.to_i > 0 + rebuilds.pop if rebuilds.last.to_i.positive? rebuild = rebuilds.empty? ? 0 : rebuilds.max.to_i + 1 end @@ -283,7 +280,7 @@ module Homebrew raise ensure ignore_interrupts do - original_tab.write if original_tab + original_tab&.write unless ARGV.include? "--skip-relocation" keg.replace_placeholders_with_locations changed_files end diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index 1c56749a34..87d8274ccb 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -89,7 +89,8 @@ module Homebrew def check_for_duplicate_pull_requests(formula) pull_requests = fetch_pull_requests(formula) - return unless pull_requests && !pull_requests.empty? + return unless pull_requests + return if pull_requests.empty? duplicates_message = <<-EOS.undent These open pull requests may be duplicates: #{pull_requests.map { |pr| "#{pr["title"]} #{pr["html_url"]}" }.join("\n")} @@ -124,7 +125,7 @@ module Homebrew Formula.each do |f| if is_devel && f.devel && f.devel.url && f.devel.url.match(base_url) guesses << f - elsif f.stable && f.stable.url && f.stable.url.match(base_url) + elsif f.stable&.url && f.stable.url.match(base_url) guesses << f end end @@ -176,7 +177,10 @@ module Homebrew rsrc.version = forced_version if forced_version odie "No version specified!" unless rsrc.version rsrc_path = rsrc.fetch - if Utils.popen_read("/usr/bin/tar", "-tf", rsrc_path) =~ %r{/.*\.} + gnu_tar_gtar_path = HOMEBREW_PREFIX/"opt/gnu-tar/bin/gtar" + gnu_tar_gtar = gnu_tar_gtar_path if gnu_tar_gtar_path.executable? + tar = which("gtar") || gnu_tar_gtar || which("tar") + if Utils.popen_read(tar, "-tf", rsrc_path) =~ %r{/.*\.} new_hash = rsrc_path.sha256 elsif new_url.include? ".tar" odie "#{formula}: no url/#{hash_type} specified!" @@ -293,9 +297,7 @@ module Homebrew ohai "git fetch --unshallow origin" if shallow ohai "git checkout --no-track -b #{branch} origin/master" ohai "git commit --no-edit --verbose --message='#{formula.name} #{new_formula_version}#{devel_message}' -- #{formula.path}" - ohai "hub fork --no-remote" - ohai "hub fork" - ohai "hub fork (to read $HUB_REMOTE)" + ohai "hub fork # read $HUB_REMOTE" ohai "git push --set-upstream $HUB_REMOTE #{branch}:#{branch}" ohai "hub pull-request --browse -m '#{formula.name} #{new_formula_version}#{devel_message}'" ohai "git checkout -" @@ -305,9 +307,9 @@ module Homebrew safe_system "git", "commit", "--no-edit", "--verbose", "--message=#{formula.name} #{new_formula_version}#{devel_message}", "--", formula.path - safe_system "hub", "fork", "--no-remote" - quiet_system "hub", "fork" - remote = Utils.popen_read("hub fork 2>&1")[/fatal: remote (.+) already exists\./, 1] + remote = Utils.popen_read("hub fork 2>&1")[/remote:? (\S+)/, 1] + # repeat for hub 2.2 backwards compatibility: + remote = Utils.popen_read("hub fork 2>&1")[/remote:? (\S+)/, 1] if remote.to_s.empty? odie "cannot get remote from 'hub'!" if remote.to_s.empty? safe_system "git", "push", "--set-upstream", remote, "#{branch}:#{branch}" pr_message = <<-EOS.undent diff --git a/Library/Homebrew/dev-cmd/man.rb b/Library/Homebrew/dev-cmd/man.rb index 472bb7c2b1..b2bb3c8c34 100644 --- a/Library/Homebrew/dev-cmd/man.rb +++ b/Library/Homebrew/dev-cmd/man.rb @@ -48,12 +48,9 @@ module Homebrew def path_glob_commands(glob) Pathname.glob(glob) .sort_by { |source_file| sort_key_for_path(source_file) } - .map do |source_file| - source_file.read.lines - .grep(/^#:/) - .map { |line| line.slice(2..-1) } - .join - end.reject { |s| s.strip.empty? || s.include?("@hide_from_man_page") } + .map(&:read).map(&:lines) + .map { |lines| lines.grep(/^#:/).map { |line| line.slice(2..-1) }.join } + .reject { |s| s.strip.empty? || s.include?("@hide_from_man_page") } end def build_man_page diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb index e2492203d3..6445bc34cf 100644 --- a/Library/Homebrew/dev-cmd/mirror.rb +++ b/Library/Homebrew/dev-cmd/mirror.rb @@ -25,9 +25,9 @@ module Homebrew "public_download_numbers": true, "public_stats": true} EOS - curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}", - "-H", "Content-Type: application/json", - "-d", package_blob, bintray_repo_url + curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", + "--header", "Content-Type: application/json", + "--data", package_blob, bintray_repo_url puts end @@ -40,8 +40,8 @@ module Homebrew content_url = "https://api.bintray.com/content/homebrew/mirror" content_url += "/#{bintray_package}/#{f.pkg_version}/#{filename}" content_url += "?publish=1" - curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}", - "-T", download, content_url + curl "--silent", "--fail", "--user", "#{bintray_user}:#{bintray_key}", + "--upload-file", download, content_url puts ohai "Mirrored #{filename}!" end diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index 9681bb2bc0..8cb2703035 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -69,13 +69,13 @@ module Homebrew tap = nil ARGV.named.each do |arg| - if arg.to_i > 0 + if arg.to_i.positive? issue = arg url = "https://github.com/Homebrew/homebrew-core/pull/#{arg}" tap = CoreTap.instance elsif (testing_match = arg.match %r{/job/Homebrew.*Testing/(\d+)/}) tap = ARGV.value("tap") - tap = if tap && tap.start_with?("homebrew/") + tap = if tap&.start_with?("homebrew/") Tap.fetch("homebrew", tap.strip_prefix("homebrew/")) elsif tap odie "Tap option did not start with \"homebrew/\": #{tap}" @@ -154,8 +154,8 @@ module Homebrew begin f = Formula[name] - # Make sure we catch syntax errors. - rescue Exception + rescue Exception # rubocop:disable Lint/RescueException + # Make sure we catch syntax errors. next end @@ -228,7 +228,7 @@ module Homebrew "https://github.com/BrewTestBot/homebrew-#{tap.repo}/compare/homebrew:master...pr-#{issue}" end - curl "--silent", "--fail", "-o", "/dev/null", "-I", bottle_commit_url + curl "--silent", "--fail", "--output", "/dev/null", "--head", bottle_commit_url safe_system "git", "checkout", "--quiet", "-B", bottle_branch, orig_revision pull_patch bottle_commit_url, "bottle commit" @@ -303,7 +303,7 @@ module Homebrew extra_msg = @description ? "(#{@description})" : nil ohai "Fetching patch #{extra_msg}" puts "Patch: #{patch_url}" - curl patch_url, "-s", "-o", patchpath + curl_download patch_url, to: patchpath end def apply_patch @@ -350,7 +350,7 @@ module Homebrew files << Regexp.last_match(1) if line =~ %r{^\+\+\+ b/(.*)} end files.each do |file| - if tap && tap.formula_file?(file) + if tap&.formula_file?(file) formula_name = File.basename(file, ".rb") formulae << formula_name unless formulae.include?(formula_name) else @@ -433,10 +433,10 @@ module Homebrew end version = info.pkg_version ohai "Publishing on Bintray: #{package} #{version}" - curl "-w", '\n', "--silent", "--fail", - "-u#{creds[:user]}:#{creds[:key]}", "-X", "POST", - "-H", "Content-Type: application/json", - "-d", '{"publish_wait_for_secs": 0}', + curl "--write-out", '\n', "--silent", "--fail", + "--user", "#{creds[:user]}:#{creds[:key]}", "--request", "POST", + "--header", "Content-Type: application/json", + "--data", '{"publish_wait_for_secs": 0}', "https://api.bintray.com/content/homebrew/#{repo}/#{package}/#{version}/publish" true rescue => e @@ -587,7 +587,7 @@ module Homebrew # We're in the cache; make sure to force re-download loop do begin - curl url, "-o", filename + curl_download url, continue_at: 0, to: filename break rescue if retry_count >= max_curl_retries @@ -606,7 +606,7 @@ module Homebrew end def check_bintray_mirror(name, url) - headers = curl_output("--connect-timeout", "15", "--head", url)[0] + headers, = curl_output("--connect-timeout", "15", "--location", "--head", url) status_code = headers.scan(%r{^HTTP\/.* (\d+)}).last.first return if status_code.start_with?("2") opoo "The Bintray mirror #{url} is not reachable (HTTP status code #{status_code})." diff --git a/Library/Homebrew/dev-cmd/release-notes.rb b/Library/Homebrew/dev-cmd/release-notes.rb index e578869bf7..4960239565 100644 --- a/Library/Homebrew/dev-cmd/release-notes.rb +++ b/Library/Homebrew/dev-cmd/release-notes.rb @@ -10,10 +10,8 @@ module Homebrew def release_notes previous_tag = ARGV.named.first - unless previous_tag - previous_tag = Utils.popen_read("git tag --list --sort=-version:refname") + previous_tag ||= Utils.popen_read("git tag --list --sort=-version:refname") .lines.first.chomp - end odie "Could not find any previous tags!" unless previous_tag end_ref = ARGV.named[1] || "origin/master" diff --git a/Library/Homebrew/dev-cmd/test.rb b/Library/Homebrew/dev-cmd/test.rb index c678171acb..6622a8c257 100644 --- a/Library/Homebrew/dev-cmd/test.rb +++ b/Library/Homebrew/dev-cmd/test.rb @@ -65,8 +65,6 @@ module Homebrew args << "--devel" end - Sandbox.print_sandbox_message if Sandbox.test? - Utils.safe_fork do if Sandbox.test? sandbox = Sandbox.new @@ -86,7 +84,7 @@ module Homebrew rescue ::Test::Unit::AssertionFailedError => e ofail "#{f.full_name}: failed" puts e.message - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException ofail "#{f.full_name}: failed" puts e, e.backtrace ensure diff --git a/Library/Homebrew/dev-cmd/update-test.rb b/Library/Homebrew/dev-cmd/update-test.rb index aa7fe6a924..1f1cdbeed1 100644 --- a/Library/Homebrew/dev-cmd/update-test.rb +++ b/Library/Homebrew/dev-cmd/update-test.rb @@ -81,7 +81,7 @@ module Homebrew safe_system "git", "reset", "--hard", start_commit # update ENV["PATH"] - ENV["PATH"] = "#{curdir}/bin:/usr/local/bin:/usr/bin:/bin" + ENV["PATH"] = PATH.new(ENV["PATH"]).prepend(curdir/"bin") # run brew update oh1 "Running brew update..." diff --git a/Library/Homebrew/development_tools.rb b/Library/Homebrew/development_tools.rb index 996dea87cc..b7787d8493 100644 --- a/Library/Homebrew/development_tools.rb +++ b/Library/Homebrew/development_tools.rb @@ -114,7 +114,7 @@ class DevelopmentTools @non_apple_gcc_version = {} end - def curl_handles_most_https_homepages? + def curl_handles_most_https_certificates? true end diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index ceb6ad4d1d..3edf31012f 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -151,8 +151,10 @@ module Homebrew return unless File.directory?(dir) files = Dir.chdir(dir) do - Dir[pattern].select { |f| File.file?(f) && !File.symlink?(f) } - Dir.glob(white_list) - end.map { |file| File.join(dir, file) } + (Dir.glob(pattern) - Dir.glob(white_list)) + .select { |f| File.file?(f) && !File.symlink?(f) } + .map { |f| File.join(dir, f) } + end return if files.empty? inject_file_list(files, message) @@ -427,15 +429,15 @@ module Homebrew end def check_user_path_1 - $seen_prefix_bin = false - $seen_prefix_sbin = false + @seen_prefix_bin = false + @seen_prefix_sbin = false message = "" paths(ENV["HOMEBREW_PATH"]).each do |p| case p when "/usr/bin" - unless $seen_prefix_bin + unless @seen_prefix_bin # only show the doctor message if there are any conflicts # rationale: a default install should not trigger any brew doctor messages conflicts = Dir["#{HOMEBREW_PREFIX}/bin/*"] @@ -458,9 +460,9 @@ module Homebrew end end when "#{HOMEBREW_PREFIX}/bin" - $seen_prefix_bin = true + @seen_prefix_bin = true when "#{HOMEBREW_PREFIX}/sbin" - $seen_prefix_sbin = true + @seen_prefix_sbin = true end end @@ -468,7 +470,7 @@ module Homebrew end def check_user_path_2 - return if $seen_prefix_bin + return if @seen_prefix_bin <<-EOS.undent Homebrew's bin was not found in your PATH. @@ -478,7 +480,7 @@ module Homebrew end def check_user_path_3 - return if $seen_prefix_sbin + return if @seen_prefix_sbin # Don't complain about sbin not being in the path if it doesn't exist sbin = HOMEBREW_PREFIX/"sbin" @@ -522,7 +524,7 @@ module Homebrew homebrew_owned = @found.all? do |path| Pathname.new(path).realpath.to_s.start_with? "#{HOMEBREW_CELLAR}/gettext" end - return if gettext && gettext.linked_keg.directory? && homebrew_owned + return if gettext&.linked_keg&.directory? && homebrew_owned inject_file_list @found, <<-EOS.undent gettext files detected at a system prefix. @@ -540,7 +542,7 @@ module Homebrew rescue nil end - if libiconv && libiconv.linked_keg.directory? + if libiconv&.linked_keg&.directory? unless libiconv.keg_only? <<-EOS.undent A libiconv formula is installed and linked. @@ -952,6 +954,7 @@ module Homebrew Putting non-prefixed coreutils in your path can cause gmp builds to fail. EOS rescue FormulaUnavailableError + return end def check_for_non_prefixed_findutils @@ -966,6 +969,7 @@ module Homebrew Putting non-prefixed findutils in your path can cause python builds to fail. EOS rescue FormulaUnavailableError + return end def check_for_pydistutils_cfg_in_home diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index 7173347143..e69a56ddfa 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -18,8 +18,7 @@ class AbstractDownloadStrategy end # Download and cache the resource as {#cached_location}. - def fetch - end + def fetch; end # Suppress output def shutup! @@ -37,13 +36,11 @@ class AbstractDownloadStrategy # Unpack {#cached_location} into the current working directory, and possibly # chdir into the newly-unpacked directory. # Unlike {Resource#stage}, this does not take a block. - def stage - end + def stage; end # @!attribute [r] cached_location # The path to the cached file or directory associated with the resource. - def cached_location - end + def cached_location; end # @!attribute [r] # return most recent modified time for all files in the current working directory after stage. @@ -204,14 +201,11 @@ class VCSDownloadStrategy < AbstractDownloadStrategy true end - def clone_repo - end + def clone_repo; end - def update - end + def update; end - def current_revision - end + def current_revision; end def extract_ref(specs) key = REF_TYPES.find { |type| specs.key?(type) } @@ -331,20 +325,10 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy if cached_location.exist? puts "Already downloaded: #{cached_location}" else - had_incomplete_download = temporary_path.exist? begin _fetch rescue ErrorDuringExecution - # 33 == range not supported - # try wiping the incomplete download and retrying once - unless $CHILD_STATUS.exitstatus == 33 && had_incomplete_download - raise CurlDownloadStrategyError, @url - end - - ohai "Trying a full download" - temporary_path.unlink - had_incomplete_download = false - retry + raise CurlDownloadStrategyError, @url end ignore_interrupts { temporary_path.rename(cached_location) } end @@ -375,75 +359,59 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy ohai "Downloading from #{url}" end - urls = actual_urls(url) - unless urls.empty? - ohai "Downloading from #{urls.last}" - if !ENV["HOMEBREW_NO_INSECURE_REDIRECT"].nil? && url.start_with?("https://") && - urls.any? { |u| !u.start_with? "https://" } - puts "HTTPS to HTTP redirect detected & HOMEBREW_NO_INSECURE_REDIRECT is set." - raise CurlDownloadStrategyError, url - end - url = urls.last - end - - curl url, "-C", downloaded_size, "-o", temporary_path + curl_download resolved_url(url), to: temporary_path end # Curl options to be always passed to curl, - # with raw head calls (`curl -I`) or with actual `fetch`. + # with raw head calls (`curl --head`) or with actual `fetch`. def _curl_opts - copts = [] - copts << "--user" << meta.fetch(:user) if meta.key?(:user) - copts + return ["--user", meta.fetch(:user)] if meta.key?(:user) + [] end - def actual_urls(url) - urls = [] - curl_args = _curl_opts << "-I" << "-L" << url - Utils.popen_read("curl", *curl_args).scan(/^Location: (.+)$/).map do |m| - urls << URI.join(urls.last || url, m.first.chomp).to_s + def resolved_url(url) + redirect_url, _, status = curl_output( + *_curl_opts, "--silent", "--head", + "--write-out", "%{redirect_url}", + "--output", "/dev/null", + url.to_s + ) + + return url unless status.success? + return url if redirect_url.empty? + + ohai "Downloading from #{redirect_url}" + if ENV["HOMEBREW_NO_INSECURE_REDIRECT"] && + url.start_with?("https://") && !redirect_url.start_with?("https://") + puts "HTTPS to HTTP redirect detected & HOMEBREW_NO_INSECURE_REDIRECT is set." + raise CurlDownloadStrategyError, url end - urls + + redirect_url end - def downloaded_size - temporary_path.size? || 0 - end - - def curl(*args) + def curl(*args, **options) args.concat _curl_opts args << "--connect-timeout" << "5" unless mirrors.empty? - super + super(*args, **options) end end # Detect and download from Apache Mirror class CurlApacheMirrorDownloadStrategy < CurlDownloadStrategy def apache_mirrors - rd, wr = IO.pipe - buf = "" + mirrors, = Open3.capture3( + *curl_args(*_curl_opts, "--silent", "--location", "#{@url}&asjson=1"), + ) - pid = fork do - ENV.delete "HOMEBREW_CURL_VERBOSE" - rd.close - $stdout.reopen(wr) - $stderr.reopen(wr) - curl "#{@url}&asjson=1" - end - wr.close - - rd.readline if ARGV.verbose? # Remove Homebrew output - buf << rd.read until rd.eof? - rd.close - Process.wait(pid) - buf + JSON.parse(mirrors) end def _fetch return super if @tried_apache_mirror @tried_apache_mirror = true - mirrors = JSON.parse(apache_mirrors) + mirrors = apache_mirrors path_info = mirrors.fetch("path_info") @url = mirrors.fetch("preferred") + path_info @mirrors |= %W[https://archive.apache.org/dist/#{path_info}] @@ -460,7 +428,7 @@ end class CurlPostDownloadStrategy < CurlDownloadStrategy def _fetch base_url, data = @url.split("?") - curl base_url, "-d", data, "-C", downloaded_size, "-o", temporary_path + curl_download base_url, "--data", data, to: temporary_path end end @@ -530,7 +498,7 @@ class S3DownloadStrategy < CurlDownloadStrategy s3url = obj.public_url end - curl s3url, "-C", downloaded_size, "-o", temporary_path + curl_download s3url, to: temporary_path end end @@ -566,7 +534,7 @@ class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy end def _fetch - curl download_url, "-C", downloaded_size, "-o", temporary_path + curl_download download_url, to: temporary_path end private @@ -615,7 +583,7 @@ class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDo def _fetch # HTTP request header `Accept: application/octet-stream` is required. # Without this, the GitHub API will respond with metadata, not binary. - curl download_url, "-C", downloaded_size, "-o", temporary_path, "-H", "Accept: application/octet-stream" + curl_download download_url, "--header", "Accept: application/octet-stream", to: temporary_path end private @@ -915,18 +883,27 @@ class GitHubGitDownloadStrategy < GitDownloadStrategy def github_last_commit return if ENV["HOMEBREW_NO_GITHUB_API"] - output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ - "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" + output, _, status = curl_output( + "--silent", "--head", "--location", + "-H", "Accept: application/vnd.github.v3.sha", + "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{@ref}" + ) - commit = output[/^ETag: \"(\h+)\"/, 1] if status.success? + return unless status.success? + + commit = output[/^ETag: \"(\h+)\"/, 1] version.update_commit(commit) if commit commit end def multiple_short_commits_exist?(commit) return if ENV["HOMEBREW_NO_GITHUB_API"] - output, _, status = curl_output "-H", "Accept: application/vnd.github.v3.sha", \ - "-I", "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" + + output, _, status = curl_output( + "--silent", "--head", "--location", + "-H", "Accept: application/vnd.github.v3.sha", + "https://api.github.com/repos/#{@user}/#{@repo}/commits/#{commit}" + ) !(status.success? && output && output[/^Status: (200)/, 1] == "200") end @@ -1159,15 +1136,13 @@ class DownloadStrategyDetector SubversionDownloadStrategy when %r{^cvs://} CVSDownloadStrategy - when %r{^https?://(.+?\.)?googlecode\.com/hg} - MercurialDownloadStrategy - when %r{^hg://} + when %r{^hg://}, %r{^https?://(.+?\.)?googlecode\.com/hg} MercurialDownloadStrategy when %r{^bzr://} BazaarDownloadStrategy when %r{^fossil://} FossilDownloadStrategy - when %r{^http://svn\.apache\.org/repos/}, %r{^svn\+http://} + when %r{^svn\+http://}, %r{^http://svn\.apache\.org/repos/} SubversionDownloadStrategy when %r{^https?://(.+?\.)?sourceforge\.net/hgweb/} MercurialDownloadStrategy diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 8b4cddc591..22a7fe0232 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -416,7 +416,7 @@ class BuildError < RuntimeError puts - if issues && !issues.empty? + unless issues&.empty? puts "These open issues may also help:" puts issues.map { |i| "#{i["title"]} #{i["html_url"]}" }.join("\n") end @@ -555,12 +555,12 @@ end # raised when a single patch file is not found and apply hasn't been specified class MissingApplyError < RuntimeError; end -class BottleVersionMismatchError < RuntimeError - def initialize(bottle_file, bottle_version, formula, formula_version) +class BottleFormulaUnavailableError < RuntimeError + def initialize(bottle_path, formula_path) super <<-EOS.undent - Bottle version mismatch - Bottle: #{bottle_file} (#{bottle_version}) - Formula: #{formula.full_name} (#{formula_version}) + This bottle does not contain the formula file: + #{bottle_path} + #{formula_path} EOS end end diff --git a/Library/Homebrew/extend/ARGV.rb b/Library/Homebrew/extend/ARGV.rb index c6cb54f5da..63a0f3e40b 100644 --- a/Library/Homebrew/extend/ARGV.rb +++ b/Library/Homebrew/extend/ARGV.rb @@ -144,10 +144,10 @@ module HomebrewArgvExtension def value(name) arg_prefix = "--#{name}=" flag_with_value = find { |arg| arg.start_with?(arg_prefix) } - flag_with_value.strip_prefix(arg_prefix) if flag_with_value + flag_with_value&.strip_prefix(arg_prefix) end - # Returns an array of values that were given as a comma-seperated list. + # Returns an array of values that were given as a comma-separated list. # @see value def values(name) return unless val = value(name) @@ -236,7 +236,7 @@ module HomebrewArgvExtension def bottle_arch arch = value "bottle-arch" - arch.to_sym if arch + arch&.to_sym end def build_from_source? diff --git a/Library/Homebrew/extend/ENV.rb b/Library/Homebrew/extend/ENV.rb index 283e90b696..ea1b995011 100644 --- a/Library/Homebrew/extend/ENV.rb +++ b/Library/Homebrew/extend/ENV.rb @@ -28,7 +28,7 @@ module EnvActivation end def clear_sensitive_environment! - ENV.keys.each do |key| + ENV.each_key do |key| next unless /(cookie|key|token)/i =~ key ENV.delete key end diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb index b51ade48b9..15488ee191 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/ENV/std.rb b/Library/Homebrew/extend/ENV/std.rb index a2e800803f..4e5d0683ae 100644 --- a/Library/Homebrew/extend/ENV/std.rb +++ b/Library/Homebrew/extend/ENV/std.rb @@ -233,8 +233,8 @@ module Stdenv def make_jobs # '-j' requires a positive integral argument - if self["HOMEBREW_MAKE_JOBS"].to_i > 0 - self["HOMEBREW_MAKE_JOBS"].to_i + if (jobs = self["HOMEBREW_MAKE_JOBS"].to_i).positive? + jobs else Hardware::CPU.cores end diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 692fd36239..b4f0dfcac0 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -24,8 +24,7 @@ module Superenv end # @private - def self.bin - end + def self.bin; end def reset super @@ -138,7 +137,6 @@ module Superenv def determine_pkg_config_libdir PATH.new( - "/usr/lib/pkgconfig", homebrew_extra_pkg_config_paths, ).existing end @@ -325,11 +323,9 @@ module Superenv end end - def set_x11_env_if_installed - end + def set_x11_env_if_installed; end - def set_cpu_flags(*) - end + def set_cpu_flags(_, _ = "", _ = {}); end end require "extend/os/extend/ENV/super" diff --git a/Library/Homebrew/extend/cachable.rb b/Library/Homebrew/extend/cachable.rb new file mode 100644 index 0000000000..69d86ccb7b --- /dev/null +++ b/Library/Homebrew/extend/cachable.rb @@ -0,0 +1,9 @@ +module Cachable + def cache + @cache ||= {} + end + + def clear_cache + cache.clear + end +end diff --git a/Library/Homebrew/extend/fileutils.rb b/Library/Homebrew/extend/fileutils.rb index 52d4cbf511..ed5bfe6c33 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/os/mac/development_tools.rb b/Library/Homebrew/extend/os/mac/development_tools.rb index caa85ffca5..1931b398d9 100644 --- a/Library/Homebrew/extend/os/mac/development_tools.rb +++ b/Library/Homebrew/extend/os/mac/development_tools.rb @@ -9,7 +9,7 @@ class DevelopmentTools @locate[key] = if (located_tool = original_locate(tool)) located_tool elsif MacOS.version > :tiger - path = Utils.popen_read("/usr/bin/xcrun", "-no-cache", "-find", tool).chomp + path = Utils.popen_read("/usr/bin/xcrun", "-no-cache", "-find", tool, err: :close).chomp Pathname.new(path) if File.executable?(path) end end @@ -43,11 +43,16 @@ class DevelopmentTools end def custom_installation_instructions - if MacOS.version > :tiger + if MacOS.version > :leopard <<-EOS.undent Install GNU's GCC brew install gcc EOS + elsif MacOS.version > :tiger + <<-EOS.undent + Install GNU's GCC + brew install gcc@4.6 + EOS else # Tiger doesn't ship with apple-gcc42, and this is required to build # some software that doesn't build properly with FSF GCC. @@ -55,7 +60,7 @@ class DevelopmentTools Install Apple's GCC brew install apple-gcc42 or GNU's GCC - brew install gcc + brew install gcc@4.6 EOS end end @@ -77,10 +82,10 @@ class DevelopmentTools end end - def curl_handles_most_https_homepages? - # The system Curl is too old for some modern HTTPS homepages on + def curl_handles_most_https_certificates? + # The system Curl is too old for some modern HTTPS certificates on # older macOS versions. - MacOS.version >= :el_capitan + ENV["HOMEBREW_SYSTEM_CURL_TOO_OLD"].nil? end def subversion_handles_most_https_certificates? diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb index 0cdd7b1156..9f7b18b492 100644 --- a/Library/Homebrew/extend/os/mac/diagnostic.rb +++ b/Library/Homebrew/extend/os/mac/diagnostic.rb @@ -195,8 +195,9 @@ module Homebrew end def check_ruby_version - ruby_version = "2.0" - return if RUBY_VERSION[/\d\.\d/] == ruby_version + ruby_version = "2.3.3" + return if RUBY_VERSION == ruby_version + return if ARGV.homebrew_developer? && OS::Mac.prerelease? <<-EOS.undent Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew diff --git a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb index f97a2dbbbf..5872c2264f 100644 --- a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb +++ b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb @@ -28,7 +28,7 @@ module Superenv # @private def homebrew_extra_pkg_config_paths paths = \ - ["#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}"] + ["/usr/lib/pkgconfig", "#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}"] paths << "#{MacOS::X11.lib}/pkgconfig" << "#{MacOS::X11.share}/pkgconfig" if x11? paths end @@ -96,9 +96,13 @@ module Superenv self["SDKROOT"] = MacOS.sdk_path end - # Filter out symbols known not to be defined on 10.11 since GNU Autotools - # can't reliably figure this out with Xcode 8 on its own yet. - if MacOS.version == "10.11" && MacOS::Xcode.installed? && MacOS::Xcode.version >= "8.0" + # Filter out symbols known not to be defined since GNU Autotools can't + # reliably figure this out with Xcode 8 and above. + if MacOS.version == "10.12" && MacOS::Xcode.installed? && MacOS::Xcode.version >= "9.0" + %w[fmemopen futimens open_memstream utimensat].each do |s| + ENV["ac_cv_func_#{s}"] = "no" + end + elsif MacOS.version == "10.11" && MacOS::Xcode.installed? && MacOS::Xcode.version >= "8.0" %w[basename_r clock_getres clock_gettime clock_settime dirname_r getentropy mkostemp mkostemps timingsafe_bcmp].each do |s| ENV["ac_cv_func_#{s}"] = "no" diff --git a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb index 66e0387744..32e5774f69 100644 --- a/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb +++ b/Library/Homebrew/extend/os/mac/formula_cellar_checks.rb @@ -67,11 +67,19 @@ module FormulaCellarChecks checker = LinkageChecker.new(keg, formula) return unless checker.broken_dylibs? - problem_if_output <<-EOS.undent - The installation was broken. - Broken dylib links found: - #{checker.broken_dylibs.to_a * "\n "} + output = <<-EOS.undent + #{formula} has broken dynamic library links: + #{checker.broken_dylibs.to_a * "\n "} EOS + tab = Tab.for_keg(keg) + if tab.poured_from_bottle + output += <<-EOS.undent + Rebuild this from source with: + brew reinstall --build-from-source #{formula} + If that's successful, file an issue#{formula.tap ? " here:\n #{formula.tap.issues_url}" : "."} + EOS + end + problem_if_output output end def audit_installed diff --git a/Library/Homebrew/extend/os/mac/hardware/cpu.rb b/Library/Homebrew/extend/os/mac/hardware/cpu.rb index 22d118e1a5..b97c280cd2 100644 --- a/Library/Homebrew/extend/os/mac/hardware/cpu.rb +++ b/Library/Homebrew/extend/os/mac/hardware/cpu.rb @@ -50,6 +50,8 @@ module Hardware :broadwell when 0x37fc219f # Skylake :skylake + when 0x0f817246 # Kaby Lake + :kabylake else :dunno end diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index 767d83ff91..e3d6880ba6 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 @@ -186,7 +186,7 @@ class Pathname begin tf.chown(uid, gid) tf.chmod(old_stat.mode) - rescue Errno::EPERM + rescue Errno::EPERM # rubocop:disable Lint/HandleExceptions end File.rename(tf.path, self) diff --git a/Library/Homebrew/extend/string.rb b/Library/Homebrew/extend/string.rb index ae7a209dbf..b96f129949 100644 --- a/Library/Homebrew/extend/string.rb +++ b/Library/Homebrew/extend/string.rb @@ -60,7 +60,7 @@ module StringInreplaceExtension result end - # Looks for Makefile style variable defintions and replaces the + # Looks for Makefile style variable definitions and replaces the # value with "new_value", or removes the definition entirely. def change_make_var!(flag, new_value) return if gsub!(/^#{Regexp.escape(flag)}[ \t]*=[ \t]*(.*)$/, "#{flag}=#{new_value}", false) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 8cea85a995..61042aae76 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -472,7 +472,7 @@ class Formula return true if devel && tab.devel_version && tab.devel_version < devel.version if options[:fetch_head] - return false unless head && head.downloader.is_a?(VCSDownloadStrategy) + return false unless head&.downloader.is_a?(VCSDownloadStrategy) downloader = head.downloader downloader.shutup! unless ARGV.verbose? downloader.commit_outdated?(version.version.commit) @@ -1041,14 +1041,14 @@ class Formula # keg's formula is deleted. begin keg = Keg.for(path) - rescue NotAKegError, Errno::ENOENT + rescue NotAKegError, Errno::ENOENT # rubocop:disable Lint/HandleExceptions # file doesn't belong to any keg. else tab_tap = Tab.for_keg(keg).tap return false if tab_tap.nil? # this keg doesn't below to any core/tap formula, most likely coming from a DIY install. begin Formulary.factory(keg.name) - rescue FormulaUnavailableError + rescue FormulaUnavailableError # rubocop:disable Lint/HandleExceptions # formula for this keg is deleted, so defer to whitelist rescue TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError return false # this keg belongs to another formula @@ -1115,8 +1115,8 @@ class Formula # @private def unlock - @lock.unlock unless @lock.nil? - @oldname_lock.unlock unless @oldname_lock.nil? + @lock&.unlock + @oldname_lock&.unlock end def migration_needed? @@ -1182,7 +1182,8 @@ class Formula # Returns false if the formula wasn't installed with an alias. def installed_alias_target_changed? target = current_installed_alias_target - target && target.name != name + return false unless target + target.name != name end # Is this formula the target of an alias used to install an old formula? @@ -1378,12 +1379,13 @@ class Formula # An array of all installed {Formula} # @private def self.installed - @installed ||= racks.map do |rack| + @installed ||= racks.flat_map do |rack| begin Formulary.from_rack(rack) rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError + [] end - end.compact.uniq(&:name) + end.uniq(&:name) end def self.installed_with_alias_path(alias_path) @@ -1440,13 +1442,14 @@ class Formula # True if this formula is provided by Homebrew itself # @private def core_formula? - tap && tap.core_tap? + tap&.core_tap? end # True if this formula is provided by external Tap # @private def tap? - tap && !tap.core_tap? + return false unless tap + !tap.core_tap? end # @private @@ -1525,10 +1528,10 @@ class Formula "oldname" => oldname, "aliases" => aliases, "versions" => { - "stable" => (stable.version.to_s if stable), + "stable" => stable&.version.to_s, "bottle" => bottle ? true : false, - "devel" => (devel.version.to_s if devel), - "head" => (head.version.to_s if head), + "devel" => devel&.version.to_s, + "head" => head&.version.to_s, }, "revision" => revision, "version_scheme" => version_scheme, @@ -1570,7 +1573,7 @@ class Formula "root_url" => bottle_spec.root_url, } bottle_info["files"] = {} - bottle_spec.collector.keys.each do |os| + bottle_spec.collector.keys.each do |os| # rubocop:disable Performance/HashEachMethods checksum = bottle_spec.collector[os] bottle_info["files"][os] = { "url" => "#{bottle_spec.root_url}/#{Bottle::Filename.create(self, os, bottle_spec.rebuild)}", @@ -1613,6 +1616,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 +1630,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! @@ -1638,7 +1643,7 @@ class Formula with_logging("test") do test end - rescue Exception + rescue Exception # rubocop:disable Lint/RescueException staging.retain! if ARGV.debug? raise end @@ -1646,6 +1651,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 @@ -1662,8 +1668,7 @@ class Formula end # @private - def test - end + def test; end # @private def test_fixtures(file) @@ -1678,8 +1683,7 @@ class Formula # system "./configure", "--prefix=#{prefix}" # system "make", "install" # end - def install - end + def install; end protected @@ -1888,11 +1892,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 +1913,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 @@ -1924,29 +1931,29 @@ class Formula end end - def self.method_added(method) - case method - when :brew - raise "You cannot override Formula#brew in class #{name}" - when :test - define_method(:test_defined?) { true } - when :options - instance = allocate - - specs.each do |spec| - instance.options.each do |opt, desc| - spec.option(opt[/^--(.+)$/, 1], desc) - end - end - - remove_method(:options) - end - end - # The methods below define the formula DSL. class << self include BuildEnvironment::DSL + def method_added(method) + case method + when :brew + raise "You cannot override Formula#brew in class #{name}" + when :test + define_method(:test_defined?) { true } + when :options + instance = allocate + + specs.each do |spec| + instance.options.each do |opt, desc| + spec.option(opt[/^--(.+)$/, 1], desc) + end + end + + remove_method(:options) + end + end + # The reason for why this software is not linked (by default) to # {::HOMEBREW_PREFIX}. # @private diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index b2a5283818..7cd87e7513 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -58,13 +58,14 @@ class FormulaInstaller @options = Options.new @invalid_option_names = [] @requirement_messages = [] - - @@attempted ||= Set.new - @poured_bottle = false @pour_failed = false end + def self.attempted + @attempted ||= Set.new + end + # When no build tools are available and build flags are passed through ARGV, # it's necessary to interrupt the user before any sort of installation # can proceed. Only invoked when the user has no developer tools. @@ -84,14 +85,12 @@ class FormulaInstaller return false if @pour_failed - bottle = formula.bottle - return false unless bottle + return false if !formula.bottled? && !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 @@ -102,6 +101,7 @@ class FormulaInstaller return false end + bottle = formula.bottle_specification unless bottle.compatible_cellar? if install_bottle_options[:warn] opoo <<-EOS.undent @@ -146,7 +146,7 @@ class FormulaInstaller end def check_install_sanity - raise FormulaInstallationAlreadyAttemptedError, formula if @@attempted.include?(formula) + raise FormulaInstallationAlreadyAttemptedError, formula if self.class.attempted.include?(formula) return if ignore_deps? @@ -237,6 +237,15 @@ class FormulaInstaller raise CannotInstallFormulaError, message end + # Warn if a more recent version of this formula is available in the tap. + begin + if formula.pkg_version < (v = Formulary.factory(formula.full_name).pkg_version) + opoo "#{formula.full_name} #{v} is available and more recent than version #{formula.pkg_version}." + end + rescue FormulaUnavailableError + nil + end + check_conflicts if !pour_bottle? && !formula.bottle_unneeded? && !DevelopmentTools.installed? @@ -270,7 +279,7 @@ class FormulaInstaller oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip end - if formula.tap && !formula.tap.private? + unless formula.tap&.private? action = "#{formula.full_name} #{options}".strip Utils::Analytics.report_event("install", action) @@ -279,12 +288,12 @@ class FormulaInstaller end end - @@attempted << formula + self.class.attempted << formula if pour_bottle?(warn: true) begin pour - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException # any exceptions must leave us with nothing installed ignore_interrupts do formula.prefix.rmtree if formula.prefix.directory? @@ -310,7 +319,12 @@ class FormulaInstaller clean # Store the formula used to build the keg in the keg. - s = formula.path.read.gsub(/ bottle do.+?end\n\n?/m, "") + formula_contents = if formula.local_bottle_path + Utils::Bottles.formula_contents formula.local_bottle_path, name: formula.name + else + formula.path.read + end + s = formula_contents.gsub(/ bottle do.+?end\n\n?/m, "") brew_prefix = formula.prefix/".brew" brew_prefix.mkdir Pathname(brew_prefix/"#{formula.name}.rb").atomic_write(s) @@ -554,14 +568,14 @@ class FormulaInstaller oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}" fi.install fi.finish - rescue Exception + rescue Exception # rubocop:disable Lint/RescueException ignore_interrupts do tmp_keg.rename(installed_keg) if tmp_keg && !installed_keg.directory? linked_keg.link if keg_was_linked end raise else - ignore_interrupts { tmp_keg.rmtree if tmp_keg && tmp_keg.directory? } + ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? } end def caveats @@ -604,6 +618,12 @@ class FormulaInstaller # let's reset Utils.git_available? if we just installed git Utils.clear_git_available_cache if formula.name == "git" + + # use installed curl when it's needed and available + if formula.name == "curl" && + !DevelopmentTools.curl_handles_most_https_certificates? + ENV["HOMEBREW_CURL"] = formula.opt_bin/"curl" + end ensure unlock end @@ -679,8 +699,6 @@ class FormulaInstaller #{formula.specified_path} ].concat(build_argv) - Sandbox.print_sandbox_message if Sandbox.formula?(formula) - Utils.safe_fork do # Invalidate the current sudo timestamp in case a build script calls sudo. # Travis CI's Linux sudoless workers have a weird sudo that fails here. @@ -706,7 +724,7 @@ class FormulaInstaller if !formula.prefix.directory? || Keg.new(formula.prefix).empty_installation? raise "Empty installation" end - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException e.options = display_options(formula) if e.is_a?(BuildError) ignore_interrupts do # any exceptions must leave us with nothing installed @@ -767,7 +785,7 @@ class FormulaInstaller puts " brew link #{formula.name}" @show_summary_heading = true Homebrew.failed = true - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "An unexpected error occurred during the `brew link` step" puts "The formula built, but is not symlinked into #{HOMEBREW_PREFIX}" puts e @@ -798,7 +816,7 @@ class FormulaInstaller formula.plist_path.chmod 0644 log = formula.var/"log" log.mkpath if formula.plist.include? log.to_s - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "Failed to install plist file" ohai e, e.backtrace if debug? Homebrew.failed = true @@ -806,7 +824,7 @@ class FormulaInstaller def fix_dynamic_linkage(keg) keg.fix_dynamic_linkage - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "Failed to fix install linkage" puts "The formula built, but you may encounter issues using it or linking other" puts "formula against it." @@ -818,7 +836,7 @@ class FormulaInstaller def clean ohai "Cleaning" if verbose? Cleaner.new(formula).clean - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException opoo "The cleaning step did not complete successfully" puts "Still, the installation was successful, so we will link it into your prefix" ohai e, e.backtrace if debug? @@ -828,7 +846,7 @@ class FormulaInstaller def post_install Homebrew.run_post_install(formula) - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException opoo "The post-install step did not complete successfully" puts "You can try again using `brew postinstall #{formula.full_name}`" ohai e, e.backtrace if debug? @@ -888,27 +906,31 @@ class FormulaInstaller super end + def self.locked + @locked ||= [] + end + private attr_predicate :hold_locks? def lock - return unless (@@locked ||= []).empty? + return unless self.class.locked.empty? unless ignore_deps? formula.recursive_dependencies.each do |dep| - @@locked << dep.to_formula + self.class.locked << dep.to_formula end end - @@locked.unshift(formula) - @@locked.uniq! - @@locked.each(&:lock) + self.class.locked.unshift(formula) + self.class.locked.uniq! + self.class.locked.each(&:lock) @hold_locks = true end def unlock return unless hold_locks? - @@locked.each(&:unlock) - @@locked.clear + self.class.locked.each(&:unlock) + self.class.locked.clear @hold_locks = false end diff --git a/Library/Homebrew/formula_versions.rb b/Library/Homebrew/formula_versions.rb index bb68035679..c10c69e674 100644 --- a/Library/Homebrew/formula_versions.rb +++ b/Library/Homebrew/formula_versions.rb @@ -44,7 +44,7 @@ class FormulaVersions # continue walking the history ohai "#{e} in #{name} at revision #{rev}", e.backtrace if ARGV.debug? rescue FormulaUnavailableError - # Suppress this error + return ensure Homebrew.raise_deprecation_exceptions = false end diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 195d15cece..fddeac6312 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -1,18 +1,19 @@ require "digest/md5" require "tap" +require "extend/cachable" # The Formulary is responsible for creating instances of Formula. # It is not meant to be used directly from formulae. module Formulary - FORMULAE = {} + extend Cachable def self.formula_class_defined?(path) - FORMULAE.key?(path) + cache.key?(path) end def self.formula_class_get(path) - FORMULAE.fetch(path) + cache.fetch(path) end def self.load_formula(name, path, contents, namespace) @@ -44,7 +45,7 @@ module Formulary contents = path.open("r") { |f| ensure_utf8_encoding(f).read } namespace = "FormulaNamespace#{Digest::MD5.hexdigest(path.to_s)}" klass = load_formula(name, path, contents, namespace) - FORMULAE[path] = klass + cache[path] = klass end if IO.method_defined?(:set_encoding) @@ -122,14 +123,18 @@ module Formulary super name, Formulary.path(full_name) end - def get_formula(spec, alias_path: nil) - formula = super - formula.local_bottle_path = @bottle_filename - formula_version = formula.pkg_version - bottle_version = Utils::Bottles.resolve_version(@bottle_filename) - unless formula_version == bottle_version - raise BottleVersionMismatchError.new(@bottle_filename, bottle_version, formula, formula_version) + def get_formula(spec, **) + contents = Utils::Bottles.formula_contents @bottle_filename, name: name + formula = begin + Formulary.from_contents name, @bottle_filename, contents, spec + rescue FormulaUnreadableError => e + opoo <<-EOS.undent + Unreadable formula in #{@bottle_filename}: + #{e} + EOS + super end + formula.local_bottle_path = @bottle_filename formula end end @@ -165,7 +170,7 @@ module Formulary def load_file HOMEBREW_CACHE_FORMULA.mkpath FileUtils.rm_f(path) - curl url, "-o", path + curl_download url, to: path super rescue MethodDeprecatedError => e if url =~ %r{github.com/([\w-]+)/homebrew-([\w-]+)/} diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 9f79781b41..32b5377a0e 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -45,11 +45,15 @@ module Homebrew @failed == true end - attr_writer :raise_deprecation_exceptions + attr_writer :raise_deprecation_exceptions, :auditing def raise_deprecation_exceptions? @raise_deprecation_exceptions == true end + + def auditing? + @auditing == true + end end end diff --git a/Library/Homebrew/gpg.rb b/Library/Homebrew/gpg.rb index cb9e367dfc..de2089ddad 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.0") || - gpg_version == Version.create("2.1") + gpg_version >= Version.create("2.0") end end @@ -20,7 +19,7 @@ class Gpg find_gpg("gpg2") end - GPG_EXECUTABLE = gpg2 || gpg + GPG_EXECUTABLE = gpg || gpg2 def self.available? File.executable?(GPG_EXECUTABLE.to_s) @@ -41,8 +40,27 @@ class Gpg 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/install_renamed.rb b/Library/Homebrew/install_renamed.rb index 5e200244f3..dc5d4cda8b 100644 --- a/Library/Homebrew/install_renamed.rb +++ b/Library/Homebrew/install_renamed.rb @@ -16,12 +16,12 @@ module InstallRenamed end end - def +(path) - super(path).extend(InstallRenamed) + def +(other) + super(other).extend(InstallRenamed) end - def /(path) - super(path).extend(InstallRenamed) + def /(other) + super(other).extend(InstallRenamed) end private diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 8fcbecfbda..677a97c85b 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -253,6 +253,11 @@ class Keg FileUtils.rm_rf bad_tap_opt if bad_tap_opt.directory? end + aliases.each do |a| + alias_symlink = opt/a + alias_symlink.delete if alias_symlink.symlink? || alias_symlink.exist? + end + Pathname.glob("#{opt_record}@*").each do |a| a = a.basename next if aliases.include?(a) @@ -333,7 +338,7 @@ class Keg dir if dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") } when :fish then path/"share/fish/vendor_completions.d" end - dir && dir.directory? && !dir.children.empty? + dir&.directory? && !dir.children.empty? end def functions_installed?(shell) diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb index ad4c010210..71773db816 100644 --- a/Library/Homebrew/keg_relocate.rb +++ b/Library/Homebrew/keg_relocate.rb @@ -124,6 +124,7 @@ class Keg next true if pn.symlink? next true if pn.directory? next false if pn.basename.to_s == "orig-prefix.txt" # for python virtualenvs + next true if pn == self/".brew/#{name}.rb" next true if Metafiles::EXTENSIONS.include?(pn.extname) if pn.text_executable? text_files << pn @@ -156,7 +157,7 @@ class Keg libtool_files = [] path.find do |pn| - next if pn.symlink? || pn.directory? || pn.extname != ".la" + next if pn.symlink? || pn.directory? || ![".la", ".lai"].include?(pn.extname) libtool_files << pn end libtool_files diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb index 0f8e3a4e62..bfec556d03 100644 --- a/Library/Homebrew/language/python.rb +++ b/Library/Homebrew/language/python.rb @@ -23,7 +23,7 @@ module Language else homebrew_site_packages(version) end - block.call python, version if block + block&.call python, version end ENV["PYTHONPATH"] = original_pythonpath end diff --git a/Library/Homebrew/locale.rb b/Library/Homebrew/locale.rb index 5e778f3b4d..c430d11b64 100644 --- a/Library/Homebrew/locale.rb +++ b/Library/Homebrew/locale.rb @@ -2,9 +2,9 @@ class Locale class ParserError < StandardError end - LANGUAGE_REGEX = /(?:[a-z]{2,3})/ # ISO 639-1 or ISO 639-2 - REGION_REGEX = /(?:[A-Z]{2})/ # ISO 3166-1 - SCRIPT_REGEX = /(?:[A-Z][a-z]{3})/ # ISO 15924 + LANGUAGE_REGEX = /(?:[a-z]{2,3})/ # ISO 639-1 or ISO 639-2 + REGION_REGEX = /(?:[A-Z]{2}|\d{3})/ # ISO 3166-1 or UN M.49 + SCRIPT_REGEX = /(?:[A-Z][a-z]{3})/ # ISO 15924 LOCALE_REGEX = /\A((?:#{LANGUAGE_REGEX}|#{REGION_REGEX}|#{SCRIPT_REGEX})(?:\-|$)){1,3}\Z/ @@ -44,8 +44,6 @@ class Locale raise ParserError, "'#{value}' does not match #{regex}" unless value =~ regex instance_variable_set(:"@#{key}", value) end - - self end def include?(other) diff --git a/Library/Homebrew/manpages/brew-cask.1.md b/Library/Homebrew/manpages/brew-cask.1.md index bfb9cd7a53..1c7c198b07 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 @@ -19,7 +19,7 @@ names, and other aspects of this manual are still subject to change. ## FREQUENTLY USED COMMANDS - * `install` [--force] [--skip-cask-deps] [--require-sha] [ ... ]: + * `install` [--force] [--skip-cask-deps] [--require-sha] [--language=[, ... ]] [ ... ]: Install Cask identified by . * `uninstall` [--force] [ ... ]: @@ -34,7 +34,7 @@ names, and other aspects of this manual are still subject to change. ## COMMANDS - * `audit` [ ... ]: + * `audit` [--language=[, ... ]] [ ... ]: Check the given Casks for installability. If no tokens are given on the command line, all Casks are audited. @@ -85,7 +85,7 @@ names, and other aspects of this manual are still subject to change. If is given, summarize the staged files associated with the given Cask. - + * `outdated` [--greedy] [--verbose|--quiet] [ ...]: 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` [ | //]: - Without an argument, display all Casks available for install; otherwise - perform a substring search of known Cask tokens for or, if the - text is delimited by slashes (//), 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 or, + if the text is delimited by slashes (//), it is interpreted as a Ruby regular expression. * `style` [--fix] [ ... ]: @@ -166,6 +167,9 @@ in a future version. * `--appdir=`: Target location for Applications. The default value is `/Applications`. + * `--language=[, ... ]]`: + Set language of the Cask to install. The first matching language is used, otherwise the default language on the Cask. The default value is the `language of your system`. + * `--colorpickerdir=`: Target location for Color Pickers. The default value is `~/Library/ColorPickers`. @@ -255,7 +259,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/migrator.rb b/Library/Homebrew/migrator.rb index 7ecdafe2fb..0eb7492dfd 100644 --- a/Library/Homebrew/migrator.rb +++ b/Library/Homebrew/migrator.rb @@ -100,7 +100,7 @@ class Migrator begin migrator = Migrator.new(formula) migrator.migrate - rescue Exception => e + rescue => e onoe e end end @@ -196,7 +196,7 @@ class Migrator update_tabs rescue Interrupt ignore_interrupts { backup_oldname } - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "Error occurred while migrating." puts e puts e.backtrace if ARGV.debug? @@ -304,7 +304,7 @@ class Migrator puts puts "You can try again using:" puts " brew link #{formula.name}" - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "An unexpected error occurred during linking" puts e puts e.backtrace if ARGV.debug? diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb index 4d90ddb618..a3d182a2bf 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/official_taps.rb b/Library/Homebrew/official_taps.rb index dcb65d9f80..ed966c804e 100644 --- a/Library/Homebrew/official_taps.rb +++ b/Library/Homebrew/official_taps.rb @@ -1,5 +1,4 @@ OFFICIAL_TAPS = %w[ - apache nginx php science @@ -17,6 +16,7 @@ OFFICIAL_CMD_TAPS = { }.freeze DEPRECATED_OFFICIAL_TAPS = %w[ + apache binary completions devel-only diff --git a/Library/Homebrew/options.rb b/Library/Homebrew/options.rb index 9f12535317..05dd643ffe 100644 --- a/Library/Homebrew/options.rb +++ b/Library/Homebrew/options.rb @@ -69,29 +69,29 @@ class Options @options.each(*args, &block) end - def <<(o) - @options << o + def <<(other) + @options << other self end - def +(o) - self.class.new(@options + o) + def +(other) + self.class.new(@options + other) end - def -(o) - self.class.new(@options - o) + def -(other) + self.class.new(@options - other) end - def &(o) - self.class.new(@options & o) + def &(other) + self.class.new(@options & other) end - def |(o) - self.class.new(@options | o) + def |(other) + self.class.new(@options | other) end - def *(arg) - @options.to_a * arg + def *(other) + @options.to_a * other end def empty? diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 5074665fca..853f75140f 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -11,7 +11,7 @@ module OS module Mac module_function - ::MacOS = self # rubocop:disable Style/ConstantName + ::MacOS = self # rubocop:disable Naming/ConstantName raise "Loaded OS::Mac on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"] @@ -34,12 +34,12 @@ module OS def prerelease? # TODO: bump version when new OS is released - version >= "10.13" + version >= "10.14" end def outdated_release? # TODO: bump version when new OS is released - version < "10.10" + version < "10.11" end def cat @@ -104,7 +104,7 @@ module OS # Returns the path to an SDK or nil, following the rules set by #sdk. def sdk_path(v = nil) s = sdk(v) - s.path unless s.nil? + s&.path end # See these issues for some history: @@ -129,8 +129,8 @@ module OS paths << path if path.exist? end - # Finally, some users make their MacPorts or Fink directorie - # read-only in order to try out Homebrew, but this doens't work as + # Finally, some users make their MacPorts or Fink directories + # read-only in order to try out Homebrew, but this doesn't work as # some build scripts error out when trying to read from these now # unreadable paths. %w[/sw /opt/local].map { |p| Pathname.new(p) }.each do |path| @@ -229,7 +229,9 @@ module OS end def app_with_bundle_id(*ids) - path = mdfind(*ids).first + path = mdfind(*ids) + .reject { |p| p.include?("/Backups.backupdb/") } + .first Pathname.new(path) unless path.nil? || path.empty? end diff --git a/Library/Homebrew/os/mac/linkage_checker.rb b/Library/Homebrew/os/mac/linkage_checker.rb index 7868278523..f6aa4c2f33 100644 --- a/Library/Homebrew/os/mac/linkage_checker.rb +++ b/Library/Homebrew/os/mac/linkage_checker.rb @@ -5,7 +5,7 @@ require "formula" class LinkageChecker attr_reader :keg, :formula attr_reader :brewed_dylibs, :system_dylibs, :broken_dylibs, :variable_dylibs - attr_reader :undeclared_deps, :reverse_links + attr_reader :undeclared_deps, :unnecessary_deps, :reverse_links def initialize(keg, formula = nil) @keg = keg @@ -16,6 +16,7 @@ class LinkageChecker @variable_dylibs = Set.new @undeclared_deps = [] @reverse_links = Hash.new { |h, k| h[k] = Set.new } + @unnecessary_deps = [] check_dylibs end @@ -51,7 +52,7 @@ class LinkageChecker end end - @undeclared_deps = check_undeclared_deps if formula + @undeclared_deps, @unnecessary_deps = check_undeclared_deps if formula end def check_undeclared_deps @@ -77,6 +78,12 @@ class LinkageChecker a <=> b end end + unnecessary_deps = declared_dep_names.reject do |full_name| + name = full_name.split("/").last + next true if Formula[name].bin.directory? + @brewed_dylibs.keys.map { |x| x.split("/").last }.include?(name) + end + [undeclared_deps, unnecessary_deps] end def display_normal_output @@ -84,7 +91,8 @@ class LinkageChecker display_items "Homebrew libraries", @brewed_dylibs display_items "Variable-referenced libraries", @variable_dylibs display_items "Missing libraries", @broken_dylibs - display_items "Possible undeclared dependencies", @undeclared_deps + display_items "Undeclared dependencies with linkage", @undeclared_deps + display_items "Dependencies with no linkage", @unnecessary_deps end def display_reverse_output @@ -102,6 +110,7 @@ class LinkageChecker def display_test_output display_items "Missing libraries", @broken_dylibs + display_items "Possible unnecessary dependencies", @unnecessary_deps puts "No broken dylib links" if @broken_dylibs.empty? end @@ -113,6 +122,10 @@ class LinkageChecker !@undeclared_deps.empty? end + def unnecessary_deps? + !@unnecessary_deps.empty? + end + private # Whether or not dylib is a harmless broken link, meaning that it's diff --git a/Library/Homebrew/os/mac/version.rb b/Library/Homebrew/os/mac/version.rb index 89016bb4d1..db6dfcb1a6 100644 --- a/Library/Homebrew/os/mac/version.rb +++ b/Library/Homebrew/os/mac/version.rb @@ -12,6 +12,7 @@ module OS mountain_lion: "10.8", lion: "10.7", snow_leopard: "10.6", + leopard_64: "10.5", leopard: "10.5", tiger: "10.4", }.freeze diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index e23a7cf3d9..5071aafcf0 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -183,7 +183,7 @@ module OS end module CLT - extend self + module_function STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo".freeze FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI".freeze @@ -216,7 +216,7 @@ module OS # on the older supported platform for that Xcode release, i.e there's no # CLT package for 10.11 that contains the Clang version from Xcode 8. case MacOS.version - when "10.13" then "900.0.31" + when "10.13" then "900.0.37" when "10.12" then "802.0.42" when "10.11" then "800.0.42.1" when "10.10" then "700.1.81" diff --git a/Library/Homebrew/patch.rb b/Library/Homebrew/patch.rb index 7045adf5e6..8a1b00930d 100644 --- a/Library/Homebrew/patch.rb +++ b/Library/Homebrew/patch.rb @@ -60,8 +60,7 @@ class EmbeddedPatch false end - def contents - end + def contents; end def apply data = contents.gsub("HOMEBREW_PREFIX", HOMEBREW_PREFIX) @@ -87,10 +86,13 @@ class DATAPatch < EmbeddedPatch def contents data = "" path.open("rb") do |f| - begin + loop do line = f.gets - end until line.nil? || line =~ /^__END__$/ - data << line while line = f.gets + break if line.nil? || line =~ /^__END__$/ + end + while line = f.gets + data << line + end end data end diff --git a/Library/Homebrew/pkg_version.rb b/Library/Homebrew/pkg_version.rb index 761a349fda..b68d78cf8e 100644 --- a/Library/Homebrew/pkg_version.rb +++ b/Library/Homebrew/pkg_version.rb @@ -23,7 +23,7 @@ class PkgVersion end def to_s - if revision > 0 + if revision.positive? "#{version}_#{revision}" else version.to_s diff --git a/Library/Homebrew/postinstall.rb b/Library/Homebrew/postinstall.rb index 0b6d8f6b01..53a5b7e751 100644 --- a/Library/Homebrew/postinstall.rb +++ b/Library/Homebrew/postinstall.rb @@ -14,7 +14,7 @@ begin formula = ARGV.resolved_formulae.first formula.extend(Debrew::Formula) if ARGV.debug? formula.run_post_install -rescue Exception => e +rescue Exception => e # rubocop:disable Lint/RescueException Marshal.dump(e, error_pipe) error_pipe.close exit! 1 diff --git a/Library/Homebrew/readall.rb b/Library/Homebrew/readall.rb index 3595c16be4..5f4fd947c6 100644 --- a/Library/Homebrew/readall.rb +++ b/Library/Homebrew/readall.rb @@ -52,7 +52,7 @@ module Readall Formulary.factory(file) rescue Interrupt raise - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException onoe "Invalid formula: #{file}" puts e failed = true diff --git a/Library/Homebrew/requirements/gpg2_requirement.rb b/Library/Homebrew/requirements/gpg2_requirement.rb index 97fabcca0d..ebdd71f6ef 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" - # MacGPG2/GPGTools installs GnuPG 2.0.x as a vanilla `gpg` symlink - # pointing to `gpg2`, as do we. Ensure we're actually using a 2.0 `gpg`. - # Support both the 2.0.x "stable" and 2.1.x "modern" series. - satisfy(build_env: false) { Gpg.gpg2 || Gpg.gpg } + # 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/requirements/java_requirement.rb b/Library/Homebrew/requirements/java_requirement.rb index ab6dca51df..de3a33eb45 100644 --- a/Library/Homebrew/requirements/java_requirement.rb +++ b/Library/Homebrew/requirements/java_requirement.rb @@ -69,14 +69,14 @@ class JavaRequirement < Requirement rescue FormulaUnavailableError nil end - javas << jdk.bin/"java" if jdk && jdk.installed? + javas << jdk.bin/"java" if jdk&.installed? javas << which("java") javas end def preferred_java possible_javas.detect do |java| - next false unless java && java.executable? + next false unless java&.executable? next true unless @version next true if satisfies_version(java) end diff --git a/Library/Homebrew/requirements/ruby_requirement.rb b/Library/Homebrew/requirements/ruby_requirement.rb index acc6559248..a9ec8c42d2 100644 --- a/Library/Homebrew/requirements/ruby_requirement.rb +++ b/Library/Homebrew/requirements/ruby_requirement.rb @@ -41,9 +41,7 @@ class RubyRequirement < Requirement def rubies rubies = which_all("ruby") ruby_formula = Formula["ruby"] - if ruby_formula && ruby_formula.installed? - rubies.unshift ruby_formula.bin/"ruby" - end + rubies.unshift ruby_formula.bin/"ruby" if ruby_formula&.installed? rubies.uniq end diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb index b1144e075d..8dc49bb459 100644 --- a/Library/Homebrew/rubocops.rb +++ b/Library/Homebrew/rubocops.rb @@ -11,3 +11,4 @@ require_relative "./rubocops/conflicts_cop" require_relative "./rubocops/options_cop" require_relative "./rubocops/urls_cop" require_relative "./rubocops/lines_cop" +require_relative "./rubocops/class_cop" diff --git a/Library/Homebrew/rubocops/class_cop.rb b/Library/Homebrew/rubocops/class_cop.rb new file mode 100644 index 0000000000..dad81abfc5 --- /dev/null +++ b/Library/Homebrew/rubocops/class_cop.rb @@ -0,0 +1,41 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAudit + class ClassName < FormulaCop + DEPRECATED_CLASSES = %w[ + GithubGistFormula + ScriptFileFormula + AmazonWebServicesFormula + ].freeze + + def audit_formula(_node, _class_node, parent_class_node, _body_node) + parent_class = class_name(parent_class_node) + return unless DEPRECATED_CLASSES.include?(parent_class) + problem "#{parent_class} is deprecated, use Formula instead" + end + + private + + def autocorrect(node) + lambda do |corrector| + corrector.replace(node.source_range, "Formula") + end + end + end + end + + module FormulaAuditStrict + # - `test do ..end` should be defined in the formula + class Test < FormulaCop + MSG = "A `test do` test block should be added".freeze + + def audit_formula(_node, _class_node, _parent_class_node, body_node) + return if find_block(body_node, :test) + problem MSG + end + end + end + end +end diff --git a/Library/Homebrew/rubocops/components_order_cop.rb b/Library/Homebrew/rubocops/components_order_cop.rb index f1179d9a4d..3bf2ede160 100644 --- a/Library/Homebrew/rubocops/components_order_cop.rb +++ b/Library/Homebrew/rubocops/components_order_cop.rb @@ -87,8 +87,8 @@ module RuboCop # preceding_comp_arr: array containing components of same type order_idx, curr_p_idx, preceding_comp_arr = get_state(node1) - # curr_p_idx > 0 means node1 needs to be grouped with its own kind - if curr_p_idx > 0 + # curr_p_idx.positive? means node1 needs to be grouped with its own kind + if curr_p_idx.positive? node2 = preceding_comp_arr[curr_p_idx - 1] indentation = " " * (start_column(node2) - line_start_column(node2)) line_breaks = node2.multiline? ? "\n\n" : "\n" diff --git a/Library/Homebrew/rubocops/conflicts_cop.rb b/Library/Homebrew/rubocops/conflicts_cop.rb index c1b8015599..6f05d05673 100644 --- a/Library/Homebrew/rubocops/conflicts_cop.rb +++ b/Library/Homebrew/rubocops/conflicts_cop.rb @@ -18,7 +18,7 @@ module RuboCop def audit_formula(_node, _class_node, _parent_class_node, body) return unless versioned_formula? - problem MSG if !formula_file_name.start_with?(*WHITELIST) && + problem MSG if !@formula_name.start_with?(*WHITELIST) && method_called_ever?(body, :conflicts_with) end end diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb index 76c7e43e9d..63c75194cf 100644 --- a/Library/Homebrew/rubocops/extend/formula_cop.rb +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -1,4 +1,5 @@ require "parser/current" +require_relative "../../extend/string" module RuboCop module Cop @@ -13,7 +14,7 @@ module RuboCop return unless formula_class?(node) return unless respond_to?(:audit_formula) class_node, parent_class_node, @body = *node - @formula_name = class_name(class_node) + @formula_name = Pathname.new(@file_path).basename(".rb").to_s audit_formula(node, class_node, parent_class_node, @body) end @@ -110,11 +111,15 @@ module RuboCop # Matches a method with a receiver, # EX: to match `Formula.factory(name)` # call `find_instance_method_call(node, "Formula", :factory)` + # EX: to match `build.head?` + # call `find_instance_method_call(node, :build, :head?)` # yields to a block with matching method node 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 && (method.receiver.const_name == instance || method.receiver.method_name == instance) + next if method.receiver.nil? + next if method.receiver.const_name != instance && + method.receiver.method_name != instance @offense_source_range = method.source_range @offensive_node = method return true unless block_given? @@ -146,17 +151,14 @@ module RuboCop case type when :required - type_match = !node.method_args.nil? && - (node.method_args.first.str_type? || node.method_args.first.sym_type?) + type_match = required_dependency?(node) if type_match && !name_match - name_match = node_equals?(node.method_args.first, name) + name_match = required_dependency_name?(node, name) end when :build, :optional, :recommended, :run - type_match = !node.method_args.nil? && - node.method_args.first.hash_type? && - node.method_args.first.values.first.children.first == type + type_match = dependency_type_hash_match?(node, type) if type_match && !name_match - name_match = node_equals?(node.method_args.first.keys.first.children.first, name) + name_match = dependency_name_hash_match?(node, name) end else type_match = false @@ -183,6 +185,22 @@ module RuboCop nil end + def_node_search :required_dependency?, <<-EOS.undent + (send nil :depends_on ({str sym} _)) + EOS + + def_node_search :required_dependency_name?, <<-EOS.undent + (send nil :depends_on ({str sym} %1)) + EOS + + def_node_search :dependency_type_hash_match?, <<-EOS.undent + (hash (pair ({str sym} _) ({str sym} %1))) + EOS + + def_node_search :dependency_name_hash_match?, <<-EOS.undent + (hash (pair ({str sym} %1) ({str sym} _))) + EOS + # To compare node with appropriate Ruby variable def node_equals?(node, var) node == Parser::CurrentRuby.parse(var.inspect) @@ -309,7 +327,7 @@ module RuboCop # Returns the array of arguments of the method_node def parameters(method_node) - method_node.method_args if method_node.send_type? || method_node.block_type? + method_node.arguments if method_node.send_type? || method_node.block_type? end # Returns true if the given parameters are present in method call @@ -415,12 +433,7 @@ module RuboCop # Returns true if the formula is versioned def versioned_formula? - formula_file_name.include?("@") || @formula_name.match(/AT\d+/) - end - - # Returns filename of the formula without the extension - def formula_file_name - File.basename(processed_source.buffer.name, ".rb") + @formula_name.include?("@") end # Returns printable component name @@ -443,7 +456,14 @@ module RuboCop def formula_class?(node) _, class_node, = *node - class_node && string_content(class_node) == "Formula" + class_names = %w[ + Formula + GithubGistFormula + ScriptFileFormula + AmazonWebServicesFormula + ] + + class_node && class_names.include?(string_content(class_node)) end def file_path_allowed? diff --git a/Library/Homebrew/rubocops/lines_cop.rb b/Library/Homebrew/rubocops/lines_cop.rb index ef199fb6ed..97bc45bc01 100644 --- a/Library/Homebrew/rubocops/lines_cop.rb +++ b/Library/Homebrew/rubocops/lines_cop.rb @@ -22,19 +22,12 @@ module RuboCop 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)}" + problem "Use a space in class inheritance: class #{@formula_name.capitalize} < #{class_name(parent_class_node)}" end end class Comments < FormulaCop def audit_formula(_node, _class_node, _parent_class_node, _body_node) - # Commented-out cmake support from default template - audit_comments do |comment| - next unless comment.include?('# system "cmake') - problem "Commented cmake call found" - end - - # Comments from default template audit_comments do |comment| [ "# PLEASE REMOVE", @@ -45,6 +38,7 @@ module RuboCop "# 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" @@ -55,7 +49,7 @@ module RuboCop audit_comments do |comment| # Commented-out depends_on next unless comment =~ /#\s*depends_on\s+(.+)\s*$/ - problem "Commented-out dep #{Regexp.last_match(1)}" + problem "Commented-out dependency #{Regexp.last_match(1)}" end end end @@ -75,33 +69,33 @@ module RuboCop problem "\"inreplace do |s|\" is preferred over \"|#{block_arg.source}|\"." end - [:rebuild, :version_scheme].each do |m| - find_method_with_args(body_node, m, 0) do - problem "'#{m} 0' should be removed" + [:rebuild, :version_scheme].each do |method_name| + find_method_with_args(body_node, method_name, 0) do + problem "'#{method_name} 0' should be removed" end end - [:mac?, :linux?].each do |m| + [:mac?, :linux?].each do |method_name| next unless formula_tap == "homebrew-core" - find_instance_method_call(body_node, "OS", m) do |check| + find_instance_method_call(body_node, "OS", method_name) do |check| problem "Don't use #{check.source}; Homebrew/core only supports macOS" end end - [:debug?, :verbose?, :value].each do |m| - find_instance_method_call(body_node, "ARGV", m) do + [:debug?, :verbose?, :value].each do |method_name| + find_instance_method_call(body_node, "ARGV", method_name) do problem "Use build instead of ARGV to check options" end end - find_instance_method_call(body_node, :man, :+) do |m| - next unless match = regex_match_group(parameters(m).first, %r{man[1-8]}) - problem "\"#{m.source}\" should be \"#{match[0]}\"" + find_instance_method_call(body_node, :man, :+) do |method| + next unless match = regex_match_group(parameters(method).first, %r{man[1-8]}) + problem "\"#{method.source}\" should be \"#{match[0]}\"" end # Avoid hard-coding compilers - find_every_method_call_by_name(body_node, :system).each do |m| - param = parameters(m).first + find_every_method_call_by_name(body_node, :system).each do |method| + param = parameters(method).first if match = regex_match_group(param, %r{(/usr/bin/)?(gcc|llvm-gcc|clang)\s?}) problem "Use \"\#{ENV.cc}\" instead of hard-coding \"#{match[2]}\"" elsif match = regex_match_group(param, %r{(/usr/bin/)?((g|llvm-g|clang)\+\+)\s?}) @@ -109,8 +103,8 @@ module RuboCop end end - find_instance_method_call(body_node, "ENV", :[]=) do |m| - param = parameters(m)[1] + find_instance_method_call(body_node, "ENV", :[]=) do |method| + param = parameters(method)[1] if match = regex_match_group(param, %r{(/usr/bin/)?(gcc|llvm-gcc|clang)\s?}) problem "Use \"\#{ENV.cc}\" instead of hard-coding \"#{match[2]}\"" elsif match = regex_match_group(param, %r{(/usr/bin/)?((g|llvm-g|clang)\+\+)\s?}) @@ -136,29 +130,29 @@ module RuboCop end end - find_every_method_call_by_name(body_node, :depends_on).each do |m| - key, value = destructure_hash(parameters(m).first) + find_every_method_call_by_name(body_node, :depends_on).each do |method| + key, value = destructure_hash(parameters(method).first) next if (key.nil? || value.nil?) next unless match = regex_match_group(value, %r{(lua|perl|python|ruby)(\d*)}) - problem "#{match[1]} modules should be vendored rather than use deprecated #{m.source}`" + problem "#{match[1]} modules should be vendored rather than use deprecated #{method.source}`" end - find_every_method_call_by_name(body_node, :system).each do |m| - next unless match = regex_match_group(parameters(m).first, %r{(env|export)(\s+)?}) + find_every_method_call_by_name(body_node, :system).each do |method| + next unless match = regex_match_group(parameters(method).first, %r{(env|export)(\s+)?}) problem "Use ENV instead of invoking '#{match[1]}' to modify the environment" end - find_every_method_call_by_name(body_node, :depends_on).each do |m| - next if modifier?(m.parent) - param = parameters(m).first + find_every_method_call_by_name(body_node, :depends_on).each do |method| + next if modifier?(method.parent) + param = parameters(method).first dep, option = hash_dep(param) next if dep.nil? || option.nil? offending_node(param) problem "Dependency #{string_content(dep)} should not use option #{string_content(option)}" end - find_instance_method_call(body_node, :version, :==) do |m| - next unless parameters_passed?(m, "HEAD") + find_instance_method_call(body_node, :version, :==) do |method| + next unless parameters_passed?(method, "HEAD") problem "Use 'build.head?' instead of inspecting 'version'" end @@ -167,8 +161,8 @@ module RuboCop problem "Use `depends_on :fortran` instead of `ENV.fortran`" end - find_instance_method_call(body_node, "ARGV", :include?) do |m| - param = parameters(m).first + find_instance_method_call(body_node, "ARGV", :include?) do |method| + param = parameters(method).first next unless match = regex_match_group(param, %r{--(HEAD|devel)}) problem "Use \"if build.#{match[1].downcase}?\" instead" end @@ -202,14 +196,21 @@ module RuboCop problem "'fails_with :llvm' is now a no-op so should be removed" end - find_method_with_args(body_node, :system, /^(otool|install_name_tool|lipo)$/) do + find_method_with_args(body_node, :system, /(otool|install_name_tool|lipo)/) do + next if @formula_name == "cctools" problem "Use ruby-macho instead of calling #{@offensive_node.source}" end - # - find_method_with_args(body_node, :system, /npm/, /install/) do |m| - next if @formula_name =~ /^kibana(\@\d+(\.\d+)?)?$/ - problem "Use Language::Node for npm install args" unless languageNode?(m) + + find_every_method_call_by_name(body_node, :system).each do |method_node| + # Skip Kibana: npm cache edge (see formula for more details) + next if @formula_name =~ /^kibana(\@\d+(\.\d+)?)?$/i + first_param, second_param = parameters(method_node) + next if !node_equals?(first_param, "npm") || + !node_equals?(second_param, "install") + offending_node(method_node) + problem "Use Language::Node for npm install args" unless languageNodeModule?(method_node) end + if find_method_def(body_node, :test) problem "Use new-style test definitions (test do)" end @@ -219,12 +220,15 @@ module RuboCop end find_method_with_args(body_node, :skip_clean, :all) do - problem "`skip_clean :all` is deprecated; brew no longer strips symbols\n" \ - "\tPass explicit paths to prevent Homebrew from removing empty folders." + problem <<-EOS.undent.chomp + `skip_clean :all` is deprecated; brew no longer strips symbols + Pass explicit paths to prevent Homebrew from removing empty folders. + EOS end find_instance_method_call(body_node, :build, :universal?) do - problem "macOS has been 64-bit only so build.universal? is deprecated." + next if @formula_name == "wine" + problem "macOS has been 64-bit only since 10.6 so build.universal? is deprecated." end find_instance_method_call(body_node, "ENV", :universal_binary) do @@ -235,85 +239,85 @@ module RuboCop problem 'Use "depends_on :x11" instead of "ENV.x11"' end - find_every_method_call_by_name(body_node, :assert).each do |m| - if method_called?(m, :include?) && !method_called?(m, :!) + find_every_method_call_by_name(body_node, :assert).each do |method| + if method_called?(method, :include?) && !method_called?(method, :!) problem "Use `assert_match` instead of `assert ...include?`" end end - find_every_method_call_by_name(body_node, :depends_on).each do |m| - next unless method_called?(m, :new) + find_every_method_call_by_name(body_node, :depends_on).each do |method| + next unless method_called?(method, :new) problem "`depends_on` can take requirement classes instead of instances" end os = [:leopard?, :snow_leopard?, :lion?, :mountain_lion?] os.each do |version| - find_instance_method_call(body_node, "MacOS", version) do |m| - problem "\"#{m.source}\" is deprecated, use a comparison to MacOS.version instead" + find_instance_method_call(body_node, "MacOS", version) do |method| + problem "\"#{method.source}\" is deprecated, use a comparison to MacOS.version instead" end end - find_instance_method_call(body_node, "Dir", :[]) do |m| - path = parameters(m).first + find_instance_method_call(body_node, "Dir", :[]) do |method| + path = parameters(method).first next if !path.str_type? next unless match = regex_match_group(path, /^[^\*{},]+$/) problem "Dir([\"#{string_content(path)}\"]) is unnecessary; just use \"#{match[0]}\"" end - fileUtils_methods= Regexp.new(FileUtils.singleton_methods(false).map { |m| Regexp.escape(m) }.join "|") - find_every_method_call_by_name(body_node, :system).each do |m| - param = parameters(m).first + fileUtils_methods= Regexp.new(FileUtils.singleton_methods(false).map { |m| '(?-mix:^'+Regexp.escape(m)+'$)' }.join '|') + find_every_method_call_by_name(body_node, :system).each do |method| + param = parameters(method).first next unless match = regex_match_group(param, fileUtils_methods) - problem "Use the `#{match}` Ruby method instead of `#{m.source}`" + problem "Use the `#{match}` Ruby method instead of `#{method.source}`" end if find_method_def(@processed_source.ast) problem "Define method #{method_name(@offensive_node)} in the class body, not at the top-level" end - find_instance_method_call(body_node, :build, :without?) do |m| - next unless unless_modifier?(m.parent) - correct = m.source.gsub("out?", "?") - problem "Use if #{correct} instead of unless #{m.source}" + find_instance_method_call(body_node, :build, :without?) do |method| + next unless unless_modifier?(method.parent) + correct = method.source.gsub("out?", "?") + problem "Use if #{correct} instead of unless #{method.source}" end - find_instance_method_call(body_node, :build, :with?) do |m| - next unless unless_modifier?(m.parent) - correct = m.source.gsub("?", "out?") - problem "Use if #{correct} instead of unless #{m.source}" + find_instance_method_call(body_node, :build, :with?) do |method| + next unless unless_modifier?(method.parent) + correct = method.source.gsub("?", "out?") + problem "Use if #{correct} instead of unless #{method.source}" end - find_instance_method_call(body_node, :build, :with?) do |m| - next unless negated?(m.parent) + find_instance_method_call(body_node, :build, :with?) do |method| + next unless negated?(method.parent) problem "Don't negate 'build.with?': use 'build.without?'" end - find_instance_method_call(body_node, :build, :without?) do |m| - next unless negated?(m.parent) + find_instance_method_call(body_node, :build, :without?) do |method| + next unless negated?(method.parent) problem "Don't negate 'build.without?': use 'build.with?'" end - find_instance_method_call(body_node, :build, :without?) do |m| - arg = parameters(m).first + find_instance_method_call(body_node, :build, :without?) do |method| + arg = parameters(method).first next unless match = regex_match_group(arg, %r{-?-?without-(.*)}) problem "Don't duplicate 'without': Use `build.without? \"#{match[1]}\"` to check for \"--without-#{match[1]}\"" end - find_instance_method_call(body_node, :build, :with?) do |m| - arg = parameters(m).first + find_instance_method_call(body_node, :build, :with?) do |method| + arg = parameters(method).first next unless match = regex_match_group(arg, %r{-?-?with-(.*)}) problem "Don't duplicate 'with': Use `build.with? \"#{match[1]}\"` to check for \"--with-#{match[1]}\"" end - find_instance_method_call(body_node, :build, :include?) do |m| - arg = parameters(m).first + find_instance_method_call(body_node, :build, :include?) do |method| + arg = parameters(method).first next unless match = regex_match_group(arg, %r{with(out)?-(.*)}) problem "Use build.with#{match[1]}? \"#{match[2]}\" instead of build.include? 'with#{match[1]}-#{match[2]}'" end - find_instance_method_call(body_node, :build, :include?) do |m| - arg = parameters(m).first + find_instance_method_call(body_node, :build, :include?) do |method| + arg = parameters(method).first next unless match = regex_match_group(arg, %r{\-\-(.*)}) problem "Reference '#{match[1]}' without dashes" end @@ -352,19 +356,11 @@ module RuboCop (dstr _ (begin (send nil %1)) $(str _ ))} EOS - # This is Pattern Matching method for AST - # Takes the AST node as argument and yields matching node if block given - # Else returns boolean for the match - def_node_search :languageNode?, <<-PATTERN + # Node Pattern search for Language::Node + def_node_search :languageNodeModule?, <<-EOS.undent (const (const nil :Language) :Node) - PATTERN - + EOS end end end end - -# Strict rules ported early -# find_method_with_args(@processed_source.ast, :require, "formula") do |m| -# problem "#{m.source} is now unnecessary" -# end diff --git a/Library/Homebrew/rubocops/patches_cop.rb b/Library/Homebrew/rubocops/patches_cop.rb index fb14d8acca..7ee1a127a3 100644 --- a/Library/Homebrew/rubocops/patches_cop.rb +++ b/Library/Homebrew/rubocops/patches_cop.rb @@ -25,13 +25,26 @@ 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}/ - problem "GitHub/Gist patches should specify a revision:\n#{patch_url}" + if patch_url !~ /[a-fA-F0-9]{40}/ + problem <<-EOS.undent.chomp + GitHub/Gist patches should specify a revision: + #{patch_url} + EOS end end @@ -46,15 +59,24 @@ module RuboCop end if regex_match_group(patch, %r{macports/trunk}) - problem "MacPorts patches should specify a revision instead of trunk:\n#{patch_url}" + problem <<-EOS.undent.chomp + MacPorts patches should specify a revision instead of trunk: + #{patch_url} + EOS end if regex_match_group(patch, %r{^http://trac\.macports\.org}) - problem "Patches from MacPorts Trac should be https://, not http:\n#{patch_url}" + problem <<-EOS.undent.chomp + Patches from MacPorts Trac should be https://, not http: + #{patch_url} + EOS end return unless regex_match_group(patch, %r{^http://bugs\.debian\.org}) - problem "Patches from Debian should be https://, not http:\n#{patch_url}" + problem <<-EOS.undent.chomp + Patches from Debian should be https://, not http: + #{patch_url} + EOS end end end diff --git a/Library/Homebrew/rubocops/urls_cop.rb b/Library/Homebrew/rubocops/urls_cop.rb index 676e735238..071a4c42d6 100644 --- a/Library/Homebrew/rubocops/urls_cop.rb +++ b/Library/Homebrew/rubocops/urls_cop.rb @@ -104,8 +104,10 @@ module RuboCop end if url =~ %r{^https?://prdownloads\.} - problem "Don't use prdownloads in SourceForge urls (url is #{url}).\n" \ - "\tSee: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/" + problem <<-EOS.undent.chomp + Don't use prdownloads in SourceForge urls (url is #{url}). + See: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/ + EOS end if url =~ %r{^http://\w+\.dl\.} diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb index 0de9707731..7d23e59664 100644 --- a/Library/Homebrew/sandbox.rb +++ b/Library/Homebrew/sandbox.rb @@ -18,12 +18,6 @@ class Sandbox !ARGV.no_sandbox? end - def self.print_sandbox_message - return if @printed_sandbox_message - ohai "Using the sandbox" - @printed_sandbox_message = true - end - def initialize @profile = SandboxProfile.new end @@ -173,7 +167,7 @@ class Sandbox def add_rule(rule) s = "(" - s << ((rule[:allow]) ? "allow" : "deny") + s << (rule[:allow] ? "allow" : "deny") s << " #{rule[:operation]}" s << " (#{rule[:filter]})" if rule[:filter] s << " (with #{rule[:modifier]})" if rule[:modifier] diff --git a/Library/Homebrew/shims/super/cc b/Library/Homebrew/shims/super/cc index d894d3d69a..afe72156fb 100755 --- a/Library/Homebrew/shims/super/cc +++ b/Library/Homebrew/shims/super/cc @@ -43,6 +43,8 @@ class Cmd else :cc end + elsif @args.include?("-xc++-header") || @args.each_cons(2).include?(["-x", "c++-header"]) + :cxx elsif @args.include? "-E" :ccE else diff --git a/Library/Homebrew/shims/super/make b/Library/Homebrew/shims/super/make index 7b49e56c0b..46de531310 100755 --- a/Library/Homebrew/shims/super/make +++ b/Library/Homebrew/shims/super/make @@ -1,5 +1,10 @@ #!/bin/bash -export MAKE=${HOMEBREW_MAKE:-make} +if [[ -n "$HOMEBREW_MAKE" && "$HOMEBREW_MAKE" != "make" ]] +then + export MAKE="$HOMEBREW_MAKE" +else + MAKE="make" +fi export HOMEBREW_CCCFG="O$HOMEBREW_CCCFG" -exec xcrun $MAKE "$@" +exec xcrun "$MAKE" "$@" diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index c6e7043506..dd6026fcf4 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -51,8 +51,18 @@ class SoftwareSpec @owner = owner @resource.owner = self resources.each_value do |r| - r.owner = self - r.version ||= (version.head? ? Version.create("HEAD") : version.dup) + r.owner = self + r.version ||= begin + if version.nil? + raise "#{full_name}: version missing for \"#{r.name}\" resource!" + end + + if version.head? + Version.create("HEAD") + else + version.dup + end + end end patches.each { |p| p.owner = self } end @@ -257,7 +267,7 @@ class Bottle end def suffix - s = (rebuild > 0) ? ".#{rebuild}" : "" + s = rebuild.positive? ? ".#{rebuild}" : "" ".bottle#{s}.tar.gz" end end diff --git a/Library/Homebrew/system_config.rb b/Library/Homebrew/system_config.rb index 5663295c28..3e1acd4ff2 100644 --- a/Library/Homebrew/system_config.rb +++ b/Library/Homebrew/system_config.rb @@ -134,7 +134,7 @@ class SystemConfig # java_home doesn't exist on all macOSs; it might be missing on older versions. return "N/A" unless File.executable? "/usr/libexec/java_home" - java_xml = Utils.popen_read("/usr/libexec/java_home", "--xml", "--failfast") + java_xml = Utils.popen_read("/usr/libexec/java_home", "--xml", "--failfast", err: :close) return "N/A" unless $CHILD_STATUS.success? javas = [] REXML::XPath.each(REXML::Document.new(java_xml), "//key[text()='JVMVersion']/following-sibling::string") do |item| diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index e7df883561..aa0208d51a 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -3,18 +3,16 @@ require "ostruct" require "options" require "json" require "development_tools" +require "extend/cachable" # Inherit from OpenStruct to gain a generic initialization method that takes a # hash and creates an attribute for each key and value. `Tab.new` probably # should not be called directly, instead use one of the class methods like # `Tab.create`. class Tab < OpenStruct - FILENAME = "INSTALL_RECEIPT.json".freeze - CACHE = {} + extend Cachable - def self.clear_cache - CACHE.clear - end + FILENAME = "INSTALL_RECEIPT.json".freeze # Instantiates a Tab for a new installation of a formula. def self.create(formula, compiler, stdlib) @@ -57,7 +55,7 @@ class Tab < OpenStruct # Returns the Tab for an install receipt at `path`. # Results are cached. def self.from_file(path) - CACHE.fetch(path) { |p| CACHE[p] = from_file_content(File.read(p), p) } + cache.fetch(path) { |p| cache[p] = from_file_content(File.read(p), p) } end # Like Tab.from_file, but bypass the cache. @@ -324,7 +322,7 @@ class Tab < OpenStruct "poured_from_bottle" => poured_from_bottle, "installed_as_dependency" => installed_as_dependency, "installed_on_request" => installed_on_request, - "changed_files" => changed_files && changed_files.map(&:to_s), + "changed_files" => changed_files&.map(&:to_s), "time" => time, "source_modified_time" => source_modified_time.to_i, "HEAD" => self.HEAD, @@ -343,7 +341,7 @@ class Tab < OpenStruct # will no longer be valid. Formula.clear_installed_formulae_cache unless tabfile.exist? - CACHE[tabfile] = self + self.class.cache[tabfile] = self tabfile.atomic_write(to_json) end diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index f232be4281..78dc4cf4e7 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -1,4 +1,5 @@ require "extend/string" +require "extend/cachable" require "readall" # a {Tap} is used to extend the formulae provided by Homebrew core. @@ -8,14 +9,10 @@ require "readall" # {#user} represents Github username and {#repo} represents repository # name without leading `homebrew-`. class Tap + extend Cachable + TAP_DIRECTORY = HOMEBREW_LIBRARY/"Taps" - CACHE = {} - - def self.clear_cache - CACHE.clear - end - def self.fetch(*args) case args.length when 1 @@ -38,7 +35,7 @@ class Tap end cache_key = "#{user}/#{repo}".downcase - CACHE.fetch(cache_key) { |key| CACHE[key] = Tap.new(user, repo) } + cache.fetch(cache_key) { |key| cache[key] = Tap.new(user, repo) } end def self.from_path(path) @@ -551,11 +548,9 @@ class CoreTap < Tap @instance ||= new end - def self.ensure_installed!(options = {}) + def self.ensure_installed! return if instance.installed? - args = ["tap", instance.name] - args << "-q" if options.fetch(:quiet, true) - safe_system HOMEBREW_BREW_FILE, *args + safe_system HOMEBREW_BREW_FILE, "tap", instance.name end # @private @@ -650,6 +645,5 @@ class TapConfig tap.path.cd do safe_system "git", "config", "--local", "--replace-all", "homebrew.#{key}", value.to_s end - value end end diff --git a/Library/Homebrew/test.rb b/Library/Homebrew/test.rb index d9ec8250ee..3d5e62a884 100644 --- a/Library/Homebrew/test.rb +++ b/Library/Homebrew/test.rb @@ -27,7 +27,7 @@ begin Timeout.timeout TEST_TIMEOUT_SECONDS do raise "test returned false" if formula.run_test == false end -rescue Exception => e +rescue Exception => e # rubocop:disable Lint/RescueException Marshal.dump(e, error_pipe) error_pipe.close exit! 1 diff --git a/Library/Homebrew/test/Gemfile b/Library/Homebrew/test/Gemfile index dbe76b51c4..b6d1405ff6 100644 --- a/Library/Homebrew/test/Gemfile +++ b/Library/Homebrew/test/Gemfile @@ -1,10 +1,12 @@ source "https://rubygems.org" +require_relative "../constants" + gem "parallel_tests" gem "rspec" gem "rspec-its", require: false gem "rspec-wait", require: false -gem "rubocop" +gem "rubocop", HOMEBREW_RUBOCOP_VERSION group :coverage do gem "codecov", require: false diff --git a/Library/Homebrew/test/Gemfile.lock b/Library/Homebrew/test/Gemfile.lock index ccfd91542d..ba12d091d4 100644 --- a/Library/Homebrew/test/Gemfile.lock +++ b/Library/Homebrew/test/Gemfile.lock @@ -9,15 +9,15 @@ GEM diff-lcs (1.3) docile (1.1.5) json (2.1.0) - parallel (1.11.2) - parallel_tests (2.14.1) + parallel (1.12.0) + parallel_tests (2.17.0) parallel parser (2.4.0.0) ast (~> 2.2) powerpack (0.1.1) rainbow (2.2.2) rake - rake (12.0.0) + rake (12.1.0) rspec (3.6.0) rspec-core (~> 3.6.0) rspec-expectations (~> 3.6.0) @@ -36,20 +36,20 @@ GEM rspec-support (3.6.0) rspec-wait (0.0.9) rspec (>= 3, < 4) - rubocop (0.49.1) + rubocop (0.50.0) parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) - rainbow (>= 1.99.1, < 3.0) + rainbow (>= 2.2.2, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - ruby-progressbar (1.8.1) - simplecov (0.14.1) + ruby-progressbar (1.9.0) + simplecov (0.15.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) - simplecov-html (0.10.0) - unicode-display_width (1.2.1) + simplecov-html (0.10.2) + unicode-display_width (1.3.0) url (0.3.2) PLATFORMS @@ -61,8 +61,8 @@ DEPENDENCIES rspec rspec-its rspec-wait - rubocop + rubocop (= 0.50.0) simplecov BUNDLED WITH - 1.14.6 + 1.15.4 diff --git a/Library/Homebrew/test/bottle_hooks_spec.rb b/Library/Homebrew/test/bottle_hooks_spec.rb index e70b558a18..eb66173805 100644 --- a/Library/Homebrew/test/bottle_hooks_spec.rb +++ b/Library/Homebrew/test/bottle_hooks_spec.rb @@ -8,7 +8,7 @@ describe Homebrew::Hooks::Bottles do let(:formula) do double( - bottle: nil, + bottled?: false, local_bottle_path: nil, bottle_disabled?: false, some_random_method: true, diff --git a/Library/Homebrew/test/cask/accessibility_spec.rb b/Library/Homebrew/test/cask/accessibility_spec.rb index 9e56f6bd33..b77bcb0020 100644 --- a/Library/Homebrew/test/cask/accessibility_spec.rb +++ b/Library/Homebrew/test/cask/accessibility_spec.rb @@ -1,7 +1,7 @@ # TODO: this test should be named after the corresponding class, once # that class is abstracted from installer.rb. describe "Accessibility Access", :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-accessibility-access.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-accessibility-access")) } let(:fake_system_command) { class_double(Hbc::SystemCommand) } let(:installer) { Hbc::Installer.new(cask, command: fake_system_command) } diff --git a/Library/Homebrew/test/cask/artifact/alt_target_spec.rb b/Library/Homebrew/test/cask/artifact/alt_target_spec.rb index 9e8d83bb4a..847bf25fa8 100644 --- a/Library/Homebrew/test/cask/artifact/alt_target_spec.rb +++ b/Library/Homebrew/test/cask/artifact/alt_target_spec.rb @@ -1,9 +1,13 @@ describe Hbc::Artifact::App, :cask do describe "activate to alternate target" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-alt-target.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-alt-target")) } let(:install_phase) { - -> { Hbc::Artifact::App.new(cask).install_phase } + lambda do + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end + end } let(:source_path) { cask.staged_path.join("Caffeine.app") } diff --git a/Library/Homebrew/test/cask/artifact/app_spec.rb b/Library/Homebrew/test/cask/artifact/app_spec.rb index 0add472e21..4ead8b7f94 100644 --- a/Library/Homebrew/test/cask/artifact/app_spec.rb +++ b/Library/Homebrew/test/cask/artifact/app_spec.rb @@ -1,14 +1,14 @@ describe Hbc::Artifact::App, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("local-caffeine")) } let(:command) { Hbc::SystemCommand } let(:force) { false } - let(:app) { Hbc::Artifact::App.new(cask, command: command, force: force) } + let(:app) { cask.artifacts.find { |a| a.is_a?(described_class) } } let(:source_path) { cask.staged_path.join("Caffeine.app") } let(:target_path) { Hbc.appdir.join("Caffeine.app") } - let(:install_phase) { app.install_phase } - let(:uninstall_phase) { app.uninstall_phase } + let(:install_phase) { app.install_phase(command: command, force: force) } + let(:uninstall_phase) { app.uninstall_phase(command: command, force: force) } before(:each) do InstallHelper.install_without_artifacts(cask) @@ -105,8 +105,8 @@ describe Hbc::Artifact::App, :cask do describe "target is user-owned but contains read-only files" do before(:each) do - system "/usr/bin/touch", "--", "#{target_path}/foo" - system "/bin/chmod", "--", "0555", target_path + FileUtils.touch "#{target_path}/foo" + FileUtils.chmod 0555, target_path end it "overwrites the existing app" do @@ -138,7 +138,7 @@ describe Hbc::Artifact::App, :cask do end after(:each) do - system "/bin/chmod", "--", "0755", target_path + FileUtils.chmod 0755, target_path end end end @@ -206,8 +206,8 @@ describe Hbc::Artifact::App, :cask do end describe "summary" do - let(:description) { app.summary[:english_description] } - let(:contents) { app.summary[:contents] } + let(:description) { app.class.english_description } + let(:contents) { app.summarize_installed } it "returns the correct english_description" do expect(description).to eq("Apps") @@ -217,14 +217,13 @@ describe Hbc::Artifact::App, :cask do it "returns the path to the app" do install_phase - expect(contents).to eq(["#{target_path} (#{target_path.abv})"]) + expect(contents).to eq("#{target_path} (#{target_path.abv})") end end describe "app is missing" do it "returns a warning and the supposed path to the app" do - expect(contents.size).to eq(1) - expect(contents[0]).to match(/.*Missing App.*: #{target_path}/) + expect(contents).to match(/.*Missing App.*: #{target_path}/) end end end diff --git a/Library/Homebrew/test/cask/artifact/binary_spec.rb b/Library/Homebrew/test/cask/artifact/binary_spec.rb index ce00e39357..6a3f1da346 100644 --- a/Library/Homebrew/test/cask/artifact/binary_spec.rb +++ b/Library/Homebrew/test/cask/artifact/binary_spec.rb @@ -1,9 +1,10 @@ describe Hbc::Artifact::Binary, :cask do let(:cask) { - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-binary.rb").tap do |cask| + Hbc::CaskLoader.load(cask_path("with-binary")).tap do |cask| InstallHelper.install_without_artifacts(cask) end } + let(:artifacts) { cask.artifacts.select { |a| a.is_a?(described_class) } } let(:expected_path) { Hbc.binarydir.join("binary") } before(:each) do @@ -16,7 +17,7 @@ describe Hbc::Artifact::Binary, :cask do context "when --no-binaries is specified" do let(:cask) { - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-binary.rb") + Hbc::CaskLoader.load(cask_path("with-binary")) } it "doesn't link the binary when --no-binaries is specified" do @@ -26,7 +27,9 @@ describe Hbc::Artifact::Binary, :cask do end it "links the binary to the proper directory" do - Hbc::Artifact::Binary.new(cask).install_phase + artifacts.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(expected_path).to be_a_symlink expect(expected_path.readlink).to exist @@ -34,7 +37,7 @@ describe Hbc::Artifact::Binary, :cask do context "when the binary is not executable" do let(:cask) { - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-non-executable-binary.rb").tap do |cask| + Hbc::CaskLoader.load(cask_path("with-non-executable-binary")).tap do |cask| InstallHelper.install_without_artifacts(cask) end } @@ -45,7 +48,9 @@ describe Hbc::Artifact::Binary, :cask do expect(FileUtils).to receive(:chmod) .with("+x", cask.staged_path.join("naked_non_executable")).and_call_original - Hbc::Artifact::Binary.new(cask).install_phase + artifacts.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(expected_path).to be_a_symlink expect(expected_path.readlink).to be_executable @@ -56,7 +61,9 @@ describe Hbc::Artifact::Binary, :cask do FileUtils.touch expected_path expect { - Hbc::Artifact::Binary.new(cask).install_phase + artifacts.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end }.to raise_error(Hbc::CaskError) expect(expected_path).not_to be :symlink? @@ -65,7 +72,9 @@ describe Hbc::Artifact::Binary, :cask do it "clobbers an existing symlink" do expected_path.make_symlink("/tmp") - Hbc::Artifact::Binary.new(cask).install_phase + artifacts.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(File.readlink(expected_path)).not_to eq("/tmp") end @@ -73,21 +82,27 @@ describe Hbc::Artifact::Binary, :cask do it "creates parent directory if it doesn't exist" do FileUtils.rmdir Hbc.binarydir - Hbc::Artifact::Binary.new(cask).install_phase + artifacts.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(expected_path.exist?).to be true end context "binary is inside an app package" do let(:cask) { - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-embedded-binary.rb").tap do |cask| + Hbc::CaskLoader.load(cask_path("with-embedded-binary")).tap do |cask| InstallHelper.install_without_artifacts(cask) end } it "links the binary to the proper directory" do - Hbc::Artifact::App.new(cask).install_phase - Hbc::Artifact::Binary.new(cask).install_phase + cask.artifacts.select { |a| a.is_a?(Hbc::Artifact::App) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end + artifacts.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(expected_path).to be_a_symlink expect(expected_path.readlink).to exist diff --git a/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb b/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb index cb2ef9850d..ea567abee4 100644 --- a/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb +++ b/Library/Homebrew/test/cask/artifact/generic_artifact_spec.rb @@ -1,8 +1,12 @@ describe Hbc::Artifact::Artifact, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-generic-artifact.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-generic-artifact")) } let(:install_phase) { - -> { Hbc::Artifact::Artifact.new(cask).install_phase } + lambda do + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end + end } let(:source_path) { cask.staged_path.join("Caffeine.app") } @@ -12,11 +16,11 @@ describe Hbc::Artifact::Artifact, :cask do InstallHelper.install_without_artifacts(cask) end - describe "with no target" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-generic-artifact-no-target.rb") } - - it "fails to install with no target" do - expect(install_phase).to raise_error(Hbc::CaskInvalidError) + context "without target" do + it "fails to load" do + expect { + Hbc::CaskLoader.load(cask_path("with-generic-artifact-no-target")) + }.to raise_error(Hbc::CaskInvalidError, /target required for Generic Artifact/) end end diff --git a/Library/Homebrew/test/cask/artifact/nested_container_spec.rb b/Library/Homebrew/test/cask/artifact/nested_container_spec.rb index be7ba5ff87..f9cd056f8a 100644 --- a/Library/Homebrew/test/cask/artifact/nested_container_spec.rb +++ b/Library/Homebrew/test/cask/artifact/nested_container_spec.rb @@ -1,11 +1,13 @@ describe Hbc::Artifact::NestedContainer, :cask do describe "install" do it "extracts the specified paths as containers" do - cask = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/nested-app.rb").tap do |c| + cask = Hbc::CaskLoader.load(cask_path("nested-app")).tap do |c| InstallHelper.install_without_artifacts(c) end - Hbc::Artifact::NestedContainer.new(cask).install_phase + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(cask.staged_path.join("MyNestedApp.app")).to be_a_directory end diff --git a/Library/Homebrew/test/cask/artifact/pkg_spec.rb b/Library/Homebrew/test/cask/artifact/pkg_spec.rb index 3e62616ea2..7f1b64d1a0 100644 --- a/Library/Homebrew/test/cask/artifact/pkg_spec.rb +++ b/Library/Homebrew/test/cask/artifact/pkg_spec.rb @@ -1,5 +1,5 @@ describe Hbc::Artifact::Pkg, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-installable.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-installable")) } let(:fake_system_command) { class_double(Hbc::SystemCommand) } before(:each) do @@ -8,7 +8,7 @@ describe Hbc::Artifact::Pkg, :cask do describe "install_phase" do it "runs the system installer on the specified pkgs" do - pkg = Hbc::Artifact::Pkg.new(cask, command: fake_system_command) + pkg = cask.artifacts.find { |a| a.is_a?(described_class) } expect(fake_system_command).to receive(:run!).with( "/usr/sbin/installer", @@ -17,15 +17,15 @@ describe Hbc::Artifact::Pkg, :cask do print_stdout: true, ) - pkg.install_phase + pkg.install_phase(command: fake_system_command) end end describe "choices" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-choices.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-choices")) } it "passes the choice changes xml to the system installer" do - pkg = Hbc::Artifact::Pkg.new(cask, command: fake_system_command) + pkg = cask.artifacts.find { |a| a.is_a?(described_class) } file = double(path: Pathname.new("/tmp/choices.xml")) @@ -57,7 +57,7 @@ describe Hbc::Artifact::Pkg, :cask do print_stdout: true, ) - pkg.install_phase + pkg.install_phase(command: fake_system_command) end end end diff --git a/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb b/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb index 51b1431f03..18cc4ca91a 100644 --- a/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb +++ b/Library/Homebrew/test/cask/artifact/postflight_block_spec.rb @@ -11,7 +11,9 @@ describe Hbc::Artifact::PostflightBlock, :cask do end end - described_class.new(cask).install_phase + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(called).to be true expect(yielded_arg).to be_kind_of(Hbc::DSL::Postflight) @@ -30,7 +32,9 @@ describe Hbc::Artifact::PostflightBlock, :cask do end end - described_class.new(cask).uninstall_phase + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.uninstall_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(called).to be true expect(yielded_arg).to be_kind_of(Hbc::DSL::UninstallPostflight) diff --git a/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb b/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb index b13c4ab9d3..405cdbd6f8 100644 --- a/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb +++ b/Library/Homebrew/test/cask/artifact/preflight_block_spec.rb @@ -11,7 +11,9 @@ describe Hbc::Artifact::PreflightBlock, :cask do end end - described_class.new(cask).install_phase + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(called).to be true expect(yielded_arg).to be_kind_of Hbc::DSL::Preflight @@ -30,7 +32,9 @@ describe Hbc::Artifact::PreflightBlock, :cask do end end - described_class.new(cask).uninstall_phase + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.uninstall_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect(called).to be true expect(yielded_arg).to be_kind_of Hbc::DSL::UninstallPreflight diff --git a/Library/Homebrew/test/cask/artifact/suite_spec.rb b/Library/Homebrew/test/cask/artifact/suite_spec.rb index 8c217a9e0c..80d3e917f6 100644 --- a/Library/Homebrew/test/cask/artifact/suite_spec.rb +++ b/Library/Homebrew/test/cask/artifact/suite_spec.rb @@ -1,7 +1,13 @@ describe Hbc::Artifact::Suite, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-suite.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-suite")) } - let(:install_phase) { -> { Hbc::Artifact::Suite.new(cask).install_phase } } + let(:install_phase) { + lambda do + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end + end + } let(:target_path) { Hbc.appdir.join("Caffeine") } let(:source_path) { cask.staged_path.join("Caffeine") } diff --git a/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb b/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb index a1fdd3b74c..7f2ef75b34 100644 --- a/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb +++ b/Library/Homebrew/test/cask/artifact/two_apps_correct_spec.rb @@ -1,9 +1,13 @@ describe Hbc::Artifact::App, :cask do describe "multiple apps" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-two-apps-correct.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-two-apps-correct")) } let(:install_phase) { - -> { Hbc::Artifact::App.new(cask).install_phase } + lambda do + cask.artifacts.select { |a| a.is_a?(described_class) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end + end } let(:source_path_mini) { cask.staged_path.join("Caffeine Mini.app") } @@ -27,7 +31,7 @@ describe Hbc::Artifact::App, :cask do end describe "when apps are in a subdirectory" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-two-apps-subdir.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-two-apps-subdir")) } it "installs both apps using the proper target directory" do install_phase.call diff --git a/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb b/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb index 6427ec32c3..cda5b4488c 100644 --- a/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb +++ b/Library/Homebrew/test/cask/artifact/two_apps_incorrect_spec.rb @@ -2,7 +2,7 @@ describe Hbc::Artifact::App, :cask do # FIXME: Doesn't actually raise because the `app` stanza is not evaluated on load. # it "must raise" do # lambda { - # Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-two-apps-incorrect.rb") + # Hbc::CaskLoader.load(cask_path("with-two-apps-incorrect")) # }.must_raise # # TODO: later give the user a nice exception for this case and check for it here # end diff --git a/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb b/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb index 8cd0b1e41e..c6ff7d30e0 100644 --- a/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb +++ b/Library/Homebrew/test/cask/artifact/uninstall_no_zap_spec.rb @@ -1,8 +1,8 @@ describe Hbc::Artifact::Zap, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-installable.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-installable")) } let(:zap_artifact) { - Hbc::Artifact::Zap.new(cask) + cask.artifacts.find { |a| a.is_a?(described_class) } } before(:each) do diff --git a/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb b/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb index 0e522bc210..b9872ab9e5 100644 --- a/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb +++ b/Library/Homebrew/test/cask/artifact/uninstall_zap_shared_examples.rb @@ -1,12 +1,12 @@ shared_examples "#uninstall_phase or #zap_phase" do - let(:artifact_name) { described_class.artifact_name } - let(:artifact) { described_class.new(cask, command: fake_system_command) } + let(:artifact_dsl_key) { described_class.dsl_key } + let(:artifact) { cask.artifacts.find { |a| a.is_a?(described_class) } } let(:fake_system_command) { Hbc::FakeSystemCommand } - subject { artifact.public_send(:"#{artifact_name}_phase") } + subject { artifact.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command) } context "using :launchctl" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-launchctl.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl")) } let(:launchctl_list_cmd) { %w[/bin/launchctl list my.fancy.package.service] } let(:launchctl_remove_cmd) { %w[/bin/launchctl remove my.fancy.package.service] } let(:unknown_response) { "launchctl list returned unknown response\n" } @@ -61,7 +61,7 @@ shared_examples "#uninstall_phase or #zap_phase" do context "using :pkgutil" do let(:fake_system_command) { class_double(Hbc::SystemCommand) } - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-pkgutil.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) } let(:main_pkg_id) { "my.fancy.package.main" } let(:agent_pkg_id) { "my.fancy.package.agent" } @@ -85,7 +85,7 @@ shared_examples "#uninstall_phase or #zap_phase" do end context "using :kext" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-kext.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-kext")) } let(:kext_id) { "my.fancy.package.kernelextension" } it "is supported" do @@ -110,7 +110,7 @@ shared_examples "#uninstall_phase or #zap_phase" do end context "using :quit" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-quit.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-quit")) } let(:bundle_id) { "my.fancy.package.app" } let(:quit_application_script) do %Q(tell application id "#{bundle_id}" to quit) @@ -130,7 +130,7 @@ shared_examples "#uninstall_phase or #zap_phase" do end context "using :signal" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-signal.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-signal")) } let(:bundle_id) { "my.fancy.package.app" } let(:signals) { %w[TERM KILL] } let(:unix_pids) { [12_345, 67_890] } @@ -149,6 +149,8 @@ shared_examples "#uninstall_phase or #zap_phase" do end [:delete, :trash].each do |directive| + next if directive == :trash && ENV["HOMEBREW_TESTS_COVERAGE"].nil? + context "using :#{directive}" do let(:dir) { TEST_TMPDIR } let(:absolute_path) { Pathname.new("#{dir}/absolute_path") } @@ -170,10 +172,10 @@ shared_examples "#uninstall_phase or #zap_phase" do end let(:fake_system_command) { Hbc::NeverSudoSystemCommand } - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-#{directive}.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-#{directive}")) } before(:each) do - allow_any_instance_of(Hbc::Artifact::UninstallBase).to receive(:trash_paths) + allow_any_instance_of(Hbc::Artifact::AbstractUninstall).to receive(:trash_paths) .and_wrap_original do |method, *args| result = method.call(*args) FileUtils.rm_rf result.stdout.split("\0") @@ -196,7 +198,7 @@ shared_examples "#uninstall_phase or #zap_phase" do context "using :rmdir" do let(:fake_system_command) { Hbc::NeverSudoSystemCommand } - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-rmdir.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-rmdir")) } let(:empty_directory) { Pathname.new("#{TEST_TMPDIR}/empty_directory_path") } let(:ds_store) { empty_directory.join(".DS_Store") } @@ -223,8 +225,8 @@ shared_examples "#uninstall_phase or #zap_phase" do [:script, :early_script].each do |script_type| context "using #{script_type.inspect}" do let(:fake_system_command) { Hbc::NeverSudoSystemCommand } - let(:token) { "with-#{artifact_name}-#{script_type}".tr("_", "-") } - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/#{token}.rb") } + let(:token) { "with-#{artifact_dsl_key}-#{script_type}".tr("_", "-") } + let(:cask) { Hbc::CaskLoader.load(cask_path(token.to_s)) } let(:script_pathname) { cask.staged_path.join("MyFancyPkg", "FancyUninstaller.tool") } it "is supported" do @@ -250,7 +252,7 @@ shared_examples "#uninstall_phase or #zap_phase" do end context "using :login_item" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-#{artifact_name}-login-item.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-login-item")) } it "is supported" do Hbc::FakeSystemCommand.expects_command( diff --git a/Library/Homebrew/test/cask/audit_spec.rb b/Library/Homebrew/test/cask/audit_spec.rb index ddc773e3e2..7e140acb24 100644 --- a/Library/Homebrew/test/cask/audit_spec.rb +++ b/Library/Homebrew/test/cask/audit_spec.rb @@ -265,19 +265,14 @@ describe Hbc::Audit, :cask do end describe "generic artifact checks" do - context "with no target" do - let(:cask_token) { "generic-artifact-no-target" } - it { is_expected.to fail_with(/target required for generic artifact/) } - end - context "with relative target" do let(:cask_token) { "generic-artifact-relative-target" } - it { is_expected.to fail_with(/target must be absolute path for generic artifact/) } + it { is_expected.to fail_with(/target must be absolute path for Generic Artifact/) } end context "with absolute target" do let(:cask_token) { "generic-artifact-absolute-target" } - it { should_not fail_with(/target required for generic artifact/) } + it { should_not fail_with(/target required for Generic Artifact/) } end end diff --git a/Library/Homebrew/test/cask/cask_loader/from_content_loader_spec.rb b/Library/Homebrew/test/cask/cask_loader/from_content_loader_spec.rb new file mode 100644 index 0000000000..e735d5eca8 --- /dev/null +++ b/Library/Homebrew/test/cask/cask_loader/from_content_loader_spec.rb @@ -0,0 +1,57 @@ +describe Hbc::CaskLoader::FromContentLoader do + alias_matcher :be_able_to_load, :be_can_load + + describe "::can_load?" do + it "returns true for Casks specified with `cask \"token\" do … end`" do + expect(described_class).to be_able_to_load <<~EOS + cask "token" do + end + EOS + end + + it "returns true for Casks specified with `cask \"token\" do; end`" do + expect(described_class).to be_able_to_load <<~EOS + cask "token" do; end + EOS + end + + it "returns true for Casks specified with `cask 'token' do … end`" do + expect(described_class).to be_able_to_load <<~EOS + cask 'token' do + end + EOS + end + + it "returns true for Casks specified with `cask 'token' do; end`" do + expect(described_class).to be_able_to_load <<~EOS + cask 'token' do; end + EOS + end + + it "returns true for Casks specified with `cask(\"token\") { … }`" do + expect(described_class).to be_able_to_load <<~EOS + cask("token") { + } + EOS + end + + it "returns true for Casks specified with `cask(\"token\") {}`" do + expect(described_class).to be_able_to_load <<~EOS + cask("token") {} + EOS + end + + it "returns true for Casks specified with `cask('token') { … }`" do + expect(described_class).to be_able_to_load <<~EOS + cask('token') { + } + EOS + end + + it "returns true for Casks specified with `cask('token') {}`" do + expect(described_class).to be_able_to_load <<~EOS + cask('token') {} + EOS + end + end +end diff --git a/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb b/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb new file mode 100644 index 0000000000..df2de1f828 --- /dev/null +++ b/Library/Homebrew/test/cask/cask_loader/from_uri_loader_spec.rb @@ -0,0 +1,21 @@ +describe Hbc::CaskLoader::FromURILoader do + alias_matcher :be_able_to_load, :be_can_load + + describe "::can_load?" do + it "returns true when given an URI" do + expect(described_class).to be_able_to_load(URI("http://example.com/")) + end + + it "returns true when given a String which can be parsed to a URI" do + expect(described_class).to be_able_to_load("http://example.com/") + end + + it "returns false when given a String with Cask contents containing a URL" do + expect(described_class).not_to be_able_to_load <<~EOS + cask 'token' do + url 'http://example.com/' + end + EOS + end + end +end diff --git a/Library/Homebrew/test/cask/cask_spec.rb b/Library/Homebrew/test/cask/cask_spec.rb index a6ecc207f7..5858a7c6db 100644 --- a/Library/Homebrew/test/cask/cask_spec.rb +++ b/Library/Homebrew/test/cask/cask_spec.rb @@ -171,4 +171,38 @@ describe Hbc::Cask, :cask do end end end + + describe "full_name" do + context "when it is a core cask" do + it "is the cask token" do + c = Hbc::CaskLoader.load("local-caffeine") + expect(c.full_name).to eq("local-caffeine") + end + end + + context "when it is from a non-core tap" do + it "returns the fully-qualified name of the cask" do + c = Hbc::CaskLoader.load("third-party/tap/third-party-cask") + expect(c.full_name).to eq("third-party/tap/third-party-cask") + end + end + + context "when it is from no known tap" do + it "retuns the cask token" do + file = Tempfile.new(%w[tapless-cask .rb]) + + begin + cask_name = File.basename(file.path, ".rb") + file.write "cask '#{cask_name}'" + file.close + + c = Hbc::CaskLoader.load(file.path) + expect(c.full_name).to eq(cask_name) + ensure + file.close + file.unlink + end + end + end + end end diff --git a/Library/Homebrew/test/cask/cli/audit_spec.rb b/Library/Homebrew/test/cask/cli/audit_spec.rb index 01f506c8c4..da8bf12737 100644 --- a/Library/Homebrew/test/cask/cli/audit_spec.rb +++ b/Library/Homebrew/test/cask/cli/audit_spec.rb @@ -1,13 +1,19 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Audit, :cask do - let(:cask) { double("cask", token: nil) } + let(:cask) { Hbc::Cask.new(nil) } + + it_behaves_like "a command that handles invalid options" describe "selection of Casks to audit" do it "audits all Casks if no tokens are given" do + expect(cask).to be_a Hbc::Cask + allow(Hbc).to receive(:all).and_return([cask, cask]) expect(Hbc::Auditor).to receive(:audit).twice.and_return(true) - Hbc::CLI::Audit.run + described_class.run end it "audits specified Casks if tokens are given" do @@ -18,7 +24,7 @@ describe Hbc::CLI::Audit, :cask do .with(cask, audit_download: false, check_token_conflicts: false) .and_return(true) - Hbc::CLI::Audit.run(cask_token) + described_class.run(cask_token) end end @@ -29,7 +35,7 @@ describe Hbc::CLI::Audit, :cask do .with(cask, audit_download: false, check_token_conflicts: false) .and_return(true) - Hbc::CLI::Audit.run("casktoken") + described_class.run("casktoken") end it "download a Cask if --download flag is set" do @@ -38,7 +44,7 @@ describe Hbc::CLI::Audit, :cask do .with(cask, audit_download: true, check_token_conflicts: false) .and_return(true) - Hbc::CLI::Audit.run("casktoken", "--download") + described_class.run("casktoken", "--download") end end @@ -49,7 +55,7 @@ describe Hbc::CLI::Audit, :cask do .with(cask, audit_download: false, check_token_conflicts: false) .and_return(true) - Hbc::CLI::Audit.run("casktoken") + described_class.run("casktoken") end it "checks for token conflicts if --token-conflicts flag is set" do @@ -58,7 +64,7 @@ describe Hbc::CLI::Audit, :cask do .with(cask, audit_download: false, check_token_conflicts: true) .and_return(true) - Hbc::CLI::Audit.run("casktoken", "--token-conflicts") + described_class.run("casktoken", "--token-conflicts") end end end diff --git a/Library/Homebrew/test/cask/cli/cat_spec.rb b/Library/Homebrew/test/cask/cli/cat_spec.rb index b726a0b366..6b54a2e4b0 100644 --- a/Library/Homebrew/test/cask/cli/cat_spec.rb +++ b/Library/Homebrew/test/cask/cli/cat_spec.rb @@ -1,4 +1,10 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Cat, :cask do + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + describe "given a basic Cask" do let(:basic_cask_content) { <<-EOS.undent @@ -16,42 +22,19 @@ describe Hbc::CLI::Cat, :cask do it "displays the Cask file content about the specified Cask" do expect { - Hbc::CLI::Cat.run("basic-cask") + described_class.run("basic-cask") }.to output(basic_cask_content).to_stdout end it "can display multiple Casks" do expect { - Hbc::CLI::Cat.run("basic-cask", "basic-cask") + described_class.run("basic-cask", "basic-cask") }.to output(basic_cask_content * 2).to_stdout end - - it "fails when option is unknown" do - expect { - Hbc::CLI::Cat.run("--notavalidoption", "basic-cask") - }.to raise_error(/invalid option/) - end end it "raises an exception when the Cask does not exist" do - expect { Hbc::CLI::Cat.run("notacask") } - .to output(/is unavailable/).to_stderr - .and raise_error(Hbc::CaskError, "Cat incomplete.") - end - - describe "when no Cask is specified" do - it "raises an exception" do - expect { - Hbc::CLI::Cat.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - describe "when no Cask is specified, but an invalid option" do - it "raises an exception" do - expect { - Hbc::CLI::Cat.run("--notavalidoption") - }.to raise_error(/invalid option/) - end + expect { described_class.run("notacask") } + .to raise_error(Hbc::CaskUnavailableError, /is unavailable/) end end diff --git a/Library/Homebrew/test/cask/cli/cleanup_spec.rb b/Library/Homebrew/test/cask/cli/cleanup_spec.rb index 64e3ef49fe..7cf00352d3 100644 --- a/Library/Homebrew/test/cask/cli/cleanup_spec.rb +++ b/Library/Homebrew/test/cask/cli/cleanup_spec.rb @@ -1,3 +1,5 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Cleanup, :cask do let(:cache_location) { Pathname.new(Dir.mktmpdir).realpath } let(:outdated_only) { false } @@ -12,6 +14,8 @@ describe Hbc::CLI::Cleanup, :cask do cache_location.rmtree end + it_behaves_like "a command that handles invalid options" + describe "cleanup" do let(:cask_token) { "caffeine" } let(:cask_tokens) { [cask_token] } diff --git a/Library/Homebrew/test/cask/cli/create_spec.rb b/Library/Homebrew/test/cask/cli/create_spec.rb index d77b0a2aac..60c03db752 100644 --- a/Library/Homebrew/test/cask/cli/create_spec.rb +++ b/Library/Homebrew/test/cask/cli/create_spec.rb @@ -1,3 +1,6 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Create, :cask do around(:each) do |example| begin @@ -13,6 +16,9 @@ describe Hbc::CLI::Create, :cask do allow_any_instance_of(described_class).to receive(:exec_editor) end + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "opens the editor for the specified Cask" do command = described_class.new("new-cask") expect(command).to receive(:exec_editor).with(Hbc::CaskLoader.path("new-cask")) @@ -39,7 +45,7 @@ describe Hbc::CLI::Create, :cask do it "raises an exception when more than one Cask is given" do expect { described_class.run("additional-cask", "another-cask") - }.to raise_error(/Only one Cask can be created at a time./) + }.to raise_error(/Only one Cask can be created at a time\./) end it "raises an exception when the Cask already exists" do @@ -53,26 +59,4 @@ describe Hbc::CLI::Create, :cask do expect(command).to receive(:exec_editor).with(Hbc::CaskLoader.path("local-caff")) command.run end - - describe "when no Cask is specified" do - it "raises an exception" do - expect { - described_class.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - context "when an invalid option is specified" do - it "raises an exception when no Cask is specified" do - expect { - described_class.run("--notavalidoption") - }.to raise_error(/invalid option/) - end - - it "raises an exception" do - expect { - described_class.run("--notavalidoption", "yet-another-cask") - }.to raise_error(/invalid option/) - end - end end diff --git a/Library/Homebrew/test/cask/cli/doctor_spec.rb b/Library/Homebrew/test/cask/cli/doctor_spec.rb index b24c777eb7..e3967060ed 100644 --- a/Library/Homebrew/test/cask/cli/doctor_spec.rb +++ b/Library/Homebrew/test/cask/cli/doctor_spec.rb @@ -1,4 +1,8 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Doctor, :cask do + it_behaves_like "a command that handles invalid options" + it "displays some nice info about the environment" do expect { Hbc::CLI::Doctor.run diff --git a/Library/Homebrew/test/cask/cli/edit_spec.rb b/Library/Homebrew/test/cask/cli/edit_spec.rb index 5d5cbf4b93..3475220205 100644 --- a/Library/Homebrew/test/cask/cli/edit_spec.rb +++ b/Library/Homebrew/test/cask/cli/edit_spec.rb @@ -1,8 +1,14 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Edit, :cask do before(:each) do allow_any_instance_of(described_class).to receive(:exec_editor) end + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "opens the editor for the specified Cask" do command = described_class.new("local-caffeine") expect(command).to receive(:exec_editor).with(Hbc::CaskLoader.path("local-caffeine")) @@ -12,7 +18,7 @@ describe Hbc::CLI::Edit, :cask do it "raises an error when given more than one argument" do expect { described_class.new("local-caffeine", "local-transmission") - }.to raise_error(/Only one Cask can be created at a time./) + }.to raise_error(/Only one Cask can be edited at a time\./) end it "raises an exception when the Cask doesnt exist" do @@ -20,20 +26,4 @@ describe Hbc::CLI::Edit, :cask do described_class.run("notacask") }.to raise_error(Hbc::CaskUnavailableError) end - - describe "when no Cask is specified" do - it "raises an exception" do - expect { - described_class.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - describe "when no Cask is specified, but an invalid option" do - it "raises an exception" do - expect { - described_class.run("--notavalidoption") - }.to raise_error(/invalid option/) - end - end end diff --git a/Library/Homebrew/test/cask/cli/fetch_spec.rb b/Library/Homebrew/test/cask/cli/fetch_spec.rb index f71c23fb67..67cf6e61d9 100644 --- a/Library/Homebrew/test/cask/cli/fetch_spec.rb +++ b/Library/Homebrew/test/cask/cli/fetch_spec.rb @@ -1,14 +1,20 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Fetch, :cask do let(:local_transmission) { - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") + Hbc::CaskLoader.load(cask_path("local-transmission")) } let(:local_caffeine) { - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + Hbc::CaskLoader.load(cask_path("local-caffeine")) } + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "allows download the installer of a Cask" do - Hbc::CLI::Fetch.run("local-transmission", "local-caffeine") + described_class.run("local-transmission", "local-caffeine") expect(Hbc::CurlDownloadStrategy.new(local_transmission).cached_location).to exist expect(Hbc::CurlDownloadStrategy.new(local_caffeine).cached_location).to exist end @@ -19,7 +25,7 @@ describe Hbc::CLI::Fetch, :cask do Hbc::Download.new(local_transmission).perform old_ctime = File.stat(download_stategy.cached_location).ctime - Hbc::CLI::Fetch.run("local-transmission") + described_class.run("local-transmission") new_ctime = File.stat(download_stategy.cached_location).ctime expect(old_ctime.to_i).to eq(new_ctime.to_i) @@ -32,7 +38,7 @@ describe Hbc::CLI::Fetch, :cask do old_ctime = File.stat(download_stategy.cached_location).ctime sleep(1) - Hbc::CLI::Fetch.run("local-transmission", "--force") + described_class.run("local-transmission", "--force") download_stategy = Hbc::CurlDownloadStrategy.new(local_transmission) new_ctime = File.stat(download_stategy.cached_location).ctime @@ -41,23 +47,7 @@ describe Hbc::CLI::Fetch, :cask do it "properly handles Casks that are not present" do expect { - Hbc::CLI::Fetch.run("notacask") - }.to raise_error(Hbc::CaskError, "Fetch incomplete.") - end - - describe "when no Cask is specified" do - it "raises an exception" do - expect { - Hbc::CLI::Fetch.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - describe "when no Cask is specified, but an invalid option" do - it "raises an exception" do - expect { - Hbc::CLI::Fetch.run("--notavalidoption") - }.to raise_error(/invalid option/) - end + described_class.run("notacask") + }.to raise_error(Hbc::CaskUnavailableError) end end diff --git a/Library/Homebrew/test/cask/cli/home_spec.rb b/Library/Homebrew/test/cask/cli/home_spec.rb index e985fb6cd8..8960d2acc0 100644 --- a/Library/Homebrew/test/cask/cli/home_spec.rb +++ b/Library/Homebrew/test/cask/cli/home_spec.rb @@ -1,8 +1,12 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Home, :cask do before do allow(described_class).to receive(:open_url) end + it_behaves_like "a command that handles invalid options" + it "opens the homepage for the specified Cask" do expect(described_class).to receive(:open_url).with("http://example.com/local-caffeine") described_class.run("local-caffeine") diff --git a/Library/Homebrew/test/cask/cli/info_spec.rb b/Library/Homebrew/test/cask/cli/info_spec.rb index bffe900ec4..e24eead118 100644 --- a/Library/Homebrew/test/cask/cli/info_spec.rb +++ b/Library/Homebrew/test/cask/cli/info_spec.rb @@ -1,7 +1,13 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Info, :cask do + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "displays some nice info about the specified Cask" do expect { - Hbc::CLI::Info.run("local-caffeine") + described_class.run("local-caffeine") }.to output(<<-EOS.undent).to_stdout local-caffeine: 1.2.3 http://example.com/local-caffeine @@ -10,7 +16,7 @@ describe Hbc::CLI::Info, :cask do ==> Name None ==> Artifacts - Caffeine.app (app) + Caffeine.app (App) EOS end @@ -24,7 +30,7 @@ describe Hbc::CLI::Info, :cask do ==> Name None ==> Artifacts - Caffeine.app (app) + Caffeine.app (App) local-transmission: 2.61 http://example.com/local-transmission Not installed @@ -32,26 +38,20 @@ describe Hbc::CLI::Info, :cask do ==> Name None ==> Artifacts - Transmission.app (app) + Transmission.app (App) EOS } it "displays the info" do expect { - Hbc::CLI::Info.run("local-caffeine", "local-transmission") + described_class.run("local-caffeine", "local-transmission") }.to output(expected_output).to_stdout end - - it "throws away stray options" do - expect { - Hbc::CLI::Info.run("--notavalidoption", "local-caffeine", "local-transmission") - }.to raise_error(/invalid option/) - end end it "should print caveats if the Cask provided one" do expect { - Hbc::CLI::Info.run("with-caveats") + described_class.run("with-caveats") }.to output(<<-EOS.undent).to_stdout with-caveats: 1.2.3 http://example.com/local-caffeine @@ -60,7 +60,7 @@ describe Hbc::CLI::Info, :cask do ==> Name None ==> Artifacts - Caffeine.app (app) + Caffeine.app (App) ==> Caveats Here are some things you might want to know. @@ -77,7 +77,7 @@ describe Hbc::CLI::Info, :cask do it 'should not print "Caveats" section divider if the caveats block has no output' do expect { - Hbc::CLI::Info.run("with-conditional-caveats") + described_class.run("with-conditional-caveats") }.to output(<<-EOS.undent).to_stdout with-conditional-caveats: 1.2.3 http://example.com/local-caffeine @@ -86,23 +86,39 @@ describe Hbc::CLI::Info, :cask do ==> Name None ==> Artifacts - Caffeine.app (app) + Caffeine.app (App) EOS end - describe "when no Cask is specified" do - it "raises an exception" do - expect { - Hbc::CLI::Info.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end + it "prints languages specified in the Cask" do + expect { + described_class.run("with-languages") + }.to output(<<-EOS.undent).to_stdout + with-languages: 1.2.3 + http://example.com/local-caffeine + Not installed + From: https://github.com/caskroom/homebrew-spec/blob/master/Casks/with-languages.rb + ==> Name + None + ==> Languages + zh, en-US + ==> Artifacts + Caffeine.app (App) + EOS end - describe "when no Cask is specified, but an invalid option" do - it "raises an exception" do - expect { - Hbc::CLI::Info.run("--notavalidoption") - }.to raise_error(/invalid option/) - end + it 'does not print "Languages" section divider if the languages block has no output' do + expect { + described_class.run("without-languages") + }.to output(<<-EOS.undent).to_stdout + without-languages: 1.2.3 + http://example.com/local-caffeine + Not installed + From: https://github.com/caskroom/homebrew-spec/blob/master/Casks/without-languages.rb + ==> Name + None + ==> Artifacts + Caffeine.app (App) + EOS end end diff --git a/Library/Homebrew/test/cask/cli/install_spec.rb b/Library/Homebrew/test/cask/cli/install_spec.rb index 64feacce91..c918a35296 100644 --- a/Library/Homebrew/test/cask/cli/install_spec.rb +++ b/Library/Homebrew/test/cask/cli/install_spec.rb @@ -1,4 +1,10 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Install, :cask do + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "displays the installation progress" do output = Regexp.new <<-EOS.undent ==> Downloading file:.*caffeine.zip @@ -9,103 +15,65 @@ describe Hbc::CLI::Install, :cask do EOS expect { - Hbc::CLI::Install.run("local-caffeine") + described_class.run("local-caffeine") }.to output(output).to_stdout end it "allows staging and activation of multiple Casks at once" do - Hbc::CLI::Install.run("local-transmission", "local-caffeine") + described_class.run("local-transmission", "local-caffeine") - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).to be_installed expect(Hbc.appdir.join("Transmission.app")).to be_a_directory - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb")).to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-caffeine"))).to be_installed expect(Hbc.appdir.join("Caffeine.app")).to be_a_directory end it "skips double install (without nuking existing installation)" do - Hbc::CLI::Install.run("local-transmission") - Hbc::CLI::Install.run("local-transmission") - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).to be_installed + described_class.run("local-transmission") + described_class.run("local-transmission") + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).to be_installed end it "prints a warning message on double install" do - Hbc::CLI::Install.run("local-transmission") + described_class.run("local-transmission") expect { - Hbc::CLI::Install.run("local-transmission") + described_class.run("local-transmission") }.to output(/Warning: Cask 'local-transmission' is already installed./).to_stderr end it "allows double install with --force" do - Hbc::CLI::Install.run("local-transmission") + described_class.run("local-transmission") expect { expect { - Hbc::CLI::Install.run("local-transmission", "--force") + described_class.run("local-transmission", "--force") }.to output(/It seems there is already an App at.*overwriting\./).to_stderr }.to output(/local-transmission was successfully installed!/).to_stdout end it "skips dependencies with --skip-cask-deps" do - Hbc::CLI::Install.run("with-depends-on-cask-multiple", "--skip-cask-deps") - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-cask-multiple.rb")).to be_installed - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb")).not_to be_installed - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).not_to be_installed + described_class.run("with-depends-on-cask-multiple", "--skip-cask-deps") + expect(Hbc::CaskLoader.load(cask_path("with-depends-on-cask-multiple"))).to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-caffeine"))).not_to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).not_to be_installed end it "properly handles Casks that are not present" do expect { - Hbc::CLI::Install.run("notacask") - }.to raise_error(Hbc::CaskError, "Install incomplete.") + described_class.run("notacask") + }.to raise_error(Hbc::CaskUnavailableError) end it "returns a suggestion for a misspelled Cask" do expect { - begin - Hbc::CLI::Install.run("localcaffeine") - rescue Hbc::CaskError - nil - end - }.to output(/Cask 'localcaffeine' is unavailable: No Cask with this name exists\. Did you mean:\nlocal-caffeine/).to_stderr + described_class.run("localcaffeine") + }.to raise_error(Hbc::CaskUnavailableError, /Cask 'localcaffeine' is unavailable: No Cask with this name exists\. Did you mean “local-caffeine”?/) end it "returns multiple suggestions for a Cask fragment" do expect { - begin - Hbc::CLI::Install.run("local-caf") - rescue Hbc::CaskError - nil - end - }.to output(/Cask 'local-caf' is unavailable: No Cask with this name exists\. Did you mean one of:\nlocal-caffeine/).to_stderr - end - - describe "when no Cask is specified" do - with_options = lambda do |options| - it "raises an exception" do - expect { - Hbc::CLI::Install.run(*options) - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - describe "without options" do - with_options.call([]) - end - - describe "with --force" do - with_options.call(["--force"]) - end - - describe "with --skip-cask-deps" do - with_options.call(["--skip-cask-deps"]) - end - - describe "with an invalid option" do - it "raises an error" do - expect { - Hbc::CLI::Install.run("--notavalidoption") - }.to raise_error(/invalid option/) - end - end + described_class.run("local") + }.to raise_error(Hbc::CaskUnavailableError, /Cask 'local' is unavailable: No Cask with this name exists\. Did you mean one of these\?\nlocal-caffeine\nlocal-transmission/) end end diff --git a/Library/Homebrew/test/cask/cli/list_spec.rb b/Library/Homebrew/test/cask/cli/list_spec.rb index ecca3035f5..301ca9b899 100644 --- a/Library/Homebrew/test/cask/cli/list_spec.rb +++ b/Library/Homebrew/test/cask/cli/list_spec.rb @@ -1,4 +1,8 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::List, :cask do + it_behaves_like "a command that handles invalid options" + it "lists the installed Casks in a pretty fashion" do casks = %w[local-caffeine local-transmission].map { |c| Hbc::CaskLoader.load(c) } @@ -7,13 +11,33 @@ describe Hbc::CLI::List, :cask do end expect { - Hbc::CLI::List.run + described_class.run }.to output(<<-EOS.undent).to_stdout local-caffeine local-transmission EOS end + it "lists full names" do + casks = %w[ + local-caffeine + third-party/tap/third-party-cask + local-transmission + ].map { |c| Hbc::CaskLoader.load(c) } + + casks.each do |c| + InstallHelper.install_with_caskfile(c) + end + + expect { + described_class.run("--full-name") + }.to output(<<-EOS.undent).to_stdout + local-caffeine + local-transmission + third-party/tap/third-party-cask + EOS + end + describe "lists versions" do let(:casks) { ["local-caffeine", "local-transmission"] } let(:expected_output) { @@ -29,29 +53,31 @@ describe Hbc::CLI::List, :cask do it "of all installed Casks" do expect { - Hbc::CLI::List.run("--versions") + described_class.run("--versions") }.to output(expected_output).to_stdout end it "of given Casks" do expect { - Hbc::CLI::List.run("--versions", "local-caffeine", "local-transmission") + described_class.run("--versions", "local-caffeine", "local-transmission") }.to output(expected_output).to_stdout end end describe "given a set of installed Casks" do - let(:caffeine) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") } - let(:transmission) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") } + let(:caffeine) { Hbc::CaskLoader.load(cask_path("local-caffeine")) } + let(:transmission) { Hbc::CaskLoader.load(cask_path("local-transmission")) } let(:casks) { [caffeine, transmission] } it "lists the installed files for those Casks" do casks.each(&InstallHelper.method(:install_without_artifacts_with_caskfile)) - Hbc::Artifact::App.new(transmission).install_phase + transmission.artifacts.select { |a| a.is_a?(Hbc::Artifact::App) }.each do |artifact| + artifact.install_phase(command: Hbc::NeverSudoSystemCommand, force: false) + end expect { - Hbc::CLI::List.run("local-transmission", "local-caffeine") + described_class.run("local-transmission", "local-caffeine") }.to output(<<-EOS.undent).to_stdout ==> Apps #{Hbc.appdir.join("Transmission.app")} (#{Hbc.appdir.join("Transmission.app").abv}) diff --git a/Library/Homebrew/test/cask/cli/options_spec.rb b/Library/Homebrew/test/cask/cli/options_spec.rb index 98eb05f7e9..82d830795b 100644 --- a/Library/Homebrew/test/cask/cli/options_spec.rb +++ b/Library/Homebrew/test/cask/cli/options_spec.rb @@ -1,6 +1,6 @@ describe Hbc::CLI, :cask do it "supports setting the appdir" do - Hbc::CLI.new.process_options("help", "--appdir=/some/path/foo") + described_class.new.process_options("help", "--appdir=/some/path/foo") expect(Hbc.appdir).to eq(Pathname.new("/some/path/foo")) end @@ -8,13 +8,13 @@ describe Hbc::CLI, :cask do it "supports setting the appdir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--appdir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.appdir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the prefpanedir" do - Hbc::CLI.new.process_options("help", "--prefpanedir=/some/path/foo") + described_class.new.process_options("help", "--prefpanedir=/some/path/foo") expect(Hbc.prefpanedir).to eq(Pathname.new("/some/path/foo")) end @@ -22,13 +22,13 @@ describe Hbc::CLI, :cask do it "supports setting the prefpanedir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--prefpanedir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.prefpanedir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the qlplugindir" do - Hbc::CLI.new.process_options("help", "--qlplugindir=/some/path/foo") + described_class.new.process_options("help", "--qlplugindir=/some/path/foo") expect(Hbc.qlplugindir).to eq(Pathname.new("/some/path/foo")) end @@ -36,13 +36,13 @@ describe Hbc::CLI, :cask do it "supports setting the qlplugindir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--qlplugindir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.qlplugindir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the colorpickerdir" do - Hbc::CLI.new.process_options("help", "--colorpickerdir=/some/path/foo") + described_class.new.process_options("help", "--colorpickerdir=/some/path/foo") expect(Hbc.colorpickerdir).to eq(Pathname.new("/some/path/foo")) end @@ -50,13 +50,13 @@ describe Hbc::CLI, :cask do it "supports setting the colorpickerdir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--colorpickerdir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.colorpickerdir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the dictionarydir" do - Hbc::CLI.new.process_options("help", "--dictionarydir=/some/path/foo") + described_class.new.process_options("help", "--dictionarydir=/some/path/foo") expect(Hbc.dictionarydir).to eq(Pathname.new("/some/path/foo")) end @@ -64,13 +64,13 @@ describe Hbc::CLI, :cask do it "supports setting the dictionarydir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--dictionarydir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.dictionarydir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the fontdir" do - Hbc::CLI.new.process_options("help", "--fontdir=/some/path/foo") + described_class.new.process_options("help", "--fontdir=/some/path/foo") expect(Hbc.fontdir).to eq(Pathname.new("/some/path/foo")) end @@ -78,13 +78,13 @@ describe Hbc::CLI, :cask do it "supports setting the fontdir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--fontdir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.fontdir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the servicedir" do - Hbc::CLI.new.process_options("help", "--servicedir=/some/path/foo") + described_class.new.process_options("help", "--servicedir=/some/path/foo") expect(Hbc.servicedir).to eq(Pathname.new("/some/path/foo")) end @@ -92,13 +92,13 @@ describe Hbc::CLI, :cask do it "supports setting the servicedir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--servicedir=/some/path/bar" - Hbc::CLI.new.process_options("help") + described_class.new.process_options("help") expect(Hbc.servicedir).to eq(Pathname.new("/some/path/bar")) end it "allows additional options to be passed through" do - rest = Hbc::CLI.new.process_options("edit", "foo", "--create", "--appdir=/some/path/qux") + rest = described_class.new.process_options("edit", "foo", "--create", "--appdir=/some/path/qux") expect(Hbc.appdir).to eq(Pathname.new("/some/path/qux")) expect(rest).to eq(%w[edit foo --create]) @@ -106,7 +106,7 @@ describe Hbc::CLI, :cask do describe "--help" do it "sets the Cask help method to true" do - command = Hbc::CLI.new("foo", "--help") + command = described_class.new("foo", "--help") expect(command.help?).to be true end end diff --git a/Library/Homebrew/test/cask/cli/outdated_spec.rb b/Library/Homebrew/test/cask/cli/outdated_spec.rb index 946092f894..5bbf18d21a 100644 --- a/Library/Homebrew/test/cask/cli/outdated_spec.rb +++ b/Library/Homebrew/test/cask/cli/outdated_spec.rb @@ -1,11 +1,13 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Outdated, :cask do let(:installed) do [ - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb"), - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/outdated/local-caffeine.rb"), - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/outdated/local-transmission.rb"), - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/version-latest-string.rb"), - Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/outdated/auto-updates.rb"), + Hbc::CaskLoader.load(cask_path("basic-cask")), + Hbc::CaskLoader.load(cask_path("outdated/local-caffeine")), + Hbc::CaskLoader.load(cask_path("outdated/local-transmission")), + Hbc::CaskLoader.load(cask_path("version-latest-string")), + Hbc::CaskLoader.load(cask_path("outdated/auto-updates")), ] end @@ -15,6 +17,8 @@ describe Hbc::CLI::Outdated, :cask do allow_any_instance_of(described_class).to receive(:verbose?).and_return(true) end + it_behaves_like "a command that handles invalid options" + describe 'without --greedy it ignores the Casks with "vesion latest" or "auto_updates true"' do it "checks all the installed Casks when no token is provided" do expect { @@ -70,7 +74,7 @@ describe Hbc::CLI::Outdated, :cask do end it 'does not include the Casks with "auto_updates true" when the version did not change' do - cask = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/auto-updates.rb") + cask = Hbc::CaskLoader.load(cask_path("auto-updates")) InstallHelper.install_with_caskfile(cask) expect { diff --git a/Library/Homebrew/test/cask/cli/reinstall_spec.rb b/Library/Homebrew/test/cask/cli/reinstall_spec.rb index 3a9c3e2f53..95294b6958 100644 --- a/Library/Homebrew/test/cask/cli/reinstall_spec.rb +++ b/Library/Homebrew/test/cask/cli/reinstall_spec.rb @@ -1,6 +1,10 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Reinstall, :cask do + it_behaves_like "a command that handles invalid options" + it "displays the reinstallation progress" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) Hbc::Installer.new(caffeine).install @@ -23,16 +27,16 @@ describe Hbc::CLI::Reinstall, :cask do it "allows reinstalling a Cask" do Hbc::CLI::Install.run("local-transmission") - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).to be_installed Hbc::CLI::Reinstall.run("local-transmission") - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).to be_installed end it "allows reinstalling a non installed Cask" do - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).not_to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).not_to be_installed Hbc::CLI::Reinstall.run("local-transmission") - expect(Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb")).to be_installed + expect(Hbc::CaskLoader.load(cask_path("local-transmission"))).to be_installed end end diff --git a/Library/Homebrew/test/cask/cli/search_spec.rb b/Library/Homebrew/test/cask/cli/search_spec.rb index e237ad464f..a4f796f3c6 100644 --- a/Library/Homebrew/test/cask/cli/search_spec.rb +++ b/Library/Homebrew/test/cask/cli/search_spec.rb @@ -1,9 +1,15 @@ +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Search, :cask do before(:each) do allow(Tty).to receive(:width).and_return(0) end + it_behaves_like "a command that handles invalid options" + it "lists the available Casks that match the search term" do + allow(GitHub).to receive(:search_code).and_return([]) + expect { Hbc::CLI::Search.run("local") }.to output(<<-EOS.undent).to_stdout.as_tty @@ -14,6 +20,8 @@ describe Hbc::CLI::Search, :cask do end it "outputs a plain list when stdout is not a TTY" do + allow(GitHub).to receive(:search_code).and_return([]) + expect { Hbc::CLI::Search.run("local") }.to output(<<-EOS.undent).to_stdout @@ -22,16 +30,37 @@ 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 +84,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/cask/cli/shared_examples/invalid_option.rb b/Library/Homebrew/test/cask/cli/shared_examples/invalid_option.rb new file mode 100644 index 0000000000..12a05be922 --- /dev/null +++ b/Library/Homebrew/test/cask/cli/shared_examples/invalid_option.rb @@ -0,0 +1,15 @@ +shared_examples "a command that handles invalid options" do + context "when an invalid option is specified" do + it "raises an exception when no Cask is specified" do + expect { + described_class.run("--not-a-valid-option") + }.to raise_error("invalid option: --not-a-valid-option") + end + + it "raises an exception when a Cask is specified" do + expect { + described_class.run("--not-a-valid-option", "basic-cask") + }.to raise_error("invalid option: --not-a-valid-option") + end + end +end diff --git a/Library/Homebrew/test/cask/cli/shared_examples/requires_cask_token.rb b/Library/Homebrew/test/cask/cli/shared_examples/requires_cask_token.rb new file mode 100644 index 0000000000..dc1e471e5a --- /dev/null +++ b/Library/Homebrew/test/cask/cli/shared_examples/requires_cask_token.rb @@ -0,0 +1,9 @@ +shared_examples "a command that requires a Cask token" do + context "when no Cask is specified" do + it "raises an exception " do + expect { + described_class.run + }.to raise_error(Hbc::CaskUnspecifiedError, "This command requires a Cask token.") + end + end +end diff --git a/Library/Homebrew/test/cask/cli/style_spec.rb b/Library/Homebrew/test/cask/cli/style_spec.rb index 2007b87d71..12cd348a09 100644 --- a/Library/Homebrew/test/cask/cli/style_spec.rb +++ b/Library/Homebrew/test/cask/cli/style_spec.rb @@ -1,11 +1,13 @@ require "open3" require "rubygems" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Style, :cask do let(:args) { [] } let(:cli) { described_class.new(*args) } - around(&:run) + it_behaves_like "a command that handles invalid options" describe "#run" do subject { cli.run } diff --git a/Library/Homebrew/test/cask/cli/uninstall_spec.rb b/Library/Homebrew/test/cask/cli/uninstall_spec.rb index 1a1c57e88b..80b7edbd38 100644 --- a/Library/Homebrew/test/cask/cli/uninstall_spec.rb +++ b/Library/Homebrew/test/cask/cli/uninstall_spec.rb @@ -1,6 +1,12 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Uninstall, :cask do + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "displays the uninstallation progress" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) Hbc::Installer.new(caffeine).install @@ -10,31 +16,29 @@ describe Hbc::CLI::Uninstall, :cask do EOS expect { - Hbc::CLI::Uninstall.run("local-caffeine") + described_class.run("local-caffeine") }.to output(output).to_stdout end it "shows an error when a bad Cask is provided" do - expect { Hbc::CLI::Uninstall.run("notacask") } - .to output(/is unavailable/).to_stderr - .and raise_error(Hbc::CaskError, "Uninstall incomplete.") + expect { described_class.run("notacask") } + .to raise_error(Hbc::CaskUnavailableError, /is unavailable/) end it "shows an error when a Cask is provided that's not installed" do - expect { Hbc::CLI::Uninstall.run("local-caffeine") } - .to output(/is not installed/).to_stderr - .and raise_error(Hbc::CaskError, "Uninstall incomplete.") + expect { described_class.run("local-caffeine") } + .to raise_error(Hbc::CaskNotInstalledError, /is not installed/) end it "tries anyway on a non-present Cask when --force is given" do expect { - Hbc::CLI::Uninstall.run("local-caffeine", "--force") + described_class.run("local-caffeine", "--force") }.not_to raise_error end it "can uninstall and unlink multiple Casks at once" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") - transmission = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) + transmission = Hbc::CaskLoader.load(cask_path("local-transmission")) Hbc::Installer.new(caffeine).install Hbc::Installer.new(transmission).install @@ -42,7 +46,7 @@ describe Hbc::CLI::Uninstall, :cask do expect(caffeine).to be_installed expect(transmission).to be_installed - Hbc::CLI::Uninstall.run("local-caffeine", "local-transmission") + described_class.run("local-caffeine", "local-transmission") expect(caffeine).not_to be_installed expect(Hbc.appdir.join("Transmission.app")).not_to exist @@ -51,7 +55,7 @@ describe Hbc::CLI::Uninstall, :cask do end it "calls `uninstall` before removing artifacts" do - cask = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-script-app.rb") + cask = Hbc::CaskLoader.load(cask_path("with-uninstall-script-app")) Hbc::Installer.new(cask).install @@ -59,7 +63,7 @@ describe Hbc::CLI::Uninstall, :cask do expect(Hbc.appdir.join("MyFancyApp.app")).to exist expect { - Hbc::CLI::Uninstall.run("with-uninstall-script-app") + described_class.run("with-uninstall-script-app") }.not_to raise_error expect(cask).not_to be_installed @@ -67,7 +71,7 @@ describe Hbc::CLI::Uninstall, :cask do end it "can uninstall Casks when the uninstall script is missing, but only when using `--force`" do - cask = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-uninstall-script-app.rb") + cask = Hbc::CaskLoader.load(cask_path("with-uninstall-script-app")) Hbc::Installer.new(cask).install @@ -75,14 +79,13 @@ describe Hbc::CLI::Uninstall, :cask do Hbc.appdir.join("MyFancyApp.app").rmtree - expect { Hbc::CLI::Uninstall.run("with-uninstall-script-app") } - .to output(/does not exist/).to_stderr - .and raise_error(Hbc::CaskError, "Uninstall incomplete.") + expect { described_class.run("with-uninstall-script-app") } + .to raise_error(Hbc::CaskError, /uninstall script .* does not exist/) expect(cask).to be_installed expect { - Hbc::CLI::Uninstall.run("with-uninstall-script-app", "--force") + described_class.run("with-uninstall-script-app", "--force") }.not_to raise_error expect(cask).not_to be_installed @@ -115,13 +118,13 @@ describe Hbc::CLI::Uninstall, :cask do end it "uninstalls one version at a time" do - Hbc::CLI::Uninstall.run("versioned-cask") + described_class.run("versioned-cask") expect(caskroom_path.join(first_installed_version)).to exist expect(caskroom_path.join(last_installed_version)).not_to exist expect(caskroom_path).to exist - Hbc::CLI::Uninstall.run("versioned-cask") + described_class.run("versioned-cask") expect(caskroom_path.join(first_installed_version)).not_to exist expect(caskroom_path).not_to exist @@ -130,7 +133,7 @@ describe Hbc::CLI::Uninstall, :cask do it "displays a message when versions remain installed" do expect { expect { - Hbc::CLI::Uninstall.run("versioned-cask") + described_class.run("versioned-cask") }.not_to output.to_stderr }.to output(/#{token} #{first_installed_version} is still installed./).to_stdout end @@ -160,26 +163,10 @@ describe Hbc::CLI::Uninstall, :cask do end it "can still uninstall those Casks" do - Hbc::CLI::Uninstall.run("ive-been-renamed") + described_class.run("ive-been-renamed") expect(app).not_to exist expect(caskroom_path).not_to exist end end - - describe "when no Cask is specified" do - it "raises an exception" do - expect { - Hbc::CLI::Uninstall.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - describe "when no Cask is specified, but an invalid option" do - it "raises an exception" do - expect { - Hbc::CLI::Uninstall.run("--notavalidoption") - }.to raise_error(/invalid option/) - end - end end diff --git a/Library/Homebrew/test/cask/cli/zap_spec.rb b/Library/Homebrew/test/cask/cli/zap_spec.rb index fdc5b41250..05c882854a 100644 --- a/Library/Homebrew/test/cask/cli/zap_spec.rb +++ b/Library/Homebrew/test/cask/cli/zap_spec.rb @@ -1,13 +1,18 @@ +require_relative "shared_examples/requires_cask_token" +require_relative "shared_examples/invalid_option" + describe Hbc::CLI::Zap, :cask do + it_behaves_like "a command that requires a Cask token" + it_behaves_like "a command that handles invalid options" + it "shows an error when a bad Cask is provided" do - expect { Hbc::CLI::Zap.run("notacask") } - .to output(/is unavailable/).to_stderr - .and raise_error(Hbc::CaskError, "Zap incomplete.") + expect { described_class.run("notacask") } + .to raise_error(Hbc::CaskUnavailableError, /is unavailable/) end it "can zap and unlink multiple Casks at once" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") - transmission = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) + transmission = Hbc::CaskLoader.load(cask_path("local-transmission")) Hbc::Installer.new(caffeine).install Hbc::Installer.new(transmission).install @@ -15,7 +20,7 @@ describe Hbc::CLI::Zap, :cask do expect(caffeine).to be_installed expect(transmission).to be_installed - Hbc::CLI::Zap.run("local-caffeine", "local-transmission") + described_class.run("local-caffeine", "local-transmission") expect(caffeine).not_to be_installed expect(Hbc.appdir.join("Caffeine.app")).not_to be_a_symlink @@ -46,20 +51,4 @@ describe Hbc::CLI::Zap, :cask do # # with_zap.wont_be :installed? # end - - describe "when no Cask is specified" do - it "raises an exception" do - expect { - Hbc::CLI::Zap.run - }.to raise_error(Hbc::CaskUnspecifiedError) - end - end - - describe "when no Cask is specified, but an invalid option" do - it "raises an exception" do - expect { - Hbc::CLI::Zap.run("--notavalidoption") - }.to raise_error(/invalid option/) - end - end end diff --git a/Library/Homebrew/test/cask/conflicts_with_spec.rb b/Library/Homebrew/test/cask/conflicts_with_spec.rb new file mode 100644 index 0000000000..81c22ded68 --- /dev/null +++ b/Library/Homebrew/test/cask/conflicts_with_spec.rb @@ -0,0 +1,23 @@ +describe "conflicts_with", :cask do + describe "conflicts_with cask" do + let(:local_caffeine) { + Hbc::CaskLoader.load(cask_path("local-caffeine")) + } + + let(:with_conflicts_with) { + Hbc::CaskLoader.load(cask_path("with-conflicts-with")) + } + + it "installs the dependency of a Cask and the Cask itself" do + Hbc::Installer.new(local_caffeine).install + + expect(local_caffeine).to be_installed + + expect { + Hbc::Installer.new(with_conflicts_with).install + }.to raise_error(Hbc::CaskConflictError, "Cask 'with-conflicts-with' conflicts with 'local-caffeine'.") + + expect(with_conflicts_with).not_to be_installed + end + end +end diff --git a/Library/Homebrew/test/cask/container/dmg_spec.rb b/Library/Homebrew/test/cask/container/dmg_spec.rb index 4f3f570710..df99a6264a 100644 --- a/Library/Homebrew/test/cask/container/dmg_spec.rb +++ b/Library/Homebrew/test/cask/container/dmg_spec.rb @@ -1,7 +1,7 @@ describe Hbc::Container::Dmg, :cask do describe "#mount!" do it "does not store nil mounts for dmgs with extra data" do - transmission = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") + transmission = Hbc::CaskLoader.load(cask_path("local-transmission")) dmg = Hbc::Container::Dmg.new( transmission, diff --git a/Library/Homebrew/test/cask/depends_on_spec.rb b/Library/Homebrew/test/cask/depends_on_spec.rb index c603cf6e14..453a761f7e 100644 --- a/Library/Homebrew/test/cask/depends_on_spec.rb +++ b/Library/Homebrew/test/cask/depends_on_spec.rb @@ -9,12 +9,15 @@ describe "Satisfy Dependencies and Requirements", :cask do describe "depends_on cask" do context "when depends_on cask is cyclic" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-cask-cyclic.rb") } - it { is_expected.to raise_error(Hbc::CaskCyclicDependencyError) } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-cask-cyclic")) } + it { + is_expected.to raise_error(Hbc::CaskCyclicDependencyError, + "Cask 'with-depends-on-cask-cyclic' includes cyclic dependencies on other Casks: with-depends-on-cask-cyclic-helper") + } end context do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-cask.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-cask")) } let(:dependency) { Hbc::CaskLoader.load(cask.depends_on.cask.first) } it "installs the dependency of a Cask and the Cask itself" do @@ -27,34 +30,34 @@ describe "Satisfy Dependencies and Requirements", :cask do describe "depends_on macos" do context "given an array" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-macos-array.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-macos-array")) } it { is_expected.not_to raise_error } end - context "given a comparisson" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-macos-comparison.rb") } + context "given a comparison" do + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-macos-comparison")) } it { is_expected.not_to raise_error } end context "given a string" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-macos-string.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-macos-string")) } it { is_expected.not_to raise_error } end context "given a symbol" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-macos-symbol.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-macos-symbol")) } it { is_expected.not_to raise_error } end context "when not satisfied" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-macos-failure.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-macos-failure")) } it { is_expected.to raise_error(Hbc::CaskError) } end end describe "depends_on arch" do context "when satisfied" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-arch.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-arch")) } it { is_expected.not_to raise_error } end end @@ -65,21 +68,21 @@ describe "Satisfy Dependencies and Requirements", :cask do end context "when satisfied" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-x11.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-x11")) } let(:x11_installed) { true } it { is_expected.not_to raise_error } end context "when not satisfied" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-x11.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-x11")) } let(:x11_installed) { false } it { is_expected.to raise_error(Hbc::CaskX11DependencyError) } end context "when depends_on x11: false" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-depends-on-x11-false.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("with-depends-on-x11-false")) } let(:x11_installed) { false } it { is_expected.not_to raise_error } diff --git a/Library/Homebrew/test/cask/download_strategy_spec.rb b/Library/Homebrew/test/cask/download_strategy_spec.rb index 222352c07e..17da1e36e4 100644 --- a/Library/Homebrew/test/cask/download_strategy_spec.rb +++ b/Library/Homebrew/test/cask/download_strategy_spec.rb @@ -26,9 +26,12 @@ describe "download strategies", :cask do downloader.fetch expect(downloader).to have_received(:curl).with( + "--location", + "--remote-time", + "--continue-at", "-", + "--output", kind_of(Pathname), cask.url.to_s, - "-C", 0, - "-o", kind_of(Pathname) + user_agent: :default ) end @@ -36,25 +39,25 @@ describe "download strategies", :cask do let(:url_options) { { user_agent: "Mozilla/25.0.1" } } it "adds the appropriate curl args" do - curl_args = [] - allow(downloader).to receive(:curl) { |*args| curl_args = args } + expect(downloader).to receive(:safe_system) { |*args| + expect(args.each_cons(2)).to include(["--user-agent", "Mozilla/25.0.1"]) + } downloader.fetch - - expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/25.0.1"]) end end context "with a generalized fake user agent" do + alias_matcher :a_string_matching, :match + let(:url_options) { { user_agent: :fake } } it "adds the appropriate curl args" do - curl_args = [] - allow(downloader).to receive(:curl) { |*args| curl_args = args } + expect(downloader).to receive(:safe_system) { |*args| + expect(args.each_cons(2).to_a).to include(["--user-agent", a_string_matching(/Mozilla.*Mac OS X 10.*AppleWebKit/)]) + } downloader.fetch - - expect(curl_args.each_cons(2)).to include(["-A", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io"]) end end @@ -138,7 +141,7 @@ describe "download strategies", :cask do describe Hbc::SubversionDownloadStrategy do let(:url_options) { { using: :svn } } let(:fake_system_command) { class_double(Hbc::SystemCommand) } - let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, fake_system_command) } + let(:downloader) { Hbc::SubversionDownloadStrategy.new(cask, command: fake_system_command) } before do allow(fake_system_command).to receive(:run!) end @@ -147,7 +150,7 @@ describe "download strategies", :cask do allow(downloader).to receive(:compress) allow(downloader).to receive(:fetch_repo) - expect(downloader.fetch).to equal(downloader.tarball_path) + expect(downloader.fetch).to equal(downloader.cached_location) end it "calls fetch_repo with default arguments for a simple Cask" do @@ -237,44 +240,5 @@ describe "download strategies", :cask do ) end end - - it "runs tar to serialize svn downloads" do - # sneaky stub to remake the directory, since homebrew code removes it - # before tar is called - allow(downloader).to receive(:fetch_repo) { - downloader.cached_location.mkdir - } - - downloader.fetch - - expect(fake_system_command).to have_received(:run!).with( - "/usr/bin/tar", - hash_including(args: [ - '-s/^\\.//', - "--exclude", - ".svn", - "-cf", - downloader.tarball_path, - "--", - ".", - ]), - ) - end end - - # does not work yet, because (for unknown reasons), the tar command - # returns an error code when running under the test suite - # it 'creates a tarball matching the expected checksum' do - # cask = Hbc::CaskLoader.load('svn-download-check-cask') - # downloader = Hbc::SubversionDownloadStrategy.new(cask) - # # special mocking required for tar to have something to work with - # def downloader.fetch_repo(target, url, revision = nil, ignore_externals=false) - # target.mkpath - # FileUtils.touch(target.join('empty_file.txt')) - # File.utime(1000,1000,target.join('empty_file.txt')) - # end - # expect(downloader.fetch).to equal(downloader.tarball_path) - # d = Hbc::Download.new(cask) - # d.send(:_check_sums, downloader.tarball_path, cask.sums) - # end end diff --git a/Library/Homebrew/test/cask/dsl/appcast_spec.rb b/Library/Homebrew/test/cask/dsl/appcast_spec.rb index b8903b1be8..bf703eba2d 100644 --- a/Library/Homebrew/test/cask/dsl/appcast_spec.rb +++ b/Library/Homebrew/test/cask/dsl/appcast_spec.rb @@ -4,7 +4,7 @@ describe Hbc::DSL::Appcast do subject { described_class.new(url, params) } let(:url) { "http://example.com" } - let(:uri) { Hbc::UnderscoreSupportingURI.parse(url) } + let(:uri) { URI(url) } let(:params) { {} } describe "#to_s" do @@ -33,13 +33,18 @@ describe Hbc::DSL::Appcast do describe "#calculate_checkpoint" do before do - expect(Hbc::SystemCommand).to receive(:run).with(*cmd_args).and_return(cmd_result) + expect(Hbc::SystemCommand).to receive(:run) do |executable, **options| + expect(executable).to eq "/usr/bin/curl" + expect(options[:args]).to include(*cmd_args) + expect(options[:print_stderr]).to be false + cmd_result + end allow(cmd_result).to receive(:success?).and_return(cmd_success) allow(cmd_result).to receive(:stdout).and_return(cmd_stdout) end context "when server returns a successful HTTP status" do - let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] } + let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } let(:cmd_result) { double("Hbc::SystemCommand::Result") } let(:cmd_success) { true } let(:cmd_stdout) { "hello world" } @@ -56,7 +61,7 @@ describe Hbc::DSL::Appcast do end context "when server returns a non-successful HTTP status" do - let(:cmd_args) { ["/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", Hbc::URL::FAKE_USER_AGENT, "--fail", uri], print_stderr: false] } + let(:cmd_args) { [HOMEBREW_USER_AGENT_FAKE_SAFARI, "--compressed", "--location", "--fail", uri] } let(:cmd_result) { double("Hbc::SystemCommand::Result") } let(:cmd_success) { false } let(:cmd_stdout) { "some error message from the server" } diff --git a/Library/Homebrew/test/cask/dsl/caveats_spec.rb b/Library/Homebrew/test/cask/dsl/caveats_spec.rb index aa662e4d02..1b82d98211 100644 --- a/Library/Homebrew/test/cask/dsl/caveats_spec.rb +++ b/Library/Homebrew/test/cask/dsl/caveats_spec.rb @@ -1,7 +1,7 @@ require "test/support/helper/spec/shared_examples/hbc_dsl_base" describe Hbc::DSL::Caveats, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("basic-cask")) } let(:dsl) { Hbc::DSL::Caveats.new(cask) } it_behaves_like Hbc::DSL::Base diff --git a/Library/Homebrew/test/cask/dsl/postflight_spec.rb b/Library/Homebrew/test/cask/dsl/postflight_spec.rb index d2b080ca30..4ac3ae7cf7 100644 --- a/Library/Homebrew/test/cask/dsl/postflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/postflight_spec.rb @@ -2,7 +2,7 @@ require "test/support/helper/spec/shared_examples/hbc_dsl_base" require "test/support/helper/spec/shared_examples/hbc_staged" describe Hbc::DSL::Postflight, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("basic-cask")) } let(:dsl) { Hbc::DSL::Postflight.new(cask, Hbc::FakeSystemCommand) } it_behaves_like Hbc::DSL::Base diff --git a/Library/Homebrew/test/cask/dsl/preflight_spec.rb b/Library/Homebrew/test/cask/dsl/preflight_spec.rb index b93be95ff8..f78944c504 100644 --- a/Library/Homebrew/test/cask/dsl/preflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/preflight_spec.rb @@ -2,7 +2,7 @@ require "test/support/helper/spec/shared_examples/hbc_dsl_base" require "test/support/helper/spec/shared_examples/hbc_staged" describe Hbc::DSL::Preflight, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("basic-cask")) } let(:dsl) { Hbc::DSL::Preflight.new(cask, Hbc::FakeSystemCommand) } it_behaves_like Hbc::DSL::Base diff --git a/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb b/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb index f89a181ce2..b2af700dbd 100644 --- a/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb @@ -1,7 +1,7 @@ require "test/support/helper/spec/shared_examples/hbc_dsl_base" describe Hbc::DSL::UninstallPostflight, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("basic-cask")) } let(:dsl) { Hbc::DSL::UninstallPostflight.new(cask, Hbc::FakeSystemCommand) } it_behaves_like Hbc::DSL::Base diff --git a/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb b/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb index 15a0ea1565..8e7fa47eb4 100644 --- a/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb @@ -2,7 +2,7 @@ require "test/support/helper/spec/shared_examples/hbc_dsl_base" require "test/support/helper/spec/shared_examples/hbc_staged" describe Hbc::DSL::UninstallPreflight, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("basic-cask")) } let(:dsl) { Hbc::DSL::UninstallPreflight.new(cask, Hbc::FakeSystemCommand) } it_behaves_like Hbc::DSL::Base diff --git a/Library/Homebrew/test/cask/dsl_spec.rb b/Library/Homebrew/test/cask/dsl_spec.rb index 7f2207a877..28cf6f4b2b 100644 --- a/Library/Homebrew/test/cask/dsl_spec.rb +++ b/Library/Homebrew/test/cask/dsl_spec.rb @@ -1,5 +1,5 @@ describe Hbc::DSL, :cask do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/#{token}.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path(token.to_s)) } let(:token) { "basic-cask" } context "stanzas" do @@ -177,6 +177,36 @@ describe Hbc::DSL, :cask do expect(cask.call.sha256).to eq("xyz789") expect(cask.call.url.to_s).to eq("https://example.org/en-US.zip") end + + it "returns an empty array if no languages are specified" do + cask = lambda do + Hbc::Cask.new("cask-with-apps") do + url "https://example.org/file.zip" + end + end + + expect(cask.call.languages).to be_empty + end + + it "returns an array of available languages" do + cask = lambda do + Hbc::Cask.new("cask-with-apps") do + language "zh" do + sha256 "abc123" + "zh-CN" + end + + language "en-US", default: true do + sha256 "xyz789" + "en-US" + end + + url "https://example.org/file.zip" + end + end + + expect(cask.call.languages).to eq(["zh", "en-US"]) + end end describe "app stanza" do @@ -186,12 +216,12 @@ describe Hbc::DSL, :cask do app "Bar.app" end - expect(Array(cask.artifacts[:app])).to eq([["Foo.app"], ["Bar.app"]]) + expect(cask.artifacts.map(&:to_s)).to eq(["Foo.app (App)", "Bar.app (App)"]) end it "allow app stanzas to be empty" do cask = Hbc::Cask.new("cask-with-no-apps") - expect(Array(cask.artifacts[:app])).to eq([]) + expect(cask.artifacts).to be_empty end end @@ -219,7 +249,7 @@ describe Hbc::DSL, :cask do pkg "Bar.pkg" end - expect(Array(cask.artifacts[:pkg])).to eq([["Foo.pkg"], ["Bar.pkg"]]) + expect(cask.artifacts.map(&:to_s)).to eq(["Foo.pkg (Pkg)", "Bar.pkg (Pkg)"]) end end @@ -471,10 +501,10 @@ describe Hbc::DSL, :cask do let(:token) { "with-installer-script" } it "allows installer script to be specified" do - expect(cask.artifacts[:installer].first.script[:executable]).to eq("/usr/bin/true") - expect(cask.artifacts[:installer].first.script[:args]).to eq(["--flag"]) - expect(cask.artifacts[:installer].to_a[1].script[:executable]).to eq("/usr/bin/false") - expect(cask.artifacts[:installer].to_a[1].script[:args]).to eq(["--flag"]) + expect(cask.artifacts.to_a[0].path).to eq(Pathname("/usr/bin/true")) + expect(cask.artifacts.to_a[0].args[:args]).to eq(["--flag"]) + expect(cask.artifacts.to_a[1].path).to eq(Pathname("/usr/bin/false")) + expect(cask.artifacts.to_a[1].args[:args]).to eq(["--flag"]) end end @@ -482,7 +512,9 @@ describe Hbc::DSL, :cask do let(:token) { "with-installer-manual" } it "allows installer manual to be specified" do - expect(cask.artifacts[:installer].first.manual).to eq("Caffeine.app") + installer = cask.artifacts.first + expect(installer).to be_a(Hbc::Artifact::Installer::ManualInstaller) + expect(installer.path).to eq(cask.staged_path.join("Caffeine.app")) end end end @@ -492,7 +524,7 @@ describe Hbc::DSL, :cask do let(:token) { "stage-only" } it "allows stage_only stanza to be specified" do - expect(cask.artifacts[:stage_only].first).to eq([true]) + expect(cask.artifacts).to contain_exactly a_kind_of Hbc::Artifact::StageOnly end end @@ -513,12 +545,12 @@ describe Hbc::DSL, :cask do end end - describe "appdir" do + describe "#appdir" do context "interpolation of the appdir in stanzas" do let(:token) { "appdir-interpolation" } it "is allowed" do - expect(cask.artifacts[:binary].first).to eq(["#{Hbc.appdir}/some/path"]) + expect(cask.artifacts.first.source).to eq(Hbc.appdir/"some/path") end end @@ -531,10 +563,35 @@ describe Hbc::DSL, :cask do binary "#{appdir}/some/path" end - expect(cask.artifacts[:binary].first).to eq(["#{original_appdir}/some/path"]) + expect(cask.artifacts.first.source).to eq(original_appdir/"some/path") ensure Hbc.appdir = original_appdir end end end + + describe "#artifacts" do + it "sorts artifacts according to the preferable installation order" do + cask = Hbc::Cask.new("appdir-trailing-slash") do + postflight do + next + end + + preflight do + next + end + + binary "binary" + + app "App.app" + end + + expect(cask.artifacts.map(&:class).map(&:dsl_key)).to eq [ + :preflight, + :app, + :binary, + :postflight, + ] + end + end end diff --git a/Library/Homebrew/test/cask/installer_spec.rb b/Library/Homebrew/test/cask/installer_spec.rb index 6f7c6d3d7e..2dc27f04c8 100644 --- a/Library/Homebrew/test/cask/installer_spec.rb +++ b/Library/Homebrew/test/cask/installer_spec.rb @@ -5,7 +5,7 @@ describe Hbc::Installer, :cask do } it "downloads and installs a nice fresh Cask" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) Hbc::Installer.new(caffeine).install @@ -14,7 +14,7 @@ describe Hbc::Installer, :cask do end it "works with dmg-based Casks" do - asset = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-dmg.rb") + asset = Hbc::CaskLoader.load(cask_path("container-dmg")) Hbc::Installer.new(asset).install @@ -23,7 +23,7 @@ describe Hbc::Installer, :cask do end it "works with tar-gz-based Casks" do - asset = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-tar-gz.rb") + asset = Hbc::CaskLoader.load(cask_path("container-tar-gz")) Hbc::Installer.new(asset).install @@ -32,7 +32,7 @@ describe Hbc::Installer, :cask do end it "works with xar-based Casks" do - asset = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-xar.rb") + asset = Hbc::CaskLoader.load(cask_path("container-xar")) Hbc::Installer.new(asset).install @@ -41,7 +41,7 @@ describe Hbc::Installer, :cask do end it "works with pure bzip2-based Casks" do - asset = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-bzip2.rb") + asset = Hbc::CaskLoader.load(cask_path("container-bzip2")) Hbc::Installer.new(asset).install @@ -50,7 +50,7 @@ describe Hbc::Installer, :cask do end it "works with pure gzip-based Casks" do - asset = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-gzip.rb") + asset = Hbc::CaskLoader.load(cask_path("container-gzip")) Hbc::Installer.new(asset).install @@ -59,21 +59,21 @@ describe Hbc::Installer, :cask do end it "blows up on a bad checksum" do - bad_checksum = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/bad-checksum.rb") + bad_checksum = Hbc::CaskLoader.load(cask_path("bad-checksum")) expect { Hbc::Installer.new(bad_checksum).install }.to raise_error(Hbc::CaskSha256MismatchError) end it "blows up on a missing checksum" do - missing_checksum = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/missing-checksum.rb") + missing_checksum = Hbc::CaskLoader.load(cask_path("missing-checksum")) expect { Hbc::Installer.new(missing_checksum).install }.to raise_error(Hbc::CaskSha256MissingError) end it "installs fine if sha256 :no_check is used" do - no_checksum = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/no-checksum.rb") + no_checksum = Hbc::CaskLoader.load(cask_path("no-checksum")) Hbc::Installer.new(no_checksum).install @@ -81,14 +81,14 @@ describe Hbc::Installer, :cask do end it "fails to install if sha256 :no_check is used with --require-sha" do - no_checksum = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/no-checksum.rb") + no_checksum = Hbc::CaskLoader.load(cask_path("no-checksum")) expect { Hbc::Installer.new(no_checksum, require_sha: true).install }.to raise_error(Hbc::CaskNoShasumError) end it "installs fine if sha256 :no_check is used with --require-sha and --force" do - no_checksum = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/no-checksum.rb") + no_checksum = Hbc::CaskLoader.load(cask_path("no-checksum")) Hbc::Installer.new(no_checksum, require_sha: true, force: true).install @@ -96,7 +96,7 @@ describe Hbc::Installer, :cask do end it "prints caveats if they're present" do - with_caveats = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-caveats.rb") + with_caveats = Hbc::CaskLoader.load(cask_path("with-caveats")) expect { Hbc::Installer.new(with_caveats).install @@ -106,7 +106,7 @@ describe Hbc::Installer, :cask do end it "prints installer :manual instructions when present" do - with_installer_manual = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-installer-manual.rb") + with_installer_manual = Hbc::CaskLoader.load(cask_path("with-installer-manual")) expect { Hbc::Installer.new(with_installer_manual).install @@ -116,7 +116,7 @@ describe Hbc::Installer, :cask do end it "does not extract __MACOSX directories from zips" do - with_macosx_dir = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-macosx-dir.rb") + with_macosx_dir = Hbc::CaskLoader.load(cask_path("with-macosx-dir")) Hbc::Installer.new(with_macosx_dir).install @@ -124,7 +124,7 @@ describe Hbc::Installer, :cask do end it "allows already-installed Casks which auto-update to be installed if force is provided" do - with_auto_updates = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/auto-updates.rb") + with_auto_updates = Hbc::CaskLoader.load(cask_path("auto-updates")) expect(with_auto_updates).not_to be_installed @@ -137,7 +137,7 @@ describe Hbc::Installer, :cask do # unlike the CLI, the internal interface throws exception on double-install it "installer method raises an exception when already-installed Casks are attempted" do - transmission = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") + transmission = Hbc::CaskLoader.load(cask_path("local-transmission")) expect(transmission).not_to be_installed @@ -151,7 +151,7 @@ describe Hbc::Installer, :cask do end it "allows already-installed Casks to be installed if force is provided" do - transmission = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") + transmission = Hbc::CaskLoader.load(cask_path("local-transmission")) expect(transmission).not_to be_installed @@ -163,7 +163,7 @@ describe Hbc::Installer, :cask do end it "works naked-pkg-based Casks" do - naked_pkg = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-pkg.rb") + naked_pkg = Hbc::CaskLoader.load(cask_path("container-pkg")) Hbc::Installer.new(naked_pkg).install @@ -171,7 +171,7 @@ describe Hbc::Installer, :cask do end it "works properly with an overridden container :type" do - naked_executable = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/naked-executable.rb") + naked_executable = Hbc::CaskLoader.load(cask_path("naked-executable")) Hbc::Installer.new(naked_executable).install @@ -179,7 +179,7 @@ describe Hbc::Installer, :cask do end it "works fine with a nested container" do - nested_app = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/nested-app.rb") + nested_app = Hbc::CaskLoader.load(cask_path("nested-app")) Hbc::Installer.new(nested_app).install @@ -187,7 +187,7 @@ describe Hbc::Installer, :cask do end it "generates and finds a timestamped metadata directory for an installed Cask" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) Hbc::Installer.new(caffeine).install @@ -196,7 +196,7 @@ describe Hbc::Installer, :cask do end it "generates and finds a metadata subdirectory for an installed Cask" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) Hbc::Installer.new(caffeine).install @@ -208,7 +208,7 @@ describe Hbc::Installer, :cask do describe "uninstall" do it "fully uninstalls a Cask" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) installer = Hbc::Installer.new(caffeine) installer.install @@ -220,7 +220,7 @@ describe Hbc::Installer, :cask do end it "uninstalls all versions if force is set" do - caffeine = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-caffeine.rb") + caffeine = Hbc::CaskLoader.load(cask_path("local-caffeine")) mutated_version = caffeine.version + ".1" Hbc::Installer.new(caffeine).install diff --git a/Library/Homebrew/test/cask/staged_spec.rb b/Library/Homebrew/test/cask/staged_spec.rb index 73a909f35b..0229018c77 100644 --- a/Library/Homebrew/test/cask/staged_spec.rb +++ b/Library/Homebrew/test/cask/staged_spec.rb @@ -3,7 +3,7 @@ # to be invoking bundle_identifier off of the installer instance. describe "Operations on staged Casks", :cask do describe "bundle ID" do - let(:cask) { Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/local-transmission.rb") } + let(:cask) { Hbc::CaskLoader.load(cask_path("local-transmission")) } let(:installer) { Hbc::Installer.new(cask) } it "fetches the bundle ID from a staged cask" do installer.install diff --git a/Library/Homebrew/test/cask/underscore_supporting_uri_spec.rb b/Library/Homebrew/test/cask/underscore_supporting_uri_spec.rb deleted file mode 100644 index 49d3ea63f7..0000000000 --- a/Library/Homebrew/test/cask/underscore_supporting_uri_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -describe Hbc::UnderscoreSupportingURI, :cask do - describe "parse" do - it "works like normal on normal URLs" do - uri = Hbc::UnderscoreSupportingURI.parse("http://example.com/TestCask.dmg") - expect(uri).to eq(URI("http://example.com/TestCask.dmg")) - end - - it "works just fine on URIs with underscores" do - uri = Hbc::UnderscoreSupportingURI.parse("http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V3.0.0.dmg") - expect(uri.host).to include("_") - expect(uri.to_s).to eq("http://dl_dir.qq.com/qqfile/qq/QQforMac/QQ_V3.0.0.dmg") - end - end -end diff --git a/Library/Homebrew/test/cleanup_spec.rb b/Library/Homebrew/test/cleanup_spec.rb index 4e5e42efac..da262c5ca0 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 - expect(path).not_to exist + it "cleans all logs if prune all" do + ARGV << "--prune=all" + described_class.cleanup_logs + expect(path).not_to exist + end + + 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/commands_spec.rb b/Library/Homebrew/test/cmd/commands_spec.rb index cf6f567404..46ed3ddcfe 100644 --- a/Library/Homebrew/test/cmd/commands_spec.rb +++ b/Library/Homebrew/test/cmd/commands_spec.rb @@ -68,7 +68,7 @@ describe Homebrew do expect(cmds).to include("t1"), "Executable files should be included" expect(cmds).to include("t2"), "Executable Ruby files should be included" - expect(cmds).not_to include("t3"), "Executable files with a non Ruby extension shoudn't be included" + expect(cmds).not_to include("t3"), "Executable files with a non Ruby extension shouldn't be included" expect(cmds).not_to include("t4"), "Non-executable files shouldn't be included" end end diff --git a/Library/Homebrew/test/cmd/home_spec.rb b/Library/Homebrew/test/cmd/home_spec.rb index 5a40704929..cf9453af2d 100644 --- a/Library/Homebrew/test/cmd/home_spec.rb +++ b/Library/Homebrew/test/cmd/home_spec.rb @@ -7,10 +7,10 @@ describe "brew home", :integration_test do end it "opens the homepage for a given Formula" do - setup_test_formula "testball" + setup_test_formula "testballhome" - expect { brew "home", "testball", "HOMEBREW_BROWSER" => "echo" } - .to output("#{Formula["testball"].homepage}\n").to_stdout + expect { brew "home", "testballhome", "HOMEBREW_BROWSER" => "echo" } + .to output("#{Formula["testballhome"].homepage}\n").to_stdout .and not_to_output.to_stderr .and be_a_success end diff --git a/Library/Homebrew/test/cmd/search_remote_tap_spec.rb b/Library/Homebrew/test/cmd/search_remote_tap_spec.rb index b0beb122c4..eb256b9245 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 06b7073d88..36ddde3e1d 100644 --- a/Library/Homebrew/test/cmd/search_spec.rb +++ b/Library/Homebrew/test/cmd/search_spec.rb @@ -1,6 +1,7 @@ describe "brew search", :integration_test do before(:each) do setup_test_formula "testball" + setup_remote_tap "caskroom/cask" end it "lists all available Formulae when no argument is given" do @@ -13,7 +14,7 @@ describe "brew search", :integration_test do it "supports searching by name" do expect { brew "search", "testball" } .to output(/testball/).to_stdout - .and not_to_output.to_stderr + .and output(/Searching/).to_stderr .and be_a_success end @@ -24,6 +25,13 @@ describe "brew search", :integration_test do .and be_a_success end + it "falls back to a GitHub tap search when no formula is found", :needs_network do + expect { brew "search", "caskroom/cask/firefox" } + .to output(/firefox/).to_stdout + .and output(/Searching/).to_stderr + .and be_a_success + end + describe "--desc" do let(:desc_cache) { HOMEBREW_CACHE/"desc_cache.json" } @@ -44,8 +52,8 @@ describe "brew search", :integration_test do "fink" => "http://pdb.finkproject.org/pdb/browse.php?summary=testball", "debian" => "https://packages.debian.org/search?keywords=testball&searchon=names&suite=all§ion=all", "opensuse" => "https://software.opensuse.org/search?q=testball", - "fedora" => "https://admin.fedoraproject.org/pkgdb/packages/%2Atestball%2A/", - "ubuntu" => "http://packages.ubuntu.com/search?keywords=testball&searchon=names&suite=all§ion=all", + "fedora" => "https://apps.fedoraproject.org/packages/s/testball", + "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/dependency_expansion_spec.rb b/Library/Homebrew/test/dependency_expansion_spec.rb index f955237a97..d6ecdf5527 100644 --- a/Library/Homebrew/test/dependency_expansion_spec.rb +++ b/Library/Homebrew/test/dependency_expansion_spec.rb @@ -69,7 +69,7 @@ describe Dependency do end end - it "merges dependencies and perserves env_proc" do + it "merges dependencies and preserves env_proc" do env_proc = double dep = described_class.new("foo", [], env_proc) allow(dep).to receive(:to_formula).and_return(double(deps: [], name: "foo")) diff --git a/Library/Homebrew/test/dev-cmd/audit_spec.rb b/Library/Homebrew/test/dev-cmd/audit_spec.rb index 037865fdf1..3e99bd06bc 100644 --- a/Library/Homebrew/test/dev-cmd/audit_spec.rb +++ b/Library/Homebrew/test/dev-cmd/audit_spec.rb @@ -150,70 +150,6 @@ describe FormulaAuditor do end end - describe "#audit_class" do - specify "missing test" do - fa = formula_auditor "foo", <<-EOS.undent - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_class - expect(fa.problems).to eq([]) - - fa = formula_auditor "foo", <<-EOS.undent, strict: true - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_class - expect(fa.problems).to eq(["A `test do` test block should be added"]) - end - - specify "GithubGistFormula", :needs_compat do - ENV.delete("HOMEBREW_DEVELOPER") - - fa = formula_auditor "foo", <<-EOS.undent - class Foo < GithubGistFormula - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_class - expect(fa.problems) - .to eq(["GithubGistFormula is deprecated, use Formula instead"]) - end - - specify "ScriptFileFormula", :needs_compat do - ENV.delete("HOMEBREW_DEVELOPER") - - fa = formula_auditor "foo", <<-EOS.undent - class Foo < ScriptFileFormula - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_class - expect(fa.problems) - .to eq(["ScriptFileFormula is deprecated, use Formula instead"]) - end - - specify "AmazonWebServicesFormula", :needs_compat do - ENV.delete("HOMEBREW_DEVELOPER") - - fa = formula_auditor "foo", <<-EOS.undent - class Foo < AmazonWebServicesFormula - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_class - expect(fa.problems) - .to eq(["AmazonWebServicesFormula is deprecated, use Formula instead"]) - end - end - describe "#line_problems" do specify "pkgshare" do fa = formula_auditor "foo", <<-EOS.undent, strict: true diff --git a/Library/Homebrew/test/download_strategies_spec.rb b/Library/Homebrew/test/download_strategies_spec.rb index 8c376a6493..06d6fa855c 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/exceptions_spec.rb b/Library/Homebrew/test/exceptions_spec.rb index 33547ea32e..0a8313355f 100644 --- a/Library/Homebrew/test/exceptions_spec.rb +++ b/Library/Homebrew/test/exceptions_spec.rb @@ -181,8 +181,8 @@ describe DuplicateResourceError do its(:to_s) { is_expected.to eq("Resource is defined more than once") } end -describe BottleVersionMismatchError do - subject { described_class.new("/foo.bottle.tar.gz", "1.0", formula, "1.1") } +describe BottleFormulaUnavailableError do + subject { described_class.new("/foo.bottle.tar.gz", "foo/1.0/.brew/foo.rb") } let(:formula) { double(Formula, full_name: "foo") } - its(:to_s) { is_expected.to match(/Bottle version mismatch/) } + its(:to_s) { is_expected.to match(/This bottle does not contain the formula file/) } end diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb index 7365b27589..7b729312b7 100644 --- a/Library/Homebrew/test/formula_installer_spec.rb +++ b/Library/Homebrew/test/formula_installer_spec.rb @@ -107,7 +107,7 @@ describe FormulaInstaller do end EOS - Formulary::FORMULAE.delete(dep_path) + Formulary.cache.delete(dep_path) dependency = Formulary.factory(dep_name) dependent = formula do diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb index 234ebc93c3..3180ad9a7b 100644 --- a/Library/Homebrew/test/formulary_spec.rb +++ b/Library/Homebrew/test/formulary_spec.rb @@ -14,7 +14,7 @@ describe Formulary do bottle do cellar :any_skip_relocation root_url "file://#{bottle_dir}" - sha256 "9abc8ce779067e26556002c4ca6b9427b9874d25f0cafa7028e05b5c5c410cb4" => :#{Utils::Bottles.tag} + sha256 "d48bbbe583dcfbfa608579724fc6f0328b3cd316935c6ea22f134610aaf2952f" => :#{Utils::Bottles.tag} end def install diff --git a/Library/Homebrew/test/locale_spec.rb b/Library/Homebrew/test/locale_spec.rb index 9e4d09e837..9c684f0e7f 100644 --- a/Library/Homebrew/test/locale_spec.rb +++ b/Library/Homebrew/test/locale_spec.rb @@ -9,6 +9,10 @@ describe Locale do expect(described_class.parse("zh-CN-Hans")).to eql(described_class.new("zh", "CN", "Hans")) end + it "correctly parses a string with a UN M.49 region code" do + expect(described_class.parse("es-419")).to eql(described_class.new("es", "419", nil)) + end + context "raises a ParserError when given" do it "an empty string" do expect { described_class.parse("") }.to raise_error(Locale::ParserError) diff --git a/Library/Homebrew/test/missing_formula_spec.rb b/Library/Homebrew/test/missing_formula_spec.rb index a48f12ecd1..0a905004ba 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/os/mac/diagnostic_spec.rb b/Library/Homebrew/test/os/mac/diagnostic_spec.rb index d6186e46bb..83d95c2ef1 100644 --- a/Library/Homebrew/test/os/mac/diagnostic_spec.rb +++ b/Library/Homebrew/test/os/mac/diagnostic_spec.rb @@ -47,15 +47,10 @@ describe Homebrew::Diagnostic::Checks do end specify "#check_ruby_version" do - allow(MacOS).to receive(:version).and_return(OS::Mac::Version.new("10.13")) - stub_const("RUBY_VERSION", "2.3.3p222") + allow(MacOS).to receive(:version).and_return(OS::Mac::Version.new("10.12")) + stub_const("RUBY_VERSION", "1.8.6") expect(subject.check_ruby_version) - .to match <<-EOS.undent - Ruby version 2.3.3p222 is unsupported on 10.13. Homebrew - is developed and tested on Ruby 2.0, and may not work correctly - on other Rubies. Patches are accepted as long as they don't cause breakage - on supported Rubies. - EOS + .to match "Ruby version 1.8.6 is unsupported on 10.12" end end diff --git a/Library/Homebrew/test/pathname_spec.rb b/Library/Homebrew/test/pathname_spec.rb index 0bc19c5acf..69314e5f49 100644 --- a/Library/Homebrew/test/pathname_spec.rb +++ b/Library/Homebrew/test/pathname_spec.rb @@ -295,7 +295,7 @@ describe FileUtils do let(:dst) { mktmpdir } describe "#mkdir" do - it "creates indermediate directories" do + it "creates intermediate directories" do described_class.mkdir dst/"foo/bar/baz" do expect(dst/"foo/bar/baz").to exist, "foo/bar/baz was not created" expect(dst/"foo/bar/baz").to be_a_directory, "foo/bar/baz was not a directory structure" diff --git a/Library/Homebrew/test/requirement_spec.rb b/Library/Homebrew/test/requirement_spec.rb index 71372aa69e..11a3da8f4e 100644 --- a/Library/Homebrew/test/requirement_spec.rb +++ b/Library/Homebrew/test/requirement_spec.rb @@ -48,7 +48,7 @@ describe Requirement do it { is_expected.to be_fatal } end - context "#fatal is ommitted" do + context "#fatal is omitted" do it { is_expected.not_to be_fatal } end end @@ -184,7 +184,7 @@ describe Requirement do it { is_expected.to have_a_default_formula } end - context "#default_formula ommitted" do + context "#default_formula omitted" do it { is_expected.not_to have_a_default_formula } end end diff --git a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb index 563f7ad4be..b1afdc3f94 100644 --- a/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/bottle_block_cop_spec.rb @@ -24,7 +24,7 @@ describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -60,7 +60,7 @@ describe RuboCop::Cop::FormulaAuditStrict::BottleBlock do end EOS - new_source = autocorrect_source(cop, source) + new_source = autocorrect_source(source) expect(new_source).to eq(corrected_source) end end diff --git a/Library/Homebrew/test/rubocops/caveats_cop_spec.rb b/Library/Homebrew/test/rubocops/caveats_cop_spec.rb index d44808a5d7..4dbe65cfb8 100644 --- a/Library/Homebrew/test/rubocops/caveats_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/caveats_cop_spec.rb @@ -25,7 +25,7 @@ describe RuboCop::Cop::FormulaAudit::Caveats do column: 5, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) diff --git a/Library/Homebrew/test/rubocops/checksum_cop_spec.rb b/Library/Homebrew/test/rubocops/checksum_cop_spec.rb index 644152c32c..2f508bbf52 100644 --- a/Library/Homebrew/test/rubocops/checksum_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/checksum_cop_spec.rb @@ -34,7 +34,7 @@ describe RuboCop::Cop::FormulaAudit::Checksum do column: 14, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -68,7 +68,7 @@ describe RuboCop::Cop::FormulaAudit::Checksum do column: 14, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -102,7 +102,7 @@ describe RuboCop::Cop::FormulaAudit::Checksum do column: 31, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -142,7 +142,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do column: 20, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -175,7 +175,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do column: 12, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -215,7 +215,7 @@ describe RuboCop::Cop::FormulaAudit::ChecksumCase do end EOS - new_source = autocorrect_source(cop, source) + new_source = autocorrect_source(source) expect(new_source).to eq(corrected_source) end end diff --git a/Library/Homebrew/test/rubocops/class_cop_spec.rb b/Library/Homebrew/test/rubocops/class_cop_spec.rb new file mode 100644 index 0000000000..59252587c0 --- /dev/null +++ b/Library/Homebrew/test/rubocops/class_cop_spec.rb @@ -0,0 +1,81 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/class_cop" + +describe RuboCop::Cop::FormulaAudit::ClassName do + subject(:cop) { described_class.new } + + context "When auditing formula" do + it "with deprecated inheritance" do + formulas = [{ + "class" => "GithubGistFormula", + }, { + "class" => "ScriptFileFormula", + }, { + "class" => "AmazonWebServicesFormula", + }] + + formulas.each do |formula| + source = <<-EOS.undent + class Foo < #{formula["class"]} + url 'http://example.com/foo-1.0.tgz' + end + EOS + + expected_offenses = [{ message: "#{formula["class"]} is deprecated, use Formula instead", + severity: :convention, + line: 1, + column: 12, + source: source }] + + inspect_source(source) + + expected_offenses.zip(cop.offenses.reverse).each do |expected, actual| + expect_offense(expected, actual) + end + end + end + + it "with deprecated inheritance and autocorrect" do + source = <<-EOS.undent + class Foo < AmazonWebServicesFormula + url 'http://example.com/foo-1.0.tgz' + end + EOS + corrected_source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + end + EOS + + new_source = autocorrect_source(source) + expect(new_source).to eq(corrected_source) + end + end +end + +describe RuboCop::Cop::FormulaAuditStrict::Test do + subject(:cop) { described_class.new } + + context "When auditing formula" do + it "without a test block" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + end + EOS + expected_offenses = [{ message: described_class::MSG, + severity: :convention, + line: 1, + column: 0, + source: source }] + + inspect_source(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/components_order_cop_spec.rb b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb index 25467c6353..f093f49279 100644 --- a/Library/Homebrew/test/rubocops/components_order_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb @@ -21,7 +21,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -47,7 +47,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -74,7 +74,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -99,7 +99,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -129,7 +129,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do end EOS - corrected_source = autocorrect_source(cop, source) + corrected_source = autocorrect_source(source) expect(corrected_source).to eq(correct_source) end @@ -156,7 +156,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do end end EOS - corrected_source = autocorrect_source(cop, source) + corrected_source = autocorrect_source(source) expect(corrected_source).to eq(correct_source) end end diff --git a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb index fd635a1264..9fbe159041 100644 --- a/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/components_redundancy_cop_spec.rb @@ -23,7 +23,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -46,7 +46,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -70,7 +70,7 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsRedundancy do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) diff --git a/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb b/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb index 4fbab6c9ed..8874ecc963 100644 --- a/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/conflicts_cop_spec.rb @@ -22,7 +22,7 @@ describe RuboCop::Cop::FormulaAudit::Conflicts do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source, "/homebrew-core/Formula/foo@2.0.rb") expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -36,7 +36,7 @@ describe RuboCop::Cop::FormulaAudit::Conflicts do desc 'Bar' end EOS - inspect_source(cop, source) + inspect_source(source, "/homebrew-core/Formula/foo@2.0.rb") expect(cop.offenses).to eq([]) end end diff --git a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb index 74ce478fb1..ac8893e180 100644 --- a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb @@ -20,7 +20,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do column: 0, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -37,7 +37,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do msg = <<-EOS.undent Description is too long. "name: desc" should be less than 80 characters. - Length is calculated as Foo + desc. (currently 95) + Length is calculated as foo + desc. (currently 95) EOS expected_offenses = [{ message: msg, severity: :convention, @@ -45,7 +45,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source, "/homebrew-core/Formula/foo.rb") expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end @@ -62,7 +62,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do msg = <<-EOS.undent Description is too long. "name: desc" should be less than 80 characters. - Length is calculated as Foo + desc. (currently 98) + Length is calculated as foo + desc. (currently 98) EOS expected_offenses = [{ message: msg, severity: :convention, @@ -70,7 +70,7 @@ describe RuboCop::Cop::FormulaAuditStrict::DescLength do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source, "/homebrew-core/Formula/foo.rb") expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end @@ -96,7 +96,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do column: 8, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end @@ -116,7 +116,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do column: 8, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end @@ -136,7 +136,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do column: 8, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end @@ -156,7 +156,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do column: 8, source: source }] - inspect_source(cop, source) + inspect_source(source, "/homebrew-core/Formula/foo.rb") expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end @@ -176,7 +176,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do end EOS - corrected_source = autocorrect_source(cop, source) + corrected_source = autocorrect_source(source, "/homebrew-core/Formula/foo.rb") expect(corrected_source).to eq(correct_source) end end diff --git a/Library/Homebrew/test/rubocops/homepage_cop_spec.rb b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb index c03efd8257..6c7f248baa 100644 --- a/Library/Homebrew/test/rubocops/homepage_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb @@ -20,7 +20,7 @@ describe RuboCop::Cop::FormulaAudit::Homepage do column: 0, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -41,7 +41,7 @@ describe RuboCop::Cop::FormulaAudit::Homepage do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -72,7 +72,7 @@ describe RuboCop::Cop::FormulaAudit::Homepage do end EOS - inspect_source(cop, source) + inspect_source(source) if homepage =~ %r{http:\/\/www\.freedesktop\.org} if homepage =~ /Software/ expected_offenses = [{ message: "#{homepage} should be styled " \ diff --git a/Library/Homebrew/test/rubocops/lines_cop_spec.rb b/Library/Homebrew/test/rubocops/lines_cop_spec.rb index 6f1f8a48c1..962827ca3a 100644 --- a/Library/Homebrew/test/rubocops/lines_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/lines_cop_spec.rb @@ -42,7 +42,7 @@ describe RuboCop::Cop::FormulaAudit::Lines do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses.reverse).each do |expected, actual| expect_offense(expected, actual) @@ -50,12 +50,6 @@ describe RuboCop::Cop::FormulaAudit::Lines do end end end - def expect_offense(expected, actual) - expect(actual.message).to eq(expected[:message]) - expect(actual.severity).to eq(expected[:severity]) - expect(actual.line).to eq(expected[:line]) - expect(actual.column).to eq(expected[:column]) - end end describe RuboCop::Cop::FormulaAudit::ClassInheritance do @@ -76,19 +70,13 @@ describe RuboCop::Cop::FormulaAudit::ClassInheritance do column: 10, source: source }] - inspect_source(cop, source) + inspect_source(source, '/homebrew-core/Formula/foo.rb') expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end end - def expect_offense(expected, actual) - expect(actual.message).to eq(expected[:message]) - expect(actual.severity).to eq(expected[:severity]) - expect(actual.line).to eq(expected[:line]) - expect(actual.column).to eq(expected[:column]) - end end describe RuboCop::Cop::FormulaAudit::Comments do @@ -104,13 +92,13 @@ describe RuboCop::Cop::FormulaAudit::Comments do end EOS - expected_offenses = [{ message: "Commented cmake call found", + expected_offenses = [{ message: "Please remove default template comments", severity: :convention, line: 4, column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -132,7 +120,7 @@ describe RuboCop::Cop::FormulaAudit::Comments do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -148,25 +136,19 @@ describe RuboCop::Cop::FormulaAudit::Comments do end EOS - expected_offenses = [{ message: 'Commented-out dep "foo"', + expected_offenses = [{ message: 'Commented-out dependency "foo"', severity: :convention, line: 4, column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end end - def expect_offense(expected, actual) - expect(actual.message).to eq(expected[:message]) - expect(actual.severity).to eq(expected[:severity]) - expect(actual.line).to eq(expected[:line]) - expect(actual.column).to eq(expected[:column]) - end end describe RuboCop::Cop::FormulaAudit::Miscellaneous do @@ -188,7 +170,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -212,7 +194,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -237,7 +219,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -264,7 +246,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 7, source: source }] - inspect_source(cop, source, "/homebrew-core/") + inspect_source(source, "/homebrew-core/") expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -292,7 +274,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -317,7 +299,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -342,7 +324,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -358,14 +340,16 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do end EOS - expected_offenses = [{ message: "`skip_clean :all` is deprecated; brew no longer strips symbols\n" \ - "\tPass explicit paths to prevent Homebrew from removing empty folders.", + expected_offenses = [{ message: <<-EOS.undent.chomp, + `skip_clean :all` is deprecated; brew no longer strips symbols + Pass explicit paths to prevent Homebrew from removing empty folders. + EOS severity: :convention, line: 4, column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -383,19 +367,34 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do end EOS - expected_offenses = [{ message: "macOS has been 64-bit only so build.universal? is deprecated.", + expected_offenses = [{ message: "macOS has been 64-bit only since 10.6 so build.universal? is deprecated.", severity: :convention, line: 4, column: 5, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with build.universal? exempted formula" do + source = <<-EOS.undent + class Wine < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + if build.universal? + "foo" + end + end + EOS + + inspect_source(source, "/homebrew-core/Formula/wine.rb") + expect(cop.offenses).to eq([]) + end + it "with ENV.universal_binary" do source = <<-EOS.undent class Foo < Formula @@ -413,14 +412,14 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 5, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end - it "with ENV.universal_binary" do + it "with ENV.universal_binary 2" do source = <<-EOS.undent class Foo < Formula desc "foo" @@ -437,7 +436,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 5, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -459,13 +458,26 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 10, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with ruby-macho alternatives audit exempted formula" do + source = <<-EOS.undent + class Cctools < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + system "install_name_tool", "-id" + end + EOS + + inspect_source(source, "/homebrew-core/Formula/cctools.rb") + expect(cop.offenses).to eq([]) + end + it "with npm install without language::Node args" do source = <<-EOS.undent class Foo < Formula @@ -478,16 +490,29 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do expected_offenses = [{ message: "Use Language::Node for npm install args", severity: :convention, line: 4, - column: 17, + column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with npm install without language::Node args in kibana" do + source = <<-EOS.undent + class KibanaAT44 < Formula + desc "foo" + url 'http://example.com/foo-1.0.tgz' + system "npm", "install" + end + EOS + + inspect_source(source, "/homebrew-core/Formula/kibana@4.4.rb") + expect(cop.offenses).to eq([]) + end + it "with assert include" do source = <<-EOS.undent class Foo < Formula @@ -503,7 +528,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 9, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -525,12 +550,13 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 13, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with old style OS check" do source = <<-EOS.undent class Foo < Formula @@ -546,7 +572,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 21, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -569,12 +595,13 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 13, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with system call to fileUtils Method" do source = <<-EOS.undent class Foo < Formula @@ -590,12 +617,13 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 10, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with a top-level function def " do source = <<-EOS.undent def test @@ -613,7 +641,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 0, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -637,7 +665,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 18, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -661,7 +689,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 18, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -685,7 +713,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 14, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -709,7 +737,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 14, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -733,7 +761,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 30, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -757,7 +785,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 27, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -781,7 +809,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 30, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -805,7 +833,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 30, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -829,7 +857,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 14, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -853,7 +881,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 22, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -877,7 +905,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 12, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -901,7 +929,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 12, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -925,7 +953,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 28, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -949,7 +977,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 28, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -973,7 +1001,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 17, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -997,7 +1025,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 18, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1021,12 +1049,13 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 47, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end + it "with formula path shortcut long form 3" do source = <<-EOS.undent class Foo < Formula @@ -1044,7 +1073,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 46, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1066,7 +1095,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 24, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1088,7 +1117,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 10, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1110,7 +1139,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 13, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1134,7 +1163,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 5, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1158,7 +1187,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1182,7 +1211,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 26, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1206,7 +1235,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 14, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1228,7 +1257,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1250,7 +1279,7 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -1272,18 +1301,11 @@ describe RuboCop::Cop::FormulaAudit::Miscellaneous do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) end end - - end - def expect_offense(expected, actual) - expect(actual.message).to eq(expected[:message]) - expect(actual.severity).to eq(expected[:severity]) - expect(actual.line).to eq(expected[:line]) - expect(actual.column).to eq(expected[:column]) end end diff --git a/Library/Homebrew/test/rubocops/options_cop_spec.rb b/Library/Homebrew/test/rubocops/options_cop_spec.rb index b89b3d9b59..c27389a68b 100644 --- a/Library/Homebrew/test/rubocops/options_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/options_cop_spec.rb @@ -21,7 +21,7 @@ describe RuboCop::Cop::FormulaAudit::Options do column: 10, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -48,7 +48,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Options do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -72,7 +72,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Options do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -95,7 +95,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Options do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -122,7 +122,7 @@ describe RuboCop::Cop::NewFormulaAudit::Options do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) diff --git a/Library/Homebrew/test/rubocops/patches_cop_spec.rb b/Library/Homebrew/test/rubocops/patches_cop_spec.rb index 4bd79bf357..4f9ca2df84 100644 --- a/Library/Homebrew/test/rubocops/patches_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/patches_cop_spec.rb @@ -13,7 +13,7 @@ describe RuboCop::Cop::FormulaAudit::Patches do url 'http://example.com/foo-1.0.tgz' end EOS - inspect_source(cop, source) + inspect_source(source) expect(cop.offenses).to eq([]) end @@ -28,13 +28,13 @@ 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) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -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 @@ -60,42 +61,65 @@ describe RuboCop::Cop::FormulaAudit::Patches do end EOS - inspect_source(cop, source) - if patch_url =~ %r{/raw\.github\.com/} - expected_offenses = [{ message: "GitHub/Gist patches should specify a revision:\n#{patch_url}", - severity: :convention, - line: 5, - column: 12, - source: source }] + inspect_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: "MacPorts patches should specify a revision instead of trunk:\n#{patch_url}", - 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: "Patches from MacPorts Trac should be https://, not http:\n#{patch_url}", - 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: "Patches from Debian should be https://, not http:\n#{patch_url}", - 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: "use GitHub pull request URLs:\n"\ - " https://github.com/foo/foo-bar/pull/100.patch\n"\ - "Rather than patch-diff:\n"\ - " https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch\n", - 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 @@ -116,19 +140,21 @@ 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: "Patches from MacPorts Trac should be https://, not http:\n"\ - "http://trac.macports.org/export/68507/trunk/dports/net/trafshow/files/", - 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) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -157,42 +183,56 @@ describe RuboCop::Cop::FormulaAudit::Patches do end EOS - inspect_source(cop, source) - if patch_url =~ %r{/raw\.github\.com/} - expected_offenses = [{ message: "GitHub/Gist patches should specify a revision:\n#{patch_url}", - severity: :convention, - line: 5, - column: 16, - source: source }] + inspect_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: "MacPorts patches should specify a revision instead of trunk:\n#{patch_url}", - 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: "Patches from MacPorts Trac should be https://, not http:\n#{patch_url}", - 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: "Patches from Debian should be https://, not http:\n#{patch_url}", - 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: "use GitHub pull request URLs:\n"\ - " https://github.com/foo/foo-bar/pull/100.patch\n"\ - "Rather than patch-diff:\n"\ - " https://patch-diff.githubusercontent.com/raw/foo/foo-bar/pull/100.patch\n", - 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/rubocops/text_cop_spec.rb b/Library/Homebrew/test/rubocops/text_cop_spec.rb index b218e9c256..4908017703 100644 --- a/Library/Homebrew/test/rubocops/text_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/text_cop_spec.rb @@ -7,6 +7,54 @@ describe RuboCop::Cop::FormulaAudit::Text do subject(:cop) { described_class.new } context "When auditing formula text" do + it "with both openssl and libressl optional dependencies" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + depends_on "openssl" + depends_on "libressl" => :optional + end + EOS + + expected_offenses = [{ message: "Formulae should not depend on both OpenSSL and LibreSSL (even optionally).", + severity: :convention, + line: 6, + column: 2, + source: source }] + + inspect_source(source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "with both openssl and libressl dependencies" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + depends_on "openssl" + depends_on "libressl" + end + EOS + + expected_offenses = [{ message: "Formulae should not depend on both OpenSSL and LibreSSL (even optionally).", + severity: :convention, + line: 6, + column: 2, + source: source }] + + inspect_source(source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + it "When xcodebuild is called without SYMROOT" do source = <<-EOS.undent class Foo < Formula @@ -25,7 +73,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -50,7 +98,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -75,7 +123,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -100,7 +148,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -125,7 +173,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -162,7 +210,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -189,7 +237,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 0, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -219,7 +267,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) @@ -244,7 +292,7 @@ describe RuboCop::Cop::FormulaAudit::Text do column: 4, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses).each do |expected, actual| expect_offense(expected, actual) diff --git a/Library/Homebrew/test/rubocops/urls_cop_spec.rb b/Library/Homebrew/test/rubocops/urls_cop_spec.rb index 280da6314c..ad939a1a2a 100644 --- a/Library/Homebrew/test/rubocops/urls_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/urls_cop_spec.rb @@ -54,8 +54,10 @@ describe RuboCop::Cop::FormulaAudit::Urls do "col" => 2, }, { "url" => "http://prdownloads.sourceforge.net/foo/foo-1.tar.gz", - "msg" => "Don't use prdownloads in SourceForge urls (url is http://prdownloads.sourceforge.net/foo/foo-1.tar.gz).\n" \ - "\tSee: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/", + "msg" => <<-EOS.undent.chomp, + Don't use prdownloads in SourceForge urls (url is http://prdownloads.sourceforge.net/foo/foo-1.tar.gz). + See: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/ + EOS "col" => 2, }, { "url" => "http://foo.dl.sourceforge.net/sourceforge/foozip/foozip_1.0.tar.bz2", @@ -67,8 +69,11 @@ describe RuboCop::Cop::FormulaAudit::Urls do "col" => 2, }, { "url" => "http://http.debian.net/debian/dists/foo/", - "msg" => "Please use a secure mirror for Debian URLs.\nWe recommend:\n"\ - " https://mirrors.ocf.berkeley.edu/debian/dists/foo/\n", + "msg" => <<-EOS.undent, + Please use a secure mirror for Debian URLs. + We recommend: + https://mirrors.ocf.berkeley.edu/debian/dists/foo/ + EOS "col" => 2, }, { "url" => "http://foo.googlecode.com/files/foo-1.0.zip", @@ -96,8 +101,12 @@ describe RuboCop::Cop::FormulaAudit::Urls do "col" => 2, }, { "url" => "https://codeload.github.com/foo/bar/tar.gz/v0.1.1", - "msg" => "Use GitHub archive URLs:\n https://github.com/foo/bar/archive/v0.1.1.tar.gz\n"\ - "Rather than codeload:\n https://codeload.github.com/foo/bar/tar.gz/v0.1.1\n", + "msg" => <<-EOS.undent, + Use GitHub archive URLs: + https://github.com/foo/bar/archive/v0.1.1.tar.gz + Rather than codeload: + https://codeload.github.com/foo/bar/tar.gz/v0.1.1 + EOS "col" => 2, }, { "url" => "https://central.maven.org/maven2/com/bar/foo/1.1/foo-1.1.jar", @@ -117,7 +126,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do column: formula["col"], source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses.reverse).each do |expected, actual| expect_offense(expected, actual) @@ -151,7 +160,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do column: formula["col"], source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses.reverse).each do |expected, actual| expect_offense(expected, actual) @@ -174,7 +183,7 @@ describe RuboCop::Cop::FormulaAudit::Urls do column: 2, source: source }] - inspect_source(cop, source) + inspect_source(source) expected_offenses.zip(cop.offenses.reverse).each do |expected, actual| expect_offense(expected, actual) @@ -213,13 +222,13 @@ describe RuboCop::Cop::FormulaAuditStrict::PyPiUrls do column: formula["col"], source: source }] - inspect_source(cop, source) + inspect_source(source) # Check for expected offenses expected_offenses.zip(cop.offenses.reverse).each do |expected, actual| expect_offense(expected, actual) end # Check for expected auto corrected source - new_source = autocorrect_source(cop, source) + new_source = autocorrect_source(source) expect(new_source).to eq(corrected_source) end end diff --git a/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz b/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz index d88838a94d..62ea6c264b 100644 Binary files a/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz and b/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz differ diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-conflicts-with.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-conflicts-with.rb index ab3631743c..13d1fc4fc6 100644 --- a/Library/Homebrew/test/support/fixtures/cask/Casks/with-conflicts-with.rb +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-conflicts-with.rb @@ -5,7 +5,7 @@ cask 'with-conflicts-with' do url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" homepage 'http://example.com/with-conflicts-with' - conflicts_with formula: 'unar' + conflicts_with cask: 'local-caffeine' app 'Caffeine.app' end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/with-languages.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/with-languages.rb new file mode 100644 index 0000000000..90ff638468 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/with-languages.rb @@ -0,0 +1,18 @@ +cask 'with-languages' do + version '1.2.3' + + language "zh" do + sha256 "abc123" + "zh-CN" + end + + language "en-US", default: true do + sha256 "xyz789" + "en-US" + end + + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage 'http://example.com/local-caffeine' + + app 'Caffeine.app' +end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/without-languages.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/without-languages.rb new file mode 100644 index 0000000000..4c0ce955ab --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/without-languages.rb @@ -0,0 +1,9 @@ +cask 'without-languages' do + version '1.2.3' + sha256 '67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94' + + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage 'http://example.com/local-caffeine' + + app 'Caffeine.app' +end diff --git a/Library/Homebrew/test/support/fixtures/testball_bottle.rb b/Library/Homebrew/test/support/fixtures/testball_bottle.rb index 9453255e6e..5a6be7c5fc 100644 --- a/Library/Homebrew/test/support/fixtures/testball_bottle.rb +++ b/Library/Homebrew/test/support/fixtures/testball_bottle.rb @@ -6,7 +6,7 @@ class TestballBottle < Formula stable.bottle do cellar :any_skip_relocation root_url "file://#{TEST_FIXTURE_DIR}/bottles" - sha256 "9abc8ce779067e26556002c4ca6b9427b9874d25f0cafa7028e05b5c5c410cb4" => Utils::Bottles.tag + sha256 "d48bbbe583dcfbfa608579724fc6f0328b3cd316935c6ea22f134610aaf2952f" => Utils::Bottles.tag end cxxstdlib_check :skip end diff --git a/Library/Homebrew/test/support/fixtures/third-party/Casks/third-party-cask.rb b/Library/Homebrew/test/support/fixtures/third-party/Casks/third-party-cask.rb new file mode 100644 index 0000000000..d7add05229 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/third-party/Casks/third-party-cask.rb @@ -0,0 +1,9 @@ +cask 'third-party-cask' do + version '1.2.3' + sha256 '8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b' + + url 'http://example.com/ThirdParty.dmg' + homepage 'http://example.com/' + + app 'ThirdParty.app' +end diff --git a/Library/Homebrew/test/support/helper/fixtures.rb b/Library/Homebrew/test/support/helper/fixtures.rb index 716fe20081..460fb4aeff 100644 --- a/Library/Homebrew/test/support/helper/fixtures.rb +++ b/Library/Homebrew/test/support/helper/fixtures.rb @@ -8,6 +8,10 @@ module Test def bundle_path(name) Pathname.new("#{TEST_FIXTURE_DIR}/mach/#{name}.bundle") end + + def cask_path(name) + Pathname.new("#{TEST_FIXTURE_DIR}/cask/Casks/#{name}.rb") + end end end end diff --git a/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb b/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb index c51d339a7c..fc83149d01 100644 --- a/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb +++ b/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb @@ -18,6 +18,7 @@ HOMEBREW_CASK_DIRS = [ RSpec.shared_context "Homebrew-Cask" do around(:each) do |example| + third_party_tap = Tap.fetch("third-party", "tap") begin dirs = HOMEBREW_CASK_DIRS.map do |dir| Pathname.new(TEST_TMPDIR).join("cask-#{dir}").tap do |path| @@ -31,11 +32,18 @@ RSpec.shared_context "Homebrew-Cask" do FileUtils.ln_sf TEST_FIXTURE_DIR.join("cask"), tap.path end + third_party_tap.tap do |tap| + FileUtils.mkdir_p tap.path.dirname + FileUtils.ln_sf TEST_FIXTURE_DIR.join("third-party"), tap.path + end + example.run ensure FileUtils.rm_rf dirs Hbc.default_tap.path.unlink FileUtils.rm_rf Hbc.default_tap.path.parent + third_party_tap.path.unlink + FileUtils.rm_rf third_party_tap.path.parent end 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 0000000000..bb6cda0b12 --- /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 0000000000..48fc1338e6 --- /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 = "https://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/github_spec.rb b/Library/Homebrew/test/utils/github_spec.rb index 9b539262fb..a132894f92 100644 --- a/Library/Homebrew/test/utils/github_spec.rb +++ b/Library/Homebrew/test/utils/github_spec.rb @@ -2,12 +2,38 @@ require "utils/github" describe GitHub do describe "::search_code", :needs_network do - it "searches code" do - results = subject.search_code("repo:Homebrew/brew", "path:/", "filename:readme", "language:markdown") + it "queries GitHub code with the passed parameters" do + results = subject.search_code(repo: "Homebrew/brew", path: "/", + filename: "readme", language: "markdown") expect(results.count).to eq(1) expect(results.first["name"]).to eq("README.md") expect(results.first["path"]).to eq("README.md") end end + + describe "::query_string" do + it "builds a query with the given hash parameters formatted as key:value" do + query = subject.query_string(user: "Homebrew", repo: "brew") + expect(query).to eq("q=user%3AHomebrew+repo%3Abrew&per_page=100") + end + + it "adds a variable number of top-level string parameters to the query when provided" do + query = subject.query_string("value1", "value2", user: "Homebrew") + expect(query).to eq("q=value1+value2+user%3AHomebrew&per_page=100") + end + + it "turns array values into multiple key:value parameters" do + query = subject.query_string(user: ["Homebrew", "caskroom"]) + expect(query).to eq("q=user%3AHomebrew+user%3Acaskroom&per_page=100") + end + end + + describe "::search_issues", :needs_network do + it "queries GitHub issues with the passed parameters" do + results = subject.search_issues("brew search", repo: "Homebrew/brew", author: "avetamine", is: "closed") + expect(results).not_to be_empty + expect(results.last["title"]).to eq("brew search : 422 Unprocessable Entity") + 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 0000000000..4edb365a03 --- /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/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb index 37bd83c4f3..3b5355b151 100644 --- a/Library/Homebrew/test/utils_spec.rb +++ b/Library/Homebrew/test/utils_spec.rb @@ -296,4 +296,33 @@ describe "globally-scoped helper methods" do expect(ENV["PATH"]).not_to eq("/bin") end end + + describe "#tap_and_name_comparison" do + describe "both strings are only names" do + it "alphabetizes the strings" do + expect(%w[a b].sort(&tap_and_name_comparison)).to eq(%w[a b]) + expect(%w[b a].sort(&tap_and_name_comparison)).to eq(%w[a b]) + end + end + + describe "both strings include tap" do + it "alphabetizes the strings" do + expect(%w[a/z/z b/z/z].sort(&tap_and_name_comparison)).to eq(%w[a/z/z b/z/z]) + expect(%w[b/z/z a/z/z].sort(&tap_and_name_comparison)).to eq(%w[a/z/z b/z/z]) + + expect(%w[z/a/z z/b/z].sort(&tap_and_name_comparison)).to eq(%w[z/a/z z/b/z]) + expect(%w[z/b/z z/a/z].sort(&tap_and_name_comparison)).to eq(%w[z/a/z z/b/z]) + + expect(%w[z/z/a z/z/b].sort(&tap_and_name_comparison)).to eq(%w[z/z/a z/z/b]) + expect(%w[z/z/b z/z/a].sort(&tap_and_name_comparison)).to eq(%w[z/z/a z/z/b]) + end + end + + describe "only one string includes tap" do + it "prefers the string without tap" do + expect(%w[a/z/z z].sort(&tap_and_name_comparison)).to eq(%w[z a/z/z]) + expect(%w[z a/z/z].sort(&tap_and_name_comparison)).to eq(%w[z a/z/z]) + end + end + end end diff --git a/Library/Homebrew/test/version_spec.rb b/Library/Homebrew/test/version_spec.rb index cee57e9352..d0393afa65 100644 --- a/Library/Homebrew/test/version_spec.rb +++ b/Library/Homebrew/test/version_spec.rb @@ -241,8 +241,18 @@ describe Version do describe "::detect" do matcher :be_detected_from do |url, specs = {}| - match do |version| - Version.detect(url, specs) == version + detected = Version.detect(url, specs) + + match do |expected| + detected == expected + end + + failure_message do |expected| + message = <<-EOS + expected: %s + detected: %s + EOS + format(message, expected, detected) end end @@ -639,6 +649,11 @@ describe Version do .to be_detected_from("ftp://gcc.gnu.org/pub/gcc/snapshots/6-20151227/gcc-6-20151227.tar.bz2") end + specify "semver in middle of URL" do + expect(Version.create("7.1.10")) + .to be_detected_from("https://php.net/get/php-7.1.10.tar.gz/from/this/mirror") + end + specify "from URL" do expect(Version.create("1.2.3")) .to be_detected_from("http://github.com/foo/bar.git", tag: "v1.2.3") diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index e3137ac49c..237ffa74e8 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -102,7 +102,7 @@ def odeprecated(method, replacement = nil, disable: false, disable_on: nil, call if ARGV.homebrew_developer? || disable || Homebrew.raise_deprecation_exceptions? raise MethodDeprecatedError, message - else + elsif !Homebrew.auditing? opoo "#{message}\n" end end @@ -229,10 +229,9 @@ module Homebrew EOS end - # Hash of Module => Set(method_names) - @injected_dump_stat_modules = {} - + # rubocop:disable Style/GlobalVars def inject_dump_stats!(the_module, pattern) + @injected_dump_stat_modules ||= {} @injected_dump_stat_modules[the_module] ||= [] injected_methods = @injected_dump_stat_modules[the_module] the_module.module_eval do @@ -260,11 +259,12 @@ module Homebrew end end end + # rubocop:enable Style/GlobalVars end def with_system_path old_path = ENV["PATH"] - ENV["PATH"] = "/usr/bin:/bin" + ENV["PATH"] = PATH.new("/usr/bin", "/bin") yield ensure ENV["PATH"] = old_path @@ -560,3 +560,15 @@ end def shell_profile Utils::Shell.profile end + +def tap_and_name_comparison + proc do |a, b| + if a.include?("/") && !b.include?("/") + 1 + elsif !a.include?("/") && b.include?("/") + -1 + else + a <=> b + end + end +end diff --git a/Library/Homebrew/utils/analytics.rb b/Library/Homebrew/utils/analytics.rb index a89995ba93..9766c14db3 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/bottles.rb b/Library/Homebrew/utils/bottles.rb index 927963bc1e..66b5fb640d 100644 --- a/Library/Homebrew/utils/bottles.rb +++ b/Library/Homebrew/utils/bottles.rb @@ -29,9 +29,11 @@ module Utils end def receipt_path(bottle_file) - Utils.popen_read("tar", "-tzf", bottle_file).lines.map(&:chomp).find do |line| + path = Utils.popen_read("tar", "-tzf", bottle_file).lines.map(&:chomp).find do |line| line =~ %r{.+/.+/INSTALL_RECEIPT.json} end + raise "This bottle does not contain the file INSTALL_RECEIPT.json: #{bottle_file}" unless path + path end def resolve_formula_names(bottle_file) @@ -52,6 +54,15 @@ module Utils def resolve_version(bottle_file) PkgVersion.parse receipt_path(bottle_file).split("/")[1] end + + def formula_contents(bottle_file, + name: resolve_formula_names(bottle_file)[0]) + bottle_version = resolve_version bottle_file + formula_path = "#{name}/#{bottle_version}/.brew/#{name}.rb" + contents = Utils.popen_read "tar", "-xOzf", bottle_file, formula_path + raise BottleFormulaUnavailableError.new(bottle_file, formula_path) unless $CHILD_STATUS.success? + contents + end end class Bintray diff --git a/Library/Homebrew/utils/curl.rb b/Library/Homebrew/utils/curl.rb index 5a40ae8469..7807d20344 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -1,42 +1,57 @@ require "pathname" require "open3" -def curl_args(options = {}) +def curl_executable curl = Pathname.new ENV["HOMEBREW_CURL"] curl = Pathname.new "/usr/bin/curl" unless curl.exist? - raise "#{curl} is not executable" unless curl.exist? && curl.executable? + return curl if curl.executable? + raise "#{curl} is not executable" +end +def curl_args(*extra_args, show_output: false, user_agent: :default) args = [ - curl.to_s, - "--remote-time", - "--location", + curl_executable.to_s, + "--show-error", ] - case options[:user_agent] - when :browser - args << "--user-agent" << HOMEBREW_USER_AGENT_FAKE_SAFARI + args << "--user-agent" << case user_agent + when :browser, :fake + HOMEBREW_USER_AGENT_FAKE_SAFARI + when :default + HOMEBREW_USER_AGENT_CURL else - args << "--user-agent" << HOMEBREW_USER_AGENT_CURL + user_agent end - unless options[:show_output] + unless show_output + args << "--fail" args << "--progress-bar" unless ARGV.verbose? args << "--verbose" if ENV["HOMEBREW_CURL_VERBOSE"] - args << "--fail" args << "--silent" if !$stdout.tty? || ENV["TRAVIS"] end - args += options[:extra_args] if options[:extra_args] - args + args + extra_args end def curl(*args) - safe_system(*curl_args(extra_args: args)) + safe_system(*curl_args(*args)) end -def curl_output(*args) - curl_args = curl_args(extra_args: args, show_output: true) - Open3.popen3(*curl_args) do |_, stdout, stderr, wait_thread| - [stdout.read, stderr.read, wait_thread.value] +def curl_download(*args, to: nil, continue_at: "-", **options) + had_incomplete_download ||= File.exist?(to) + 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. + # HTTP status 416: Requested range not satisfiable + if ($CHILD_STATUS.exitstatus == 33 || had_incomplete_download) && continue_at == "-" + continue_at = 0 + had_incomplete_download = false + retry end + + raise +end + +def curl_output(*args, **options) + Open3.capture3(*curl_args(*args, show_output: true, **options)) end diff --git a/Library/Homebrew/utils/fork.rb b/Library/Homebrew/utils/fork.rb index 92f5bf899b..57ddbfae29 100644 --- a/Library/Homebrew/utils/fork.rb +++ b/Library/Homebrew/utils/fork.rb @@ -14,7 +14,7 @@ module Utils read.close write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) yield - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException Marshal.dump(e, write) write.close exit! @@ -26,8 +26,11 @@ module Utils ignore_interrupts(:quietly) do # the child will receive the interrupt and marshal it back begin socket = server.accept_nonblock + # rubocop:disable Lint/ShadowedException + # FIXME: https://github.com/bbatsov/rubocop/issues/4843 rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR retry unless Process.waitpid(pid, Process::WNOHANG) + # rubocop:enable Lint/ShadowedException else socket.send_io(write) socket.close @@ -36,9 +39,9 @@ module Utils data = read.read read.close Process.wait(pid) unless socket.nil? - raise Marshal.load(data) unless data.nil? || data.empty? + raise Marshal.load(data) unless data.nil? || data.empty? # rubocop:disable Security/MarshalLoad 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 43d93b64ec..f1113af661 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}" @@ -28,8 +27,7 @@ end module Utils def self.git_available? - return @git if instance_variable_defined?(:@git) - @git = quiet_system HOMEBREW_SHIMS_PATH/"scm/git", "--version" + @git ||= quiet_system HOMEBREW_SHIMS_PATH/"scm/git", "--version" end def self.git_path @@ -50,21 +48,20 @@ module Utils return if git_available? # we cannot install brewed git if homebrew/core is unavailable. - raise "Git is unavailable" unless CoreTap.instance.installed? - - begin - oh1 "Installing git" - safe_system HOMEBREW_BREW_FILE, "install", "git" - rescue - raise "Git is unavailable" + if CoreTap.instance.installed? + begin + oh1 "Installing git" + safe_system HOMEBREW_BREW_FILE, "install", "git" + rescue + raise "Git is unavailable" + end end - clear_git_available_cache raise "Git is unavailable" unless git_available? end def self.clear_git_available_cache - remove_instance_variable(:@git) if instance_variable_defined?(:@git) + @git = nil @git_path = nil @git_version = nil end diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 1a781cee64..df0811e954 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -86,15 +86,9 @@ module GitHub def api_credentials_type token, username = api_credentials - if token && !token.empty? - if username && !username.empty? - :keychain - else - :environment - end - else - :none - end + return :none if !token || token.empty? + return :keychain if !username || username.empty? + :environment end def api_credentials_error_message(response_headers, needed_scopes) @@ -133,7 +127,7 @@ module GitHub def open(url, data: nil, scopes: [].freeze) # This is a no-op if the user is opting out of using the GitHub API. - return if ENV["HOMEBREW_NO_GITHUB_API"] + return block_given? ? yield({}) : {} if ENV["HOMEBREW_NO_GITHUB_API"] args = %W[--header application/vnd.github.v3+json --write-out \n%{http_code}] args += curl_args @@ -166,7 +160,7 @@ module GitHub args += ["--dump-header", headers_tmpfile.path] - output, errors, status = curl_output(url.to_s, *args) + output, errors, status = curl_output(url.to_s, "--location", *args) output, _, http_code = output.rpartition("\n") output, _, http_code = output.rpartition("\n") if http_code == "000" headers = headers_tmpfile.read @@ -227,72 +221,60 @@ module GitHub end end - def issues_matching(query, qualifiers = {}) - uri = URI.parse("#{API_URL}/search/issues") - uri.query = build_query_string(query, qualifiers) - open(uri) { |json| json["items"] } + def search_issues(query, **qualifiers) + search("issues", query, **qualifiers) end def repository(user, repo) - open(URI.parse("#{API_URL}/repos/#{user}/#{repo}")) + open(url_to("repos", user, repo)) end - def search_code(*params) - uri = URI.parse("#{API_URL}/search/code") - uri.query = "q=#{uri_escape(params.join(" "))}" - open(uri) { |json| json["items"] } - end - - def build_query_string(query, qualifiers) - s = "q=#{uri_escape(query)}+" - s << build_search_qualifier_string(qualifiers) - s << "&per_page=100" - end - - def build_search_qualifier_string(qualifiers) - { - repo: "Homebrew/homebrew-core", - in: "title", - }.update(qualifiers).map do |qualifier, value| - "#{qualifier}:#{value}" - end.join("+") - end - - def uri_escape(query) - if URI.respond_to?(:encode_www_form_component) - URI.encode_www_form_component(query) - else - require "erb" - ERB::Util.url_encode(query) - end + def search_code(**qualifiers) + search("code", **qualifiers) end def issues_for_formula(name, options = {}) tap = options[:tap] || CoreTap.instance - issues_matching(name, state: "open", repo: "#{tap.user}/homebrew-#{tap.repo}") + search_issues(name, state: "open", repo: "#{tap.user}/homebrew-#{tap.repo}") end def print_pull_requests_matching(query) - return [] if ENV["HOMEBREW_NO_GITHUB_API"] - - open_or_closed_prs = issues_matching(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" } - if !open_prs.empty? + prs = if !open_prs.empty? puts "Open pull requests:" - prs = open_prs - elsif !open_or_closed_prs.empty? - puts "Closed pull requests:" - prs = open_or_closed_prs + open_prs else - return + puts "Closed pull requests:" unless open_or_closed_prs.empty? + open_or_closed_prs end prs.each { |i| puts "#{i["title"]} (#{i["html_url"]})" } end def private_repo?(full_name) - uri = URI.parse("#{API_URL}/repos/#{full_name}") + uri = url_to "repos", full_name open(uri) { |json| json["private"] } end + + def query_string(*main_params, **qualifiers) + params = main_params + + params += qualifiers.flat_map do |key, value| + Array(value).map { |v| "#{key}:#{v}" } + end + + "q=#{URI.encode_www_form_component(params.join(" "))}&per_page=100" + end + + def url_to(*subroutes) + URI.parse([API_URL, *subroutes].join("/")) + end + + def search(entity, *queries, **qualifiers) + uri = url_to "search", entity + uri.query = query_string(*queries, **qualifiers) + open(uri) { |json| json.fetch("items", []) } + end end diff --git a/Library/Homebrew/utils/popen.rb b/Library/Homebrew/utils/popen.rb index 4e03711a16..2fa3ade462 100644 --- a/Library/Homebrew/utils/popen.rb +++ b/Library/Homebrew/utils/popen.rb @@ -1,20 +1,20 @@ module Utils - def self.popen_read(*args, &block) - popen(args, "rb", &block) + def self.popen_read(*args, **options, &block) + popen(args, "rb", options, &block) end - def self.popen_write(*args, &block) - popen(args, "wb", &block) + def self.popen_write(*args, **options, &block) + popen(args, "wb", options, &block) end - def self.popen(args, mode) + def self.popen(args, mode, options = {}) IO.popen("-", mode) do |pipe| if pipe return pipe.read unless block_given? yield pipe else - $stderr.reopen("/dev/null", "w") - exec(*args) + options[:err] ||= :close unless ENV["HOMEBREW_STDERR"] + exec(*args, options) end end end diff --git a/Library/Homebrew/utils/ruby.sh b/Library/Homebrew/utils/ruby.sh index 6945c068bb..9a3ab2e818 100644 --- a/Library/Homebrew/utils/ruby.sh +++ b/Library/Homebrew/utils/ruby.sh @@ -2,7 +2,8 @@ setup-ruby-path() { local vendor_dir local vendor_ruby_current_version local vendor_ruby_path - local ruby_version_major + local ruby_old_version + local minimum_ruby_version="2.3.3" vendor_dir="$HOMEBREW_LIBRARY/Homebrew/vendor" vendor_ruby_current_version="$vendor_dir/portable-ruby/current" @@ -21,7 +22,7 @@ setup-ruby-path() { if [[ $(readlink "$vendor_ruby_current_version") != "$(<"$vendor_dir/portable-ruby-version")" ]] then - if ! brew vendor-install ruby --quiet + if ! brew vendor-install ruby then onoe "Failed to upgrade vendor Ruby." fi @@ -36,14 +37,12 @@ setup-ruby-path() { if [[ -n "$HOMEBREW_RUBY_PATH" ]] then - ruby_version_major="$("$HOMEBREW_RUBY_PATH" --version)" - ruby_version_major="${ruby_version_major#ruby }" - ruby_version_major="${ruby_version_major%%.*}" + ruby_old_version="$("$HOMEBREW_RUBY_PATH" -rrubygems -e "puts Gem::Version.new('$minimum_ruby_version') > Gem::Version.new(RUBY_VERSION)")" fi - if [[ "$ruby_version_major" != "2" || -n "$HOMEBREW_FORCE_VENDOR_RUBY" ]] + if [[ "$ruby_old_version" == "true" || -n "$HOMEBREW_FORCE_VENDOR_RUBY" ]] then - brew vendor-install ruby --quiet + brew vendor-install ruby if [[ ! -x "$vendor_ruby_path" ]] then odie "Failed to install vendor Ruby." diff --git a/Library/Homebrew/utils/shell.rb b/Library/Homebrew/utils/shell.rb index 5327f6ecf6..8c1c5f984b 100644 --- a/Library/Homebrew/utils/shell.rb +++ b/Library/Homebrew/utils/shell.rb @@ -51,8 +51,6 @@ module Utils end end - private - SHELL_PROFILE_MAP = { bash: "~/.bash_profile", csh: "~/.cshrc", @@ -65,8 +63,6 @@ module Utils UNSAFE_SHELL_CHAR = %r{([^A-Za-z0-9_\-.,:/@\n])} - module_function - def csh_quote(str) # ruby's implementation of shell_escape str = str.to_s diff --git a/Library/Homebrew/utils/svn.rb b/Library/Homebrew/utils/svn.rb index fb49ac2e99..150b7eee74 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" diff --git a/Library/Homebrew/vendor/portable-ruby-version b/Library/Homebrew/vendor/portable-ruby-version index 633c00da36..0bee604df7 100644 --- a/Library/Homebrew/vendor/portable-ruby-version +++ b/Library/Homebrew/vendor/portable-ruby-version @@ -1 +1 @@ -2.0.0-p648 +2.3.3 diff --git a/Library/Homebrew/version.rb b/Library/Homebrew/version.rb index b03a4bc332..d43e0c6657 100644 --- a/Library/Homebrew/version.rb +++ b/Library/Homebrew/version.rb @@ -213,110 +213,6 @@ class Version end end - def initialize(val) - unless val.respond_to?(:to_str) - raise TypeError, "Version value must be a string; got a #{val.class} (#{val})" - end - @version = val.to_str - end - - def detected_from_url? - false - end - - def head? - false - end - - def null? - false - end - - def <=>(other) - # Needed to retain API compatibility with older string comparisons - # for compiler versions, etc. - other = Version.new(other) if other.is_a? String - # Used by the *_build_version comparisons, which formerly returned Fixnum - other = Version.new(other.to_s) if other.is_a? Integer - return 1 if other.nil? - - return unless other.is_a?(Version) - return 0 if version == other.version - return 1 if head? && !other.head? - return -1 if !head? && other.head? - return 0 if head? && other.head? - - ltokens = tokens - rtokens = other.tokens - max = max(ltokens.length, rtokens.length) - l = r = 0 - - while l < max - a = ltokens[l] || NULL_TOKEN - b = rtokens[r] || NULL_TOKEN - - if a == b - l += 1 - r += 1 - next - elsif a.numeric? && b.numeric? - return a <=> b - elsif a.numeric? - return 1 if a > NULL_TOKEN - l += 1 - elsif b.numeric? - return -1 if b > NULL_TOKEN - r += 1 - else - return a <=> b - end - end - - 0 - end - alias eql? == - - def hash - version.hash - end - - def to_f - version.to_f - end - - def to_s - version.dup - end - alias to_str to_s - - protected - - attr_reader :version - - def tokens - @tokens ||= tokenize - end - - private - - def max(a, b) - (a > b) ? a : b - end - - def tokenize - version.scan(SCAN_PATTERN).map! do |token| - case token - when /\A#{AlphaToken::PATTERN}\z/o then AlphaToken - when /\A#{BetaToken::PATTERN}\z/o then BetaToken - when /\A#{RCToken::PATTERN}\z/o then RCToken - when /\A#{PreToken::PATTERN}\z/o then PreToken - when /\A#{PatchToken::PATTERN}\z/o then PatchToken - when /\A#{NumericToken::PATTERN}\z/o then NumericToken - when /\A#{StringToken::PATTERN}\z/o then StringToken - end.new(token) - end - end - def self.parse(spec) version = _parse(spec) version.nil? ? NULL : new(version) @@ -456,6 +352,116 @@ class Version # e.g. http://www.ijg.org/files/jpegsrc.v8d.tar.gz m = /\.v(\d+[a-z]?)/.match(stem) return m.captures.first unless m.nil? + + # e.g. https://secure.php.net/get/php-7.1.10.tar.bz2/from/this/mirror + m = /[-.vV]?((?:\d+\.)+\d+(?:[-_.]?(?i:alpha|beta|pre|rc)\.?\d{,2})?)/.match(spec_s) + return m.captures.first unless m.nil? + end + + private_class_method :_parse + + def initialize(val) + unless val.respond_to?(:to_str) + raise TypeError, "Version value must be a string; got a #{val.class} (#{val})" + end + @version = val.to_str + end + + def detected_from_url? + false + end + + def head? + false + end + + def null? + false + end + + def <=>(other) + # Needed to retain API compatibility with older string comparisons + # for compiler versions, etc. + other = Version.new(other) if other.is_a? String + # Used by the *_build_version comparisons, which formerly returned Fixnum + other = Version.new(other.to_s) if other.is_a? Integer + return 1 if other.nil? + + return unless other.is_a?(Version) + return 0 if version == other.version + return 1 if head? && !other.head? + return -1 if !head? && other.head? + return 0 if head? && other.head? + + ltokens = tokens + rtokens = other.tokens + max = max(ltokens.length, rtokens.length) + l = r = 0 + + while l < max + a = ltokens[l] || NULL_TOKEN + b = rtokens[r] || NULL_TOKEN + + if a == b + l += 1 + r += 1 + next + elsif a.numeric? && b.numeric? + return a <=> b + elsif a.numeric? + return 1 if a > NULL_TOKEN + l += 1 + elsif b.numeric? + return -1 if b > NULL_TOKEN + r += 1 + else + return a <=> b + end + end + + 0 + end + alias eql? == + + def hash + version.hash + end + + def to_f + version.to_f + end + + def to_s + version.dup + end + alias to_str to_s + + protected + + attr_reader :version + + def tokens + @tokens ||= tokenize + end + + private + + def max(a, b) + (a > b) ? a : b + end + + def tokenize + version.scan(SCAN_PATTERN).map! do |token| + case token + when /\A#{AlphaToken::PATTERN}\z/o then AlphaToken + when /\A#{BetaToken::PATTERN}\z/o then BetaToken + when /\A#{RCToken::PATTERN}\z/o then RCToken + when /\A#{PreToken::PATTERN}\z/o then PreToken + when /\A#{PatchToken::PATTERN}\z/o then PatchToken + when /\A#{NumericToken::PATTERN}\z/o then NumericToken + when /\A#{StringToken::PATTERN}\z/o then StringToken + end.new(token) + end end end diff --git a/README.md b/README.md index 4d63942ae2..2ff99df3aa 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Second, read the [Troubleshooting Checklist](https://docs.brew.sh/Troubleshootin [![Travis](https://img.shields.io/travis/Homebrew/brew.svg)](https://travis-ci.org/Homebrew/brew) [![Codecov](https://img.shields.io/codecov/c/github/Homebrew/brew.svg)](https://codecov.io/gh/Homebrew/brew) -We'd love you to contribute to Homebrew. First, please read our [Contribution Guide](https://github.com/Homebrew/brew/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/Homebrew/brew/blob/master/CODEOFCONDUCT.md#code-of-conduct). +We'd love you to contribute to Homebrew. First, please read our [Contribution Guide](https://github.com/Homebrew/brew/blob/master/CONTRIBUTING.md) and [Code of Conduct](https://github.com/Homebrew/brew/blob/master/CODE_OF_CONDUCT.md#code-of-conduct). We explicitly welcome contributions from people who have never contributed to open-source before: we were all beginners once! We can help build on a partially working pull request with the aim of getting it merged. We are also actively seeking to diversify our contributors and especially welcome contributions from women from all backgrounds and people of colour. @@ -65,6 +65,10 @@ Our Jenkins CI installation is hosted by [DigitalOcean](https://m.do.co/c/7e39c3 ![DigitalOcean](https://cloud.githubusercontent.com/assets/125011/26827038/4b7b5ade-4ab3-11e7-811b-fed3ab0e934d.png) +Our physical hardware is hosted by [Commsworld](https://www.commsworld.com). + +![Commsworld powered by Fluency](https://user-images.githubusercontent.com/125011/30822845-1716bc2c-a222-11e7-843e-ea7c7b6a1503.png) + Our bottles (binary packages) are hosted by [Bintray](https://bintray.com/homebrew). [![Downloads by Bintray](https://bintray.com/docs/images/downloads_by_bintray_96.png)](https://bintray.com/homebrew) diff --git a/completions/bash/brew b/completions/bash/brew index 03a05cb37d..60c272f739 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -563,10 +563,10 @@ _brew() { if [[ "$i" -eq "$COMP_CWORD" ]] then - # Do not auto-complete "instal" abbreviation for "install" command. + # Do not auto-complete "*instal" or "*uninstal" aliases for "*install" commands. # Prefix newline to prevent not checking the first command. - local cmds=$'\n'"$(brew commands --quiet --include-aliases)" - __brewcomp "${cmds/$'\n'instal$'\n'/$'\n'}" + local cmds=$'\n'"$(brew commands --quiet --include-aliases | grep -v instal$)" + __brewcomp "${cmds}" return fi diff --git a/completions/zsh/_brew b/completions/zsh/_brew index 61dc26b42d..22792860dd 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -284,8 +284,8 @@ _brew_create() { ':url:_urls' } -# brew deps [--1] [-n] [--union] [--full-name] [--installed] [--include-build] [--include-optional] [--skip-recommended] formulae -# brew deps --tree [filters] (formulae|--installed) +# brew deps [--1] [-n] [--union] [--full-name] [--installed] [--include-build] [--include-optional] [--skip-recommended] [--include-requirements] formulae +# brew deps --tree [--1] [filters] (formulae|--installed) # brew deps [filters] (--installed|--all) # The filters placeholder is any combination of options --include-build, --include-optional, and --skip-recommended as documented above. _brew_deps() { @@ -294,6 +294,7 @@ _brew_deps() { '--include-build[include \:build dependencies]' \ '--include-optional[include \:optional dependencies]' \ '--skip-recommended[skip \:recommended type dependencies]' \ + '--include-requirements[include requirements]' \ '--1[only show dependencies one level down, instead of recursing]' \ '-n[show dependencies in topological order]' \ '--union[show the union of dependencies for formulae, instead of the intersection]' \ @@ -303,6 +304,11 @@ _brew_deps() { - tree-deps \ '--tree[show dependencies as a tree]' \ '(*)--installed[show dependencies for all installed formulae]' \ + '--include-build[include \:build dependencies]' \ + '--include-optional[include \:optional dependencies]' \ + '--skip-recommended[skip \:recommended type dependencies]' \ + '--include-requirements[include requirements]' \ + '--1[only show dependencies one level down, instead of recursing]' \ '(--installed)*:formulae:__brew_formulae' \ - installed-all \ '--include-build[include \:build dependencies]' \ @@ -586,8 +592,14 @@ _brew_style() { # brew switch name version: _brew_switch() { - _message "name version" - return 1 + local -a versions + if [[ -n ${words[2]} ]]; then + versions=(${$(brew ls "${words[2]}" --versions)#${words[2]}}) + fi + _arguments -S \ + '1::formula:__brew_formulae' \ + "2:: :(${versions[*]})" \ + && ret=0 } # brew tap: diff --git a/docs/Analytics.md b/docs/Analytics.md index 15fa6fc93b..18dc7bfa62 100644 --- a/docs/Analytics.md +++ b/docs/Analytics.md @@ -37,7 +37,9 @@ As far as we can tell it would be impossible for Google to match the randomly ge Homebrew's analytics are sent throughout Homebrew's execution to Google Analytics over HTTPS. ## Who? -Homebrew's analytics are accessible to Homebrew's current maintainers. Contact @MikeMcQuaid if you are a maintainer and need access. +Homebrew's detailed analytics are accessible to Homebrew's current maintainers. Contact @MikeMcQuaid if you are a maintainer and need access. + +Summaries of installation and error analytics are publicly available [here](https://brew.sh/analytics/). ## How? The code is viewable in [analytics.rb](https://github.com/Homebrew/brew/blob/master/Library/Homebrew/utils/analytics.rb) and [analytics.sh](https://github.com/Homebrew/brew/blob/master/Library/Homebrew/utils/analytics.sh). They are done in a separate background process and fail fast to avoid delaying any execution. They will fail immediately and silently if you have no network connection. diff --git a/docs/FAQ.md b/docs/FAQ.md index 23f53d54ce..9d76bf4ab6 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -26,6 +26,8 @@ To allow that formulae to update again: brew unpin +Note that pinned, outdated formulae that are depended on by another formula will be upgraded when required as we do not allow formulae to be built against non-latest versions. + ## How do I uninstall old versions of a formula? By default, Homebrew does not uninstall old versions of a formula, so over time you will accumulate old versions. To remove them, simply use: @@ -66,13 +68,12 @@ Be careful as this is a destructive operation. Which is usually: `~/Library/Caches/Homebrew` ## My Mac `.app`s don’t find `/usr/local/bin` utilities! -GUI apps on macOS don’t have `/usr/local/bin` in their `PATH` by default. -If you’re on Mountain Lion, you can fix this by running -`launchctl setenv PATH "/usr/local/bin:$PATH"`. [More details -here](https://stackoverflow.com/questions/135688/setting-environment-variables-in-os-x/5444960#5444960), -including how to set this across reboots. If you’re pre-Mountain Lion, -[here’s an official -alternative](https://developer.apple.com/legacy/library/qa/qa1067/_index.html). +GUI apps on macOS don’t have `/usr/local/bin` in their `PATH` by +default. If you’re on Mountain Lion or later, you can fix this by +running `sudo launchctl config user path "/usr/local/bin:$PATH"` and +then rebooting, as documented in `man launchctl`. Note that this sets +the launchctl PATH for _all users_. For earlier versions of macOS, see +[this page](https://developer.apple.com/legacy/library/qa/qa1067/_index.html). ## How do I contribute to Homebrew? Read [CONTRIBUTING.md](https://github.com/Homebrew/brew/blob/master/CONTRIBUTING.md). diff --git a/docs/Formula-Cookbook.md b/docs/Formula-Cookbook.md index e60afe071b..0266d39e53 100644 --- a/docs/Formula-Cookbook.md +++ b/docs/Formula-Cookbook.md @@ -693,7 +693,7 @@ Note that [`option`](http://www.rubydoc.info/github/Homebrew/brew/master/Formula ### File level operations -You can use the file utilities provided by Ruby's [`FileUtils`](http://www.ruby-doc.org/stdlib/libdoc/fileutils/rdoc/index.html). These are included in the `Formula` class, so you do not need the `FileUtils.` prefix to use them. +You can use the file utilities provided by Ruby's [`FileUtils`](https://www.ruby-doc.org/stdlib/libdoc/fileutils/rdoc/index.html). These are included in the `Formula` class, so you do not need the `FileUtils.` prefix to use them. When creating symlinks, take special care to ensure they are *relative* symlinks. This makes it easier to create a relocatable bottle. For example, to create a symlink in `bin` to an executable in `libexec`, use diff --git a/docs/Homebrew-and-Python.md b/docs/Homebrew-and-Python.md index 692e5c617b..8b263cb388 100644 --- a/docs/Homebrew-and-Python.md +++ b/docs/Homebrew-and-Python.md @@ -24,13 +24,13 @@ The Python formulae install [pip](http://www.pip-installer.org) (as `pip2` or `p Setuptools can be updated via pip, without having to re-brew Python: ```sh -pip install --upgrade setuptools +python2 -m pip --upgrade setuptools ``` Similarly, pip can be used to upgrade itself via: ```sh -pip install --upgrade pip +python2 -m pip install --upgrade pip ``` ### Note on `pip install --user` @@ -39,7 +39,7 @@ The normal `pip install --user` is disabled for brewed Python. This is because o A possible workaround (which puts executable scripts in `~/Library/Python/./bin`) is: ```sh -pip install --user --install-option="--prefix=" +python2 -m pip install --user --install-option="--prefix=" ``` ## `site-packages` and the `PYTHONPATH` @@ -86,7 +86,7 @@ CFLAGS=-I$(brew --prefix)/include LDFLAGS=-L$(brew --prefix)/lib pip install 2 10.10 or higher is recommended. 10.5–10.9 are -supported on a best-effort basis. For 10.4 and 10.5, see +supported on a best-effort basis. For 10.4 see [Tigerbrew](https://github.com/mistydemeo/tigerbrew). 3 Most formulae require a compiler. A handful diff --git a/docs/Interesting-Taps-and-Forks.md b/docs/Interesting-Taps-and-Forks.md index 6b70fadecb..5e3fc19d24 100644 --- a/docs/Interesting-Taps-and-Forks.md +++ b/docs/Interesting-Taps-and-Forks.md @@ -5,11 +5,9 @@ Homebrew has the capability to add (and remove) multiple taps to your local inst ## Main taps -* [homebrew/apache](https://github.com/Homebrew/homebrew-apache): A tap for Apache modules, extending macOS's built-in Apache. These brews may require unconventional additional setup, as explained in the caveats. +* [homebrew/nginx](https://github.com/Homebrew/homebrew-nginx): A tap for NGINX modules, intended for its `nginx-full` formula which includes more module options. -* [homebrew/nginx](https://github.com/Homebrew/homebrew-nginx): Feature rich Nginx tap for modules. - -* [homebrew/php](https://github.com/Homebrew/homebrew-php): Repository for php-related formulae. +* [homebrew/php](https://github.com/Homebrew/homebrew-php): Repository for PHP-related formulae. * [homebrew/science](https://github.com/Homebrew/homebrew-science): A collection of scientific libraries and tools. @@ -21,8 +19,6 @@ You can be added as a maintainer for one of the Homebrew organization taps and a * [InstantClientTap/instantclient](https://github.com/InstantClientTap/homebrew-instantclient): A tap for Oracle Instant Client. The packages need to be downloaded manually. -* [besport/ocaml](https://github.com/besport/homebrew-ocaml): A tap for Ocaml libraries, though with caveats, it requires you install its customized ocaml formula. Perhaps a template for more work. - * [osx-cross/avr](https://github.com/osx-cross/homebrew-avr): GNU AVR toolchain (Libc, compilers and other tools for Atmel MCUs, useful for Arduino hackers and AVR programmers). * [petere/postgresql](https://github.com/petere/homebrew-postgresql): Allows installing multiple PostgreSQL versions in parallel. @@ -31,6 +27,10 @@ You can be added as a maintainer for one of the Homebrew organization taps and a * [dunn/emacs](https://github.com/dunn/homebrew-emacs): A tap for Emacs packages. +* [sidaf/pentest](https://github.com/sidaf/homebrew-pentest): Tools for penetration testing. + +* [osrf/simulation](https://github.com/osrf/homebrew-simulation): Tools for robotics simulation. + ## Interesting forks * [mistydemeo/tigerbrew](https://github.com/mistydemeo/tigerbrew): Experimental Tiger PowerPC version diff --git a/docs/Kickstarter-Supporters.md b/docs/Kickstarter-Supporters.md index 84ba662b47..146718aecc 100644 --- a/docs/Kickstarter-Supporters.md +++ b/docs/Kickstarter-Supporters.md @@ -379,7 +379,7 @@ These wonderful people supported our Kickstarter by giving us £10 or more: * [Andrew Brown](http://pvalu.es) * [Bethany Sumner](http://www.bethanysumner.com/) * [Orta](http://orta.io) -* [Michał Gołębiowski](https://github.com/mgol) +* [Michał Gołębiowski-Owczarek](https://github.com/mgol) * [Adam C. Foltzer](http://www.acfoltzer.net/) * [Steve Hiemstra](https://www.speg.com) * [Anton Sipos](http://www.softwarefuturism.com) diff --git a/docs/Manpage.md b/docs/Manpage.md index 49f6bb5aee..f929911479 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -77,7 +77,7 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note a bug report, you will likely be asked for this information if you do not provide it. - * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] `formulae`: + * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] [`--include-requirements`] `formulae`: Show dependencies for `formulae`. When given multiple formula arguments, show the intersection of dependencies for `formulae`. @@ -98,15 +98,22 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note `formulae`. To include the `:build` type dependencies, pass `--include-build`. Similarly, pass `--include-optional` to include `:optional` dependencies. To skip `:recommended` type dependencies, pass `--skip-recommended`. + To include requirements in addition to dependencies, pass `--include-requirements`. - * `deps` `--tree` [`filters`] (`formulae`|`--installed`): + * `deps` `--tree` [`--1`] [`filters`] [`--annotate`] (`formulae`|`--installed`): Show dependencies as a tree. When given multiple formula arguments, output individual trees for every formula. + If `--1` is passed, only one level of children is displayed. + If `--installed` is passed, output a tree for every installed formula. The `filters` placeholder is any combination of options `--include-build`, - `--include-optional`, and `--skip-recommended` as documented above. + `--include-optional`, `--skip-recommended`, and `--include-requirements` as + documented above. + + If `--annotate` is passed, the build, optional, and recommended dependencies + are marked as such in the output. * `deps` [`filters`] (`--installed`|`--all`): Show dependencies for installed or all available formulae. Every line of @@ -364,7 +371,8 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note * `pin` `formulae`: Pin the specified `formulae`, preventing them from being upgraded when - issuing the `brew upgrade` command. See also `unpin`. + issuing the `brew upgrade `formulae`` command (but can still be upgraded + as dependencies for other formulae). See also `unpin`. * `postinstall` `formula`: Rerun the post-install steps for `formula`. @@ -529,8 +537,8 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note source. This is useful for creating patches for the software. * `unpin` `formulae`: - Unpin `formulae`, allowing them to be upgraded by `brew upgrade`. See also - `pin`. + Unpin `formulae`, allowing them to be upgraded by `brew upgrade `formulae``. + See also `pin`. * `untap` `tap`: Remove a tapped repository. diff --git a/docs/Prose-Style-Guidelines.md b/docs/Prose-Style-Guidelines.md index 232ccb1bea..4fe19bdb82 100644 --- a/docs/Prose-Style-Guidelines.md +++ b/docs/Prose-Style-Guidelines.md @@ -16,7 +16,7 @@ Homebrew's audience includes users with a wide range of education and experience We strive for "correct" but not "fancy" usage. Think newspaper article, not academic paper. -This is a set of guidelines to be applied using human judgment, not a set of hard and fast rules. It is like [The Economist's Style Guide](http://www.economist.com/styleguide/introduction) or [Garner's Modern American Usage](https://en.wikipedia.org/wiki/Garner's_Modern_American_Usage). It is less like the [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide). All guidelines here are open to interpretation and discussion. 100% conformance to these guidelines is *not* a goal. +This is a set of guidelines to be applied using human judgment, not a set of hard and fast rules. It is like [The Economist's Style Guide](https://www.economist.com/styleguide/introduction) or [Garner's Modern American Usage](https://en.wikipedia.org/wiki/Garner's_Modern_American_Usage). It is less like the [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide). All guidelines here are open to interpretation and discussion. 100% conformance to these guidelines is *not* a goal. The intent of this document is to help authors make decisions about clarity, style, and consistency. It is not to help settle arguments about who knows English better. Don't use this document to be a jerk. diff --git a/docs/Python-for-Formula-Authors.md b/docs/Python-for-Formula-Authors.md index 5b9d6ced64..129f5ba5ed 100644 --- a/docs/Python-for-Formula-Authors.md +++ b/docs/Python-for-Formula-Authors.md @@ -10,33 +10,7 @@ Bindings are a special case of libraries that allow Python code to interact with Homebrew is happy to accept applications that are built in Python, whether the apps are available from PyPI or not. Homebrew generally won't accept libraries that can be installed correctly with `pip install foo`. Bindings may be installed for packages that provide them, especially if equivalent functionality isn't available through pip. -## Running `setup.py` - -Homebrew provides a helper method, `Language::Python.setup_install_args`, which returns arguments for invoking `setup.py`. Your formula should use this instead of invoking `setup.py` explicitly. The syntax is: - -```ruby -system "python", *Language::Python.setup_install_args(prefix) -``` - -where `prefix` is the destination prefix (usually `libexec` or `prefix`). - -## Python module dependencies - -In general, applications should unconditionally bundle all of their dependencies and libraries and should install any unsatisfied dependencies; these strategies are discussed in depth in the following sections. - -In the rare instance that this proves impractical, you can specify a Python module as an external dependency using this syntax: - -```ruby -depends_on "numpy" => :python -``` - -Or if the import name is different from the module name: - -```ruby -depends_on "MacFSEvents" => [:python, "fsevents"] -``` - -If you submit a formula with this syntax to core, you may be asked to rewrite it as a `Requirement`. +Applications should unconditionally bundle all of their Python-language dependencies and libraries and should install any unsatisfied dependencies; these strategies are discussed in depth in the following sections. ## Applications @@ -147,9 +121,9 @@ in case you need to do different things for different resources. ## Bindings -To add an option to a formula to build Python bindings, use `depends_on :python => :recommended` and install the bindings conditionally on `build.with? "python"` in your `install` method. +Build bindings with system Python by default (don't add an option) and they should be usable with any binary-compatible Python. If that isn't the case, it's an upstream bug; [here's some advice for resolving it](http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/). -Python bindings should be optional because if the formula is bottled, any `:recommended` or mandatory dependencies on `:python` are always resolved by installing the Homebrew `python` formula, which will upset users that prefer to use the system Python. This is because we cannot generally create a binary package that works against both versions of Python. +To add bindings for Python 3, please add `depends_on :python3 => :optional` and make the bindings conditional on `build.with?("python3")`. ### Dependencies @@ -179,9 +153,7 @@ Sometimes we have to `inreplace` a `Makefile` to use our prefix for the Python b ### Python declarations -Libraries **should** declare a dependency on `:python` or `:python3` as appropriate, which will respectively cause the formula to use the first python or python3 discovered in `PATH` at install time. If a library supports both Python 2.x and Python 3.x, the `:python` dependency **should** be `:recommended` (i.e. built by default) and the `:python3` dependency should be `:optional`. Python 2.x libraries **must** function when they are installed against either the system Python or Homebrew Python. - -Formulae that declare a dependency on `:python` will always be bottled against Homebrew's python, since we cannot in general build binary packages that can be imported from both Pythons. Users can add `--build-from-source` after `brew install` to compile against whichever python is in `PATH`. +Python 2 libraries do not need a `depends_on :python` declaration; they will be built with system Python, but should still be usable with any other Python 2.7. If this is not the case, it is an upstream bug; [here is some advice for resolving it](http://blog.tim-smith.us/2015/09/python-extension-modules-os-x/). Libraries built for Python 3 should include `depends_on :python3`, which will bottle against Homebrew's python3, and use the first python3 discovered in `PATH` at build time when installing from source with `brew install --build-from-source`. If a library supports both Python 2.x and Python 3.x, the `:python3` dependency should be `:optional`. Python 2.x libraries must function when they are installed against either the system Python or Homebrew Python. ### Installing @@ -191,7 +163,7 @@ Most formulae presently just install to `prefix`. ### Dependencies -The dependencies of libraries must be installed so that they are importable. The principle of minimum surprise suggests that installing a Homebrew library should not alter the other libraries in a user's `sys.path`. The best way to achieve this is to only install dependencies if they are not already installed. To minimize the potential for linking conflicts, dependencies should be installed to `libexec/"vendor"` and added to `sys.path` by writing a second .pth file (named like "homebrew-foo-dependencies.pth") to the `prefix` site-packages. +The dependencies of libraries must be installed so that they are importable. To minimize the potential for linking conflicts, dependencies should be installed to `libexec/"vendor"` and added to `sys.path` by writing a second .pth file (named like "homebrew-foo-dependencies.pth") to the `prefix` site-packages. The [matplotlib](https://github.com/Homebrew/homebrew-science/blob/master/matplotlib.rb) formula in [homebrew/science](https://github.com/Homebrew/homebrew-science) deploys this strategy. @@ -209,6 +181,16 @@ Distutils and pip use a "flat" installation hierarchy that installs modules as i Distribute (not to be confused with distutils) is an obsolete fork of setuptools. Distlib is a package maintained outside the standard library which is used by pip for some low-level packaging operations and is not relevant to most `setup.py` users. +### Running `setup.py` + +In the event that a formula needs to interact with `setup.py` instead of calling `pip`, Homebrew provides a helper method, `Language::Python.setup_install_args`, which returns useful arguments for invoking `setup.py`. Your formula should use this instead of invoking `setup.py` explicitly. The syntax is: + +```ruby +system "python", *Language::Python.setup_install_args(prefix) +``` + +where `prefix` is the destination prefix (usually `libexec` or `prefix`). + ### What is `--single-version-externally-managed`? `--single-version-externally-managed` ("SVEM") is a setuptools-only [argument to `setup.py install`](https://setuptools.readthedocs.io/en/latest/setuptools.html?#install-run-easy-install-or-old-style-installation). The primary effect of SVEM is to use distutils to perform the install instead of using setuptools' `easy_install`. diff --git a/manpages/brew-cask.1 b/manpages/brew-cask.1 index f11d48c094..f2acf1842c 100644 --- a/manpages/brew-cask.1 +++ b/manpages/brew-cask.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW\-CASK" "1" "July 2017" "Homebrew" "brew-cask" +.TH "BREW\-CASK" "1" "September 2017" "Homebrew" "brew-cask" . .SH "NAME" \fBbrew\-cask\fR \- a friendly binary installer for macOS @@ -18,7 +18,7 @@ Homebrew\-Cask works robustly enough that we welcome new users, but the project .SH "FREQUENTLY USED COMMANDS" . .TP -\fBinstall\fR [\-\-force] [\-\-skip\-cask\-deps] [\-\-require\-sha] \fItoken\fR [ \fItoken\fR \.\.\. ] +\fBinstall\fR [\-\-force] [\-\-skip\-cask\-deps] [\-\-require\-sha] [\-\-language=\fIiso\-language\fR[,\fIiso\-language\fR \.\.\. ]] \fItoken\fR [ \fItoken\fR \.\.\. ] Install Cask identified by \fItoken\fR\. . .TP @@ -35,7 +35,7 @@ The tokens returned by \fBsearch\fR are suitable as arguments for most other com .SH "COMMANDS" . .TP -\fBaudit\fR [ \fItoken\fR \.\.\. ] +\fBaudit\fR [\-\-language=\fIiso\-language\fR[,\fIiso\-language\fR \.\.\. ]] [ \fItoken\fR \.\.\. ] Check the given Casks for installability\. If no tokens are given on the command line, all Casks are audited\. . .TP @@ -103,7 +103,7 @@ Reinstall the given Cask\. . .TP \fBsearch\fR or \fB\-S\fR [\fItext\fR | /\fIregexp\fR/] -Without an argument, display all Casks available for install; otherwise perform a substring search of known Cask tokens for \fItext\fR or, if the text is delimited by slashes (/\fIregexp\fR/), it is interpreted as a Ruby regular expression\. +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 \fItext\fR or, if the text is delimited by slashes (/\fIregexp\fR/), it is interpreted as a Ruby regular expression\. . .TP \fBstyle\fR [\-\-fix] [ \fItoken\fR \.\.\. ] @@ -169,6 +169,10 @@ Give additional feedback during installation\. Target location for Applications\. The default value is \fB/Applications\fR\. . .TP +\fB\-\-language=[, \.\.\. ]]\fR +Set language of the Cask to install\. The first matching language is used, otherwise the default language on the Cask\. The default value is the \fBlanguage of your system\fR\. +. +.TP \fB\-\-colorpickerdir=\fR Target location for Color Pickers\. The default value is \fB~/Library/ColorPickers\fR\. . diff --git a/manpages/brew.1 b/manpages/brew.1 index 2d8ba3f09f..b3d8402752 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "BREW" "1" "July 2017" "Homebrew" "brew" +.TH "BREW" "1" "September 2017" "Homebrew" "brew" . .SH "NAME" \fBbrew\fR \- The missing package manager for macOS @@ -88,7 +88,7 @@ If \fB\-\-quiet\fR is passed, list only the names of commands without the header Show Homebrew and system configuration useful for debugging\. If you file a bug report, you will likely be asked for this information if you do not provide it\. . .TP -\fBdeps\fR [\fB\-\-1\fR] [\fB\-n\fR] [\fB\-\-union\fR] [\fB\-\-full\-name\fR] [\fB\-\-installed\fR] [\fB\-\-include\-build\fR] [\fB\-\-include\-optional\fR] [\fB\-\-skip\-recommended\fR] \fIformulae\fR +\fBdeps\fR [\fB\-\-1\fR] [\fB\-n\fR] [\fB\-\-union\fR] [\fB\-\-full\-name\fR] [\fB\-\-installed\fR] [\fB\-\-include\-build\fR] [\fB\-\-include\-optional\fR] [\fB\-\-skip\-recommended\fR] [\fB\-\-include\-requirements\fR] \fIformulae\fR Show dependencies for \fIformulae\fR\. When given multiple formula arguments, show the intersection of dependencies for \fIformulae\fR\. . .IP @@ -107,17 +107,23 @@ If \fB\-\-full\-name\fR is passed, list dependencies by their full name\. If \fB\-\-installed\fR is passed, only list those dependencies that are currently installed\. . .IP -By default, \fBdeps\fR shows required and recommended dependencies for \fIformulae\fR\. To include the \fB:build\fR type dependencies, pass \fB\-\-include\-build\fR\. Similarly, pass \fB\-\-include\-optional\fR to include \fB:optional\fR dependencies\. To skip \fB:recommended\fR type dependencies, pass \fB\-\-skip\-recommended\fR\. +By default, \fBdeps\fR shows required and recommended dependencies for \fIformulae\fR\. To include the \fB:build\fR type dependencies, pass \fB\-\-include\-build\fR\. Similarly, pass \fB\-\-include\-optional\fR to include \fB:optional\fR dependencies\. To skip \fB:recommended\fR type dependencies, pass \fB\-\-skip\-recommended\fR\. To include requirements in addition to dependencies, pass \fB\-\-include\-requirements\fR\. . .TP -\fBdeps\fR \fB\-\-tree\fR [\fIfilters\fR] (\fIformulae\fR|\fB\-\-installed\fR) +\fBdeps\fR \fB\-\-tree\fR [\fB\-\-1\fR] [\fIfilters\fR] [\fB\-\-annotate\fR] (\fIformulae\fR|\fB\-\-installed\fR) Show dependencies as a tree\. When given multiple formula arguments, output individual trees for every formula\. . .IP +If \fB\-\-1\fR is passed, only one level of children is displayed\. +. +.IP If \fB\-\-installed\fR is passed, output a tree for every installed formula\. . .IP -The \fIfilters\fR placeholder is any combination of options \fB\-\-include\-build\fR, \fB\-\-include\-optional\fR, and \fB\-\-skip\-recommended\fR as documented above\. +The \fIfilters\fR placeholder is any combination of options \fB\-\-include\-build\fR, \fB\-\-include\-optional\fR, \fB\-\-skip\-recommended\fR, and \fB\-\-include\-requirements\fR as documented above\. +. +.IP +If \fB\-\-annotate\fR is passed, the build, optional, and recommended dependencies are marked as such in the output\. . .TP \fBdeps\fR [\fIfilters\fR] (\fB\-\-installed\fR|\fB\-\-all\fR) @@ -378,7 +384,7 @@ If \fB\-\-fetch\-HEAD\fR is passed, fetch the upstream repository to detect if t . .TP \fBpin\fR \fIformulae\fR -Pin the specified \fIformulae\fR, preventing them from being upgraded when issuing the \fBbrew upgrade\fR command\. See also \fBunpin\fR\. +Pin the specified \fIformulae\fR, preventing them from being upgraded when issuing the \fBbrew upgrade \fR command (but can still be upgraded as dependencies for other formulae)\. See also \fBunpin\fR\. . .TP \fBpostinstall\fR \fIformula\fR @@ -549,7 +555,7 @@ If \fB\-\-git\fR (or \fB\-g\fR) is passed, a Git repository will be initialized . .TP \fBunpin\fR \fIformulae\fR -Unpin \fIformulae\fR, allowing them to be upgraded by \fBbrew upgrade\fR\. See also \fBpin\fR\. +Unpin \fIformulae\fR, allowing them to be upgraded by \fBbrew upgrade \fR\. See also \fBpin\fR\. . .TP \fBuntap\fR \fItap\fR