Merge branch 'master' into add-service-block-to-formula-api

This commit is contained in:
apainintheneck 2023-03-22 20:06:32 -07:00
commit 6ccf21c2c9
121 changed files with 828 additions and 621 deletions

View File

@ -31,7 +31,7 @@ jobs:
uses: Homebrew/actions/setup-homebrew@master
- name: Cache Bundler RubyGems
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -73,7 +73,7 @@ jobs:
uses: Homebrew/actions/setup-homebrew@master
- name: Cache Bundler RubyGems
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -231,7 +231,7 @@ jobs:
uses: Homebrew/actions/setup-homebrew@master
- name: Cache Bundler RubyGems
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -276,7 +276,7 @@ jobs:
HOMEBREW_NO_INSTALL_FROM_API: 1
- name: Cache Bundler RubyGems
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -289,7 +289,7 @@ jobs:
run: mkdir tests
- name: Cache parallel tests log
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: tests
key: ${{ runner.os }}-${{ matrix.test-flags }}-parallel_runtime_rspec-${{ github.sha }}

View File

@ -23,7 +23,7 @@ jobs:
steps:
- name: Re-run this workflow
if: github.event_name == 'schedule' || github.event.action == 'closed'
uses: reitermarkus/rerun-workflow@1a15891377b6667377207cdd85b628b79ebd6f81
uses: reitermarkus/rerun-workflow@7381e98aa2bc4464acef4b60ade8d1d1d90e6e65
with:
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
continuous-label: waiting for feedback

View File

@ -28,6 +28,7 @@ gem "rubocop", require: false
gem "rubocop-ast", require: false
gem "simplecov", require: false
gem "simplecov-cobertura", require: false
gem "stackprof", require: false
gem "warning", require: false
group :sorbet, optional: true do

View File

@ -188,6 +188,7 @@ GEM
sorbet (>= 0.5.9204)
sorbet-runtime (>= 0.5.9204)
thor (>= 0.19.2)
stackprof (0.2.24)
tapioca (0.7.3)
bundler (>= 1.17.3)
pry (>= 0.12.2)
@ -257,6 +258,7 @@ DEPENDENCIES
sorbet-runtime
sorbet-static-and-runtime
spoom
stackprof
tapioca
warning

View File

@ -1,6 +1,7 @@
# typed: false
# typed: true
# frozen_string_literal: true
# `HOMEBREW_STACKPROF` should be set via `brew prof --stackprof`, not manually.
if ENV["HOMEBREW_STACKPROF"]
require "rubygems"
require "stackprof"
@ -35,8 +36,8 @@ begin
empty_argv = ARGV.empty?
help_flag_list = %w[-h --help --usage -?]
help_flag = !ENV["HOMEBREW_HELP"].nil?
help_cmd_index = nil
cmd = nil
help_cmd_index = T.let(nil, T.nilable(Integer))
cmd = T.let(nil, T.nilable(String))
ARGV.each_with_index do |arg, i|
break if help_flag && cmd
@ -64,7 +65,7 @@ begin
path.prepend(HOMEBREW_SHIMS_PATH/"shared")
homebrew_path.prepend(HOMEBREW_SHIMS_PATH/"shared")
ENV["PATH"] = path
ENV["PATH"] = path.to_s
require "commands"
require "settings"
@ -76,7 +77,7 @@ begin
homebrew_path.append(Tap.cmd_directories)
# External commands expect a normal PATH
ENV["PATH"] = homebrew_path
ENV["PATH"] = homebrew_path.to_s
end
# Usage instructions should be displayed if and only if one of:

View File

@ -6,8 +6,6 @@ require "timeout"
require "utils/user"
require "cask/artifact/abstract_artifact"
require "cask/pkg"
require "extend/hash_validator"
using HashValidator
module Cask
module Artifact
@ -38,7 +36,7 @@ module Cask
attr_reader :directives
def initialize(cask, directives)
directives.assert_valid_keys!(*ORDERED_DIRECTIVES)
directives.assert_valid_keys(*ORDERED_DIRECTIVES)
super(cask, **directives)
directives[:signal] = Array(directives[:signal]).flatten.each_slice(2).to_a

View File

@ -3,9 +3,6 @@
require "cask/artifact/moved"
require "extend/hash_validator"
using HashValidator
module Cask
module Artifact
# Generic artifact corresponding to the `artifact` stanza.

View File

@ -3,9 +3,6 @@
require "cask/artifact/abstract_artifact"
require "extend/hash_validator"
using HashValidator
module Cask
module Artifact
# Artifact corresponding to the `installer` stanza.
@ -67,7 +64,7 @@ module Cask
)
end
args.assert_valid_keys!(*VALID_KEYS)
args.assert_valid_keys(*VALID_KEYS)
new(cask, **args)
end

View File

@ -6,9 +6,6 @@ require "plist"
require "utils/user"
require "cask/artifact/abstract_artifact"
require "extend/hash_validator"
using HashValidator
module Cask
module Artifact
# Artifact corresponding to the `pkg` stanza.
@ -18,7 +15,7 @@ module Cask
attr_reader :path, :stanza_options
def self.from_args(cask, path, **stanza_options)
stanza_options.assert_valid_keys!(:allow_untrusted, :choices)
stanza_options.assert_valid_keys(:allow_untrusted, :choices)
new(cask, path, **stanza_options)
end

View File

@ -3,9 +3,6 @@
require "cask/artifact/abstract_artifact"
require "extend/hash_validator"
using HashValidator
module Cask
module Artifact
# Superclass for all artifacts which have a source and a target location.
@ -20,7 +17,7 @@ module Cask
if target_hash
raise CaskInvalidError unless target_hash.respond_to?(:keys)
target_hash.assert_valid_keys!(:target)
target_hash.assert_valid_keys(:target)
end
target_hash ||= {}

View File

@ -20,9 +20,6 @@ module Cask
extend APIHashable
include Metadata
# TODO: can be removed when API JSON is regenerated with HOMEBREW_PREFIX_PLACEHOLDER.
HOMEBREW_OLD_PREFIX_PLACEHOLDER = "$(brew --prefix)"
attr_reader :token, :sourcefile_path, :source, :config, :default_config, :loader
attr_accessor :download, :allow_reassignment

View File

@ -326,13 +326,10 @@ module Cask
end
def from_h_string_gsubs(string, appdir)
# TODO: HOMEBREW_OLD_PREFIX_PLACEHOLDER can be removed when API JSON is
# regenerated with HOMEBREW_PREFIX_PLACEHOLDER.
string.to_s
.gsub(HOMEBREW_HOME_PLACEHOLDER, Dir.home)
.gsub(HOMEBREW_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX)
.gsub(HOMEBREW_CASK_APPDIR_PLACEHOLDER, appdir)
.gsub(Cask::HOMEBREW_OLD_PREFIX_PLACEHOLDER, HOMEBREW_PREFIX)
end
def from_h_array_gsubs(array, appdir)

View File

@ -11,7 +11,6 @@ require "cask/config"
require "cask/cmd/abstract_command"
require "cask/cmd/audit"
require "cask/cmd/fetch"
require "cask/cmd/install"
require "cask/cmd/reinstall"

View File

@ -1,41 +0,0 @@
# typed: false
# frozen_string_literal: true
module Cask
class Cmd
# Cask implementation of the `brew fetch` command.
#
# @api private
class Fetch < AbstractCommand
extend T::Sig
def self.parser
super do
switch "--force",
description: "Force redownloading even if files already exist in local cache."
end
end
sig { void }
def run
require "cask/download"
require "cask/installer"
options = {
quarantine: args.quarantine?,
}.compact
options[:quarantine] = true if options[:quarantine].nil?
casks.each do |cask|
puts Installer.caveats(cask)
ohai "Downloading external files for Cask #{cask}"
download = Download.new(cask, **options)
download.clear_cache if args.force?
downloaded_path = download.fetch
ohai "Success! Downloaded to: #{downloaded_path}"
end
end
end
end
end

View File

@ -85,8 +85,7 @@ module Cask
if dry_run
if (casks_to_install = casks.reject(&:installed?).presence)
plural = ::Utils.pluralize("cask", casks_to_install.count)
ohai "Would install #{casks_to_install.count} #{plural}:"
ohai "Would install #{::Utils.pluralize("cask", casks_to_install.count, include_count: true)}:"
puts casks_to_install.map(&:full_name).join(" ")
end
casks.each do |cask|
@ -97,8 +96,8 @@ module Cask
.map(&:name)
next if dep_names.blank?
plural = ::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y")
ohai "Would install #{dep_names.count} #{plural} for #{cask.full_name}:"
ohai "Would install #{::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y",
include_count: true)} for #{cask.full_name}:"
puts dep_names.join(" ")
end
return

View File

@ -6,9 +6,6 @@ require "json"
require "lazy_object"
require "locale"
require "extend/hash_validator"
using HashValidator
module Cask
# Configuration for installing casks.
#
@ -110,8 +107,8 @@ module Cask
return
end
@env&.assert_valid_keys!(*self.class.defaults.keys)
@explicit.assert_valid_keys!(*self.class.defaults.keys)
@env&.assert_valid_keys(*self.class.defaults.keys)
@explicit.assert_valid_keys(*self.class.defaults.keys)
end
sig { returns(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]) }
@ -169,10 +166,12 @@ module Cask
DEFAULT_DIRS.each_key do |dir|
define_method(dir) do
T.bind(self, Config)
explicit.fetch(dir, env.fetch(dir, default.fetch(dir)))
end
define_method(:"#{dir}=") do |path|
T.bind(self, Config)
explicit[dir] = Pathname(path).expand_path
end
end

View File

@ -339,6 +339,7 @@ module Cask
ORDINARY_ARTIFACT_CLASSES.each do |klass|
define_method(klass.dsl_key) do |*args, **kwargs|
T.bind(self, DSL)
if [*artifacts.map(&:class), klass].include?(Artifact::StageOnly) &&
(artifacts.map(&:class) & ACTIVATABLE_ARTIFACT_CLASSES).any?
raise CaskInvalidError.new(cask, "'stage_only' must be the only activatable artifact.")
@ -355,6 +356,7 @@ module Cask
ARTIFACT_BLOCK_CLASSES.each do |klass|
[klass.dsl_key, klass.uninstall_dsl_key].each do |dsl_key|
define_method(dsl_key) do |&block|
T.bind(self, DSL)
artifacts.add(klass.new(cask, dsl_key => block))
end
end

View File

@ -1,11 +1,8 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "delegate"
require "extend/hash_validator"
using HashValidator
module Cask
class DSL
# Class corresponding to the `conflicts_with` stanza.
@ -22,16 +19,16 @@ module Cask
].freeze
def initialize(**options)
options.assert_valid_keys!(*VALID_KEYS)
options.assert_valid_keys(*VALID_KEYS)
conflicts = options.transform_values { |v| Set.new(Array(v)) }
conflicts = options.transform_values { |v| Set.new(Kernel.Array(v)) }
conflicts.default = Set.new
super(conflicts)
end
def to_json(generator)
transform_values(&:to_a).to_json(generator)
__getobj__.transform_values(&:to_a).to_json(generator)
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "delegate"
@ -38,7 +38,7 @@ module Cask
pairs.each do |key, value|
raise "invalid depends_on key: '#{key.inspect}'" unless VALID_KEYS.include?(key)
self[key] = send(:"#{key}=", *value)
__getobj__[key] = send(:"#{key}=", *value)
end
end
@ -54,15 +54,18 @@ module Cask
def macos=(*args)
raise "Only a single 'depends_on macos' is allowed." if defined?(@macos)
# workaround for https://github.com/sorbet/sorbet/issues/6860
first_arg = args.first&.to_s
begin
@macos = if args.count > 1
MacOSRequirement.new([args], comparator: "==")
elsif MacOSVersions::SYMBOLS.key?(args.first)
MacOSRequirement.new([args.first], comparator: "==")
elsif /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/ =~ args.first
MacOSRequirement.new([version.to_sym], comparator: comparator)
elsif /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/ =~ args.first
MacOSRequirement.new([version], comparator: comparator)
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/.match(first_arg))
MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator])
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/.match(first_arg))
MacOSRequirement.new([md[:version]], comparator: md[:comparator])
else # rubocop:disable Lint/DuplicateBranch
MacOSRequirement.new([args.first], comparator: "==")
end

View File

@ -0,0 +1,5 @@
# typed: strict
class Cask::DSL::DependsOn
include Kernel
end

View File

@ -32,6 +32,7 @@ module Cask
def define_divider_deletion_method(divider)
method_name = deletion_method_name(divider)
define_method(method_name) do
T.bind(self, Version)
version { delete(divider) }
end
end
@ -49,6 +50,7 @@ module Cask
def define_divider_conversion_method(left_divider, right_divider)
method_name = conversion_method_name(left_divider, right_divider)
define_method(method_name) do
T.bind(self, Version)
version { gsub(left_divider, right_divider) }
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "formula_installer"
@ -112,8 +112,9 @@ module Cask
install_artifacts
if @cask.tap&.should_report_analytics?
::Utils::Analytics.report_event(:cask_install, @cask.token, on_request: true)
if (tap = @cask.tap) && tap.should_report_analytics?
::Utils::Analytics.report_event(:cask_install, package_name: @cask.token, tap_name: tap.name,
on_request: true)
end
purge_backed_up_versioned_files
@ -241,7 +242,7 @@ module Cask
save_download_sha if @cask.version.latest?
rescue => e
begin
already_installed_artifacts.each do |artifact|
already_installed_artifacts&.each do |artifact|
if artifact.respond_to?(:uninstall_phase)
odebug "Reverting installation of artifact of class #{artifact.class}"
artifact.uninstall_phase(command: @command, verbose: verbose?, force: force?)
@ -296,7 +297,7 @@ module Cask
graph = ::Utils::TopologicalHash.graph_package_dependencies(@cask)
raise CaskSelfReferencingDependencyError, cask.token if graph[@cask].include?(@cask)
raise CaskSelfReferencingDependencyError, @cask.token if graph[@cask].include?(@cask)
::Utils::TopologicalHash.graph_package_dependencies(primary_container.dependencies, graph)
@ -454,7 +455,7 @@ module Cask
artifacts = @cask.artifacts
odebug "Uninstalling artifacts"
odebug "#{artifacts.length} #{::Utils.pluralize("artifact", artifacts.length)} defined", artifacts
odebug "#{::Utils.pluralize("artifact", artifacts.length, include_count: true)} defined", artifacts
artifacts.each do |artifact|
if artifact.respond_to?(:uninstall_phase)

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "development_tools"
@ -11,29 +11,27 @@ module Cask
module Quarantine
extend T::Sig
module_function
QUARANTINE_ATTRIBUTE = "com.apple.quarantine"
QUARANTINE_SCRIPT = (HOMEBREW_LIBRARY_PATH/"cask/utils/quarantine.swift").freeze
def swift
def self.swift
@swift ||= DevelopmentTools.locate("swift")
end
private :swift
private_class_method :swift
def xattr
def self.xattr
@xattr ||= DevelopmentTools.locate("xattr")
end
private :xattr
private_class_method :xattr
def swift_target_args
def self.swift_target_args
["-target", "#{Hardware::CPU.arch}-apple-macosx#{MacOS.version}"]
end
private :swift_target_args
private_class_method :swift_target_args
sig { returns(Symbol) }
def check_quarantine_support
def self.check_quarantine_support
odebug "Checking quarantine support"
if !system_command(xattr, args: ["-h"], print_stderr: false).success?
@ -58,13 +56,13 @@ module Cask
end
end
def available?
def self.available?
@status ||= check_quarantine_support
@status == :quarantine_available
end
def detect(file)
def self.detect(file)
return if file.nil?
odebug "Verifying Gatekeeper status of #{file}"
@ -76,13 +74,13 @@ module Cask
quarantine_status
end
def status(file)
def self.status(file)
system_command(xattr,
args: ["-p", QUARANTINE_ATTRIBUTE, file],
print_stderr: false).stdout.rstrip
end
def toggle_no_translocation_bit(attribute)
def self.toggle_no_translocation_bit(attribute)
fields = attribute.split(";")
# Fields: status, epoch, download agent, event ID
@ -94,7 +92,7 @@ module Cask
fields.join(";")
end
def release!(download_path: nil)
def self.release!(download_path: nil)
return unless detect(download_path)
odebug "Releasing #{download_path} from quarantine"
@ -112,7 +110,7 @@ module Cask
raise CaskQuarantineReleaseError.new(download_path, quarantiner.stderr)
end
def cask!(cask: nil, download_path: nil, action: true)
def self.cask!(cask: nil, download_path: nil, action: true)
return if cask.nil? || download_path.nil?
return if detect(download_path)
@ -139,7 +137,7 @@ module Cask
end
end
def propagate(from: nil, to: nil)
def self.propagate(from: nil, to: nil)
return if from.nil? || to.nil?
raise CaskError, "#{from} was not quarantined properly." unless detect(from)

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "env_config"
@ -124,7 +124,9 @@ module Cask
return true if caught_exceptions.empty?
raise MultipleCaskErrors, caught_exceptions if caught_exceptions.count > 1
raise caught_exceptions.first if caught_exceptions.count == 1
raise caught_exceptions.fetch(0) if caught_exceptions.count == 1
false
end
def self.upgrade_cask(

View File

@ -94,7 +94,8 @@ class Caveats
end
end
end
s << "\n"
s << "\n" unless s.end_with?("\n")
s
end
private

View File

@ -124,7 +124,7 @@ module Homebrew
return unless HOMEBREW_CELLAR.exist?
count = Formula.racks.length
puts "#{count} #{Utils.pluralize("keg", count)}, #{HOMEBREW_CELLAR.dup.abv}"
puts "#{Utils.pluralize("keg", count, include_count: true)}, #{HOMEBREW_CELLAR.dup.abv}"
end
sig { params(args: CLI::Args).void }

View File

@ -0,0 +1,166 @@
# typed: false
# frozen_string_literal: true
require "cli/parser"
require "formula"
module Homebrew
module_function
sig { returns(CLI::Parser) }
def postgresql_upgrade_database_args
Homebrew::CLI::Parser.new do
description <<~EOS
Upgrades the database for the `postgresql` formula.
EOS
named_args :none
end
end
sig { void }
def postgresql_upgrade_database
postgresql_upgrade_database_args.parse
name = "postgresql"
pg = Formula[name]
bin = pg.bin
var = pg.var
version = pg.version
pg_version_file = var/"postgres/PG_VERSION"
pg_version_installed = version.to_s[/^\d+/]
pg_version_data = pg_version_file.read.chomp
if pg_version_installed == pg_version_data
odie <<~EOS
#{name} data already upgraded!
EOS
end
datadir = var/"postgres"
old_datadir = var/"postgres.old"
if old_datadir.exist?
odie <<~EOS
#{old_datadir} already exists!
Remove it if you want to upgrade data automatically.
EOS
end
old_pg_name = "#{name}@#{pg_version_data}"
old_pg_glob = "#{HOMEBREW_CELLAR}/#{old_pg_name}/#{pg_version_data}.*/bin"
old_bin = Pathname.glob(old_pg_glob).first
old_bin ||= begin
Formula[old_pg_name]
ohai "brew install #{old_pg_name}"
system "brew", "install", old_pg_name
Pathname.glob(old_pg_glob).first
rescue FormulaUnavailableError
nil
end
odie "No #{name} #{pg_version_data}.* version installed!" unless old_bin
server_stopped = false
moved_data = false
initdb_run = false
upgraded = false
begin
# Following instructions from:
# https://www.postgresql.org/docs/10/static/pgupgrade.html
ohai "Upgrading #{name} data from #{pg_version_data} to #{pg_version_installed}..."
services_json_output = Utils.popen_read("brew", "services", "info", "--all", "--json")
services_json = JSON.parse(services_json_output)
loaded_service_names = services_json.select { |sj| sj[:loaded] }.map { |sj| sj[:name] }
if loaded_service_names.include?(name)
system "brew", "services", "stop", name
service_stopped = true
elsif quiet_system "#{bin}/pg_ctl", "-D", datadir, "status"
system "#{bin}/pg_ctl", "-D", datadir, "stop"
server_stopped = true
end
# Shut down old server if it is up via brew services
system "brew", "services", "stop", old_pg_name if loaded_service_names.include?(old_pg_name)
# get 'lc_collate' from old DB"
unless quiet_system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "status"
system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "start"
end
initdb_args = []
locale_settings = %w[
lc_collate
lc_ctype
lc_messages
lc_monetary
lc_numeric
lc_time
server_encoding
]
locale_settings.each do |setting|
sql = "SELECT setting FROM pg_settings WHERE name LIKE '#{setting}';"
value = Utils.popen_read("#{old_bin}/psql", "postgres", "-qtAX", "-U", ENV.fetch("USER"), "-c", sql).strip
next if value.empty?
initdb_args += if setting == "server_encoding"
["-E #{value}"]
else
["--#{setting.tr("_", "-")}=#{value}"]
end
end
if quiet_system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "status"
system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "stop"
end
ohai "Moving #{name} data from #{datadir} to #{old_datadir}..."
FileUtils.mv datadir, old_datadir
moved_data = true
(var/"postgres").mkpath
ohai "Creating database..."
safe_system "#{bin}/initdb", *initdb_args, "#{var}/postgres"
initdb_run = true
ohai "Migrating and upgrading data..."
(var/"log").cd do
safe_system "#{bin}/pg_upgrade",
"-r",
"-b", old_bin,
"-B", bin,
"-d", old_datadir,
"-D", datadir,
"-j", Hardware::CPU.cores.to_s
end
upgraded = true
ohai "Upgraded #{name} data from #{pg_version_data} to #{pg_version_installed}!"
ohai "Your #{name} #{pg_version_data} data remains at #{old_datadir}"
ensure
if upgraded
if server_stopped
safe_system "#{bin}/pg_ctl", "-D", datadir, "start"
elsif service_stopped
safe_system "brew", "services", "start", name
end
else
onoe "Upgrading #{name} data from #{pg_version_data} to #{pg_version_installed} failed!"
if initdb_run
ohai "Removing empty #{name} initdb database..."
FileUtils.rm_r datadir
end
if moved_data
ohai "Moving #{name} data back from #{old_datadir} to #{datadir}..."
FileUtils.mv old_datadir, datadir
end
if server_stopped
system "#{bin}/pg_ctl", "-D", datadir, "start"
elsif service_stopped
system "brew", "services", "start", name
end
end
end
end
end

View File

@ -57,10 +57,10 @@ module Homebrew
command_count += tap.command_files.size
private_count += 1 if tap.private?
end
info = "#{tap_count} #{Utils.pluralize("tap", tap_count)}"
info = Utils.pluralize("tap", tap_count, include_count: true)
info += ", #{private_count} private"
info += ", #{formula_count} #{Utils.pluralize("formula", formula_count, plural: "e")}"
info += ", #{command_count} #{Utils.pluralize("command", command_count)}"
info += ", #{Utils.pluralize("formula", formula_count, plural: "e", include_count: true)}"
info += ", #{Utils.pluralize("command", command_count, include_count: true)}"
info += ", #{Tap::TAP_DIRECTORY.dup.abv}" if Tap::TAP_DIRECTORY.directory?
puts info
else

View File

@ -233,8 +233,7 @@ module Homebrew
unless updated_taps.empty?
auto_update_header args: args
noun = Utils.pluralize("tap", updated_taps.count)
puts "Updated #{updated_taps.count} #{noun} (#{updated_taps.to_sentence})."
puts "Updated #{Utils.pluralize("tap", updated_taps.count, include_count: true)} (#{updated_taps.to_sentence})."
updated = true
end
@ -656,12 +655,12 @@ class ReporterHub
output_dump_formula_or_cask_report "Outdated Casks", outdated_casks
elsif report_all
if (changed_formulae = select_formula_or_cask(:M).count) && changed_formulae.positive?
noun = Utils.pluralize("formula", changed_formulae, plural: "e")
ohai "Modified Formulae", "Modified #{changed_formulae} #{noun}."
ohai "Modified Formulae",
"Modified #{Utils.pluralize("formula", changed_formulae, plural: "e", include_count: true)}."
end
if (changed_casks = select_formula_or_cask(:MC).count) && changed_casks.positive?
ohai "Modified Casks", "Modified #{changed_casks} #{Utils.pluralize("cask", changed_casks)}."
ohai "Modified Casks", "Modified #{Utils.pluralize("cask", changed_casks, include_count: true)}."
end
else
outdated_formulae = Formula.installed.select(&:outdated?).map(&:name)

View File

@ -12,6 +12,10 @@ git() {
# HOMEBREW_LIBRARY is set by bin/brew
# shellcheck disable=SC2154
GIT_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/git" --homebrew=print-path)"
if [[ -z "${GIT_EXECUTABLE}" ]]
then
odie "Can't find a working Git!"
fi
fi
"${GIT_EXECUTABLE}" "$@"
}

View File

@ -25,6 +25,10 @@ curl() {
if [[ -z "${CURL_EXECUTABLE}" ]]
then
CURL_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/curl" --homebrew=print-path)"
if [[ -z "${CURL_EXECUTABLE}" ]]
then
odie "Can't find a working Curl!"
fi
fi
"${CURL_EXECUTABLE}" "$@"
}
@ -33,6 +37,10 @@ git() {
if [[ -z "${GIT_EXECUTABLE}" ]]
then
GIT_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/git" --homebrew=print-path)"
if [[ -z "${GIT_EXECUTABLE}" ]]
then
odie "Can't find a working Git!"
fi
fi
"${GIT_EXECUTABLE}" "$@"
}

View File

@ -275,22 +275,21 @@ module Homebrew
if total_problems_count.positive?
puts new_formula_problem_lines.map { |s| " #{s}" }
errors_summary = "#{total_problems_count} #{Utils.pluralize("problem", total_problems_count)}"
errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true)
error_sources = []
if formula_count.positive?
error_sources << "#{formula_count} #{Utils.pluralize("formula", formula_count, plural: "e")}"
error_sources << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
end
error_sources << "#{cask_count} #{Utils.pluralize("cask", cask_count)}" if cask_count.positive?
error_sources << "#{tap_count} #{Utils.pluralize("tap", tap_count)}" if tap_count.positive?
error_sources << Utils.pluralize("cask", cask_count, include_count: true) if cask_count.positive?
error_sources << Utils.pluralize("tap", tap_count, include_count: true) if tap_count.positive?
errors_summary += " in #{error_sources.to_sentence}" if error_sources.any?
errors_summary += " detected"
if corrected_problem_count.positive?
noun = Utils.pluralize("problem", corrected_problem_count)
errors_summary += ", #{corrected_problem_count} #{noun} corrected"
errors_summary += ", #{Utils.pluralize("problem", corrected_problem_count, include_count: true)} corrected"
end
ofail errors_summary

View File

@ -64,7 +64,8 @@ module Homebrew
results[user] = scan_repositories(repos, user, args)
grand_totals[user] = total(results[user])
puts "#{user} contributed #{grand_totals[user].values.sum} times #{time_period(args)}."
puts "#{user} contributed #{Utils.pluralize("time", grand_totals[user].values.sum,
include_count: true)} #{time_period(args)}."
puts generate_csv(T.must(user), results[user], grand_totals[user]) if args.csv?
return
end
@ -79,7 +80,8 @@ module Homebrew
results[username] = scan_repositories(repos, username, args)
grand_totals[username] = total(results[username])
puts "#{username} contributed #{grand_totals[username].values.sum} times #{time_period(args)}."
puts "#{username} contributed #{Utils.pluralize("time", grand_totals[username].values.sum,
include_count: true)} #{time_period(args)}."
end
puts generate_maintainers_csv(grand_totals) if args.csv?

View File

@ -22,6 +22,8 @@ module Homebrew
description: "Download the bottles but don't upload them."
switch "--no-commit",
description: "Do not generate a new commit before uploading."
switch "--no-cherry-pick",
description: "Do not cherry-pick commits from the pull request branch."
switch "-n", "--dry-run",
description: "Print what would be done rather than doing it."
switch "--clean",
@ -447,10 +449,16 @@ module Homebrew
ohai "Fetching #{tap} pull request ##{pr}"
Dir.mktmpdir pr do |dir|
cd dir do
original_commit = ENV["GITHUB_SHA"].presence || tap.path.git_head
current_branch_head = ENV["GITHUB_SHA"] || tap.git_head
original_commit = if args.no_cherry_pick?
# TODO: Handle the case where `merge-base` returns multiple commits.
Utils.safe_popen_read("git", "-C", tap.path, "merge-base", "origin/HEAD", current_branch_head).strip
else
current_branch_head
end
unless args.no_commit?
cherry_pick_pr!(user, repo, pr, path: tap.path, args: args)
cherry_pick_pr!(user, repo, pr, path: tap.path, args: args) unless args.no_cherry_pick?
if !args.no_autosquash? && !args.dry_run?
autosquash!(original_commit, tap: tap,
verbose: args.verbose?, resolve: args.resolve?, reason: args.message)

View File

@ -908,7 +908,7 @@ module Homebrew
0
end
"#{tap.path} (#{cask_count} #{Utils.pluralize("cask", cask_count)})"
"#{tap.path} (#{Utils.pluralize("cask", cask_count, include_count: true)})"
end
end)

View File

@ -491,10 +491,13 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
end
end
filename = content_disposition.filename if filename.blank?
next if filename.blank?
# Servers may include '/' in their Content-Disposition filename header. Take only the basename of this, because:
# - Unpacking code assumes this is a single file - not something living in a subdirectory.
# - Directory traversal attacks are possible without limiting this to just the basename.
File.basename(filename || content_disposition.filename)
File.basename(filename)
end
filenames = lines.map(&parse_content_disposition).compact

View File

@ -1,8 +1,5 @@
# typed: strict
module GitRepositoryExtension
include Kernel
sig { params(args: T.any(String, Pathname)).returns(Pathname) }
def join(*args); end
requires_ancestor { Pathname }
end

View File

@ -1,13 +0,0 @@
# typed: false
# frozen_string_literal: true
module HashValidator
refine Hash do
def assert_valid_keys!(*valid_keys)
unknown_keys = keys - valid_keys
return if unknown_keys.empty?
raise ArgumentError, "invalid keys: #{unknown_keys.map(&:inspect).join(", ")}"
end
end
end

View File

@ -1,6 +0,0 @@
# typed: strict
class Hash
sig { params(valid_keys: T.untyped).void }
def assert_valid_keys!(*valid_keys); end
end

View File

@ -199,13 +199,13 @@ module Kernel
if seconds > 59
minutes = seconds / 60
seconds %= 60
res = +"#{minutes} #{Utils.pluralize("minute", minutes)}"
res = +Utils.pluralize("minute", minutes, include_count: true)
return res.freeze if seconds.zero?
res << " "
end
res << "#{seconds} #{Utils.pluralize("second", seconds)}"
res << Utils.pluralize("second", seconds, include_count: true)
res.freeze
end

View File

@ -0,0 +1,6 @@
# typed: strict
class Object
sig { returns(T::Boolean) }
def present?; end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "simulate_system"
@ -9,17 +9,15 @@ module OnSystem
ARCH_OPTIONS = [:intel, :arm].freeze
BASE_OS_OPTIONS = [:macos, :linux].freeze
module_function
sig { params(arch: Symbol).returns(T::Boolean) }
def arch_condition_met?(arch)
def self.arch_condition_met?(arch)
raise ArgumentError, "Invalid arch condition: #{arch.inspect}" if ARCH_OPTIONS.exclude?(arch)
arch == Homebrew::SimulateSystem.current_arch
end
sig { params(os_name: Symbol, or_condition: T.nilable(Symbol)).returns(T::Boolean) }
def os_condition_met?(os_name, or_condition = nil)
def self.os_condition_met?(os_name, or_condition = nil)
return Homebrew::SimulateSystem.send("simulating_or_running_on_#{os_name}?") if BASE_OS_OPTIONS.include?(os_name)
raise ArgumentError, "Invalid OS condition: #{os_name.inspect}" unless MacOSVersions::SYMBOLS.key?(os_name)
@ -46,17 +44,17 @@ module OnSystem
end
sig { params(method_name: Symbol).returns(Symbol) }
def condition_from_method_name(method_name)
def self.condition_from_method_name(method_name)
method_name.to_s.sub(/^on_/, "").to_sym
end
sig { params(base: Class).void }
def setup_arch_methods(base)
def self.setup_arch_methods(base)
ARCH_OPTIONS.each do |arch|
base.define_method("on_#{arch}") do |&block|
@on_system_blocks_exist = true
return unless OnSystem.arch_condition_met? OnSystem.condition_from_method_name(__method__)
return unless OnSystem.arch_condition_met? OnSystem.condition_from_method_name(T.must(__method__))
@called_in_on_system_block = true
result = block.call
@ -75,12 +73,12 @@ module OnSystem
end
sig { params(base: Class).void }
def setup_base_os_methods(base)
def self.setup_base_os_methods(base)
BASE_OS_OPTIONS.each do |base_os|
base.define_method("on_#{base_os}") do |&block|
@on_system_blocks_exist = true
return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(__method__)
return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(T.must(__method__))
@called_in_on_system_block = true
result = block.call
@ -118,12 +116,12 @@ module OnSystem
end
sig { params(base: Class).void }
def setup_macos_methods(base)
def self.setup_macos_methods(base)
MacOSVersions::SYMBOLS.each_key do |os_name|
base.define_method("on_#{os_name}") do |or_condition = nil, &block|
@on_system_blocks_exist = true
os_condition = OnSystem.condition_from_method_name __method__
os_condition = OnSystem.condition_from_method_name T.must(__method__)
return unless OnSystem.os_condition_met? os_condition, or_condition
@called_in_on_system_block = true

View File

@ -11,8 +11,6 @@ module UnpackStrategy
include UnpackStrategy
include SystemCommand::Mixin
using Magic
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
def extract_to_dir(unpack_dir, basename:, verbose:)
with_env(TZ: "UTC") do

View File

@ -1,9 +1,5 @@
# typed: strict
module UnpackStrategy
class Zip
module MacOSZipExtension
include Kernel
end
end
module UnpackStrategy::Zip::MacOSZipExtension
include Kernel
end

View File

@ -343,14 +343,24 @@ class Pathname
chmod saved_perms if saved_perms
end
# @private
def which_install_info
@which_install_info ||=
if File.executable?("/usr/bin/install-info")
"/usr/bin/install-info"
elsif Formula["texinfo"].any_version_installed?
Formula["texinfo"].opt_bin/"install-info"
end
end
# @private
def install_info
quiet_system "/usr/bin/install-info", "--quiet", to_s, "#{dirname}/dir"
quiet_system(which_install_info, "--quiet", to_s, "#{dirname}/dir")
end
# @private
def uninstall_info
quiet_system "/usr/bin/install-info", "--delete", "--quiet", to_s, "#{dirname}/dir"
quiet_system(which_install_info, "--delete", "--quiet", to_s, "#{dirname}/dir")
end
# Writes an exec script in this folder for each target pathname.
@ -456,6 +466,32 @@ class Pathname
def rpaths
[]
end
sig { returns(String) }
def magic_number
@magic_number ||= if directory?
""
else
# Length of the longest regex (currently Tar).
max_magic_number_length = 262
# FIXME: The `T.let` is a workaround until we have https://github.com/sorbet/sorbet/pull/6865
T.let(binread(max_magic_number_length), T.nilable(String)) || ""
end
end
sig { returns(String) }
def file_type
@file_type ||= system_command("file", args: ["-b", self], print_stderr: false)
.stdout.chomp
end
sig { returns(T::Array[String]) }
def zipinfo
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
.stdout
.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
end
end
require "extend/os/pathname"

View File

@ -1,19 +1,9 @@
# typed: strict
module DiskUsageExtension
include Kernel
def exist?; end
def symlink?; end
def resolved_path; end
requires_ancestor { Pathname }
end
module ObserverPathnameExtension
include Kernel
def dirname; end
def basename; end
requires_ancestor { Pathname }
end

View File

@ -466,6 +466,16 @@ module Homebrew
"They must not be upgraded to version 7.11 or newer."
end
def audit_keg_only_reason
return unless @core_tap
return unless formula.keg_only?
keg_only_message = text.to_s.match(/keg_only\s+["'](.*)["']/)&.captures&.first
return unless keg_only_message&.include?("HOMEBREW_PREFIX")
problem "`keg_only` reason should not include `HOMEBREW_PREFIX` as it creates confusing `brew info` output."
end
def audit_versioned_keg_only
return unless @versioned_formula
return unless @core_tap

View File

@ -414,9 +414,9 @@ class FormulaInstaller
options = display_options(formula).join(" ")
oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip if show_header?
if formula.tap&.should_report_analytics?
action = "#{formula.full_name} #{options}".strip
Utils::Analytics.report_event(:formula_install, action, on_request: installed_on_request?)
if (tap = formula.tap) && tap.should_report_analytics?
Utils::Analytics.report_event(:formula_install, package_name: formula.name, tap_name: tap.name,
on_request: installed_on_request?, options: options)
end
self.class.attempted << formula

View File

@ -326,8 +326,8 @@ module Homebrew
if dry_run
if (formulae_name_to_install = formulae_to_install.map(&:name))
plural = Utils.pluralize("formula", formulae_name_to_install.count, plural: "e")
ohai "Would install #{formulae_name_to_install.count} #{plural}:"
ohai "Would install #{Utils.pluralize("formula", formulae_name_to_install.count,
plural: "e", include_count: true)}:"
puts formulae_name_to_install.join(" ")
formula_installers.each do |fi|
@ -355,8 +355,8 @@ module Homebrew
def print_dry_run_dependencies(formula, dependencies, &block)
return if dependencies.empty?
plural = Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y")
ohai "Would install #{dependencies.count} #{plural} for #{formula.name}:"
ohai "Would install #{Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y",
include_count: true)} for #{formula.name}:"
formula_names = dependencies.map(&:first).map(&:to_formula).map(&block)
puts formula_names.join(" ")
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "keg_relocate"
@ -524,8 +524,8 @@ class Keg
end
def delete_pyc_files!
find { |pn| pn.delete if PYC_EXTENSIONS.include?(pn.extname) }
find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" }
path.find { |pn| pn.delete if PYC_EXTENSIONS.include?(pn.extname) }
path.find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" }
end
def binary_executable_or_library_files

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
# Performs {Formula#mktemp}'s functionality, and tracks the results.
@ -70,7 +70,7 @@ class Mktemp
begin
chown(nil, group_id, @tmpdir)
rescue Errno::EPERM
opoo "Failed setting group \"#{Etc.getgrgid(group_id).name}\" on #{@tmpdir}"
opoo "Failed setting group \"#{T.must(Etc.getgrgid(group_id)).name}\" on #{@tmpdir}"
end
begin

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
# A formula option.

View File

@ -0,0 +1,8 @@
# typed: strict
class Options
# This is a workaround to enable `alias to_ary to_a`
# @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238
sig { returns(T::Array[Option]) }
def to_a; end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
# {Pathname} extension for dealing with ELF files.

View File

@ -0,0 +1,5 @@
# typed: strict
module ELFShim
requires_ancestor { Pathname }
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "macho"

View File

@ -0,0 +1,5 @@
# typed: strict
module MachOShim
include Kernel
end

View File

@ -45,7 +45,7 @@ module OS
sig { returns(T.self_type) }
def strip_patch
# Big Sur is 11.x but Catalina is 10.15.x.
if major >= 11
if T.must(major) >= 11
self.class.new(major.to_s)
else
major_minor

View File

@ -1,10 +0,0 @@
# typed: strict
module OS
module Mac
class Version
sig { returns(Token) }
def major; end
end
end
end

View File

@ -36,7 +36,15 @@ module RuboCop
end
def toplevel_stanzas
@toplevel_stanzas ||= stanzas.select(&:toplevel_stanza?)
# If a `cask` block only contains one stanza, it is that stanza's direct parent,
# otherwise stanzas are grouped in a block and `cask` is that block's parent.
is_toplevel_stanza = if cask_body.begin_block?
->(stanza) { stanza.parent_node.parent.cask_block? }
else
->(stanza) { stanza.parent_node.cask_block? }
end
@toplevel_stanzas ||= stanzas.select(&is_toplevel_stanza)
end
def sorted_toplevel_stanzas

View File

@ -52,10 +52,6 @@ module RuboCop
stanza_group == other.stanza_group
end
def toplevel_stanza?
parent_node.cask_block? || parent_node.parent.cask_block?
end
def ==(other)
self.class == other.class && stanza_node == other.stanza_node
end

View File

@ -17,6 +17,8 @@ module RuboCop
def_node_matcher :cask_block?, "(block (send nil? :cask _) args ...)"
def_node_matcher :arch_variable?, "(lvasgn _ (send nil? :on_arch_conditional ...))"
def_node_matcher :begin_block?, "(begin ...)"
def stanza?
return true if arch_variable?

View File

@ -0,0 +1,88 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for types exported from the `stackprof` gem.
# Please instead update this file by running `bin/tapioca gem stackprof`.
module StackProf
class << self
def results(*_arg0); end
def run(*_arg0); end
def running?; end
def sample; end
def start(*_arg0); end
def stop; end
def use_postponed_job!; end
end
end
class StackProf::Middleware
def initialize(app, options = T.unsafe(nil)); end
def call(env); end
class << self
def enabled; end
def enabled=(_arg0); end
def enabled?(env); end
def interval; end
def interval=(_arg0); end
def metadata; end
def metadata=(_arg0); end
def mode; end
def mode=(_arg0); end
def path; end
def path=(_arg0); end
def raw; end
def raw=(_arg0); end
def save; end
end
end
class StackProf::Report
def initialize(data); end
def +(other); end
def add_lines(a, b); end
def convert_to_d3_flame_graph_format(name, stacks, depth); end
def data; end
def files; end
def flamegraph_row(f, x, y, weight, addr); end
def flamegraph_stacks(raw); end
def frames(sort_by_total = T.unsafe(nil)); end
def max_samples; end
def modeline; end
def normalized_frames; end
def overall_samples; end
def print_alphabetical_flamegraph(f = T.unsafe(nil), skip_common = T.unsafe(nil)); end
def print_callgrind(f = T.unsafe(nil)); end
def print_d3_flamegraph(f = T.unsafe(nil), skip_common = T.unsafe(nil)); end
def print_debug; end
def print_dump(f = T.unsafe(nil)); end
def print_file(filter, f = T.unsafe(nil)); end
def print_files(sort_by_total = T.unsafe(nil), limit = T.unsafe(nil), f = T.unsafe(nil)); end
def print_flamegraph(f, skip_common, alphabetical = T.unsafe(nil)); end
def print_graphviz(options = T.unsafe(nil), f = T.unsafe(nil)); end
def print_json(f = T.unsafe(nil)); end
def print_method(name, f = T.unsafe(nil)); end
def print_stackcollapse; end
def print_text(sort_by_total = T.unsafe(nil), limit = T.unsafe(nil), select_files = T.unsafe(nil), reject_files = T.unsafe(nil), select_names = T.unsafe(nil), reject_names = T.unsafe(nil), f = T.unsafe(nil)); end
def print_timeline_flamegraph(f = T.unsafe(nil), skip_common = T.unsafe(nil)); end
def version; end
def walk_method(name); end
private
def callers_for(addr); end
def root_frames; end
def source_display(f, file, lines, range = T.unsafe(nil)); end
class << self
def from_file(file); end
def from_json(json); end
def parse_json(json); end
end
end
StackProf::Report::MARSHAL_SIGNATURE = T.let(T.unsafe(nil), String)
StackProf::VERSION = T.let(T.unsafe(nil), String)

View File

@ -6469,6 +6469,8 @@ end
class RuboCop::AST::Node
def arch_variable?(param0=T.unsafe(nil)); end
def begin_block?(param0=T.unsafe(nil)); end
def block_args(param0=T.unsafe(nil)); end
def block_body(param0=T.unsafe(nil)); end

View File

@ -4,7 +4,6 @@
# typed: false
module ::StackProf; end
module T::InterfaceWrapper::Helpers; end
module T::Private::Abstract::Hooks; end
module T::Private::Methods::MethodHooks; end

View File

@ -1,23 +0,0 @@
# typed: strict
# This file contains temporary definitions for fixes that have
# been submitted upstream to https://github.com/sorbet/sorbet.
class Module
# https://github.com/sorbet/sorbet/pull/3732
sig do
params(
arg0: T.any(Symbol, String),
arg1: T.any(Proc, Method, UnboundMethod)
)
.returns(Symbol)
end
sig do
params(
arg0: T.any(Symbol, String),
blk: T.proc.bind(T.untyped).returns(T.untyped),
)
.returns(Symbol)
end
def define_method(arg0, arg1=T.unsafe(nil), &blk); end
end

View File

@ -7,7 +7,6 @@ require "shellwords"
require "extend/io"
require "extend/predicable"
require "extend/hash_validator"
require "extend/time"

View File

@ -475,15 +475,15 @@ class Tap
contents = []
if (command_count = command_files.count).positive?
contents << "#{command_count} #{Utils.pluralize("command", command_count)}"
contents << Utils.pluralize("command", command_count, include_count: true)
end
if (cask_count = cask_files.count).positive?
contents << "#{cask_count} #{Utils.pluralize("cask", cask_count)}"
contents << Utils.pluralize("cask", cask_count, include_count: true)
end
if (formula_count = formula_files.count).positive?
contents << "#{formula_count} #{Utils.pluralize("formula", formula_count, plural: "e")}"
contents << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
end
contents

View File

@ -1,60 +0,0 @@
# typed: false
# frozen_string_literal: true
describe Cask::Cmd::Fetch, :cask do
let(:local_transmission) do
Cask::CaskLoader.load(cask_path("local-transmission"))
end
let(:local_caffeine) do
Cask::CaskLoader.load(cask_path("local-caffeine"))
end
it "allows downloading the installer of a Cask" do
transmission_location = CurlDownloadStrategy.new(
local_transmission.url.to_s, local_transmission.token, local_transmission.version,
cache: Cask::Cache.path, **local_transmission.url.specs
).cached_location
caffeine_location = CurlDownloadStrategy.new(
local_caffeine.url.to_s, local_caffeine.token, local_caffeine.version,
cache: Cask::Cache.path, **local_caffeine.url.specs
).cached_location
expect(transmission_location).not_to exist
expect(caffeine_location).not_to exist
described_class.run("local-transmission", "local-caffeine")
expect(transmission_location).to exist
expect(caffeine_location).to exist
end
it "prevents double fetch (without nuking existing installation)" do
cached_location = Cask::Download.new(local_transmission).fetch
old_ctime = File.stat(cached_location).ctime
described_class.run("local-transmission", "--no-quarantine")
new_ctime = File.stat(cached_location).ctime
expect(old_ctime.to_i).to eq(new_ctime.to_i)
end
it "allows double fetch with --force" do
cached_location = Cask::Download.new(local_transmission).fetch
old_ctime = File.stat(cached_location).ctime
sleep(1)
described_class.run("local-transmission", "--force", "--no-quarantine")
new_ctime = File.stat(cached_location).ctime
expect(new_ctime.to_i).to be > old_ctime.to_i
end
it "properly handles Casks that are not present" do
expect do
described_class.run("notacask")
end.to raise_error(Cask::CaskUnavailableError)
end
end

View File

@ -2,7 +2,6 @@
# frozen_string_literal: true
require "cask/cmd/audit"
require "cask/cmd/fetch"
require "cask/cmd/install"
require "cask/cask_loader"
require "cask/download"
@ -29,7 +28,8 @@ describe Cask::Quarantine, :cask do
end
it "quarantines Cask fetches" do
Cask::Cmd::Fetch.run("local-transmission")
download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: true)
download.fetch
local_transmission = Cask::CaskLoader.load(cask_path("local-transmission"))
cached_location = Cask::Download.new(local_transmission).fetch
@ -50,7 +50,8 @@ describe Cask::Quarantine, :cask do
end
it "quarantines Cask installs even if the fetch was not" do
Cask::Cmd::Fetch.run("local-transmission", "--no-quarantine")
download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: false)
download.fetch
Cask::Cmd::Install.run("local-transmission")
@ -144,7 +145,8 @@ describe Cask::Quarantine, :cask do
end
it "does not quarantine Cask fetches" do
Cask::Cmd::Fetch.run("local-transmission", "--no-quarantine")
download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: false)
download.fetch
local_transmission = Cask::CaskLoader.load(cask_path("local-transmission"))
cached_location = Cask::Download.new(local_transmission).fetch
@ -165,7 +167,8 @@ describe Cask::Quarantine, :cask do
end
it "does not quarantine Cask installs even if the fetch was" do
Cask::Cmd::Fetch.run("local-transmission")
download = Cask::Download.new(Cask::CaskLoader.load("local-transmission"), quarantine: true)
download.fetch
Cask::Cmd::Install.run("local-transmission", "--no-quarantine")

View File

@ -249,6 +249,32 @@ describe Caveats do
expect(caveats).to include("#{f.opt_share}/pkgconfig")
end
end
context "when joining different caveat types together" do
let(:f) do
formula do
url "foo-1.0"
keg_only "some reason"
def caveats
"something else"
end
service do
run [bin/"cmd"]
end
end
end
let(:caveats) { described_class.new(f).caveats }
it "adds the correct amount of new lines to the output" do
expect(caveats).to include("something else")
expect(caveats).to include("keg-only")
expect(caveats).to include("if you don't want/need a background service")
expect(caveats.count("\n")).to eq(9)
end
end
end
describe "shell completions" do

View File

@ -6,7 +6,7 @@ require "formula_installer"
describe Utils::Analytics do
before do
described_class.clear_additional_tags_cache
described_class.clear_cache
end
describe "::label_google" do
@ -45,64 +45,68 @@ describe Utils::Analytics do
end
end
describe "::additional_tags_influx" do
describe "::default_tags_influx" do
let(:ci) { ", CI" if ENV["CI"] }
it "returns OS_VERSION and prefix when HOMEBREW_PREFIX is a custom prefix on intel" do
expect(Homebrew).to receive(:default_prefix?).and_return(false).at_least(:once)
expect(described_class.additional_tags_influx).to have_key(:prefix)
expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix"
expect(described_class.default_tags_influx).to have_key(:prefix)
expect(described_class.default_tags_influx[:prefix]).to eq "custom-prefix"
end
it "returns OS_VERSION, ARM and prefix when HOMEBREW_PREFIX is a custom prefix on arm" do
expect(Homebrew).to receive(:default_prefix?).and_return(false).at_least(:once)
expect(described_class.additional_tags_influx).to have_key(:arch)
expect(described_class.additional_tags_influx[:arch]).to eq HOMEBREW_PHYSICAL_PROCESSOR
expect(described_class.additional_tags_influx).to have_key(:prefix)
expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix"
expect(described_class.default_tags_influx).to have_key(:arch)
expect(described_class.default_tags_influx[:arch]).to eq HOMEBREW_PHYSICAL_PROCESSOR
expect(described_class.default_tags_influx).to have_key(:prefix)
expect(described_class.default_tags_influx[:prefix]).to eq "custom-prefix"
end
it "returns OS_VERSION, Rosetta and prefix when HOMEBREW_PREFIX is a custom prefix on Rosetta", :needs_macos do
expect(Homebrew).to receive(:default_prefix?).and_return(false).at_least(:once)
expect(described_class.additional_tags_influx).to have_key(:prefix)
expect(described_class.additional_tags_influx[:prefix]).to eq "custom-prefix"
expect(described_class.default_tags_influx).to have_key(:prefix)
expect(described_class.default_tags_influx[:prefix]).to eq "custom-prefix"
end
it "does not include prefix when HOMEBREW_PREFIX is the default prefix" do
expect(Homebrew).to receive(:default_prefix?).and_return(true).at_least(:once)
expect(described_class.additional_tags_influx).to have_key(:prefix)
expect(described_class.additional_tags_influx[:prefix]).to eq HOMEBREW_PREFIX.to_s
expect(described_class.default_tags_influx).to have_key(:prefix)
expect(described_class.default_tags_influx[:prefix]).to eq HOMEBREW_PREFIX.to_s
end
it "includes CI when ENV['CI'] is set" do
ENV["CI"] = "1"
expect(described_class.additional_tags_influx).to have_key(:ci)
expect(described_class.default_tags_influx).to have_key(:ci)
end
it "includes developer when ENV['HOMEBREW_DEVELOPER'] is set" do
expect(Homebrew::EnvConfig).to receive(:developer?).and_return(true)
expect(described_class.additional_tags_influx).to have_key(:developer)
expect(described_class.default_tags_influx).to have_key(:developer)
end
end
describe "::report_event" do
let(:f) { formula { url "foo-1.0" } }
let(:options) { ["--head"].join }
let(:action) { "#{f.full_name} #{options}".strip }
let(:package_name) { f.name }
let(:tap_name) { f.tap.name }
let(:on_request) { false }
let(:options) { "--HEAD" }
context "when ENV vars is set" do
it "returns nil when HOMEBREW_NO_ANALYTICS is true" do
ENV["HOMEBREW_NO_ANALYTICS"] = "true"
expect(described_class).not_to receive(:report_google)
expect(described_class).not_to receive(:report_influx)
described_class.report_event(:install, action)
described_class.report_event(:install, package_name: package_name, tap_name: tap_name,
on_request: on_request, options: options)
end
it "returns nil when HOMEBREW_NO_ANALYTICS_THIS_RUN is true" do
ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"] = "true"
expect(described_class).not_to receive(:report_google)
expect(described_class).not_to receive(:report_influx)
described_class.report_event(:install, action)
described_class.report_event(:install, package_name: package_name, tap_name: tap_name,
on_request: on_request, options: options)
end
it "returns nil when HOMEBREW_ANALYTICS_DEBUG is true" do
@ -112,7 +116,8 @@ describe Utils::Analytics do
expect(described_class).to receive(:report_google)
expect(described_class).to receive(:report_influx)
described_class.report_event(:install, action)
described_class.report_event(:install, package_name: package_name, tap_name: tap_name,
on_request: on_request, options: options)
end
end
@ -120,40 +125,47 @@ describe Utils::Analytics do
ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN")
ENV.delete("HOMEBREW_NO_ANALYTICS")
ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true"
expect(Homebrew::EnvConfig).to receive(:developer?).and_return(false)
expect(described_class).to receive(:report_google).with(:event, hash_including(ea: action)).once
expect(described_class).to receive(:report_influx).with(:install, "formula_name --head", false,
hash_including(developer: false)).once
described_class.report_event(:install, action)
expect(described_class).to receive(:report_google).with(:event,
hash_including(ea: "#{package_name} #{options}")).once
expect(described_class).to receive(:report_influx).with(:install, hash_including(package_name: package_name,
on_request: on_request)).once
described_class.report_event(:install, package_name: package_name, tap_name: tap_name,
on_request: on_request, options: options)
end
it "sends to google twice on request" do
ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN")
ENV.delete("HOMEBREW_NO_ANALYTICS")
ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true"
expect(Homebrew::EnvConfig).to receive(:developer?).and_return(false)
expect(described_class).to receive(:report_google).with(:event, hash_including(ea: action, ec: :install)).once
expect(described_class).to receive(:report_google).with(:event,
hash_including(ea: action,
hash_including(ea: "#{package_name} #{options}",
ec: :install)).once
expect(described_class).to receive(:report_google).with(:event,
hash_including(ea: "#{package_name} #{options}",
ec: :install_on_request)).once
expect(described_class).to receive(:report_influx).with(:install, "formula_name --head", true,
hash_including(developer: false)).once
expect(described_class).to receive(:report_influx).with(:install,
hash_including(package_name: package_name,
on_request: true)).once
described_class.report_event(:install, action, on_request: true)
described_class.report_event(:install, package_name: package_name, tap_name: tap_name,
on_request: true, options: options)
end
end
describe "::report_influx" do
let(:f) { formula { url "foo-1.0" } }
let(:options) { ["--head"].join }
let(:action) { "#{f.full_name} #{options}".strip }
let(:package_name) { f.name }
let(:tap_name) { f.tap.name }
let(:on_request) { false }
let(:options) { "--HEAD" }
it "outputs in debug mode" do
ENV.delete("HOMEBREW_NO_ANALYTICS_THIS_RUN")
ENV.delete("HOMEBREW_NO_ANALYTICS")
ENV["HOMEBREW_ANALYTICS_DEBUG"] = "true"
expect(described_class).to receive(:deferred_curl).once
described_class.report_influx(:install, action, true, developer: true, CI: true)
described_class.report_influx(:install, package_name: package_name, tap_name: tap_name, on_request: on_request,
options: options)
end
end

View File

@ -70,6 +70,12 @@ describe Utils do
expect(described_class.pluralize("foo", 1, singular: "o", plural: "es")).to eq("fooo")
expect(described_class.pluralize("foo", 2, singular: "o", plural: "es")).to eq("fooes")
end
it "includes the count when requested" do
expect(described_class.pluralize("foo", 0, include_count: true)).to eq("0 foos")
expect(described_class.pluralize("foo", 1, include_count: true)).to eq("1 foo")
expect(described_class.pluralize("foo", 2, include_count: true)).to eq("2 foos")
end
end
describe ".underscore" do

View File

@ -1,29 +1,8 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "system_command"
# Helper module for iterating over directory trees.
#
# @api private
module PathnameEachDirectory
refine Pathname do
extend T::Sig
sig {
type_parameters(:T)
.params(
_block: T.proc.params(path: Pathname).returns(T.type_parameter(:T)),
).returns(T.type_parameter(:T))
}
def each_directory(&_block)
find do |path|
yield path if path.directory?
end
end
end
end
# Module containing all available strategies for unpacking archives.
#
# @api private
@ -33,38 +12,6 @@ module UnpackStrategy
include SystemCommand::Mixin
using PathnameEachDirectory
# Helper module for identifying the file type.
module Magic
# Length of the longest regex (currently Tar).
MAX_MAGIC_NUMBER_LENGTH = 262
private_constant :MAX_MAGIC_NUMBER_LENGTH
refine Pathname do
def magic_number
@magic_number ||= if directory?
""
else
binread(MAX_MAGIC_NUMBER_LENGTH) || ""
end
end
def file_type
@file_type ||= system_command("file", args: ["-b", self], print_stderr: false)
.stdout.chomp
end
def zipinfo
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
.stdout
.encode(Encoding::UTF_8, invalid: :replace)
.split("\n")
end
end
end
private_constant :Magic
def self.strategies
@strategies ||= [
Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd.
@ -195,7 +142,7 @@ module UnpackStrategy
end
# Ensure all extracted directories are writable.
tmp_unpack_dir.each_directory do |path|
each_directory(tmp_unpack_dir) do |path|
next if path.writable?
FileUtils.chmod "u+w", path, verbose: verbose
@ -208,6 +155,19 @@ module UnpackStrategy
def dependencies
[]
end
# Helper method for iterating over directory trees.
sig {
params(
pathname: Pathname,
_block: T.proc.params(path: Pathname).void,
).returns(T.nilable(Pathname))
}
def each_directory(pathname, &_block)
pathname.find do |path|
yield path if path.directory?
end
end
end
require "unpack_strategy/air"

View File

@ -3,14 +3,3 @@
module UnpackStrategy
include Kernel
end
class Pathname
sig { returns(String) }
def magic_number; end
sig { returns(String) }
def file_type; end
sig { returns(T::Array[String]) }
def zipinfo; end
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".air"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Bazaar < Directory
extend T::Sig
using Magic
def self.can_extract?(path)
super && (path/".bzr").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".bz2"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".cab"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Compress < Tar
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".Z"]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking CVS repositories.
class Cvs < Directory
using Magic
def self.can_extract?(path)
super && (path/"CVS").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "tempfile"
@ -12,6 +12,8 @@ module UnpackStrategy
# Helper module for listing the contents of a volume mounted from a disk image.
module Bom
extend T::Sig
DMG_METADATA = Set.new(%w[
.background
.com.apple.timemachine.donotpresent
@ -35,117 +37,107 @@ module UnpackStrategy
end
end
refine Pathname do
extend T::Sig
# Check if path is considered disk image metadata.
sig { params(pathname: Pathname).returns(T::Boolean) }
def self.dmg_metadata?(pathname)
DMG_METADATA.include?(pathname.cleanpath.ascend.to_a.last.to_s)
end
# Check if path is considered disk image metadata.
sig { returns(T::Boolean) }
def dmg_metadata?
DMG_METADATA.include?(cleanpath.ascend.to_a.last.to_s)
# Check if path is a symlink to a system directory (commonly to /Applications).
sig { params(pathname: Pathname).returns(T::Boolean) }
def self.system_dir_symlink?(pathname)
pathname.symlink? && MacOS.system_dir?(pathname.dirname.join(pathname.readlink))
end
sig { params(pathname: Pathname).returns(String) }
def self.bom(pathname)
tries = 0
result = loop do
# We need to use `find` here instead of Ruby in order to properly handle
# file names containing special characters, such as “e” + “´” vs. “é”.
r = system_command("find", args: [".", "-print0"], chdir: pathname, print_stderr: false)
tries += 1
# Spurious bug on CI, which in most cases can be worked around by retrying.
break r unless r.stderr.match?(/Interrupted system call/i)
raise "Command `#{r.command.shelljoin}` was interrupted." if tries >= 3
end
# Check if path is a symlink to a system directory (commonly to /Applications).
sig { returns(T::Boolean) }
def system_dir_symlink?
symlink? && MacOS.system_dir?(dirname.join(readlink))
end
odebug "Command `#{result.command.shelljoin}` in '#{pathname}' took #{tries} tries." if tries > 1
sig { returns(String) }
def bom
tries = 0
result = loop do
# We need to use `find` here instead of Ruby in order to properly handle
# file names containing special characters, such as “e” + “´” vs. “é”.
r = system_command("find", args: [".", "-print0"], chdir: self, print_stderr: false)
tries += 1
bom_paths = result.stdout.split("\0")
# Spurious bug on CI, which in most cases can be worked around by retrying.
break r unless r.stderr.match?(/Interrupted system call/i)
raise EmptyError, pathname if bom_paths.empty?
raise "Command `#{r.command.shelljoin}` was interrupted." if tries >= 3
end
odebug "Command `#{result.command.shelljoin}` in '#{self}' took #{tries} tries." if tries > 1
bom_paths = result.stdout.split("\0")
raise EmptyError, self if bom_paths.empty?
bom_paths
.reject { |path| Pathname(path).dmg_metadata? }
.reject { |path| (self/path).system_dir_symlink? }
.join("\n")
end
bom_paths
.reject { |path| dmg_metadata?(Pathname(path)) }
.reject { |path| system_dir_symlink?(pathname/path) }
.join("\n")
end
end
private_constant :Bom
# Strategy for unpacking a volume mounted from a disk image.
class Mount
extend T::Sig
using Bom
include UnpackStrategy
def eject(verbose: false)
tries ||= 3
tries = 3
begin
return unless path.exist?
return unless path.exist?
if tries > 1
disk_info = system_command!(
"diskutil",
args: ["info", "-plist", path],
print_stderr: false,
verbose: verbose,
)
if tries > 1
disk_info = system_command!(
"diskutil",
args: ["info", "-plist", path],
print_stderr: false,
verbose: verbose,
)
# For HFS, just use <mount-path>
# For APFS, find the <physical-store> corresponding to <mount-path>
eject_paths = disk_info.plist
.fetch("APFSPhysicalStores", [])
.map { |store| store["APFSPhysicalStore"] }
.compact
.presence || [path]
# For HFS, just use <mount-path>
# For APFS, find the <physical-store> corresponding to <mount-path>
eject_paths = disk_info.plist
.fetch("APFSPhysicalStores", [])
.map { |store| store["APFSPhysicalStore"] }
.compact
.presence || [path]
eject_paths.each do |eject_path|
eject_paths.each do |eject_path|
system_command! "diskutil",
args: ["eject", eject_path],
print_stderr: false,
verbose: verbose
end
else
system_command! "diskutil",
args: ["eject", eject_path],
args: ["unmount", "force", path],
print_stderr: false,
verbose: verbose
end
else
system_command! "diskutil",
args: ["unmount", "force", path],
print_stderr: false,
verbose: verbose
end
rescue ErrorDuringExecution => e
raise e if (tries -= 1).zero?
rescue ErrorDuringExecution => e
raise e if (tries -= 1).zero?
sleep 1
retry
sleep 1
retry
end
end
private
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
def extract_to_dir(unpack_dir, basename:, verbose:)
tries = 3
bom = begin
tries ||= 10
path.bom
Bom.bom(path)
rescue Bom::EmptyError => e
raise "#{e} No retries left." if (tries -= 1).zero?
raise e if (tries -= 1).zero?
sleep 1
retry
end
# TODO: Remove this if we actually ever hit this, i.e. if we actually found
# some files after waiting longer for the DMG to be mounted.
raise "BOM for path '#{path}' was empty but retrying for #{10 - tries} seconds helped." if tries != 10
Tempfile.open(["", ".bom"]) do |bomfile|
bomfile.close

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Executable < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".sh", ".bash"]

View File

@ -11,8 +11,6 @@ module UnpackStrategy
include UnpackStrategy
extend SystemCommand::Mixin
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking Git repositories.
class Git < Directory
using Magic
def self.can_extract?(path)
super && (path/".git").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".gz"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Jar < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".apk", ".jar"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".lha", ".lzh"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class LuaRock < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".rock"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".lz"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".lzma"]

View File

@ -6,8 +6,6 @@ require_relative "directory"
module UnpackStrategy
# Strategy for unpacking Mercurial repositories.
class Mercurial < Directory
using Magic
def self.can_extract?(path)
super && (path/".hg").directory?
end

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class MicrosoftOfficeXml < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Otf < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".otf"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".7z"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".pax"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
class Pkg < Uncompressed
extend T::Sig
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".pkg", ".mkpg"]

View File

@ -8,8 +8,6 @@ module UnpackStrategy
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".rar"]

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