diff options
Diffstat (limited to 'Library')
205 files changed, 2816 insertions, 1890 deletions
diff --git a/Library/.rubocop.yml b/Library/.rubocop.yml index 442315e6b..8099c7ad0 100644 --- a/Library/.rubocop.yml +++ b/Library/.rubocop.yml @@ -1,13 +1,17 @@ AllCops: TargetRubyVersion: 2.0 Exclude: - - '**/Rakefile' - - '**/.*' - '**/Casks/**/*' - '**/vendor/**/*' require: ./Homebrew/rubocops.rb +FormulaAudit/Text: + Enabled: true + +FormulaAudit/Caveats: + Enabled: true + FormulaAuditStrict/BottleBlock: Enabled: true @@ -20,65 +24,97 @@ FormulaAuditStrict/ComponentsOrder: FormulaAuditStrict/ComponentsRedundancy: Enabled: true -Metrics/AbcSize: - Enabled: false - -Metrics/BlockLength: - Enabled: false +FormulaAudit/Homepage: + Enabled: true -Metrics/ClassLength: +# `system` is a special case and aligns on second argument +Layout/AlignParameters: Enabled: false -Metrics/CyclomaticComplexity: - Enabled: false +Layout/CaseIndentation: + EnforcedStyle: end -Metrics/LineLength: - Enabled: false +Layout/EmptyLineBetweenDefs: + AllowAdjacentOneLineDefs: true -Metrics/MethodLength: - Enabled: false +Layout/IndentArray: + EnforcedStyle: special_inside_parentheses -Metrics/ModuleLength: - CountComments: false +Layout/IndentHeredoc: + EnforcedStyle: unindent -Metrics/PerceivedComplexity: +# conflicts with DSL-style path concatenation with `/` +Layout/SpaceAroundOperators: Enabled: false # favor parens-less DSL-style arguments Lint/AmbiguousOperator: Enabled: false +# so many of these in formulae and can't be autocorrected Lint/AmbiguousRegexpLiteral: Enabled: false +# favor parens-less DSL-style arguments +Lint/AmbiguousBlockAssociation: + Enabled: false + +# assignment in conditions are useful sometimes Lint/AssignmentInCondition: Enabled: false Lint/EndAlignment: EnforcedStyleAlignWith: variable +# so many of these in formulae and can't be autocorrected Lint/ParenthesesAsGroupedExpression: Enabled: false -Style/Alias: - EnforcedStyle: prefer_alias +# TODO: try to bring down all metrics maximums +Metrics/AbcSize: + Max: 250 -Style/AlignHash: - Enabled: false +Metrics/BlockLength: + Max: 1250 -# `system` is a special case and aligns on second argument -Style/AlignParameters: +Metrics/ClassLength: + Max: 1500 + +Metrics/CyclomaticComplexity: + Max: 75 + +Metrics/LineLength: + Max: 400 + +Metrics/MethodLength: + Max: 250 + +Metrics/ModuleLength: + CountComments: false + Exclude: + - '**/bin/**/*' + - '**/cmd/**/*' + - '**/lib/**/*' + +Metrics/PerceivedComplexity: + Max: 80 + +# makes code less readable for minor performance increases +Performance/Caller: Enabled: false +Style/Alias: + EnforcedStyle: prefer_alias + +Style/AutoResourceCleanup: + Enabled: true + Style/BarePercentLiterals: EnforcedStyle: percent_q Style/BlockDelimiters: EnforcedStyle: line_count_based -Style/CaseIndentation: - EnforcedStyle: end - Style/ClassAndModuleChildren: EnforcedStyle: nested @@ -91,16 +127,22 @@ Style/CommandLiteral: Style/ConditionalAssignment: Enabled: false +# most of our APIs are internal so don't require docs Style/Documentation: Enabled: false -Style/EmptyLineBetweenDefs: - AllowAdjacentOneLineDefs: true +Style/Encoding: + Enabled: true # dashes in filenames are typical Style/FileName: Regex: !ruby/regexp /^[\w\@\-\+\.]+(\.rb)?$/ +# falsely flags e.g. curl formatting arguments as format strings +Style/FormatStringToken: + Enabled: false + +# so many of these in formulae and can't be autocorrected Style/GuardClause: Enabled: false @@ -113,13 +155,6 @@ Style/HashSyntax: - '**/lib/**/*' - '**/spec/**/*' -# disabled until it respects line length -Style/IfUnlessModifier: - Enabled: false - -Style/IndentArray: - EnforcedStyle: special_inside_parentheses - # only for numbers >= 1_000_000 Style/NumericLiterals: MinDigits: 7 @@ -152,13 +187,6 @@ Style/RaiseArgs: Style/RegexpLiteral: EnforcedStyle: slashes -# conflicts with DSL-style path concatenation with `/` -Style/SpaceAroundOperators: - Enabled: false - -Style/SingleLineBlockParams: - Enabled: false - # not a problem for typical shell users Style/SpecialGlobalVars: Enabled: false @@ -171,17 +199,22 @@ Style/StringLiterals: Style/StringLiteralsInInterpolation: EnforcedStyle: double_quotes +Style/SymbolArray: + EnforcedStyle: brackets + Style/TernaryParentheses: - Enabled: false + EnforcedStyle: require_parentheses_when_complex # makes diffs nicer Style/TrailingCommaInLiteral: EnforcedStyleForMultiline: comma + Style/TrailingCommaInArguments: EnforcedStyleForMultiline: comma +# we have too many variables like sha256 where this harms readability Style/VariableNumber: Enabled: false Style/WordArray: - Enabled: false + MinSize: 4 diff --git a/Library/Homebrew/.rubocop.yml b/Library/Homebrew/.rubocop.yml index 938bf21b7..26c944529 100644 --- a/Library/Homebrew/.rubocop.yml +++ b/Library/Homebrew/.rubocop.yml @@ -9,9 +9,8 @@ AllCops: - '**/Casks/**/*' - '**/vendor/**/*' -Style/BlockDelimiters: +Layout/MultilineMethodCallIndentation: Exclude: - - '**/cask/spec/**/*' - '**/*_spec.rb' # so many of these in formulae but none in here @@ -27,12 +26,12 @@ Lint/NestedMethodDefinition: Lint/ParenthesesAsGroupedExpression: Enabled: true -Metrics/ModuleLength: - CountComments: false +Metrics/ParameterLists: + CountKeywordArgs: false + +Style/BlockDelimiters: Exclude: - - 'cask/lib/hbc/locations.rb' - - 'cask/lib/hbc/macos.rb' - - 'cask/lib/hbc/utils.rb' + - '**/*_spec.rb' # so many of these in formulae but none in here Style/GuardClause: diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index e07599ac6..7222f7e87 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -21,10 +21,11 @@ if ARGV == %w[--version] || ARGV == %w[-v] end def require?(path) + return false if path.nil? require path rescue LoadError => e # we should raise on syntax errors but not if the file doesn't exist. - raise unless e.to_s.include? path + raise unless e.message.include?(path) end begin @@ -48,20 +49,24 @@ begin end path = PATH.new(ENV["PATH"]) + homebrew_path = PATH.new(ENV["HOMEBREW_PATH"]) # Add contributed commands to PATH before checking. - path.append(Pathname.glob(Tap::TAP_DIRECTORY/"*/*/cmd")) + tap_cmds = Pathname.glob(Tap::TAP_DIRECTORY/"*/*/cmd") + path.append(tap_cmds) + homebrew_path.append(tap_cmds) # Add SCM wrappers. path.append(HOMEBREW_SHIMS_PATH/"scm") + homebrew_path.append(HOMEBREW_SHIMS_PATH/"scm") ENV["PATH"] = path if cmd - internal_cmd = require? HOMEBREW_LIBRARY_PATH.join("cmd", cmd) + internal_cmd = require? HOMEBREW_LIBRARY_PATH/"cmd"/cmd unless internal_cmd - internal_cmd = require? HOMEBREW_LIBRARY_PATH.join("dev-cmd", cmd) + internal_cmd = require? HOMEBREW_LIBRARY_PATH/"dev-cmd"/cmd if internal_cmd && !ARGV.homebrew_developer? system "git", "config", "--file=#{HOMEBREW_REPOSITORY}/.git/config", "--replace-all", "homebrew.devcmdrun", "true" @@ -88,6 +93,9 @@ begin system(HOMEBREW_BREW_FILE, "uninstall", "--force", "brew-cask") end + # External commands expect a normal PATH + ENV["PATH"] = homebrew_path unless internal_cmd + if internal_cmd Homebrew.send cmd.to_s.tr("-", "_").downcase elsif which "brew-#{cmd}" @@ -115,7 +123,6 @@ begin odie "Unknown command: #{cmd}" end end - rescue UsageError => e require "cmd/help" Homebrew.help cmd, usage_error: e.message diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 02ce5e1c1..b76280099 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -145,7 +145,7 @@ export HOMEBREW_MACOS_VERSION export HOMEBREW_USER_AGENT export HOMEBREW_USER_AGENT_CURL -if [[ -n "$HOMEBREW_MACOS" ]] +if [[ -n "$HOMEBREW_MACOS" && -x "/usr/bin/xcode-select" ]] then XCODE_SELECT_PATH=$('/usr/bin/xcode-select' --print-path 2>/dev/null) if [[ "$XCODE_SELECT_PATH" = "/" ]] diff --git a/Library/Homebrew/build_environment.rb b/Library/Homebrew/build_environment.rb index e3299fb94..dc28b2293 100644 --- a/Library/Homebrew/build_environment.rb +++ b/Library/Homebrew/build_environment.rb @@ -22,12 +22,12 @@ class BuildEnvironment def userpaths? @settings.include? :userpaths end -end -module BuildEnvironmentDSL - def env(*settings) - @env ||= BuildEnvironment.new - @env.merge(settings) + module DSL + def env(*settings) + @env ||= BuildEnvironment.new + @env.merge(settings) + end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact.rb b/Library/Homebrew/cask/lib/hbc/artifact.rb index b155a125a..074d15017 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact.rb @@ -25,10 +25,17 @@ require "hbc/artifact/zap" module Hbc module Artifact - # NOTE: order is important here, since we want to extract nested containers - # before we handle any other artifacts + # 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, @@ -49,17 +56,16 @@ module Hbc VstPlugin, Vst3Plugin, ScreenSaver, - Uninstall, PostflightBlock, Zap, ].freeze - def self.for_cask(cask, command: SystemCommand, force: false) + 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, command: command, force: force) } + .map { |klass| klass.new(cask, options) } end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/base.rb b/Library/Homebrew/cask/lib/hbc/artifact/base.rb index 924493fc0..2d9330b13 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/base.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/base.rb @@ -10,7 +10,7 @@ module Hbc end def self.artifact_english_article - @artifact_english_article ||= artifact_english_name =~ /^[aeiou]/i ? "an" : "a" + @artifact_english_article ||= (artifact_english_name =~ /^[aeiou]/i) ? "an" : "a" end def self.artifact_dsl_key @@ -43,7 +43,7 @@ module Hbc 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.reject! { |k| !permitted_keys.include?(k) } + arguments.select! { |k| permitted_keys.include?(k) } # key warnings override_keys = override_arguments.keys @@ -65,10 +65,19 @@ module Hbc {} end - def initialize(cask, command: SystemCommand, force: false) + def verbose? + @verbose + end + + def force? + @force + end + + def initialize(cask, command: SystemCommand, force: false, verbose: false) @cask = cask @command = command @force = force + @verbose = verbose end end end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb index 21d123ab9..7178c2af6 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/binary.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/binary.rb @@ -3,10 +3,6 @@ require "hbc/artifact/symlinked" module Hbc module Artifact class Binary < Symlinked - def install_phase - super if CLI.binaries? - end - def link super return if source.executable? diff --git a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb index eaaa49e20..3fe969c0c 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/moved.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/moved.rb @@ -20,7 +20,7 @@ module Hbc def move 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 + raise CaskError, "#{message}." unless force? opoo "#{message}; overwriting." delete end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb b/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb index c43481c82..be0a6be71 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/pkg.rb @@ -48,7 +48,7 @@ module Hbc "-pkg", source, "-target", "/" ] - args << "-verboseR" if CLI.verbose? + args << "-verboseR" if verbose? args << "-allowUntrusted" if pkg_install_opts :allow_untrusted with_choices_file do |choices_path| args << "-applyChoiceChangesXML" << choices_path if choices_path diff --git a/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb b/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb index 7757d32d0..4dba46c9d 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/relocated.rb @@ -35,10 +35,10 @@ module Hbc altnames = "(#{altnames})" # Some packges are shipped as u=rx (e.g. Bitcoin Core) - @command.run!("/bin/chmod", args: ["--", "u+rw", file.to_s, file.realpath.to_s]) + @command.run!("/bin/chmod", args: ["--", "u+rw", file, file.realpath]) @command.run!("/usr/bin/xattr", - args: ["-w", ALT_NAME_ATTRIBUTE, altnames, file.to_s], + args: ["-w", ALT_NAME_ATTRIBUTE, altnames, file], print_stderr: false) end diff --git a/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb b/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb index 478f313b5..7dc772380 100644 --- a/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb +++ b/Library/Homebrew/cask/lib/hbc/artifact/uninstall_base.rb @@ -173,7 +173,7 @@ module Hbc 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 @@ -197,9 +197,7 @@ module Hbc paths.each do |path| resolved_path = Pathname.new(path) - if path.start_with?("~") - resolved_path = resolved_path.expand_path - end + resolved_path = resolved_path.expand_path if path.start_with?("~") if resolved_path.relative? || resolved_path.split.any? { |part| part.to_s == ".." } opoo "Skipping #{Formatter.identifier(action)} for relative path '#{path}'." diff --git a/Library/Homebrew/cask/lib/hbc/audit.rb b/Library/Homebrew/cask/lib/hbc/audit.rb index 12cefb939..cee1fe807 100644 --- a/Library/Homebrew/cask/lib/hbc/audit.rb +++ b/Library/Homebrew/cask/lib/hbc/audit.rb @@ -1,16 +1,18 @@ require "hbc/checkable" require "hbc/download" require "digest" +require "utils/git" module Hbc class Audit include Checkable - attr_reader :cask, :download + attr_reader :cask, :commit_range, :download - def initialize(cask, download: false, check_token_conflicts: false, command: SystemCommand) + def initialize(cask, download: false, check_token_conflicts: false, commit_range: nil, command: SystemCommand) @cask = cask @download = download + @commit_range = commit_range @check_token_conflicts = check_token_conflicts @command = command end @@ -21,6 +23,7 @@ module Hbc def run! check_required_stanzas + check_version_and_checksum check_version check_sha256 check_appcast @@ -57,6 +60,24 @@ module Hbc add_error "at least one activatable artifact stanza is required" if installable_artifacts.empty? end + def check_version_and_checksum + return if @cask.sourcefile_path.nil? + + tap = Tap.select { |t| t.cask_file?(@cask.sourcefile_path) }.first + return if tap.nil? + + return if commit_range.nil? + 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) + + 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)" + end + def check_version return unless cask.version check_no_string_version_latest diff --git a/Library/Homebrew/cask/lib/hbc/auditor.rb b/Library/Homebrew/cask/lib/hbc/auditor.rb index ec17f3cad..48f36a54d 100644 --- a/Library/Homebrew/cask/lib/hbc/auditor.rb +++ b/Library/Homebrew/cask/lib/hbc/auditor.rb @@ -1,14 +1,15 @@ module Hbc class Auditor - def self.audit(cask, audit_download: false, check_token_conflicts: false) - new(cask, audit_download, check_token_conflicts).audit + def self.audit(cask, audit_download: false, check_token_conflicts: false, commit_range: nil) + new(cask, audit_download, check_token_conflicts, commit_range).audit end - attr_reader :cask + attr_reader :cask, :commit_range - def initialize(cask, audit_download, check_token_conflicts) + def initialize(cask, audit_download, check_token_conflicts, commit_range) @cask = cask @audit_download = audit_download + @commit_range = commit_range @check_token_conflicts = check_token_conflicts end @@ -50,7 +51,8 @@ module Hbc def audit_cask_instance(cask) download = audit_download? && Download.new(cask) audit = Audit.new(cask, download: download, - check_token_conflicts: check_token_conflicts?) + check_token_conflicts: check_token_conflicts?, + commit_range: commit_range) audit.run! puts audit.summary audit.success? diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index a193a394e..e1cdb5dea 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -66,7 +66,7 @@ module Hbc return [] if current == version # collect all installed versions that are different than tap version and return them - installed.select { |v| v != version } + installed.reject { |v| v == version } end def to_s @@ -74,8 +74,6 @@ module Hbc end def dumpcask - return unless CLI.debug? - odebug "Cask instance dumps in YAML:" odebug "Cask instance toplevel:", to_yaml [ diff --git a/Library/Homebrew/cask/lib/hbc/cask_loader.rb b/Library/Homebrew/cask/lib/hbc/cask_loader.rb index c392e6b72..a0c44d9b4 100644 --- a/Library/Homebrew/cask/lib/hbc/cask_loader.rb +++ b/Library/Homebrew/cask/lib/hbc/cask_loader.rb @@ -54,7 +54,7 @@ module Hbc class FromURILoader < FromPathLoader def self.can_load?(ref) - !(ref.to_s !~ ::URI.regexp) + ref.to_s.match?(::URI.regexp) end def initialize(url) @@ -80,7 +80,7 @@ module Hbc class FromTapLoader < FromPathLoader def self.can_load?(ref) - !(ref.to_s !~ HOMEBREW_TAP_CASK_REGEX) + ref.to_s.match?(HOMEBREW_TAP_CASK_REGEX) end def initialize(tapped_name) diff --git a/Library/Homebrew/cask/lib/hbc/cli.rb b/Library/Homebrew/cask/lib/hbc/cli.rb index aa795fc59..99980b88d 100644 --- a/Library/Homebrew/cask/lib/hbc/cli.rb +++ b/Library/Homebrew/cask/lib/hbc/cli.rb @@ -2,8 +2,9 @@ require "optparse" require "shellwords" require "extend/optparse" +require "hbc/cli/options" -require "hbc/cli/base" +require "hbc/cli/abstract_command" require "hbc/cli/audit" require "hbc/cli/cat" require "hbc/cli/cleanup" @@ -23,7 +24,7 @@ require "hbc/cli/uninstall" require "hbc/cli/--version" require "hbc/cli/zap" -require "hbc/cli/internal_use_base" +require "hbc/cli/abstract_internal_command" require "hbc/cli/internal_audit_modified_casks" require "hbc/cli/internal_appcast_checkpoint" require "hbc/cli/internal_checkurl" @@ -44,54 +45,36 @@ module Hbc "remove" => "uninstall", "abv" => "info", "dr" => "doctor", - # aliases from Homebrew that we don't (yet) support - # 'ln' => 'link', - # 'configure' => 'diy', - # '--repo' => '--repository', - # 'environment' => '--env', - # '-c1' => '--config', }.freeze - OPTIONS = { - "--caskroom=" => :caskroom=, - "--appdir=" => :appdir=, - "--colorpickerdir=" => :colorpickerdir=, - "--prefpanedir=" => :prefpanedir=, - "--qlplugindir=" => :qlplugindir=, - "--dictionarydir=" => :dictionarydir=, - "--fontdir=" => :fontdir=, - "--servicedir=" => :servicedir=, - "--input_methoddir=" => :input_methoddir=, - "--internet_plugindir=" => :internet_plugindir=, - "--audio_unit_plugindir=" => :audio_unit_plugindir=, - "--vst_plugindir=" => :vst_plugindir=, - "--vst3_plugindir=" => :vst3_plugindir=, - "--screen_saverdir=" => :screen_saverdir=, - }.freeze + include Options - FLAGS = { - ["--[no-]binaries", :binaries] => true, - ["--debug", :debug] => false, - ["--verbose", :verbose] => false, - ["--outdated", :outdated] => false, - ["--help", :help] => false, - }.freeze + option "--appdir=PATH", ->(value) { Hbc.appdir = value } + option "--colorpickerdir=PATH", ->(value) { Hbc.colorpickerdir = value } + option "--prefpanedir=PATH", ->(value) { Hbc.prefpanedir = value } + option "--qlplugindir=PATH", ->(value) { Hbc.qlplugindir = value } + option "--dictionarydir=PATH", ->(value) { Hbc.dictionarydir = value } + option "--fontdir=PATH", ->(value) { Hbc.fontdir = value } + option "--servicedir=PATH", ->(value) { Hbc.servicedir = value } + option "--input_methoddir=PATH", ->(value) { Hbc.input_methoddir = value } + option "--internet_plugindir=PATH", ->(value) { Hbc.internet_plugindir = value } + option "--audio_unit_plugindir=PATH", ->(value) { Hbc.audio_unit_plugindir = value } + option "--vst_plugindir=PATH", ->(value) { Hbc.vst_plugindir = value } + option "--vst3_plugindir=PATH", ->(value) { Hbc.vst3_plugindir = value } + option "--screen_saverdir=PATH", ->(value) { Hbc.screen_saverdir = value } - FLAGS.each do |(_, method), default_value| - instance_variable_set(:"@#{method}", default_value) + option "--help", :help, false - define_singleton_method(:"#{method}=") do |arg| - instance_variable_set(:"@#{method}", arg) - end + # handled in OS::Mac + option "--language a,b,c", ->(*) { raise OptionParser::InvalidOption } - define_singleton_method(:"#{method}?") do - instance_variable_get(:"@#{method}") - end - end + # override default handling of --version + option "--version", ->(*) { raise OptionParser::InvalidOption } def self.command_classes @command_classes ||= constants.map(&method(:const_get)) - .select { |sym| sym.respond_to?(:run) } + .select { |klass| klass.respond_to?(:run) } + .reject(&:abstract?) .sort_by(&:command_name) end @@ -99,21 +82,21 @@ module Hbc @commands ||= command_classes.map(&:command_name) end - def self.lookup_command(command_string) + def self.lookup_command(command_name) @lookup ||= Hash[commands.zip(command_classes)] - command_string = ALIASES.fetch(command_string, command_string) - @lookup.fetch(command_string, command_string) + command_name = ALIASES.fetch(command_name, command_name) + @lookup.fetch(command_name, command_name) end def self.should_init?(command) - (command.is_a? Class) && (command < CLI::Base) && command.needs_init? + command.is_a?(Class) && !command.abstract? && command.needs_init? end def self.run_command(command, *rest) if command.respond_to?(:run) # usual case: built-in command verb command.run(*rest) - elsif require? which("brewcask-#{command}.rb").to_s + elsif require?(which("brewcask-#{command}.rb")) # external command as Ruby library on PATH, Homebrew-style elsif command.to_s.include?("/") && require?(command.to_s) # external command as Ruby library with literal path, useful @@ -145,20 +128,26 @@ module Hbc end end - def self.process(arguments) - unless ENV["MACOS_VERSION"].nil? - MacOS.full_version = ENV["MACOS_VERSION"] - end + def self.run(*args) + new(*args).run + end + + def initialize(*args) + @args = process_options(*args) + end + + def run + command_name, *args = *@args + command = help? ? "help" : self.class.lookup_command(command_name) + + MacOS.full_version = ENV["MACOS_VERSION"] unless ENV["MACOS_VERSION"].nil? - command_string, *rest = *arguments - rest = process_options(rest) - command = help? ? "help" : lookup_command(command_string) Hbc.default_tap.install unless Hbc.default_tap.installed? - Hbc.init if should_init?(command) - run_command(command, *rest) - rescue CaskError, CaskSha256MismatchError, ArgumentError => e + Hbc.init if self.class.should_init?(command) + self.class.run_command(command, *args) + rescue CaskError, CaskSha256MismatchError, ArgumentError, OptionParser::InvalidOption => e msg = e.message - msg << e.backtrace.join("\n") if debug? + msg << e.backtrace.join("\n") if ARGV.debug? onoe msg exit 1 rescue StandardError, ScriptError, NoMemoryError => e @@ -188,60 +177,25 @@ module Hbc list.sort end - def self.parser - # If you modify these arguments, please update USAGE.md - @parser ||= OptionParser.new do |opts| - opts.on("--language STRING") do - # handled in OS::Mac - end - - OPTIONS.each do |option, method| - opts.on("#{option}" "PATH", Pathname) do |path| - Hbc.public_send(method, path) - end - end + def process_options(*args) + all_args = Shellwords.shellsplit(ENV["HOMEBREW_CASK_OPTS"] || "") + args - opts.on("--binarydir=PATH") do - opoo <<-EOS.undent - Option --binarydir is obsolete! - Homebrew-Cask now uses the same location as your Homebrew installation for executable links. - EOS - end + non_options = [] - FLAGS.keys.each do |flag, method| - opts.on(flag) do |bool| - send(:"#{method}=", bool) - end - end - - opts.on("--version") do - raise OptionParser::InvalidOption # override default handling of --version - end + if idx = all_args.index("--") + non_options += all_args.drop(idx) + all_args = all_args.first(idx) end - end - def self.process_options(args) - all_args = Shellwords.shellsplit(ENV["HOMEBREW_CASK_OPTS"] || "") + args - remaining = [] - until all_args.empty? + remaining = all_args.select do |arg| begin - head = all_args.shift - remaining.concat(parser.parse([head])) - rescue OptionParser::InvalidOption - remaining << head - retry - rescue OptionParser::MissingArgument - raise ArgumentError, "The option '#{head}' requires an argument." - rescue OptionParser::AmbiguousOption - raise ArgumentError, "There is more than one possible option that starts with '#{head}'." + !process_arguments([arg]).empty? + rescue OptionParser::InvalidOption, OptionParser::MissingArgument, OptionParser::AmbiguousOption + true end end - # for compat with Homebrew, not certain if this is desirable - self.verbose = true if ARGV.verbose? - self.debug = true if ARGV.debug? - - remaining + remaining + non_options end class NullCommand diff --git a/Library/Homebrew/cask/lib/hbc/cli/--version.rb b/Library/Homebrew/cask/lib/hbc/cli/--version.rb index bbc719c3b..1833c51c6 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/--version.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/--version.rb @@ -1,12 +1,17 @@ module Hbc class CLI - class Version < Base + class Version < AbstractCommand def self.command_name "--#{super}" end - def self.run(*args) - raise ArgumentError, "#{command_name} does not take arguments." unless args.empty? + def initialize(*) + super + return if args.empty? + raise ArgumentError, "#{self.class.command_name} does not take arguments." + end + + def run puts Hbc.full_version end diff --git a/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb b/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb new file mode 100644 index 000000000..cdb7f5ec8 --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/cli/abstract_command.rb @@ -0,0 +1,45 @@ +require_relative "options" + +module Hbc + class CLI + class AbstractCommand + include Options + + option "--[no-]binaries", :binaries, true + option "--debug", :debug, false + option "--verbose", :verbose, false + option "--outdated", :outdated_only, false + + def self.command_name + @command_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase + end + + def self.abstract? + name.split("::").last.match?(/^Abstract[^a-z]/) + end + + def self.visible + true + end + + def self.help + nil + end + + def self.needs_init? + false + end + + def self.run(*args) + new(*args).run + end + + attr_accessor :args + private :args= + + def initialize(*args) + @args = process_arguments(*args) + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_use_base.rb b/Library/Homebrew/cask/lib/hbc/cli/abstract_internal_command.rb index b1f9c5631..8af71e589 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_use_base.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/abstract_internal_command.rb @@ -1,6 +1,6 @@ module Hbc class CLI - class InternalUseBase < Base + class AbstractInternalCommand < AbstractCommand def self.command_name super.sub(/^internal_/i, "_") end diff --git a/Library/Homebrew/cask/lib/hbc/cli/audit.rb b/Library/Homebrew/cask/lib/hbc/cli/audit.rb index ec1c33754..74d1ebfa7 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/audit.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/audit.rb @@ -1,51 +1,27 @@ module Hbc class CLI - class Audit < Base + class Audit < AbstractCommand + option "--download", :download, false + option "--token-conflicts", :token_conflicts, false + def self.help "verifies installability of Casks" end - def self.run(*args) - failed_casks = new(args, Auditor).run - return if failed_casks.empty? - raise CaskError, "audit failed for casks: #{failed_casks.join(" ")}" - end - - def initialize(args, auditor) - @args = args - @auditor = auditor - end - def run - casks_to_audit.each_with_object([]) do |cask, failed| - failed << cask unless audit(cask) + casks_to_audit = args.empty? ? Hbc.all : args.map(&CaskLoader.public_method(:load)) + + failed_casks = casks_to_audit.reject do |cask| + audit(cask) end + + return if failed_casks.empty? + raise CaskError, "audit failed for casks: #{failed_casks.join(" ")}" end def audit(cask) odebug "Auditing Cask #{cask}" - @auditor.audit(cask, audit_download: audit_download?, - check_token_conflicts: check_token_conflicts?) - end - - def audit_download? - @args.include?("--download") - end - - def check_token_conflicts? - @args.include?("--token-conflicts") - end - - def casks_to_audit - if cask_tokens.empty? - Hbc.all - else - cask_tokens.map { |token| CaskLoader.load(token) } - end - end - - def cask_tokens - @cask_tokens ||= self.class.cask_tokens_from(@args) + Auditor.audit(cask, audit_download: download?, check_token_conflicts: token_conflicts?) end def self.needs_init? diff --git a/Library/Homebrew/cask/lib/hbc/cli/base.rb b/Library/Homebrew/cask/lib/hbc/cli/base.rb deleted file mode 100644 index 3301cad91..000000000 --- a/Library/Homebrew/cask/lib/hbc/cli/base.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Hbc - class CLI - class Base - def self.command_name - @command_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase - end - - def self.visible - true - end - - def self.cask_tokens_from(args) - args.reject { |a| a.empty? || a.chars.first == "-" } - end - - def self.help - nil - end - - def self.needs_init? - false - 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 52f6e0eab..e68481b46 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/cat.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/cat.rb @@ -1,14 +1,17 @@ module Hbc class CLI - class Cat < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - # only respects the first argument - cask_token = cask_tokens.first.sub(/\.rb$/i, "") - cask_path = CaskLoader.path(cask_token) - raise CaskUnavailableError, cask_token.to_s unless cask_path.exist? - puts File.open(cask_path, &:read) + class Cat < AbstractCommand + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + args.each do |cask_token| + cask_path = CaskLoader.path(cask_token) + raise CaskUnavailableError, cask_token.to_s unless cask_path.exist? + puts File.open(cask_path, &:read) + end end def self.help diff --git a/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb b/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb index 9ebccabd0..40b37dd5d 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/cleanup.rb @@ -1,6 +1,6 @@ module Hbc class CLI - class Cleanup < Base + class Cleanup < AbstractCommand OUTDATED_DAYS = 10 OUTDATED_TIMESTAMP = Time.now - (60 * 60 * 24 * OUTDATED_DAYS) @@ -12,30 +12,15 @@ module Hbc true end - def self.run(*args) - if args.empty? - default.cleanup! - else - default.cleanup(args) - end - end - - def self.default - @default ||= new(Hbc.cache, CLI.outdated?) - end + attr_reader :cache_location - attr_reader :cache_location, :outdated_only - def initialize(cache_location, outdated_only) + def initialize(*args, cache_location: Hbc.cache) + super(*args) @cache_location = Pathname.new(cache_location) - @outdated_only = outdated_only - end - - def cleanup! - remove_cache_files end - def cleanup(tokens) - remove_cache_files(*tokens) + def run + remove_cache_files(*@args) end def cache_files @@ -46,7 +31,7 @@ module Hbc end def outdated?(file) - outdated_only && file && file.stat.mtime > OUTDATED_TIMESTAMP + outdated_only? && file && file.stat.mtime > OUTDATED_TIMESTAMP end def incomplete?(file) @@ -68,7 +53,7 @@ module Hbc def remove_cache_files(*tokens) message = "Removing cached downloads" message.concat " for #{tokens.join(", ")}" unless tokens.empty? - message.concat " older than #{OUTDATED_DAYS} days old" if outdated_only + message.concat " older than #{OUTDATED_DAYS} days old" if outdated_only? ohai message deletable_cache_files = if tokens.empty? @@ -90,14 +75,18 @@ module Hbc paths.each do |item| next unless item.exist? processed_files += 1 - if Utils.file_locked?(item) + + begin + LockFile.new(item.basename).with_lock do + puts item + item_size = File.size?(item) + cleanup_size += item_size unless item_size.nil? + item.unlink + end + rescue OperationInProgressError puts "skipping: #{item} is locked" next end - puts item - item_size = File.size?(item) - cleanup_size += item_size unless item_size.nil? - item.unlink end if processed_files.zero? diff --git a/Library/Homebrew/cask/lib/hbc/cli/create.rb b/Library/Homebrew/cask/lib/hbc/cli/create.rb index 5e143d085..8de101092 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/create.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/create.rb @@ -1,17 +1,20 @@ module Hbc class CLI - class Create < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - cask_token = cask_tokens.first.sub(/\.rb$/i, "") - cask_path = CaskLoader.path(cask_token) - odebug "Creating Cask #{cask_token}" + class Create < AbstractCommand + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + raise ArgumentError, "Only one Cask can be created at a time." if args.count > 1 + end + def run + cask_token = args.first + cask_path = CaskLoader.path(cask_token) raise CaskAlreadyCreatedError, cask_token if cask_path.exist? + odebug "Creating Cask #{cask_token}" File.open(cask_path, "w") do |f| - f.write template(cask_token) + f.write self.class.template(cask_token) end exec_editor cask_path diff --git a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb index 031f78824..cd6ebbc12 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/doctor.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/doctor.rb @@ -1,30 +1,36 @@ module Hbc class CLI - class Doctor < Base - def self.run + class Doctor < AbstractCommand + def initialize(*) + super + return if args.empty? + raise ArgumentError, "#{self.class.command_name} does not take arguments." + end + + def run ohai "Homebrew-Cask Version", Hbc.full_version - ohai "Homebrew-Cask Install Location", render_install_location - ohai "Homebrew-Cask Staging Location", render_staging_location(Hbc.caskroom) - ohai "Homebrew-Cask Cached Downloads", render_cached_downloads + ohai "Homebrew-Cask Install Location", self.class.render_install_location + ohai "Homebrew-Cask Staging Location", self.class.render_staging_location(Hbc.caskroom) + ohai "Homebrew-Cask Cached Downloads", self.class.render_cached_downloads ohai "Homebrew-Cask Taps:" - puts render_taps(Hbc.default_tap, *alt_taps) - ohai "Contents of $LOAD_PATH", render_load_path($LOAD_PATH) + puts self.class.render_taps(Hbc.default_tap, *self.class.alt_taps) + ohai "Contents of $LOAD_PATH", self.class.render_load_path($LOAD_PATH) ohai "Environment Variables" - environment_variables = [ - "RUBYLIB", - "RUBYOPT", - "RUBYPATH", - "RBENV_VERSION", - "CHRUBY_VERSION", - "GEM_HOME", - "GEM_PATH", - "BUNDLE_PATH", - "PATH", - "SHELL", + environment_variables = %w[ + RUBYLIB + RUBYOPT + RUBYPATH + RBENV_VERSION + CHRUBY_VERSION + GEM_HOME + GEM_PATH + BUNDLE_PATH + PATH + SHELL ] - (locale_variables + environment_variables).sort.each(&method(:render_env_var)) + (self.class.locale_variables + environment_variables).sort.each(&self.class.method(:render_env_var)) end def self.locale_variables @@ -45,12 +51,11 @@ module Hbc end def self.alt_taps - Tap.select { |t| t.cask_dir && t != Hbc.default_tap } + Tap.select { |t| t.cask_dir.exist? && t != Hbc.default_tap } end def self.cask_count_for_tap(tap) - count = tap.cask_files.count - "#{count} #{count == 1 ? "cask" : "casks"}" + Formatter.pluralize(tap.cask_files.count, "cask") rescue StandardError "0 #{error_string "error reading #{tap.path}"}" end @@ -107,7 +112,7 @@ module Hbc end def self.render_cached_downloads - cleanup = CLI::Cleanup.default + cleanup = CLI::Cleanup.new count = cleanup.cache_files.count size = cleanup.disk_cleanup_size msg = user_tilde(Hbc.cache.to_s) diff --git a/Library/Homebrew/cask/lib/hbc/cli/edit.rb b/Library/Homebrew/cask/lib/hbc/cli/edit.rb index 1f1e0d918..dec0fe36b 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/edit.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/edit.rb @@ -1,16 +1,21 @@ module Hbc class CLI - class Edit < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - # only respects the first argument - cask_token = cask_tokens.first.sub(/\.rb$/i, "") + class Edit < AbstractCommand + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + raise ArgumentError, "Only one Cask can be created at a time." if args.count > 1 + end + + def run + cask_token = args.first cask_path = CaskLoader.path(cask_token) - odebug "Opening editor for Cask #{cask_token}" + unless cask_path.exist? raise CaskUnavailableError, %Q(#{cask_token}, run "brew cask create #{cask_token}" to create a new Cask) end + + odebug "Opening editor for Cask #{cask_token}" exec_editor cask_path end diff --git a/Library/Homebrew/cask/lib/hbc/cli/fetch.rb b/Library/Homebrew/cask/lib/hbc/cli/fetch.rb index 83dba672c..2c1cc5f66 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/fetch.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/fetch.rb @@ -1,15 +1,18 @@ module Hbc class CLI - class Fetch < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - force = args.include? "--force" + class Fetch < AbstractCommand + option "--force", :force, false - cask_tokens.each do |cask_token| + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + args.each do |cask_token| ohai "Downloading external files for Cask #{cask_token}" cask = CaskLoader.load(cask_token) - downloaded_path = Download.new(cask, force: force).perform + downloaded_path = Download.new(cask, force: force?).perform Verify.all(cask, downloaded_path) ohai "Success! Downloaded to -> #{downloaded_path}" end diff --git a/Library/Homebrew/cask/lib/hbc/cli/home.rb b/Library/Homebrew/cask/lib/hbc/cli/home.rb index 66be49186..009bc1e3e 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/home.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/home.rb @@ -1,19 +1,24 @@ module Hbc class CLI - class Home < Base - def self.run(*cask_tokens) - if cask_tokens.empty? + class Home < AbstractCommand + def run + casks = args.map(&CaskLoader.public_method(:load)) + + if casks.empty? odebug "Opening project homepage" - system "/usr/bin/open", "--", "https://caskroom.github.io/" + self.class.open_url "https://caskroom.github.io/" else - cask_tokens.each do |cask_token| - odebug "Opening homepage for Cask #{cask_token}" - cask = CaskLoader.load(cask_token) - system "/usr/bin/open", "--", cask.homepage + casks.each do |cask| + odebug "Opening homepage for Cask #{cask}" + self.class.open_url cask.homepage end end end + def self.open_url(url) + SystemCommand.run!(OS::PATH_OPEN, args: ["--", url]) + end + def self.help "opens the homepage of the given Cask" end diff --git a/Library/Homebrew/cask/lib/hbc/cli/info.rb b/Library/Homebrew/cask/lib/hbc/cli/info.rb index 625b4ecae..623c4b737 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/info.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/info.rb @@ -1,14 +1,17 @@ module Hbc class CLI - class Info < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - cask_tokens.each do |cask_token| + class Info < AbstractCommand + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + args.each do |cask_token| odebug "Getting info for Cask #{cask_token}" cask = CaskLoader.load(cask_token) - info(cask) + self.class.info(cask) end end @@ -20,7 +23,7 @@ module Hbc puts "#{cask.token}: #{cask.version}" puts Formatter.url(cask.homepage) if cask.homepage installation_info(cask) - puts "From: #{Formatter.url(repo_info(cask))}" + repo_info(cask) name_info(cask) artifact_info(cask) Installer.print_caveats(cask) @@ -38,7 +41,7 @@ module Hbc puts versioned_staged_path.to_s .concat(" (") .concat(versioned_staged_path.exist? ? versioned_staged_path.abv : Formatter.error("does not exist")) - .concat(")") + .concat(")") end else puts "Not installed" @@ -46,19 +49,24 @@ module Hbc end def self.name_info(cask) - ohai cask.name.size > 1 ? "Names" : "Name" + ohai((cask.name.size > 1) ? "Names" : "Name") puts cask.name.empty? ? Formatter.error("None") : cask.name end def self.repo_info(cask) user, repo, token = QualifiedToken.parse(Hbc.all_tokens.detect { |t| t.split("/").last == cask.token }) + + return if user.nil? || repo.nil? + remote_tap = Tap.fetch(user, repo) - if remote_tap.custom_remote? && !remote_tap.remote.nil? - return remote_tap.remote.to_s + url = if remote_tap.custom_remote? && !remote_tap.remote.nil? + remote_tap.remote + else + "#{remote_tap.default_remote}/blob/master/Casks/#{token}.rb" end - "#{remote_tap.default_remote}/blob/master/Casks/#{token}.rb" + puts "From: #{Formatter.url(url)}" end def self.artifact_info(cask) @@ -66,7 +74,7 @@ module Hbc DSL::ORDINARY_ARTIFACT_TYPES.each do |type| next if cask.artifacts[type].empty? cask.artifacts[type].each do |artifact| - activatable_item = type == :stage_only ? "<none>" : artifact.first + activatable_item = (type == :stage_only) ? "<none>" : artifact.first puts "#{activatable_item} (#{type})" end end diff --git a/Library/Homebrew/cask/lib/hbc/cli/install.rb b/Library/Homebrew/cask/lib/hbc/cli/install.rb index 438f860c1..72f85fc69 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/install.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/install.rb @@ -1,28 +1,33 @@ module Hbc class CLI - class Install < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - force = args.include? "--force" - skip_cask_deps = args.include? "--skip-cask-deps" - require_sha = args.include? "--require-sha" - retval = install_casks cask_tokens, force, skip_cask_deps, require_sha + class Install < AbstractCommand + option "--force", :force, false + option "--skip-cask-deps", :skip_cask_deps, false + option "--require-sha", :require_sha, false + + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + retval = install_casks # retval is ternary: true/false/nil raise CaskError, "nothing to install" if retval.nil? raise CaskError, "install incomplete" unless retval end - def self.install_casks(cask_tokens, force, skip_cask_deps, require_sha) + def install_casks count = 0 - cask_tokens.each do |cask_token| + args.each do |cask_token| begin cask = CaskLoader.load(cask_token) - Installer.new(cask, - force: force, - skip_cask_deps: skip_cask_deps, - require_sha: require_sha).install + Installer.new(cask, binaries: binaries?, + verbose: verbose?, + force: force?, + skip_cask_deps: skip_cask_deps?, + require_sha: require_sha?).install count += 1 rescue CaskAlreadyInstalledError => e opoo e.message @@ -31,7 +36,7 @@ module Hbc opoo e.message count += 1 rescue CaskUnavailableError => e - warn_unavailable_with_suggestion cask_token, e + self.class.warn_unavailable_with_suggestion cask_token, e rescue CaskNoShasumError => e opoo e.message count += 1 @@ -39,7 +44,8 @@ module Hbc onoe e.message end end - count.zero? ? nil : count == cask_tokens.length + + count.zero? ? nil : count == args.length end def self.warn_unavailable_with_suggestion(cask_token, e) 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 da3567108..cd2679782 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_appcast_checkpoint.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_appcast_checkpoint.rb @@ -1,15 +1,18 @@ module Hbc class CLI - class InternalAppcastCheckpoint < InternalUseBase - def self.run(*args) - calculate = args.include? "--calculate" - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - - if cask_tokens.all? { |t| t =~ %r{^https?://} && t !~ /\.rb$/ } - appcask_checkpoint_for_url(cask_tokens) + class InternalAppcastCheckpoint < AbstractInternalCommand + option "--calculate", :calculate, false + + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + if args.all? { |t| t =~ %r{^https?://} && t !~ /\.rb$/ } + self.class.appcask_checkpoint_for_url(args) else - appcask_checkpoint(cask_tokens, calculate) + self.class.appcask_checkpoint(args, calculate?) end end @@ -40,7 +43,7 @@ module Hbc if checkpoint.nil? onoe "Could not retrieve `appcast` checkpoint for cask '#{cask}': #{result[:command_result].stderr}" else - puts cask_tokens.count > 1 ? "#{checkpoint} #{cask}": checkpoint + puts((cask_tokens.count > 1) ? "#{checkpoint} #{cask}" : checkpoint) count += 1 end end 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 9467cccc7..f1a0308e5 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 @@ -1,26 +1,29 @@ module Hbc class CLI - class InternalAuditModifiedCasks < InternalUseBase + class InternalAuditModifiedCasks < AbstractInternalCommand RELEVANT_STANZAS = [:version, :sha256, :url, :appcast].freeze + option "--cleanup", :cleanup, false + def self.needs_init? true end - def self.run(*args) - commit_range = commit_range(args) - cleanup = args.any? { |a| a =~ /^-+c(leanup)?$/i } - new(commit_range, cleanup: cleanup).run - end + attr_accessor :commit_range + private :commit_range= - def self.commit_range(args) - posargs = args.reject { |a| a.empty? || a.chars.first == "-" } - odie usage unless posargs.size == 1 - posargs.first - end + def initialize(*) + super + + if args.count != 1 + raise ArgumentError, <<-EOS.undent + This command requires exactly one argument. + + #{self.class.usage} + EOS + end - def self.posargs(args) - args.reject { |a| a.empty? || a.chars.first == "-" } + @commit_range = args.first end def self.help @@ -41,17 +44,6 @@ module Hbc EOS end - def initialize(commit_range, cleanup: false) - @commit_range = commit_range - @cleanup = cleanup - end - - attr_reader :commit_range - - def cleanup? - @cleanup - end - def run at_exit do cleanup @@ -97,7 +89,8 @@ module Hbc audit_download = audit_download?(cask, cask_file) check_token_conflicts = added_cask_files.include?(cask_file) success = Auditor.audit(cask, audit_download: audit_download, - check_token_conflicts: check_token_conflicts) + check_token_conflicts: check_token_conflicts, + commit_range: commit_range) failed_casks << cask unless success end diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb index b7d95957d..a8c3d5c8f 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_checkurl.rb @@ -1,8 +1,8 @@ module Hbc class CLI - class InternalCheckurl < InternalUseBase - def self.run(*args) - casks_to_check = args.empty? ? Hbc.all : args.map { |arg| CaskLoader.load(arg) } + class InternalCheckurl < AbstractInternalCommand + def run + casks_to_check = args.empty? ? Hbc.all : args.map(&CaskLoader.public_method(:load)) casks_to_check.each do |cask| odebug "Checking URL for Cask #{cask}" checker = UrlChecker.new(cask) diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb index 8017a32cf..78dbf1622 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_dump.rb @@ -1,20 +1,22 @@ module Hbc class CLI - class InternalDump < InternalUseBase - def self.run(*arguments) - cask_tokens = cask_tokens_from(arguments) - raise CaskUnspecifiedError if cask_tokens.empty? - retval = dump_casks(*cask_tokens) + class InternalDump < AbstractInternalCommand + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + retval = dump_casks # retval is ternary: true/false/nil raise CaskError, "nothing to dump" if retval.nil? raise CaskError, "dump incomplete" unless retval end - def self.dump_casks(*cask_tokens) - CLI.debug = true # Yuck. At the moment this is the only way to make dumps visible + def dump_casks count = 0 - cask_tokens.each do |cask_token| + args.each do |cask_token| begin cask = CaskLoader.load(cask_token) count += 1 @@ -23,7 +25,7 @@ module Hbc opoo "#{cask_token} was not found or would not load: #{e}" end end - count.zero? ? nil : count == cask_tokens.length + count.zero? ? nil : count == args.length end def self.help diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb index 0908ee05e..beac77b29 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_help.rb @@ -1,12 +1,18 @@ module Hbc class CLI - class InternalHelp < InternalUseBase - def self.run(*_ignored) + class InternalHelp < AbstractInternalCommand + def initialize(*) + super + return if args.empty? + raise ArgumentError, "#{self.class.command_name} does not take arguments." + end + + def run max_command_len = CLI.commands.map(&:length).max puts "Unstable Internal-use Commands:\n\n" CLI.command_classes.each do |klass| next if klass.visible - puts " #{klass.command_name.ljust(max_command_len)} #{help_for(klass)}" + puts " #{klass.command_name.ljust(max_command_len)} #{self.class.help_for(klass)}" end puts "\n" end diff --git a/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb b/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb index 303aa7ffe..86dee7c9c 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/internal_stanza.rb @@ -1,6 +1,6 @@ module Hbc class CLI - class InternalStanza < InternalUseBase + class InternalStanza < AbstractInternalCommand # Syntax # # brew cask _stanza <stanza_name> [ --table | --yaml | --inspect | --quiet ] [ <cask_token> ... ] @@ -50,71 +50,82 @@ module Hbc :uninstall_postflight, ] - def self.run(*args) + option "--table", :table, false + option "--quiet", :quiet, false + option "--yaml", :yaml, false + option "--inspect", :inspect, false + + attr_accessor :format + private :format, :format= + + attr_accessor :stanza + private :stanza, :stanza= + + def initialize(*) + super raise ArgumentError, "No stanza given." if args.empty? - table = args.include? "--table" - quiet = args.include? "--quiet" - format = :to_yaml if args.include? "--yaml" - format = :inspect if args.include? "--inspect" - cask_tokens = cask_tokens_from(args) - stanza = cask_tokens.shift.to_sym - cask_tokens = Hbc.all_tokens if cask_tokens.empty? + @stanza = args.shift.to_sym - retval = print_stanzas(stanza, format, table, quiet, *cask_tokens) + @format = :to_yaml if yaml? + @format = :inspect if inspect? + end + def run + retval = print_stanzas # retval is ternary: true/false/nil if retval.nil? - exit 1 if quiet + exit 1 if quiet? raise CaskError, "nothing to print" elsif !retval - exit 1 if quiet + exit 1 if quiet? raise CaskError, "print incomplete" end end - def self.print_stanzas(stanza, format = nil, table = nil, quiet = nil, *cask_tokens) + def print_stanzas count = 0 if ARTIFACTS.include?(stanza) artifact_name = stanza - stanza = :artifacts + @stanza = :artifacts end + cask_tokens = args.empty? ? Hbc.all_tokens : args cask_tokens.each do |cask_token| - print "#{cask_token}\t" if table + print "#{cask_token}\t" if table? begin cask = CaskLoader.load(cask_token) rescue StandardError - opoo "Cask '#{cask_token}' was not found" unless quiet + opoo "Cask '#{cask_token}' was not found" unless quiet? puts "" next end unless cask.respond_to?(stanza) - opoo "no such stanza '#{stanza}' on Cask '#{cask_token}'" unless quiet + opoo "no such stanza '#{stanza}' on Cask '#{cask_token}'" unless quiet? puts "" next end begin - value = cask.send(stanza) + value = cask.send(@stanza) rescue StandardError - opoo "failure calling '#{stanza}' on Cask '#{cask_token}'" unless quiet + opoo "failure calling '#{stanza}' on Cask '#{cask_token}'" unless quiet? puts "" next end if artifact_name && !value.key?(artifact_name) - opoo "no such stanza '#{artifact_name}' on Cask '#{cask_token}'" unless quiet + opoo "no such stanza '#{artifact_name}' on Cask '#{cask_token}'" unless quiet? puts "" next end value = value.fetch(artifact_name).to_a.flatten if artifact_name - if format - puts value.send(format) + if @format + puts value.send(@format) elsif artifact_name || value.is_a?(Symbol) puts value.inspect else diff --git a/Library/Homebrew/cask/lib/hbc/cli/list.rb b/Library/Homebrew/cask/lib/hbc/cli/list.rb index d9bf2187b..9d2ded4be 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/list.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/list.rb @@ -1,19 +1,18 @@ module Hbc class CLI - class List < Base - def self.run(*arguments) - @options = {} - @options[:one] = true if arguments.delete("-1") - @options[:versions] = true if arguments.delete("--versions") + class List < AbstractCommand + option "-1", :one, false + option "--versions", :versions, false - if arguments.delete("-l") - @options[:one] = true - opoo "Option -l is obsolete! Implying option -1." - end + option "-l", (lambda do |*| + one = true # rubocop:disable Lint/UselessAssignment + opoo "Option -l is obsolete! Implying option -1." + end) - retval = arguments.any? ? list(*arguments) : list_installed + def run + retval = args.any? ? list : list_installed # retval is ternary: true/false/nil - if retval.nil? && !arguments.any? + if retval.nil? && args.none? opoo "nothing to list" # special case: avoid exit code elsif retval.nil? raise CaskError, "nothing to list" @@ -22,22 +21,22 @@ module Hbc end end - def self.list(*cask_tokens) + def list count = 0 - cask_tokens.each do |cask_token| + args.each do |cask_token| odebug "Listing files for Cask #{cask_token}" begin cask = CaskLoader.load(cask_token) if cask.installed? - if @options[:one] + if one? puts cask.token - elsif @options[:versions] - puts format_versioned(cask) + elsif versions? + puts self.class.format_versioned(cask) else cask = CaskLoader.load_from_file(cask.installed_caskfile) - list_artifacts(cask) + self.class.list_artifacts(cask) end count += 1 @@ -49,7 +48,7 @@ module Hbc end end - count.zero? ? nil : count == cask_tokens.length + count.zero? ? nil : count == args.length end def self.list_artifacts(cask) @@ -59,13 +58,13 @@ module Hbc end end - def self.list_installed + def list_installed installed_casks = Hbc.installed - if @options[:one] + if one? puts installed_casks.map(&:to_s) - elsif @options[:versions] - puts installed_casks.map(&method(:format_versioned)) + elsif versions? + puts installed_casks.map(&self.class.method(:format_versioned)) elsif !installed_casks.empty? puts Formatter.columns(installed_casks.map(&:to_s)) end diff --git a/Library/Homebrew/cask/lib/hbc/cli/options.rb b/Library/Homebrew/cask/lib/hbc/cli/options.rb new file mode 100644 index 000000000..75dd77212 --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/cli/options.rb @@ -0,0 +1,64 @@ +module Hbc + class CLI + module Options + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + def options + @options ||= {} + return @options unless superclass.respond_to?(:options) + superclass.options.merge(@options) + end + + def option(name, method, default_value = nil) + @options ||= {} + @options[name] = method + + return if method.respond_to?(:call) + + define_method(:"#{method}=") do |value| + instance_variable_set(:"@#{method}", value) + end + + if [true, false].include?(default_value) + define_method(:"#{method}?") do + return default_value unless instance_variable_defined?(:"@#{method}") + instance_variable_get(:"@#{method}") == true + end + else + define_method(:"#{method}") do + return default_value unless instance_variable_defined?(:"@#{method}") + instance_variable_get(:"@#{method}") + end + end + end + end + + def process_arguments(*arguments) + parser = OptionParser.new do |opts| + next if self.class.options.nil? + + self.class.options.each do |option_name, option_method| + option_type = case option_name.split(/(\ |\=)/).last + when "PATH" + Pathname + when /\w+(,\w+)+/ + Array + end + + opts.on(option_name, *option_type) do |value| + if option_method.respond_to?(:call) + option_method.call(value) + else + send(:"#{option_method}=", value) + end + end + end + end + parser.parse(*arguments) + end + end + end +end diff --git a/Library/Homebrew/cask/lib/hbc/cli/outdated.rb b/Library/Homebrew/cask/lib/hbc/cli/outdated.rb index 5956f59ac..7877ead05 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/outdated.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/outdated.rb @@ -1,20 +1,20 @@ module Hbc class CLI - class Outdated < Base - def self.run(*args) - greedy = args.include?("--greedy") - verbose = ($stdout.tty? || CLI.verbose?) && !args.include?("--quiet") + class Outdated < AbstractCommand + option "--greedy", :greedy, false + option "--quiet", :quiet, false - cask_tokens = cask_tokens_from(args) - casks_to_check = if cask_tokens.empty? - Hbc.installed - else - cask_tokens.map { |token| CaskLoader.load(token) } - end + def initialize(*) + super + self.verbose = ($stdout.tty? || verbose?) && !quiet? + end + + def run + casks_to_check = args.empty? ? Hbc.installed : args.map(&CaskLoader.public_method(:load)) casks_to_check.each do |cask| odebug "Checking update info of Cask #{cask}" - list_if_outdated(cask, greedy, verbose) + self.class.list_if_outdated(cask, greedy?, verbose?) end end diff --git a/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb b/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb index c2ed8f462..eb5f45c90 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/reinstall.rb @@ -1,26 +1,29 @@ module Hbc class CLI class Reinstall < Install - def self.install_casks(cask_tokens, force, skip_cask_deps, require_sha) + def install_casks count = 0 - cask_tokens.each do |cask_token| + args.each do |cask_token| begin cask = CaskLoader.load(cask_token) Installer.new(cask, - force: force, - skip_cask_deps: skip_cask_deps, - require_sha: require_sha).reinstall + binaries: binaries?, + verbose: verbose?, + force: force?, + skip_cask_deps: skip_cask_deps?, + require_sha: require_sha?).reinstall count += 1 rescue CaskUnavailableError => e - warn_unavailable_with_suggestion cask_token, e + self.class.warn_unavailable_with_suggestion cask_token, e rescue CaskNoShasumError => e opoo e.message count += 1 end end - count.zero? ? nil : count == cask_tokens.length + + count.zero? ? nil : count == args.length end def self.help diff --git a/Library/Homebrew/cask/lib/hbc/cli/search.rb b/Library/Homebrew/cask/lib/hbc/cli/search.rb index 7abd744e4..b24091aef 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/search.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/search.rb @@ -1,8 +1,13 @@ module Hbc class CLI - class Search < Base - def self.run(*arguments) - render_results(*search(*arguments)) + class Search < AbstractCommand + def initialize(*args) + @args = args + end + + def run + results = self.class.search(*args) + self.class.render_results(*results) end def self.extract_regexp(string) diff --git a/Library/Homebrew/cask/lib/hbc/cli/style.rb b/Library/Homebrew/cask/lib/hbc/cli/style.rb index 191aefd3c..97208232b 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/style.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/style.rb @@ -2,25 +2,19 @@ require "English" module Hbc class CLI - class Style < Base + class Style < AbstractCommand def self.help "checks Cask style using RuboCop" end - def self.run(*args) - retval = new(args).run - raise CaskError, "style check failed" unless retval - end - - attr_reader :args - def initialize(args) - @args = args - end + option "--fix", :fix, false def run install_rubocop - system "rubocop", *rubocop_args, "--", *cask_paths - $CHILD_STATUS.success? + cache_env = { "XDG_CACHE_HOME" => "#{HOMEBREW_CACHE}/style" } + system(cache_env, "rubocop", *rubocop_args, "--", *cask_paths) + raise CaskError, "style check failed" unless $CHILD_STATUS.success? + true end def install_rubocop @@ -34,19 +28,15 @@ module Hbc end def cask_paths - @cask_paths ||= if cask_tokens.empty? + @cask_paths ||= if args.empty? Hbc.all_tapped_cask_dirs - elsif cask_tokens.any? { |file| File.exist?(file) } - cask_tokens + elsif args.any? { |file| File.exist?(file) } + args else - cask_tokens.map { |token| CaskLoader.path(token) } + args.map { |token| CaskLoader.path(token) } end end - def cask_tokens - @cask_tokens ||= self.class.cask_tokens_from(args) - end - def rubocop_args fix? ? autocorrect_args : default_args end @@ -63,10 +53,6 @@ module Hbc def autocorrect_args default_args + ["--auto-correct"] end - - def fix? - args.any? { |arg| arg =~ /--(fix|(auto-?)?correct)/ } - end end end end diff --git a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb index 1ee3230ad..33ee5afa9 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/uninstall.rb @@ -1,23 +1,26 @@ module Hbc class CLI - class Uninstall < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - force = args.include? "--force" + class Uninstall < AbstractCommand + option "--force", :force, false - cask_tokens.each do |cask_token| + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + args.each do |cask_token| odebug "Uninstalling Cask #{cask_token}" cask = CaskLoader.load(cask_token) - raise CaskNotInstalledError, cask unless cask.installed? || force + raise CaskNotInstalledError, cask unless cask.installed? || force? 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? end - Installer.new(cask, force: force).uninstall + Installer.new(cask, binaries: binaries?, verbose: verbose?, force: force?).uninstall next if (versions = cask.versions).empty? diff --git a/Library/Homebrew/cask/lib/hbc/cli/zap.rb b/Library/Homebrew/cask/lib/hbc/cli/zap.rb index 83da1c932..3c07ff9e8 100644 --- a/Library/Homebrew/cask/lib/hbc/cli/zap.rb +++ b/Library/Homebrew/cask/lib/hbc/cli/zap.rb @@ -1,13 +1,16 @@ module Hbc class CLI - class Zap < Base - def self.run(*args) - cask_tokens = cask_tokens_from(args) - raise CaskUnspecifiedError if cask_tokens.empty? - cask_tokens.each do |cask_token| + class Zap < AbstractCommand + def initialize(*) + super + raise CaskUnspecifiedError if args.empty? + end + + def run + args.each do |cask_token| odebug "Zapping Cask #{cask_token}" cask = CaskLoader.load(cask_token) - Installer.new(cask).zap + Installer.new(cask, verbose: verbose?).zap end end diff --git a/Library/Homebrew/cask/lib/hbc/container.rb b/Library/Homebrew/cask/lib/hbc/container.rb index 961e31968..93e825e03 100644 --- a/Library/Homebrew/cask/lib/hbc/container.rb +++ b/Library/Homebrew/cask/lib/hbc/container.rb @@ -6,6 +6,7 @@ require "hbc/container/criteria" require "hbc/container/dmg" require "hbc/container/executable" require "hbc/container/generic_unar" +require "hbc/container/gpg" require "hbc/container/gzip" require "hbc/container/lzma" require "hbc/container/naked" @@ -40,6 +41,7 @@ module Hbc Gzip, # pure gzip Lzma, # pure lzma Xz, # pure xz + Gpg, # GnuPG signed data Executable, ] # for explicit use only (never autodetected): diff --git a/Library/Homebrew/cask/lib/hbc/container/cab.rb b/Library/Homebrew/cask/lib/hbc/container/cab.rb index b3cf01452..010fccbc4 100644 --- a/Library/Homebrew/cask/lib/hbc/container/cab.rb +++ b/Library/Homebrew/cask/lib/hbc/container/cab.rb @@ -6,11 +6,7 @@ module Hbc class Container class Cab < Base def self.me?(criteria) - cabextract = which("cabextract") - - criteria.magic_number(/^(MSCF|MZ)/n) && - !cabextract.nil? && - criteria.command.run(cabextract, args: ["-t", "--", criteria.path.to_s]).stderr.empty? + criteria.magic_number(/^(MSCF|MZ)/n) end def extract diff --git a/Library/Homebrew/cask/lib/hbc/container/dmg.rb b/Library/Homebrew/cask/lib/hbc/container/dmg.rb index 1b96df4ec..113c6fb11 100644 --- a/Library/Homebrew/cask/lib/hbc/container/dmg.rb +++ b/Library/Homebrew/cask/lib/hbc/container/dmg.rb @@ -86,7 +86,7 @@ module Hbc Dir.chdir(mount) do Dir.glob("**/*", File::FNM_DOTMATCH).map do |path| next if skip_path?(Pathname(path)) - path == "." ? path : path.prepend("./") + (path == ".") ? path : path.prepend("./") end.compact.join("\n").concat("\n") end end diff --git a/Library/Homebrew/cask/lib/hbc/container/gpg.rb b/Library/Homebrew/cask/lib/hbc/container/gpg.rb new file mode 100644 index 000000000..3f37b5aa6 --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/container/gpg.rb @@ -0,0 +1,41 @@ +require "tmpdir" + +require "hbc/container/base" + +module Hbc + class Container + class Gpg < Base + def self.me?(criteria) + criteria.extension(/^(gpg)$/) + end + + def import_key + if @cask.gpg.nil? + raise CaskError, "Expected to find gpg public key in formula. Cask '#{@cask}' must add: 'gpg :embedded, key_id: [Public Key ID]' or 'gpg :embedded, key_url: [Public Key URL]'" + end + + args = if @cask.gpg.key_id + ["--recv-keys", @cask.gpg.key_id] + elsif @cask.gpg.key_url + ["--fetch-key", @cask.gpg.key_url.to_s] + end + + @command.run!("gpg", args: args) + end + + def extract + if (gpg = which("gpg")).nil? + raise CaskError, "Expected to find gpg executable. Cask '#{@cask}' must add: depends_on formula: 'gpg'" + end + + import_key + + Dir.mktmpdir do |unpack_dir| + @command.run!(gpg, args: ["--batch", "--yes", "--output", Pathname(unpack_dir).join(@path.basename(".gpg")), "--decrypt", @path]) + + extract_nested_inside(unpack_dir) + end + 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 137af319a..5b32b4840 100644 --- a/Library/Homebrew/cask/lib/hbc/download_strategy.rb +++ b/Library/Homebrew/cask/lib/hbc/download_strategy.rb @@ -82,10 +82,16 @@ module Hbc end def clear_cache - [cached_location, temporary_path].each do |f| - next unless f.exist? - raise CurlDownloadStrategyError, "#{f} is in use by another process" if Utils.file_locked?(f) - f.unlink + [cached_location, temporary_path].each do |path| + next unless path.exist? + + begin + LockFile.new(path.basename).with_lock do + path.unlink + end + rescue OperationInProgressError + raise CurlDownloadStrategyError, "#{path} is in use by another process" + end end end @@ -105,10 +111,8 @@ module Hbc else had_incomplete_download = temporary_path.exist? begin - File.open(temporary_path, "a+") do |f| - f.flock(File::LOCK_EX) + LockFile.new(temporary_path.basename).with_lock do _fetch - f.flock(File::LOCK_UN) end rescue ErrorDuringExecution # 33 == range not supported @@ -208,11 +212,11 @@ module Hbc class SubversionDownloadStrategy < HbVCSDownloadStrategy def cache_tag # TODO: pass versions as symbols, support :head here - version == "head" ? "svn-HEAD" : "svn" + (version == "head") ? "svn-HEAD" : "svn" end def repo_valid? - @clone.join(".svn").directory? + (@clone/".svn").directory? end def repo_url diff --git a/Library/Homebrew/cask/lib/hbc/dsl.rb b/Library/Homebrew/cask/lib/hbc/dsl.rb index 4707ae76a..92245e8fb 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl.rb @@ -119,9 +119,7 @@ module Hbc def language_eval return @language if instance_variable_defined?(:@language) - if @language_blocks.nil? || @language_blocks.empty? - return @language = nil - end + return @language = nil if @language_blocks.nil? || @language_blocks.empty? MacOS.languages.map(&Locale.method(:parse)).each do |locale| key = @language_blocks.keys.detect do |strings| diff --git a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb index e27870622..d302d0946 100644 --- a/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb +++ b/Library/Homebrew/cask/lib/hbc/dsl/appcast.rb @@ -12,7 +12,7 @@ module Hbc end def calculate_checkpoint - result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, @uri], print_stderr: false) + result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--fail", @uri], print_stderr: false) checkpoint = if result.success? processed_appcast_text = result.stdout.gsub(%r{<pubDate>[^<]*</pubDate>}m, "") diff --git a/Library/Homebrew/cask/lib/hbc/exceptions.rb b/Library/Homebrew/cask/lib/hbc/exceptions.rb index 14c52e94b..b9d305a9b 100644 --- a/Library/Homebrew/cask/lib/hbc/exceptions.rb +++ b/Library/Homebrew/cask/lib/hbc/exceptions.rb @@ -113,7 +113,7 @@ module Hbc end def to_s - "Cask '#{token}' definition is invalid" + (!submsg.empty? ? ": #{submsg}" : "") + "Cask '#{token}' definition is invalid#{": #{submsg}" unless submsg.empty?}" end end diff --git a/Library/Homebrew/cask/lib/hbc/installer.rb b/Library/Homebrew/cask/lib/hbc/installer.rb index f02f07806..7da9731e5 100644 --- a/Library/Homebrew/cask/lib/hbc/installer.rb +++ b/Library/Homebrew/cask/lib/hbc/installer.rb @@ -14,19 +14,35 @@ module Hbc include Staged include Verify - attr_reader :force, :skip_cask_deps - PERSISTENT_METADATA_SUBDIRS = ["gpg"].freeze - def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, require_sha: false) + def initialize(cask, command: SystemCommand, force: false, skip_cask_deps: false, binaries: true, verbose: false, require_sha: false) @cask = cask @command = command @force = force @skip_cask_deps = skip_cask_deps + @binaries = binaries + @verbose = verbose @require_sha = require_sha @reinstall = false end + def skip_cask_deps? + @skip_cask_deps + end + + def force? + @force + end + + def binaries? + @binaries + end + + def verbose? + @verbose + end + def self.print_caveats(cask) odebug "Printing caveats" return if cask.caveats.empty? @@ -59,7 +75,7 @@ module Hbc odebug "Hbc::Installer#fetch" satisfy_dependencies - verify_has_sha if @require_sha && !@force + verify_has_sha if @require_sha && !force? download verify end @@ -77,7 +93,7 @@ module Hbc def install odebug "Hbc::Installer#install" - if @cask.installed? && !force && !@reinstall + if @cask.installed? && !force? && !@reinstall raise CaskAlreadyInstalledAutoUpdatesError, @cask if @cask.auto_updates raise CaskAlreadyInstalledError, @cask end @@ -108,7 +124,7 @@ module Hbc installed_cask = installed_caskfile.exist? ? CaskLoader.load_from_file(installed_caskfile) : @cask # Always force uninstallation, ignore method parameter - Installer.new(installed_cask, force: true).uninstall + Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true).uninstall end def summary @@ -156,12 +172,17 @@ module Hbc already_installed_artifacts = [] odebug "Installing artifacts" - artifacts = Artifact.for_cask(@cask, command: @command, force: force) + artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?) odebug "#{artifacts.length} artifact/s defined", artifacts artifacts.each do |artifact| next unless artifact.respond_to?(:install_phase) odebug "Installing artifact of class #{artifact.class}" + + if artifact.is_a?(Artifact::Binary) + next unless binaries? + end + artifact.install_phase already_installed_artifacts.unshift(artifact) end @@ -189,7 +210,7 @@ module Hbc arch_dependencies x11_dependencies formula_dependencies - cask_dependencies unless skip_cask_deps + cask_dependencies unless skip_cask_deps? puts "complete" end @@ -254,7 +275,7 @@ module Hbc if dep.installed? puts "already installed" else - Installer.new(dep, force: false, skip_cask_deps: true).install + Installer.new(dep, binaries: binaries?, verbose: verbose?, skip_cask_deps: true, force: false).install puts "done" end end @@ -330,16 +351,12 @@ module Hbc disable_accessibility_access uninstall_artifacts purge_versioned_files - purge_caskroom_path if force + purge_caskroom_path if force? end def uninstall_artifacts odebug "Un-installing artifacts" - artifacts = Artifact.for_cask(@cask, command: @command, force: force) - - # Make sure the `uninstall` stanza is run first, as it - # may depend on other artifacts still being installed. - artifacts = artifacts.sort_by { |a| a.is_a?(Artifact::Uninstall) ? -1 : 1 } + artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?) odebug "#{artifacts.length} artifact/s defined", artifacts diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb index c14079bc8..6414a9e80 100644 --- a/Library/Homebrew/cask/lib/hbc/system_command.rb +++ b/Library/Homebrew/cask/lib/hbc/system_command.rb @@ -94,7 +94,7 @@ module Hbc loop do readable_sources = IO.select(sources)[0] readable_sources.delete_if(&:eof?).first(1).each do |source| - type = (source == sources[0] ? :stdout : :stderr) + type = ((source == sources[0]) ? :stdout : :stderr) begin yield(type, source.readline_nonblock || "") rescue IO::WaitReadable, EOFError @@ -154,7 +154,7 @@ module Hbc def self._parse_plist(command, output) raise CaskError, "Empty plist input" unless output =~ /\S/ output.sub!(/\A(.*?)(<\?\s*xml)/m, '\2') - _warn_plist_garbage(command, Regexp.last_match[1]) if CLI.debug? + _warn_plist_garbage(command, Regexp.last_match[1]) if ARGV.debug? output.sub!(%r{(<\s*/\s*plist\s*>)(.*?)\Z}m, '\1') _warn_plist_garbage(command, Regexp.last_match[2]) xml = Plist.parse_xml(output) diff --git a/Library/Homebrew/cask/lib/hbc/utils.rb b/Library/Homebrew/cask/lib/hbc/utils.rb index b2b65a08e..59e85aaeb 100644 --- a/Library/Homebrew/cask/lib/hbc/utils.rb +++ b/Library/Homebrew/cask/lib/hbc/utils.rb @@ -2,8 +2,6 @@ require "yaml" require "open3" require "stringio" -require "hbc/utils/file" - BUG_REPORTS_URL = "https://github.com/caskroom/homebrew-cask#reporting-bugs".freeze # monkeypatch Object - not a great idea @@ -29,7 +27,7 @@ end # global methods def odebug(title, *sput) - return unless Hbc::CLI.debug? + return unless ARGV.debug? puts Formatter.headline(title, color: :magenta) puts sput unless sput.empty? end diff --git a/Library/Homebrew/cask/lib/hbc/utils/file.rb b/Library/Homebrew/cask/lib/hbc/utils/file.rb deleted file mode 100644 index 6b80f33ce..000000000 --- a/Library/Homebrew/cask/lib/hbc/utils/file.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Hbc - module Utils - module_function - - def file_locked?(file) - unlocked = File.open(file).flock(File::LOCK_EX | File::LOCK_NB) - # revert lock if file was unlocked before check - File.open(file).flock(File::LOCK_UN) if unlocked - !unlocked - rescue - true - end - end -end diff --git a/Library/Homebrew/cmd/cask.rb b/Library/Homebrew/cmd/cask.rb index 8a68b8d9a..550081d46 100644 --- a/Library/Homebrew/cmd/cask.rb +++ b/Library/Homebrew/cmd/cask.rb @@ -5,6 +5,6 @@ module Homebrew module_function def cask - Hbc::CLI.process(ARGV) + Hbc::CLI.run(*ARGV) end end diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb index f80bdfb0d..bbf0c1b0b 100644 --- a/Library/Homebrew/cmd/deps.rb +++ b/Library/Homebrew/cmd/deps.rb @@ -38,6 +38,7 @@ #: `--include-optional`, and `--skip-recommended` as documented above. # encoding: UTF-8 + require "formula" require "ostruct" @@ -112,10 +113,10 @@ module Homebrew end else deps = f.deps.reject do |dep| - ignores.any? { |ignore| dep.send(ignore) } && !includes.any? { |include| dep.send(include) } + ignores.any? { |ignore| dep.send(ignore) } && includes.none? { |include| dep.send(include) } end reqs = f.requirements.reject do |req| - ignores.any? { |ignore| req.send(ignore) } && !includes.any? { |include| req.send(include) } + ignores.any? { |ignore| req.send(ignore) } && includes.none? { |include| req.send(include) } end end @@ -160,7 +161,7 @@ module Homebrew else "├──" end - prefix_ext = i == max ? " " : "│ " + prefix_ext = (i == max) ? " " : "│ " puts prefix + "#{chr} #{dep_display_name(dep)}" recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_ext) end diff --git a/Library/Homebrew/cmd/help.rb b/Library/Homebrew/cmd/help.rb index 1378e7b1f..fc3878f16 100644 --- a/Library/Homebrew/cmd/help.rb +++ b/Library/Homebrew/cmd/help.rb @@ -1,27 +1,27 @@ -HOMEBREW_HELP = <<-EOS.freeze -Example usage: - brew search [TEXT|/REGEX/] - brew (info|home|options) [FORMULA...] - brew install FORMULA... - brew update - brew upgrade [FORMULA...] - brew uninstall FORMULA... - brew list [FORMULA...] +HOMEBREW_HELP = <<-EOS.unindent.freeze + Example usage: + brew search [TEXT|/REGEX/] + brew (info|home|options) [FORMULA...] + brew install FORMULA... + brew update + brew upgrade [FORMULA...] + brew uninstall FORMULA... + brew list [FORMULA...] -Troubleshooting: - brew config - brew doctor - brew install -vd FORMULA + Troubleshooting: + brew config + brew doctor + brew install -vd FORMULA -Developers: - brew create [URL [--no-fetch]] - brew edit [FORMULA...] - http://docs.brew.sh/Formula-Cookbook.html + Developers: + brew create [URL [--no-fetch]] + brew edit [FORMULA...] + http://docs.brew.sh/Formula-Cookbook.html -Further help: - man brew - brew help [COMMAND] - brew home + Further help: + man brew + brew help [COMMAND] + brew home EOS # NOTE Keep the lenth of vanilla --help less than 25 lines! diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb index e7ad6821d..826f31bbd 100644 --- a/Library/Homebrew/cmd/info.rb +++ b/Library/Homebrew/cmd/info.rb @@ -123,8 +123,16 @@ module Homebrew puts f.desc if f.desc puts Formatter.url(f.homepage) if f.homepage - conflicts = f.conflicts.map(&:name).sort! - puts "Conflicts with: #{conflicts*", "}" unless conflicts.empty? + conflicts = f.conflicts.map do |c| + reason = " (because #{c.reason})" if c.reason + "#{c.name}#{reason}" + end.sort! + unless conflicts.empty? + puts <<-EOS.undent + Conflicts with: + #{conflicts.join("\n ")} + EOS + end kegs = f.installed_kegs.sort_by(&:version) if kegs.empty? diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index 08d1ffccf..423b47884 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -95,9 +95,8 @@ module Homebrew args << "--verbose" if ARGV.verbose? ARGV.casks.each do |c| - cmd = "brew", "cask", "install", c, *args - ohai cmd.join " " - system(*cmd) + ohai "brew cask install #{c} #{args.join " "}" + system("#{HOMEBREW_PREFIX}/bin/brew", "cask", "install", c, *args) end end @@ -146,8 +145,17 @@ module Homebrew # linked to opt, because installing without any warnings can break # dependencies. Therefore before performing other checks we need to be # sure --force flag is passed. - opoo "#{f.full_name} is a keg-only and another version is linked to opt." - puts "Use `brew install --force` if you want to install this version" + if f.outdated? + optlinked_version = Keg.for(f.opt_prefix).version + onoe <<-EOS.undent + #{f.full_name} #{optlinked_version} is already installed + To upgrade to #{f.version}, run `brew upgrade #{f.name}` + EOS + else + opoo <<-EOS.undent + #{f.full_name} #{f.pkg_version} is already installed + EOS + end elsif (ARGV.build_head? && new_head_installed) || prefix_installed # After we're sure that --force flag is passed for linked to opt # keg-only we need to be sure that the version we're attempting to @@ -159,30 +167,38 @@ module Homebrew f.pkg_version end - msg = "#{f.full_name}-#{installed_version} already installed" + msg = "#{f.full_name} #{installed_version} is already installed" linked_not_equals_installed = f.linked_version != installed_version if f.linked? && linked_not_equals_installed - msg << ", however linked version is #{f.linked_version}" - opoo msg - puts "You can use `brew switch #{f} #{installed_version}` to link this version." + msg = <<-EOS.undent + #{msg} + The currently linked version is #{f.linked_version} + You can use `brew switch #{f} #{installed_version}` to link this version. + EOS elsif !f.linked? || f.keg_only? - msg << ", it's just not linked." - opoo msg - else - opoo msg + msg = <<-EOS.undent + #{msg}, it's just not linked. + You can use `brew link #{f}` to link this version. + EOS end + opoo msg elsif !f.any_version_installed? && old_formula = f.old_installed_formulae.first - msg = "#{old_formula.full_name}-#{old_formula.installed_version} already installed" + msg = "#{old_formula.full_name} #{old_formula.installed_version} already installed" if !old_formula.linked? && !old_formula.keg_only? - msg << ", it's just not linked." + msg = <<-EOS.undent + #{msg}, it's just not linked. + You can use `brew link #{old_formula.full_name}` to link this version. + EOS end opoo msg elsif f.migration_needed? && !ARGV.force? # Check if the formula we try to install is the same as installed # but not migrated one. If --force passed then install anyway. - opoo "#{f.oldname} already installed, it's just not migrated" - puts "You can migrate formula with `brew migrate #{f}`" - puts "Or you can force install it with `brew install #{f} --force`" + opoo <<-EOS.undent + #{f.oldname} already installed, it's just not migrated + You can migrate formula with `brew migrate #{f}` + Or you can force install it with `brew install #{f} --force` + EOS else # If none of the above is true and the formula is linked, then # FormulaInstaller will handle this case. diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb index b8bd135e0..6c4b912e8 100644 --- a/Library/Homebrew/cmd/link.rb +++ b/Library/Homebrew/cmd/link.rb @@ -72,9 +72,7 @@ module Homebrew puts "#{n} symlinks created" end - if keg_only && !ARGV.homebrew_developer? - puts_keg_only_path_message(keg) - end + puts_keg_only_path_message(keg) if keg_only && !ARGV.homebrew_developer? end end end diff --git a/Library/Homebrew/cmd/list.rb b/Library/Homebrew/cmd/list.rb index 790d858aa..f5c4e68ac 100644 --- a/Library/Homebrew/cmd/list.rb +++ b/Library/Homebrew/cmd/list.rb @@ -129,7 +129,7 @@ module Homebrew names.each do |d| versions = d.subdirs.map { |pn| pn.basename.to_s } next if ARGV.include?("--multiple") && versions.length < 2 - puts "#{d.basename} #{versions*" "}" + puts "#{d.basename} #{versions * " "}" end end end diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index f71a14ba1..2b35dfc36 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -107,14 +107,14 @@ module Homebrew dirname, filename = File.split(match["path"]) next unless valid_dirnames.include?(dirname) tap = Tap.fetch(match["repository"]["full_name"]) - next if tap.installed? + next if tap.installed? && match["repository"]["owner"]["login"] != "caskroom" "#{tap.name}/#{File.basename(filename, ".rb")}" end.compact end def search_formulae(regex) aliases = Formula.alias_full_names - results = (Formula.full_names+aliases).grep(regex).sort + results = (Formula.full_names + aliases).grep(regex).sort results.map do |name| begin diff --git a/Library/Homebrew/cmd/style.rb b/Library/Homebrew/cmd/style.rb index cf41d91ee..7da71749c 100644 --- a/Library/Homebrew/cmd/style.rb +++ b/Library/Homebrew/cmd/style.rb @@ -19,6 +19,7 @@ require "utils" require "json" +require "open3" module Homebrew module_function @@ -72,7 +73,11 @@ module Homebrew args = %w[ --force-exclusion ] - args << "--auto-correct" if fix + if fix + args << "--auto-correct" + else + args << "--parallel" + end if options[:except_cops] options[:except_cops].map! { |cop| RuboCop::Cop::Cop.registry.qualified_cop_name(cop.to_s, "") } @@ -94,26 +99,28 @@ module Homebrew if files.nil? args << "--config" << HOMEBREW_LIBRARY_PATH/".rubocop.yml" - args += [HOMEBREW_LIBRARY_PATH] + args << HOMEBREW_LIBRARY_PATH else args << "--config" << HOMEBREW_LIBRARY/".rubocop.yml" args += files end + cache_env = { "XDG_CACHE_HOME" => "#{HOMEBREW_CACHE}/style" } + case output_type when :print args << "--display-cop-names" if ARGV.include? "--display-cop-names" args << "--format" << "simple" if files - system "rubocop", *args + system(cache_env, "rubocop", *args) !$?.success? when :json - json = Utils.popen_read_text("rubocop", "--format", "json", *args) + json, _, status = Open3.capture3(cache_env, "rubocop", "--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 # native extension problems. # JSON needs to be at least 2 characters. - if $?.exitstatus.nil? || $?.exitstatus > 1 || json.to_s.length < 2 + if !(0..1).cover?(status.exitstatus) || json.to_s.length < 2 raise "Error running `rubocop --format json #{args.join " "}`" end RubocopResults.new(JSON.parse(json)) diff --git a/Library/Homebrew/cmd/switch.rb b/Library/Homebrew/cmd/switch.rb index 6eed1fd96..a40b4d88e 100644 --- a/Library/Homebrew/cmd/switch.rb +++ b/Library/Homebrew/cmd/switch.rb @@ -25,7 +25,7 @@ module Homebrew end # Does the target version exist? - unless (rack+version).directory? + unless (rack/version).directory? onoe "#{name} does not have a version \"#{version}\" in the Cellar." versions = rack.subdirs.map { |d| Keg.new(d).version } @@ -41,7 +41,7 @@ module Homebrew keg.unlink end - keg = Keg.new(rack+version) + keg = Keg.new(rack/version) # Link new version, if not keg-only if keg_only?(rack) diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index 9c51a0d1c..4839ba1e0 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -55,7 +55,7 @@ module Homebrew if rack.directory? versions = rack.subdirs.map(&:basename) - verb = versions.length == 1 ? "is" : "are" + verb = Formatter.pluralize(versions.length, "is", "are") puts "#{keg.name} #{versions.join(", ")} #{verb} still installed." puts "Remove all versions with `brew uninstall --force #{keg.name}`." end @@ -109,11 +109,11 @@ module Homebrew protected def are(items) - items.count == 1 ? "is" : "are" + Formatter.pluralize(items.count, "is", "are", show_count: false) end def they(items) - items.count == 1 ? "it" : "they" + Formatter.pluralize(items.count, "it", "they", show_count: false) end def list(items) diff --git a/Library/Homebrew/cmd/unpack.rb b/Library/Homebrew/cmd/unpack.rb index 89992e1f0..51c2b59f0 100644 --- a/Library/Homebrew/cmd/unpack.rb +++ b/Library/Homebrew/cmd/unpack.rb @@ -29,7 +29,7 @@ module Homebrew raise "Cannot write to #{unpack_dir}" unless unpack_dir.writable_real? formulae.each do |f| - stage_dir = unpack_dir.join("#{f.name}-#{f.version}") + stage_dir = unpack_dir/"#{f.name}-#{f.version}" if stage_dir.exist? raise "Destination #{stage_dir} already exists!" unless ARGV.force? diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb index bab174184..24684c3b6 100644 --- a/Library/Homebrew/cmd/uses.rb +++ b/Library/Homebrew/cmd/uses.rb @@ -98,10 +98,10 @@ module Homebrew reqs = reqs_by_formula.map(&:last) else deps = f.deps.reject do |dep| - ignores.any? { |ignore| dep.send(ignore) } && !includes.any? { |include| dep.send(include) } + ignores.any? { |ignore| dep.send(ignore) } && includes.none? { |include| dep.send(include) } end reqs = f.requirements.reject do |req| - ignores.any? { |ignore| req.send(ignore) } && !includes.any? { |include| req.send(include) } + ignores.any? { |ignore| req.send(ignore) } && includes.none? { |include| req.send(include) } end end next true if deps.any? do |dep| diff --git a/Library/Homebrew/cmd/vendor-install.sh b/Library/Homebrew/cmd/vendor-install.sh index 8f08e49fb..fe7e26dd4 100644 --- a/Library/Homebrew/cmd/vendor-install.sh +++ b/Library/Homebrew/cmd/vendor-install.sh @@ -39,10 +39,10 @@ fetch() { if [[ -n "$HOMEBREW_QUIET" ]] then - curl_args+=(--silent) + curl_args[${#curl_args[*]}]="--silent" elif [[ -z "$HOMEBREW_VERBOSE" ]] then - curl_args+=(--progress-bar) + curl_args[${#curl_args[*]}]="--progress-bar" fi temporary_path="$CACHED_LOCATION.incomplete" @@ -76,12 +76,21 @@ fetch() { trap - SIGINT fi - if [[ -n "$(which shasum)" ]] + if [[ -x "$(which shasum)" ]] then sha="$(shasum -a 256 "$CACHED_LOCATION" | cut -d' ' -f1)" - elif [[ -n "$(which sha256sum)" ]] + elif [[ -x "$(which sha256sum)" ]] then sha="$(sha256sum "$CACHED_LOCATION" | cut -d' ' -f1)" + elif [[ -x "$(which ruby)" ]] + then + sha="$(ruby <<EOSCRIPT + require 'digest/sha2' + digest = Digest::SHA256.new + File.open('$CACHED_LOCATION', 'rb') { |f| digest.update(f.read) } + puts digest.hexdigest +EOSCRIPT +)" else odie "Cannot verify the checksum ('shasum' or 'sha256sum' not found)!" fi diff --git a/Library/Homebrew/compat/hbc.rb b/Library/Homebrew/compat/hbc.rb index 353a72488..3ff8fccb7 100644 --- a/Library/Homebrew/compat/hbc.rb +++ b/Library/Homebrew/compat/hbc.rb @@ -2,6 +2,7 @@ require "compat/hbc/cask_loader" require "compat/hbc/cli/update" require "compat/hbc/cache" require "compat/hbc/caskroom" +require "compat/hbc/cli" module Hbc class << self diff --git a/Library/Homebrew/compat/hbc/cli.rb b/Library/Homebrew/compat/hbc/cli.rb new file mode 100644 index 000000000..0173bce9c --- /dev/null +++ b/Library/Homebrew/compat/hbc/cli.rb @@ -0,0 +1,19 @@ +require "cask/lib/hbc/cli/options" + +module Hbc + class CLI + include Options + + option "--binarydir=PATH", (lambda do |*| + opoo <<-EOS.undent + Option --binarydir is obsolete! + Homebrew-Cask now uses the same location as your Homebrew installation for executable links. + EOS + end) + + option "--caskroom=PATH", (lambda do |value| + Hbc.caskroom = value + odeprecated "`brew cask` with the `--caskroom` flag", disable_on: Time.utc(2017, 10, 31) + end) + end +end diff --git a/Library/Homebrew/compat/hbc/cli/update.rb b/Library/Homebrew/compat/hbc/cli/update.rb index 7820997cb..95321e898 100644 --- a/Library/Homebrew/compat/hbc/cli/update.rb +++ b/Library/Homebrew/compat/hbc/cli/update.rb @@ -1,8 +1,8 @@ -require "cask/lib/hbc/cli/base" +require "cask/lib/hbc/cli/abstract_command" module Hbc class CLI - class Update < Base + class Update < AbstractCommand def self.run(*_ignored) odeprecated "`brew cask update`", "`brew update`", disable_on: Time.utc(2017, 7, 1) result = SystemCommand.run(HOMEBREW_BREW_FILE, args: ["update"], diff --git a/Library/Homebrew/compat/utils.rb b/Library/Homebrew/compat/utils.rb index 3842e8a83..f2cca4726 100644 --- a/Library/Homebrew/compat/utils.rb +++ b/Library/Homebrew/compat/utils.rb @@ -19,5 +19,5 @@ end def plural(n, s = "s") odeprecated "#plural", "Formatter.pluralize" - n == 1 ? "" : s + (n == 1) ? "" : s end diff --git a/Library/Homebrew/constants.rb b/Library/Homebrew/constants.rb index 827d5827f..4ccf18624 100644 --- a/Library/Homebrew/constants.rb +++ b/Library/Homebrew/constants.rb @@ -1,3 +1,3 @@ # RuboCop version used for `brew style` and `brew cask style` -HOMEBREW_RUBOCOP_VERSION = "0.47.1".freeze -HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.12.0".freeze # has to be updated when RuboCop version changes +HOMEBREW_RUBOCOP_VERSION = "0.49.1".freeze +HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.13.0".freeze # has to be updated when RuboCop version changes diff --git a/Library/Homebrew/cxxstdlib.rb b/Library/Homebrew/cxxstdlib.rb index ad859badd..8a67a9c53 100644 --- a/Library/Homebrew/cxxstdlib.rb +++ b/Library/Homebrew/cxxstdlib.rb @@ -16,7 +16,7 @@ class CxxStdlib if type && ![:libstdcxx, :libcxx].include?(type) raise ArgumentError, "Invalid C++ stdlib type: #{type}" end - klass = compiler.to_s =~ GNU_GCC_REGEXP ? GnuStdlib : AppleStdlib + klass = (compiler.to_s =~ GNU_GCC_REGEXP) ? GnuStdlib : AppleStdlib klass.new(type, compiler) end diff --git a/Library/Homebrew/debrew.rb b/Library/Homebrew/debrew.rb index 668a0b4d2..7ff2c0360 100644 --- a/Library/Homebrew/debrew.rb +++ b/Library/Homebrew/debrew.rb @@ -50,7 +50,7 @@ module Debrew choice = nil while choice.nil? - menu.entries.each_with_index { |e, i| puts "#{i+1}. #{e.name}" } + menu.entries.each_with_index { |e, i| puts "#{i + 1}. #{e.name}" } print menu.prompt unless menu.prompt.nil? input = $stdin.gets || exit @@ -58,7 +58,7 @@ module Debrew i = input.to_i if i > 0 - choice = menu.entries[i-1] + choice = menu.entries[i - 1] else possible = menu.entries.find_all { |e| e.name.start_with?(input) } diff --git a/Library/Homebrew/descriptions.rb b/Library/Homebrew/descriptions.rb index a338bb73a..ac1d68216 100644 --- a/Library/Homebrew/descriptions.rb +++ b/Library/Homebrew/descriptions.rb @@ -121,7 +121,7 @@ class Descriptions blank = Formatter.warning("[no description]") @descriptions.keys.sort.each do |full_name| short_name = short_names[full_name] - printed_name = short_name_counts[short_name] == 1 ? short_name : full_name + printed_name = (short_name_counts[short_name] == 1) ? short_name : full_name description = @descriptions[full_name] || blank puts "#{Tty.bold}#{printed_name}:#{Tty.reset} #{description}" end diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index cb25ca794..aaac9c96b 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -307,25 +307,45 @@ class FormulaAuditor unversioned_name = unversioned_formula.basename(".rb") problem "#{formula} is versioned but no #{unversioned_name} formula exists" end - elsif ARGV.build_stable? - versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")] - needs_versioned_alias = !versioned_formulae.empty? && - formula.tap && - formula.aliases.grep(/.@\d/).empty? - if needs_versioned_alias - _, last_alias_version = File.basename(versioned_formulae.sort.reverse.first) - .gsub(/\.rb$/, "") - .split("@") - major, minor, = formula.version.to_s.split(".") - alias_name = if last_alias_version.split(".").length == 1 - "#{formula.name}@#{major}" + elsif ARGV.build_stable? && + !(versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")]).empty? + versioned_aliases = formula.aliases.grep(/.@\d/) + _, last_alias_version = + File.basename(versioned_formulae.sort.reverse.first) + .gsub(/\.rb$/, "").split("@") + major, minor, = formula.version.to_s.split(".") + alias_name_major = "#{formula.name}@#{major}" + alias_name_major_minor = "#{alias_name_major}.#{minor}" + alias_name = if last_alias_version.split(".").length == 1 + alias_name_major + else + alias_name_major_minor + end + valid_alias_names = [alias_name_major, alias_name_major_minor] + + if formula.tap && !formula.tap.core_tap? + valid_alias_names.map! { |a| "#{formula.tap}/#{a}" } + end + + valid_versioned_aliases = versioned_aliases & valid_alias_names + invalid_versioned_aliases = versioned_aliases - valid_alias_names + + if valid_versioned_aliases.empty? + if formula.tap + problem <<-EOS.undent + Formula has other versions so create a versioned alias: + cd #{formula.tap.alias_dir} + ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} + EOS else - "#{formula.name}@#{major}.#{minor}" + problem "Formula has other versions so create an alias named #{alias_name}." end + end + + unless invalid_versioned_aliases.empty? problem <<-EOS.undent - Formula has other versions so create an alias: - cd #{formula.tap.alias_dir} - ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} + Formula has invalid versioned aliases: + #{invalid_versioned_aliases.join("\n ")} EOS end end @@ -571,78 +591,7 @@ class FormulaAuditor def audit_homepage homepage = formula.homepage - if homepage.nil? || homepage.empty? - problem "Formula should have a homepage." - return - end - - unless homepage =~ %r{^https?://} - problem "The homepage should start with http or https (URL is #{homepage})." - end - - # Check for http:// GitHub homepage urls, https:// is preferred. - # Note: only check homepages that are repo pages, not *.github.com hosts - if homepage.start_with? "http://github.com/" - problem "Please use https:// for #{homepage}" - end - - # Savannah has full SSL/TLS support but no auto-redirect. - # Doesn't apply to the download URLs, only the homepage. - if homepage.start_with? "http://savannah.nongnu.org/" - problem "Please use https:// for #{homepage}" - end - - # Freedesktop is complicated to handle - It has SSL/TLS, but only on certain subdomains. - # To enable https Freedesktop change the URL from http://project.freedesktop.org/wiki to - # https://wiki.freedesktop.org/project_name. - # "Software" is redirected to https://wiki.freedesktop.org/www/Software/project_name - if homepage =~ %r{^http://((?:www|nice|libopenraw|liboil|telepathy|xorg)\.)?freedesktop\.org/(?:wiki/)?} - if homepage =~ /Software/ - problem "#{homepage} should be styled `https://wiki.freedesktop.org/www/Software/project_name`" - else - problem "#{homepage} should be styled `https://wiki.freedesktop.org/project_name`" - end - end - - # Google Code homepages should end in a slash - if homepage =~ %r{^https?://code\.google\.com/p/[^/]+[^/]$} - problem "#{homepage} should end with a slash" - end - - # People will run into mixed content sometimes, but we should enforce and then add - # exemptions as they are discovered. Treat mixed content on homepages as a bug. - # Justify each exemptions with a code comment so we can keep track here. - case homepage - when %r{^http://[^/]*\.github\.io/}, - %r{^http://[^/]*\.sourceforge\.io/} - problem "Please use https:// for #{homepage}" - end - - if homepage =~ %r{^http://([^/]*)\.(sf|sourceforge)\.net(/|$)} - problem "#{homepage} should be `https://#{$1}.sourceforge.io/`" - end - - # There's an auto-redirect here, but this mistake is incredibly common too. - # Only applies to the homepage and subdomains for now, not the FTP URLs. - if homepage =~ %r{^http://((?:build|cloud|developer|download|extensions|git|glade|help|library|live|nagios|news|people|projects|rt|static|wiki|www)\.)?gnome\.org} - problem "Please use https:// for #{homepage}" - end - - # Compact the above into this list as we're able to remove detailed notations, etc over time. - case homepage - when %r{^http://[^/]*\.apache\.org}, - %r{^http://packages\.debian\.org}, - %r{^http://wiki\.freedesktop\.org/}, - %r{^http://((?:www)\.)?gnupg\.org/}, - %r{^http://ietf\.org}, - %r{^http://[^/.]+\.ietf\.org}, - %r{^http://[^/.]+\.tools\.ietf\.org}, - %r{^http://www\.gnu\.org/}, - %r{^http://code\.google\.com/}, - %r{^http://bitbucket\.org/}, - %r{^http://(?:[^/]*\.)?archive\.org} - problem "Please use https:// for #{homepage}" - end + return if homepage.nil? || homepage.empty? return unless @online @@ -795,6 +744,15 @@ class FormulaAuditor return if @new_formula fv = FormulaVersions.new(formula) + + previous_version_and_checksum = fv.previous_version_and_checksum("origin/master") + [:stable, :devel].each do |spec_sym| + next unless spec = formula.send(spec_sym) + next unless previous_version_and_checksum[spec_sym][:version] == spec.version + next if previous_version_and_checksum[spec_sym][:checksum] == spec.checksum + problem "#{spec_sym}: sha256 changed without the version also changing; please create an issue upstream to rule out malicious circumstances and to find out why the file changed." + end + attributes = [:revision, :version_scheme] attributes_map = fv.version_attributes_map(attributes, "origin/master") @@ -892,14 +850,6 @@ class FormulaAuditor end def audit_text - if text =~ /system\s+['"]scons/ - problem "use \"scons *args\" instead of \"system 'scons', *args\"" - end - - if text =~ /system\s+['"]xcodebuild/ - problem %q(use "xcodebuild *args" instead of "system 'xcodebuild', *args") - end - bin_names = Set.new bin_names << formula.name bin_names += formula.aliases @@ -909,44 +859,16 @@ class FormulaAuditor end bin_names.each do |name| ["system", "shell_output", "pipe_output"].each do |cmd| - if text =~ %r{(def test|test do).*(#{Regexp.escape HOMEBREW_PREFIX}/bin/)?#{cmd}[\(\s]+['"]#{Regexp.escape name}[\s'"]}m + if text =~ %r{(def test|test do).*(#{Regexp.escape(HOMEBREW_PREFIX)}/bin/)?#{cmd}[\(\s]+['"]#{Regexp.escape(name)}[\s'"]}m problem %Q(fully scope test #{cmd} calls e.g. #{cmd} "\#{bin}/#{name}") end end end - - if text =~ /xcodebuild[ (]*["'*]*/ && !text.include?("SYMROOT=") - problem 'xcodebuild should be passed an explicit "SYMROOT"' - end - - if text.include? "Formula.factory(" - problem "\"Formula.factory(name)\" is deprecated in favor of \"Formula[name]\"" - end - - if text.include?("def plist") && !text.include?("plist_options") - problem "Please set plist_options when using a formula-defined plist." - end - - if text =~ /depends_on\s+['"]openssl['"]/ && text =~ /depends_on\s+['"]libressl['"]/ - problem "Formulae should not depend on both OpenSSL and LibreSSL (even optionally)." - end - - if text =~ /virtualenv_(create|install_with_resources)/ && - text =~ /resource\s+['"]setuptools['"]\s+do/ - problem "Formulae using virtualenvs do not need a `setuptools` resource." - end - - if text =~ /system\s+['"]go['"],\s+['"]get['"]/ - problem "Formulae should not use `go get`. If non-vendored resources are required use `go_resource`s." - end - - return unless text.include?('require "language/go"') && !text.include?("go_resource") - problem "require \"language/go\" is unnecessary unless using `go_resource`s" end def audit_lines text.without_patch.split("\n").each_with_index do |line, lineno| - line_problems(line, lineno+1) + line_problems(line, lineno + 1) end end @@ -1028,16 +950,19 @@ class FormulaAuditor problem ":apr is deprecated. Usage should be \"apr-util\"" end - if line =~ /depends_on :tex/ - problem ":tex is deprecated" - end + problem ":tex is deprecated" if line =~ /depends_on :tex/ if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+:(lua|perl|python|ruby)(\d*)/ problem "#{$2} modules should be vendored rather than use deprecated `depends_on \"#{$1}\" => :#{$2}#{$3}`" end - if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+.*(?<!\?[( ])['"](.+)['"]/ - problem "Dependency #{$1} should not use option #{$2}" + if line =~ /depends_on\s+['"](.+)['"]\s+=>\s+(.*)/ + dep = $1 + $2.split(" ").map do |o| + break if ["if", "unless"].include?(o) + next unless o =~ /^\[?['"](.*)['"]/ + problem "Dependency #{dep} should not use option #{$1}" + end end # Commented-out depends_on @@ -1224,11 +1149,6 @@ class FormulaAuditor problem "Use pkgshare instead of (share#{$1}\"#{formula.name}\")" end - def audit_caveats - return unless formula.caveats.to_s.include?("setuid") - problem "Don't recommend setuid in the caveats, suggest sudo instead." - end - def audit_reverse_migration # Only enforce for new formula being re-added to core and official taps return unless @strict @@ -1577,6 +1497,14 @@ class ResourceAuditor problem "#{u} should be `https://search.maven.org/remotecontent?filepath=#{$1}`" end + # Check pypi urls + if @strict + urls.each do |p| + next unless p =~ %r{^https?://pypi.python.org/(.*)} + problem "#{p} should be `https://files.pythonhosted.org/#{$1}`" + end + end + return unless @online urls.each do |url| next if !@strict && mirrors.include?(url) diff --git a/Library/Homebrew/dev-cmd/bottle.rb b/Library/Homebrew/dev-cmd/bottle.rb index 8d3038a5a..d8aefc4c0 100644 --- a/Library/Homebrew/dev-cmd/bottle.rb +++ b/Library/Homebrew/dev-cmd/bottle.rb @@ -248,7 +248,7 @@ module Homebrew mv "#{relocatable_tar_path}.gz", bottle_path end - if bottle_path.size > 1*1024*1024 + if bottle_path.size > 1 * 1024 * 1024 ohai "Detecting if #{filename} is relocatable..." end @@ -314,8 +314,8 @@ module Homebrew old_spec = f.bottle_specification if ARGV.include?("--keep-old") && !old_spec.checksums.empty? - mismatches = [:root_url, :prefix, :cellar, :rebuild].select do |key| - old_spec.send(key) != bottle.send(key) + mismatches = [:root_url, :prefix, :cellar, :rebuild].reject do |key| + old_spec.send(key) == bottle.send(key) end mismatches.delete(:cellar) if old_spec.cellar == :any && bottle.cellar == :any_skip_relocation unless mismatches.empty? @@ -382,9 +382,7 @@ module Homebrew bottle = BottleSpecification.new bottle.root_url bottle_hash["bottle"]["root_url"] cellar = bottle_hash["bottle"]["cellar"] - if cellar == "any" || cellar == "any_skip_relocation" - cellar = cellar.to_sym - end + cellar = cellar.to_sym if ["any", "any_skip_relocation"].include?(cellar) bottle.cellar cellar bottle.prefix bottle_hash["bottle"]["prefix"] bottle.rebuild bottle_hash["bottle"]["rebuild"] diff --git a/Library/Homebrew/dev-cmd/edit.rb b/Library/Homebrew/dev-cmd/edit.rb index df5bc0605..b3a319088 100644 --- a/Library/Homebrew/dev-cmd/edit.rb +++ b/Library/Homebrew/dev-cmd/edit.rb @@ -21,12 +21,12 @@ module Homebrew # If no brews are listed, open the project root in an editor. if ARGV.named.empty? editor = File.basename which_editor - if editor == "mate" || editor == "subl" + if ["mate", "subl"].include?(editor) # If the user is using TextMate or Sublime Text, # give a nice project view instead. - exec_editor HOMEBREW_REPOSITORY+"bin/brew", - HOMEBREW_REPOSITORY+"README.md", - HOMEBREW_REPOSITORY+".gitignore", + exec_editor HOMEBREW_REPOSITORY/"bin/brew", + HOMEBREW_REPOSITORY/"README.md", + HOMEBREW_REPOSITORY/".gitignore", *library_folders else exec_editor HOMEBREW_REPOSITORY diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb index 9d08da95b..492898a47 100644 --- a/Library/Homebrew/dev-cmd/pull.rb +++ b/Library/Homebrew/dev-cmd/pull.rb @@ -14,7 +14,7 @@ #: #: ~ The URL of a commit on GitHub #: -#: ~ A "https://bot.brew.sh/job/..." string specifying a testing job ID +#: ~ A "https://jenkins.brew.sh/job/..." string specifying a testing job ID #: #: If `--bottle` is passed, handle bottles, pulling the bottle-update #: commit and publishing files on Bintray. diff --git a/Library/Homebrew/dev-cmd/release-notes.rb b/Library/Homebrew/dev-cmd/release-notes.rb index eb398fcfb..bd6363865 100644 --- a/Library/Homebrew/dev-cmd/release-notes.rb +++ b/Library/Homebrew/dev-cmd/release-notes.rb @@ -27,13 +27,13 @@ module Homebrew .lines.grep(/Merge pull request/) output.map! do |s| - s.gsub(/.*Merge pull request #(\d+)[^>]*(>>)*/, - "https://github.com/Homebrew/brew/pull/\\1") + s.gsub(%r{.*Merge pull request #(\d+) from ([^/]+)/[^>]*(>>)*}, + "https://github.com/Homebrew/brew/pull/\\1 (@\\2)") end if ARGV.include?("--markdown") output.map! do |s| - /(.*\d)+ - (.*)/ =~ s - "- [#{$2}](#{$1})" + /(.*\d)+ \(@(.+)\) - (.*)/ =~ s + "- [#{$3}](#{$1}) (@#{$2})" end end diff --git a/Library/Homebrew/dev-cmd/tap-new.rb b/Library/Homebrew/dev-cmd/tap-new.rb index 48449e98d..dcb41265c 100644 --- a/Library/Homebrew/dev-cmd/tap-new.rb +++ b/Library/Homebrew/dev-cmd/tap-new.rb @@ -49,6 +49,10 @@ module Homebrew env: OSX=10.12 osx_image: xcode8.3 rvm: system + cache: + directories: + - $HOME/.gem/ruby + - Library/Homebrew/vendor/bundle before_install: - export TRAVIS_COMMIT="$(git rev-parse --verify -q HEAD)" diff --git a/Library/Homebrew/dev-cmd/tests.rb b/Library/Homebrew/dev-cmd/tests.rb index 72d6143fc..13f4d7b1e 100644 --- a/Library/Homebrew/dev-cmd/tests.rb +++ b/Library/Homebrew/dev-cmd/tests.rb @@ -59,10 +59,11 @@ module Homebrew ENV["GIT_#{role}_DATE"] = "Sun Jan 22 19:59:13 2017 +0000" end - Homebrew.install_gem_setup_path! "bundler" - unless quiet_system("bundle", "check") - system "bundle", "install" - end + # TODO: unpin this version when this error no longer shows: + # bundler-1.15.0/lib/bundler/shared_helpers.rb:25: + # stack level too deep (SystemStackError) + Homebrew.install_gem_setup_path! "bundler", "1.14.6" + system "bundle", "install" unless quiet_system("bundle", "check") parallel = true @@ -79,20 +80,24 @@ module Homebrew Dir.glob("test/**/*_spec.rb").reject { |p| p =~ %r{^test/vendor/bundle/} } end - opts = [] - - if ENV["CI"] - opts << "--combine-stderr" - opts << "--serialize-stdout" + opts = if ENV["CI"] + %w[ + --combine-stderr + --serialize-stdout + ] + else + %w[ + --nice + ] end - args = [ - "--color", - "-I", HOMEBREW_LIBRARY_PATH/"test", - "--require", "spec_helper", - "--format", "progress", - "--format", "ParallelTests::RSpec::RuntimeLogger", - "--out", "tmp/parallel_runtime_rspec.log" + args = ["-I", HOMEBREW_LIBRARY_PATH/"test"] + args += %W[ + --color + --require spec_helper + --format progress + --format ParallelTests::RSpec::RuntimeLogger + --out #{HOMEBREW_CACHE}/tests/parallel_runtime_rspec.log ] args << "--seed" << ARGV.next if ARGV.include? "--seed" diff --git a/Library/Homebrew/dev-cmd/update-test.rb b/Library/Homebrew/dev-cmd/update-test.rb index add05bc7c..aa7fe6a92 100644 --- a/Library/Homebrew/dev-cmd/update-test.rb +++ b/Library/Homebrew/dev-cmd/update-test.rb @@ -33,14 +33,20 @@ module Homebrew elsif date = ARGV.value("before") Utils.popen_read("git", "rev-list", "-n1", "--before=#{date}", "origin/master").chomp elsif ARGV.include?("--to-tag") - previous_tag = - Utils.popen_read("git", "tag", "--list", "--sort=-version:refname").lines[1] - unless previous_tag - safe_system "git", "fetch", "--tags", "--depth=1" - previous_tag = - Utils.popen_read("git", "tag", "--list", "--sort=-version:refname").lines[1] + tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname") + previous_tag = tags.lines[1] + previous_tag ||= begin + if (HOMEBREW_REPOSITORY/".git/shallow").exist? + safe_system "git", "fetch", "--tags", "--depth=1" + tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname") + elsif OS.linux? + tags = Utils.popen_read("git tag --list | sort -rV") + end + tags.lines[1] end - previous_tag.to_s.chomp + previous_tag = previous_tag.to_s.chomp + odie "Could not find previous tag in:\n#{tags}" if previous_tag.empty? + previous_tag else Utils.popen_read("git", "rev-parse", "origin/master").chomp end diff --git a/Library/Homebrew/development_tools.rb b/Library/Homebrew/development_tools.rb index 8f417b082..996dea87c 100644 --- a/Library/Homebrew/development_tools.rb +++ b/Library/Homebrew/development_tools.rb @@ -50,8 +50,8 @@ class DevelopmentTools def gcc_4_2_build_version @gcc_4_2_build_version ||= begin - gcc = locate("gcc-4.2") || HOMEBREW_PREFIX.join("opt/apple-gcc42/bin/gcc-4.2") - if gcc.exist? && !gcc.realpath.basename.to_s.start_with?("llvm")&& + gcc = locate("gcc-4.2") || HOMEBREW_PREFIX/"opt/apple-gcc42/bin/gcc-4.2" + if gcc.exist? && !gcc.realpath.basename.to_s.start_with?("llvm") && build_version = `#{gcc} --version 2>/dev/null`[/build (\d{4,})/, 1] Version.new build_version else @@ -96,7 +96,7 @@ class DevelopmentTools def non_apple_gcc_version(cc) (@non_apple_gcc_version ||= {}).fetch(cc) do - path = HOMEBREW_PREFIX.join("opt", "gcc", "bin", cc) + path = HOMEBREW_PREFIX/"opt/gcc/bin"/cc path = locate(cc) unless path.exist? version = if path && build_version = `#{path} --version`[/gcc(?:-\d(?:\.\d)? \(.+\))? (\d\.\d\.\d)/, 1] diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index 1544e6765..2741184cc 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -100,7 +100,7 @@ module Homebrew # See https://github.com/Homebrew/legacy-homebrew/pull/9986 def check_path_for_trailing_slashes - bad_paths = PATH.new(ENV["PATH"]).select { |p| p.end_with?("/") } + bad_paths = PATH.new(ENV["HOMEBREW_PATH"]).select { |p| p.end_with?("/") } return if bad_paths.empty? inject_file_list bad_paths, <<-EOS.undent @@ -119,7 +119,7 @@ module Homebrew return unless which("python") anaconda_directory = which("anaconda").realpath.dirname - python_binary = Utils.popen_read which("python"), "-c", "import sys; sys.stdout.write(sys.executable)" + python_binary = Utils.popen_read(which("python"), "-c", "import sys; sys.stdout.write(sys.executable)") python_directory = Pathname.new(python_binary).realpath.dirname # Only warn if Python lives with Anaconda, since is most problematic case. @@ -158,7 +158,7 @@ module Homebrew "libosxfuse_i32.2.dylib", # OSXFuse "libosxfuse_i64.2.dylib", # OSXFuse "libosxfuse.2.dylib", # OSXFuse - "libTrAPI.dylib", # TrAPI / Endpoint Security VPN + "libTrAPI.dylib", # TrAPI/Endpoint Security VPN "libntfs-3g.*.dylib", # NTFS-3G "libntfs.*.dylib", # NTFS-3G "libublio.*.dylib", # NTFS-3G @@ -415,23 +415,6 @@ module Homebrew EOS end - def check_homebrew_prefix - return if HOMEBREW_PREFIX.to_s == "/usr/local" - - # Allow our Jenkins CI tests to live outside of /usr/local. - if ENV["JENKINS_HOME"] && - ENV["GIT_URL"].to_s.start_with?("https://github.com/Homebrew/brew") - return - end - - <<-EOS.undent - Your Homebrew's prefix is not /usr/local. - You can install Homebrew anywhere you want but some bottles (binary packages) - can only be used with a /usr/local prefix and some formulae (packages) - may not build correctly with a non-/usr/local prefix. - EOS - end - def check_user_path_1 $seen_prefix_bin = false $seen_prefix_sbin = false @@ -487,7 +470,7 @@ module Homebrew return if $seen_prefix_sbin # Don't complain about sbin not being in the path if it doesn't exist - sbin = (HOMEBREW_PREFIX+"sbin") + sbin = HOMEBREW_PREFIX/"sbin" return unless sbin.directory? && !sbin.children.empty? <<-EOS.undent @@ -513,33 +496,6 @@ module Homebrew EOS end - def check_which_pkg_config - binary = which "pkg-config" - return if binary.nil? - - mono_config = Pathname.new("/usr/bin/pkg-config") - if mono_config.exist? && mono_config.realpath.to_s.include?("Mono.framework") - <<-EOS.undent - You have a non-Homebrew 'pkg-config' in your PATH: - /usr/bin/pkg-config => #{mono_config.realpath} - - This was most likely created by the Mono installer. `./configure` may - have problems finding brew-installed packages using this other pkg-config. - - Mono no longer installs this file as of 3.0.4. You should - `sudo rm /usr/bin/pkg-config` and upgrade to the latest version of Mono. - EOS - elsif binary.to_s != "#{HOMEBREW_PREFIX}/bin/pkg-config" - <<-EOS.undent - You have a non-Homebrew 'pkg-config' in your PATH: - #{binary} - - `./configure` may have problems finding brew-installed packages using - this other pkg-config. - EOS - end - end - def check_for_gettext find_relative_paths("lib/libgettextlib.dylib", "lib/libintl.dylib", @@ -716,39 +672,6 @@ module Homebrew EOS end - def check_filesystem_case_sensitive - dirs_to_check = [ - HOMEBREW_PREFIX, - HOMEBREW_REPOSITORY, - HOMEBREW_CELLAR, - HOMEBREW_TEMP, - ] - case_sensitive_dirs = dirs_to_check.select do |dir| - # We select the dir as being case-sensitive if either the UPCASED or the - # downcased variant is missing. - # Of course, on a case-insensitive fs, both exist because the os reports so. - # In the rare situation when the user has indeed a downcased and an upcased - # dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive - # but we don't care because: 1. there is more than one dir checked, 2. the - # check is not vital and 3. we would have to touch files otherwise. - upcased = Pathname.new(dir.to_s.upcase) - downcased = Pathname.new(dir.to_s.downcase) - dir.exist? && !(upcased.exist? && downcased.exist?) - end - return if case_sensitive_dirs.empty? - - volumes = Volumes.new - case_sensitive_vols = case_sensitive_dirs.map do |case_sensitive_dir| - volumes.get_mounts(case_sensitive_dir) - end - case_sensitive_vols.uniq! - - <<-EOS.undent - The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive. - The default macOS filesystem is case-insensitive. Please report any apparent problems. - EOS - end - def check_git_version # https://help.github.com/articles/https-cloning-errors return unless Utils.git_available? diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb index a0025cef3..9ca30bb9c 100644 --- a/Library/Homebrew/download_strategy.rb +++ b/Library/Homebrew/download_strategy.rb @@ -139,7 +139,7 @@ class VCSDownloadStrategy < AbstractDownloadStrategy super @ref_type, @ref = extract_ref(meta) @revision = meta[:revision] - @clone = HOMEBREW_CACHE.join(cache_filename) + @clone = HOMEBREW_CACHE/cache_filename end def fetch @@ -322,7 +322,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy def initialize(name, resource) super @mirrors = resource.mirrors.dup - @tarball_path = HOMEBREW_CACHE.join("#{name}-#{version}#{ext}") + @tarball_path = HOMEBREW_CACHE/"#{name}-#{version}#{ext}" @temporary_path = Pathname.new("#{cached_location}.incomplete") end @@ -520,6 +520,9 @@ class S3DownloadStrategy < CurlDownloadStrategy bucket = $1 key = $2 + ENV["AWS_ACCESS_KEY_ID"] = ENV["HOMEBREW_AWS_ACCESS_KEY_ID"] + ENV["AWS_SECRET_ACCESS_KEY"] = ENV["HOMEBREW_AWS_SECRET_ACCESS_KEY"] + obj = AWS::S3.new.buckets[bucket].objects[key] begin s3url = obj.url_for(:get) @@ -695,7 +698,7 @@ class SubversionDownloadStrategy < VCSDownloadStrategy end def repo_valid? - cached_location.join(".svn").directory? + (cached_location/".svn").directory? end def clone_repo @@ -708,7 +711,7 @@ class SubversionDownloadStrategy < VCSDownloadStrategy fetch_repo cached_location, @url, main_revision, true externals do |external_name, external_url| - fetch_repo cached_location+external_name, external_url, @ref[external_name], true + fetch_repo cached_location/external_name, external_url, @ref[external_name], true end else fetch_repo cached_location, @url @@ -770,7 +773,7 @@ class GitDownloadStrategy < VCSDownloadStrategy end def shallow_dir? - git_dir.join("shallow").exist? + (git_dir/"shallow").exist? end def support_depth? @@ -778,7 +781,7 @@ class GitDownloadStrategy < VCSDownloadStrategy end def git_dir - cached_location.join(".git") + cached_location/".git" end def ref? @@ -794,7 +797,7 @@ class GitDownloadStrategy < VCSDownloadStrategy end def submodules? - cached_location.join(".gitmodules").exist? + (cached_location/".gitmodules").exist? end def clone_args @@ -984,7 +987,7 @@ class CVSDownloadStrategy < VCSDownloadStrategy end def repo_valid? - cached_location.join("CVS").directory? + (cached_location/"CVS").directory? end def clone_repo @@ -1001,8 +1004,8 @@ class CVSDownloadStrategy < VCSDownloadStrategy def split_url(in_url) parts = in_url.split(/:/) - mod=parts.pop - url=parts.join(":") + mod = parts.pop + url = parts.join(":") [mod, url] end end @@ -1042,7 +1045,7 @@ class MercurialDownloadStrategy < VCSDownloadStrategy end def repo_valid? - cached_location.join(".hg").directory? + (cached_location/".hg").directory? end def clone_repo @@ -1082,7 +1085,7 @@ class BazaarDownloadStrategy < VCSDownloadStrategy end def repo_valid? - cached_location.join(".bzr").directory? + (cached_location/".bzr").directory? end def clone_repo diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 6751b2224..488ef7064 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -325,10 +325,10 @@ class FormulaConflictError < RuntimeError def message message = [] - message << "Cannot install #{formula.full_name} because conflicting formulae are installed.\n" + message << "Cannot install #{formula.full_name} because conflicting formulae are installed." message.concat conflicts.map { |c| conflict_message(c) } << "" message << <<-EOS.undent - Please `brew unlink #{conflicts.map(&:name)*" "}` before continuing. + Please `brew unlink #{conflicts.map(&:name) * " "}` before continuing. Unlinking removes a formula's symlinks from #{HOMEBREW_PREFIX}. You can link the formula again after the install finishes. You can --force this diff --git a/Library/Homebrew/extend/ARGV.rb b/Library/Homebrew/extend/ARGV.rb index 8cc2ccbc0..c6cb54f5d 100644 --- a/Library/Homebrew/extend/ARGV.rb +++ b/Library/Homebrew/extend/ARGV.rb @@ -138,7 +138,7 @@ module HomebrewArgvExtension end def next - at(@n+1) || raise(UsageError) + at(@n + 1) || raise(UsageError) end def value(name) diff --git a/Library/Homebrew/extend/ENV/std.rb b/Library/Homebrew/extend/ENV/std.rb index c4cc0985f..4a871f9bb 100644 --- a/Library/Homebrew/extend/ENV/std.rb +++ b/Library/Homebrew/extend/ENV/std.rb @@ -98,7 +98,7 @@ module Stdenv # @private def determine_cxx dir, base = determine_cc.split - dir / base.to_s.sub("gcc", "g++").sub("clang", "clang++") + dir/base.to_s.sub("gcc", "g++").sub("clang", "clang++") end def gcc_4_0 @@ -210,6 +210,8 @@ module Stdenv end alias generic_set_cpu_flags set_cpu_flags + def x11; end + # @private def effective_arch if ARGV.build_bottle? diff --git a/Library/Homebrew/extend/os/extend/ENV/std.rb b/Library/Homebrew/extend/os/extend/ENV/std.rb index 8e6b13a61..18ede5b9e 100644 --- a/Library/Homebrew/extend/os/extend/ENV/std.rb +++ b/Library/Homebrew/extend/os/extend/ENV/std.rb @@ -1,2 +1,6 @@ require "extend/ENV/std" -require "extend/os/mac/extend/ENV/std" if OS.mac? +if OS.mac? + require "extend/os/mac/extend/ENV/std" +elsif OS.linux? + require "extend/os/linux/extend/ENV/std" +end diff --git a/Library/Homebrew/extend/os/linux/extend/ENV/std.rb b/Library/Homebrew/extend/os/linux/extend/ENV/std.rb new file mode 100644 index 000000000..ed19d2495 --- /dev/null +++ b/Library/Homebrew/extend/os/linux/extend/ENV/std.rb @@ -0,0 +1,7 @@ +module Stdenv + def libxml2 + append "CPPFLAGS", "-I#{Formula["libxml2"].include/"libxml2"}" + rescue FormulaUnavailableError + nil + end +end diff --git a/Library/Homebrew/extend/os/mac/diagnostic.rb b/Library/Homebrew/extend/os/mac/diagnostic.rb index ff936c75a..58d8c633d 100644 --- a/Library/Homebrew/extend/os/mac/diagnostic.rb +++ b/Library/Homebrew/extend/os/mac/diagnostic.rb @@ -307,6 +307,77 @@ module Homebrew We recommend only installing stable releases of XQuartz. EOS end + + def check_filesystem_case_sensitive + dirs_to_check = [ + HOMEBREW_PREFIX, + HOMEBREW_REPOSITORY, + HOMEBREW_CELLAR, + HOMEBREW_TEMP, + ] + case_sensitive_dirs = dirs_to_check.select do |dir| + # We select the dir as being case-sensitive if either the UPCASED or the + # downcased variant is missing. + # Of course, on a case-insensitive fs, both exist because the os reports so. + # In the rare situation when the user has indeed a downcased and an upcased + # dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive + # but we don't care because: 1. there is more than one dir checked, 2. the + # check is not vital and 3. we would have to touch files otherwise. + upcased = Pathname.new(dir.to_s.upcase) + downcased = Pathname.new(dir.to_s.downcase) + dir.exist? && !(upcased.exist? && downcased.exist?) + end + return if case_sensitive_dirs.empty? + + volumes = Volumes.new + case_sensitive_vols = case_sensitive_dirs.map do |case_sensitive_dir| + volumes.get_mounts(case_sensitive_dir) + end + case_sensitive_vols.uniq! + + <<-EOS.undent + The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive. + The default macOS filesystem is case-insensitive. Please report any apparent problems. + EOS + end + + def check_homebrew_prefix + return if HOMEBREW_PREFIX.to_s == "/usr/local" + + <<-EOS.undent + Your Homebrew's prefix is not /usr/local. + You can install Homebrew anywhere you want but some bottles (binary packages) + can only be used with a /usr/local prefix and some formulae (packages) + may not build correctly with a non-/usr/local prefix. + EOS + end + + def check_which_pkg_config + binary = which "pkg-config" + return if binary.nil? + + mono_config = Pathname.new("/usr/bin/pkg-config") + if mono_config.exist? && mono_config.realpath.to_s.include?("Mono.framework") + <<-EOS.undent + You have a non-Homebrew 'pkg-config' in your PATH: + /usr/bin/pkg-config => #{mono_config.realpath} + + This was most likely created by the Mono installer. `./configure` may + have problems finding brew-installed packages using this other pkg-config. + + Mono no longer installs this file as of 3.0.4. You should + `sudo rm /usr/bin/pkg-config` and upgrade to the latest version of Mono. + EOS + elsif binary.to_s != "#{HOMEBREW_PREFIX}/bin/pkg-config" + <<-EOS.undent + You have a non-Homebrew 'pkg-config' in your PATH: + #{binary} + + `./configure` may have problems finding brew-installed packages using + this other pkg-config. + EOS + end + end end end end diff --git a/Library/Homebrew/extend/os/mac/extend/ENV/std.rb b/Library/Homebrew/extend/os/mac/extend/ENV/std.rb index 4853ecf0c..dd273cfbc 100644 --- a/Library/Homebrew/extend/os/mac/extend/ENV/std.rb +++ b/Library/Homebrew/extend/os/mac/extend/ENV/std.rb @@ -7,7 +7,7 @@ module Stdenv # Mountain Lion's sed is stricter, and errors out when # it encounters files with mixed character sets delete("LC_ALL") - self["LC_CTYPE"]="C" + self["LC_CTYPE"] = "C" end # Add lib and include etc. from the current macosxsdk to compiler flags: diff --git a/Library/Homebrew/extend/os/mac/keg_relocate.rb b/Library/Homebrew/extend/os/mac/keg_relocate.rb index 0b2ecd1c9..707710be6 100644 --- a/Library/Homebrew/extend/os/mac/keg_relocate.rb +++ b/Library/Homebrew/extend/os/mac/keg_relocate.rb @@ -63,9 +63,9 @@ class Keg # expensive recursive search if possible. def fixed_name(file, bad_name) if bad_name.start_with? PREFIX_PLACEHOLDER - bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX.to_s) + bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) elsif bad_name.start_with? CELLAR_PLACEHOLDER - bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR.to_s) + bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR) elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist? "@loader_path/#{bad_name}" elsif file.mach_o_executable? && (lib + bad_name).exist? @@ -89,7 +89,7 @@ class Keg # the basename of the file itself. basename = File.basename(file.dylib_id) relative_dirname = file.dirname.relative_path_from(path) - opt_record.join(relative_dirname, basename).to_s + (opt_record/relative_dirname/basename).to_s end # Matches framework references like `XXX.framework/Versions/YYY/XXX` and diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index c413e9e94..767d83ff9 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -246,7 +246,7 @@ class Pathname rmdir true rescue Errno::ENOTEMPTY - if (ds_store = self+".DS_Store").exist? && children.length == 1 + if (ds_store = join(".DS_Store")).exist? && children.length == 1 ds_store.unlink retry else @@ -322,7 +322,7 @@ class Pathname def sha256 require "digest/sha2" - incremental_hash(Digest::SHA2) + incremental_hash(Digest::SHA256) end def verify_checksum(expected) @@ -343,7 +343,7 @@ class Pathname # @private def resolved_path - symlink? ? dirname+readlink : self + symlink? ? dirname.join(readlink) : self end # @private @@ -353,7 +353,7 @@ class Pathname # The link target contains NUL bytes false else - (dirname+link).exist? + dirname.join(link).exist? end # @private @@ -367,7 +367,7 @@ class Pathname if !other.respond_to?(:to_str) && !other.respond_to?(:to_path) odeprecated "Pathname#/ with #{other.class}", "a String or a Pathname" end - self + other.to_s + join(other.to_s) end end @@ -403,7 +403,7 @@ class Pathname mkpath targets.each do |target| target = Pathname.new(target) # allow pathnames or strings - (self+target.basename).write <<-EOS.undent + join(target.basename).write <<-EOS.undent #!/bin/bash exec "#{target}" "$@" EOS @@ -427,7 +427,7 @@ class Pathname Pathname.glob("#{self}/*") do |file| next if file.directory? dst.install(file) - new_file = dst+file.basename + new_file = dst.join(file.basename) file.write_env_script(new_file, env) end end @@ -435,7 +435,7 @@ class Pathname # Writes an exec script that invokes a java jar def write_jar_script(target_jar, script_name, java_opts = "") mkpath - (self+script_name).write <<-EOS.undent + join(script_name).write <<-EOS.undent #!/bin/bash exec java #{java_opts} -jar #{target_jar} "$@" EOS diff --git a/Library/Homebrew/extend/string.rb b/Library/Homebrew/extend/string.rb index 162666d2d..ae7a209db 100644 --- a/Library/Homebrew/extend/string.rb +++ b/Library/Homebrew/extend/string.rb @@ -1,7 +1,11 @@ +# Contains backports from newer versions of Ruby +require_relative "../vendor/backports/string" + class String def undent gsub(/^[ \t]{#{(slice(/^[ \t]+/) || '').length}}/, "") end + alias unindent undent # eg: # if foo then <<-EOS.undent_________________________________________________________72 diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 17a34dd13..d9254d23d 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1,5 +1,5 @@ require "formula_support" -require "formula_lock" +require "lock_file" require "formula_pin" require "hardware" require "utils/bottles" @@ -621,14 +621,14 @@ class Formula # No `make install` available? # <pre>bin.install "binary1"</pre> def bin - prefix+"bin" + prefix/"bin" end # The directory where the formula's documentation should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def doc - share+"doc"+name + share/"doc"/name end # The directory where the formula's headers should be installed. @@ -638,14 +638,14 @@ class Formula # No `make install` available? # <pre>include.install "example.h"</pre> def include - prefix+"include" + prefix/"include" end # The directory where the formula's info files should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def info - share+"info" + share/"info" end # The directory where the formula's libraries should be installed. @@ -655,7 +655,7 @@ class Formula # No `make install` available? # <pre>lib.install "example.dylib"</pre> def lib - prefix+"lib" + prefix/"lib" end # The directory where the formula's binaries should be installed. @@ -664,7 +664,7 @@ class Formula # symlinked into HOMEBREW_PREFIX from one of the other directories and # instead manually create symlinks or wrapper scripts into e.g. {#bin}. def libexec - prefix+"libexec" + prefix/"libexec" end # The root directory where the formula's manual pages should be installed. @@ -673,7 +673,7 @@ class Formula # Often one of the more specific `man` functions should be used instead # e.g. {#man1} def man - share+"man" + share/"man" end # The directory where the formula's man1 pages should be installed. @@ -683,14 +683,14 @@ class Formula # No `make install` available? # <pre>man1.install "example.1"</pre> def man1 - man+"man1" + man/"man1" end # The directory where the formula's man2 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man2 - man+"man2" + man/"man2" end # The directory where the formula's man3 pages should be installed. @@ -700,42 +700,42 @@ class Formula # No `make install` available? # <pre>man3.install "man.3"</pre> def man3 - man+"man3" + man/"man3" end # The directory where the formula's man4 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man4 - man+"man4" + man/"man4" end # The directory where the formula's man5 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man5 - man+"man5" + man/"man5" end # The directory where the formula's man6 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man6 - man+"man6" + man/"man6" end # The directory where the formula's man7 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man7 - man+"man7" + man/"man7" end # The directory where the formula's man8 pages should be installed. # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def man8 - man+"man8" + man/"man8" end # The directory where the formula's `sbin` binaries should be installed. @@ -743,7 +743,7 @@ class Formula # `brew link` for formulae that are not keg-only. # Generally we try to migrate these to {#bin} instead. def sbin - prefix+"sbin" + prefix/"sbin" end # The directory where the formula's shared files should be installed. @@ -762,7 +762,7 @@ class Formula # Install `./example_code/simple/ones` to share/demos/examples # <pre>(share/"demos").install "example_code/simple/ones" => "examples"</pre> def share - prefix+"share" + prefix/"share" end # The directory where the formula's shared files should be installed, @@ -773,7 +773,7 @@ class Formula # No `make install` available? # <pre>pkgshare.install "examples"</pre> def pkgshare - prefix+"share"+name + prefix/"share"/name end # The directory where Emacs Lisp files should be installed, with the @@ -782,7 +782,7 @@ class Formula # Install an Emacs mode included with a software package: # <pre>elisp.install "contrib/emacs/example-mode.el"</pre> def elisp - prefix+"share/emacs/site-lisp"+name + prefix/"share/emacs/site-lisp"/name end # The directory where the formula's Frameworks should be installed. @@ -790,7 +790,7 @@ class Formula # `brew link` for formulae that are not keg-only. # This is not symlinked into `HOMEBREW_PREFIX`. def frameworks - prefix+"Frameworks" + prefix/"Frameworks" end # The directory where the formula's kernel extensions should be installed. @@ -798,7 +798,7 @@ class Formula # `brew link` for formulae that are not keg-only. # This is not symlinked into `HOMEBREW_PREFIX`. def kext_prefix - prefix+"Library/Extensions" + prefix/"Library/Extensions" end # The directory where the formula's configuration files should be installed. @@ -807,14 +807,14 @@ class Formula # This directory is not inside the `HOMEBREW_CELLAR` so it is persisted # across upgrades. def etc - (HOMEBREW_PREFIX+"etc").extend(InstallRenamed) + (HOMEBREW_PREFIX/"etc").extend(InstallRenamed) end # The directory where the formula's variable files should be installed. # This directory is not inside the `HOMEBREW_CELLAR` so it is persisted # across upgrades. def var - HOMEBREW_PREFIX+"var" + HOMEBREW_PREFIX/"var" end # The directory where the formula's ZSH function files should be @@ -822,7 +822,7 @@ class Formula # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def zsh_function - share+"zsh/site-functions" + share/"zsh/site-functions" end # The directory where the formula's fish function files should be @@ -830,7 +830,7 @@ class Formula # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def fish_function - share+"fish/vendor_functions.d" + share/"fish/vendor_functions.d" end # The directory where the formula's Bash completion files should be @@ -838,7 +838,7 @@ class Formula # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def bash_completion - prefix+"etc/bash_completion.d" + prefix/"etc/bash_completion.d" end # The directory where the formula's ZSH completion files should be @@ -846,7 +846,7 @@ class Formula # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def zsh_completion - share+"zsh/site-functions" + share/"zsh/site-functions" end # The directory where the formula's fish completion files should be @@ -854,7 +854,7 @@ class Formula # This is symlinked into `HOMEBREW_PREFIX` after installation or with # `brew link` for formulae that are not keg-only. def fish_completion - share+"fish/vendor_completions.d" + share/"fish/vendor_completions.d" end # The directory used for as the prefix for {#etc} and {#var} files on @@ -862,13 +862,13 @@ class Formula # there after pouring a bottle. # @private def bottle_prefix - prefix+".bottle" + prefix/".bottle" end # The directory where the formula's installation or test logs will be written. # @private def logs - HOMEBREW_LOGS+name + HOMEBREW_LOGS + name end # The prefix, if any, to use in filenames for logging current activity @@ -921,12 +921,12 @@ class Formula # The generated launchd {.plist} service name. def plist_name - "homebrew.mxcl."+name + "homebrew.mxcl." + name end # The generated launchd {.plist} file path. def plist_path - prefix+(plist_name+".plist") + prefix + (plist_name + ".plist") end # @private @@ -951,39 +951,39 @@ class Formula end def opt_bin - opt_prefix+"bin" + opt_prefix/"bin" end def opt_include - opt_prefix+"include" + opt_prefix/"include" end def opt_lib - opt_prefix+"lib" + opt_prefix/"lib" end def opt_libexec - opt_prefix+"libexec" + opt_prefix/"libexec" end def opt_sbin - opt_prefix+"sbin" + opt_prefix/"sbin" end def opt_share - opt_prefix+"share" + opt_prefix/"share" end def opt_pkgshare - opt_prefix+"share"+name + opt_prefix/"share"/name end def opt_elisp - opt_prefix+"share/emacs/site-lisp"+name + opt_prefix/"share/emacs/site-lisp"/name end def opt_frameworks - opt_prefix+"Frameworks" + opt_prefix/"Frameworks" end # Indicates that this formula supports bottles. (Not necessarily that one @@ -1553,11 +1553,11 @@ class Formula def missing_dependencies(hide: nil) hide ||= [] missing_dependencies = recursive_dependencies do |dependent, dep| - if dep.optional? || dep.recommended? + if dep.build? + Dependency.prune + elsif dep.optional? || dep.recommended? tab = Tab.for_formula(dependent) Dependency.prune unless tab.with?(dep) - elsif dep.build? - Dependency.prune end end @@ -1588,7 +1588,7 @@ class Formula "revision" => revision, "version_scheme" => version_scheme, "installed" => [], - "linked_keg" => (linked_keg.resolved_path.basename.to_s if linked_keg.exist?), + "linked_keg" => (linked_version.to_s if linked_keg.exist?), "pinned" => pinned?, "outdated" => outdated?, "keg_only" => keg_only?, @@ -1673,11 +1673,13 @@ class Formula old_temp = ENV["TEMP"] old_tmp = ENV["TMP"] old_term = ENV["TERM"] - old_path = ENV["HOMEBREW_PATH"] + old_path = ENV["PATH"] + old_homebrew_path = ENV["HOMEBREW_PATH"] ENV["CURL_HOME"] = old_curl_home || old_home ENV["TMPDIR"] = ENV["TEMP"] = ENV["TMP"] = HOMEBREW_TEMP ENV["TERM"] = "dumb" + ENV["PATH"] = PATH.new(old_path).append(HOMEBREW_PREFIX/"bin") ENV["HOMEBREW_PATH"] = nil ENV.clear_sensitive_environment! @@ -1704,7 +1706,8 @@ class Formula ENV["TEMP"] = old_temp ENV["TMP"] = old_tmp ENV["TERM"] = old_term - ENV["HOMEBREW_PATH"] = old_path + ENV["PATH"] = old_path + ENV["HOMEBREW_PATH"] = old_homebrew_path @prefix_returns_versioned_prefix = false end @@ -1719,7 +1722,7 @@ class Formula # @private def test_fixtures(file) - HOMEBREW_LIBRARY_PATH.join("test", "support", "fixtures", file) + HOMEBREW_LIBRARY_PATH/"test/support/fixtures"/file end # This method is overridden in {Formula} subclasses to provide the installation instructions. @@ -1784,7 +1787,7 @@ class Formula pretty_args[i] = "import setuptools..." end end - ohai "#{cmd} #{pretty_args*" "}".strip + ohai "#{cmd} #{pretty_args * " "}".strip @exec_count ||= 0 @exec_count += 1 @@ -1943,8 +1946,10 @@ class Formula old_curl_home = ENV["CURL_HOME"] old_path = ENV["HOMEBREW_PATH"] - ENV["HOME"] = env_home - ENV["CURL_HOME"] = old_curl_home || old_home + unless ARGV.interactive? + ENV["HOME"] = env_home + ENV["CURL_HOME"] = old_curl_home || old_home + end ENV["HOMEBREW_PATH"] = nil setup_home env_home @@ -1955,8 +1960,10 @@ class Formula yield staging ensure @buildpath = nil - ENV["HOME"] = old_home - ENV["CURL_HOME"] = old_curl_home + unless ARGV.interactive? + ENV["HOME"] = old_home + ENV["CURL_HOME"] = old_curl_home + end ENV["HOMEBREW_PATH"] = old_path end end @@ -1993,7 +2000,7 @@ class Formula # The methods below define the formula DSL. class << self - include BuildEnvironmentDSL + include BuildEnvironment::DSL # The reason for why this software is not linked (by default) to # {::HOMEBREW_PREFIX}. @@ -2362,7 +2369,7 @@ class Formula end # If this formula conflicts with another one. - # <pre>conflicts_with "imagemagick", :because => "because this is just a stupid example"</pre> + # <pre>conflicts_with "imagemagick", :because => "because both install 'convert' binaries"</pre> def conflicts_with(*names) opts = names.last.is_a?(Hash) ? names.pop : {} names.each { |name| conflicts << FormulaConflict.new(name, opts[:because]) } diff --git a/Library/Homebrew/formula_cellar_checks.rb b/Library/Homebrew/formula_cellar_checks.rb index 7f7d77569..4718fc1a0 100644 --- a/Library/Homebrew/formula_cellar_checks.rb +++ b/Library/Homebrew/formula_cellar_checks.rb @@ -20,7 +20,7 @@ module FormulaCellarChecks def check_manpages # Check for man pages that aren't in share/man - return unless (formula.prefix+"man").directory? + return unless (formula.prefix/"man").directory? <<-EOS.undent A top-level "man" directory was found @@ -31,7 +31,7 @@ module FormulaCellarChecks def check_infopages # Check for info pages that aren't in share/info - return unless (formula.prefix+"info").directory? + return unless (formula.prefix/"info").directory? <<-EOS.undent A top-level "info" directory was found @@ -61,9 +61,9 @@ module FormulaCellarChecks valid_extensions = %w[.a .dylib .framework .jnilib .la .o .so .jar .prl .pm .sh] - non_libraries = formula.lib.children.select do |g| - next if g.directory? - !valid_extensions.include? g.extname + non_libraries = formula.lib.children.reject do |g| + next true if g.directory? + valid_extensions.include? g.extname end return if non_libraries.empty? diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index db9115aae..fa3096542 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -197,7 +197,7 @@ class FormulaInstaller return if pinned_unsatisfied_deps.empty? raise CannotInstallFormulaError, - "You must `brew unpin #{pinned_unsatisfied_deps*" "}` as installing #{formula.full_name} requires the latest version of pinned dependencies" + "You must `brew unpin #{pinned_unsatisfied_deps * " "}` as installing #{formula.full_name} requires the latest version of pinned dependencies" end def build_bottle_preinstall @@ -218,16 +218,16 @@ class FormulaInstaller # relink the active keg if possible (because it is slow). if formula.linked_keg.directory? message = <<-EOS.undent - #{formula.name} #{formula.linked_keg.resolved_path.basename} is already installed + #{formula.name} #{formula.linked_version} is already installed EOS message += if formula.outdated? && !formula.head? <<-EOS.undent - To upgrade to #{formula.version}, run `brew upgrade #{formula.name}` + To upgrade to #{formula.pkg_version}, run `brew upgrade #{formula.name}` EOS else # some other version is already installed *and* linked <<-EOS.undent - To install #{formula.version}, first run `brew unlink #{formula.name}` + To install #{formula.pkg_version}, first run `brew unlink #{formula.name}` EOS end raise CannotInstallFormulaError, message @@ -472,7 +472,7 @@ class FormulaInstaller def effective_build_options_for(dependent, inherited_options = []) args = dependent.build.used_options - args |= dependent == formula ? options : inherited_options + args |= (dependent == formula) ? options : inherited_options args |= Tab.for_formula(dependent).used_options args &= dependent.options BuildOptions.new(args, dependent.options) @@ -656,13 +656,15 @@ class FormulaInstaller Sandbox.print_sandbox_message if Sandbox.formula?(formula) Utils.safe_fork do - # Invalidate the current sudo timestamp in case a build script calls sudo - system "/usr/bin/sudo", "-k" + # 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. + system "/usr/bin/sudo", "-k" unless ENV["TRAVIS_SUDO"] == "false" if Sandbox.formula?(formula) sandbox = Sandbox.new formula.logs.mkpath sandbox.record_log(formula.logs/"build.sandbox.log") + sandbox.allow_write_path(ENV["HOME"]) if ARGV.interactive? sandbox.allow_write_temp_and_cache sandbox.allow_write_log(formula) sandbox.allow_write_xcode @@ -678,7 +680,6 @@ class FormulaInstaller if !formula.prefix.directory? || Keg.new(formula.prefix).empty_installation? raise "Empty installation" end - rescue Exception ignore_interrupts do # any exceptions must leave us with nothing installed diff --git a/Library/Homebrew/formula_pin.rb b/Library/Homebrew/formula_pin.rb index 0558c714c..c31280800 100644 --- a/Library/Homebrew/formula_pin.rb +++ b/Library/Homebrew/formula_pin.rb @@ -11,7 +11,7 @@ class FormulaPin def pin_at(version) HOMEBREW_PINNED_KEGS.mkpath - version_path = @f.rack.join(version) + version_path = @f.rack/version path.make_relative_symlink(version_path) unless pinned? || !version_path.exist? end diff --git a/Library/Homebrew/formula_versions.rb b/Library/Homebrew/formula_versions.rb index 70706a2f0..bb6803567 100644 --- a/Library/Homebrew/formula_versions.rb +++ b/Library/Homebrew/formula_versions.rb @@ -17,6 +17,7 @@ class FormulaVersions @repository = formula.tap.path @entry_name = @path.relative_path_from(repository).to_s @current_formula = formula + @formula_at_revision = {} end def rev_list(branch) @@ -32,20 +33,20 @@ class FormulaVersions end def formula_at_revision(rev) - contents = file_contents_at_revision(rev) - - begin - Homebrew.raise_deprecation_exceptions = true - nostdout { yield Formulary.from_contents(name, path, contents) } - rescue *IGNORED_EXCEPTIONS => e - # We rescue these so that we can skip bad versions and - # continue walking the history - ohai "#{e} in #{name} at revision #{rev}", e.backtrace if ARGV.debug? - rescue FormulaUnavailableError - # Suppress this error - ensure - Homebrew.raise_deprecation_exceptions = false + Homebrew.raise_deprecation_exceptions = true + + yield @formula_at_revision[rev] ||= begin + contents = file_contents_at_revision(rev) + nostdout { Formulary.from_contents(name, path, contents) } end + rescue *IGNORED_EXCEPTIONS => e + # We rescue these so that we can skip bad versions and + # continue walking the history + ohai "#{e} in #{name} at revision #{rev}", e.backtrace if ARGV.debug? + rescue FormulaUnavailableError + # Suppress this error + ensure + Homebrew.raise_deprecation_exceptions = false end def bottle_version_map(branch) @@ -63,6 +64,26 @@ class FormulaVersions map end + def previous_version_and_checksum(branch) + map = {} + + rev_list(branch) do |rev| + formula_at_revision(rev) do |f| + [:stable, :devel].each do |spec_sym| + next unless spec = f.send(spec_sym) + map[spec_sym] ||= { version: spec.version, checksum: spec.checksum } + end + end + + break if map[:stable] || map[:devel] + end + + map[:stable] ||= {} + map[:devel] ||= {} + + map + end + def version_attributes_map(attributes, branch) attributes_map = {} return attributes_map if attributes.empty? diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index d4b9c5d77..b52269e30 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -1,6 +1,6 @@ require "extend/pathname" require "keg_relocate" -require "formula_lock" +require "lock_file" require "ostruct" class Keg @@ -259,7 +259,7 @@ class Keg dirs = [] - TOP_LEVEL_DIRECTORIES.map { |d| path.join(d) }.each do |dir| + TOP_LEVEL_DIRECTORIES.map { |d| path/d }.each do |dir| next unless dir.exist? dir.find do |src| dst = HOMEBREW_PREFIX + src.relative_path_from(path) @@ -301,7 +301,7 @@ class Keg def completion_installed?(shell) dir = case shell - when :bash then path.join("etc", "bash_completion.d") + when :bash then path/"etc/bash_completion.d" when :zsh dir = path/"share/zsh/site-functions" dir if dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") } @@ -328,7 +328,7 @@ class Keg end def python_site_packages_installed? - path.join("lib", "python2.7", "site-packages").directory? + (path/"lib/python2.7/site-packages").directory? end def python_pth_files_installed? @@ -367,7 +367,7 @@ class Keg dep_formula = Formulary.factory(dep["full_name"]) dep_formula == to_formula rescue FormulaUnavailableError - next "#{tap}/#{name}" == dep["full_name"] + next dep["full_name"] == "#{tap}/#{name}" end end end @@ -468,7 +468,10 @@ class Keg end def aliases - Formulary.from_rack(rack).aliases + formula = Formulary.from_rack(rack) + aliases = formula.aliases + return aliases if formula.stable? + aliases.reject { |a| a.include?("@") } rescue FormulaUnavailableError [] end @@ -565,7 +568,7 @@ class Keg # symlinks the contents of path+relative_dir recursively into #{HOMEBREW_PREFIX}/relative_dir def link_dir(relative_dir, mode) - root = path+relative_dir + root = path/relative_dir return unless root.exist? root.find do |src| next if src == root diff --git a/Library/Homebrew/keg_relocate.rb b/Library/Homebrew/keg_relocate.rb index 6044426ee..ad4c01021 100644 --- a/Library/Homebrew/keg_relocate.rb +++ b/Library/Homebrew/keg_relocate.rb @@ -68,17 +68,7 @@ class Keg relocation.old_cellar => relocation.new_cellar, relocation.old_repository => relocation.new_repository, } - - # Order matters here since `HOMEBREW_CELLAR` and `HOMEBREW_REPOSITORY` are - # children of `HOMEBREW_PREFIX` by default. - regexp = Regexp.union( - relocation.old_cellar, - relocation.old_repository, - relocation.old_prefix, - ) - - changed = s.gsub!(regexp, replacements) - + changed = s.gsub!(Regexp.union(replacements.keys), replacements) next unless changed changed_files += [first, *rest].map { |file| file.relative_path_from(path) } @@ -118,7 +108,7 @@ class Keg end def lib - path.join("lib") + path/"lib" end def text_files @@ -133,6 +123,7 @@ class Keg files = Set.new path.find.reject { |pn| 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 Metafiles::EXTENSIONS.include?(pn.extname) if pn.text_executable? text_files << pn diff --git a/Library/Homebrew/language/haskell.rb b/Library/Homebrew/language/haskell.rb index 4b7f88a99..f3758c2f5 100644 --- a/Library/Homebrew/language/haskell.rb +++ b/Library/Homebrew/language/haskell.rb @@ -47,7 +47,7 @@ module Language def cabal_install(*args) # cabal hardcodes 64 as the maximum number of parallel jobs # https://github.com/Homebrew/legacy-homebrew/issues/49509 - make_jobs = ENV.make_jobs > 64 ? 64 : ENV.make_jobs + make_jobs = (ENV.make_jobs > 64) ? 64 : ENV.make_jobs # cabal-install's dependency-resolution backtracking strategy can easily # need more than the default 2,000 maximum number of "backjumps," since diff --git a/Library/Homebrew/language/node.rb b/Library/Homebrew/language/node.rb index ab2206a5b..b9abaea54 100644 --- a/Library/Homebrew/language/node.rb +++ b/Library/Homebrew/language/node.rb @@ -4,6 +4,17 @@ module Language "cache=#{HOMEBREW_CACHE}/npm_cache\n" end + def self.pack_for_installation + # Homebrew assumes the buildpath/testpath will always be disposable + # and from npm 5.0.0 the logic changed so that when a directory is + # fed to `npm install` only symlinks are created linking back to that + # directory, consequently breaking that assumption. We require a tarball + # because npm install creates a "real" installation when fed a tarball. + output = Utils.popen_read("npm pack").chomp + raise "npm failed to pack #{Dir.pwd}" unless $?.exitstatus.zero? + output + end + def self.setup_npm_environment npmrc = Pathname.new("#{ENV["HOME"]}/.npmrc") # only run setup_npm_environment once per formula @@ -14,7 +25,7 @@ module Language npmrc.write npm_cache_config # explicitly use our npm and node-gyp executables instead of the user # managed ones in HOMEBREW_PREFIX/lib/node_modules which might be broken - ENV.prepend_path "PATH", Formula["node"].opt_libexec/"npm/bin" + ENV.prepend_path "PATH", Formula["node"].opt_libexec/"bin" end def self.std_npm_install_args(libexec) @@ -22,8 +33,16 @@ module Language # tell npm to not install .brew_home by adding it to the .npmignore file # (or creating a new one if no .npmignore file already exists) open(".npmignore", "a") { |f| f.write("\n.brew_home\n") } + + pack = pack_for_installation + # npm install args for global style module format installed into libexec - ["--verbose", "--global", "--prefix=#{libexec}", "."] + %W[ + --verbose + --global + --prefix=#{libexec} + #{Dir.pwd}/#{pack} + ] end def self.local_npm_install_args diff --git a/Library/Homebrew/formula_lock.rb b/Library/Homebrew/lock_file.rb index bf747fea2..ffd29f817 100644 --- a/Library/Homebrew/formula_lock.rb +++ b/Library/Homebrew/lock_file.rb @@ -1,9 +1,9 @@ require "fcntl" -class FormulaLock +class LockFile def initialize(name) - @name = name - @path = HOMEBREW_LOCK_DIR/"#{@name}.brewing" + @name = name.to_s + @path = HOMEBREW_LOCK_DIR/"#{@name}.lock" @lockfile = nil end @@ -35,3 +35,15 @@ class FormulaLock @lockfile.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end end + +class FormulaLock < LockFile + def initialize(name) + super("#{name}.formula") + end +end + +class CaskLock < LockFile + def initialize(name) + super("#{name}.cask") + end +end diff --git a/Library/Homebrew/manpages/brew-cask.1.md b/Library/Homebrew/manpages/brew-cask.1.md index 41b04dbb7..bfb9cd7a5 100644 --- a/Library/Homebrew/manpages/brew-cask.1.md +++ b/Library/Homebrew/manpages/brew-cask.1.md @@ -87,12 +87,12 @@ names, and other aspects of this manual are still subject to change. given Cask. * `outdated` [--greedy] [--verbose|--quiet] [ <token> ...]: - Without token arguments, display all the installed Casks that have newer - versions available in the tap; otherwise check only the tokens given + Without token arguments, display all the installed Casks that have newer + versions available in the tap; otherwise check only the tokens given in the command line. - If `--greedy` is given then also include in the output the Casks having + If `--greedy` is given then also include in the output the Casks having `auto_updates true` or `version :latest`. Otherwise they are skipped - because there is no reliable way to know when updates are available for + because there is no reliable way to know when updates are available for them.<br> `--verbose` forces the display of the outdated and latest version.<br> `--quiet` suppresses the display of versions. @@ -145,7 +145,7 @@ names, and other aspects of this manual are still subject to change. ## OPTIONS -To make these options persistent, see the [ENVIRONMENT][] section, below. +To make these options persistent, see the [ENVIRONMENT](#environment) section, below. Some of these (such as `--prefpanedir`) may be subject to removal in a future version. @@ -160,9 +160,6 @@ in a future version. * `--require-sha`: Abort Cask installation if the Cask does not have a checksum defined. - * `--caskroom=<path>`: - Set location of the Caskroom, where all binaries are stored. The default value is `$(brew --prefix)/Caskroom`. - * `--verbose`: Give additional feedback during installation. @@ -255,7 +252,13 @@ Environment variables specific to Homebrew-Cask: the command-line. This is particularly useful to make options persistent. For example, you might add to your .bash_profile or .zshenv something like: - export HOMEBREW_CASK_OPTS='--appdir=/Applications --caskroom=/etc/Caskroom' + 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. + ## SEE ALSO diff --git a/Library/Homebrew/manpages/brew.1.md.erb b/Library/Homebrew/manpages/brew.1.md.erb index 7ae30bb16..0baa96755 100644 --- a/Library/Homebrew/manpages/brew.1.md.erb +++ b/Library/Homebrew/manpages/brew.1.md.erb @@ -98,8 +98,15 @@ can take several different forms: The formula file will be cached for later use. ## ENVIRONMENT + * `HOMEBREW_ARTIFACT_DOMAIN`: + If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries. + + * `HOMEBREW_AUTO_UPDATE_SECS`: + If set, Homebrew will only check for autoupdates once per this seconds interval. + + *Default:* `60`. - * `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`: + * `HOMEBREW_AWS_ACCESS_KEY_ID`, `HOMEBREW_AWS_SECRET_ACCESS_KEY`: When using the `S3` download strategy, Homebrew will look in these variables for access credentials (see <https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-environment> @@ -107,30 +114,9 @@ can take several different forms: the `S3` download strategy will download with a public (unsigned) URL. - * `BROWSER`: - If set, and `HOMEBREW_BROWSER` is not, use `BROWSER` as the web browser - when opening project homepages. - - * `EDITOR`: - If set, and `HOMEBREW_EDITOR` and `VISUAL` are not, use `EDITOR` as the text editor. - - * `GIT`: - When using Git, Homebrew will use `GIT` if set, - a Homebrew-built Git if installed, or the system-provided binary. - - Set this to force Homebrew to use a particular git binary. - * `HOMEBREW_BOTTLE_DOMAIN`: If set, instructs Homebrew to use the given URL as a download mirror for bottles. - * `HOMEBREW_ARTIFACT_DOMAIN`: - If set, instructs Homebrew to use the given URL as a download mirror for bottles and binaries. - - * `HOMEBREW_AUTO_UPDATE_SECS`: - If set, Homebrew will only check for autoupdates once per this seconds interval. - - *Default:* `60`. - * `HOMEBREW_BROWSER`: If set, uses this setting as the browser when opening project homepages, instead of the OS default browser. @@ -178,6 +164,12 @@ can take several different forms: If set, Homebrew will always use its vendored, relocatable Ruby 2.0 version even if the system version of Ruby is >=2.0. + * `HOMEBREW_GIT`: + When using Git, Homebrew will use `GIT` if set, + a Homebrew-built Git if installed, or the system-provided binary. + + Set this to force Homebrew to use a particular git binary. + * `HOMEBREW_GITHUB_API_TOKEN`: A personal access token for the GitHub API, which you can create at <https://github.com/settings/tokens>. If set, GitHub will allow you a @@ -243,9 +235,6 @@ can take several different forms: * `HOMEBREW_VERBOSE`: If set, Homebrew always assumes `--verbose` when running commands. - * `VISUAL`: - If set, and `HOMEBREW_EDITOR` is not, use `VISUAL` as the text editor. - ## USING HOMEBREW BEHIND A PROXY Homebrew uses several commands for downloading files (e.g. `curl`, `git`, `svn`). diff --git a/Library/Homebrew/migrator.rb b/Library/Homebrew/migrator.rb index 3cb6c5178..7ecdafe2f 100644 --- a/Library/Homebrew/migrator.rb +++ b/Library/Homebrew/migrator.rb @@ -1,5 +1,5 @@ require "formula" -require "formula_lock" +require "lock_file" require "keg" require "tab" @@ -248,7 +248,7 @@ class Migrator # Pathname#make_relative_symlink, where Pathname#relative_path_from # is used to find relative path from source to destination parent and # it assumes no symlinks. - src_oldname = old_pin_record.dirname.join(old_pin_link_record).expand_path + src_oldname = (old_pin_record.dirname/old_pin_link_record).expand_path new_pin_record.make_relative_symlink(src_oldname.sub(oldname, newname)) old_pin_record.delete end @@ -363,7 +363,7 @@ class Migrator backup_old_tabs if pinned? && !old_pin_record.symlink? - src_oldname = old_pin_record.dirname.join(old_pin_link_record).expand_path + src_oldname = (old_pin_record.dirname/old_pin_link_record).expand_path old_pin_record.make_relative_symlink(src_oldname) new_pin_record.delete end diff --git a/Library/Homebrew/missing_formula.rb b/Library/Homebrew/missing_formula.rb index eac3d7725..75498b128 100644 --- a/Library/Homebrew/missing_formula.rb +++ b/Library/Homebrew/missing_formula.rb @@ -126,10 +126,17 @@ module Homebrew relative_path = path.relative_path_from tap.path tap.path.cd do - ohai "Searching for a previously deleted formula..." unless silent + unless silent + ohai "Searching for a previously deleted formula..." + if (tap.path/".git/shallow").exist? + opoo <<-EOS.undent + #{tap} is shallow clone. To get complete history run: + git -C "$(brew --repo #{tap})" fetch --unshallow + + EOS + end + end - # We know this may return incomplete results for shallow clones but - # we don't want to nag everyone with a shallow clone to unshallow it. log_command = "git log --name-only --max-count=1 --format=%H\\\\n%h\\\\n%B -- #{relative_path}" hash, short_hash, *commit_message, relative_path = Utils.popen_read(log_command).gsub("\\n", "\n").lines.map(&:chomp) diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index dba2480ef..bc384da30 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -90,7 +90,7 @@ module OS @locator ||= SDKLocator.new begin sdk = if v.nil? - Xcode.version.to_i >= 7 ? @locator.latest_sdk : @locator.sdk_for(version) + (Xcode.version.to_i >= 7) ? @locator.latest_sdk : @locator.sdk_for(version) else @locator.sdk_for v end diff --git a/Library/Homebrew/os/mac/architecture_list.rb b/Library/Homebrew/os/mac/architecture_list.rb index 595c8f169..6f498c51f 100644 --- a/Library/Homebrew/os/mac/architecture_list.rb +++ b/Library/Homebrew/os/mac/architecture_list.rb @@ -28,12 +28,12 @@ module ArchitectureListExtension end def ppc? - (Hardware::CPU::PPC_32BIT_ARCHS+Hardware::CPU::PPC_64BIT_ARCHS).any? { |a| include? a } + (Hardware::CPU::PPC_32BIT_ARCHS + Hardware::CPU::PPC_64BIT_ARCHS).any? { |a| include? a } end # @private def remove_ppc! - (Hardware::CPU::PPC_32BIT_ARCHS+Hardware::CPU::PPC_64BIT_ARCHS).each { |a| delete a } + (Hardware::CPU::PPC_32BIT_ARCHS + Hardware::CPU::PPC_64BIT_ARCHS).each { |a| delete a } end def as_arch_flags diff --git a/Library/Homebrew/os/mac/linkage_checker.rb b/Library/Homebrew/os/mac/linkage_checker.rb index e72227fc4..6720ddcbe 100644 --- a/Library/Homebrew/os/mac/linkage_checker.rb +++ b/Library/Homebrew/os/mac/linkage_checker.rb @@ -62,10 +62,10 @@ class LinkageChecker declared_deps = formula.deps.reject { |dep| filter_out.call(dep) }.map(&:name) declared_requirement_deps = formula.requirements.reject { |req| filter_out.call(req) }.map(&:default_formula).compact declared_dep_names = (declared_deps + declared_requirement_deps).map { |dep| dep.split("/").last } - undeclared_deps = @brewed_dylibs.keys.select do |full_name| + undeclared_deps = @brewed_dylibs.keys.reject do |full_name| name = full_name.split("/").last - next false if name == formula.name - !declared_dep_names.include?(name) + next true if name == formula.name + declared_dep_names.include?(name) end undeclared_deps.sort do |a, b| if a.include?("/") && !b.include?("/") diff --git a/Library/Homebrew/patch.rb b/Library/Homebrew/patch.rb index 1b7751ba3..7f5a6af35 100644 --- a/Library/Homebrew/patch.rb +++ b/Library/Homebrew/patch.rb @@ -108,8 +108,14 @@ class StringPatch < EmbeddedPatch end class ExternalPatch + extend Forwardable + attr_reader :resource, :strip + def_delegators :resource, + :url, :fetch, :patch_files, :verify_download_integrity, :cached_download, + :clear_cache + def initialize(strip, &block) @strip = strip @resource = Resource::Patch.new(&block) @@ -148,30 +154,6 @@ class ExternalPatch end end - def url - resource.url - end - - def fetch - resource.fetch - end - - def patch_files - resource.patch_files - end - - def verify_download_integrity(fn) - resource.verify_download_integrity(fn) - end - - def cached_download - resource.cached_download - end - - def clear_cache - resource.clear_cache - end - def inspect "#<#{self.class.name}: #{strip.inspect} #{url.inspect}>" end diff --git a/Library/Homebrew/requirement.rb b/Library/Homebrew/requirement.rb index 6c20e7917..cac8c6232 100644 --- a/Library/Homebrew/requirement.rb +++ b/Library/Homebrew/requirement.rb @@ -159,7 +159,7 @@ class Requirement end class << self - include BuildEnvironmentDSL + include BuildEnvironment::DSL attr_reader :env_proc, :build attr_rw :fatal, :default_formula diff --git a/Library/Homebrew/requirements/java_requirement.rb b/Library/Homebrew/requirements/java_requirement.rb index 653846edd..ab6dca51d 100644 --- a/Library/Homebrew/requirements/java_requirement.rb +++ b/Library/Homebrew/requirements/java_requirement.rb @@ -46,7 +46,7 @@ class JavaRequirement < Requirement if exact_version? @version else - @version[0, @version.length-1] + @version[0, @version.length - 1] end end diff --git a/Library/Homebrew/rubocops.rb b/Library/Homebrew/rubocops.rb index 9dd365ee8..c4a38cdb7 100644 --- a/Library/Homebrew/rubocops.rb +++ b/Library/Homebrew/rubocops.rb @@ -2,3 +2,6 @@ require_relative "./rubocops/bottle_block_cop" require_relative "./rubocops/formula_desc_cop" require_relative "./rubocops/components_order_cop" require_relative "./rubocops/components_redundancy_cop" +require_relative "./rubocops/homepage_cop" +require_relative "./rubocops/text_cop" +require_relative "./rubocops/caveats_cop" diff --git a/Library/Homebrew/rubocops/caveats_cop.rb b/Library/Homebrew/rubocops/caveats_cop.rb new file mode 100644 index 000000000..3935d5638 --- /dev/null +++ b/Library/Homebrew/rubocops/caveats_cop.rb @@ -0,0 +1,16 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAudit + class Caveats < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, _body_node) + caveats_strings.each do |n| + next unless regex_match_group(n, /\bsetuid\b/i) + problem "Don't recommend setuid in the caveats, suggest sudo instead." + end + end + end + end + end +end diff --git a/Library/Homebrew/rubocops/components_order_cop.rb b/Library/Homebrew/rubocops/components_order_cop.rb index a6259133d..a63490395 100644 --- a/Library/Homebrew/rubocops/components_order_cop.rb +++ b/Library/Homebrew/rubocops/components_order_cop.rb @@ -36,7 +36,7 @@ module RuboCop [{ name: :test, type: :block_call }], ] - present_components = component_precedence_list.map do |components| + @present_components = component_precedence_list.map do |components| relevant_components = [] components.each do |component| case component[:type] @@ -51,20 +51,63 @@ module RuboCop relevant_components.delete_if(&:nil?) end - present_components = present_components.delete_if(&:empty?) - - present_components.each_cons(2) do |preceding_component, succeeding_component| - offensive_nodes = check_precedence(preceding_component, succeeding_component) - component_problem offensive_nodes[0], offensive_nodes[1] if offensive_nodes + # Check if each present_components is above rest of the present_components + @present_components.take(@present_components.size - 1).each_with_index do |preceding_component, p_idx| + next if preceding_component.empty? + @present_components.drop(p_idx + 1).each do |succeeding_component| + next if succeeding_component.empty? + @offensive_nodes = check_precedence(preceding_component, succeeding_component) + component_problem @offensive_nodes[0], @offensive_nodes[1] if @offensive_nodes + end end end private + # Method to format message for reporting component precedence violations def component_problem(c1, c2) - # Method to format message for reporting component precedence violations problem "`#{format_component(c1)}` (line #{line_number(c1)}) should be put before `#{format_component(c2)}` (line #{line_number(c2)})" end + + # autocorrect method gets called just after component_problem method call + def autocorrect(_node) + succeeding_node = @offensive_nodes[0] + preceding_node = @offensive_nodes[1] + lambda do |corrector| + reorder_components(corrector, succeeding_node, preceding_node) + end + end + + # Reorder two nodes in the source, using the corrector instance in autocorrect method + # Components of same type are grouped together when rewriting the source + # Linebreaks are introduced if components are of two different methods/blocks/multilines + def reorder_components(corrector, node1, node2) + # order_idx : node1's index in component_precedence_list + # curr_p_idx: node1's index in preceding_comp_arr + # 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 + node2 = preceding_comp_arr[curr_p_idx - 1] + indentation = " " * (start_column(node2) - line_start_column(node2)) + line_breaks = node2.multiline? ? "\n\n" : "\n" + corrector.insert_after(node2.source_range, line_breaks + indentation + node1.source) + else + indentation = " " * (start_column(node2) - line_start_column(node2)) + # No line breaks upto version_scheme, order_idx == 8 + line_breaks = (order_idx > 8) ? "\n\n" : "\n" + corrector.insert_before(node2.source_range, node1.source + line_breaks + indentation) + end + corrector.remove(range_with_surrounding_space(node1.source_range, :left)) + end + + # Returns precedence index and component's index to properly reorder and group during autocorrect + def get_state(node1) + @present_components.each_with_index do |comp, idx| + return [idx, comp.index(node1), comp] if comp.member?(node1) + end + end end end end diff --git a/Library/Homebrew/rubocops/extend/formula_cop.rb b/Library/Homebrew/rubocops/extend/formula_cop.rb index 4120be6ef..75a3e72d5 100644 --- a/Library/Homebrew/rubocops/extend/formula_cop.rb +++ b/Library/Homebrew/rubocops/extend/formula_cop.rb @@ -1,3 +1,5 @@ +require "parser/current" + module RuboCop module Cop class FormulaCop < Cop @@ -7,11 +9,11 @@ module RuboCop def on_class(node) file_path = processed_source.buffer.name return unless file_path_allowed?(file_path) - class_node, parent_class_node, body = *node - return unless formula_class?(parent_class_node) + return unless formula_class?(node) return unless respond_to?(:audit_formula) + class_node, parent_class_node, @body = *node @formula_name = class_name(class_node) - audit_formula(node, class_node, parent_class_node, body) + audit_formula(node, class_node, parent_class_node, @body) end # Checks for regex match of pattern in the node and @@ -22,7 +24,11 @@ module RuboCop return unless match_object node_begin_pos = start_column(node) line_begin_pos = line_start_column(node) - @column = node_begin_pos + match_object.begin(0) - line_begin_pos + 1 + if node_begin_pos == line_begin_pos + @column = node_begin_pos + match_object.begin(0) - line_begin_pos + else + @column = node_begin_pos + match_object.begin(0) - line_begin_pos + 1 + end @length = match_object.to_s.length @line_no = line_number(node) @source_buf = source_buffer(node) @@ -31,6 +37,12 @@ module RuboCop match_object end + # Returns all string nodes among the descendants of given node + def find_strings(node) + return [] if node.nil? + node.each_descendant(:str) + end + # Returns method_node matching method_name def find_node_method_by_name(node, method_name) return if node.nil? @@ -46,12 +58,100 @@ module RuboCop nil end - # Returns an array of method call nodes matching method_name inside node + # Set the given node as the offending node when required in custom cops + def offending_node(node) + @offensive_node = node + @offense_source_range = node.source_range + end + + # Returns an array of method call nodes matching method_name inside node with depth first order (Children nodes) def find_method_calls_by_name(node, method_name) return if node.nil? node.each_child_node(:send).select { |method_node| method_name == method_node.method_name } end + # Returns an array of method call nodes matching method_name in every descendant of node + def find_every_method_call_by_name(node, method_name) + return if node.nil? + node.each_descendant(:send).select { |method_node| method_name == method_node.method_name } + end + + # Given a method_name and arguments, yields to a block with + # matching method passed as a parameter to the block + def find_method_with_args(node, method_name, *args) + methods = find_every_method_call_by_name(node, method_name) + methods.each do |method| + next unless parameters_passed?(method, *args) + yield method + end + end + + # Matches a method with a receiver, + # EX: to match `Formula.factory(name)` + # call `find_instance_method_call(node, "Formula", :factory)` + # 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.const_name == instance + @offense_source_range = method.source_range + @offensive_node = method + yield method + end + end + + # Returns nil if does not depend on dependency_name + # args: node - dependency_name - dependency's name + def depends_on?(dependency_name) + dependency_nodes = find_every_method_call_by_name(@body, :depends_on) + idx = dependency_nodes.index do |n| + depends_on_name_type?(n, dependency_name, :required) || + depends_on_name_type?(n, dependency_name, :build) || + depends_on_name_type?(n, dependency_name, :optional) || + depends_on_name_type?(n, dependency_name, :recommended) || + depends_on_name_type?(n, dependency_name, :run) + end + return if idx.nil? + @offense_source_range = dependency_nodes[idx].source_range + @offensive_node = dependency_nodes[idx] + end + + # Returns true if given dependency name and dependency type exist in given dependency method call node + # TODO: Add case where key of hash is an array + def depends_on_name_type?(node, name = nil, type = :required) + if name + name_match = false + else + name_match = true # Match only by type when name is nil + end + + case type + when :required + type_match = !node.method_args.nil? && node.method_args.first.str_type? + if type_match && !name_match + name_match = node_equals?(node.method_args.first, 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 + if type_match && !name_match + name_match = node_equals?(node.method_args.first.keys.first.children.first, name) + end + end + + if type_match || name_match + @offensive_node = node + @offense_source_range = node.source_range + end + type_match && name_match + end + + # To compare node with appropriate Ruby variable + def node_equals?(node, var) + node == Parser::CurrentRuby.parse(var.inspect) + end + # Returns a block named block_name inside node def find_block(node, block_name) return if node.nil? @@ -112,6 +212,17 @@ module RuboCop false end + # Check if method_name is called among every descendant node of given node + def method_called_ever?(node, method_name) + node.each_descendant(:send) do |call_node| + next unless call_node.method_name == method_name + @offensive_node = call_node + @offense_source_range = call_node.source_range + return true + end + false + end + # Checks for precedence, returns the first pair of precedence violating nodes def check_precedence(first_nodes, next_nodes) next_nodes.each do |each_next_node| @@ -132,12 +243,35 @@ module RuboCop true end + # Return all the caveats' string nodes in an array + def caveats_strings + find_strings(find_method_def(@body, :caveats)) + end + # Returns the array of arguments of the method_node def parameters(method_node) return unless method_node.send_type? method_node.method_args end + # Returns true if the given parameters are present in method call + # and sets the method call as the offending node + # params can be string, symbol, array, hash, matching regex + def parameters_passed?(method_node, *params) + method_params = parameters(method_node) + @offensive_node = method_node + @offense_source_range = method_node.source_range + params.all? do |given_param| + method_params.any? do |method_param| + if given_param.class == Regexp + regex_match_group(method_param, given_param) + else + node_equals?(method_param, given_param) + end + end + end + end + # Returns the begin position of the node's line in source code def line_start_column(node) node.source_range.source_buffer.line_range(node.loc.line).begin_pos @@ -180,9 +314,18 @@ module RuboCop node.source_range.source_buffer end - # Returns the string representation if node is of type str + # Returns the string representation if node is of type str(plain) or dstr(interpolated) or const def string_content(node) - node.str_content if node.type == :str + case node.type + when :str + return node.str_content if node.type == :str + when :dstr + return node.each_child_node(:str).map(&:str_content).join("") if node.type == :dstr + when :const + return node.const_name if node.type == :const + else + "" + end end # Returns printable component name @@ -197,8 +340,9 @@ module RuboCop private - def formula_class?(parent_class_node) - parent_class_node && parent_class_node.const_name == "Formula" + def formula_class?(node) + _, class_node, = *node + class_node && string_content(class_node) == "Formula" end def file_path_allowed?(file_path) diff --git a/Library/Homebrew/rubocops/formula_desc_cop.rb b/Library/Homebrew/rubocops/formula_desc_cop.rb index 1fbf1ddbf..54a14b39d 100644 --- a/Library/Homebrew/rubocops/formula_desc_cop.rb +++ b/Library/Homebrew/rubocops/formula_desc_cop.rb @@ -32,15 +32,20 @@ module RuboCop # Check if command-line is wrongly used in formula's desc if match = regex_match_group(desc, /(command ?line)/i) - problem "Description should use \"command-line\" instead of \"#{match}\"" + c = match.to_s.chars.first + problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\"" end if match = regex_match_group(desc, /^(an?)\s/i) problem "Description shouldn't start with an indefinite article (#{match})" end + if regex_match_group(desc, /^[a-z]/) + problem "Description should start with a capital letter" + end + # Check if formula's name is used in formula's desc - problem "Description shouldn't include the formula name" if regex_match_group(desc, /^#{@formula_name}/i) + problem "Description shouldn't include the formula name" if regex_match_group(desc, /^#{@formula_name}\b/i) end end end diff --git a/Library/Homebrew/rubocops/homepage_cop.rb b/Library/Homebrew/rubocops/homepage_cop.rb new file mode 100644 index 000000000..a40c7b049 --- /dev/null +++ b/Library/Homebrew/rubocops/homepage_cop.rb @@ -0,0 +1,82 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAudit + # This cop audits `homepage` url in Formulae + class Homepage < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, formula_class_body_node) + homepage_node = find_node_method_by_name(formula_class_body_node, :homepage) + homepage = if homepage_node + string_content(parameters(homepage_node).first) + else + "" + end + + problem "Formula should have a homepage." if homepage_node.nil? || homepage.empty? + + unless homepage =~ %r{^https?://} + problem "The homepage should start with http or https (URL is #{homepage})." + end + + case homepage + # Check for http:// GitHub homepage urls, https:// is preferred. + # Note: only check homepages that are repo pages, not *.github.com hosts + when %r{^http://github.com/} + problem "Please use https:// for #{homepage}" + + # Savannah has full SSL/TLS support but no auto-redirect. + # Doesn't apply to the download URLs, only the homepage. + when %r{^http://savannah.nongnu.org/} + problem "Please use https:// for #{homepage}" + + # Freedesktop is complicated to handle - It has SSL/TLS, but only on certain subdomains. + # To enable https Freedesktop change the URL from http://project.freedesktop.org/wiki to + # https://wiki.freedesktop.org/project_name. + # "Software" is redirected to https://wiki.freedesktop.org/www/Software/project_name + when %r{^http://((?:www|nice|libopenraw|liboil|telepathy|xorg)\.)?freedesktop\.org/(?:wiki/)?} + if homepage =~ /Software/ + problem "#{homepage} should be styled `https://wiki.freedesktop.org/www/Software/project_name`" + else + problem "#{homepage} should be styled `https://wiki.freedesktop.org/project_name`" + end + + # Google Code homepages should end in a slash + when %r{^https?://code\.google\.com/p/[^/]+[^/]$} + problem "#{homepage} should end with a slash" + + # People will run into mixed content sometimes, but we should enforce and then add + # exemptions as they are discovered. Treat mixed content on homepages as a bug. + # Justify each exemptions with a code comment so we can keep track here. + + when %r{^http://[^/]*\.github\.io/}, + %r{^http://[^/]*\.sourceforge\.io/} + problem "Please use https:// for #{homepage}" + + when %r{^http://([^/]*)\.(sf|sourceforge)\.net(/|$)} + problem "#{homepage} should be `https://#{$1}.sourceforge.io/`" + + # There's an auto-redirect here, but this mistake is incredibly common too. + # Only applies to the homepage and subdomains for now, not the FTP URLs. + when %r{^http://((?:build|cloud|developer|download|extensions|git|glade|help|library|live|nagios|news|people|projects|rt|static|wiki|www)\.)?gnome\.org} + problem "Please use https:// for #{homepage}" + + # Compact the above into this list as we're able to remove detailed notations, etc over time. + when %r{^http://[^/]*\.apache\.org}, + %r{^http://packages\.debian\.org}, + %r{^http://wiki\.freedesktop\.org/}, + %r{^http://((?:www)\.)?gnupg\.org/}, + %r{^http://ietf\.org}, + %r{^http://[^/.]+\.ietf\.org}, + %r{^http://[^/.]+\.tools\.ietf\.org}, + %r{^http://www\.gnu\.org/}, + %r{^http://code\.google\.com/}, + %r{^http://bitbucket\.org/}, + %r{^http://(?:[^/]*\.)?archive\.org} + problem "Please use https:// for #{homepage}" + end + end + end + end + end +end diff --git a/Library/Homebrew/rubocops/text_cop.rb b/Library/Homebrew/rubocops/text_cop.rb new file mode 100644 index 000000000..d56c9bf46 --- /dev/null +++ b/Library/Homebrew/rubocops/text_cop.rb @@ -0,0 +1,55 @@ +require_relative "./extend/formula_cop" + +module RuboCop + module Cop + module FormulaAudit + class Text < FormulaCop + def audit_formula(_node, _class_node, _parent_class_node, body_node) + if !find_node_method_by_name(body_node, :plist_options) && + find_method_def(body_node, :plist) + problem "Please set plist_options when using a formula-defined plist." + end + + if depends_on?("openssl") && depends_on?("libressl") + problem "Formulae should not depend on both OpenSSL and LibreSSL (even optionally)." + end + + if method_called_ever?(body_node, :virtualenv_create) || + method_called_ever?(body_node, :virtualenv_install_with_resources) + find_method_with_args(body_node, :resource, "setuptools") do + problem "Formulae using virtualenvs do not need a `setuptools` resource." + end + end + + unless method_called_ever?(body_node, :go_resource) + # processed_source.ast is passed instead of body_node because `require` would be outside body_node + find_method_with_args(processed_source.ast, :require, "language/go") do + problem "require \"language/go\" is unnecessary unless using `go_resource`s" + end + end + + find_instance_method_call(body_node, "Formula", :factory) do + problem "\"Formula.factory(name)\" is deprecated in favor of \"Formula[name]\"" + end + + find_every_method_call_by_name(body_node, :xcodebuild).each do |m| + next if parameters_passed?(m, /SYMROOT=/) + problem 'xcodebuild should be passed an explicit "SYMROOT"' + end + + find_method_with_args(body_node, :system, "xcodebuild") do + problem %q(use "xcodebuild *args" instead of "system 'xcodebuild', *args") + end + + find_method_with_args(body_node, :system, "scons") do + problem "use \"scons *args\" instead of \"system 'scons', *args\"" + end + + find_method_with_args(body_node, :system, "go", "get") do + problem "Formulae should not use `go get`. If non-vendored resources are required use `go_resource`s." + end + end + end + end + end +end diff --git a/Library/Homebrew/sandbox.rb b/Library/Homebrew/sandbox.rb index 9597dafa8..e2ff84ac5 100644 --- a/Library/Homebrew/sandbox.rb +++ b/Library/Homebrew/sandbox.rb @@ -5,10 +5,6 @@ class Sandbox SANDBOX_EXEC = "/usr/bin/sandbox-exec".freeze SANDBOXED_TAPS = %w[ homebrew/core - homebrew/dupes - homebrew/fuse - homebrew/devel-only - homebrew/tex ].freeze def self.available? @@ -180,7 +176,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/scm/git b/Library/Homebrew/shims/scm/git index 82bb47c25..bfb779c25 100755 --- a/Library/Homebrew/shims/scm/git +++ b/Library/Homebrew/shims/scm/git @@ -86,7 +86,6 @@ fi case "$(lowercase "$SCM_FILE")" in git) [[ -n "$HOMEBREW_GIT" ]] && safe_exec "$(which "$HOMEBREW_GIT")" "$@" - [[ -n "$GIT" ]] && safe_exec "$(which "$GIT")" "$@" ;; svn) [[ -n "$HOMEBREW_SVN" ]] && safe_exec "$(which "$HOMEBREW_SVN")" "$@" diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index be851ca16..b6e6d1919 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -235,7 +235,7 @@ class Bottle end def suffix - s = rebuild > 0 ? ".#{rebuild}" : "" + s = (rebuild > 0) ? ".#{rebuild}" : "" ".bottle#{s}.tar.gz" end end diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index db4b1c585..6d0a3d6d1 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -23,7 +23,7 @@ class Tab < OpenStruct "homebrew_version" => HOMEBREW_VERSION, "used_options" => build.used_options.as_flags, "unused_options" => build.unused_options.as_flags, - "tabfile" => formula.prefix.join(FILENAME), + "tabfile" => formula.prefix/FILENAME, "built_as_bottle" => build.bottle?, "installed_as_dependency" => false, "installed_on_request" => true, @@ -98,7 +98,7 @@ class Tab < OpenStruct end def self.for_keg(keg) - path = keg.join(FILENAME) + path = keg/FILENAME tab = if path.exist? from_file(path) @@ -145,7 +145,7 @@ class Tab < OpenStruct paths << f.installed_prefix - path = paths.map { |pn| pn.join(FILENAME) }.find(&:file?) + path = paths.map { |pn| pn/FILENAME }.find(&:file?) if path tab = from_file(path) diff --git a/Library/Homebrew/tap_constants.rb b/Library/Homebrew/tap_constants.rb index 4ef8015c2..773eff816 100644 --- a/Library/Homebrew/tap_constants.rb +++ b/Library/Homebrew/tap_constants.rb @@ -3,7 +3,7 @@ HOMEBREW_TAP_FORMULA_REGEX = %r{^([\w-]+)/([\w-]+)/([\w+-.@]+)$} # match taps' casks, e.g. someuser/sometap/somecask HOMEBREW_TAP_CASK_REGEX = %r{^([\w-]+)/([\w-]+)/([a-z0-9\-]+)$} # match taps' directory paths, e.g. HOMEBREW_LIBRARY/Taps/someuser/sometap -HOMEBREW_TAP_DIR_REGEX = %r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/([\w-]+)/([\w-]+)} +HOMEBREW_TAP_DIR_REGEX = %r{#{Regexp.escape(HOMEBREW_LIBRARY)}/Taps/([\w-]+)/([\w-]+)} # match taps' formula paths, e.g. HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula HOMEBREW_TAP_PATH_REGEX = Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{/(.*)}.source) # match the default and the versions brew-cask tap e.g. Caskroom/cask or Caskroom/versions diff --git a/Library/Homebrew/test.rb b/Library/Homebrew/test.rb index ffffa1837..d9ec8250e 100644 --- a/Library/Homebrew/test.rb +++ b/Library/Homebrew/test.rb @@ -8,7 +8,7 @@ require "formula_assertions" require "fcntl" require "socket" -TEST_TIMEOUT_SECONDS = 5*60 +TEST_TIMEOUT_SECONDS = 5 * 60 begin error_pipe = UNIXSocket.open(ENV["HOMEBREW_ERROR_PIPE"], &:recv_io) diff --git a/Library/Homebrew/test/Gemfile b/Library/Homebrew/test/Gemfile index f3c16c710..dbe76b51c 100644 --- a/Library/Homebrew/test/Gemfile +++ b/Library/Homebrew/test/Gemfile @@ -2,11 +2,11 @@ source "https://rubygems.org" gem "parallel_tests" gem "rspec" -gem "rubocop" gem "rspec-its", require: false gem "rspec-wait", require: false +gem "rubocop" group :coverage do - gem "simplecov", require: false gem "codecov", require: false + gem "simplecov", require: false end diff --git a/Library/Homebrew/test/Gemfile.lock b/Library/Homebrew/test/Gemfile.lock index 4d4eefd7d..ccfd91542 100644 --- a/Library/Homebrew/test/Gemfile.lock +++ b/Library/Homebrew/test/Gemfile.lock @@ -2,51 +2,54 @@ GEM remote: https://rubygems.org/ specs: ast (2.3.0) - codecov (0.1.9) + codecov (0.1.10) json simplecov url diff-lcs (1.3) docile (1.1.5) - json (2.0.3) - parallel (1.10.0) - parallel_tests (2.13.0) + json (2.1.0) + parallel (1.11.2) + parallel_tests (2.14.1) parallel parser (2.4.0.0) ast (~> 2.2) powerpack (0.1.1) - rainbow (2.2.1) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) + rainbow (2.2.2) + rake + rake (12.0.0) + rspec (3.6.0) + rspec-core (~> 3.6.0) + rspec-expectations (~> 3.6.0) + rspec-mocks (~> 3.6.0) + rspec-core (3.6.0) + rspec-support (~> 3.6.0) + rspec-expectations (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) + rspec-support (~> 3.6.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.5.0) + rspec-mocks (3.6.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) + rspec-support (~> 3.6.0) + rspec-support (3.6.0) rspec-wait (0.0.9) rspec (>= 3, < 4) - rubocop (0.47.1) + rubocop (0.49.1) + parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) - simplecov (0.13.0) + simplecov (0.14.1) docile (~> 1.1.0) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) - unicode-display_width (1.1.3) + unicode-display_width (1.2.1) url (0.3.2) PLATFORMS @@ -62,4 +65,4 @@ DEPENDENCIES simplecov BUNDLED WITH - 1.14.5 + 1.14.6 diff --git a/Library/Homebrew/test/bash_spec.rb b/Library/Homebrew/test/bash_spec.rb index 1b0f15066..552607810 100644 --- a/Library/Homebrew/test/bash_spec.rb +++ b/Library/Homebrew/test/bash_spec.rb @@ -1,20 +1,20 @@ require "open3" -RSpec::Matchers.define :have_valid_bash_syntax do - match do |file| - stdout, stderr, status = Open3.capture3("/bin/bash", "-n", file) +describe "Bash" do + matcher :have_valid_bash_syntax do + match do |file| + stdout, stderr, status = Open3.capture3("/bin/bash", "-n", file) - @actual = [file, stderr] + @actual = [file, stderr] - stdout.empty? && status.success? - end + stdout.empty? && status.success? + end - failure_message do |(file, stderr)| - "expected that #{file} is a valid Bash file:\n#{stderr}" + failure_message do |(file, stderr)| + "expected that #{file} is a valid Bash file:\n#{stderr}" + end end -end -describe "Bash" do context "brew" do subject { HOMEBREW_LIBRARY_PATH.parent.parent/"bin/brew" } it { is_expected.to have_valid_bash_syntax } diff --git a/Library/Homebrew/test/bottle_hooks_spec.rb b/Library/Homebrew/test/bottle_hooks_spec.rb index 05c6ea7f0..913e3ffba 100644 --- a/Library/Homebrew/test/bottle_hooks_spec.rb +++ b/Library/Homebrew/test/bottle_hooks_spec.rb @@ -1,9 +1,9 @@ require "formula_installer" require "hooks/bottles" -RSpec::Matchers.alias_matcher :pour_bottle, :be_pour_bottle - describe Homebrew::Hooks::Bottles do + alias_matcher :pour_bottle, :be_pour_bottle + subject { FormulaInstaller.new formula } let(:formula) do diff --git a/Library/Homebrew/test/build_environment_spec.rb b/Library/Homebrew/test/build_environment_spec.rb index 5a3cec452..58bec6d1f 100644 --- a/Library/Homebrew/test/build_environment_spec.rb +++ b/Library/Homebrew/test/build_environment_spec.rb @@ -1,8 +1,8 @@ require "build_environment" -RSpec::Matchers.alias_matcher :use_userpaths, :be_userpaths - describe BuildEnvironment do + alias_matcher :use_userpaths, :be_userpaths + let(:env) { described_class.new } describe "#<<" do @@ -38,29 +38,29 @@ describe BuildEnvironment do expect(env).not_to use_userpaths end end -end -describe BuildEnvironmentDSL do - subject { double.extend(described_class) } + describe BuildEnvironment::DSL do + subject { double.extend(described_class) } - context "single argument" do - before(:each) do - subject.instance_eval do - env :userpaths + context "single argument" do + before(:each) do + subject.instance_eval do + env :userpaths + end end - end - its(:env) { is_expected.to use_userpaths } - end + its(:env) { is_expected.to use_userpaths } + end - context "multiple arguments" do - before(:each) do - subject.instance_eval do - env :userpaths, :std + context "multiple arguments" do + before(:each) do + subject.instance_eval do + env :userpaths, :std + end end - end - its(:env) { is_expected.to be_std } - its(:env) { is_expected.to use_userpaths } + its(:env) { is_expected.to be_std } + its(:env) { is_expected.to use_userpaths } + end end end diff --git a/Library/Homebrew/test/build_options_spec.rb b/Library/Homebrew/test/build_options_spec.rb index 5acc12f30..1e6c9ea35 100644 --- a/Library/Homebrew/test/build_options_spec.rb +++ b/Library/Homebrew/test/build_options_spec.rb @@ -1,10 +1,10 @@ require "build_options" require "options" -RSpec::Matchers.alias_matcher :be_built_with, :be_with -RSpec::Matchers.alias_matcher :be_built_without, :be_without - describe BuildOptions do + alias_matcher :be_built_with, :be_with + alias_matcher :be_built_without, :be_without + subject { described_class.new(args, opts) } let(:bad_build) { described_class.new(bad_args, opts) } let(:args) { Options.create(%w[--with-foo --with-bar --without-qux]) } diff --git a/Library/Homebrew/test/cask/artifact/binary_spec.rb b/Library/Homebrew/test/cask/artifact/binary_spec.rb index ee62e6439..f9b5f5b42 100644 --- a/Library/Homebrew/test/cask/artifact/binary_spec.rb +++ b/Library/Homebrew/test/cask/artifact/binary_spec.rb @@ -16,6 +16,20 @@ describe Hbc::Artifact::Binary, :cask do FileUtils.rm expected_path if expected_path.exist? end + context "when --no-binaries is specified" do + let(:cask) { + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/with-binary.rb") + } + + it "doesn't link the binary when --no-binaries is specified" do + shutup do + Hbc::Installer.new(cask, binaries: false).install + end + + expect(expected_path).not_to exist + end + end + it "links the binary to the proper directory" do shutup do Hbc::Artifact::Binary.new(cask).install_phase @@ -70,22 +84,6 @@ describe Hbc::Artifact::Binary, :cask do expect(File.readlink(expected_path)).not_to eq("/tmp") end - it "respects --no-binaries flag" do - begin - Hbc::CLI.binaries = false - - expect(Hbc::CLI).not_to be_binaries - - shutup do - Hbc::Artifact::Binary.new(cask).install_phase - end - - expect(expected_path.exist?).to be false - ensure - Hbc::CLI.binaries = true - end - end - it "creates parent directory if it doesn't exist" do FileUtils.rmdir Hbc.binarydir diff --git a/Library/Homebrew/test/cask/cli/audit_spec.rb b/Library/Homebrew/test/cask/cli/audit_spec.rb index 007ba1eb3..412db1481 100644 --- a/Library/Homebrew/test/cask/cli/audit_spec.rb +++ b/Library/Homebrew/test/cask/cli/audit_spec.rb @@ -1,59 +1,64 @@ describe Hbc::CLI::Audit, :cask do - let(:auditor) { double } let(:cask) { double } describe "selection of Casks to audit" do it "audits all Casks if no tokens are given" do allow(Hbc).to receive(:all).and_return([cask, cask]) - expect(auditor).to receive(:audit).twice + expect(Hbc::Auditor).to receive(:audit).twice.and_return(true) - run_audit([], auditor) + Hbc::CLI::Audit.run end it "audits specified Casks if tokens are given" do cask_token = "nice-app" expect(Hbc::CaskLoader).to receive(:load).with(cask_token).and_return(cask) - expect(auditor).to receive(:audit).with(cask, audit_download: false, check_token_conflicts: false) + expect(Hbc::Auditor).to receive(:audit) + .with(cask, audit_download: false, check_token_conflicts: false) + .and_return(true) - run_audit([cask_token], auditor) + Hbc::CLI::Audit.run(cask_token) end end describe "rules for downloading a Cask" do it "does not download the Cask per default" do allow(Hbc::CaskLoader).to receive(:load).and_return(cask) - expect(auditor).to receive(:audit).with(cask, audit_download: false, check_token_conflicts: false) + expect(Hbc::Auditor).to receive(:audit) + .with(cask, audit_download: false, check_token_conflicts: false) + .and_return(true) - run_audit(["casktoken"], auditor) + Hbc::CLI::Audit.run("casktoken") end it "download a Cask if --download flag is set" do allow(Hbc::CaskLoader).to receive(:load).and_return(cask) - expect(auditor).to receive(:audit).with(cask, audit_download: true, check_token_conflicts: false) + expect(Hbc::Auditor).to receive(:audit) + .with(cask, audit_download: true, check_token_conflicts: false) + .and_return(true) - run_audit(["casktoken", "--download"], auditor) + Hbc::CLI::Audit.run("casktoken", "--download") end end describe "rules for checking token conflicts" do it "does not check for token conflicts per default" do allow(Hbc::CaskLoader).to receive(:load).and_return(cask) - expect(auditor).to receive(:audit).with(cask, audit_download: false, check_token_conflicts: false) + expect(Hbc::Auditor).to receive(:audit) + .with(cask, audit_download: false, check_token_conflicts: false) + .and_return(true) - run_audit(["casktoken"], auditor) + Hbc::CLI::Audit.run("casktoken") end it "checks for token conflicts if --token-conflicts flag is set" do allow(Hbc::CaskLoader).to receive(:load).and_return(cask) - expect(auditor).to receive(:audit).with(cask, audit_download: false, check_token_conflicts: true) + expect(Hbc::Auditor).to receive(:audit) + .with(cask, audit_download: false, check_token_conflicts: true) + .and_return(true) - run_audit(["casktoken", "--token-conflicts"], auditor) + Hbc::CLI::Audit.run("casktoken", "--token-conflicts") end end - - def run_audit(args, auditor) - Hbc::CLI::Audit.new(args, auditor).run - end end diff --git a/Library/Homebrew/test/cask/cli/cat_spec.rb b/Library/Homebrew/test/cask/cli/cat_spec.rb index daf6fb960..28089b2f1 100644 --- a/Library/Homebrew/test/cask/cli/cat_spec.rb +++ b/Library/Homebrew/test/cask/cli/cat_spec.rb @@ -1,6 +1,6 @@ describe Hbc::CLI::Cat, :cask do describe "given a basic Cask" do - let(:expected_output) { + let(:basic_cask_content) { <<-EOS.undent cask 'basic-cask' do version '1.2.3' @@ -17,19 +17,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") - }.to output(expected_output).to_stdout + }.to output(basic_cask_content).to_stdout end - it "throws away additional Cask arguments and uses the first" do + it "can display multiple Casks" do expect { - Hbc::CLI::Cat.run("basic-cask", "local-caffeine") - }.to output(expected_output).to_stdout + Hbc::CLI::Cat.run("basic-cask", "basic-cask") + }.to output(basic_cask_content * 2).to_stdout end - it "throws away stray options" do + it "fails when option is unknown" do expect { Hbc::CLI::Cat.run("--notavalidoption", "basic-cask") - }.to output(expected_output).to_stdout + }.to raise_error(/invalid option/) end end @@ -51,7 +51,7 @@ describe Hbc::CLI::Cat, :cask do it "raises an exception" do expect { Hbc::CLI::Cat.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + }.to raise_error(/invalid option/) end end end diff --git a/Library/Homebrew/test/cask/cli/cleanup_spec.rb b/Library/Homebrew/test/cask/cli/cleanup_spec.rb index c7ef356c0..64e3ef49f 100644 --- a/Library/Homebrew/test/cask/cli/cleanup_spec.rb +++ b/Library/Homebrew/test/cask/cli/cleanup_spec.rb @@ -1,19 +1,24 @@ describe Hbc::CLI::Cleanup, :cask do let(:cache_location) { Pathname.new(Dir.mktmpdir).realpath } - let(:cleanup_outdated) { false } + let(:outdated_only) { false } - subject { described_class.new(cache_location, cleanup_outdated) } + subject { described_class.new(*cask_tokens, cache_location: cache_location) } + + before(:each) do + allow_any_instance_of(described_class).to receive(:outdated_only?).and_return(outdated_only) + end after do cache_location.rmtree end describe "cleanup" do - it "removes cached downloads of given casks" do - cleaned_up_cached_download = "caffeine" + let(:cask_token) { "caffeine" } + let(:cask_tokens) { [cask_token] } + it "removes cached downloads of given casks" do cached_downloads = [ - cache_location.join("#{cleaned_up_cached_download}--latest.zip"), + cache_location.join("#{cask_token}--latest.zip"), cache_location.join("transmission--2.61.dmg"), ] @@ -22,9 +27,9 @@ describe Hbc::CLI::Cleanup, :cask do cleanup_size = cached_downloads[0].disk_usage expect { - subject.cleanup(cleaned_up_cached_download) + subject.run }.to output(<<-EOS.undent).to_stdout - ==> Removing cached downloads for #{cleaned_up_cached_download} + ==> Removing cached downloads for #{cask_token} #{cached_downloads[0]} ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space. EOS @@ -32,61 +37,42 @@ describe Hbc::CLI::Cleanup, :cask do expect(cached_downloads[0].exist?).to eq(false) expect(cached_downloads[1].exist?).to eq(true) end - end - describe "cleanup!" do - it "removes cached downloads" do - cached_download = cache_location.join("SomeDownload.dmg") - FileUtils.touch(cached_download) - cleanup_size = subject.disk_cleanup_size + context "when no argument is given" do + let(:cask_tokens) { [] } - expect { - subject.cleanup! - }.to output(<<-EOS.undent).to_stdout - ==> Removing cached downloads - #{cached_download} - ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space. - EOS - - expect(cached_download.exist?).to eq(false) - end - - # TODO: uncomment when unflaky. - # it "does not removed locked files" do - # cached_download = cache_location.join("SomeDownload.dmg") - # FileUtils.touch(cached_download) - # cleanup_size = subject.disk_cleanup_size - # - # File.new(cached_download).flock(File::LOCK_EX) - # - # expect(Hbc::Utils).to be_file_locked(cached_download) - # - # expect { - # subject.cleanup! - # }.to output(<<-EOS.undent).to_stdout - # ==> Removing cached downloads - # skipping: #{cached_download} is locked - # ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space. - # EOS - # - # expect(cached_download.exist?).to eq(true) - # end - - context "when cleanup_outdated is specified" do - let(:cleanup_outdated) { true } - - it "does not remove cache files newer than 10 days old" do - cached_download = cache_location.join("SomeNewDownload.dmg") + it "removes all cached downloads" do + cached_download = cache_location.join("SomeDownload.dmg") FileUtils.touch(cached_download) + cleanup_size = subject.disk_cleanup_size expect { - subject.cleanup! + subject.run }.to output(<<-EOS.undent).to_stdout - ==> Removing cached downloads older than 10 days old - Nothing to do + ==> Removing cached downloads + #{cached_download} + ==> This operation has freed approximately #{disk_usage_readable(cleanup_size)} of disk space. EOS - expect(cached_download.exist?).to eq(true) + expect(cached_download.exist?).to eq(false) + end + + context "and :outdated_only is specified" do + let(:outdated_only) { true } + + it "does not remove cache files newer than 10 days old" do + cached_download = cache_location.join("SomeNewDownload.dmg") + FileUtils.touch(cached_download) + + expect { + subject.run + }.to output(<<-EOS.undent).to_stdout + ==> Removing cached downloads older than 10 days old + Nothing to do + EOS + + expect(cached_download.exist?).to eq(true) + end end end end diff --git a/Library/Homebrew/test/cask/cli/create_spec.rb b/Library/Homebrew/test/cask/cli/create_spec.rb index b1cee6990..d77b0a2aa 100644 --- a/Library/Homebrew/test/cask/cli/create_spec.rb +++ b/Library/Homebrew/test/cask/cli/create_spec.rb @@ -1,43 +1,26 @@ -# monkeypatch for testing -module Hbc - class CLI - class Create - def self.exec_editor(*command) - editor_commands << command - end - - def self.reset! - @editor_commands = [] - end - - def self.editor_commands - @editor_commands ||= [] +describe Hbc::CLI::Create, :cask do + around(:each) do |example| + begin + example.run + ensure + %w[new-cask additional-cask another-cask yet-another-cask local-caff].each do |cask| + FileUtils.rm_f Hbc::CaskLoader.path(cask) end end end -end -describe Hbc::CLI::Create, :cask do before(:each) do - Hbc::CLI::Create.reset! - end - - after(:each) do - %w[new-cask additional-cask another-cask yet-another-cask local-caff].each do |cask| - path = Hbc::CaskLoader.path(cask) - path.delete if path.exist? - end + allow_any_instance_of(described_class).to receive(:exec_editor) end it "opens the editor for the specified Cask" do - Hbc::CLI::Create.run("new-cask") - expect(Hbc::CLI::Create.editor_commands).to eq [ - [Hbc::CaskLoader.path("new-cask")], - ] + command = described_class.new("new-cask") + expect(command).to receive(:exec_editor).with(Hbc::CaskLoader.path("new-cask")) + command.run end it "drops a template down for the specified Cask" do - Hbc::CLI::Create.run("new-cask") + described_class.run("new-cask") template = File.read(Hbc::CaskLoader.path("new-cask")) expect(template).to eq <<-EOS.undent cask 'new-cask' do @@ -53,46 +36,43 @@ describe Hbc::CLI::Create, :cask do EOS end - it "throws away additional Cask arguments and uses the first" do - Hbc::CLI::Create.run("additional-cask", "another-cask") - expect(Hbc::CLI::Create.editor_commands).to eq [ - [Hbc::CaskLoader.path("additional-cask")], - ] - end - - it "throws away stray options" do - Hbc::CLI::Create.run("--notavalidoption", "yet-another-cask") - expect(Hbc::CLI::Create.editor_commands).to eq [ - [Hbc::CaskLoader.path("yet-another-cask")], - ] + 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./) end it "raises an exception when the Cask already exists" do expect { - Hbc::CLI::Create.run("basic-cask") + described_class.run("basic-cask") }.to raise_error(Hbc::CaskAlreadyCreatedError) end it "allows creating Casks that are substrings of existing Casks" do - Hbc::CLI::Create.run("local-caff") - expect(Hbc::CLI::Create.editor_commands).to eq [ - [Hbc::CaskLoader.path("local-caff")], - ] + command = described_class.new("local-caff") + 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 { - Hbc::CLI::Create.run + described_class.run }.to raise_error(Hbc::CaskUnspecifiedError) end end - describe "when no Cask is specified, but an invalid option" do + 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 { - Hbc::CLI::Create.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + described_class.run("--notavalidoption", "yet-another-cask") + }.to raise_error(/invalid option/) end end end diff --git a/Library/Homebrew/test/cask/cli/edit_spec.rb b/Library/Homebrew/test/cask/cli/edit_spec.rb index f5f98afc8..5d5cbf4b9 100644 --- a/Library/Homebrew/test/cask/cli/edit_spec.rb +++ b/Library/Homebrew/test/cask/cli/edit_spec.rb @@ -1,51 +1,30 @@ -# monkeypatch for testing -module Hbc - class CLI - class Edit - def self.exec_editor(*command) - editor_commands << command - end - - def self.reset! - @editor_commands = [] - end - - def self.editor_commands - @editor_commands ||= [] - end - end - end -end - describe Hbc::CLI::Edit, :cask do before(:each) do - Hbc::CLI::Edit.reset! + allow_any_instance_of(described_class).to receive(:exec_editor) end it "opens the editor for the specified Cask" do - Hbc::CLI::Edit.run("local-caffeine") - expect(Hbc::CLI::Edit.editor_commands).to eq [ - [Hbc::CaskLoader.path("local-caffeine")], - ] + command = described_class.new("local-caffeine") + expect(command).to receive(:exec_editor).with(Hbc::CaskLoader.path("local-caffeine")) + command.run end - it "throws away additional arguments and uses the first" do - Hbc::CLI::Edit.run("local-caffeine", "local-transmission") - expect(Hbc::CLI::Edit.editor_commands).to eq [ - [Hbc::CaskLoader.path("local-caffeine")], - ] + 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./) end it "raises an exception when the Cask doesnt exist" do expect { - Hbc::CLI::Edit.run("notacask") + described_class.run("notacask") }.to raise_error(Hbc::CaskUnavailableError) end describe "when no Cask is specified" do it "raises an exception" do expect { - Hbc::CLI::Edit.run + described_class.run }.to raise_error(Hbc::CaskUnspecifiedError) end end @@ -53,8 +32,8 @@ describe Hbc::CLI::Edit, :cask do describe "when no Cask is specified, but an invalid option" do it "raises an exception" do expect { - Hbc::CLI::Edit.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + 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 1571c2a70..9f3056631 100644 --- a/Library/Homebrew/test/cask/cli/fetch_spec.rb +++ b/Library/Homebrew/test/cask/cli/fetch_spec.rb @@ -69,7 +69,7 @@ describe Hbc::CLI::Fetch, :cask do it "raises an exception" do expect { Hbc::CLI::Fetch.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + }.to raise_error(/invalid option/) end end end diff --git a/Library/Homebrew/test/cask/cli/home_spec.rb b/Library/Homebrew/test/cask/cli/home_spec.rb index 7be26dd4c..e985fb6cd 100644 --- a/Library/Homebrew/test/cask/cli/home_spec.rb +++ b/Library/Homebrew/test/cask/cli/home_spec.rb @@ -1,46 +1,21 @@ -# monkeypatch for testing -module Hbc - class CLI - class Home - def self.system(*command) - system_commands << command - end - - def self.reset! - @system_commands = [] - end - - def self.system_commands - @system_commands ||= [] - end - end - end -end - describe Hbc::CLI::Home, :cask do before do - Hbc::CLI::Home.reset! + allow(described_class).to receive(:open_url) end it "opens the homepage for the specified Cask" do - Hbc::CLI::Home.run("local-caffeine") - expect(Hbc::CLI::Home.system_commands).to eq [ - ["/usr/bin/open", "--", "http://example.com/local-caffeine"], - ] + expect(described_class).to receive(:open_url).with("http://example.com/local-caffeine") + described_class.run("local-caffeine") end it "works for multiple Casks" do - Hbc::CLI::Home.run("local-caffeine", "local-transmission") - expect(Hbc::CLI::Home.system_commands).to eq [ - ["/usr/bin/open", "--", "http://example.com/local-caffeine"], - ["/usr/bin/open", "--", "http://example.com/local-transmission"], - ] + expect(described_class).to receive(:open_url).with("http://example.com/local-caffeine") + expect(described_class).to receive(:open_url).with("http://example.com/local-transmission") + described_class.run("local-caffeine", "local-transmission") end it "opens the project page when no Cask is specified" do - Hbc::CLI::Home.run - expect(Hbc::CLI::Home.system_commands).to eq [ - ["/usr/bin/open", "--", "https://caskroom.github.io/"], - ] + expect(described_class).to receive(:open_url).with("https://caskroom.github.io/") + described_class.run end end diff --git a/Library/Homebrew/test/cask/cli/info_spec.rb b/Library/Homebrew/test/cask/cli/info_spec.rb index 2f70a0b96..bffe900ec 100644 --- a/Library/Homebrew/test/cask/cli/info_spec.rb +++ b/Library/Homebrew/test/cask/cli/info_spec.rb @@ -45,7 +45,7 @@ describe Hbc::CLI::Info, :cask do it "throws away stray options" do expect { Hbc::CLI::Info.run("--notavalidoption", "local-caffeine", "local-transmission") - }.to output(expected_output).to_stdout + }.to raise_error(/invalid option/) end end @@ -102,7 +102,7 @@ describe Hbc::CLI::Info, :cask do it "raises an exception" do expect { Hbc::CLI::Info.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + }.to raise_error(/invalid option/) end end end diff --git a/Library/Homebrew/test/cask/cli/install_spec.rb b/Library/Homebrew/test/cask/cli/install_spec.rb index 219b9522e..b1b26c867 100644 --- a/Library/Homebrew/test/cask/cli/install_spec.rb +++ b/Library/Homebrew/test/cask/cli/install_spec.rb @@ -40,7 +40,7 @@ describe Hbc::CLI::Install, :cask do end expect { - Hbc::CLI::Install.run("local-transmission", "") + Hbc::CLI::Install.run("local-transmission") }.to output(/Warning: A Cask for local-transmission is already installed./).to_stderr end @@ -115,7 +115,11 @@ describe Hbc::CLI::Install, :cask do end describe "with an invalid option" do - with_options.call(["--notavalidoption"]) + it "raises an error" do + expect { + Hbc::CLI::Install.run("--notavalidoption") + }.to raise_error(/invalid option/) + end end end end diff --git a/Library/Homebrew/test/cask/cli/options_spec.rb b/Library/Homebrew/test/cask/cli/options_spec.rb index 44a391b48..98eb05f7e 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.process_options %w[help --appdir=/some/path/foo] + Hbc::CLI.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.process_options %w[help] + Hbc::CLI.new.process_options("help") expect(Hbc.appdir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the prefpanedir" do - Hbc::CLI.process_options %w[help --prefpanedir=/some/path/foo] + Hbc::CLI.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.process_options %w[help] + Hbc::CLI.new.process_options("help") expect(Hbc.prefpanedir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the qlplugindir" do - Hbc::CLI.process_options %w[help --qlplugindir=/some/path/foo] + Hbc::CLI.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.process_options %w[help] + Hbc::CLI.new.process_options("help") expect(Hbc.qlplugindir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the colorpickerdir" do - Hbc::CLI.process_options %w[help --colorpickerdir=/some/path/foo] + Hbc::CLI.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.process_options %w[help] + Hbc::CLI.new.process_options("help") expect(Hbc.colorpickerdir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the dictionarydir" do - Hbc::CLI.process_options %w[help --dictionarydir=/some/path/foo] + Hbc::CLI.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.process_options %w[help] + Hbc::CLI.new.process_options("help") expect(Hbc.dictionarydir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the fontdir" do - Hbc::CLI.process_options %w[help --fontdir=/some/path/foo] + Hbc::CLI.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.process_options %w[help] + Hbc::CLI.new.process_options("help") expect(Hbc.fontdir).to eq(Pathname.new("/some/path/bar")) end it "supports setting the servicedir" do - Hbc::CLI.process_options %w[help --servicedir=/some/path/foo] + Hbc::CLI.new.process_options("help", "--servicedir=/some/path/foo") expect(Hbc.servicedir).to eq(Pathname.new("/some/path/foo")) end @@ -92,53 +92,22 @@ describe Hbc::CLI, :cask do it "supports setting the servicedir from ENV" do ENV["HOMEBREW_CASK_OPTS"] = "--servicedir=/some/path/bar" - Hbc::CLI.process_options %w[help] + Hbc::CLI.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.process_options %w[edit foo --create --appdir=/some/path/qux] + rest = Hbc::CLI.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]) end - describe "when a mandatory argument is missing" do - it "shows a user-friendly error message" do - expect { - Hbc::CLI.process_options %w[install -f] - }.to raise_error(ArgumentError) - end - end - - describe "given an ambiguous option" do - it "shows a user-friendly error message" do - expect { - Hbc::CLI.process_options %w[edit -c] - }.to raise_error(ArgumentError) - end - end - - describe "--debug" do - it "sets the Cask debug method to true" do - begin - Hbc::CLI.process_options %w[help --debug] - expect(Hbc::CLI.debug?).to be true - ensure - Hbc::CLI.debug = false - end - end - end - describe "--help" do it "sets the Cask help method to true" do - begin - Hbc::CLI.process_options %w[foo --help] - expect(Hbc::CLI.help?).to be true - ensure - Hbc::CLI.help = false - end + command = Hbc::CLI.new("foo", "--help") + expect(command.help?).to be true end end end diff --git a/Library/Homebrew/test/cask/cli/outdated_spec.rb b/Library/Homebrew/test/cask/cli/outdated_spec.rb index a0f13009d..3d9e9bdeb 100644 --- a/Library/Homebrew/test/cask/cli/outdated_spec.rb +++ b/Library/Homebrew/test/cask/cli/outdated_spec.rb @@ -13,13 +13,13 @@ describe Hbc::CLI::Outdated, :cask do shutup do installed.each { |cask| InstallHelper.install_with_caskfile(cask) } end - allow(Hbc::CLI).to receive(:verbose?).and_return(true) + allow_any_instance_of(described_class).to receive(:verbose?).and_return(true) end 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 { - Hbc::CLI::Outdated.run + described_class.run }.to output(<<-EOS.undent).to_stdout local-caffeine (1.2.2) != 1.2.3 local-transmission (2.60) != 2.61 @@ -28,7 +28,7 @@ describe Hbc::CLI::Outdated, :cask do it "checks only the tokens specified in the command line" do expect { - Hbc::CLI::Outdated.run("local-caffeine") + described_class.run("local-caffeine") }.to output(<<-EOS.undent).to_stdout local-caffeine (1.2.2) != 1.2.3 EOS @@ -36,26 +36,32 @@ describe Hbc::CLI::Outdated, :cask do it 'ignores "auto_updates" and "latest" Casks even when their tokens are provided in the command line' do expect { - Hbc::CLI::Outdated.run("local-caffeine", "auto-updates", "version-latest-string") + described_class.run("local-caffeine", "auto-updates", "version-latest-string") }.to output(<<-EOS.undent).to_stdout local-caffeine (1.2.2) != 1.2.3 EOS end end - it "lists only the names (no versions) of the outdated Casks with --quiet" do - expect { - Hbc::CLI::Outdated.run("--quiet") - }.to output(<<-EOS.undent).to_stdout - local-caffeine - local-transmission - EOS + describe "--quiet overrides --verbose" do + before do + allow_any_instance_of(described_class).to receive(:verbose?).and_call_original + end + + it "lists only the names (no versions) of the outdated Casks with --quiet" do + expect { + described_class.run("--verbose", "--quiet") + }.to output(<<-EOS.undent).to_stdout + local-caffeine + local-transmission + EOS + end end describe "with --greedy it checks additional Casks" do it 'includes the Casks with "auto_updates true" or "version latest" with --greedy' do expect { - Hbc::CLI::Outdated.run("--greedy") + described_class.run("--greedy") }.to output(<<-EOS.undent).to_stdout auto-updates (2.57) != 2.61 local-caffeine (1.2.2) != 1.2.3 @@ -69,7 +75,7 @@ describe Hbc::CLI::Outdated, :cask do InstallHelper.install_with_caskfile(cask) expect { - Hbc::CLI::Outdated.run("--greedy") + described_class.run("--greedy") }.to output(<<-EOS.undent).to_stdout local-caffeine (1.2.2) != 1.2.3 local-transmission (2.60) != 2.61 diff --git a/Library/Homebrew/test/cask/cli/style_spec.rb b/Library/Homebrew/test/cask/cli/style_spec.rb index d41636beb..7d250c166 100644 --- a/Library/Homebrew/test/cask/cli/style_spec.rb +++ b/Library/Homebrew/test/cask/cli/style_spec.rb @@ -4,37 +4,12 @@ require "rubygems" describe Hbc::CLI::Style, :cask do let(:args) { [] } - let(:cli) { described_class.new(args) } + let(:cli) { described_class.new(*args) } around do |example| shutup { example.run } end - describe ".run" do - subject { described_class.run(args) } - - before do - allow(described_class).to receive(:new).and_return(cli) - allow(cli).to receive(:run).and_return(retval) - end - - context "when rubocop succeeds" do - let(:retval) { true } - - it "exits successfully" do - subject - end - end - - context "when rubocop fails" do - let(:retval) { false } - - it "raises an exception" do - expect { subject }.to raise_error(Hbc::CaskError) - end - end - end - describe "#run" do subject { cli.run } @@ -53,7 +28,10 @@ describe Hbc::CLI::Style, :cask do context "when rubocop fails" do let(:success) { false } - it { is_expected.to be_falsey } + + it "raises an error" do + expect { subject }.to raise_error(Hbc::CaskError) + end end end @@ -99,7 +77,7 @@ describe Hbc::CLI::Style, :cask do subject { cli.cask_paths } before do - allow(cli).to receive(:cask_tokens).and_return(tokens) + allow(cli).to receive(:args).and_return(tokens) end context "when no cask tokens are given" do @@ -136,40 +114,6 @@ describe Hbc::CLI::Style, :cask do end end - describe "#cask_tokens" do - subject { cli.cask_tokens } - - context "when no args are given" do - let(:args) { [] } - it { is_expected.to be_empty } - end - - context "when only flags are given" do - let(:args) { ["--fix"] } - it { is_expected.to be_empty } - end - - context "when only empty args are given" do - let(:args) { ["", ""] } - it { is_expected.to be_empty } - end - - context "when a cask token is given" do - let(:args) { ["adium"] } - it { is_expected.to eq(["adium"]) } - end - - context "when multiple cask tokens are given" do - let(:args) { %w[adium dropbox] } - it { is_expected.to eq(%w[adium dropbox]) } - end - - context "when cask tokens are given with flags" do - let(:args) { ["adium", "dropbox", "--fix"] } - it { is_expected.to eq(%w[adium dropbox]) } - end - end - describe "#rubocop_args" do subject { cli.rubocop_args } @@ -203,33 +147,4 @@ describe Hbc::CLI::Style, :cask do expect(subject).to include("--auto-correct", *default_args) end end - - describe "#fix?" do - subject { cli.fix? } - - context "when --fix is passed as an argument" do - let(:args) { ["adium", "--fix"] } - it { is_expected.to be_truthy } - end - - context "when --correct is passed as an argument" do - let(:args) { ["adium", "--correct"] } - it { is_expected.to be_truthy } - end - - context "when --auto-correct is passed as an argument" do - let(:args) { ["adium", "--auto-correct"] } - it { is_expected.to be_truthy } - end - - context "when --auto-correct is misspelled as --autocorrect" do - let(:args) { ["adium", "--autocorrect"] } - it { is_expected.to be_truthy } - end - - context "when no flag equivalent to --fix is passed as an argument" do - let(:args) { ["adium"] } - it { is_expected.to be_falsey } - end - end end diff --git a/Library/Homebrew/test/cask/cli/uninstall_spec.rb b/Library/Homebrew/test/cask/cli/uninstall_spec.rb index 4089c47b4..bc1f52613 100644 --- a/Library/Homebrew/test/cask/cli/uninstall_spec.rb +++ b/Library/Homebrew/test/cask/cli/uninstall_spec.rb @@ -203,7 +203,7 @@ describe Hbc::CLI::Uninstall, :cask do it "raises an exception" do expect { Hbc::CLI::Uninstall.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + }.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 58992deb5..f3af0e66f 100644 --- a/Library/Homebrew/test/cask/cli/zap_spec.rb +++ b/Library/Homebrew/test/cask/cli/zap_spec.rb @@ -18,8 +18,7 @@ describe Hbc::CLI::Zap, :cask do expect(transmission).to be_installed shutup do - Hbc::CLI::Zap.run("--notavalidoption", - "local-caffeine", "local-transmission") + Hbc::CLI::Zap.run("local-caffeine", "local-transmission") end expect(caffeine).not_to be_installed @@ -67,7 +66,7 @@ describe Hbc::CLI::Zap, :cask do it "raises an exception" do expect { Hbc::CLI::Zap.run("--notavalidoption") - }.to raise_error(Hbc::CaskUnspecifiedError) + }.to raise_error(/invalid option/) end end end diff --git a/Library/Homebrew/test/cask/cli_spec.rb b/Library/Homebrew/test/cask/cli_spec.rb index 9bf14fd89..569b831de 100644 --- a/Library/Homebrew/test/cask/cli_spec.rb +++ b/Library/Homebrew/test/cask/cli_spec.rb @@ -13,7 +13,14 @@ describe Hbc::CLI, :cask do ]) end - context ".process" do + context "when no option is specified" do + it "--binaries is true by default" do + command = Hbc::CLI::Install.new("some-cask") + expect(command.binaries?).to be true + end + end + + context "::run" do let(:noop_command) { double("CLI::Noop") } before do @@ -30,37 +37,28 @@ describe Hbc::CLI, :cask do version_command = double("CLI::Version") allow(described_class).to receive(:lookup_command).with("--version").and_return(version_command) expect(described_class).to receive(:run_command).with(version_command) - described_class.process(["--version"]) + described_class.run("--version") end it "prints help output when subcommand receives `--help` flag" do - begin - expect(described_class).to receive(:run_command).with("help") - described_class.process(%w[noop --help]) - expect(Hbc::CLI.help?).to eq(true) - ensure - Hbc::CLI.help = false - end + command = Hbc::CLI.new("noop", "--help") + expect(described_class).to receive(:run_command).with("help") + command.run + expect(command.help?).to eq(true) end it "respects the env variable when choosing what appdir to create" do allow(ENV).to receive(:[]) allow(ENV).to receive(:[]).with("HOMEBREW_CASK_OPTS").and_return("--appdir=/custom/appdir") expect(Hbc).to receive(:appdir=).with(Pathname.new("/custom/appdir")) - described_class.process("noop") - end - - it "respects the env variable when choosing a non-default Caskroom location" do - allow(ENV).to receive(:[]) - allow(ENV).to receive(:[]).with("HOMEBREW_CASK_OPTS").and_return("--caskroom=/custom/caskdir") - expect(Hbc).to receive(:caskroom=).with(Pathname.new("/custom/caskdir")) - described_class.process("noop") + described_class.run("noop") end it "exits with a status of 1 when something goes wrong" do allow(described_class).to receive(:lookup_command).and_raise(Hbc::CaskError) - expect(described_class).to receive(:exit).with(1) - described_class.process("noop") + command = Hbc::CLI.new("noop") + expect(command).to receive(:exit).with(1) + command.run end end diff --git a/Library/Homebrew/test/cask/download_strategy_spec.rb b/Library/Homebrew/test/cask/download_strategy_spec.rb index 91fe934be..27f1ad410 100644 --- a/Library/Homebrew/test/cask/download_strategy_spec.rb +++ b/Library/Homebrew/test/cask/download_strategy_spec.rb @@ -1,6 +1,6 @@ describe "download strategies", :cask do let(:url) { "http://example.com/cask.dmg" } - let(:url_options) { Hash.new } + let(:url_options) { {} } let(:cask) { instance_double(Hbc::Cask, token: "some-cask", url: Hbc::URL.new(url, url_options), diff --git a/Library/Homebrew/test/cask/dsl/appcast_spec.rb b/Library/Homebrew/test/cask/dsl/appcast_spec.rb new file mode 100644 index 000000000..b8903b1be --- /dev/null +++ b/Library/Homebrew/test/cask/dsl/appcast_spec.rb @@ -0,0 +1,74 @@ +require "cmd/cask" + +describe Hbc::DSL::Appcast do + subject { described_class.new(url, params) } + + let(:url) { "http://example.com" } + let(:uri) { Hbc::UnderscoreSupportingURI.parse(url) } + let(:params) { {} } + + describe "#to_s" do + it "returns the parsed URI string" do + expect(subject.to_s).to eq("http://example.com") + end + end + + describe "#to_yaml" do + let(:yaml) { [uri, params].to_yaml } + + context "with empty parameters" do + it "returns an YAML serialized array composed of the URI and parameters" do + expect(subject.to_yaml).to eq(yaml) + end + end + + context "with checkpoint in parameters" do + let(:params) { { checkpoint: "abc123" } } + + it "returns an YAML serialized array composed of the URI and parameters" do + expect(subject.to_yaml).to eq(yaml) + end + end + end + + describe "#calculate_checkpoint" do + before do + expect(Hbc::SystemCommand).to receive(:run).with(*cmd_args).and_return(cmd_result) + 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_result) { double("Hbc::SystemCommand::Result") } + let(:cmd_success) { true } + let(:cmd_stdout) { "hello world" } + + it "generates the content digest hash and returns a hash with the command result and the digest hash for the checkpoint" do + expected_digest = Digest::SHA2.hexdigest(cmd_stdout) + expected_result = { + checkpoint: expected_digest, + command_result: cmd_result, + } + + expect(subject.calculate_checkpoint).to eq(expected_result) + end + 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_result) { double("Hbc::SystemCommand::Result") } + let(:cmd_success) { false } + let(:cmd_stdout) { "some error message from the server" } + + it "returns a hash with the command result and nil for the checkpoint" do + expected_result = { + checkpoint: nil, + command_result: cmd_result, + } + + expect(subject.calculate_checkpoint).to eq(expected_result) + end + end + end +end diff --git a/Library/Homebrew/test/cask/installer_spec.rb b/Library/Homebrew/test/cask/installer_spec.rb index 59d61bbdd..0ae7c14a5 100644 --- a/Library/Homebrew/test/cask/installer_spec.rb +++ b/Library/Homebrew/test/cask/installer_spec.rb @@ -161,6 +161,19 @@ describe Hbc::Installer, :cask do expect(Hbc.appdir.join("container-lzma--#{asset.version}")).to be_a_file end + it "works with gpg-based Casks" do + skip("gpg not installed") if which("gpg").nil? + asset = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/container-gpg.rb") + + allow(asset).to receive(:depends_on).and_return(empty_depends_on_stub) + shutup do + Hbc::Installer.new(asset).install + end + + expect(Hbc.caskroom.join("container-gpg", asset.version)).to be_a_directory + expect(Hbc.appdir.join("container")).to be_a_file + end + it "blows up on a bad checksum" do bad_checksum = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/bad-checksum.rb") expect { diff --git a/Library/Homebrew/test/cmd/--env_spec.rb b/Library/Homebrew/test/cmd/--env_spec.rb index 7dd84132a..9ccdaa6f1 100644 --- a/Library/Homebrew/test/cmd/--env_spec.rb +++ b/Library/Homebrew/test/cmd/--env_spec.rb @@ -9,7 +9,7 @@ describe "brew --env", :integration_test do describe "--shell=bash" do it "prints the Homebrew build environment variables in Bash syntax" do expect { brew "--env", "--shell=bash" } - .to output(/export CMAKE_PREFIX_PATH="#{Regexp.quote(HOMEBREW_PREFIX.to_s)}"/).to_stdout + .to output(/export CMAKE_PREFIX_PATH="#{Regexp.quote(HOMEBREW_PREFIX)}"/).to_stdout .and not_to_output.to_stderr .and be_a_success end @@ -18,7 +18,7 @@ describe "brew --env", :integration_test do describe "--shell=fish" do it "prints the Homebrew build environment variables in Fish syntax" do expect { brew "--env", "--shell=fish" } - .to output(/set [-]gx CMAKE_PREFIX_PATH "#{Regexp.quote(HOMEBREW_PREFIX.to_s)}"/).to_stdout + .to output(/set [-]gx CMAKE_PREFIX_PATH "#{Regexp.quote(HOMEBREW_PREFIX)}"/).to_stdout .and not_to_output.to_stderr .and be_a_success end @@ -27,7 +27,7 @@ describe "brew --env", :integration_test do describe "--shell=tcsh" do it "prints the Homebrew build environment variables in Tcsh syntax" do expect { brew "--env", "--shell=tcsh" } - .to output(/setenv CMAKE_PREFIX_PATH #{Regexp.quote(HOMEBREW_PREFIX.to_s)};/).to_stdout + .to output(/setenv CMAKE_PREFIX_PATH #{Regexp.quote(HOMEBREW_PREFIX)};/).to_stdout .and not_to_output.to_stderr .and be_a_success end diff --git a/Library/Homebrew/test/cmd/--version_spec.rb b/Library/Homebrew/test/cmd/--version_spec.rb index 8992629d7..8dc183882 100644 --- a/Library/Homebrew/test/cmd/--version_spec.rb +++ b/Library/Homebrew/test/cmd/--version_spec.rb @@ -1,7 +1,7 @@ describe "brew --version", :integration_test do it "prints the Homebrew version" do expect { brew "--version" } - .to output(/^Homebrew #{Regexp.escape(HOMEBREW_VERSION.to_s)}\n/).to_stdout + .to output(/^Homebrew #{Regexp.escape(HOMEBREW_VERSION)}\n/).to_stdout .and not_to_output.to_stderr .and be_a_success end diff --git a/Library/Homebrew/test/cmd/install_spec.rb b/Library/Homebrew/test/cmd/install_spec.rb index b819c17be..6fff44131 100644 --- a/Library/Homebrew/test/cmd/install_spec.rb +++ b/Library/Homebrew/test/cmd/install_spec.rb @@ -23,7 +23,7 @@ describe "brew install", :integration_test do .and be_a_success expect { brew "install", "testball1" } - .to output(/testball1\-0\.1 already installed/).to_stderr + .to output(/testball1\ 0\.1 is already installed/).to_stderr .and not_to_output.to_stdout .and be_a_success @@ -51,7 +51,7 @@ describe "brew install", :integration_test do install_and_rename_coretap_formula "testball1", "testball2" expect { brew "install", "testball2" } .to output(/testball1 already installed, it's just not migrated/).to_stderr - .and output(/You can migrate formula with `brew migrate testball2`/).to_stdout + .and not_to_output.to_stdout .and be_a_success end @@ -106,8 +106,8 @@ describe "brew install", :integration_test do end expect { brew "install", "testball1" } - .to output(/already installed, however linked version is/).to_stderr - .and output(/`brew switch testball1 2.0`/).to_stdout + .to output(/2.0 is already installed/).to_stderr + .and not_to_output.to_stdout .and be_a_success expect { brew "unlink", "testball1" } @@ -141,8 +141,8 @@ describe "brew install", :integration_test do EOS expect { brew "install", "testball1" } - .to output(/keg-only and another version is linked to opt/).to_stderr - .and output(/Use `brew install --force`/).to_stdout + .to output(/testball1 1.0 is already installed/).to_stderr + .and not_to_output.to_stdout .and be_a_success expect { brew "install", "testball1", "--force" } @@ -185,7 +185,7 @@ describe "brew install", :integration_test do .and be_a_success expect { brew "install", "testball1", "--HEAD", "--ignore-dependencies" } - .to output(/testball1\-HEAD\-d5eb689 already installed/).to_stderr + .to output(/testball1 HEAD\-d5eb689 is already installed/).to_stderr .and not_to_output.to_stdout .and be_a_success diff --git a/Library/Homebrew/test/cmd/style_spec.rb b/Library/Homebrew/test/cmd/style_spec.rb new file mode 100644 index 000000000..3c4c3f809 --- /dev/null +++ b/Library/Homebrew/test/cmd/style_spec.rb @@ -0,0 +1,34 @@ +require "cmd/style" + +describe "brew style" do + around(:each) do |example| + begin + FileUtils.ln_s HOMEBREW_LIBRARY_PATH, HOMEBREW_LIBRARY/"Homebrew" + FileUtils.ln_s HOMEBREW_LIBRARY_PATH.parent/".rubocop.yml", HOMEBREW_LIBRARY/".rubocop.yml" + + example.run + ensure + FileUtils.rm_f HOMEBREW_LIBRARY/"Homebrew" + FileUtils.rm_f HOMEBREW_LIBRARY/".rubocop.yml" + end + end + + describe "Homebrew::check_style_json" do + let(:dir) { mktmpdir } + + it "returns RubocopResults when RuboCop reports offenses" do + formula = dir/"my-formula.rb" + + formula.write <<-'EOS'.undent + class MyFormula < Formula + + end + EOS + + rubocop_result = Homebrew.check_style_json([formula]) + + expect(rubocop_result.file_offenses(formula.realpath.to_s).map(&:message)) + .to include("Extra empty line detected at class body beginning.") + end + end +end diff --git a/Library/Homebrew/test/compiler_failure_spec.rb b/Library/Homebrew/test/compiler_failure_spec.rb index b4fab0b27..47b35d3bc 100644 --- a/Library/Homebrew/test/compiler_failure_spec.rb +++ b/Library/Homebrew/test/compiler_failure_spec.rb @@ -1,8 +1,8 @@ require "compilers" -RSpec::Matchers.alias_matcher :fail_with, :be_fails_with - describe CompilerFailure do + alias_matcher :fail_with, :be_fails_with + describe "::create" do it "creates a failure when given a symbol" do failure = described_class.create(:clang) diff --git a/Library/Homebrew/test/dependable_spec.rb b/Library/Homebrew/test/dependable_spec.rb index b646b7634..172305aa0 100644 --- a/Library/Homebrew/test/dependable_spec.rb +++ b/Library/Homebrew/test/dependable_spec.rb @@ -1,8 +1,8 @@ require "dependable" -RSpec::Matchers.alias_matcher :be_a_build_dependency, :be_build - describe Dependable do + alias_matcher :be_a_build_dependency, :be_build + subject { double(tags: tags).extend(described_class) } let(:tags) { ["foo", "bar", :build] } diff --git a/Library/Homebrew/test/dependency_collector_spec.rb b/Library/Homebrew/test/dependency_collector_spec.rb index 82d117939..c25ea9cf9 100644 --- a/Library/Homebrew/test/dependency_collector_spec.rb +++ b/Library/Homebrew/test/dependency_collector_spec.rb @@ -1,8 +1,8 @@ require "dependency_collector" -RSpec::Matchers.alias_matcher :be_a_build_requirement, :be_build - describe DependencyCollector do + alias_matcher :be_a_build_requirement, :be_build + def find_dependency(name) subject.deps.find { |dep| dep.name == name } end diff --git a/Library/Homebrew/test/dependency_spec.rb b/Library/Homebrew/test/dependency_spec.rb index 4af779cc3..4f1e8d474 100644 --- a/Library/Homebrew/test/dependency_spec.rb +++ b/Library/Homebrew/test/dependency_spec.rb @@ -1,9 +1,9 @@ require "dependency" -RSpec::Matchers.alias_matcher :be_a_build_dependency, :be_build -RSpec::Matchers.alias_matcher :be_a_runtime_dependency, :be_run - describe Dependency do + alias_matcher :be_a_build_dependency, :be_build + alias_matcher :be_a_runtime_dependency, :be_run + describe "::new" do it "accepts a single tag" do dep = described_class.new("foo", %w[bar]) diff --git a/Library/Homebrew/test/dev-cmd/audit_spec.rb b/Library/Homebrew/test/dev-cmd/audit_spec.rb index c914a9a20..25a179342 100644 --- a/Library/Homebrew/test/dev-cmd/audit_spec.rb +++ b/Library/Homebrew/test/dev-cmd/audit_spec.rb @@ -1,10 +1,6 @@ require "dev-cmd/audit" require "formulary" -RSpec::Matchers.alias_matcher :have_data, :be_data -RSpec::Matchers.alias_matcher :have_end, :be_end -RSpec::Matchers.alias_matcher :have_trailing_newline, :be_trailing_newline - module Count def self.increment @count ||= 0 @@ -13,6 +9,10 @@ module Count end describe FormulaText do + alias_matcher :have_data, :be_data + alias_matcher :have_end, :be_end + alias_matcher :have_trailing_newline, :be_trailing_newline + let(:dir) { mktmpdir } def formula_text(name, body = nil, options = {}) @@ -305,23 +305,6 @@ describe FormulaAuditor do end end - specify "#audit_caveats" do - fa = formula_auditor "foo", <<-EOS.undent - class Foo < Formula - homepage "http://example.com/foo" - url "http://example.com/foo-1.0.tgz" - - def caveats - "setuid" - end - end - EOS - - fa.audit_caveats - expect(fa.problems) - .to eq(["Don't recommend setuid in the caveats, suggest sudo instead."]) - end - describe "#audit_keg_only_style" do specify "keg_only_needs_downcasing" do fa = formula_auditor "foo", <<-EOS.undent, strict: true @@ -385,131 +368,6 @@ describe FormulaAuditor do end end - describe "#audit_homepage" do - specify "homepage URLs" do - fa = formula_auditor "foo", <<-EOS.undent, online: true - class Foo < Formula - homepage "ftp://example.com/foo" - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_homepage - expect(fa.problems) - .to eq(["The homepage should start with http or https (URL is #{fa.formula.homepage})."]) - - formula_homepages = { - "bar" => "http://www.freedesktop.org/wiki/bar", - "baz" => "http://www.freedesktop.org/wiki/Software/baz", - "qux" => "https://code.google.com/p/qux", - "quux" => "http://github.com/quux", - "corge" => "http://savannah.nongnu.org/corge", - "grault" => "http://grault.github.io/", - "garply" => "http://www.gnome.org/garply", - "sf1" => "http://foo.sourceforge.net/", - "sf2" => "http://foo.sourceforge.net", - "sf3" => "http://foo.sf.net/", - "sf4" => "http://foo.sourceforge.io/", - "waldo" => "http://www.gnu.org/waldo", - } - - formula_homepages.each do |name, homepage| - fa = formula_auditor name, <<-EOS.undent - class #{Formulary.class_s(name)} < Formula - homepage "#{homepage}" - url "http://example.com/#{name}-1.0.tgz" - end - EOS - - fa.audit_homepage - if homepage =~ %r{http:\/\/www\.freedesktop\.org} - if homepage =~ /Software/ - expect(fa.problems.first).to match( - "#{homepage} should be styled " \ - "`https://wiki.freedesktop.org/www/Software/project_name`", - ) - else - expect(fa.problems.first).to match( - "#{homepage} should be styled " \ - "`https://wiki.freedesktop.org/project_name`", - ) - end - elsif homepage =~ %r{https:\/\/code\.google\.com} - expect(fa.problems.first) - .to match("#{homepage} should end with a slash") - elsif homepage =~ /foo\.(sf|sourceforge)\.net/ - expect(fa.problems.first) - .to match("#{homepage} should be `https://foo.sourceforge.io/`") - else - expect(fa.problems.first) - .to match("Please use https:// for #{homepage}") - end - end - end - - specify "missing homepage" do - fa = formula_auditor "foo", <<-EOS.undent, online: true - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - end - EOS - - fa.audit_homepage - expect(fa.problems.first).to match("Formula should have a homepage.") - end - end - - describe "#audit_text" do - specify "xcodebuild suggests symroot" do - fa = formula_auditor "foo", <<-EOS.undent - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - homepage "http://example.com" - - def install - xcodebuild "-project", "meow.xcodeproject" - end - end - EOS - - fa.audit_text - expect(fa.problems.first) - .to match('xcodebuild should be passed an explicit "SYMROOT"') - end - - specify "bare xcodebuild also suggests symroot" do - fa = formula_auditor "foo", <<-EOS.undent - class Foo < Formula - url "http://example.com/foo-1.0.tgz" - homepage "http://example.com" - - def install - xcodebuild - end - end - EOS - - fa.audit_text - expect(fa.problems.first) - .to match('xcodebuild should be passed an explicit "SYMROOT"') - end - - specify "disallow go get usage" do - fa = formula_auditor "foo", <<-EOS.undent - class Foo <Formula - url "http://example.com/foo-1.0.tgz" - - def install - system "go", "get", "bar" - end - end - EOS - fa.audit_text - expect(fa.problems.first) - .to match("Formulae should not use `go get`. If non-vendored resources are required use `go_resource`s.") - end - end - describe "#audit_revision_and_version_scheme" do let(:origin_tap_path) { Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" } let(:formula_subpath) { "Formula/foo#{@foo_version}.rb" } diff --git a/Library/Homebrew/test/dev-cmd/pull_spec.rb b/Library/Homebrew/test/dev-cmd/pull_spec.rb index 2ebe144bb..cfd786de0 100644 --- a/Library/Homebrew/test/dev-cmd/pull_spec.rb +++ b/Library/Homebrew/test/dev-cmd/pull_spec.rb @@ -14,7 +14,7 @@ describe "brew pull", :integration_test do end end - expect { brew "pull", "https://bot.brew.sh/job/Homebrew\%20Testing/1028/" } + expect { brew "pull", "https://jenkins.brew.sh/job/Homebrew\%20Testing/1028/" } .to output(/Testing URLs require `\-\-bottle`!/).to_stderr .and not_to_output.to_stdout .and be_a_failure diff --git a/Library/Homebrew/test/diagnostic_spec.rb b/Library/Homebrew/test/diagnostic_spec.rb index 6e2c09268..058b9823c 100644 --- a/Library/Homebrew/test/diagnostic_spec.rb +++ b/Library/Homebrew/test/diagnostic_spec.rb @@ -7,7 +7,7 @@ describe Homebrew::Diagnostic::Checks do end specify "#check_path_for_trailing_slashes" do - ENV["PATH"] += File::PATH_SEPARATOR + "/foo/bar/" + ENV["HOMEBREW_PATH"] += File::PATH_SEPARATOR + "/foo/bar/" expect(subject.check_path_for_trailing_slashes) .to match("Some directories in your path end in a slash") end @@ -43,8 +43,10 @@ describe Homebrew::Diagnostic::Checks do specify "#check_access_lock_dir" do begin + prev_mode = HOMEBREW_LOCK_DIR.stat.mode mode = HOMEBREW_LOCK_DIR.stat.mode & 0777 HOMEBREW_LOCK_DIR.chmod 0555 + expect(HOMEBREW_LOCK_DIR.stat.mode).not_to eq(prev_mode) expect(subject.check_access_lock_dir) .to match("#{HOMEBREW_LOCK_DIR} isn't writable.") @@ -88,13 +90,6 @@ describe Homebrew::Diagnostic::Checks do end end - specify "#check_homebrew_prefix" do - ENV.delete("JENKINS_HOME") - # the integration tests are run in a special prefix - expect(subject.check_homebrew_prefix) - .to match("Your Homebrew's prefix is not /usr/local.") - end - specify "#check_user_path_1" do bin = HOMEBREW_PREFIX/"bin" sep = File::PATH_SEPARATOR diff --git a/Library/Homebrew/test/formatter_spec.rb b/Library/Homebrew/test/formatter_spec.rb index 6357853d8..1a74e3405 100644 --- a/Library/Homebrew/test/formatter_spec.rb +++ b/Library/Homebrew/test/formatter_spec.rb @@ -4,11 +4,11 @@ require "utils/tty" describe Formatter do describe "::columns" do let(:input) { - [ - "aa", - "bbb", - "ccc", - "dd", + %w[ + aa + bbb + ccc + dd ] } subject { described_class.columns(input) } diff --git a/Library/Homebrew/test/formula_installer_bottle_spec.rb b/Library/Homebrew/test/formula_installer_bottle_spec.rb index 8409e1ac7..824cdb36d 100644 --- a/Library/Homebrew/test/formula_installer_bottle_spec.rb +++ b/Library/Homebrew/test/formula_installer_bottle_spec.rb @@ -5,9 +5,9 @@ require "tab" require "test/support/fixtures/testball" require "test/support/fixtures/testball_bottle" -RSpec::Matchers.alias_matcher :pour_bottle, :be_pour_bottle - describe FormulaInstaller do + alias_matcher :pour_bottle, :be_pour_bottle + matcher :be_poured_from_bottle do match(&:poured_from_bottle) end diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb index efe2bf5a2..d309a17da 100644 --- a/Library/Homebrew/test/formula_installer_spec.rb +++ b/Library/Homebrew/test/formula_installer_spec.rb @@ -5,10 +5,10 @@ require "tab" require "test/support/fixtures/testball" require "test/support/fixtures/testball_bottle" -RSpec::Matchers.define_negated_matcher :need_bottle, :be_bottle_unneeded -RSpec::Matchers.alias_matcher :have_disabled_bottle, :be_bottle_disabled - describe FormulaInstaller do + define_negated_matcher :need_bottle, :be_bottle_unneeded + alias_matcher :have_disabled_bottle, :be_bottle_disabled + matcher :be_poured_from_bottle do match(&:poured_from_bottle) end diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index 1f98ca525..364dbfe98 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -1,19 +1,19 @@ require "test/support/fixtures/testball" require "formula" -RSpec::Matchers.alias_matcher :follow_installed_alias, :be_follow_installed_alias -RSpec::Matchers.alias_matcher :have_any_version_installed, :be_any_version_installed -RSpec::Matchers.alias_matcher :need_migration, :be_migration_needed +describe Formula do + alias_matcher :follow_installed_alias, :be_follow_installed_alias + alias_matcher :have_any_version_installed, :be_any_version_installed + alias_matcher :need_migration, :be_migration_needed -RSpec::Matchers.alias_matcher :have_changed_installed_alias_target, :be_installed_alias_target_changed -RSpec::Matchers.alias_matcher :supersede_an_installed_formula, :be_supersedes_an_installed_formula -RSpec::Matchers.alias_matcher :have_changed_alias, :be_alias_changed + alias_matcher :have_changed_installed_alias_target, :be_installed_alias_target_changed + alias_matcher :supersede_an_installed_formula, :be_supersedes_an_installed_formula + alias_matcher :have_changed_alias, :be_alias_changed -RSpec::Matchers.alias_matcher :have_option_defined, :be_option_defined -RSpec::Matchers.alias_matcher :have_test_defined, :be_test_defined -RSpec::Matchers.alias_matcher :pour_bottle, :be_pour_bottle + alias_matcher :have_option_defined, :be_option_defined + alias_matcher :have_test_defined, :be_test_defined + alias_matcher :pour_bottle, :be_pour_bottle -describe Formula do describe "::new" do let(:klass) do Class.new(described_class) do @@ -683,7 +683,7 @@ describe Formula do end expect(f5.deps.map(&:name)).to eq(["f3", "f4"]) - expect(f5.recursive_dependencies.map(&:name)).to eq(["f1", "f2", "f3", "f4"]) + expect(f5.recursive_dependencies.map(&:name)).to eq(%w[f1 f2 f3 f4]) expect(f5.runtime_dependencies.map(&:name)).to eq(["f1", "f4"]) end diff --git a/Library/Homebrew/test/formula_lock_spec.rb b/Library/Homebrew/test/lock_file_spec.rb index 9b5ece813..82c47a70a 100644 --- a/Library/Homebrew/test/formula_lock_spec.rb +++ b/Library/Homebrew/test/lock_file_spec.rb @@ -1,6 +1,6 @@ -require "formula_lock" +require "lock_file" -describe FormulaLock do +describe LockFile do subject { described_class.new("foo") } describe "#lock" do @@ -24,7 +24,7 @@ describe FormulaLock do expect { subject.unlock }.not_to raise_error end - it "unlocks a locked Formula" do + it "unlocks when locked" do subject.lock subject.unlock diff --git a/Library/Homebrew/test/migrator_spec.rb b/Library/Homebrew/test/migrator_spec.rb index 90ee9d8ff..900c10c02 100644 --- a/Library/Homebrew/test/migrator_spec.rb +++ b/Library/Homebrew/test/migrator_spec.rb @@ -44,9 +44,7 @@ describe Migrator do end after(:each) do - if !old_keg_record.parent.symlink? && old_keg_record.directory? - keg.unlink - end + keg.unlink if !old_keg_record.parent.symlink? && old_keg_record.directory? if new_keg_record.directory? new_keg = Keg.new(new_keg_record) diff --git a/Library/Homebrew/test/os/mac/dependency_collector_spec.rb b/Library/Homebrew/test/os/mac/dependency_collector_spec.rb index 21b15cd99..688149021 100644 --- a/Library/Homebrew/test/os/mac/dependency_collector_spec.rb +++ b/Library/Homebrew/test/os/mac/dependency_collector_spec.rb @@ -1,8 +1,8 @@ require "dependency_collector" -RSpec::Matchers.alias_matcher :need_tar_xz_dependency, :be_tar_needs_xz_dependency - describe DependencyCollector do + alias_matcher :need_tar_xz_dependency, :be_tar_needs_xz_dependency + after(:each) do described_class.clear_cache end diff --git a/Library/Homebrew/test/os/mac/diagnostic_spec.rb b/Library/Homebrew/test/os/mac/diagnostic_spec.rb index d2b38a332..787f80fec 100644 --- a/Library/Homebrew/test/os/mac/diagnostic_spec.rb +++ b/Library/Homebrew/test/os/mac/diagnostic_spec.rb @@ -39,4 +39,10 @@ describe Homebrew::Diagnostic::Checks do expect(subject.check_xcode_8_without_clt_on_el_capitan) .to match("You have Xcode 8 installed without the CLT") end + + specify "#check_homebrew_prefix" do + # the integration tests are run in a special prefix + expect(subject.check_homebrew_prefix) + .to match("Your Homebrew's prefix is not /usr/local.") + end end diff --git a/Library/Homebrew/test/pathname_spec.rb b/Library/Homebrew/test/pathname_spec.rb index 77cb6cfed..0bc19c5ac 100644 --- a/Library/Homebrew/test/pathname_spec.rb +++ b/Library/Homebrew/test/pathname_spec.rb @@ -104,7 +104,7 @@ describe Pathname do end it "preserves permissions" do - File.open(file, "w", 0100777).close + File.open(file, "w", 0100777) {} file.atomic_write("CONTENT") expect(file.stat.mode).to eq(0100777 & ~File.umask) end diff --git a/Library/Homebrew/test/requirement_spec.rb b/Library/Homebrew/test/requirement_spec.rb index 959041cf4..71372aa69 100644 --- a/Library/Homebrew/test/requirement_spec.rb +++ b/Library/Homebrew/test/requirement_spec.rb @@ -1,10 +1,10 @@ require "extend/ENV" require "requirement" -RSpec::Matchers.alias_matcher :have_a_default_formula, :be_a_default_formula -RSpec::Matchers.alias_matcher :be_a_build_requirement, :be_a_build - describe Requirement do + alias_matcher :have_a_default_formula, :be_a_default_formula + alias_matcher :be_a_build_requirement, :be_a_build + subject { klass.new } let(:klass) { Class.new(described_class) } diff --git a/Library/Homebrew/test/rubocops/caveats_cop_spec.rb b/Library/Homebrew/test/rubocops/caveats_cop_spec.rb new file mode 100644 index 000000000..d44808a5d --- /dev/null +++ b/Library/Homebrew/test/rubocops/caveats_cop_spec.rb @@ -0,0 +1,42 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/caveats_cop" + +describe RuboCop::Cop::FormulaAudit::Caveats do + subject(:cop) { described_class.new } + + context "When auditing caveats" do + it "When there is setuid mentioned in caveats" do + source = <<-EOS.undent + class Foo < Formula + homepage "http://example.com/foo" + url "http://example.com/foo-1.0.tgz" + + def caveats + "setuid" + end + end + EOS + + expected_offenses = [{ message: "Don't recommend setuid in the caveats, suggest sudo instead.", + severity: :convention, + line: 6, + column: 5, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + 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 +end diff --git a/Library/Homebrew/test/rubocops/components_order_cop_spec.rb b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb index 05ff53d8f..25467c635 100644 --- a/Library/Homebrew/test/rubocops/components_order_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/components_order_cop_spec.rb @@ -113,4 +113,51 @@ describe RuboCop::Cop::FormulaAuditStrict::ComponentsOrder do expect(actual.column).to eq(expected[:column]) end end + + context "When auditing formula components order with autocorrect" do + it "When url precedes homepage" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + end + EOS + correct_source = <<-EOS.undent + class Foo < Formula + homepage "http://example.com" + url "http://example.com/foo-1.0.tgz" + end + EOS + + corrected_source = autocorrect_source(cop, source) + expect(corrected_source).to eq(correct_source) + end + + it "When `resource` precedes `depends_on`" do + source = <<-EOS.undent + class Foo < Formula + url "https://example.com/foo-1.0.tgz" + + resource "foo2" do + url "https://example.com/foo-2.0.tgz" + end + + depends_on "openssl" + end + EOS + correct_source = <<-EOS.undent + class Foo < Formula + url "https://example.com/foo-1.0.tgz" + + depends_on "openssl" + + resource "foo2" do + url "https://example.com/foo-2.0.tgz" + end + end + EOS + corrected_source = autocorrect_source(cop, source) + expect(corrected_source).to eq(correct_source) + end + 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 581667935..f6436d6a3 100644 --- a/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb +++ b/Library/Homebrew/test/rubocops/formula_desc_cop_spec.rb @@ -31,7 +31,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do source = <<-EOS.undent class Foo < Formula url 'http://example.com/foo-1.0.tgz' - desc '#{"bar"*30}' + desc '#{"bar" * 30}' end EOS @@ -51,6 +51,31 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do end end + it "When desc is multiline string" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + desc '#{"bar" * 10}'\ + '#{"foo" * 21}' + end + EOS + + msg = <<-EOS.undent + Description is too long. "name: desc" should be less than 80 characters. + Length is calculated as Foo + desc. (currently 98) + EOS + expected_offenses = [{ message: msg, + severity: :convention, + line: 3, + column: 2, + source: source }] + + inspect_source(cop, source) + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + it "When wrong \"command-line\" usage in desc" do source = <<-EOS.undent class Foo < Formula @@ -95,7 +120,7 @@ describe RuboCop::Cop::FormulaAuditStrict::Desc do source = <<-EOS.undent class Foo < Formula url 'http://example.com/foo-1.0.tgz' - desc 'Foo' + desc 'Foo: foobar' end EOS diff --git a/Library/Homebrew/test/rubocops/homepage_cop_spec.rb b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb new file mode 100644 index 000000000..c03efd825 --- /dev/null +++ b/Library/Homebrew/test/rubocops/homepage_cop_spec.rb @@ -0,0 +1,124 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/homepage_cop" + +describe RuboCop::Cop::FormulaAudit::Homepage do + subject(:cop) { described_class.new } + + context "When auditing homepage" do + it "When there is no homepage" do + source = <<-EOS.undent + class Foo < Formula + url 'http://example.com/foo-1.0.tgz' + end + EOS + + expected_offenses = [{ message: "Formula should have a homepage.", + severity: :convention, + line: 1, + column: 0, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "Homepage with ftp" do + source = <<-EOS.undent + class Foo < Formula + homepage "ftp://example.com/foo" + url "http://example.com/foo-1.0.tgz" + end + EOS + + expected_offenses = [{ message: "The homepage should start with http or https (URL is ftp://example.com/foo).", + severity: :convention, + line: 2, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "Homepage URLs" do + formula_homepages = { + "bar" => "http://www.freedesktop.org/wiki/bar", + "baz" => "http://www.freedesktop.org/wiki/Software/baz", + "qux" => "https://code.google.com/p/qux", + "quux" => "http://github.com/quux", + "corge" => "http://savannah.nongnu.org/corge", + "grault" => "http://grault.github.io/", + "garply" => "http://www.gnome.org/garply", + "sf1" => "http://foo.sourceforge.net/", + "sf2" => "http://foo.sourceforge.net", + "sf3" => "http://foo.sf.net/", + "sf4" => "http://foo.sourceforge.io/", + "waldo" => "http://www.gnu.org/waldo", + } + + formula_homepages.each do |name, homepage| + source = <<-EOS.undent + class #{name.capitalize} < Formula + homepage "#{homepage}" + url "http://example.com/#{name}-1.0.tgz" + end + EOS + + inspect_source(cop, source) + if homepage =~ %r{http:\/\/www\.freedesktop\.org} + if homepage =~ /Software/ + expected_offenses = [{ message: "#{homepage} should be styled " \ + "`https://wiki.freedesktop.org/www/Software/project_name`", + severity: :convention, + line: 2, + column: 2, + source: source }] + else + expected_offenses = [{ message: "#{homepage} should be styled " \ + "`https://wiki.freedesktop.org/project_name`", + severity: :convention, + line: 2, + column: 2, + source: source }] + end + elsif homepage =~ %r{https:\/\/code\.google\.com} + expected_offenses = [{ message: "#{homepage} should end with a slash", + severity: :convention, + line: 2, + column: 2, + source: source }] + elsif homepage =~ /foo\.(sf|sourceforge)\.net/ + expected_offenses = [{ message: "#{homepage} should be `https://foo.sourceforge.io/`", + severity: :convention, + line: 2, + column: 2, + source: source }] + else + expected_offenses = [{ message: "Please use https:// for #{homepage}", + severity: :convention, + line: 2, + column: 2, + source: source }] + end + expected_offenses.zip([cop.offenses.last]).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 +end diff --git a/Library/Homebrew/test/rubocops/text_cop_spec.rb b/Library/Homebrew/test/rubocops/text_cop_spec.rb new file mode 100644 index 000000000..b218e9c25 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text_cop_spec.rb @@ -0,0 +1,261 @@ +require "rubocop" +require "rubocop/rspec/support" +require_relative "../../extend/string" +require_relative "../../rubocops/text_cop" + +describe RuboCop::Cop::FormulaAudit::Text do + subject(:cop) { described_class.new } + + context "When auditing formula text" do + it "When xcodebuild is called without SYMROOT" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + xcodebuild "-project", "meow.xcodeproject" + end + end + EOS + + expected_offenses = [{ message: "xcodebuild should be passed an explicit \"SYMROOT\"", + severity: :convention, + line: 6, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When xcodebuild is called without any args" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + xcodebuild + end + end + EOS + + expected_offenses = [{ message: "xcodebuild should be passed an explicit \"SYMROOT\"", + severity: :convention, + line: 6, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When go get is executed" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + system "go", "get", "bar" + end + end + EOS + + expected_offenses = [{ message: "Formulae should not use `go get`. If non-vendored resources are required use `go_resource`s.", + severity: :convention, + line: 6, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When xcodebuild is executed" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + system "xcodebuild", "foo", "bar" + end + end + EOS + + expected_offenses = [{ message: "use \"xcodebuild *args\" instead of \"system 'xcodebuild', *args\"", + severity: :convention, + line: 6, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When scons is executed" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + system "scons", "foo", "bar" + end + end + EOS + + expected_offenses = [{ message: "use \"scons *args\" instead of \"system 'scons', *args\"", + severity: :convention, + line: 6, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When plist_options are not defined when using a formula-defined plist" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + system "xcodebuild", "foo", "bar" + end + + def plist; <<-EOS.undent + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + <plist version="1.0"> + <dict> + <key>Label</key> + <string>org.nrpe.agent</string> + </dict> + </plist> + \EOS + end + end + EOS + + expected_offenses = [{ message: "Please set plist_options when using a formula-defined plist.", + severity: :convention, + line: 9, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When language/go is require'd" do + source = <<-EOS.undent + require "language/go" + + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + system "go", "get", "bar" + end + end + EOS + + expected_offenses = [{ message: "require \"language/go\" is unnecessary unless using `go_resource`s", + severity: :convention, + line: 1, + column: 0, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When formula uses virtualenv and also `setuptools` resource" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + resource "setuptools" do + url "https://foo.com/foo.tar.gz" + sha256 "db0904a28253cfe53e7dedc765c71596f3c53bb8a866ae50123320ec1a7b73fd" + end + + def install + virtualenv_create(libexec) + end + end + EOS + + expected_offenses = [{ message: "Formulae using virtualenvs do not need a `setuptools` resource.", + severity: :convention, + line: 5, + column: 2, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + end + end + + it "When Formula.factory(name) is used" do + source = <<-EOS.undent + class Foo < Formula + url "http://example.com/foo-1.0.tgz" + homepage "http://example.com" + + def install + Formula.factory(name) + end + end + EOS + + expected_offenses = [{ message: "\"Formula.factory(name)\" is deprecated in favor of \"Formula[name]\"", + severity: :convention, + line: 6, + column: 4, + source: source }] + + inspect_source(cop, source) + + expected_offenses.zip(cop.offenses).each do |expected, actual| + expect_offense(expected, actual) + 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 +end diff --git a/Library/Homebrew/test/sandbox_spec.rb b/Library/Homebrew/test/sandbox_spec.rb index 0d349f6eb..eafec4dd4 100644 --- a/Library/Homebrew/test/sandbox_spec.rb +++ b/Library/Homebrew/test/sandbox_spec.rb @@ -1,8 +1,8 @@ require "sandbox" -RSpec::Matchers.define_negated_matcher :not_matching, :matching - describe Sandbox do + define_negated_matcher :not_matching, :matching + let(:dir) { mktmpdir } let(:file) { dir/"foo" } diff --git a/Library/Homebrew/test/software_spec_spec.rb b/Library/Homebrew/test/software_spec_spec.rb index 5fd4f598a..e6eaeb204 100644 --- a/Library/Homebrew/test/software_spec_spec.rb +++ b/Library/Homebrew/test/software_spec_spec.rb @@ -1,9 +1,9 @@ require "software_spec" -RSpec::Matchers.alias_matcher :have_defined_resource, :be_resource_defined -RSpec::Matchers.alias_matcher :have_defined_option, :be_option_defined - describe SoftwareSpec do + alias_matcher :have_defined_resource, :be_resource_defined + alias_matcher :have_defined_option, :be_option_defined + let(:owner) { double(name: "some_name", full_name: "some_name", tap: "homebrew/core") } describe "#resource" do diff --git a/Library/Homebrew/test/string_spec.rb b/Library/Homebrew/test/string_spec.rb index d1b820b66..ce26d70d4 100644 --- a/Library/Homebrew/test/string_spec.rb +++ b/Library/Homebrew/test/string_spec.rb @@ -3,19 +3,19 @@ require "extend/string" describe String do describe "#undent" do it "removes leading whitespace, taking the first line as reference" do - string = <<-EOS.undent - hi -........my friend over - there + string = <<-EOS.unindent + hi + ........my friend over + there EOS expect(string).to eq("hi\n........my friend over\n there\n") end it "removes nothing if the text is not indented" do - string = <<-EOS.undent -hi -I'm not indented + string = <<-EOS.unindent + hi + I'm not indented EOS expect(string).to eq("hi\nI'm not indented\n") diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/container-gpg.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/container-gpg.rb new file mode 100644 index 000000000..630527ce2 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/container-gpg.rb @@ -0,0 +1,12 @@ +cask 'container-gpg' do + version '1.2.3' + sha256 :no_check + + url "file://#{TEST_FIXTURE_DIR}/cask/container.tar.xz.gpg" + gpg :embedded, key_id: 'B0976E51E5C047AD0FD051294E402EBF7C3C6A71' + + homepage 'https://example.com/container-gpg' + depends_on formula: 'gpg' + + app 'container' +end diff --git a/Library/Homebrew/test/support/fixtures/cask/container.tar.xz.gpg b/Library/Homebrew/test/support/fixtures/cask/container.tar.xz.gpg Binary files differnew file mode 100644 index 000000000..be250e851 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/container.tar.xz.gpg diff --git a/Library/Homebrew/test/support/fixtures/tarballs/testbottest-0.1.tbz b/Library/Homebrew/test/support/fixtures/tarballs/testbottest-0.1.tbz Binary files differdeleted file mode 100644 index 83f7ab1dd..000000000 --- a/Library/Homebrew/test/support/fixtures/tarballs/testbottest-0.1.tbz +++ /dev/null diff --git a/Library/Homebrew/test/support/fixtures/testbottest.rb b/Library/Homebrew/test/support/fixtures/testbottest.rb deleted file mode 100644 index f7695cab5..000000000 --- a/Library/Homebrew/test/support/fixtures/testbottest.rb +++ /dev/null @@ -1,14 +0,0 @@ -class Testbottest < Formula - desc "Minimal C program and Makefile used for testing Homebrew." - homepage "https://github.com/Homebrew/brew" - url "file://#{File.expand_path("..", __FILE__)}/tarballs/testbottest-0.1.tbz" - sha256 "78b54d8f31585c9773bed12b4aa4ab2ce458ebd044b9406cb24d40aa5107f082" - - def install - system "make", "install", "PREFIX=#{prefix}" - end - - test do - assert_equal "testbottest\n", shell_output("#{bin}/testbottest") - end -end diff --git a/Library/Homebrew/test/support/helper/spec/shared_examples/hbc_staged.rb b/Library/Homebrew/test/support/helper/spec/shared_examples/hbc_staged.rb index 31083ee58..1364c13a3 100644 --- a/Library/Homebrew/test/support/helper/spec/shared_examples/hbc_staged.rb +++ b/Library/Homebrew/test/support/helper/spec/shared_examples/hbc_staged.rb @@ -24,7 +24,7 @@ shared_examples Hbc::Staged do end it "can get the Info.plist file for the primary app" do - expect(staged.info_plist_file.to_s).to include Hbc.appdir.join("TestCask.app/Contents/Info.plist") + expect(staged.info_plist_file).to eq Hbc.appdir.join("TestCask.app/Contents/Info.plist") end it "can execute commands on the Info.plist file" do diff --git a/Library/Homebrew/test/tab_spec.rb b/Library/Homebrew/test/tab_spec.rb index 1b0836c93..93ae42ce4 100644 --- a/Library/Homebrew/test/tab_spec.rb +++ b/Library/Homebrew/test/tab_spec.rb @@ -1,9 +1,9 @@ require "tab" require "formula" -RSpec::Matchers.alias_matcher :be_built_with, :be_with - describe Tab do + alias_matcher :be_built_with, :be_with + matcher :be_poured_from_bottle do match do |actual| actual.poured_from_bottle == true diff --git a/Library/Homebrew/test/tap_spec.rb b/Library/Homebrew/test/tap_spec.rb index 50e4522af..27b5e0c4b 100644 --- a/Library/Homebrew/test/tap_spec.rb +++ b/Library/Homebrew/test/tap_spec.rb @@ -1,9 +1,9 @@ -RSpec::Matchers.alias_matcher :have_formula_file, :be_formula_file -RSpec::Matchers.alias_matcher :have_custom_remote, :be_custom_remote - describe Tap do include FileUtils + alias_matcher :have_formula_file, :be_formula_file + alias_matcher :have_custom_remote, :be_custom_remote + subject { described_class.new("Homebrew", "foo") } let(:path) { Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" } let(:formula_file) { path/"Formula/foo.rb" } diff --git a/Library/Homebrew/test/utils_spec.rb b/Library/Homebrew/test/utils_spec.rb index be224990a..f3bf98486 100644 --- a/Library/Homebrew/test/utils_spec.rb +++ b/Library/Homebrew/test/utils_spec.rb @@ -188,14 +188,14 @@ describe "globally-scoped helper methods" do end specify "#which_editor" do - ENV["HOMEBREW_EDITOR"] = "vemate" + ENV["HOMEBREW_EDITOR"] = "vemate -w" ENV["HOMEBREW_PATH"] = dir editor = "#{dir}/vemate" FileUtils.touch editor FileUtils.chmod 0755, editor - expect(which_editor).to eql editor + expect(which_editor).to eq("vemate -w") end specify "#gzip" do diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index c37633e41..cde2ee306 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -75,7 +75,7 @@ def odeprecated(method, replacement = nil, disable: false, disable_on: nil, call backtrace = caller tap_message = nil caller_message = backtrace.detect do |line| - next unless line =~ %r{^#{Regexp.escape HOMEBREW_LIBRARY}/Taps/([^/]+/[^/]+)/} + next unless line =~ %r{^#{Regexp.escape(HOMEBREW_LIBRARY)}/Taps/([^/]+/[^/]+)/} tap = Tap.fetch $1 tap_message = "\nPlease report this to the #{tap} tap!" true @@ -175,7 +175,7 @@ module Homebrew end def system(cmd, *args) - puts "#{cmd} #{args*" "}" if ARGV.verbose? + puts "#{cmd} #{args * " "}" if ARGV.verbose? _system(cmd, *args) end @@ -262,6 +262,14 @@ ensure ENV["PATH"] = old_path end +def with_homebrew_path + old_path = ENV["PATH"] + ENV["PATH"] = ENV["HOMEBREW_PATH"] + yield +ensure + ENV["PATH"] = old_path +end + def with_custom_locale(locale) old_locale = ENV["LC_ALL"] ENV["LC_ALL"] = locale @@ -320,21 +328,17 @@ def which_all(cmd, path = ENV["PATH"]) end def which_editor - editor = ENV.values_at("HOMEBREW_EDITOR", "VISUAL").compact.reject(&:empty?).first - if editor - editor_name, _, editor_args = editor.partition " " - editor_path = which(editor_name, ENV["HOMEBREW_PATH"]) - editor = if editor_args.to_s.empty? - editor_path.to_s - else - "#{editor_path} #{editor_args}" - end - return editor + editor = ENV.values_at("HOMEBREW_EDITOR", "HOMEBREW_VISUAL").compact.reject(&:empty?).first + return editor unless editor.nil? + + # Find Textmate, BBEdit / TextWrangler, or vim + %w[mate edit vim].each do |candidate| + editor = candidate if which(candidate, ENV["HOMEBREW_PATH"]) end # Find Textmate editor = which("mate", ENV["HOMEBREW_PATH"]) - # Find BBEdit / TextWrangler + # Find BBEdit/TextWrangler editor ||= which("edit", ENV["HOMEBREW_PATH"]) # Find vim editor ||= which("vim", ENV["HOMEBREW_PATH"]) @@ -347,16 +351,16 @@ def which_editor or HOMEBREW_EDITOR to your preferred text editor. EOS - editor.to_s + editor end def exec_editor(*args) puts "Editing #{args.join "\n"}" - safe_exec(which_editor, *args) + with_homebrew_path { safe_exec(which_editor, *args) } end def exec_browser(*args) - browser = ENV["HOMEBREW_BROWSER"] || ENV["BROWSER"] + browser = ENV["HOMEBREW_BROWSER"] browser ||= OS::PATH_OPEN if defined?(OS::PATH_OPEN) return unless browser safe_exec(browser, *args) diff --git a/Library/Homebrew/utils/formatter.rb b/Library/Homebrew/utils/formatter.rb index 099b1c6d3..a29b43c8d 100644 --- a/Library/Homebrew/utils/formatter.rb +++ b/Library/Homebrew/utils/formatter.rb @@ -91,17 +91,17 @@ module Formatter output end - def pluralize(count, singular, plural = nil) - return "#{count} #{singular}" if count == 1 + def pluralize(count, singular, plural = nil, show_count: true) + return (show_count ? "#{count} #{singular}" : singular.to_s) if count == 1 - *adjectives, noun = singular.split(" ") + *adjectives, noun = singular.to_s.split(" ") plural ||= { "formula" => "formulae", }.fetch(noun, "#{noun}s") - words = adjectives << plural + words = adjectives.push(plural).join(" ") - "#{count} #{words.join(" ")}" + show_count ? "#{count} #{words}" : words end end diff --git a/Library/Homebrew/utils/git.rb b/Library/Homebrew/utils/git.rb index 1b4d24894..43d93b64e 100644 --- a/Library/Homebrew/utils/git.rb +++ b/Library/Homebrew/utils/git.rb @@ -1,3 +1,31 @@ +require "open3" + +module Git + module_function + + def last_revision_commit_of_file(repo, file, before_commit: nil) + args = [before_commit.nil? ? "--skip=1" : before_commit.split("..").first] + + out, = Open3.capture3( + HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo, + "log", "--oneline", "--max-count=1", *args, "--", file + ) + out.split(" ").first + end + + 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) + + out, = Open3.capture3( + HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo, + "show", "#{commit_hash}:#{relative_file}" + ) + out + end +end + module Utils def self.git_available? return @git if instance_variable_defined?(:@git) diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index 88c5199c2..5ba381529 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -164,7 +164,7 @@ module GitHub args += ["--data", "@#{data_tmpfile.path}"] end - args += ["--dump-header", headers_tmpfile.path.to_s] + args += ["--dump-header", headers_tmpfile.path] output, errors, status = curl_output(url.to_s, *args) output, _, http_code = output.rpartition("\n") diff --git a/Library/Homebrew/utils/lock.sh b/Library/Homebrew/utils/lock.sh index 7f0638c1a..7f2bb8b73 100644 --- a/Library/Homebrew/utils/lock.sh +++ b/Library/Homebrew/utils/lock.sh @@ -38,14 +38,19 @@ _create_lock() { local lock_fd="$1" local name="$2" local ruby="/usr/bin/ruby" + local python="/usr/bin/python" [[ -x "$ruby" ]] || ruby="$(which ruby 2>/dev/null)" + [[ -x "$python" ]] || python="$(which python 2>/dev/null)" - if [[ -n "$ruby" ]] + if [[ -x "$ruby" ]] && "$ruby" -e "exit(RUBY_VERSION >= '1.8.7')" then "$ruby" -e "File.new($lock_fd).flock(File::LOCK_EX | File::LOCK_NB) || exit(1)" - elif [[ -n "$(which flock)" ]] + elif [[ -x "$(which flock)" ]] then flock -n "$lock_fd" + elif [[ -x "$python" ]] + then + "$python" -c "import fcntl; fcntl.flock($lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)" else onoe "Cannot create $name lock, please avoid running Homebrew in parallel." fi diff --git a/Library/Homebrew/utils/popen.rb b/Library/Homebrew/utils/popen.rb index 350d9a09f..4e03711a1 100644 --- a/Library/Homebrew/utils/popen.rb +++ b/Library/Homebrew/utils/popen.rb @@ -3,10 +3,6 @@ module Utils popen(args, "rb", &block) end - def self.popen_read_text(*args, &block) - popen(args, "r", &block) - end - def self.popen_write(*args, &block) popen(args, "wb", &block) end diff --git a/Library/Homebrew/utils/tty.rb b/Library/Homebrew/utils/tty.rb index 505165dc5..642a33b91 100644 --- a/Library/Homebrew/utils/tty.rb +++ b/Library/Homebrew/utils/tty.rb @@ -6,7 +6,7 @@ module Tty end def width - `/usr/bin/tput cols`.strip.to_i + (`/bin/stty size`.split[1] || 80).to_i end def truncate(string) diff --git a/Library/Homebrew/vendor/README.md b/Library/Homebrew/vendor/README.md index b408631c7..79b118ce4 100644 --- a/Library/Homebrew/vendor/README.md +++ b/Library/Homebrew/vendor/README.md @@ -5,6 +5,8 @@ Vendored Dependencies * [ruby-macho](https://github.com/Homebrew/ruby-macho), version 1.1.0 +* [backports](https://github.com/marcandre/backports), version 3.8.0 + ## Licenses: ### plist @@ -52,3 +54,27 @@ Vendored Dependencies > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > THE SOFTWARE. + +### backports + +> Copyright (c) 2009 Marc-Andre Lafortune + +> Permission is hereby granted, free of charge, to any person obtaining +> a copy of this software and associated documentation files (the +> "Software"), to deal in the Software without restriction, including +> without limitation the rights to use, copy, modify, merge, publish, +> distribute, sublicense, and/or sell copies of the Software, and to +> permit persons to whom the Software is furnished to do so, subject to +> the following conditions: + +> The above copyright notice and this permission notice shall be +> included in all copies or substantial portions of the Software. + +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/Library/Homebrew/vendor/backports/string.rb b/Library/Homebrew/vendor/backports/string.rb new file mode 100644 index 000000000..6299a3fb4 --- /dev/null +++ b/Library/Homebrew/vendor/backports/string.rb @@ -0,0 +1,12 @@ +# Taken from https://github.com/marcandre/backports/blob/v3.8.0/lib/backports/2.4.0/string/match.rb +unless String.method_defined? :match? + class String + def match?(*args) + # Fiber to avoid setting $~ + f = Fiber.new do + !match(*args).nil? + end + f.resume + end + end +end diff --git a/Library/Homebrew/version.rb b/Library/Homebrew/version.rb index 56ef37f72..f99914c4d 100644 --- a/Library/Homebrew/version.rb +++ b/Library/Homebrew/version.rb @@ -280,7 +280,7 @@ class Version private def max(a, b) - a > b ? a : b + (a > b) ? a : b end def tokenize @@ -307,7 +307,7 @@ class Version spec_s = spec.to_s stem = if spec.directory? - spec.basename.to_s + spec.basename elsif %r{((?:sourceforge\.net|sf\.net)/.*)/download$} =~ spec_s Pathname.new(spec.dirname).stem elsif /\.[^a-zA-Z]+$/ =~ spec_s @@ -316,6 +316,11 @@ class Version spec.stem end + # date-based versioning + # e.g. ltopers-v2017-04-14.tar.gz + m = /-v?(\d{4}-\d{2}-\d{2})/.match(stem) + return m.captures.first unless m.nil? + # GitHub tarballs # e.g. https://github.com/foo/bar/tarball/v1.2.3 # e.g. https://github.com/sam-github/libnet/tarball/libnet-1.1.4 @@ -344,11 +349,6 @@ class Version m = /[-v]((?:\d+\.)*\d+)$/.match(spec_s) return m.captures.first unless m.nil? - # date-based versioning - # e.g. ltopers-v2017-04-14.tar.gz - m = /-v?(\d{4}-\d{2}-\d{2})/.match(stem) - return m.captures.first unless m.nil? - # e.g. lame-398-1 m = /-((?:\d)+-\d+)/.match(stem) return m.captures.first unless m.nil? |
