Merge branch 'master' into check-for-master-no-refactor

This commit is contained in:
Ben Muschol 2017-09-27 16:36:10 -04:00 committed by GitHub
commit 2d6bd04007
180 changed files with 2251 additions and 1137 deletions

View File

@ -15,26 +15,27 @@ matrix:
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
git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot;
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";
else
umask 022;
git clone --depth=1 https://github.com/Homebrew/homebrew-test-bot Library/Taps/homebrew/homebrew-test-bot;
git fetch --unshallow;
export PATH="$PWD/bin:$PATH";
HOMEBREW_CORE_TAP_DIR="$(brew --repo "homebrew/core")";
mkdir -p "$HOMEBREW_CORE_TAP_DIR";
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 @@
AllCops:
TargetRubyVersion: 2.0
TargetRubyVersion: 2.3
Exclude:
- '**/Casks/**/*'
- '**/vendor/**/*'
@ -119,7 +119,7 @@ Style/Encoding:
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
@ -189,8 +189,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

@ -42,12 +42,12 @@ Style/HashSyntax:
EnforcedStyle: ruby19_no_mixed_keys
# we won't change backward compatible method names
Style/MethodName:
Naming/MethodName:
Exclude:
- 'compat/**/*'
# we won't change backward compatible predicate names
Style/PredicateName:
Naming/PredicateName:
Exclude:
- 'compat/**/*'
NameWhitelist: is_32_bit?, is_64_bit?

View File

@ -81,7 +81,7 @@ Security/MarshalLoad:
- 'utils/fork.rb'
# Offense count: 1
Style/AccessorMethodName:
Naming/AccessorMethodName:
Exclude:
- 'extend/ENV/super.rb'
@ -136,10 +136,3 @@ Style/MutableConstant:
- 'formulary.rb'
- 'tab.rb'
- 'tap.rb'
# Offense count: 8
Style/OpMethod:
Exclude:
- 'dependencies.rb'
- 'install_renamed.rb'
- 'options.rb'

View File

@ -105,7 +105,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 +115,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"

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

View File

@ -33,7 +33,7 @@ module Hbc
# We want to extract nested containers before we
# handle any other artifacts.
#
TYPES = [
CLASSES = [
PreflightBlock,
Uninstall,
NestedContainer,
@ -60,12 +60,9 @@ module Hbc
Zap,
].freeze
def self.for_cask(cask, options = {})
def self.for_cask(cask)
odebug "Determining which artifacts are present in Cask #{cask}"
TYPES
.select { |klass| klass.me?(cask) }
.map { |klass| klass.new(cask, options) }
CLASSES.flat_map { |klass| klass.for_cask(cask) }
end
end
end

View File

@ -1,34 +1,28 @@
module Hbc
module Artifact
class Base
class AbstractArtifact
extend Predicable
def self.artifact_name
@artifact_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase
def self.english_name
@english_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2')
end
def self.artifact_english_name
@artifact_english_name ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1 \2')
def self.english_article
@english_article ||= (english_name =~ /^[aeiou]/i) ? "an" : "a"
end
def self.artifact_english_article
@artifact_english_article ||= (artifact_english_name =~ /^[aeiou]/i) ? "an" : "a"
def self.dsl_key
@dsl_key ||= name.sub(/^.*:/, "").gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym
end
def self.artifact_dsl_key
@artifact_dsl_key ||= artifact_name.to_sym
def self.dirmethod
@dirmethod ||= "#{dsl_key}dir".to_sym
end
def self.artifact_dirmethod
@artifact_dirmethod ||= "#{artifact_name}dir".to_sym
def self.for_cask(cask)
cask.artifacts[dsl_key].to_a
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)
@ -63,17 +57,14 @@ module Hbc
[executable, arguments]
end
def summary
{}
attr_reader :cask
def initialize(cask)
@cask = cask
end
attr_predicate :force?, :verbose?
def initialize(cask, command: SystemCommand, force: false, verbose: false)
@cask = cask
@command = command
@force = force
@verbose = verbose
def to_s
"#{summarize} (#{self.class.english_name})"
end
end
end

View File

@ -1,39 +1,51 @@
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}")
def self.for_cask(cask)
[dsl_key, uninstall_dsl_key].flat_map do |key|
[*cask.artifacts[key]].map { |block| new(cask, key => block) }
end
end
def self.me?(cask)
cask.artifacts[artifact_dsl_key].any? ||
cask.artifacts[uninstall_artifact_dsl_key].any?
attr_reader :directives
def initialize(cask, **directives)
super(cask)
@directives = directives
end
def install_phase
abstract_phase(self.class.artifact_dsl_key)
def install_phase(**)
abstract_phase(self.class.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.map { |key, val| [*val].map { |v| "#{key.inspect} => #{v.inspect}" }.join(", ") }.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, **_)
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)
@ -260,7 +277,7 @@ module Hbc
EOS
end
def uninstall_rmdir(*directories)
def uninstall_rmdir(*directories, command: nil, **_)
return if directories.empty?
ohai "Removing directories if empty:"
@ -268,10 +285,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

@ -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,31 @@
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 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,57 @@ 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, **options)
options.extend(HashValidator).assert_valid_keys(:allow_untrusted, :choices)
new(cask, path, **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, :options
def initialize(cask, path, **options)
super(cask)
@path = cask.staged_path.join(path)
@options = 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
args << "-allowUntrusted" if options.fetch(:allow_untrusted, false)
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 = 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

@ -214,12 +214,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[: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

@ -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

@ -56,7 +56,7 @@ module Hbc
class FromURILoader < FromPathLoader
def self.can_load?(ref)
ref.to_s.match?(::URI.regexp)
ref.to_s.match?(::URI::DEFAULT_PARSER.make_regexp)
end
attr_reader :url
@ -116,6 +116,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
@ -149,6 +165,7 @@ module Hbc
def self.for(ref)
[
FromInstanceLoader,
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

@ -69,12 +69,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
DSL::ORDINARY_ARTIFACT_CLASSES.flat_map { |klass| klass.for_cask(cask) }
.select { |artifact| artifact.respond_to?(:install_phase) }
.each do |artifact|
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

@ -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
@ -30,9 +30,9 @@ module Hbc
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?
Artifact.for_cask(cask).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,17 @@ module Hbc
end
def self.search_remote(query)
matches = GitHub.search_code(user: "caskroom", path: "Casks",
filename: query, extension: "rb")
matches = begin
GitHub.search_code(
user: "caskroom",
path: "Casks",
filename: query,
extension: "rb",
)
rescue GitHub::Error => error
opoo "Error searching on GitHub: #{error}\n"
[]
end
matches.map do |match|
tap = Tap.fetch(match["repository"]["full_name"])
next if tap.installed?

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}"

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

@ -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

@ -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_TYPES = (ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key) - [:stage_only]).freeze
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 [
@ -72,15 +69,15 @@ module Hbc
:url,
:version,
:appdir,
*ORDINARY_ARTIFACT_TYPES,
*ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
*ACTIVATABLE_ARTIFACT_TYPES,
*SPECIAL_ARTIFACT_TYPES,
*ARTIFACT_BLOCK_TYPES,
*ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
].freeze
attr_reader :token
def initialize(token)
@token = token
attr_reader :token, :cask
def initialize(cask)
@cask = cask
@token = cask.token
end
def name(*args)
@ -93,12 +90,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)
@ -113,7 +112,7 @@ module Hbc
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
@ -162,8 +161,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[:nested_container] << Artifact::NestedContainer.new(cask, container.nested)
end
end
end
@ -173,7 +172,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 +181,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 +194,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
@ -237,39 +236,29 @@ module Hbc
set_unique_stanza(:auto_updates, auto_updates.nil?) { auto_updates }
end
ORDINARY_ARTIFACT_TYPES.each do |type|
ORDINARY_ARTIFACT_CLASSES.each do |klass|
type = klass.dsl_key
define_method(type) do |*args|
if type == :stage_only
if args != [true]
raise CaskInvalidError.new(token, "'stage_only' takes a single argument: true")
begin
if [*artifacts.keys, type].include?(:stage_only) && (artifacts.keys & ACTIVATABLE_ARTIFACT_TYPES).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[type].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[dsl_key] << block
end
end
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

@ -159,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)
@ -177,7 +177,7 @@ module Hbc
already_installed_artifacts = []
odebug "Installing artifacts"
artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?)
artifacts = Artifact.for_cask(@cask)
odebug "#{artifacts.length} artifact/s defined", artifacts
artifacts.each do |artifact|
@ -188,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
@ -196,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
@ -361,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
@ -374,25 +374,27 @@ module Hbc
def uninstall_artifacts
odebug "Un-installing artifacts"
artifacts = Artifact.for_cask(@cask, command: @command, verbose: verbose?, force: force?)
artifacts = Artifact.for_cask(@cask)
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 = Artifact::Zap.for_cask(@cask)).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[:app].to_a.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

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

@ -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

@ -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

@ -67,7 +67,7 @@ module Homebrew
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

View File

@ -119,10 +119,10 @@ module Homebrew
when :print
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

@ -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

@ -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.

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

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

@ -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) }

View File

@ -16,7 +16,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

View File

@ -51,7 +51,7 @@ class Dependency
end
def modify_build_environment
env_proc.call unless env_proc.nil?
env_proc&.call
end
def inspect

View File

@ -54,6 +54,7 @@ module Homebrew
def audit
Homebrew.inject_dump_stats!(FormulaAuditor, /^audit_/) if ARGV.switch? "D"
Homebrew.auditing = true
formula_count = 0
problem_count = 0
@ -201,19 +202,24 @@ class FormulaAuditor
@specs = %w[stable devel head].map { |s| formula.send(s) }.compact
end
def self.check_http_content(url, name, user_agents: [:default])
def self.check_http_content(url, user_agents: [:default], check_content: false, strict: false, require_http: false)
return unless url.start_with? "http"
details = nil
user_agent = nil
hash_needed = url.start_with?("http:") && name != "curl"
hash_needed = url.start_with?("http:") && !require_http
user_agents.each do |ua|
details = http_content_headers_and_checksum(url, hash_needed: hash_needed, user_agent: ua)
user_agent = ua
break if details[:status].to_s.start_with?("2")
end
return "The URL #{url} is not reachable" unless details[:status]
unless details[:status]
# Hack around https://github.com/Homebrew/brew/issues/3199
return if MacOS.version == :el_capitan
return "The URL #{url} is not reachable"
end
unless details[:status].start_with? "2"
return "The URL #{url} is not reachable (HTTP status code #{details[:status]})"
end
@ -236,8 +242,32 @@ class FormulaAuditor
details[:content_length] == secure_details[:content_length]
file_match = details[:file_hash] == secure_details[:file_hash]
return if !etag_match && !content_length_match && !file_match
"The URL #{url} could use HTTPS rather than HTTP"
if etag_match || content_length_match || file_match
return "The URL #{url} should use HTTPS rather than HTTP"
end
return unless check_content
no_protocol_file_contents = %r{https?:\\?/\\?/}
details[:file] = details[:file].gsub(no_protocol_file_contents, "/")
secure_details[:file] = secure_details[:file].gsub(no_protocol_file_contents, "/")
# Check for the same content after removing all protocols
if details[:file] == secure_details[:file]
return "The URL #{url} should use HTTPS rather than HTTP"
end
return unless strict
# Same size, different content after normalization
# (typical causes: Generated ID, Timestamp, Unix time)
if details[:file].length == secure_details[:file].length
return "The URL #{url} may be able to use HTTPS rather than HTTP. Please verify it in a browser."
end
lenratio = (100 * secure_details[:file].length / details[:file].length).to_i
return unless (90..110).cover?(lenratio)
"The URL #{url} may be able to use HTTPS rather than HTTP. Please verify it in a browser."
end
def self.http_content_headers_and_checksum(url, hash_needed: false, user_agent: :default)
@ -260,6 +290,7 @@ class FormulaAuditor
etag: headers[%r{ETag: ([wW]\/)?"(([^"]|\\")*)"}, 2],
content_length: headers[/Content-Length: (\d+)/, 1],
file_hash: output_hash,
file: output,
}
end
@ -327,7 +358,7 @@ class FormulaAuditor
end
valid_alias_names = [alias_name_major, alias_name_major_minor]
if formula.tap && !formula.tap.core_tap?
unless formula.tap&.core_tap?
versioned_aliases.map! { |a| "#{formula.tap}/#{a}" }
valid_alias_names.map! { |a| "#{formula.tap}/#{a}" }
end
@ -356,21 +387,6 @@ class FormulaAuditor
end
end
def audit_class
if @strict
unless formula.test_defined?
problem "A `test do` test block should be added"
end
end
classes = %w[GithubGistFormula ScriptFileFormula AmazonWebServicesFormula]
klass = classes.find do |c|
Object.const_defined?(c) && formula.class < Object.const_get(c)
end
problem "#{klass} is deprecated, use Formula instead" if klass
end
# core aliases + tap alias names + tap alias full name
@@aliases ||= Formula.aliases + Formula.tap_aliases
@ -403,6 +419,7 @@ class FormulaAuditor
@@local_official_taps_name_map ||= Tap.select(&:official?).flat_map(&:formula_names)
.each_with_object({}) do |tap_formula_full_name, name_map|
next if tap_formula_full_name.start_with?("homebrew/science/")
tap_formula_name = tap_formula_full_name.split("/").last
name_map[tap_formula_name] ||= []
name_map[tap_formula_name] << tap_formula_full_name
@ -413,6 +430,7 @@ class FormulaAuditor
if @online
Homebrew.search_taps(name, silent: true).each do |tap_formula_full_name|
next if tap_formula_full_name.start_with?("homebrew/science/")
tap_formula_name = tap_formula_full_name.split("/").last
next if tap_formula_name != name
same_name_tap_formulae << tap_formula_full_name
@ -563,10 +581,11 @@ class FormulaAuditor
return unless @online
return unless DevelopmentTools.curl_handles_most_https_homepages?
return unless DevelopmentTools.curl_handles_most_https_certificates?
if http_content_problem = FormulaAuditor.check_http_content(homepage,
formula.name,
user_agents: [:browser, :default])
user_agents: [:browser, :default],
check_content: true,
strict: @strict)
problem http_content_problem
end
end
@ -616,13 +635,14 @@ class FormulaAuditor
end
%w[Stable Devel HEAD].each do |name|
next unless spec = formula.send(name.downcase)
spec_name = name.downcase.to_sym
next unless spec = formula.send(spec_name)
ra = ResourceAuditor.new(spec, online: @online, strict: @strict).audit
ra = ResourceAuditor.new(spec, spec_name, online: @online, strict: @strict).audit
problems.concat ra.problems.map { |problem| "#{name}: #{problem}" }
spec.resources.each_value do |resource|
ra = ResourceAuditor.new(resource, online: @online, strict: @strict).audit
ra = ResourceAuditor.new(resource, spec_name, online: @online, strict: @strict).audit
problems.concat ra.problems.map { |problem|
"#{name} resource #{resource.name.inspect}: #{problem}"
}
@ -687,7 +707,7 @@ class FormulaAuditor
end
stable = formula.stable
case stable && stable.url
case stable&.url
when /[\d\._-](alpha|beta|rc\d)/
matched = Regexp.last_match(1)
version_prefix = stable.version.to_s.sub(/\d+$/, "")
@ -865,8 +885,8 @@ class FormulaAuditor
problem "Use spaces instead of tabs for indentation" if line =~ /^[ ]*\t/
if line.include?("ENV.x11")
problem "Use \"depends_on :x11\" instead of \"ENV.x11\""
if line.include?("ENV.java_cache")
problem "In-formula ENV.java_cache usage has been deprecated & should be removed."
end
# Avoid hard-coding compilers
@ -882,14 +902,6 @@ class FormulaAuditor
problem "Use ENV instead of invoking '#{Regexp.last_match(1)}' to modify the environment"
end
if formula.name != "wine" && line =~ /ENV\.universal_binary/
problem "macOS has been 64-bit only since 10.6 so ENV.universal_binary is deprecated."
end
if line =~ /build\.universal\?/
problem "macOS has been 64-bit only so build.universal? is deprecated."
end
if line =~ /version == ['"]HEAD['"]/
problem "Use 'build.head?' instead of inspecting 'version'"
end
@ -930,12 +942,6 @@ class FormulaAuditor
problem "Use build instead of ARGV to check options"
end
problem "Use new-style option definitions" if line.include?("def options")
if line.end_with?("def test")
problem "Use new-style test definitions (test do)"
end
if line.include?("MACOS_VERSION")
problem "Use MacOS.version instead of MACOS_VERSION"
end
@ -949,11 +955,6 @@ class FormulaAuditor
problem "\"#{$&}\" is deprecated, use a comparison to MacOS.version instead"
end
if line =~ /skip_clean\s+:all/
problem "`skip_clean :all` is deprecated; brew no longer strips symbols\n" \
"\tPass explicit paths to prevent Homebrew from removing empty folders."
end
if line =~ /depends_on [A-Z][\w:]+\.new$/
problem "`depends_on` can take requirement classes instead of instances"
end
@ -992,30 +993,6 @@ class FormulaAuditor
problem "Use `assert_match` instead of `assert ...include?`"
end
if line.include?('system "npm", "install"') && !line.include?("Language::Node") &&
formula.name !~ /^kibana(\@\d+(\.\d+)?)?$/
problem "Use Language::Node for npm install args"
end
if line.include?("fails_with :llvm")
problem "'fails_with :llvm' is now a no-op so should be removed"
end
if line =~ /system\s+['"](otool|install_name_tool|lipo)/ && formula.name != "cctools"
problem "Use ruby-macho instead of calling #{Regexp.last_match(1)}"
end
if formula.tap.to_s == "homebrew/core"
["OS.mac?", "OS.linux?"].each do |check|
next unless line.include?(check)
problem "Don't use #{check}; Homebrew/core only supports macOS"
end
end
if line =~ /((revision|version_scheme)\s+0)/
problem "'#{Regexp.last_match(1)}' should be removed"
end
return unless @strict
problem "`#{Regexp.last_match(1)}` in formulae is deprecated" if line =~ /(env :(std|userpaths))/
@ -1041,7 +1018,7 @@ class FormulaAuditor
def audit_reverse_migration
# Only enforce for new formula being re-added to core and official taps
return unless @strict
return unless formula.tap && formula.tap.official?
return unless formula.tap&.official?
return unless formula.tap.tap_migrations.key?(formula.name)
problem <<-EOS.undent
@ -1116,10 +1093,10 @@ class FormulaAuditor
end
class ResourceAuditor
attr_reader :problems
attr_reader :version, :checksum, :using, :specs, :url, :mirrors, :name
attr_reader :name, :version, :checksum, :url, :mirrors, :using, :specs, :owner
attr_reader :spec_name, :problems
def initialize(resource, options = {})
def initialize(resource, spec_name, options = {})
@name = resource.name
@version = resource.version
@checksum = resource.checksum
@ -1127,6 +1104,8 @@ class ResourceAuditor
@mirrors = resource.mirrors
@using = resource.using
@specs = resource.specs
@owner = resource.owner
@spec_name = spec_name
@online = options[:online]
@strict = options[:strict]
@problems = []
@ -1203,11 +1182,29 @@ class ResourceAuditor
problem "Redundant :using value in URL"
end
def self.curl_openssl_and_deps
@curl_openssl_and_deps ||= begin
formulae_names = ["curl", "openssl"]
formulae_names += formulae_names.flat_map do |f|
Formula[f].recursive_dependencies.map(&:name)
end
formulae_names.uniq
rescue FormulaUnavailableError
[]
end
end
def audit_urls
urls = [url] + mirrors
if name == "curl" && !urls.find { |u| u.start_with?("http://") }
problem "should always include at least one HTTP url"
curl_openssl_or_deps = ResourceAuditor.curl_openssl_and_deps.include?(owner.name)
if spec_name == :stable && curl_openssl_or_deps
problem "should not use xz tarballs" if url.end_with?(".xz")
unless urls.find { |u| u.start_with?("http://") }
problem "should always include at least one HTTP mirror"
end
end
return unless @online
@ -1219,7 +1216,7 @@ class ResourceAuditor
# A `brew mirror`'ed URL is usually not yet reachable at the time of
# pull request.
next if url =~ %r{^https://dl.bintray.com/homebrew/mirror/}
if http_content_problem = FormulaAuditor.check_http_content(url, name)
if http_content_problem = FormulaAuditor.check_http_content(url, require_http: curl_openssl_or_deps)
problem http_content_problem
end
elsif strategy <= GitDownloadStrategy
@ -1228,6 +1225,7 @@ class ResourceAuditor
end
elsif strategy <= SubversionDownloadStrategy
next unless DevelopmentTools.subversion_handles_most_https_certificates?
next unless Utils.svn_available?
unless Utils.svn_remote_exists url
problem "The URL #{url} is not a valid svn URL"
end

View File

@ -47,7 +47,7 @@ BOTTLE_ERB = <<-EOS.freeze
<% elsif cellar != BottleSpecification::DEFAULT_CELLAR %>
cellar "<%= cellar %>"
<% end %>
<% if rebuild > 0 %>
<% if rebuild.positive? %>
rebuild <%= rebuild %>
<% end %>
<% checksums.each do |checksum_type, checksum_values| %>
@ -186,7 +186,7 @@ module Homebrew
ohai "Determining #{f.full_name} bottle rebuild..."
versions = FormulaVersions.new(f)
rebuilds = versions.bottle_version_map("origin/master")[f.pkg_version]
rebuilds.pop if rebuilds.last.to_i > 0
rebuilds.pop if rebuilds.last.to_i.positive?
rebuild = rebuilds.empty? ? 0 : rebuilds.max.to_i + 1
end
@ -283,7 +283,7 @@ module Homebrew
raise
ensure
ignore_interrupts do
original_tab.write if original_tab
original_tab&.write
unless ARGV.include? "--skip-relocation"
keg.replace_placeholders_with_locations changed_files
end

View File

@ -89,7 +89,8 @@ module Homebrew
def check_for_duplicate_pull_requests(formula)
pull_requests = fetch_pull_requests(formula)
return unless pull_requests && !pull_requests.empty?
return unless pull_requests
return if pull_requests.empty?
duplicates_message = <<-EOS.undent
These open pull requests may be duplicates:
#{pull_requests.map { |pr| "#{pr["title"]} #{pr["html_url"]}" }.join("\n")}
@ -124,7 +125,7 @@ module Homebrew
Formula.each do |f|
if is_devel && f.devel && f.devel.url && f.devel.url.match(base_url)
guesses << f
elsif f.stable && f.stable.url && f.stable.url.match(base_url)
elsif f.stable&.url && f.stable.url.match(base_url)
guesses << f
end
end
@ -296,9 +297,7 @@ module Homebrew
ohai "git fetch --unshallow origin" if shallow
ohai "git checkout --no-track -b #{branch} origin/master"
ohai "git commit --no-edit --verbose --message='#{formula.name} #{new_formula_version}#{devel_message}' -- #{formula.path}"
ohai "hub fork --no-remote"
ohai "hub fork"
ohai "hub fork (to read $HUB_REMOTE)"
ohai "hub fork # read $HUB_REMOTE"
ohai "git push --set-upstream $HUB_REMOTE #{branch}:#{branch}"
ohai "hub pull-request --browse -m '#{formula.name} #{new_formula_version}#{devel_message}'"
ohai "git checkout -"
@ -308,9 +307,9 @@ module Homebrew
safe_system "git", "commit", "--no-edit", "--verbose",
"--message=#{formula.name} #{new_formula_version}#{devel_message}",
"--", formula.path
safe_system "hub", "fork", "--no-remote"
quiet_system "hub", "fork"
remote = Utils.popen_read("hub fork 2>&1")[/fatal: remote (.+) already exists\./, 1]
remote = Utils.popen_read("hub fork 2>&1")[/remote:? (\S+)/, 1]
# repeat for hub 2.2 backwards compatibility:
remote = Utils.popen_read("hub fork 2>&1")[/remote:? (\S+)/, 1] if remote.to_s.empty?
odie "cannot get remote from 'hub'!" if remote.to_s.empty?
safe_system "git", "push", "--set-upstream", remote, "#{branch}:#{branch}"
pr_message = <<-EOS.undent

View File

@ -69,13 +69,13 @@ module Homebrew
tap = nil
ARGV.named.each do |arg|
if arg.to_i > 0
if arg.to_i.positive?
issue = arg
url = "https://github.com/Homebrew/homebrew-core/pull/#{arg}"
tap = CoreTap.instance
elsif (testing_match = arg.match %r{/job/Homebrew.*Testing/(\d+)/})
tap = ARGV.value("tap")
tap = if tap && tap.start_with?("homebrew/")
tap = if tap&.start_with?("homebrew/")
Tap.fetch("homebrew", tap.strip_prefix("homebrew/"))
elsif tap
odie "Tap option did not start with \"homebrew/\": #{tap}"
@ -350,7 +350,7 @@ module Homebrew
files << Regexp.last_match(1) if line =~ %r{^\+\+\+ b/(.*)}
end
files.each do |file|
if tap && tap.formula_file?(file)
if tap&.formula_file?(file)
formula_name = File.basename(file, ".rb")
formulae << formula_name unless formulae.include?(formula_name)
else

View File

@ -10,10 +10,8 @@ module Homebrew
def release_notes
previous_tag = ARGV.named.first
unless previous_tag
previous_tag = Utils.popen_read("git tag --list --sort=-version:refname")
previous_tag ||= Utils.popen_read("git tag --list --sort=-version:refname")
.lines.first.chomp
end
odie "Could not find any previous tags!" unless previous_tag
end_ref = ARGV.named[1] || "origin/master"

View File

@ -114,7 +114,7 @@ class DevelopmentTools
@non_apple_gcc_version = {}
end
def curl_handles_most_https_homepages?
def curl_handles_most_https_certificates?
true
end

View File

@ -522,7 +522,7 @@ module Homebrew
homebrew_owned = @found.all? do |path|
Pathname.new(path).realpath.to_s.start_with? "#{HOMEBREW_CELLAR}/gettext"
end
return if gettext && gettext.linked_keg.directory? && homebrew_owned
return if gettext&.linked_keg&.directory? && homebrew_owned
inject_file_list @found, <<-EOS.undent
gettext files detected at a system prefix.
@ -540,7 +540,7 @@ module Homebrew
rescue
nil
end
if libiconv && libiconv.linked_keg.directory?
if libiconv&.linked_keg&.directory?
unless libiconv.keg_only?
<<-EOS.undent
A libiconv formula is installed and linked.

View File

@ -331,21 +331,11 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
if cached_location.exist?
puts "Already downloaded: #{cached_location}"
else
had_incomplete_download = temporary_path.exist?
begin
_fetch
rescue ErrorDuringExecution
# 33 == range not supported
# try wiping the incomplete download and retrying once
unless $CHILD_STATUS.exitstatus == 33 && had_incomplete_download
raise CurlDownloadStrategyError, @url
end
ohai "Trying a full download"
temporary_path.unlink
had_incomplete_download = false
retry
end
ignore_interrupts { temporary_path.rename(cached_location) }
end
rescue CurlDownloadStrategyError

View File

@ -416,7 +416,7 @@ class BuildError < RuntimeError
puts
if issues && !issues.empty?
unless issues&.empty?
puts "These open issues may also help:"
puts issues.map { |i| "#{i["title"]} #{i["html_url"]}" }.join("\n")
end

View File

@ -144,10 +144,10 @@ module HomebrewArgvExtension
def value(name)
arg_prefix = "--#{name}="
flag_with_value = find { |arg| arg.start_with?(arg_prefix) }
flag_with_value.strip_prefix(arg_prefix) if flag_with_value
flag_with_value&.strip_prefix(arg_prefix)
end
# Returns an array of values that were given as a comma-seperated list.
# Returns an array of values that were given as a comma-separated list.
# @see value
def values(name)
return unless val = value(name)
@ -236,7 +236,7 @@ module HomebrewArgvExtension
def bottle_arch
arch = value "bottle-arch"
arch.to_sym if arch
arch&.to_sym
end
def build_from_source?

View File

@ -28,7 +28,7 @@ module EnvActivation
end
def clear_sensitive_environment!
ENV.keys.each do |key|
ENV.each_key do |key|
next unless /(cookie|key|token)/i =~ key
ENV.delete key
end

View File

@ -260,10 +260,6 @@ module SharedEnvExtension
set_cpu_flags(flags)
end
def java_cache
append "_JAVA_OPTIONS", "-Duser.home=#{HOMEBREW_CACHE}/java_cache"
end
# ld64 is a newer linker provided for Xcode 2.5
# @private
def ld64

View File

@ -233,8 +233,8 @@ module Stdenv
def make_jobs
# '-j' requires a positive integral argument
if self["HOMEBREW_MAKE_JOBS"].to_i > 0
self["HOMEBREW_MAKE_JOBS"].to_i
if (jobs = self["HOMEBREW_MAKE_JOBS"].to_i).positive?
jobs
else
Hardware::CPU.cores
end

View File

@ -9,7 +9,7 @@ class DevelopmentTools
@locate[key] = if (located_tool = original_locate(tool))
located_tool
elsif MacOS.version > :tiger
path = Utils.popen_read("/usr/bin/xcrun", "-no-cache", "-find", tool).chomp
path = Utils.popen_read("/usr/bin/xcrun", "-no-cache", "-find", tool, err: :close).chomp
Pathname.new(path) if File.executable?(path)
end
end
@ -43,11 +43,16 @@ class DevelopmentTools
end
def custom_installation_instructions
if MacOS.version > :tiger
if MacOS.version > :leopard
<<-EOS.undent
Install GNU's GCC
brew install gcc
EOS
elsif MacOS.version > :tiger
<<-EOS.undent
Install GNU's GCC
brew install gcc@4.6
EOS
else
# Tiger doesn't ship with apple-gcc42, and this is required to build
# some software that doesn't build properly with FSF GCC.
@ -55,7 +60,7 @@ class DevelopmentTools
Install Apple's GCC
brew install apple-gcc42
or GNU's GCC
brew install gcc
brew install gcc@4.6
EOS
end
end
@ -77,10 +82,10 @@ class DevelopmentTools
end
end
def curl_handles_most_https_homepages?
# The system Curl is too old for some modern HTTPS homepages on
def curl_handles_most_https_certificates?
# The system Curl is too old for some modern HTTPS certificates on
# older macOS versions.
MacOS.version >= :el_capitan
ENV["HOMEBREW_SYSTEM_CURL_TOO_OLD"].nil?
end
def subversion_handles_most_https_certificates?

View File

@ -195,8 +195,9 @@ module Homebrew
end
def check_ruby_version
ruby_version = "2.0"
return if RUBY_VERSION[/\d\.\d/] == ruby_version
ruby_version = "2.3.3"
return if RUBY_VERSION == ruby_version
return if ARGV.homebrew_developer? && OS::Mac.prerelease?
<<-EOS.undent
Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew

View File

@ -96,9 +96,13 @@ module Superenv
self["SDKROOT"] = MacOS.sdk_path
end
# Filter out symbols known not to be defined on 10.11 since GNU Autotools
# can't reliably figure this out with Xcode 8 on its own yet.
if MacOS.version == "10.11" && MacOS::Xcode.installed? && MacOS::Xcode.version >= "8.0"
# Filter out symbols known not to be defined since GNU Autotools can't
# reliably figure this out with Xcode 8 and above.
if MacOS.version == "10.12" && MacOS::Xcode.installed? && MacOS::Xcode.version >= "9.0"
%w[fmemopen futimens open_memstream utimensat].each do |s|
ENV["ac_cv_func_#{s}"] = "no"
end
elsif MacOS.version == "10.11" && MacOS::Xcode.installed? && MacOS::Xcode.version >= "8.0"
%w[basename_r clock_getres clock_gettime clock_settime dirname_r
getentropy mkostemp mkostemps timingsafe_bcmp].each do |s|
ENV["ac_cv_func_#{s}"] = "no"

View File

@ -50,6 +50,8 @@ module Hardware
:broadwell
when 0x37fc219f # Skylake
:skylake
when 0x0f817246 # Kaby Lake
:kabylake
else
:dunno
end

View File

@ -60,7 +60,7 @@ module StringInreplaceExtension
result
end
# Looks for Makefile style variable defintions and replaces the
# Looks for Makefile style variable definitions and replaces the
# value with "new_value", or removes the definition entirely.
def change_make_var!(flag, new_value)
return if gsub!(/^#{Regexp.escape(flag)}[ \t]*=[ \t]*(.*)$/, "#{flag}=#{new_value}", false)

View File

@ -472,7 +472,7 @@ class Formula
return true if devel && tab.devel_version && tab.devel_version < devel.version
if options[:fetch_head]
return false unless head && head.downloader.is_a?(VCSDownloadStrategy)
return false unless head&.downloader.is_a?(VCSDownloadStrategy)
downloader = head.downloader
downloader.shutup! unless ARGV.verbose?
downloader.commit_outdated?(version.version.commit)
@ -1115,8 +1115,8 @@ class Formula
# @private
def unlock
@lock.unlock unless @lock.nil?
@oldname_lock.unlock unless @oldname_lock.nil?
@lock&.unlock
@oldname_lock&.unlock
end
def migration_needed?
@ -1182,7 +1182,8 @@ class Formula
# Returns false if the formula wasn't installed with an alias.
def installed_alias_target_changed?
target = current_installed_alias_target
target && target.name != name
return false unless target
target.name != name
end
# Is this formula the target of an alias used to install an old formula?
@ -1440,13 +1441,14 @@ class Formula
# True if this formula is provided by Homebrew itself
# @private
def core_formula?
tap && tap.core_tap?
tap&.core_tap?
end
# True if this formula is provided by external Tap
# @private
def tap?
tap && !tap.core_tap?
return false unless tap
!tap.core_tap?
end
# @private
@ -1525,10 +1527,10 @@ class Formula
"oldname" => oldname,
"aliases" => aliases,
"versions" => {
"stable" => (stable.version.to_s if stable),
"stable" => stable&.version.to_s,
"bottle" => bottle ? true : false,
"devel" => (devel.version.to_s if devel),
"head" => (head.version.to_s if head),
"devel" => devel&.version.to_s,
"head" => head&.version.to_s,
},
"revision" => revision,
"version_scheme" => version_scheme,
@ -1570,7 +1572,7 @@ class Formula
"root_url" => bottle_spec.root_url,
}
bottle_info["files"] = {}
bottle_spec.collector.keys.each do |os|
bottle_spec.collector.keys.each do |os| # rubocop:disable Performance/HashEachMethods
checksum = bottle_spec.collector[os]
bottle_info["files"][os] = {
"url" => "#{bottle_spec.root_url}/#{Bottle::Filename.create(self, os, bottle_spec.rebuild)}",
@ -1613,6 +1615,7 @@ class Formula
def run_test
@prefix_returns_versioned_prefix = true
old_home = ENV["HOME"]
old_java_opts = ENV["_JAVA_OPTIONS"]
old_curl_home = ENV["CURL_HOME"]
old_tmpdir = ENV["TMPDIR"]
old_temp = ENV["TEMP"]
@ -1626,6 +1629,7 @@ class Formula
ENV["TERM"] = "dumb"
ENV["PATH"] = PATH.new(old_path).append(HOMEBREW_PREFIX/"bin")
ENV["HOMEBREW_PATH"] = nil
ENV["_JAVA_OPTIONS"] = "#{old_java_opts} -Duser.home=#{HOMEBREW_CACHE}/java_cache"
ENV.clear_sensitive_environment!
@ -1646,6 +1650,7 @@ class Formula
ensure
@testpath = nil
ENV["HOME"] = old_home
ENV["_JAVA_OPTIONS"] = old_java_opts
ENV["CURL_HOME"] = old_curl_home
ENV["TMPDIR"] = old_tmpdir
ENV["TEMP"] = old_temp
@ -1888,11 +1893,13 @@ class Formula
mkdir_p env_home
old_home = ENV["HOME"]
old_java_opts = ENV["_JAVA_OPTIONS"]
old_curl_home = ENV["CURL_HOME"]
old_path = ENV["HOMEBREW_PATH"]
unless ARGV.interactive?
ENV["HOME"] = env_home
ENV["_JAVA_OPTIONS"] = "#{old_java_opts} -Duser.home=#{HOMEBREW_CACHE}/java_cache"
ENV["CURL_HOME"] = old_curl_home || old_home
end
ENV["HOMEBREW_PATH"] = nil
@ -1907,6 +1914,7 @@ class Formula
@buildpath = nil
unless ARGV.interactive?
ENV["HOME"] = old_home
ENV["_JAVA_OPTIONS"] = old_java_opts
ENV["CURL_HOME"] = old_curl_home
end
ENV["HOMEBREW_PATH"] = old_path

View File

@ -85,13 +85,12 @@ class FormulaInstaller
return false if @pour_failed
bottle = formula.bottle
return false unless bottle
return false if !bottle && !formula.local_bottle_path
return true if force_bottle?
return false if build_from_source? || build_bottle? || interactive?
return false if ARGV.cc
return false unless options.empty?
return false if formula.bottle_disabled?
return true if formula.local_bottle_path
unless formula.pour_bottle?
if install_bottle_options[:warn] && formula.pour_bottle_check_unsatisfied_reason
opoo <<-EOS.undent
@ -270,7 +269,7 @@ class FormulaInstaller
oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip
end
if formula.tap && !formula.tap.private?
unless formula.tap&.private?
action = "#{formula.full_name} #{options}".strip
Utils::Analytics.report_event("install", action)
@ -561,7 +560,7 @@ class FormulaInstaller
end
raise
else
ignore_interrupts { tmp_keg.rmtree if tmp_keg && tmp_keg.directory? }
ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? }
end
def caveats
@ -604,6 +603,12 @@ class FormulaInstaller
# let's reset Utils.git_available? if we just installed git
Utils.clear_git_available_cache if formula.name == "git"
# use installed curl when it's needed and available
if formula.name == "curl" &&
!DevelopmentTools.curl_handles_most_https_certificates?
ENV["HOMEBREW_CURL"] = formula.opt_bin/"curl"
end
ensure
unlock
end

View File

@ -45,11 +45,15 @@ module Homebrew
@failed == true
end
attr_writer :raise_deprecation_exceptions
attr_writer :raise_deprecation_exceptions, :auditing
def raise_deprecation_exceptions?
@raise_deprecation_exceptions == true
end
def auditing?
@auditing == true
end
end
end

View File

@ -7,8 +7,7 @@ class Gpg
next unless gpg_short_version
gpg_version = Version.create(gpg_short_version.to_s)
@version = gpg_version
gpg_version == Version.create("2.1") ||
gpg_version == Version.create("2.0")
gpg_version >= Version.create("2.0")
end
end
@ -38,12 +37,30 @@ class Gpg
Key-Length: 2048
Subkey-Type: RSA
Subkey-Length: 2048
Passphrase: ''
Name-Real: Testing
Name-Email: testing@foo.bar
Expire-Date: 1d
%no-protection
%commit
EOS
system GPG_EXECUTABLE, "--batch", "--gen-key", "batch.gpg"
end
def self.cleanup_test_processes!
odie "No GPG present to test against!" unless available?
gpgconf = Pathname.new(GPG_EXECUTABLE).parent/"gpgconf"
system gpgconf, "--kill", "gpg-agent"
system gpgconf, "--homedir", "keyrings/live", "--kill",
"gpg-agent"
end
def self.test(path)
create_test_key(path)
begin
yield
ensure
cleanup_test_processes!
end
end
end

View File

@ -16,12 +16,12 @@ module InstallRenamed
end
end
def +(path)
super(path).extend(InstallRenamed)
def +(other)
super(other).extend(InstallRenamed)
end
def /(path)
super(path).extend(InstallRenamed)
def /(other)
super(other).extend(InstallRenamed)
end
private

View File

@ -338,7 +338,7 @@ class Keg
dir if dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") }
when :fish then path/"share/fish/vendor_completions.d"
end
dir && dir.directory? && !dir.children.empty?
dir&.directory? && !dir.children.empty?
end
def functions_installed?(shell)

View File

@ -156,7 +156,7 @@ class Keg
libtool_files = []
path.find do |pn|
next if pn.symlink? || pn.directory? || pn.extname != ".la"
next if pn.symlink? || pn.directory? || ![".la", ".lai"].include?(pn.extname)
libtool_files << pn
end
libtool_files

View File

@ -23,7 +23,7 @@ module Language
else
homebrew_site_packages(version)
end
block.call python, version if block
block&.call python, version
end
ENV["PYTHONPATH"] = original_pythonpath
end

View File

@ -44,8 +44,6 @@ class Locale
raise ParserError, "'#{value}' does not match #{regex}" unless value =~ regex
instance_variable_set(:"@#{key}", value)
end
self
end
def include?(other)

View File

@ -1,5 +1,5 @@
brew-cask(1) - a friendly binary installer for macOS
========================================================
====================================================
## SYNOPSIS
@ -101,9 +101,10 @@ names, and other aspects of this manual are still subject to change.
Reinstall the given Cask.
* `search` or `-S` [<text> | /<regexp>/]:
Without an argument, display all Casks available for install; otherwise
perform a substring search of known Cask tokens for <text> or, if the
text is delimited by slashes (/<regexp>/), it is interpreted as a
Without an argument, display all locally available Casks for install; no
online search is performed.
Otherwise perform a substring search of known Cask tokens for <text> or,
if the text is delimited by slashes (/<regexp>/), it is interpreted as a
Ruby regular expression.
* `style` [--fix] [ <token> ... ]:

View File

@ -31,7 +31,7 @@ module Homebrew
#{Formatter.url("https://pip.readthedocs.io/en/stable/installing/")}
EOS
when "pil" then <<-EOS.undent
Instead of PIL, consider `pip install pillow` or `brew install Homebrew/science/pillow`.
Instead of PIL, consider `pip2 install pillow`.
EOS
when "macruby" then <<-EOS.undent
MacRuby is not packaged and is on an indefinite development hiatus.
@ -53,7 +53,7 @@ module Homebrew
ruin SSH's security.
EOS
when "gsutil" then <<-EOS.undent
Install gsutil with `pip install gsutil`
Install gsutil with `pip2 install gsutil`
EOS
when "gfortran" then <<-EOS.undent
GNU Fortran is now provided as part of GCC, and can be installed with:

View File

@ -69,29 +69,29 @@ class Options
@options.each(*args, &block)
end
def <<(o)
@options << o
def <<(other)
@options << other
self
end
def +(o)
self.class.new(@options + o)
def +(other)
self.class.new(@options + other)
end
def -(o)
self.class.new(@options - o)
def -(other)
self.class.new(@options - other)
end
def &(o)
self.class.new(@options & o)
def &(other)
self.class.new(@options & other)
end
def |(o)
self.class.new(@options | o)
def |(other)
self.class.new(@options | other)
end
def *(arg)
@options.to_a * arg
def *(other)
@options.to_a * other
end
def empty?

View File

@ -11,7 +11,7 @@ module OS
module Mac
module_function
::MacOS = self # rubocop:disable Style/ConstantName
::MacOS = self # rubocop:disable Naming/ConstantName
raise "Loaded OS::Mac on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"]
@ -34,12 +34,12 @@ module OS
def prerelease?
# TODO: bump version when new OS is released
version >= "10.13"
version >= "10.14"
end
def outdated_release?
# TODO: bump version when new OS is released
version < "10.10"
version < "10.11"
end
def cat
@ -104,7 +104,7 @@ module OS
# Returns the path to an SDK or nil, following the rules set by #sdk.
def sdk_path(v = nil)
s = sdk(v)
s.path unless s.nil?
s&.path
end
# See these issues for some history:
@ -129,8 +129,8 @@ module OS
paths << path if path.exist?
end
# Finally, some users make their MacPorts or Fink directorie
# read-only in order to try out Homebrew, but this doens't work as
# Finally, some users make their MacPorts or Fink directories
# read-only in order to try out Homebrew, but this doesn't work as
# some build scripts error out when trying to read from these now
# unreadable paths.
%w[/sw /opt/local].map { |p| Pathname.new(p) }.each do |path|

View File

@ -5,7 +5,7 @@ require "formula"
class LinkageChecker
attr_reader :keg, :formula
attr_reader :brewed_dylibs, :system_dylibs, :broken_dylibs, :variable_dylibs
attr_reader :undeclared_deps, :reverse_links
attr_reader :undeclared_deps, :unnecessary_deps, :reverse_links
def initialize(keg, formula = nil)
@keg = keg
@ -16,6 +16,7 @@ class LinkageChecker
@variable_dylibs = Set.new
@undeclared_deps = []
@reverse_links = Hash.new { |h, k| h[k] = Set.new }
@unnecessary_deps = []
check_dylibs
end
@ -51,7 +52,7 @@ class LinkageChecker
end
end
@undeclared_deps = check_undeclared_deps if formula
@undeclared_deps, @unnecessary_deps = check_undeclared_deps if formula
end
def check_undeclared_deps
@ -77,6 +78,12 @@ class LinkageChecker
a <=> b
end
end
unnecessary_deps = declared_dep_names.reject do |full_name|
name = full_name.split("/").last
next true if Formula[name].bin.directory?
@brewed_dylibs.keys.map { |x| x.split("/").last }.include?(name)
end
[undeclared_deps, unnecessary_deps]
end
def display_normal_output
@ -84,7 +91,8 @@ class LinkageChecker
display_items "Homebrew libraries", @brewed_dylibs
display_items "Variable-referenced libraries", @variable_dylibs
display_items "Missing libraries", @broken_dylibs
display_items "Possible undeclared dependencies", @undeclared_deps
display_items "Undeclared dependencies with linkage", @undeclared_deps
display_items "Dependencies with no linkage", @unnecessary_deps
end
def display_reverse_output
@ -102,6 +110,7 @@ class LinkageChecker
def display_test_output
display_items "Missing libraries", @broken_dylibs
display_items "Possible unnecessary dependencies", @unnecessary_deps
puts "No broken dylib links" if @broken_dylibs.empty?
end
@ -113,6 +122,10 @@ class LinkageChecker
!@undeclared_deps.empty?
end
def unnecessary_deps?
!@unnecessary_deps.empty?
end
private
# Whether or not dylib is a harmless broken link, meaning that it's

View File

@ -12,6 +12,7 @@ module OS
mountain_lion: "10.8",
lion: "10.7",
snow_leopard: "10.6",
leopard_64: "10.5",
leopard: "10.5",
tiger: "10.4",
}.freeze

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