Merge branch 'master' into audit_line_rubocop_part_4_rebase_attempt_1

This commit is contained in:
Gautham Goli 2017-10-12 00:29:19 +05:30
commit 7fa51f71f1
301 changed files with 3998 additions and 2862 deletions

View File

@ -1,17 +1,22 @@
**Please note we will close your issue without comment if you delete, do not read or do not fill out the issue checklist below and provide ALL the requested information. If you repeatedly fail to use the issue template, we will block you from ever submitting issues to Homebrew again.**
# Please always follow these steps:
- [ ] Confirmed this is a problem with running a `brew` command and not `brew install`ing or the post-install behaviour of one or more formulae? If it's a formulae-specific problem please file this issue at https://github.com/Homebrew/homebrew-core/issues/new
- [ ] Confirmed this is a problem with running a `brew` command and not `brew install`ing or the post-install behaviour of one or more formulae? If it's a formulae-specific problem please file this issue at the relevant tap e.g. for Homebrew/homebrew-core https://github.com/Homebrew/homebrew-core/issues/new
- [ ] Ran `brew update` and retried your prior step?
- [ ] Ran `brew doctor`, fixed all issues and retried your prior step?
- [ ] Ran `brew config` and `brew doctor` and included their output with your issue?
**Please note we will close your issue without comment if you delete or do not fill out the issue checklist and provide ALL the requested information.**
To help us debug your issue please explain:
- What you were trying to do (and why)
- What happened (include command output)
- What you expected to happen
- Step-by-step reproduction instructions (by running `brew` commands)
# Or propose a feature:
Please replace this section with a detailed description of your proposed feature, the motivation for it, how it would be relevant to at least 90% of Homebrew users and alternatives considered.
Please note we will close this issue or ask you to create a pull-request if it's something we're not actively planning to work on.
# Features
Please replace this section with:
- a detailed description of your proposed feature
- the motivation for the feature
- how the feature would be relevant to at least 90% of Homebrew users
- what alternatives to the feature you have considered
We will close this issue or ask you to create a pull-request if it's something we're not actively planning to work on.

View File

@ -11,30 +11,31 @@ matrix:
fast_finish: true
include:
- os: osx
osx_image: xcode8.3
osx_image: xcode9
rvm: system
- os: linux
sudo: false
rvm: 2.0.0
rvm: 2.3.3
before_install:
- export HOMEBREW_NO_AUTO_UPDATE=1
- export HOMEBREW_DEVELOPER=1
- git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
HOMEBREW_REPOSITORY="$(brew --repo)";
sudo chown -R "$USER" "$HOMEBREW_REPOSITORY/Library/Taps";
mv "$HOMEBREW_REPOSITORY/Library/Taps" "$PWD/Library";
sudo rm -rf "$HOMEBREW_REPOSITORY";
sudo ln -s "$PWD" "$HOMEBREW_REPOSITORY";
git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot;
else
umask 022;
git fetch --unshallow;
export PATH="$PWD/bin:$PATH";
HOMEBREW_CORE_TAP_DIR="$(brew --repo "homebrew/core")";
mkdir -p "$HOMEBREW_CORE_TAP_DIR";
git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot;
HOMEBREW_TEST_BOT_TAP_DIR="$(brew --repo "homebrew/test-bot")";
ln -s "$HOMEBREW_TEST_BOT_TAP_DIR/.git" "$HOMEBREW_TEST_BOT_TAP_DIR/Formula" "$HOMEBREW_CORE_TAP_DIR";
umask 022;
fi
script:

View File

@ -1,5 +1,5 @@
# Contributing to Homebrew
First time contributing to Homebrew? Read our [Code of Conduct](https://github.com/Homebrew/brew/blob/master/CODEOFCONDUCT.md#code-of-conduct).
First time contributing to Homebrew? Read our [Code of Conduct](https://github.com/Homebrew/brew/blob/master/CODE_OF_CONDUCT.md#code-of-conduct).
### Report a bug

View File

@ -1,5 +1,5 @@
AllCops:
TargetRubyVersion: 2.0
TargetRubyVersion: 2.3
Exclude:
- '**/Casks/**/*'
- '**/vendor/**/*'
@ -123,7 +123,7 @@ Style/Tab:
Enabled: true
# dashes in filenames are typical
Style/FileName:
Naming/FileName:
Regex: !ruby/regexp /^[\w\@\-\+\.]+(\.rb)?$/
# falsely flags e.g. curl formatting arguments as format strings
@ -193,8 +193,25 @@ Style/TrailingCommaInArguments:
EnforcedStyleForMultiline: comma
# we have too many variables like sha256 where this harms readability
Style/VariableNumber:
Naming/VariableNumber:
Enabled: false
Style/WordArray:
MinSize: 4
# we want to add this slowly and manually
Style/FrozenStringLiteralComment:
Enabled: false
# generally rescuing StandardError is fine
Lint/RescueWithoutErrorClass:
Enabled: false
# implicitly allow EOS as we use it everywhere
Naming/HeredocDelimiterNaming:
Blacklist:
- END, EOD, EOF
# we output how to use interpolated strings too often
Lint/InterpolationCheck:
Enabled: false

View File

@ -1,6 +1,5 @@
inherit_from:
- ../.rubocop.yml
- .rubocop_todo.yml
AllCops:
Include:
@ -26,12 +25,30 @@ Lint/NestedMethodDefinition:
Lint/ParenthesesAsGroupedExpression:
Enabled: true
Metrics/BlockNesting:
Max: 5
Metrics/ModuleLength:
Max: 360
Metrics/ParameterLists:
CountKeywordArgs: false
# we won't change backward compatible method names
Naming/MethodName:
Exclude:
- 'compat/**/*'
# we won't change backward compatible predicate names
Naming/PredicateName:
Exclude:
- 'compat/**/*'
NameWhitelist: is_32_bit?, is_64_bit?
Style/BlockDelimiters:
Exclude:
- '**/*_spec.rb'
- '**/shared_examples/**/*.rb'
# so many of these in formulae but none in here
Style/GuardClause:
@ -40,14 +57,3 @@ Style/GuardClause:
# hash-rockets preferred for formulae, a: 1 preferred elsewhere
Style/HashSyntax:
EnforcedStyle: ruby19_no_mixed_keys
# we won't change backward compatible method names
Style/MethodName:
Exclude:
- 'compat/**/*'
# we won't change backward compatible predicate names
Style/PredicateName:
Exclude:
- 'compat/**/*'
NameWhitelist: is_32_bit?, is_64_bit?

View File

@ -1,145 +0,0 @@
# This configuration was generated by
# `rubocop --auto-gen-config --exclude-limit 100`
# on 2017-01-27 21:44:55 +0000 using RuboCop version 0.47.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 17
Lint/HandleExceptions:
Exclude:
- 'cmd/install.rb'
- 'cmd/reinstall.rb'
- 'cmd/tap.rb'
- 'cmd/update-report.rb'
- 'cmd/upgrade.rb'
- 'cmd/uses.rb'
- 'descriptions.rb'
- 'diagnostic.rb'
- 'extend/ENV/super.rb'
- 'extend/pathname.rb'
- 'formula.rb'
- 'formula_versions.rb'
- 'test/ENV_test.rb'
# Offense count: 3
Lint/IneffectiveAccessModifier:
Exclude:
- 'formula.rb'
- 'version.rb'
# Offense count: 1
Lint/Loop:
Exclude:
- 'patch.rb'
# Offense count: 28
Lint/RescueException:
Exclude:
- 'brew.rb'
- 'build.rb'
- 'cmd/fetch.rb'
- 'cmd/reinstall.rb'
- 'cmd/update-report.rb'
- 'debrew.rb'
- 'dev-cmd/pull.rb'
- 'dev-cmd/test.rb'
- 'formula.rb'
- 'formula_installer.rb'
- 'migrator.rb'
- 'postinstall.rb'
- 'readall.rb'
- 'test.rb'
- 'test/ENV_test.rb'
- 'utils/fork.rb'
# Offense count: 1
Lint/ShadowedException:
Exclude:
- 'utils/fork.rb'
# Offense count: 13
# Configuration parameters: CountBlocks.
Metrics/BlockNesting:
Max: 5
# Offense count: 19
# Configuration parameters: CountComments.
Metrics/ModuleLength:
Max: 400
# Offense count: 1
# Configuration parameters: CountKeywordArgs.
Metrics/ParameterLists:
Max: 6
# Offense count: 2
Security/MarshalLoad:
Exclude:
- 'dependency.rb'
- 'utils/fork.rb'
# Offense count: 1
Style/AccessorMethodName:
Exclude:
- 'extend/ENV/super.rb'
# Offense count: 6
Style/ClassVars:
Exclude:
- 'dev-cmd/audit.rb'
- 'formula_installer.rb'
- 'test/support/helper/fs_leak_logger.rb'
# Offense count: 13
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: compact, expanded
Style/EmptyMethod:
Exclude:
- 'debrew/irb.rb'
- 'download_strategy.rb'
- 'extend/ENV/super.rb'
- 'formula.rb'
- 'patch.rb'
# Offense count: 13
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
Exclude:
- 'diagnostic.rb'
- 'utils.rb'
# Offense count: 1
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: module_function, extend_self
Style/ModuleFunction:
Exclude:
- 'os/mac/xcode.rb'
# Offense count: 8
Style/MultilineBlockChain:
Exclude:
- 'cmd/search.rb'
- 'dev-cmd/aspell-dictionaries.rb'
- 'dev-cmd/audit.rb'
- 'dev-cmd/man.rb'
- 'diagnostic.rb'
- 'test/patching_test.rb'
# Offense count: 4
# Cop supports --auto-correct.
Style/MutableConstant:
Exclude:
- 'dependency_collector.rb'
- 'formulary.rb'
- 'tab.rb'
- 'tap.rb'
# Offense count: 8
Style/OpMethod:
Exclude:
- 'dependencies.rb'
- 'install_renamed.rb'
- 'options.rb'

View File

@ -11,11 +11,6 @@ SimpleCov.start do
# tests to be dropped. This causes random fluctuations in test coverage.
merge_timeout 86400
add_filter "/Homebrew/compat/"
add_filter "/Homebrew/dev-cmd/tests.rb"
add_filter "/Homebrew/test/"
add_filter "/Homebrew/vendor/"
if ENV["HOMEBREW_INTEGRATION_TEST"]
command_name "#{ENV["HOMEBREW_INTEGRATION_TEST"]} (#{$PROCESS_ID})"
@ -33,22 +28,32 @@ SimpleCov.start do
end
else
command_name "#{command_name} (#{$PROCESS_ID})"
subdirs = Dir.chdir(SimpleCov.root) { Dir.glob("*") }
.reject { |d| d.end_with?(".rb") || ["test", "vendor"].include?(d) }
.map { |d| "#{d}/**/*.rb" }.join(",")
# Not using this during integration tests makes the tests 4x times faster
# without changing the coverage.
track_files "#{SimpleCov.root}/**/*.rb"
track_files "#{SimpleCov.root}/{#{subdirs},*.rb}"
end
add_filter %r{^/compat/}
add_filter %r{^/dev-cmd/tests.rb$}
add_filter %r{^/test/}
add_filter %r{^/vendor/}
# Add groups and the proper project name to the output.
project_name "Homebrew"
add_group "Cask", "/Homebrew/cask/"
add_group "Commands", %w[/Homebrew/cmd/ /Homebrew/dev-cmd/]
add_group "Extensions", "/Homebrew/extend/"
add_group "OS", %w[/Homebrew/extend/os/ /Homebrew/os/]
add_group "Requirements", "/Homebrew/requirements/"
add_group "Scripts", %w[
/Homebrew/brew.rb
/Homebrew/build.rb
/Homebrew/postinstall.rb
/Homebrew/test.rb
add_group "Cask", %r{^/cask/}
add_group "Commands", [%r{/cmd/}, %r{^/dev-cmd/}]
add_group "Extensions", %r{^/extend/}
add_group "OS", [%r{^/extend/os/}, %r{^/os/}]
add_group "Requirements", %r{^/requirements/}
add_group "Scripts", [
%r{^/brew.rb$},
%r{^/build.rb$},
%r{^/postinstall.rb$},
%r{^/test.rb$},
]
end

View File

@ -5,8 +5,12 @@ end
std_trap = trap("INT") { exit! 130 } # no backtrace thanks
# check ruby version before requiring any modules.
RUBY_TWO = RUBY_VERSION.split(".").first.to_i >= 2
raise "Homebrew must be run under Ruby 2!" unless RUBY_TWO
RUBY_VERSION_SPLIT = RUBY_VERSION.split "."
RUBY_X = RUBY_VERSION_SPLIT[0].to_i
RUBY_Y = RUBY_VERSION_SPLIT[1].to_i
if RUBY_X < 2 || (RUBY_X == 2 && RUBY_Y < 3)
raise "Homebrew must be run under Ruby 2.3!"
end
require "pathname"
HOMEBREW_LIBRARY_PATH = Pathname.new(__FILE__).realpath.parent
@ -105,7 +109,8 @@ begin
possible_tap = OFFICIAL_CMD_TAPS.find { |_, cmds| cmds.include?(cmd) }
possible_tap = Tap.fetch(possible_tap.first) if possible_tap
if possible_tap && !possible_tap.installed?
odie "Unknown command: #{cmd}" if !possible_tap || possible_tap.installed?
brew_uid = HOMEBREW_BREW_FILE.stat.uid
tap_commands = []
if Process.uid.zero? && !brew_uid.zero?
@ -114,9 +119,6 @@ begin
tap_commands += %W[#{HOMEBREW_BREW_FILE} tap #{possible_tap}]
safe_system(*tap_commands)
exec HOMEBREW_BREW_FILE, cmd, *ARGV
else
odie "Unknown command: #{cmd}"
end
end
rescue UsageError => e
require "cmd/help"
@ -144,7 +146,7 @@ rescue MethodDeprecatedError => e
$stderr.puts " #{Formatter.url(e.issues_url)}"
end
exit 1
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
onoe e
if internal_cmd && defined?(OS::ISSUES_URL) &&
!ENV["HOMEBREW_NO_AUTO_UPDATE"]

View File

@ -23,7 +23,7 @@ HOMEBREW_VERSION="$(git -C "$HOMEBREW_REPOSITORY" describe --tags --dirty --abbr
HOMEBREW_USER_AGENT_VERSION="$HOMEBREW_VERSION"
if [[ -z "$HOMEBREW_VERSION" ]]
then
HOMEBREW_VERSION=">1.2.0 (no git repository)"
HOMEBREW_VERSION=">1.2.0 (shallow or no git repository)"
HOMEBREW_USER_AGENT_VERSION="1.X.Y"
fi
@ -105,7 +105,14 @@ then
HOMEBREW_OS_USER_AGENT_VERSION="Mac OS X $HOMEBREW_MACOS_VERSION"
printf -v HOMEBREW_MACOS_VERSION_NUMERIC "%02d%02d%02d" ${HOMEBREW_MACOS_VERSION//./ }
if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "100900" &&
if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "101000" ]]
then
HOMEBREW_SYSTEM_CURL_TOO_OLD="1"
fi
# The system Curl is too old for some modern HTTPS certificates on
# older macOS versions.
if [[ -n "$HOMEBREW_SYSTEM_CURL_TOO_OLD" &&
-x "$HOMEBREW_PREFIX/opt/curl/bin/curl" ]]
then
HOMEBREW_CURL="$HOMEBREW_PREFIX/opt/curl/bin/curl"

View File

@ -112,6 +112,10 @@ class Build
formula.extend(Debrew::Formula) if ARGV.debug?
formula.brew do |_formula, staging|
# For head builds, HOMEBREW_FORMULA_PREFIX should include the commit,
# which is not known until after the formula has been staged.
ENV["HOMEBREW_FORMULA_PREFIX"] = formula.prefix
staging.retain! if ARGV.keep_tmp?
formula.patch
@ -186,7 +190,7 @@ begin
options = Options.create(ARGV.flags_only)
build = Build.new(formula, options)
build.install
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
Marshal.dump(e, error_pipe)
error_pipe.close
exit! 1

View File

@ -25,7 +25,6 @@ require "hbc/scopes"
require "hbc/staged"
require "hbc/system_command"
require "hbc/topological_hash"
require "hbc/underscore_supporting_uri"
require "hbc/url"
require "hbc/utils"
require "hbc/verify"

View File

@ -25,47 +25,5 @@ require "hbc/artifact/zap"
module Hbc
module Artifact
# NOTE: Order is important here!
#
# The `uninstall` stanza should be run first, as it may
# depend on other artifacts still being installed.
#
# We want to extract nested containers before we
# handle any other artifacts.
#
TYPES = [
PreflightBlock,
Uninstall,
NestedContainer,
Installer,
App,
Suite,
Artifact, # generic 'artifact' stanza
Colorpicker,
Pkg,
Prefpane,
Qlplugin,
Dictionary,
Font,
Service,
StageOnly,
Binary,
InputMethod,
InternetPlugin,
AudioUnitPlugin,
VstPlugin,
Vst3Plugin,
ScreenSaver,
PostflightBlock,
Zap,
].freeze
def self.for_cask(cask, options = {})
odebug "Determining which artifacts are present in Cask #{cask}"
TYPES
.select { |klass| klass.me?(cask) }
.map { |klass| klass.new(cask, options) }
end
end
end

View File

@ -0,0 +1,109 @@
module Hbc
module Artifact
class AbstractArtifact
include Comparable
extend Predicable
def self.english_name
@english_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2')
end
def self.english_article
@english_article ||= (english_name =~ /^[aeiou]/i) ? "an" : "a"
end
def self.dsl_key
@dsl_key ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym
end
def self.dirmethod
@dirmethod ||= "#{dsl_key}dir".to_sym
end
def <=>(other)
return unless other.class < AbstractArtifact
return 0 if self.class == other.class
@@sort_order ||= [ # rubocop:disable Style/ClassVars
PreflightBlock,
# The `uninstall` stanza should be run first, as it may
# depend on other artifacts still being installed.
Uninstall,
# We want to extract nested containers before we
# handle any other artifacts.
NestedContainer,
Installer,
[
App,
Suite,
Artifact,
Colorpicker,
Prefpane,
Qlplugin,
Dictionary,
Font,
Service,
InputMethod,
InternetPlugin,
AudioUnitPlugin,
VstPlugin,
Vst3Plugin,
ScreenSaver,
],
# `pkg` should be run before `binary`, so
# targets are created prior to linking.
Pkg,
Binary,
PostflightBlock,
Zap,
].each_with_index.flat_map { |classes, i| [*classes].map { |c| [c, i] } }.to_h
(@@sort_order[self.class] <=> @@sort_order[other.class]).to_i
end
# TODO: this sort of logic would make more sense in dsl.rb, or a
# constructor called from dsl.rb, so long as that isn't slow.
def self.read_script_arguments(arguments, stanza, default_arguments = {}, override_arguments = {}, key = nil)
# TODO: when stanza names are harmonized with class names,
# stanza may not be needed as an explicit argument
description = key ? "#{stanza} #{key.inspect}" : stanza.to_s
# backward-compatible string value
arguments = { executable: arguments } if arguments.is_a?(String)
# key sanity
permitted_keys = [:args, :input, :executable, :must_succeed, :sudo, :print_stdout, :print_stderr]
unknown_keys = arguments.keys - permitted_keys
unless unknown_keys.empty?
opoo %Q{Unknown arguments to #{description} -- #{unknown_keys.inspect} (ignored). Running "brew update; brew cleanup; brew cask cleanup" will likely fix it.}
end
arguments.select! { |k| permitted_keys.include?(k) }
# key warnings
override_keys = override_arguments.keys
ignored_keys = arguments.keys & override_keys
unless ignored_keys.empty?
onoe "Some arguments to #{description} will be ignored -- :#{unknown_keys.inspect} (overridden)."
end
# extract executable
executable = arguments.key?(:executable) ? arguments.delete(:executable) : nil
arguments = default_arguments.merge arguments
arguments.merge! override_arguments
[executable, arguments]
end
attr_reader :cask
def initialize(cask)
@cask = cask
end
def to_s
"#{summarize} (#{self.class.english_name})"
end
end
end
end

View File

@ -1,39 +1,45 @@
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
module Hbc
module Artifact
class AbstractFlightBlock < Base
def self.artifact_dsl_key
class AbstractFlightBlock < AbstractArtifact
def self.dsl_key
super.to_s.sub(/_block$/, "").to_sym
end
def self.uninstall_artifact_dsl_key
artifact_dsl_key.to_s.prepend("uninstall_").to_sym
def self.uninstall_dsl_key
dsl_key.to_s.prepend("uninstall_").to_sym
end
def self.class_for_dsl_key(dsl_key)
Object.const_get("Hbc::DSL::#{dsl_key.to_s.split("_").collect(&:capitalize).join}")
attr_reader :directives
def initialize(cask, **directives)
super(cask)
@directives = directives
end
def self.me?(cask)
cask.artifacts[artifact_dsl_key].any? ||
cask.artifacts[uninstall_artifact_dsl_key].any?
def install_phase(**)
abstract_phase(self.class.dsl_key)
end
def install_phase
abstract_phase(self.class.artifact_dsl_key)
end
def uninstall_phase
abstract_phase(self.class.uninstall_artifact_dsl_key)
def uninstall_phase(**)
abstract_phase(self.class.uninstall_dsl_key)
end
private
def abstract_phase(dsl_key)
@cask.artifacts[dsl_key].each do |block|
self.class.class_for_dsl_key(dsl_key).new(@cask).instance_eval(&block)
def class_for_dsl_key(dsl_key)
namespace = self.class.name.to_s.sub(/::.*::.*$/, "")
self.class.const_get("#{namespace}::DSL::#{dsl_key.to_s.split("_").collect(&:capitalize).join}")
end
def abstract_phase(dsl_key)
return if (block = directives[dsl_key]).nil?
class_for_dsl_key(dsl_key).new(cask).instance_eval(&block)
end
def summarize
directives.keys.map(&:to_s).join(", ")
end
end
end

View File

@ -1,11 +1,11 @@
require "pathname"
require "timeout"
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
module Hbc
module Artifact
class UninstallBase < Base
class AbstractUninstall < AbstractArtifact
ORDERED_DIRECTIVES = [
:early_script,
:launchctl,
@ -20,26 +20,42 @@ module Hbc
:rmdir,
].freeze
def dispatch_uninstall_directives
directives_set = @cask.artifacts[stanza]
ohai "Running #{stanza} process for #{@cask}; your password may be necessary"
directives_set.each do |directives|
warn_for_unknown_directives(directives)
def self.from_args(cask, **directives)
new(cask, directives)
end
ORDERED_DIRECTIVES.each do |directive_sym|
directives_set.select { |h| h.key?(directive_sym) }.each do |directives|
args = directives[directive_sym]
send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args))
attr_reader :directives
def initialize(cask, directives)
super(cask)
directives[:signal] = [*directives[:signal]].flatten.each_slice(2).to_a
@directives = directives
end
def to_h
directives.to_h
end
def summarize
to_h.flat_map { |key, val| [*val].map { |v| "#{key.inspect} => #{v.inspect}" } }.join(", ")
end
private
def dispatch_uninstall_directives(**options)
ohai "Running #{stanza} process for #{@cask}; your password may be necessary"
warn_for_unknown_directives(directives)
ORDERED_DIRECTIVES.each do |directive_sym|
next unless directives.key?(directive_sym)
args = directives[directive_sym]
send("uninstall_#{directive_sym}", *(args.is_a?(Hash) ? [args] : args), **options)
end
end
def stanza
self.class.artifact_dsl_key
self.class.dsl_key
end
def warn_for_unknown_directives(directives)
@ -51,18 +67,18 @@ module Hbc
# Preserve prior functionality of script which runs first. Should rarely be needed.
# :early_script should not delete files, better defer that to :script.
# If Cask writers never need :early_script it may be removed in the future.
def uninstall_early_script(directives)
uninstall_script(directives, directive_name: :early_script)
def uninstall_early_script(directives, **options)
uninstall_script(directives, directive_name: :early_script, **options)
end
# :launchctl must come before :quit/:signal for cases where app would instantly re-launch
def uninstall_launchctl(*services)
def uninstall_launchctl(*services, command: nil, **_)
services.each do |service|
ohai "Removing launchctl service #{service}"
[false, true].each do |with_sudo|
plist_status = @command.run("/bin/launchctl", args: ["list", service], sudo: with_sudo, print_stderr: false).stdout
plist_status = command.run("/bin/launchctl", args: ["list", service], sudo: with_sudo, print_stderr: false).stdout
if plist_status =~ /^\{/
@command.run!("/bin/launchctl", args: ["remove", service], sudo: with_sudo)
command.run!("/bin/launchctl", args: ["remove", service], sudo: with_sudo)
sleep 1
end
paths = ["/Library/LaunchAgents/#{service}.plist",
@ -70,19 +86,19 @@ module Hbc
paths.each { |elt| elt.prepend(ENV["HOME"]) } unless with_sudo
paths = paths.map { |elt| Pathname(elt) }.select(&:exist?)
paths.each do |path|
@command.run!("/bin/rm", args: ["-f", "--", path], sudo: with_sudo)
command.run!("/bin/rm", args: ["-f", "--", path], sudo: with_sudo)
end
# undocumented and untested: pass a path to uninstall :launchctl
next unless Pathname(service).exist?
@command.run!("/bin/launchctl", args: ["unload", "-w", "--", service], sudo: with_sudo)
@command.run!("/bin/rm", args: ["-f", "--", service], sudo: with_sudo)
command.run!("/bin/launchctl", args: ["unload", "-w", "--", service], sudo: with_sudo)
command.run!("/bin/rm", args: ["-f", "--", service], sudo: with_sudo)
sleep 1
end
end
end
def running_processes(bundle_id)
@command.run!("/bin/launchctl", args: ["list"]).stdout.lines
def running_processes(bundle_id, command: nil)
command.run!("/bin/launchctl", args: ["list"]).stdout.lines
.map { |line| line.chomp.split("\t") }
.map { |pid, state, id| [pid.to_i, state.to_i, id] }
.select do |fields|
@ -92,16 +108,16 @@ module Hbc
end
# :quit/:signal must come before :kext so the kext will not be in use by a running process
def uninstall_quit(*bundle_ids)
def uninstall_quit(*bundle_ids, command: nil, **_)
bundle_ids.each do |bundle_id|
ohai "Quitting application ID #{bundle_id}"
next if running_processes(bundle_id).empty?
@command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{bundle_id}" to quit)], sudo: true)
next if running_processes(bundle_id, command: command).empty?
command.run!("/usr/bin/osascript", args: ["-e", %Q(tell application id "#{bundle_id}" to quit)], sudo: true)
begin
Timeout.timeout(3) do
Kernel.loop do
break if running_processes(bundle_id).empty?
break if running_processes(bundle_id, command: command).empty?
end
end
rescue Timeout::Error
@ -111,15 +127,15 @@ module Hbc
end
# :signal should come after :quit so it can be used as a backup when :quit fails
def uninstall_signal(*signals)
signals.flatten.each_slice(2) do |pair|
def uninstall_signal(*signals, command: nil, **_)
signals.each do |pair|
unless pair.size == 2
raise CaskInvalidError.new(@cask, "Each #{stanza} :signal must consist of 2 elements.")
raise CaskInvalidError.new(cask, "Each #{stanza} :signal must consist of 2 elements.")
end
signal, bundle_id = pair
ohai "Signalling '#{signal}' to application ID '#{bundle_id}'"
pids = running_processes(bundle_id).map(&:first)
pids = running_processes(bundle_id, command: command).map(&:first)
next unless pids.any?
# Note that unlike :quit, signals are sent from the current user (not
# upgraded to the superuser). This is a todo item for the future, but
@ -133,10 +149,10 @@ module Hbc
end
end
def uninstall_login_item(*login_items)
def uninstall_login_item(*login_items, command: nil, **_)
login_items.each do |name|
ohai "Removing login item #{name}"
@command.run!("/usr/bin/osascript",
command.run!("/usr/bin/osascript",
args: ["-e", %Q(tell application "System Events" to delete every login item whose name is "#{name}")],
sudo: false)
sleep 1
@ -144,23 +160,24 @@ module Hbc
end
# :kext should be unloaded before attempting to delete the relevant file
def uninstall_kext(*kexts)
def uninstall_kext(*kexts, command: nil, **_)
kexts.each do |kext|
ohai "Unloading kernel extension #{kext}"
is_loaded = @command.run!("/usr/sbin/kextstat", args: ["-l", "-b", kext], sudo: true).stdout
is_loaded = command.run!("/usr/sbin/kextstat", args: ["-l", "-b", kext], sudo: true).stdout
if is_loaded.length > 1
@command.run!("/sbin/kextunload", args: ["-b", kext], sudo: true)
command.run!("/sbin/kextunload", args: ["-b", kext], sudo: true)
sleep 1
end
@command.run!("/usr/sbin/kextfind", args: ["-b", kext], sudo: true).stdout.chomp.lines.each do |kext_path|
command.run!("/usr/sbin/kextfind", args: ["-b", kext], sudo: true).stdout.chomp.lines.each do |kext_path|
ohai "Removing kernel extension #{kext_path}"
@command.run!("/bin/rm", args: ["-rf", kext_path], sudo: true)
command.run!("/bin/rm", args: ["-rf", kext_path], sudo: true)
end
end
end
# :script must come before :pkgutil, :delete, or :trash so that the script file is not already deleted
def uninstall_script(directives, directive_name: :script)
def uninstall_script(directives, directive_name: :script, force: false, command: nil, **_)
# TODO: Create a common `Script` class to run this and Artifact::Installer.
executable, script_arguments = self.class.read_script_arguments(directives,
"uninstall",
{ must_succeed: true, sudo: false },
@ -168,25 +185,25 @@ module Hbc
directive_name)
ohai "Running uninstall script #{executable}"
raise CaskInvalidError.new(@cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil?
executable_path = @cask.staged_path.join(executable)
raise CaskInvalidError.new(cask, "#{stanza} :#{directive_name} without :executable.") if executable.nil?
executable_path = cask.staged_path.join(executable)
unless executable_path.exist?
message = "uninstall script #{executable} does not exist"
raise CaskError, "#{message}." unless force?
raise CaskError, "#{message}." unless force
opoo "#{message}, skipping."
return
end
@command.run("/bin/chmod", args: ["--", "+x", executable_path])
@command.run(executable_path, script_arguments)
command.run("/bin/chmod", args: ["--", "+x", executable_path])
command.run(executable_path, script_arguments)
sleep 1
end
def uninstall_pkgutil(*pkgs)
def uninstall_pkgutil(*pkgs, command: nil, **_)
ohai "Uninstalling packages:"
pkgs.each do |regex|
Hbc::Pkg.all_matching(regex, @command).each do |pkg|
Hbc::Pkg.all_matching(regex, command).each do |pkg|
puts pkg.package_id
pkg.uninstall
end
@ -215,28 +232,28 @@ module Hbc
end
end
def uninstall_delete(*paths)
def uninstall_delete(*paths, command: nil, **_)
return if paths.empty?
ohai "Removing files:"
each_resolved_path(:delete, paths) do |path, resolved_paths|
puts path
@command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "-r", "-f", "--"], input: resolved_paths.join("\0"), sudo: true)
command.run!("/usr/bin/xargs", args: ["-0", "--", "/bin/rm", "-r", "-f", "--"], input: resolved_paths.join("\0"), sudo: true)
end
end
def uninstall_trash(*paths)
def uninstall_trash(*paths, **options)
return if paths.empty?
resolved_paths = each_resolved_path(:trash, paths).to_a
ohai "Trashing files:"
puts resolved_paths.map(&:first)
trash_paths(*resolved_paths.flat_map(&:last))
trash_paths(*resolved_paths.flat_map(&:last), **options)
end
def trash_paths(*paths)
@command.run!("/usr/bin/osascript", args: ["-e", <<-'EOS'.undent, *paths])
def trash_paths(*paths, command: nil, **_)
result = command.run!("/usr/bin/osascript", args: ["-e", <<-'EOS'.undent, *paths])
on run argv
repeat with i from 1 to (count argv)
set item i of argv to (item i of argv as POSIX file)
@ -250,7 +267,7 @@ module Hbc
set trashedItem to POSIX path of (item i of trashedItems as string)
set output to output & trashedItem
if i < count trashedItems then
set output to output & (do shell script "printf \"\\0\"")
set output to output & character id 0
end if
end repeat
@ -258,9 +275,12 @@ module Hbc
end tell
end run
EOS
# Remove AppleScript's automatic newline.
result.tap { |r| r.stdout.sub!(/\n$/, "") }
end
def uninstall_rmdir(*directories)
def uninstall_rmdir(*directories, command: nil, **_)
return if directories.empty?
ohai "Removing directories if empty:"
@ -268,10 +288,10 @@ module Hbc
puts path
resolved_paths.select(&:directory?).each do |resolved_path|
if (ds_store = resolved_path.join(".DS_Store")).exist?
@command.run!("/bin/rm", args: ["-f", "--", ds_store], sudo: true, print_stderr: false)
command.run!("/bin/rm", args: ["-f", "--", ds_store], sudo: true, print_stderr: false)
end
@command.run("/bin/rmdir", args: ["--", resolved_path], sudo: true, print_stderr: false)
command.run("/bin/rmdir", args: ["--", resolved_path], sudo: true, print_stderr: false)
end
end
end

View File

@ -5,21 +5,32 @@ require "hbc/utils/hash_validator"
module Hbc
module Artifact
class Artifact < Moved
def self.artifact_english_name
def self.english_name
"Generic Artifact"
end
def self.artifact_dirmethod
:appdir
def self.from_args(cask, *args)
source_string, target_hash = args
if source_string.nil?
raise CaskInvalidError.new(cask.token, "no source given for #{english_name}")
end
unless target_hash.is_a?(Hash)
raise CaskInvalidError.new(cask.token, "target required for #{english_name} '#{source_string}'")
end
def load_specification(artifact_spec)
source_string, target_hash = artifact_spec
raise CaskInvalidError.new(@cask.token, "no source given for artifact") if source_string.nil?
@source = @cask.staged_path.join(source_string)
raise CaskInvalidError.new(@cask.token, "target required for generic artifact #{source_string}") unless target_hash.is_a?(Hash)
target_hash.extend(HashValidator).assert_valid_keys(:target)
@target = Pathname.new(target_hash[:target])
new(cask, source_string, **target_hash)
end
def self.resolve_target(target)
Pathname(target)
end
def initialize(cask, source, target: nil)
super(cask, source, target: target)
end
end
end

View File

@ -1,80 +0,0 @@
module Hbc
module Artifact
class Base
extend Predicable
def self.artifact_name
@artifact_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase
end
def self.artifact_english_name
@artifact_english_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2')
end
def self.artifact_english_article
@artifact_english_article ||= (artifact_english_name =~ /^[aeiou]/i) ? "an" : "a"
end
def self.artifact_dsl_key
@artifact_dsl_key ||= artifact_name.to_sym
end
def self.artifact_dirmethod
@artifact_dirmethod ||= "#{artifact_name}dir".to_sym
end
def self.me?(cask)
cask.artifacts[artifact_dsl_key].any?
end
attr_reader :force
# TODO: this sort of logic would make more sense in dsl.rb, or a
# constructor called from dsl.rb, so long as that isn't slow.
def self.read_script_arguments(arguments, stanza, default_arguments = {}, override_arguments = {}, key = nil)
# TODO: when stanza names are harmonized with class names,
# stanza may not be needed as an explicit argument
description = key ? "#{stanza} #{key.inspect}" : stanza.to_s
# backward-compatible string value
arguments = { executable: arguments } if arguments.is_a?(String)
# key sanity
permitted_keys = [:args, :input, :executable, :must_succeed, :sudo, :print_stdout, :print_stderr]
unknown_keys = arguments.keys - permitted_keys
unless unknown_keys.empty?
opoo %Q{Unknown arguments to #{description} -- #{unknown_keys.inspect} (ignored). Running "brew update; brew cleanup; brew cask cleanup" will likely fix it.}
end
arguments.select! { |k| permitted_keys.include?(k) }
# key warnings
override_keys = override_arguments.keys
ignored_keys = arguments.keys & override_keys
unless ignored_keys.empty?
onoe "Some arguments to #{description} will be ignored -- :#{unknown_keys.inspect} (overridden)."
end
# extract executable
executable = arguments.key?(:executable) ? arguments.delete(:executable) : nil
arguments = default_arguments.merge arguments
arguments.merge! override_arguments
[executable, arguments]
end
def summary
{}
end
attr_predicate :force?, :verbose?
def initialize(cask, command: SystemCommand, force: false, verbose: false)
@cask = cask
@command = command
@force = force
@verbose = verbose
end
end
end
end

View File

@ -3,13 +3,13 @@ require "hbc/artifact/symlinked"
module Hbc
module Artifact
class Binary < Symlinked
def link
super
def link(command: nil, **options)
super(command: command, **options)
return if source.executable?
if source.writable?
FileUtils.chmod "+x", source
else
@command.run!("/bin/chmod", args: ["+x", source], sudo: true)
command.run!("/bin/chmod", args: ["+x", source], sudo: true)
end
end
end

View File

@ -1,30 +1,83 @@
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
module Hbc
module Artifact
class Installer < Base
def install_phase
@cask.artifacts[self.class.artifact_dsl_key].each do |artifact|
if artifact.manual
class Installer < AbstractArtifact
VALID_KEYS = Set.new [
:manual,
:script,
]
module ManualInstaller
def install_phase(**)
puts <<-EOS.undent
To complete the installation of Cask #{@cask}, you must also
To complete the installation of Cask #{cask}, you must also
run the installer at
'#{@cask.staged_path.join(artifact.manual)}'
'#{path}'
EOS
else
executable, script_arguments = self.class.read_script_arguments(artifact.script,
self.class.artifact_dsl_key.to_s,
{ must_succeed: true, sudo: false },
print_stdout: true)
ohai "Running #{self.class.artifact_dsl_key} script #{executable}"
raise CaskInvalidError.new(@cask, "#{self.class.artifact_dsl_key} missing executable") if executable.nil?
executable_path = @cask.staged_path.join(executable)
@command.run("/bin/chmod", args: ["--", "+x", executable_path]) if File.exist?(executable_path)
@command.run(executable_path, script_arguments)
end
end
module ScriptInstaller
def install_phase(command: nil, **_)
ohai "Running #{self.class.dsl_key} script '#{path.relative_path_from(cask.staged_path)}'"
FileUtils.chmod "+x", path unless path.executable?
command.run(path, **args)
end
end
def self.from_args(cask, **args)
raise CaskInvalidError.new(cask, "'installer' stanza requires an argument.") if args.empty?
if args.key?(:script) && !args[:script].respond_to?(:key?)
if args.key?(:executable)
raise CaskInvalidError.new(cask, "'installer' stanza gave arguments for both :script and :executable.")
end
args[:executable] = args[:script]
args.delete(:script)
args = { script: args }
end
unless args.keys.count == 1
raise CaskInvalidError.new(cask, "invalid 'installer' stanza: Only one of #{VALID_KEYS.inspect} is permitted.")
end
args.extend(HashValidator).assert_valid_keys(*VALID_KEYS)
new(cask, **args)
end
attr_reader :path, :args
def initialize(cask, **args)
super(cask)
if args.key?(:manual)
@path = cask.staged_path.join(args[:manual])
@args = []
extend(ManualInstaller)
return
end
path, @args = self.class.read_script_arguments(
args[:script], self.class.dsl_key.to_s, { must_succeed: true, sudo: false }, print_stdout: true
)
raise CaskInvalidError.new(cask, "#{self.class.dsl_key} missing executable") if path.nil?
path = Pathname(path)
@path = path.absolute? ? path : cask.staged_path.join(path)
extend(ScriptInstaller)
end
def summarize
path.relative_path_from(cask.staged_path).to_s
end
def to_h
{ path: path.relative_path_from(cask.staged_path).to_s }.tap do |h|
h[:args] = args unless is_a?(ManualInstaller)
end
end
end
end

View File

@ -4,63 +4,61 @@ module Hbc
module Artifact
class Moved < Relocated
def self.english_description
"#{artifact_english_name}s"
"#{english_name}s"
end
def install_phase
each_artifact(&method(:move))
def install_phase(**options)
move(**options)
end
def uninstall_phase
each_artifact(&method(:delete))
def uninstall_phase(**options)
delete(**options)
end
def summarize_installed
if target.exist?
"#{printable_target} (#{target.abv})"
else
Formatter.error(printable_target, label: "Missing #{self.class.english_name}")
end
end
private
def move
def move(force: false, command: nil, **options)
if Utils.path_occupied?(target)
message = "It seems there is already #{self.class.artifact_english_article} #{self.class.artifact_english_name} at '#{target}'"
raise CaskError, "#{message}." unless force?
message = "It seems there is already #{self.class.english_article} #{self.class.english_name} at '#{target}'"
raise CaskError, "#{message}." unless force
opoo "#{message}; overwriting."
delete
delete(force: force, command: command, **options)
end
unless source.exist?
raise CaskError, "It seems the #{self.class.artifact_english_name} source '#{source}' is not there."
raise CaskError, "It seems the #{self.class.english_name} source '#{source}' is not there."
end
ohai "Moving #{self.class.artifact_english_name} '#{source.basename}' to '#{target}'."
ohai "Moving #{self.class.english_name} '#{source.basename}' to '#{target}'."
target.dirname.mkpath
if target.parent.writable?
FileUtils.move(source, target)
else
SystemCommand.run("/bin/mv", args: [source, target], sudo: true)
command.run("/bin/mv", args: [source, target], sudo: true)
end
add_altname_metadata target, source.basename.to_s
add_altname_metadata(target, source.basename, command: command)
end
def delete
ohai "Removing #{self.class.artifact_english_name} '#{target}'."
raise CaskError, "Cannot remove undeletable #{self.class.artifact_english_name}." if MacOS.undeletable?(target)
def delete(force: false, command: nil, **_)
ohai "Removing #{self.class.english_name} '#{target}'."
raise CaskError, "Cannot remove undeletable #{self.class.english_name}." if MacOS.undeletable?(target)
return unless Utils.path_occupied?(target)
if target.parent.writable? && !force
target.rmtree
else
Utils.gain_permissions_remove(target, command: @command)
end
end
def summarize_artifact(artifact_spec)
load_specification artifact_spec
if target.exist?
"#{printable_target} (#{target.abv})"
else
Formatter.error(printable_target, label: "Missing #{self.class.artifact_english_name}")
Utils.gain_permissions_remove(target, command: command)
end
end
end

View File

@ -1,23 +1,35 @@
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
module Hbc
module Artifact
class NestedContainer < Base
def install_phase
@cask.artifacts[:nested_container].each { |container| extract(container) }
class NestedContainer < AbstractArtifact
attr_reader :path
def initialize(cask, path)
super(cask)
@path = cask.staged_path.join(path)
end
def extract(container_relative_path)
source = @cask.staged_path.join(container_relative_path)
container = Container.for_path(source, @command)
def install_phase(**options)
extract(**options)
end
private
def summarize
path.relative_path_from(cask.staged_path).to_s
end
def extract(command: nil, verbose: nil, **_)
container = Container.for_path(path, command)
unless container
raise CaskError, "Aw dang, could not identify nested container at '#{source}'"
end
ohai "Extracting nested container #{source.basename}"
container.new(@cask, source, @command, verbose: verbose?).extract
FileUtils.remove_entry_secure(source)
ohai "Extracting nested container #{path.relative_path_from(cask.staged_path)}"
container.new(cask, path, command, verbose: verbose).extract
FileUtils.remove_entry_secure(path)
end
end
end

View File

@ -1,4 +1,4 @@
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
require "hbc/utils/hash_validator"
@ -6,62 +6,61 @@ require "vendor/plist/plist"
module Hbc
module Artifact
class Pkg < Base
class Pkg < AbstractArtifact
attr_reader :pkg_relative_path
def self.artifact_dsl_key
:pkg
def self.from_args(cask, path, **stanza_options)
stanza_options.extend(HashValidator).assert_valid_keys(
:allow_untrusted, :choices
)
new(cask, path, **stanza_options)
end
def load_pkg_description(pkg_description)
@pkg_relative_path = pkg_description.shift
@pkg_install_opts = pkg_description.shift
begin
if @pkg_install_opts.respond_to?(:keys)
@pkg_install_opts.extend(HashValidator).assert_valid_keys(:allow_untrusted, :choices)
elsif @pkg_install_opts
raise
end
raise if pkg_description.nil?
rescue StandardError
raise CaskInvalidError.new(@cask, "Bad pkg stanza")
end
attr_reader :path, :stanza_options
def initialize(cask, path, **stanza_options)
super(cask)
@path = cask.staged_path.join(path)
@stanza_options = stanza_options
end
def pkg_install_opts(opt)
@pkg_install_opts[opt] if @pkg_install_opts.respond_to?(:keys)
def summarize
path.relative_path_from(cask.staged_path).to_s
end
def install_phase
@cask.artifacts[:pkg].each { |pkg_description| run_installer(pkg_description) }
def install_phase(**options)
run_installer(**options)
end
def run_installer(pkg_description)
load_pkg_description pkg_description
ohai "Running installer for #{@cask}; your password may be necessary."
private
def run_installer(command: nil, verbose: false, **_options)
ohai "Running installer for #{cask}; your password may be necessary."
ohai "Package installers may write to any location; options such as --appdir are ignored."
source = @cask.staged_path.join(pkg_relative_path)
unless source.exist?
raise CaskError, "pkg source file not found: '#{source}'"
unless path.exist?
raise CaskError, "pkg source file not found: '#{path.relative_path_from(cask.staged_path)}'"
end
args = [
"-pkg", source,
"-pkg", path,
"-target", "/"
]
args << "-verboseR" if verbose?
args << "-allowUntrusted" if pkg_install_opts :allow_untrusted
args << "-verboseR" if verbose
if stanza_options.fetch(:allow_untrusted, false)
args << "-allowUntrusted"
end
with_choices_file do |choices_path|
args << "-applyChoiceChangesXML" << choices_path if choices_path
@command.run!("/usr/sbin/installer", sudo: true, args: args, print_stdout: true)
command.run!("/usr/sbin/installer", sudo: true, args: args, print_stdout: true)
end
end
def with_choices_file
return yield nil unless pkg_install_opts(:choices)
choices = stanza_options.fetch(:choices, {})
return yield nil if choices.empty?
Tempfile.open(["choices", ".xml"]) do |file|
begin
file.write Plist::Emit.dump(pkg_install_opts(:choices))
file.write Plist::Emit.dump(choices)
file.close
yield file.path
ensure

View File

@ -3,7 +3,7 @@ require "hbc/artifact/moved"
module Hbc
module Artifact
class Prefpane < Moved
def self.artifact_english_name
def self.english_name
"Preference Pane"
end
end

View File

@ -3,22 +3,24 @@ require "hbc/artifact/moved"
module Hbc
module Artifact
class Qlplugin < Moved
def self.artifact_english_name
def self.english_name
"QuickLook Plugin"
end
def install_phase
super
reload_quicklook
def install_phase(**options)
super(**options)
reload_quicklook(**options)
end
def uninstall_phase
super
reload_quicklook
def uninstall_phase(**options)
super(**options)
reload_quicklook(**options)
end
def reload_quicklook
@command.run!("/usr/bin/qlmanage", args: ["-r"])
private
def reload_quicklook(command: nil, **_)
command.run!("/usr/bin/qlmanage", args: ["-r"])
end
end
end

View File

@ -1,65 +1,79 @@
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
require "hbc/utils/hash_validator"
module Hbc
module Artifact
class Relocated < Base
def summary
{
english_description: self.class.english_description,
contents: @cask.artifacts[self.class.artifact_dsl_key].map(&method(:summarize_artifact)).compact,
}
class Relocated < AbstractArtifact
def self.from_args(cask, *args)
source_string, target_hash = args
if target_hash
raise CaskInvalidError unless target_hash.respond_to?(:keys)
target_hash.extend(HashValidator).assert_valid_keys(:target)
end
target_hash ||= {}
new(cask, source_string, **target_hash)
end
def self.resolve_target(target)
Hbc.public_send(dirmethod).join(target)
end
attr_reader :source, :target
def printable_target
target.to_s.sub(/^#{ENV['HOME']}(#{File::SEPARATOR}|$)/, "~/")
def initialize(cask, source, target: nil)
super(cask)
@source_string = source.to_s
@target_string = target.to_s
source = cask.staged_path.join(source)
@source = source
target ||= source.basename
@target = self.class.resolve_target(target)
end
def to_a
[@source_string].tap do |ary|
ary << { target: @target_string } unless @target_string.empty?
end
end
def summarize
target_string = @target_string.empty? ? "" : " -> #{@target_string}"
"#{@source_string}#{target_string}"
end
private
ALT_NAME_ATTRIBUTE = "com.apple.metadata:kMDItemAlternateNames".freeze
# Try to make the asset searchable under the target name. Spotlight
# respects this attribute for many filetypes, but ignores it for App
# bundles. Alfred 2.2 respects it even for App bundles.
def add_altname_metadata(file, altname)
return if altname.casecmp(file.basename).zero?
def add_altname_metadata(file, altname, command: nil)
return if altname.to_s.casecmp(file.basename.to_s).zero?
odebug "Adding #{ALT_NAME_ATTRIBUTE} metadata"
altnames = @command.run("/usr/bin/xattr",
args: ["-p", ALT_NAME_ATTRIBUTE, file.to_s],
altnames = command.run("/usr/bin/xattr",
args: ["-p", ALT_NAME_ATTRIBUTE, file],
print_stderr: false).stdout.sub(/\A\((.*)\)\Z/, '\1')
odebug "Existing metadata is: '#{altnames}'"
altnames.concat(", ") unless altnames.empty?
altnames.concat(%Q("#{altname}"))
altnames = "(#{altnames})"
# Some packges are shipped as u=rx (e.g. Bitcoin Core)
@command.run!("/bin/chmod", args: ["--", "u+rw", file, file.realpath])
# Some packages are shipped as u=rx (e.g. Bitcoin Core)
command.run!("/bin/chmod", args: ["--", "u+rw", file, file.realpath])
@command.run!("/usr/bin/xattr",
command.run!("/usr/bin/xattr",
args: ["-w", ALT_NAME_ATTRIBUTE, altnames, file],
print_stderr: false)
end
def each_artifact
@cask.artifacts[self.class.artifact_dsl_key].each do |artifact|
load_specification(artifact)
yield
end
end
def load_specification(artifact_spec)
source_string, target_hash = artifact_spec
raise CaskInvalidError if source_string.nil?
@source = @cask.staged_path.join(source_string)
if target_hash
raise CaskInvalidError unless target_hash.respond_to?(:keys)
target_hash.extend(HashValidator).assert_valid_keys(:target)
@target = Hbc.send(self.class.artifact_dirmethod).join(target_hash[:target])
else
@target = Hbc.send(self.class.artifact_dirmethod).join(source.basename)
end
def printable_target
target.to_s.sub(/^#{ENV['HOME']}(#{File::SEPARATOR}|$)/, "~/")
end
end
end

View File

@ -1,10 +1,22 @@
require "hbc/artifact/base"
require "hbc/artifact/abstract_artifact"
module Hbc
module Artifact
class StageOnly < Base
def self.artifact_dsl_key
:stage_only
class StageOnly < AbstractArtifact
def self.from_args(cask, *args)
if args != [true]
raise CaskInvalidError.new(cask.token, "'stage_only' takes only a single argument: true")
end
new(cask)
end
def initialize(cask)
super(cask)
end
def to_a
[true]
end
end
end

View File

@ -3,11 +3,11 @@ require "hbc/artifact/moved"
module Hbc
module Artifact
class Suite < Moved
def self.artifact_english_name
def self.english_name
"App Suite"
end
def self.artifact_dirmethod
def self.dirmethod
:appdir
end
end

View File

@ -8,47 +8,18 @@ module Hbc
end
def self.english_description
"#{artifact_english_name} #{link_type_english_name}s"
"#{english_name} #{link_type_english_name}s"
end
def install_phase
each_artifact(&method(:link))
def install_phase(**options)
link(**options)
end
def uninstall_phase
each_artifact(&method(:unlink))
def uninstall_phase(**options)
unlink(**options)
end
private
def link
unless source.exist?
raise CaskError, "It seems the #{self.class.link_type_english_name.downcase} source '#{source}' is not there."
end
if target.exist? && !target.symlink?
raise CaskError, "It seems there is already #{self.class.artifact_english_article} #{self.class.artifact_english_name} at '#{target}'; not linking."
end
ohai "Linking #{self.class.artifact_english_name} '#{source.basename}' to '#{target}'."
create_filesystem_link(source, target)
end
def unlink
return unless target.symlink?
ohai "Unlinking #{self.class.artifact_english_name} '#{target}'."
target.delete
end
def create_filesystem_link(source, target)
target.dirname.mkpath
@command.run!("/bin/ln", args: ["-h", "-f", "-s", "--", source, target])
add_altname_metadata source, target.basename.to_s
end
def summarize_artifact(artifact_spec)
load_specification artifact_spec
def summarize_installed
if target.symlink? && target.exist? && target.readlink.exist?
"#{printable_target} -> #{target.readlink} (#{target.readlink.abv})"
else
@ -61,6 +32,33 @@ module Hbc
Formatter.error(string, label: "Broken Link")
end
end
private
def link(**options)
unless source.exist?
raise CaskError, "It seems the #{self.class.link_type_english_name.downcase} source '#{source}' is not there."
end
if target.exist? && !target.symlink?
raise CaskError, "It seems there is already #{self.class.english_article} #{self.class.english_name} at '#{target}'; not linking."
end
ohai "Linking #{self.class.english_name} '#{source.basename}' to '#{target}'."
create_filesystem_link(**options)
end
def unlink(**)
return unless target.symlink?
ohai "Unlinking #{self.class.english_name} '#{target}'."
target.delete
end
def create_filesystem_link(command: nil, **_)
target.dirname.mkpath
command.run!("/bin/ln", args: ["-h", "-f", "-s", "--", source, target])
add_altname_metadata(source, target.basename, command: command)
end
end
end
end

View File

@ -1,10 +1,10 @@
require "hbc/artifact/uninstall_base"
require "hbc/artifact/abstract_uninstall"
module Hbc
module Artifact
class Uninstall < UninstallBase
def uninstall_phase
dispatch_uninstall_directives
class Uninstall < AbstractUninstall
def uninstall_phase(**options)
dispatch_uninstall_directives(**options)
end
end
end

View File

@ -1,10 +1,10 @@
require "hbc/artifact/uninstall_base"
require "hbc/artifact/abstract_uninstall"
module Hbc
module Artifact
class Zap < UninstallBase
def zap_phase
dispatch_uninstall_directives
class Zap < AbstractUninstall
def zap_phase(**options)
dispatch_uninstall_directives(**options)
end
end
end

View File

@ -70,12 +70,16 @@ module Hbc
previous_cask_contents = Git.last_revision_of_file(tap.path, @cask.sourcefile_path, before_commit: commit_range)
return if previous_cask_contents.empty?
previous_cask = CaskLoader.load_from_string(previous_cask_contents)
begin
previous_cask = CaskLoader.load(previous_cask_contents)
return unless previous_cask.version == cask.version
return if previous_cask.sha256 == cask.sha256
add_error "only sha256 changed (see: https://github.com/caskroom/homebrew-cask/blob/master/doc/cask_language_reference/stanzas/sha256.md)"
rescue CaskError => e
add_warning "Skipped version and checksum comparison. Reading previous version failed: #{e}"
end
end
def check_version
@ -143,7 +147,15 @@ module Hbc
def check_appcast_http_code
odebug "Verifying appcast returns 200 HTTP response code"
result = @command.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--output", "/dev/null", "--write-out", "%{http_code}", cask.appcast], print_stderr: false)
curl_executable, *args = curl_args(
"--compressed", "--location", "--fail",
"--write-out", "%{http_code}",
"--output", "/dev/null",
cask.appcast,
user_agent: :fake
)
result = @command.run(curl_executable, args: args, print_stderr: false)
if result.success?
http_code = result.stdout.chomp
add_warning "unexpected HTTP response code retrieving appcast: #{http_code}" unless http_code == "200"
@ -206,12 +218,10 @@ module Hbc
end
def check_generic_artifacts
cask.artifacts[:artifact].each do |source, target_hash|
unless target_hash.is_a?(Hash) && target_hash[:target]
add_error "target required for generic artifact #{source}"
next
cask.artifacts.select { |a| a.is_a?(Hbc::Artifact::Artifact) }.each do |artifact|
unless artifact.target.absolute?
add_error "target must be absolute path for #{artifact.class.english_name} #{artifact.source}"
end
add_error "target must be absolute path for generic artifact #{source}" unless Pathname.new(target_hash[:target]).absolute?
end
end

View File

@ -43,7 +43,7 @@ module Hbc
def audit_languages(languages)
ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.join(", ")}"
MacOS.instance_variable_set(:@languages, languages)
audit_cask_instance(CaskLoader.load_from_file(cask.sourcefile_path))
audit_cask_instance(CaskLoader.load(cask.sourcefile_path))
ensure
CLI::Cleanup.run(cask.token) if audit_download?
end

View File

@ -17,7 +17,7 @@ module Hbc
@token = token
@sourcefile_path = sourcefile_path
@tap = tap
@dsl = DSL.new(@token)
@dsl = DSL.new(self)
return unless block_given?
@dsl.instance_eval(&block)
@dsl.language_eval
@ -41,6 +41,14 @@ module Hbc
.reverse
end
def full_name
if @tap.nil? || @tap == Hbc.default_tap
token
else
"#{@tap}/#{token}"
end
end
def installed?
!versions.empty?
end

View File

@ -3,6 +3,18 @@ module Hbc
class FromContentLoader
attr_reader :content
def self.can_load?(ref)
return false unless ref.respond_to?(:to_str)
content = ref.to_str
token = /(?:"[^"]*"|'[^']*')/
curly = /\(\s*#{token}\s*\)\s*\{.*\}/
do_end = /\s+#{token}\s+do(?:\s*;\s*|\s+).*end/
regex = /\A\s*cask(?:#{curly.source}|#{do_end.source})\s*\Z/m
content.match?(regex)
end
def initialize(content)
@content = content
end
@ -56,7 +68,8 @@ module Hbc
class FromURILoader < FromPathLoader
def self.can_load?(ref)
ref.to_s.match?(::URI.regexp)
uri_regex = ::URI::DEFAULT_PARSER.make_regexp
ref.to_s.match?(Regexp.new('\A' + uri_regex.source + '\Z', uri_regex.options))
end
attr_reader :url
@ -71,7 +84,7 @@ module Hbc
begin
ohai "Downloading #{url}."
curl url, "-o", path
curl_download url, to: path
rescue ErrorDuringExecution
raise CaskUnavailableError.new(token, "Failed to download #{Formatter.url(url)}.")
end
@ -116,6 +129,22 @@ module Hbc
end
end
class FromInstanceLoader
attr_reader :cask
def self.can_load?(ref)
ref.is_a?(Cask)
end
def initialize(cask)
@cask = cask
end
def load
cask
end
end
class NullLoader < FromPathLoader
def self.can_load?(*)
true
@ -131,14 +160,6 @@ module Hbc
end
end
def self.load_from_file(path)
FromPathLoader.new(path).load
end
def self.load_from_string(content)
FromContentLoader.new(content).load
end
def self.path(ref)
self.for(ref).path
end
@ -149,6 +170,8 @@ module Hbc
def self.for(ref)
[
FromInstanceLoader,
FromContentLoader,
FromURILoader,
FromTapLoader,
FromTapPathLoader,

View File

@ -42,41 +42,32 @@ module Hbc
@args = process_arguments(*args)
end
def self.warn_unavailable_with_suggestion(cask_token, e)
exact_match, partial_matches = Search.search(cask_token)
error_message = e.message
if exact_match
error_message.concat(" Did you mean:\n#{exact_match}")
elsif !partial_matches.empty?
error_message.concat(" Did you mean one of:\n")
.concat(Formatter.columns(partial_matches.take(20)))
end
onoe error_message
end
private
def casks(alternative: -> { [] })
return to_enum(:casks, alternative: alternative) unless block_given?
count = 0
return @casks if defined?(@casks)
casks = args.empty? ? alternative.call : args
casks.each do |cask_or_token|
begin
yield cask_or_token.respond_to?(:token) ? cask_or_token : CaskLoader.load(cask_or_token)
count += 1
@casks = casks.map { |cask| CaskLoader.load(cask) }
rescue CaskUnavailableError => e
cask_token = cask_or_token
self.class.warn_unavailable_with_suggestion cask_token, e
rescue CaskError => e
onoe e.message
end
reason = [e.reason, suggestion_message(e.token)].join(" ")
raise e.class.new(e.token, reason)
end
return :empty if casks.length.zero?
(count == casks.length) ? :complete : :incomplete
def suggestion_message(cask_token)
exact_match, partial_matches = Search.search(cask_token)
if exact_match.nil? && partial_matches.count == 1
exact_match = partial_matches.first
end
if exact_match
"Did you mean “#{exact_match}”?"
elsif !partial_matches.empty?
"Did you mean one of these?\n"
.concat(Formatter.columns(partial_matches.take(20)))
else
""
end
end
end
end

View File

@ -7,10 +7,6 @@ module Hbc
end
def run
raise CaskError, "Cat incomplete." if cat_casks == :incomplete
end
def cat_casks
casks.each do |cask|
puts File.open(cask.sourcefile_path, &:read)
end

View File

@ -4,23 +4,20 @@ module Hbc
def initialize(*)
super
raise CaskUnspecifiedError if args.empty?
raise ArgumentError, "Only one Cask can be created at a time." if args.count > 1
raise ArgumentError, "Only one Cask can be edited at a time." if args.count > 1
end
def run
cask_token = args.first
cask_path = begin
CaskLoader.load(cask_token).sourcefile_path
cask = casks.first
cask_path = cask.sourcefile_path
odebug "Opening editor for Cask #{cask.token}"
exec_editor cask_path
rescue CaskUnavailableError => e
reason = e.reason.empty? ? "" : "#{e.reason} "
reason.concat("Run #{Formatter.identifier("brew cask create #{e.token}")} to create a new Cask.")
raise e.class.new(e.token, reason)
end
odebug "Opening editor for Cask #{cask_token}"
exec_editor cask_path
end
def self.help
"edits the given Cask"
end

View File

@ -9,10 +9,6 @@ module Hbc
end
def run
raise CaskError, "Fetch incomplete." if fetch_casks == :incomplete
end
def fetch_casks
casks.each do |cask|
ohai "Downloading external files for Cask #{cask}"
downloaded_path = Download.new(cask, force: force?).perform

View File

@ -23,6 +23,7 @@ module Hbc
installation_info(cask)
repo_info(cask)
name_info(cask)
language_info(cask)
artifact_info(cask)
Installer.print_caveats(cask)
end
@ -51,6 +52,13 @@ module Hbc
puts cask.name.empty? ? Formatter.error("None") : cask.name
end
def self.language_info(cask)
return if cask.languages.empty?
ohai "Languages"
puts cask.languages.join(", ")
end
def self.repo_info(cask)
user, repo, token = QualifiedToken.parse(Hbc.all_tokens.detect { |t| t.split("/").last == cask.token })
@ -69,12 +77,10 @@ module Hbc
def self.artifact_info(cask)
ohai "Artifacts"
DSL::ORDINARY_ARTIFACT_TYPES.each do |type|
next if cask.artifacts[type].empty?
cask.artifacts[type].each do |artifact|
activatable_item = (type == :stage_only) ? "<none>" : artifact.first
puts "#{activatable_item} (#{type})"
end
cask.artifacts.each do |artifact|
next unless artifact.respond_to?(:install_phase)
next unless DSL::ORDINARY_ARTIFACT_CLASSES.include?(artifact.class)
puts artifact.to_s
end
end
end

View File

@ -10,10 +10,6 @@ module Hbc
end
def run
raise CaskError, "Install incomplete." if install_casks == :incomplete
end
def install_casks
casks.each do |cask|
begin
Installer.new(cask, binaries: binaries?,

View File

@ -12,7 +12,7 @@ module Hbc
if args.all? { |t| t =~ %r{^https?://} && t !~ /\.rb$/ }
self.class.appcask_checkpoint_for_url(args)
else
self.class.appcask_checkpoint(args, calculate?)
self.class.appcask_checkpoint(casks, calculate?)
end
end
@ -23,33 +23,27 @@ module Hbc
end
end
def self.appcask_checkpoint(cask_tokens, calculate)
count = 0
cask_tokens.each do |cask_token|
cask = CaskLoader.load(cask_token)
def self.appcask_checkpoint(casks, calculate)
casks.each do |cask|
if cask.appcast.nil?
opoo "Cask '#{cask}' is missing an `appcast` stanza."
else
if calculate
checkpoint = if calculate
result = cask.appcast.calculate_checkpoint
checkpoint = result[:checkpoint]
result[:checkpoint]
else
checkpoint = cask.appcast.checkpoint
cask.appcast.checkpoint
end
if checkpoint.nil?
if calculate && checkpoint.nil?
onoe "Could not retrieve `appcast` checkpoint for cask '#{cask}': #{result[:command_result].stderr}"
elsif casks.count > 1
puts "#{checkpoint} #{cask}"
else
puts((cask_tokens.count > 1) ? "#{checkpoint} #{cask}" : checkpoint)
count += 1
puts checkpoint
end
end
end
count == cask_tokens.count
end
def self.help

View File

@ -59,7 +59,7 @@ module Hbc
end
def modified_cask_files
@modified_cask_files ||= git_filter_cask_files("AM")
@modified_cask_files ||= git_filter_cask_files("AMR")
end
def added_cask_files

View File

@ -7,10 +7,6 @@ module Hbc
end
def run
raise CaskError, "Dump incomplete." if dump_casks == :incomplet
end
def dump_casks
casks.each(&:dumpcask)
end

View File

@ -3,7 +3,7 @@ module Hbc
class InternalStanza < AbstractInternalCommand
# Syntax
#
# brew cask _stanza <stanza_name> [ --table | --yaml | --inspect | --quiet ] [ <cask_token> ... ]
# brew cask _stanza <stanza_name> [ --quiet ] [ --table | --yaml ] [ <cask_token> ... ]
#
# If no tokens are given, then data for all Casks is returned.
#
@ -14,41 +14,16 @@ module Hbc
# Examples
#
# brew cask _stanza appcast --table
# brew cask _stanza app --table alfred google-chrome adium voicemac logisim vagrant
# brew cask _stanza url --table alfred google-chrome adium voicemac logisim vagrant
# brew cask _stanza version --table alfred google-chrome adium voicemac logisim vagrant
# brew cask _stanza artifacts --table --inspect alfred google-chrome adium voicemac logisim vagrant
# brew cask _stanza artifacts --table --yaml alfred google-chrome adium voicemac logisim vagrant
# brew cask _stanza app --table alfred google-chrome adium vagrant
# brew cask _stanza url --table alfred google-chrome adium vagrant
# brew cask _stanza version --table alfred google-chrome adium vagrant
# brew cask _stanza artifacts --table alfred google-chrome adium vagrant
# brew cask _stanza artifacts --table --yaml alfred google-chrome adium vagrant
#
# TODO: this should be retrievable from Hbc::DSL
ARTIFACTS = Set.new [
:app,
:suite,
:artifact,
:prefpane,
:qlplugin,
:dictionary,
:font,
:service,
:colorpicker,
:binary,
:input_method,
:internet_plugin,
:audio_unit_plugin,
:vst_plugin,
:vst3_plugin,
:screen_saver,
:pkg,
:installer,
:stage_only,
:nested_container,
:uninstall,
:preflight,
:postflight,
:uninstall_preflight,
:uninstall_postflight,
]
ARTIFACTS =
DSL::ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key) +
DSL::ARTIFACT_BLOCK_CLASSES.map(&:dsl_key)
option "--table", :table, false
option "--quiet", :quiet, false
@ -68,16 +43,9 @@ module Hbc
@stanza = args.shift.to_sym
@format = :to_yaml if yaml?
@format = :inspect if inspect?
end
def run
return unless print_stanzas == :incomplete
exit 1 if quiet?
raise CaskError, "Print incomplete."
end
def print_stanzas
if ARTIFACTS.include?(stanza)
artifact_name = stanza
@stanza = :artifacts
@ -93,7 +61,7 @@ module Hbc
end
begin
value = cask.send(@stanza)
value = cask.send(stanza)
rescue StandardError
opoo "failure calling '#{stanza}' on Cask '#{cask}'" unless quiet?
puts ""
@ -106,11 +74,25 @@ module Hbc
next
end
value = value.fetch(artifact_name).to_a.flatten if artifact_name
if stanza == :artifacts
value = Hash[
value.map do |k, v|
v = v.map do |a|
next a.to_a if a.respond_to?(:to_a)
next a.to_h if a.respond_to?(:to_h)
a
end
if @format
puts value.send(@format)
elsif artifact_name || value.is_a?(Symbol)
[k, v]
end
]
value = value.fetch(artifact_name) if artifact_name
end
if format
puts value.send(format)
elsif value.is_a?(Symbol)
puts value.inspect
else
puts value.to_s

View File

@ -3,6 +3,7 @@ module Hbc
class List < AbstractCommand
option "-1", :one, false
option "--versions", :versions, false
option "--full-name", :full_name, false
option "-l", (lambda do |*|
one = true # rubocop:disable Lint/UselessAssignment
@ -10,8 +11,7 @@ module Hbc
end)
def run
retval = args.any? ? list : list_installed
raise CaskError, "Listing incomplete." if retval == :incomplete
args.any? ? list : list_installed
end
def list
@ -23,16 +23,16 @@ module Hbc
elsif versions?
puts self.class.format_versioned(cask)
else
cask = CaskLoader.load_from_file(cask.installed_caskfile)
cask = CaskLoader.load(cask.installed_caskfile)
self.class.list_artifacts(cask)
end
end
end
def self.list_artifacts(cask)
Artifact.for_cask(cask).each do |artifact|
summary = artifact.summary
ohai summary[:english_description], summary[:contents] unless summary.empty?
cask.artifacts.group_by(&:class).each do |klass, artifacts|
next unless klass.respond_to?(:english_description)
ohai klass.english_description, artifacts.map(&:summarize_installed)
end
end
@ -43,11 +43,11 @@ module Hbc
puts installed_casks.map(&:to_s)
elsif versions?
puts installed_casks.map(&self.class.method(:format_versioned))
elsif full_name?
puts installed_casks.map(&:full_name).sort &tap_and_name_comparison
elsif !installed_casks.empty?
puts Formatter.columns(installed_casks.map(&:to_s))
end
installed_casks.empty? ? :empty : :complete
end
def self.format_versioned(cask)

View File

@ -1,7 +1,7 @@
module Hbc
class CLI
class Reinstall < Install
def install_casks
def run
casks.each do |cask|
Installer.new(cask, binaries: binaries?,
verbose: verbose?,

View File

@ -2,9 +2,13 @@ module Hbc
class CLI
class Search < AbstractCommand
def run
if args.empty?
puts Formatter.columns(CLI.nice_listing(Hbc.all_tokens))
else
results = self.class.search(*args)
self.class.render_results(*results)
end
end
def self.extract_regexp(string)
if string =~ %r{^/(.*)/$}
@ -15,8 +19,18 @@ module Hbc
end
def self.search_remote(query)
matches = GitHub.search_code("user:caskroom", "path:Casks", "filename:#{query}", "extension:rb")
[*matches].map do |match|
matches = begin
GitHub.search_code(
user: "caskroom",
path: "Casks",
filename: query,
extension: "rb",
)
rescue GitHub::Error => error
opoo "Error searching on GitHub: #{error}\n"
[]
end
matches.map do |match|
tap = Tap.fetch(match["repository"]["full_name"])
next if tap.installed?
"#{tap.name}/#{File.basename(match["path"], ".rb")}"

View File

@ -9,10 +9,6 @@ module Hbc
end
def run
raise CaskError, "Uninstall incomplete." if uninstall_casks == :incomplete
end
def uninstall_casks
casks.each do |cask|
odebug "Uninstalling Cask #{cask}"
@ -20,7 +16,7 @@ module Hbc
if cask.installed? && !cask.installed_caskfile.nil?
# use the same cask file that was used for installation, if possible
cask = CaskLoader.load_from_file(cask.installed_caskfile) if cask.installed_caskfile.exist?
cask = CaskLoader.load(cask.installed_caskfile) if cask.installed_caskfile.exist?
end
Installer.new(cask, binaries: binaries?, verbose: verbose?, force: force?).uninstall

View File

@ -9,10 +9,6 @@ module Hbc
end
def run
raise CaskError, "Zap incomplete." if zap_casks == :incomplete
end
def zap_casks
casks.each do |cask|
odebug "Zapping Cask #{cask}"
Installer.new(cask, verbose: verbose?, force: force?).zap

View File

@ -4,6 +4,7 @@ require "hbc/container/bzip2"
require "hbc/container/cab"
require "hbc/container/criteria"
require "hbc/container/dmg"
require "hbc/container/directory"
require "hbc/container/executable"
require "hbc/container/generic_unar"
require "hbc/container/gpg"
@ -14,6 +15,7 @@ require "hbc/container/otf"
require "hbc/container/pkg"
require "hbc/container/seven_zip"
require "hbc/container/sit"
require "hbc/container/svn_repository"
require "hbc/container/tar"
require "hbc/container/ttf"
require "hbc/container/rar"
@ -43,6 +45,7 @@ module Hbc
Xz, # pure xz
Gpg, # GnuPG signed data
Executable,
SvnRepository,
]
# for explicit use only (never autodetected):
# Hbc::Container::Naked

View File

@ -20,7 +20,7 @@ module Hbc
unless children.count == 1 &&
!nested_container.directory? &&
@cask.artifacts[:nested_container].empty? &&
@cask.artifacts.none? { |a| a.is_a?(Artifact::NestedContainer) } &&
extract_nested_container(nested_container)
children.each do |src|

View File

@ -13,9 +13,11 @@ module Hbc
end
def magic_number(regex)
return false if path.directory?
# 262: length of the longest regex (currently: Hbc::Container::Tar)
@magic_number ||= File.open(@path, "rb") { |f| f.read(262) }
@magic_number =~ regex
@magic_number ||= File.open(path, "rb") { |f| f.read(262) }
@magic_number.match?(regex)
end
end
end

View File

@ -0,0 +1,24 @@
require "hbc/container/base"
module Hbc
class Container
class Directory < Base
def self.me?(*)
false
end
def extract
@path.children.each do |child|
next if skip_path?(child)
FileUtils.cp child, @cask.staged_path
end
end
private
def skip_path?(*)
false
end
end
end
end

View File

@ -8,7 +8,7 @@ module Hbc
return true if criteria.magic_number(/^#!\s*\S+/)
begin
MachO.open(criteria.path).header.executable?
criteria.path.file? && MachO.open(criteria.path).header.executable?
rescue MachO::MagicError
false
end

View File

@ -16,7 +16,7 @@ module Hbc
def target_file
return @path.basename if @nested
URI.decode(File.basename(@cask.url.path))
CGI.unescape(File.basename(@cask.url.path))
end
end
end

View File

@ -0,0 +1,15 @@
require "hbc/container/directory"
module Hbc
class Container
class SvnRepository < Directory
def self.me?(criteria)
criteria.path.join(".svn").directory?
end
def skip_path?(path)
path.basename.to_s == ".svn"
end
end
end
end

View File

@ -10,7 +10,7 @@ module Hbc
class AbstractDownloadStrategy
attr_reader :cask, :name, :url, :uri_object, :version
def initialize(cask, command = SystemCommand)
def initialize(cask, command: SystemCommand)
@cask = cask
@command = command
# TODO: this excess of attributes is a function of integrating
@ -33,8 +33,8 @@ module Hbc
class HbVCSDownloadStrategy < AbstractDownloadStrategy
REF_TYPES = [:branch, :revision, :revisions, :tag].freeze
def initialize(cask, command = SystemCommand)
super
def initialize(*args, **options)
super(*args, **options)
@ref_type, @ref = extract_ref
@clone = Hbc.cache.join(cache_filename)
end
@ -64,11 +64,6 @@ module Hbc
end
class CurlDownloadStrategy < AbstractDownloadStrategy
# TODO: should be part of url object
def mirrors
@mirrors ||= []
end
def tarball_path
@tarball_path ||= Hbc.cache.join("#{name}--#{version}#{ext}")
end
@ -95,13 +90,8 @@ module Hbc
end
end
def downloaded_size
temporary_path.size? || 0
end
def _fetch
odebug "Calling curl with args #{cask_curl_args}"
curl(*cask_curl_args)
curl_download url, *cask_curl_args, to: temporary_path, user_agent: uri_object.user_agent
end
def fetch
@ -131,33 +121,12 @@ module Hbc
ignore_interrupts { temporary_path.rename(tarball_path) }
end
tarball_path
rescue CurlDownloadStrategyError
raise if mirrors.empty?
puts "Trying a mirror..."
@url = mirrors.shift
retry
end
private
def cask_curl_args
default_curl_args.tap do |args|
args.concat(user_agent_args)
args.concat(cookies_args)
args.concat(referer_args)
end
end
def default_curl_args
[url, "-C", downloaded_size, "-o", temporary_path]
end
def user_agent_args
if uri_object.user_agent
["-A", uri_object.user_agent]
else
[]
end
cookies_args + referer_args
end
def cookies_args
@ -191,8 +160,7 @@ module Hbc
class CurlPostDownloadStrategy < CurlDownloadStrategy
def cask_curl_args
super
default_curl_args.concat(post_args)
super.concat(post_args)
end
def post_args
@ -225,8 +193,8 @@ module Hbc
# super does not provide checks for already-existing downloads
def fetch
if tarball_path.exist?
puts "Already downloaded: #{tarball_path}"
if cached_location.directory?
puts "Already downloaded: #{cached_location}"
else
@url = @url.sub(/^svn\+/, "") if @url =~ %r{^svn\+http://}
ohai "Checking out #{@url}"
@ -252,9 +220,8 @@ module Hbc
else
fetch_repo @clone, @url
end
compress
end
tarball_path
cached_location
end
# This primary reason for redefining this method is the trust_cert
@ -288,10 +255,6 @@ module Hbc
print_stderr: false)
end
def tarball_path
@tarball_path ||= cached_location.dirname.join(cached_location.basename.to_s + "-#{@cask.version}.tar")
end
def shell_quote(str)
# Oh god escaping shell args.
# See http://notetoself.vrensk.com/2008/08/escaping-single-quotes-in-ruby-harder-than-expected/
@ -304,35 +267,5 @@ module Hbc
yield name, url
end
end
private
# TODO/UPDATE: the tar approach explained below is fragile
# against challenges such as case-sensitive filesystems,
# and must be re-implemented.
#
# Seems nutty: we "download" the contents into a tape archive.
# Why?
# * A single file is tractable to the rest of the Cask toolchain,
# * An alternative would be to create a Directory container type.
# However, some type of file-serialization trick would still be
# needed in order to enable calculating a single checksum over
# a directory. So, in that alternative implementation, the
# special cases would propagate outside this class, including
# the use of tar or equivalent.
# * SubversionDownloadStrategy.cached_location is not versioned
# * tarball_path provides a needed return value for our overridden
# fetch method.
# * We can also take this private opportunity to strip files from
# the download which are protocol-specific.
def compress
Dir.chdir(cached_location) do
@command.run!("/usr/bin/tar",
args: ['-s/^\.//', "--exclude", ".svn", "-cf", Pathname.new(tarball_path), "--", "."],
print_stderr: false)
end
clear_cache
end
end
end

View File

@ -1,6 +1,8 @@
require "set"
require "locale"
require "hbc/artifact"
require "hbc/dsl/appcast"
require "hbc/dsl/base"
require "hbc/dsl/caveats"
@ -8,7 +10,6 @@ require "hbc/dsl/conflicts_with"
require "hbc/dsl/container"
require "hbc/dsl/depends_on"
require "hbc/dsl/gpg"
require "hbc/dsl/installer"
require "hbc/dsl/postflight"
require "hbc/dsl/preflight"
require "hbc/dsl/stanza_proxy"
@ -18,39 +19,35 @@ require "hbc/dsl/version"
module Hbc
class DSL
ORDINARY_ARTIFACT_TYPES = [
:app,
:artifact,
:audio_unit_plugin,
:binary,
:colorpicker,
:dictionary,
:font,
:input_method,
:internet_plugin,
:pkg,
:prefpane,
:qlplugin,
:screen_saver,
:service,
:stage_only,
:suite,
:vst_plugin,
:vst3_plugin,
ORDINARY_ARTIFACT_CLASSES = [
Artifact::Installer,
Artifact::App,
Artifact::Artifact,
Artifact::AudioUnitPlugin,
Artifact::Binary,
Artifact::Colorpicker,
Artifact::Dictionary,
Artifact::Font,
Artifact::InputMethod,
Artifact::InternetPlugin,
Artifact::Pkg,
Artifact::Prefpane,
Artifact::Qlplugin,
Artifact::ScreenSaver,
Artifact::Service,
Artifact::StageOnly,
Artifact::Suite,
Artifact::VstPlugin,
Artifact::Vst3Plugin,
Artifact::Uninstall,
Artifact::Zap,
].freeze
ACTIVATABLE_ARTIFACT_TYPES = ([:installer, *ORDINARY_ARTIFACT_TYPES] - [:stage_only]).freeze
ACTIVATABLE_ARTIFACT_CLASSES = ORDINARY_ARTIFACT_CLASSES - [Artifact::StageOnly]
SPECIAL_ARTIFACT_TYPES = [
:uninstall,
:zap,
].freeze
ARTIFACT_BLOCK_TYPES = [
:preflight,
:postflight,
:uninstall_preflight,
:uninstall_postflight,
ARTIFACT_BLOCK_CLASSES = [
Artifact::PreflightBlock,
Artifact::PostflightBlock,
].freeze
DSL_METHODS = Set.new [
@ -66,21 +63,23 @@ module Hbc
:gpg,
:homepage,
:language,
:languages,
:name,
:sha256,
:staged_path,
:url,
:version,
:appdir,
*ORDINARY_ARTIFACT_TYPES,
*ACTIVATABLE_ARTIFACT_TYPES,
*SPECIAL_ARTIFACT_TYPES,
*ARTIFACT_BLOCK_TYPES,
*ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
*ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key),
*ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
].freeze
attr_reader :token
def initialize(token)
@token = token
attr_reader :cask, :token
def initialize(cask)
@cask = cask
@token = cask.token
end
def name(*args)
@ -93,12 +92,14 @@ module Hbc
return instance_variable_get("@#{stanza}") if should_return
if instance_variable_defined?("@#{stanza}")
raise CaskInvalidError.new(token, "'#{stanza}' stanza may only appear once")
raise CaskInvalidError.new(cask, "'#{stanza}' stanza may only appear once.")
end
instance_variable_set("@#{stanza}", yield)
rescue CaskInvalidError
raise
rescue StandardError => e
raise CaskInvalidError.new(token, "'#{stanza}' stanza failed with: #{e}")
raise CaskInvalidError.new(cask, "'#{stanza}' stanza failed with: #{e}")
end
def homepage(homepage = nil)
@ -106,19 +107,21 @@ module Hbc
end
def language(*args, default: false, &block)
if !args.empty? && block_given?
if args.empty?
language_eval
elsif block_given?
@language_blocks ||= {}
@language_blocks[args] = block
return unless default
unless @language_blocks.default.nil?
raise CaskInvalidError.new(token, "Only one default language may be defined")
raise CaskInvalidError.new(cask, "Only one default language may be defined.")
end
@language_blocks.default = block
else
language_eval
raise CaskInvalidError.new(cask, "No block given to language stanza.")
end
end
@ -127,6 +130,10 @@ module Hbc
return @language = nil if @language_blocks.nil? || @language_blocks.empty?
if @language_blocks.default.nil?
raise CaskInvalidError.new(cask, "No default language specified.")
end
MacOS.languages.map(&Locale.method(:parse)).each do |locale|
key = @language_blocks.keys.detect do |strings|
strings.any? { |string| locale.include?(string) }
@ -140,6 +147,12 @@ module Hbc
@language = @language_blocks.default.call
end
def languages
return [] if @language_blocks.nil?
@language_blocks.keys.flatten
end
def url(*args, &block)
set_unique_stanza(:url, args.empty? && !block_given?) do
begin
@ -162,8 +175,8 @@ module Hbc
begin
DSL::Container.new(*args).tap do |container|
# TODO: remove this backward-compatibility section after removing nested_container
if container && container.nested
artifacts[:nested_container] << container.nested
if container&.nested
artifacts.add(Artifact::NestedContainer.new(cask, container.nested))
end
end
end
@ -173,7 +186,7 @@ module Hbc
def version(arg = nil)
set_unique_stanza(:version, arg.nil?) do
if !arg.is_a?(String) && arg != :latest
raise CaskInvalidError.new(token, "invalid 'version' value: '#{arg.inspect}'")
raise CaskInvalidError.new(cask, "invalid 'version' value: '#{arg.inspect}'")
end
DSL::Version.new(arg)
end
@ -182,7 +195,7 @@ module Hbc
def sha256(arg = nil)
set_unique_stanza(:sha256, arg.nil?) do
if !arg.is_a?(String) && arg != :no_check
raise CaskInvalidError.new(token, "invalid 'sha256' value: '#{arg.inspect}'")
raise CaskInvalidError.new(cask, "invalid 'sha256' value: '#{arg.inspect}'")
end
arg
end
@ -195,7 +208,7 @@ module Hbc
begin
@depends_on.load(*args)
rescue RuntimeError => e
raise CaskInvalidError.new(token, e)
raise CaskInvalidError.new(cask, e)
end
@depends_on
end
@ -206,7 +219,7 @@ module Hbc
end
def artifacts
@artifacts ||= Hash.new { |hash, key| hash[key] = Set.new }
@artifacts ||= SortedSet.new
end
def caskroom_path
@ -237,39 +250,27 @@ module Hbc
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
end
ORDINARY_ARTIFACT_TYPES.each do |type|
define_method(type) do |*args|
if type == :stage_only
if args != [true]
raise CaskInvalidError.new(token, "'stage_only' takes a single argument: true")
ORDINARY_ARTIFACT_CLASSES.each do |klass|
define_method(klass.dsl_key) do |*args|
begin
if [*artifacts.map(&:class), klass].include?(Artifact::StageOnly) && (artifacts.map(&:class) & ACTIVATABLE_ARTIFACT_CLASSES).any?
raise CaskInvalidError.new(cask, "'stage_only' must be the only activatable artifact.")
end
unless (artifacts.keys & ACTIVATABLE_ARTIFACT_TYPES).empty?
raise CaskInvalidError.new(token, "'stage_only' must be the only activatable artifact")
end
end
artifacts[type].add(args)
end
end
def installer(*args)
return artifacts[:installer] if args.empty?
artifacts[:installer] << DSL::Installer.new(*args)
raise "'stage_only' must be the only activatable artifact" if artifacts.key?(:stage_only)
artifacts.add(klass.from_args(cask, *args))
rescue CaskInvalidError
raise
rescue StandardError => e
raise CaskInvalidError.new(token, e)
raise CaskInvalidError.new(cask, "invalid '#{klass.dsl_key}' stanza: #{e}")
end
SPECIAL_ARTIFACT_TYPES.each do |type|
define_method(type) do |*args|
artifacts[type].merge(args)
end
end
ARTIFACT_BLOCK_TYPES.each do |type|
define_method(type) do |&block|
artifacts[type] << block
ARTIFACT_BLOCK_CLASSES.each do |klass|
[klass.dsl_key, klass.uninstall_dsl_key].each do |dsl_key|
define_method(dsl_key) do |&block|
artifacts.add(klass.new(cask, dsl_key => block))
end
end
end

View File

@ -3,16 +3,20 @@ require "hbc/system_command"
module Hbc
class DSL
class Appcast
attr_reader :parameters, :checkpoint
attr_reader :uri, :checkpoint, :parameters
def initialize(uri, parameters = {})
def initialize(uri, **parameters)
@uri = URI(uri)
@parameters = parameters
@uri = UnderscoreSupportingURI.parse(uri)
@checkpoint = @parameters[:checkpoint]
@checkpoint = parameters[:checkpoint]
end
def calculate_checkpoint
result = SystemCommand.run("/usr/bin/curl", args: ["--compressed", "--location", "--user-agent", URL::FAKE_USER_AGENT, "--fail", @uri], print_stderr: false)
curl_executable, *args = curl_args(
"--compressed", "--location", "--fail", uri,
user_agent: :fake
)
result = SystemCommand.run(curl_executable, args: args, print_stderr: false)
checkpoint = if result.success?
processed_appcast_text = result.stdout.gsub(%r{<pubDate>[^<]*</pubDate>}m, "")
@ -26,11 +30,11 @@ module Hbc
end
def to_yaml
[@uri, @parameters].to_yaml
[uri, parameters].to_yaml
end
def to_s
@uri.to_s
uri.to_s
end
end
end

View File

@ -10,14 +10,14 @@ module Hbc
def_delegators :@cask, :token, :version, :caskroom_path, :staged_path, :appdir, :language
def system_command(executable, options = {})
@command.run!(executable, options)
def system_command(executable, **options)
@command.run!(executable, **options)
end
def method_missing(method, *)
if method
underscored_class = self.class.name.gsub(/([[:lower:]])([[:upper:]][[:lower:]])/, '\1_\2').downcase
section = underscored_class.downcase.split("::").last
section = underscored_class.split("::").last
Utils.method_missing_message(method, @cask.to_s, section)
nil
else

View File

@ -48,7 +48,7 @@ module Hbc
brew cask install java
EOS
elsif java_version.include?("8") || java_version.include?("+")
elsif java_version.include?("9") || java_version.include?("+")
puts <<-EOS.undent
#{@cask} requires Java #{java_version}. You can install the latest version with

View File

@ -10,25 +10,20 @@ module Hbc
:java,
]
attr_accessor(*VALID_KEYS)
attr_accessor :pairs
attr_reader *VALID_KEYS
def initialize(pairs = {})
@pairs = pairs
VALID_KEYS.each do |key|
instance_variable_set("@#{key}", Set.new)
end
pairs.each do |key, value|
raise "invalid conflicts_with key: '#{key.inspect}'" unless VALID_KEYS.include?(key)
writer_method = "#{key}=".to_sym
send(writer_method, value)
instance_variable_set("@#{key}", instance_variable_get("@#{key}").merge([*value]))
end
end
def to_yaml
@pairs.to_yaml
end
def to_s
@pairs.inspect
end
end
end
end

View File

@ -14,11 +14,11 @@ module Hbc
def initialize(signature, parameters = {})
@parameters = parameters
@signature = UnderscoreSupportingURI.parse(signature)
@signature = URI(signature)
parameters.each do |hkey, hvalue|
raise "invalid 'gpg' parameter: '#{hkey.inspect}'" unless VALID_PARAMETERS.include?(hkey)
writer_method = "#{hkey}=".to_sym
hvalue = UnderscoreSupportingURI.parse(hvalue) if hkey == :key_url
hvalue = URI(hvalue) if hkey == :key_url
valid_id?(hvalue) if hkey == :key_id
send(writer_method, hvalue)
end
@ -35,7 +35,7 @@ module Hbc
end
def to_yaml
# bug, :key_url value is not represented as an instance of Hbc::UnderscoreSupportingURI
# bug, :key_url value is not represented as an instance of URI
[@signature, @parameters].to_yaml
end

View File

@ -1,32 +0,0 @@
module Hbc
class DSL
class Installer
VALID_KEYS = Set.new [
:manual,
:script,
]
attr_accessor(*VALID_KEYS)
def initialize(*parameters)
raise CaskInvalidError.new(token, "'installer' stanza requires an argument") if parameters.empty?
parameters = {}.merge(*parameters)
if parameters.key?(:script) && !parameters[:script].respond_to?(:key?)
if parameters.key?(:executable)
raise CaskInvalidError.new(token, "'installer' stanza gave arguments for both :script and :executable")
end
parameters[:executable] = parameters[:script]
parameters.delete(:script)
parameters = { script: parameters }
end
unless parameters.keys.length == 1
raise "invalid 'installer' stanza: only one of #{VALID_KEYS.inspect} is permitted"
end
key = parameters.keys.first
raise "invalid 'installer' stanza key: '#{key.inspect}'" unless VALID_KEYS.include?(key)
writer_method = "#{key}=".to_sym
send(writer_method, parameters[key])
end
end
end
end

View File

@ -49,7 +49,7 @@ module Hbc
end
end
DIVIDERS.keys.each do |divider|
DIVIDERS.each_key do |divider|
define_divider_methods(divider)
end

View File

@ -17,6 +17,19 @@ module Hbc
end
end
class CaskConflictError < AbstractCaskErrorWithToken
attr_reader :conflicting_cask
def initialize(token, conflicting_cask)
super(token)
@conflicting_cask = conflicting_cask
end
def to_s
"Cask '#{token}' conflicts with '#{conflicting_cask}'."
end
end
class CaskUnavailableError < AbstractCaskErrorWithToken
def to_s
"Cask '#{token}' is unavailable" << (reason.empty? ? "." : ": #{reason}")

View File

@ -86,6 +86,8 @@ module Hbc
raise CaskAlreadyInstalledError, @cask
end
check_conflicts
print_caveats
fetch
uninstall_existing_cask if @reinstall
@ -98,6 +100,21 @@ module Hbc
puts summary
end
def check_conflicts
return unless @cask.conflicts_with
@cask.conflicts_with.cask.each do |conflicting_cask|
begin
conflicting_cask = CaskLoader.load(conflicting_cask)
if conflicting_cask.installed?
raise CaskConflictError.new(@cask, conflicting_cask)
end
rescue CaskUnavailableError
next # Ignore conflicting Casks that do not exist.
end
end
end
def reinstall
odebug "Hbc::Installer#reinstall"
@reinstall = true
@ -109,7 +126,7 @@ module Hbc
# use the same cask file that was used for installation, if possible
installed_caskfile = @cask.installed_caskfile
installed_cask = installed_caskfile.exist? ? CaskLoader.load_from_file(installed_caskfile) : @cask
installed_cask = installed_caskfile.exist? ? CaskLoader.load(installed_caskfile) : @cask
# Always force uninstallation, ignore method parameter
Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true).uninstall
@ -142,7 +159,7 @@ module Hbc
odebug "Extracting primary container"
FileUtils.mkdir_p @cask.staged_path
container = if @cask.container && @cask.container.type
container = if @cask.container&.type
Container.from_type(@cask.container.type)
else
Container.for_path(@downloaded_path, @command)
@ -160,7 +177,7 @@ module Hbc
already_installed_artifacts = []
odebug "Installing artifacts"
artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?)
artifacts = @cask.artifacts
odebug "#{artifacts.length} artifact/s defined", artifacts
artifacts.each do |artifact|
@ -171,7 +188,7 @@ module Hbc
next unless binaries?
end
artifact.install_phase
artifact.install_phase(command: @command, verbose: verbose?, force: force?)
already_installed_artifacts.unshift(artifact)
end
rescue StandardError => e
@ -179,7 +196,7 @@ module Hbc
already_installed_artifacts.each do |artifact|
next unless artifact.respond_to?(:uninstall_phase)
odebug "Reverting installation of artifact of class #{artifact.class}"
artifact.uninstall_phase
artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?)
end
ensure
purge_versioned_files
@ -344,7 +361,7 @@ module Hbc
savedir = @cask.metadata_subdir("Casks", timestamp: :now, create: true)
FileUtils.copy @cask.sourcefile_path, savedir
old_savedir.rmtree unless old_savedir.nil?
old_savedir&.rmtree
end
def uninstall
@ -357,25 +374,27 @@ module Hbc
def uninstall_artifacts
odebug "Un-installing artifacts"
artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?)
artifacts = @cask.artifacts
odebug "#{artifacts.length} artifact/s defined", artifacts
artifacts.each do |artifact|
next unless artifact.respond_to?(:uninstall_phase)
odebug "Un-installing artifact of class #{artifact.class}"
artifact.uninstall_phase
artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?)
end
end
def zap
ohai %Q(Implied "brew cask uninstall #{@cask}")
uninstall_artifacts
if Artifact::Zap.me?(@cask)
ohai "Dispatching zap stanza"
Artifact::Zap.new(@cask, command: @command).zap_phase
else
if (zap_stanzas = @cask.artifacts.select { |a| a.is_a?(Artifact::Zap) }).empty?
opoo "No zap stanza present for Cask '#{@cask}'"
else
ohai "Dispatching zap stanza"
zap_stanzas.each do |stanza|
stanza.zap_phase(command: @command, verbose: verbose?, force: force?)
end
end
ohai "Removing all staged versions of Cask '#{@cask}'"
purge_caskroom_path

View File

@ -4,7 +4,7 @@ module Hbc
index = 0 if index == :first
index = 1 if index == :second
index = -1 if index == :last
Hbc.appdir.join(@cask.artifacts[:app].to_a.at(index).first, "Contents", "Info.plist")
@cask.artifacts.select { |a| a.is_a?(Artifact::App) }.at(index).target.join("Contents", "Info.plist")
end
def plist_exec(cmd)

View File

@ -61,7 +61,7 @@ module Hbc
end
def assert_success
return if processed_status && processed_status.success?
return if processed_status&.success?
raise CaskCommandFailedError.new(command, processed_output[:stdout], processed_output[:stderr], processed_status)
end
@ -112,11 +112,7 @@ module Hbc
processed_output[:stderr],
processed_status.exitstatus)
end
end
end
module Hbc
class SystemCommand
class Result
attr_accessor :command, :stdout, :stderr, :exit_status

View File

@ -1,28 +0,0 @@
require "uri"
module Hbc
module UnderscoreSupportingURI
def self.parse(maybe_uri)
return nil if maybe_uri.nil?
URI.parse(maybe_uri)
rescue URI::InvalidURIError => e
scheme, host, path = simple_parse(maybe_uri)
raise e unless path && host.include?("_")
URI.parse(without_host_underscores(scheme, host, path)).tap do |uri|
uri.instance_variable_set("@host", host)
end
end
def self.simple_parse(maybe_uri)
scheme, host_and_path = maybe_uri.split("://")
host, path = host_and_path.split("/", 2)
[scheme, host, path]
rescue StandardError
nil
end
def self.without_host_underscores(scheme, host, path)
["#{scheme}:/", host.tr("_", "-"), path].join("/")
end
end
end

View File

@ -1,8 +1,6 @@
module Hbc
class URL
FAKE_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10) https://caskroom.github.io".freeze
attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data
attr_reader :using, :revision, :trust_cert, :uri, :cookies, :referer, :data, :user_agent
extend Forwardable
def_delegators :uri, :path, :scheme, :to_s
@ -16,8 +14,8 @@ module Hbc
end
def initialize(uri, options = {})
@uri = Hbc::UnderscoreSupportingURI.parse(uri)
@user_agent = options[:user_agent]
@uri = URI(uri)
@user_agent = options.fetch(:user_agent, :default)
@cookies = options[:cookies]
@referer = options[:referer]
@using = options[:using]
@ -25,10 +23,5 @@ module Hbc
@trust_cert = options[:trust_cert]
@data = options[:data]
end
def user_agent
return FAKE_USER_AGENT if @user_agent == :fake
@user_agent
end
end
end

View File

@ -33,7 +33,7 @@ module Hbc
meta_dir = cached || cask.metadata_subdir("gpg", :now, true)
sig_path = meta_dir.join("signature.asc")
curl(cask.gpg.signature, "-o", sig_path.to_s) unless cached || force
curl_download cask.gpg.signature, to: sig_path unless cached || force
sig_path
end

View File

@ -163,7 +163,7 @@ class Caveats
def plist_caveats
s = []
if f.plist || (keg && keg.plist_installed?)
if f.plist || (keg&.plist_installed?)
plist_domain = f.plist_path.basename(".plist")
# we readlink because this path probably doesn't exist since caveats

View File

@ -6,6 +6,10 @@ module Homebrew
module Cleanup
@disk_cleanup_size = 0
class << self
attr_reader :disk_cleanup_size
end
module_function
def cleanup
@ -21,10 +25,6 @@ module Homebrew
@disk_cleanup_size += path_size
end
def disk_cleanup_size
@disk_cleanup_size
end
def unremovable_kegs
@unremovable_kegs ||= []
end

View File

@ -1,4 +1,4 @@
#: * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] <formulae>:
#: * `deps` [`--1`] [`-n`] [`--union`] [`--full-name`] [`--installed`] [`--include-build`] [`--include-optional`] [`--skip-recommended`] [`--include-requirements`] <formulae>:
#: Show dependencies for <formulae>. When given multiple formula arguments,
#: show the intersection of dependencies for <formulae>.
#:
@ -19,15 +19,22 @@
#: <formulae>. To include the `:build` type dependencies, pass `--include-build`.
#: Similarly, pass `--include-optional` to include `:optional` dependencies.
#: To skip `:recommended` type dependencies, pass `--skip-recommended`.
#: To include requirements in addition to dependencies, pass `--include-requirements`.
#:
#: * `deps` `--tree` [<filters>] (<formulae>|`--installed`):
#: * `deps` `--tree` [`--1`] [<filters>] [`--annotate`] (<formulae>|`--installed`):
#: Show dependencies as a tree. When given multiple formula arguments, output
#: individual trees for every formula.
#:
#: If `--1` is passed, only one level of children is displayed.
#:
#: If `--installed` is passed, output a tree for every installed formula.
#:
#: The <filters> placeholder is any combination of options `--include-build`,
#: `--include-optional`, and `--skip-recommended` as documented above.
#: `--include-optional`, `--skip-recommended`, and `--include-requirements` as
#: documented above.
#:
#: If `--annotate` is passed, the build, optional, and recommended dependencies
#: are marked as such in the output.
#:
#: * `deps` [<filters>] (`--installed`|`--all`):
#: Show dependencies for installed or all available formulae. Every line of
@ -37,6 +44,10 @@
#: The <filters> placeholder is any combination of options `--include-build`,
#: `--include-optional`, and `--skip-recommended` as documented above.
# The undocumented `--for-each` option will switch into the mode used by `deps --all`,
# but only list dependencies for specified formula, one specified formula per line.
# This is used for debugging the `--installed`/`--all` display mode.
# encoding: UTF-8
require "formula"
@ -52,20 +63,26 @@ module Homebrew
all?: ARGV.include?("--all"),
topo_order?: ARGV.include?("-n"),
union?: ARGV.include?("--union"),
for_each?: ARGV.include?("--for-each"),
)
if mode.installed? && mode.tree?
puts_deps_tree Formula.installed
if mode.tree?
if mode.installed?
puts_deps_tree Formula.installed, !ARGV.one?
else
raise FormulaUnspecifiedError if ARGV.named.empty?
puts_deps_tree ARGV.formulae, !ARGV.one?
end
elsif mode.all?
puts_deps Formula
elsif mode.tree?
raise FormulaUnspecifiedError if ARGV.named.empty?
puts_deps_tree ARGV.formulae
elsif ARGV.named.empty?
raise FormulaUnspecifiedError unless mode.installed?
puts_deps Formula.installed
elsif mode.for_each?
puts_deps ARGV.formulae
else
all_deps = deps_for_formulae(ARGV.formulae, !ARGV.one?, &(mode.union? ? :| : :&))
all_deps = condense_requirements(all_deps)
all_deps = all_deps.select(&:installed?) if mode.installed?
all_deps = all_deps.map(&method(:dep_display_name)).uniq
all_deps.sort! unless mode.topo_order?
@ -73,24 +90,59 @@ module Homebrew
end
end
def dep_display_name(d)
ARGV.include?("--full-name") ? d.to_formula.full_name : d.name
def condense_requirements(deps)
if ARGV.include?("--include-requirements")
deps
else
deps.map do |dep|
if dep.is_a? Dependency
dep
elsif dep.default_formula?
dep.to_dependency
end
end.compact
end
end
def dep_display_name(dep)
str = if dep.is_a? Requirement
if ARGV.include?("--include-requirements")
if dep.default_formula?
":#{dep.display_s} (#{dep_display_name(dep.to_dependency)})"
else
":#{dep.display_s}"
end
elsif dep.default_formula?
dep_display_name(dep.to_dependency)
else
# This shouldn't happen, but we'll put something here to help debugging
"::#{dep.name}"
end
else
ARGV.include?("--full-name") ? dep.to_formula.full_name : dep.name
end
if ARGV.include?("--annotate")
str = "#{str} [build]" if dep.build?
str = "#{str} [optional" if dep.optional?
str = "#{str} [recommended]" if dep.recommended?
end
str
end
def deps_for_formula(f, recursive = false)
includes = []
ignores = []
if ARGV.include? "--include-build"
if ARGV.include?("--include-build")
includes << "build?"
else
ignores << "build?"
end
if ARGV.include? "--include-optional"
if ARGV.include?("--include-optional")
includes << "optional?"
else
ignores << "optional?"
end
ignores << "recommended?" if ARGV.include? "--skip-recommended"
ignores << "recommended?" if ARGV.include?("--skip-recommended")
if recursive
deps = f.recursive_dependencies do |dependent, dep|
@ -120,7 +172,7 @@ module Homebrew
end
end
deps + reqs.select(&:default_formula?).map(&:to_dependency)
deps + reqs.to_a
end
def deps_for_formulae(formulae, recursive = false, &block)
@ -129,41 +181,55 @@ module Homebrew
def puts_deps(formulae)
formulae.each do |f|
deps = deps_for_formula(f).sort_by(&:name).map(&method(:dep_display_name))
deps = deps_for_formula(f)
deps = condense_requirements(deps)
deps = deps.sort_by(&:name).map(&method(:dep_display_name))
puts "#{f.full_name}: #{deps.join(" ")}"
end
end
def puts_deps_tree(formulae)
def puts_deps_tree(formulae, recursive = false)
formulae.each do |f|
puts "#{f.full_name} (required dependencies)"
recursive_deps_tree(f, "")
puts f.full_name
@dep_stack = []
recursive_deps_tree(f, "", recursive)
puts
end
end
def recursive_deps_tree(f, prefix)
reqs = f.requirements.select(&:default_formula?)
deps = f.deps.default
max = reqs.length - 1
reqs.each_with_index do |req, i|
chr = if i == max && deps.empty?
def recursive_deps_tree(f, prefix, recursive)
reqs = f.requirements
deps = f.deps
dependables = reqs + deps
dependables = dependables.reject(&:optional?) unless ARGV.include?("--include-optional")
dependables = dependables.reject(&:build?) unless ARGV.include?("--include-build")
dependables = dependables.reject(&:recommended?) if ARGV.include?("--skip-recommended")
max = dependables.length - 1
@dep_stack.push f.name
dependables.each_with_index do |dep, i|
next if !ARGV.include?("--include-requirements") && dep.is_a?(Requirement) && !dep.default_formula?
tree_lines = if i == max
"└──"
else
"├──"
end
puts prefix + "#{chr} :#{dep_display_name(req.to_dependency)}"
end
max = deps.length - 1
deps.each_with_index do |dep, i|
chr = if i == max
"└──"
display_s = "#{tree_lines} #{dep_display_name(dep)}"
is_circular = @dep_stack.include?(dep.name)
display_s = "#{display_s} (CIRCULAR DEPENDENCY)" if is_circular
puts "#{prefix}#{display_s}"
next if !recursive || is_circular
prefix_addition = if i == max
" "
else
"├──"
""
end
prefix_ext = (i == max) ? " " : ""
puts prefix + "#{chr} #{dep_display_name(dep)}"
recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_ext)
if dep.is_a?(Requirement) && dep.default_formula?
recursive_deps_tree(Formulary.factory(dep.to_dependency.name), prefix + prefix_addition, true)
end
if dep.is_a? Dependency
recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_addition, true)
end
end
@dep_stack.pop
end
end

View File

@ -49,8 +49,10 @@ module Homebrew
if fetch_bottle?(f)
begin
fetch_formula(f.bottle)
rescue Exception => e
raise if ARGV.homebrew_developer? || e.is_a?(Interrupt)
rescue Interrupt
raise
rescue => e
raise if ARGV.homebrew_developer?
fetched_bottle = false
onoe e.message
opoo "Bottle fetch failed: fetching the source."

View File

@ -219,6 +219,7 @@ module Homebrew
end
end
return if formulae.empty?
perform_preinstall_checks
formulae.each do |f|
@ -338,6 +339,7 @@ module Homebrew
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to install f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
return
rescue CannotInstallFormulaError => e
ofail e.message
end

View File

@ -19,15 +19,13 @@ class String
end
end
def cask
$LOAD_PATH.unshift("#{HOMEBREW_LIBRARY_PATH}/cask/lib")
require "hbc"
end
module Homebrew
module_function
def irb
$LOAD_PATH.unshift("#{HOMEBREW_LIBRARY_PATH}/cask/lib")
require "hbc"
if ARGV.include? "--examples"
puts "'v8'.f # => instance of the v8 formula"
puts ":hub.f.installed?"

View File

@ -39,15 +39,7 @@ module Homebrew
filtered_list
elsif ARGV.named.empty?
if ARGV.include? "--full-name"
full_names = Formula.installed.map(&:full_name).sort do |a, b|
if a.include?("/") && !b.include?("/")
1
elsif !a.include?("/") && b.include?("/")
-1
else
a <=> b
end
end
full_names = Formula.installed.map(&:full_name).sort &tap_and_name_comparison
return if full_names.empty?
puts Formatter.columns(full_names)
else

View File

@ -1,6 +1,7 @@
#: * `pin` <formulae>:
#: Pin the specified <formulae>, preventing them from being upgraded when
#: issuing the `brew upgrade` command. See also `unpin`.
#: issuing the `brew upgrade <formulae>` command (but can still be upgraded
#: as dependencies for other formulae). See also `unpin`.
require "formula"

View File

@ -29,8 +29,6 @@ module Homebrew
args << "--devel"
end
Sandbox.print_sandbox_message if Sandbox.formula?(formula)
Utils.safe_fork do
if Sandbox.formula?(formula)
sandbox = Sandbox.new

View File

@ -55,7 +55,7 @@ module Homebrew
else
n, d = ObserverPathnameExtension.counts
print "Pruned #{n} symbolic links "
print "and #{d} directories " if d > 0
print "and #{d} directories " if d.positive?
puts "from #{HOMEBREW_PREFIX}"
end
end

View File

@ -47,8 +47,8 @@ module Homebrew
fi.install
fi.finish
rescue FormulaInstallationAlreadyAttemptedError
# next
rescue Exception
return
rescue Exception # rubocop:disable Lint/RescueException
ignore_interrupts { restore_backup(keg, keg_was_linked) }
raise
else

View File

@ -34,38 +34,40 @@ module Homebrew
elsif ARGV.include? "--opensuse"
exec_browser "https://software.opensuse.org/search?q=#{ARGV.next}"
elsif ARGV.include? "--fedora"
exec_browser "https://admin.fedoraproject.org/pkgdb/packages/%2A#{ARGV.next}%2A/"
exec_browser "https://apps.fedoraproject.org/packages/s/#{ARGV.next}"
elsif ARGV.include? "--ubuntu"
exec_browser "http://packages.ubuntu.com/search?keywords=#{ARGV.next}&searchon=names&suite=all&section=all"
exec_browser "https://packages.ubuntu.com/search?keywords=#{ARGV.next}&searchon=names&suite=all&section=all"
elsif ARGV.include? "--desc"
query = ARGV.next
regex = query_regexp(query)
Descriptions.search(regex, :desc).print
elsif ARGV.first =~ HOMEBREW_TAP_FORMULA_REGEX
query = ARGV.first
user, repo, name = query.split("/", 3)
begin
result = Formulary.factory(query).name
results = Array(result)
rescue FormulaUnavailableError
result = search_tap(user, repo, name)
_, _, name = query.split("/", 3)
results = search_taps(name)
end
results = Array(result)
puts Formatter.columns(results) unless results.empty?
else
query = ARGV.first
regex = query_regexp(query)
local_results = search_formulae(regex)
puts Formatter.columns(local_results) unless local_results.empty?
tap_results = search_taps(query)
puts Formatter.columns(tap_results) unless tap_results.empty?
if $stdout.tty?
count = local_results.length + tap_results.length
ohai "Searching blacklisted, migrated and deleted formulae..."
if reason = Homebrew::MissingFormula.reason(query, silent: true)
if count > 0
if count.positive?
puts
puts "If you meant #{query.inspect} specifically:"
end
@ -100,10 +102,18 @@ module Homebrew
odie "#{query} is not a valid regex"
end
def search_taps(query)
def search_taps(query, silent: false)
return [] if ENV["HOMEBREW_NO_GITHUB_API"]
# Use stderr to avoid breaking parsed output
unless silent
$stderr.puts Formatter.headline("Searching taps on GitHub...", color: :blue)
end
valid_dirnames = ["Formula", "HomebrewFormula", "Casks", "."].freeze
matches = GitHub.search_code("user:Homebrew", "user:caskroom", "filename:#{query}", "extension:rb")
[*matches].map do |match|
matches = GitHub.search_code(user: ["Homebrew", "caskroom"], filename: query, extension: "rb")
matches.map do |match|
dirname, filename = File.split(match["path"])
next unless valid_dirnames.include?(dirname)
tap = Tap.fetch(match["repository"]["full_name"])
@ -113,6 +123,9 @@ module Homebrew
end
def search_formulae(regex)
# Use stderr to avoid breaking parsed output
$stderr.puts Formatter.headline("Searching local taps...", color: :blue)
aliases = Formula.alias_full_names
results = (Formula.full_names + aliases).grep(regex).sort

View File

@ -117,12 +117,13 @@ module Homebrew
case output_type
when :print
args << "--debug" if ARGV.debug?
args << "--display-cop-names" if ARGV.include? "--display-cop-names"
args << "--format" << "simple" if files
system(cache_env, "rubocop", *args)
system(cache_env, "rubocop", "_#{HOMEBREW_RUBOCOP_VERSION}_", *args)
!$CHILD_STATUS.success?
when :json
json, _, status = Open3.capture3(cache_env, "rubocop", "--format", "json", *args)
json, _, status = Open3.capture3(cache_env, "rubocop", "_#{HOMEBREW_RUBOCOP_VERSION}_", "--format", "json", *args)
# exit status of 1 just means violations were found; other numbers mean
# execution errors.
# exitstatus can also be nil if RuboCop process crashes, e.g. due to

View File

@ -64,10 +64,10 @@ module Homebrew
if tap.installed?
info += tap.pinned? ? "pinned" : "unpinned"
info += ", private" if tap.private?
if (formula_count = tap.formula_files.size) > 0
if (formula_count = tap.formula_files.size).positive?
info += ", #{Formatter.pluralize(formula_count, "formula")}"
end
if (command_count = tap.command_files.size) > 0
if (command_count = tap.command_files.size).positive?
info += ", #{Formatter.pluralize(command_count, "command")}"
end
info += ", no formulae/commands" if (formula_count + command_count).zero?

View File

@ -54,8 +54,7 @@ module Homebrew
quiet: ARGV.quieter?
rescue TapRemoteMismatchError => e
odie e
rescue TapAlreadyTappedError, TapAlreadyUnshallowError
# Do nothing.
rescue TapAlreadyTappedError, TapAlreadyUnshallowError # rubocop:disable Lint/HandleExceptions
end
end
end

View File

@ -77,7 +77,7 @@ module Homebrew
def unlinkapps_unlink?(target_app, opts = {})
# Skip non-symlinks and symlinks that don't point into the Homebrew prefix.
app = target_app.readlink.to_s if target_app.symlink?
return false unless app && app.start_with?(*UNLINKAPPS_PREFIXES)
return false unless app&.start_with?(*UNLINKAPPS_PREFIXES)
if opts.fetch(:prune, false)
!File.exist?(app) # Remove only broken symlinks in prune mode.

View File

@ -1,6 +1,6 @@
#: * `unpin` <formulae>:
#: Unpin <formulae>, allowing them to be upgraded by `brew upgrade`. See also
#: `pin`.
#: Unpin <formulae>, allowing them to be upgraded by `brew upgrade <formulae>`.
#: See also `pin`.
require "formula"

View File

@ -124,7 +124,7 @@ module Homebrew
return if ENV["HOMEBREW_UPDATE_TEST"]
core_tap = CoreTap.instance
return if core_tap.installed?
CoreTap.ensure_installed! quiet: false
CoreTap.ensure_installed!
revision = core_tap.git_head
ENV["HOMEBREW_UPDATE_BEFORE_HOMEBREW_HOMEBREW_CORE"] = revision
ENV["HOMEBREW_UPDATE_AFTER_HOMEBREW_HOMEBREW_CORE"] = revision
@ -203,6 +203,7 @@ module Homebrew
end
new_homebrew_repository = Pathname.new "/usr/local/Homebrew"
new_homebrew_repository.rmdir_if_possible
if new_homebrew_repository.exist?
ofail <<-EOS.undent
#{new_homebrew_repository} already exists.
@ -371,7 +372,7 @@ class Reporter
new_version = formula.pkg_version
old_version = FormulaVersions.new(formula).formula_at_revision(@initial_revision, &:pkg_version)
next if new_version == old_version
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
onoe "#{e.message}\n#{e.backtrace.join "\n"}" if ARGV.homebrew_developer?
end
@report[:M] << tap.formula_file_to_name(src)
@ -459,7 +460,7 @@ class Reporter
unless Formulary.factory(new_full_name).keg_only?
system HOMEBREW_BREW_FILE, "link", new_full_name, "--overwrite"
end
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
onoe "#{e.message}\n#{e.backtrace.join "\n"}" if ARGV.homebrew_developer?
end
next
@ -520,7 +521,7 @@ class Reporter
begin
f = Formulary.factory(new_full_name)
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
onoe "#{e.message}\n#{e.backtrace.join "\n"}" if ARGV.homebrew_developer?
next
end

View File

@ -385,6 +385,12 @@ EOS
if ! git --version >/dev/null 2>&1
then
# we need a new enough curl to install git
if [[ -n "$HOMEBREW_SYSTEM_CURL_TOO_OLD" &&
! -x "$HOMEBREW_PREFIX/opt/curl/bin/curl" ]]
then
brew install curl
fi
# we cannot install brewed git if homebrew/core is unavailable.
[[ -d "$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-core" ]] && brew install git
unset GIT_EXECUTABLE
@ -564,6 +570,7 @@ EOS
-d "$HOMEBREW_LIBRARY/LinkedKegs" ||
(-n "$HOMEBREW_DEVELOPER" && -z "$HOMEBREW_UPDATE_PREINSTALL") ]]
then
unset HOMEBREW_RUBY_PATH
brew update-report "$@"
return $?
elif [[ -z "$HOMEBREW_UPDATE_PREINSTALL" ]]

View File

@ -150,6 +150,7 @@ module Homebrew
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to upgrade f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
return
rescue CannotInstallFormulaError => e
ofail e
rescue BuildError => e

View File

@ -74,12 +74,13 @@ module Homebrew
end
end
dep_formulae = deps.map do |dep|
dep_formulae = deps.flat_map do |dep|
begin
dep.to_formula
rescue
[]
end
end
end.compact
reqs_by_formula = ([f] + dep_formulae).flat_map do |formula|
formula.requirements.map { |req| [formula, req] }
@ -118,6 +119,7 @@ module Homebrew
rescue FormulaUnavailableError
# Silently ignore this case as we don't care about things used in
# taps that aren't currently tapped.
next
end
end
end

View File

@ -13,16 +13,16 @@ if [[ -n "$HOMEBREW_MACOS" ]]
then
if [[ "$HOMEBREW_PROCESSOR" = "Intel" ]]
then
ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.0.0-p648.leopard_64.bottle.tar.gz"
ruby_SHA="5c1240abe4be91c9774a0089c2a38a8ccfff87c009e8e5786730c659d5e633f7"
ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz"
ruby_SHA="34ce9e4c9c1be28db564d744165aa29291426f8a3d2ef806ba4f0b9175aedb2b"
else
ruby_URL=""
ruby_SHA=""
fi
elif [[ -n "$HOMEBREW_LINUX" ]]
then
ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.0.0-p648.x86_64_linux.bottle.tar.gz"
ruby_SHA="dbb5118a22a6a75cc77e62544a3d8786d383fab1bdaf8c154951268807357bf0"
ruby_URL="https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.x86_64_linux.bottle.tar.gz"
ruby_SHA="543c18bd33a300e6c16671437b1e0f17b03bb64e6a485fc15ff7de1eb1a0bc2a"
fi
fetch() {
@ -45,20 +45,25 @@ fetch() {
curl_args[${#curl_args[*]}]="--progress-bar"
fi
if [[ "$HOMEBREW_MACOS_VERSION_NUMERIC" -lt "100600" ]]
then
curl_args[${#curl_args[*]}]="--insecure"
fi
temporary_path="$CACHED_LOCATION.incomplete"
mkdir -p "$HOMEBREW_CACHE"
[[ -n "$HOMEBREW_QUIET" ]] || echo "==> Downloading $VENDOR_URL"
[[ -n "$HOMEBREW_QUIET" ]] || echo "==> Downloading $VENDOR_URL" >&2
if [[ -f "$CACHED_LOCATION" ]]
then
[[ -n "$HOMEBREW_QUIET" ]] || echo "Already downloaded: $CACHED_LOCATION"
[[ -n "$HOMEBREW_QUIET" ]] || echo "Already downloaded: $CACHED_LOCATION" >&2
else
if [[ -f "$temporary_path" ]]
then
"$HOMEBREW_CURL" "${curl_args[@]}" -C - "$VENDOR_URL" -o "$temporary_path"
if [[ $? -eq 33 ]]
then
[[ -n "$HOMEBREW_QUIET" ]] || echo "Trying a full download"
[[ -n "$HOMEBREW_QUIET" ]] || echo "Trying a full download" >&2
rm -f "$temporary_path"
"$HOMEBREW_CURL" "${curl_args[@]}" "$VENDOR_URL" -o "$temporary_path"
fi
@ -135,7 +140,7 @@ install() {
fi
safe_cd "$VENDOR_DIR"
[[ -n "$HOMEBREW_QUIET" ]] || echo "==> Unpacking $(basename "$VENDOR_URL")"
[[ -n "$HOMEBREW_QUIET" ]] || echo "==> Pouring $(basename "$VENDOR_URL")" >&2
tar "$tar_args" "$CACHED_LOCATION"
safe_cd "$VENDOR_DIR/portable-$VENDOR_NAME"

View File

@ -3,4 +3,8 @@ module SharedEnvExtension
odeprecated "ENV.j1", "ENV.deparallelize"
deparallelize
end
def java_cache
# odeprecated "ENV.java_cache"
end
end

View File

@ -16,7 +16,7 @@ end
# This formula serves as the base class for several very similar
# formulae for Amazon Web Services related tools.
class AmazonWebServicesFormula < Formula
# Use this method to peform a standard install for Java-based tools,
# Use this method to perform a standard install for Java-based tools,
# keeping the .jars out of HOMEBREW_PREFIX/lib
def install
odeprecated "AmazonWebServicesFormula#install", "Formula#install"

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
# RuboCop version used for `brew style` and `brew cask style`
HOMEBREW_RUBOCOP_VERSION = "0.49.1".freeze
HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.13.1".freeze # has to be updated when RuboCop version changes
HOMEBREW_RUBOCOP_VERSION = "0.50.0"
HOMEBREW_RUBOCOP_CASK_VERSION = "~> 0.14.2" # has to be updated when RuboCop version changes

View File

@ -9,7 +9,7 @@ module Debrew
module Raise
def raise(*)
super
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
e.extend(Ignorable)
super(e) unless Debrew.debug(e) == :ignore
end
@ -57,7 +57,7 @@ module Debrew
input.chomp!
i = input.to_i
if i > 0
if i.positive?
choice = menu.entries[i - 1]
else
possible = menu.entries.find_all { |e| e.name.start_with?(input) }
@ -92,7 +92,7 @@ module Debrew
yield
rescue SystemExit
original_raise
rescue Exception => e
rescue Exception => e # rubocop:disable Lint/RescueException
debug(e)
ensure
@active = false
@ -119,7 +119,7 @@ module Debrew
if e.is_a?(Ignorable)
menu.choice(:irb) do
puts "When you exit this IRB session, execution will continue."
set_trace_func proc { |event, _, _, id, binding, klass|
set_trace_func proc { |event, _, _, id, binding, klass| # rubocop:disable Metrics/ParameterLists
if klass == Raise && id == :raise && event == "return"
set_trace_func(nil)
synchronize { IRB.start_within(binding) }

View File

@ -4,8 +4,7 @@ module IRB
@setup_done = false
extend Module.new {
def parse_opts
end
def parse_opts; end
def start_within(binding)
unless @setup_done
@ -16,7 +15,7 @@ module IRB
workspace = WorkSpace.new(binding)
irb = Irb.new(workspace)
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
@CONF[:IRB_RC]&.call(irb.context)
@CONF[:MAIN_CONTEXT] = irb.context
trap("SIGINT") do

Some files were not shown because too many files have changed in this diff Show More