Merge remote-tracking branch 'origin/master' into test-runners

This commit is contained in:
Carlo Cabrera 2023-04-05 23:55:01 +08:00
commit ffcc4cd75f
No known key found for this signature in database
GPG Key ID: C74D447FC549A1D0
92 changed files with 854 additions and 569 deletions

View File

@ -38,7 +38,7 @@ jobs:
restore-keys: ${{ runner.os }}-rubygems-
- name: Install Bundler RubyGems
run: brew install-bundler-gems --groups=all
run: brew install-bundler-gems --groups=sorbet
- name: Install shellcheck and shfmt
run: brew install shellcheck shfmt
@ -80,7 +80,7 @@ jobs:
restore-keys: ${{ runner.os }}-rubygems-
- name: Install Bundler RubyGems
run: brew install-bundler-gems --groups=all
run: brew install-bundler-gems --groups=sorbet
- name: Run brew style on homebrew-core
run: brew style --display-cop-names homebrew/core
@ -142,7 +142,7 @@ jobs:
restore-keys: ${{ runner.os }}-rubygems-
- name: Install Bundler RubyGems
run: brew install-bundler-gems --groups=all
run: brew install-bundler-gems --groups=sorbet
- name: Set up the homebrew/core tap
run: brew tap homebrew/core
@ -173,7 +173,7 @@ jobs:
restore-keys: ${{ runner.os }}-rubygems-
- name: Install Bundler RubyGems
run: brew install-bundler-gems --groups=all
run: brew install-bundler-gems --groups=sorbet
- name: Set up Homebrew all cask taps
run: |
@ -209,7 +209,7 @@ jobs:
# Can't cache this because we need to check that it doesn't fail the
# "uncommitted RubyGems" step with a cold cache.
- name: Install Bundler RubyGems
run: brew install-bundler-gems --groups=all
run: brew install-bundler-gems --groups=sorbet
- name: Check for uncommitted RubyGems
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
@ -318,7 +318,7 @@ jobs:
restore-keys: ${{ runner.os }}-rubygems-
- name: Install Bundler RubyGems
run: brew install-bundler-gems --groups=all
run: brew install-bundler-gems --groups=sorbet
- name: Create parallel test log directory
run: mkdir tests

View File

@ -27,8 +27,12 @@ gem "rspec-retry", require: false
gem "rspec-sorbet", require: false
gem "rubocop", require: false
gem "rubocop-ast", require: false
# NOTE: ruby-prof v1.4.3 is the last version that supports Ruby 2.6.x
# TODO: remove explicit version when HOMEBREW_REQUIRED_RUBY_VERSION >= 2.7
gem "ruby-prof", "1.4.3", require: false
gem "simplecov", require: false
gem "simplecov-cobertura", require: false
gem "stackprof", require: false
gem "warning", require: false
group :sorbet, optional: true do
@ -38,13 +42,6 @@ group :sorbet, optional: true do
gem "tapioca", require: false
end
group :prof, optional: true do
# NOTE: ruby-prof v1.4.3 is the last version that supports Ruby 2.6.x
# TODO: remove explicit version when HOMEBREW_REQUIRED_RUBY_VERSION >= 2.7
gem "ruby-prof", "1.4.3", require: false
gem "stackprof", require: false
end
# vendored gems
gem "activesupport"
gem "addressable"

View File

@ -7,7 +7,7 @@ GEM
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.1)
addressable (2.8.2)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
bindata (2.4.15)
@ -82,7 +82,7 @@ GEM
parser
rainbow (~> 3.0)
sorbet-runtime (>= 0.5)
parser (3.2.1.1)
parser (3.2.2.0)
ast (~> 2.4.1)
patchelf (1.4.0)
elftools (>= 1.2)

View File

@ -104,7 +104,7 @@ module Cask
message << "during #{section} " if section
message << "on Cask #{token}."
opoo "#{message}\n#{error_message_with_suggestions}"
ofail "#{message}\n#{error_message_with_suggestions}"
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -60,10 +60,10 @@ module Homebrew
odie "No #{name} #{pg_version_data}.* version installed!" unless old_bin
server_stopped = false
moved_data = false
initdb_run = false
upgraded = false
server_stopped = T.let(false, T::Boolean)
moved_data = T.let(false, T::Boolean)
initdb_run = T.let(false, T::Boolean)
upgraded = T.let(false, T::Boolean)
begin
# Following instructions from:
@ -88,7 +88,7 @@ module Homebrew
system "#{old_bin}/pg_ctl", "-w", "-D", datadir, "start"
end
initdb_args = []
initdb_args = T.let([], T::Array[String])
locale_settings = %w[
lc_collate
lc_ctype

View File

@ -32,6 +32,11 @@ module Commands
"lc" => "livecheck",
"tc" => "typecheck",
}.freeze
# This pattern is used to split descriptions at full stops. We only consider a
# dot as a full stop if it is either followed by a whitespace or at the end of
# the description. In this way we can prevent cutting off a sentence in the
# middle due to dots in URLs or paths.
DESCRIPTION_SPLITTING_PATTERN = /\.(?>\s|$)/.freeze
def valid_internal_cmd?(cmd)
require?(HOMEBREW_CMD_PATH/cmd)
@ -203,7 +208,7 @@ module Commands
if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path))
if short
cmd_parser.description.split(".").first
cmd_parser.description.split(DESCRIPTION_SPLITTING_PATTERN).first
else
cmd_parser.description
end
@ -215,7 +220,7 @@ module Commands
match_data = /^#: (?<desc>\w.*+)$/.match(line)
if match_data
desc = match_data[:desc]
return T.must(desc).split(".").first if short
return T.must(desc).split(DESCRIPTION_SPLITTING_PATTERN).first if short
return desc
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "mutex_m"
@ -47,7 +47,7 @@ module Debrew
menu = new
yield menu
choice = nil
choice = T.let(nil, T.nilable(Entry))
while choice.nil?
menu.entries.each_with_index { |e, i| puts "#{i + 1}. #{e.name}" }
print menu.prompt unless menu.prompt.nil?
@ -90,7 +90,7 @@ module Debrew
yield
rescue SystemExit
raise
rescue Exception => e # rubocop:disable Lint/RescueException
rescue Ignorable::ExceptionMixin => e
e.ignore if debug(e) == :ignore # execution jumps back to where the exception was thrown
ensure
Ignorable.unhook_raise
@ -99,7 +99,7 @@ module Debrew
end
def self.debug(exception)
raise(exception) if !active? || !debugged_exceptions.add?(exception) || !try_lock
raise(exception) if !active? || !debugged_exceptions.add?(exception) || !mu_try_lock
begin
puts exception.backtrace.first.to_s
@ -119,7 +119,7 @@ module Debrew
set_trace_func proc { |event, _, _, id, binding, klass|
if klass == Object && id == :raise && event == "return"
set_trace_func(nil)
synchronize { IRB.start_within(binding) }
mu_synchronize { IRB.start_within(binding) }
end
}
@ -134,7 +134,7 @@ module Debrew
end
end
ensure
unlock
mu_unlock
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "delegate"
@ -16,19 +16,19 @@ class Dependencies < SimpleDelegator
alias eql? ==
def optional
select(&:optional?)
__getobj__.select(&:optional?)
end
def recommended
select(&:recommended?)
__getobj__.select(&:recommended?)
end
def build
select(&:build?)
__getobj__.select(&:build?)
end
def required
select(&:required?)
__getobj__.select(&:required?)
end
def default
@ -37,7 +37,7 @@ class Dependencies < SimpleDelegator
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{to_a}>"
"#<#{self.class.name}: #{__getobj__}>"
end
end
@ -52,11 +52,11 @@ class Requirements < SimpleDelegator
end
def <<(other)
if other.is_a?(Comparable)
grep(other.class) do |req|
if other.is_a?(Object) && other.is_a?(Comparable)
__getobj__.grep(other.class) do |req|
return self if req > other
delete(req)
__getobj__.delete(req)
end
end
super
@ -65,6 +65,6 @@ class Requirements < SimpleDelegator
sig { returns(String) }
def inspect
"#<#{self.class.name}: {#{to_a.join(", ")}}>"
"#<#{self.class.name}: {#{__getobj__.to_a.join(", ")}}>"
end
end

View File

@ -2,6 +2,10 @@
class Dependencies < SimpleDelegator
include Kernel
# This is a workaround to enable `alias eql? ==`
# @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238
sig { params(arg0: BasicObject).returns(T::Boolean) }
def ==(arg0); end
end
class Requirements < SimpleDelegator

View File

@ -24,14 +24,9 @@ module Homebrew
def install_bundler_gems
args = install_bundler_gems_args.parse
groups = args.groups
# Clear previous settings. We want to fully replace - not append.
Homebrew::Settings.delete(:gemgroups) if groups
Homebrew::Settings.delete(:gemgroups) if args.groups
groups ||= []
groups |= VALID_GEM_GROUPS if groups.delete("all")
Homebrew.install_bundler_gems!(groups: groups)
Homebrew.install_bundler_gems!(groups: args.groups || [])
end
end

View File

@ -95,7 +95,7 @@ module Homebrew
.reject { |line| line.start_with?("#") || line.blank? }
.map(&:strip)
named_args = T.unsafe(CLI::NamedArgs).new(*names, parent: args)
named_args = CLI::NamedArgs.new(*names, parent: args)
named_args.to_formulae_and_casks(ignore_unavailable: true)
rescue Errno::ENOENT => e
onoe e

View File

@ -24,8 +24,6 @@ module Homebrew
def prof
args = prof_args.parse
Homebrew.install_bundler_gems!(groups: ["prof"])
brew_rb = (HOMEBREW_LIBRARY_PATH/"brew.rb").resolved_path
FileUtils.mkdir_p "prof"
cmd = args.named.first
@ -43,12 +41,16 @@ module Homebrew
end
if args.stackprof?
# Already installed from Gemfile but use this to setup PATH and LOADPATH
Homebrew.install_gem_setup_path! "stackprof"
with_env HOMEBREW_STACKPROF: "1" do
system(*HOMEBREW_RUBY_EXEC_ARGS, brew_rb, *args.named)
end
output_filename = "prof/d3-flamegraph.html"
safe_system "stackprof --d3-flamegraph prof/stackprof.dump > #{output_filename}"
else
# Already installed from Gemfile but use this to setup PATH and LOADPATH
Homebrew.install_gem_setup_path! "ruby-prof"
output_filename = "prof/call_stack.html"
safe_system "ruby-prof", "--printer=call_stack", "--file=#{output_filename}", brew_rb, "--", *args.named
end

View File

@ -81,7 +81,7 @@ module Homebrew
- name: Cache Homebrew Bundler RubyGems
id: cache
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 }}

View File

@ -88,7 +88,7 @@ module Homebrew
def tests
args = tests_args.parse
Homebrew.install_bundler_gems!(groups: ["prof"])
Homebrew.install_bundler_gems!
require "byebug" if args.byebug?

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -6,10 +6,8 @@ require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def typecheck_args
def self.typecheck_args
Homebrew::CLI::Parser.new do
description <<~EOS
Check for typechecking errors using Sorbet.
@ -44,7 +42,7 @@ module Homebrew
end
sig { void }
def typecheck
def self.typecheck
args = typecheck_args.parse
Homebrew.install_bundler_gems!(groups: ["sorbet"])
@ -95,9 +93,10 @@ module Homebrew
srb_exec += ["--ignore", args.ignore] if args.ignore.present?
if args.file.present? || args.dir.present?
cd("sorbet")
srb_exec += ["--file", "../#{args.file}"] if args.file
srb_exec += ["--dir", "../#{args.dir}"] if args.dir
cd("sorbet") do
srb_exec += ["--file", "../#{args.file}"] if args.file
srb_exec += ["--dir", "../#{args.dir}"] if args.dir
end
end
success = system(*srb_exec)
return if success

View File

@ -202,7 +202,7 @@ module Homebrew
when MacOSRequirement
next true unless r.version_specified?
macos_version.public_send(r.comparator, r.version)
macos_version.compare(r.comparator, r.version)
when XcodeRequirement
next true unless r.version

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "stringio"
@ -8,10 +8,8 @@ require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def unpack_args
def self.unpack_args
Homebrew::CLI::Parser.new do
description <<~EOS
Unpack the source files for <formula> into subdirectories of the current
@ -33,7 +31,7 @@ module Homebrew
end
end
def unpack
def self.unpack
args = unpack_args.parse
formulae = args.named.to_formulae
@ -69,10 +67,11 @@ module Homebrew
next unless args.git?
ohai "Setting up Git repository"
cd stage_dir
system "git", "init", "-q"
system "git", "add", "-A"
system "git", "commit", "-q", "-m", "brew-unpack"
cd(stage_dir) do
system "git", "init", "-q"
system "git", "add", "-A"
system "git", "commit", "-q", "-m", "brew-unpack"
end
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -40,7 +40,8 @@ module Homebrew
named_sponsors = []
logo_sponsors = []
largest_monthly_amount = 0
# FIXME: This T.let should be unnecessary https://github.com/sorbet/sorbet/issues/6894
largest_monthly_amount = T.let(0, T.untyped)
GitHub.sponsorships("Homebrew").each do |s|
largest_monthly_amount = [s[:monthly_amount], s[:closest_tier_monthly_amount]].max

View File

@ -54,7 +54,7 @@ module Homebrew
start_commit = T.let("", T.untyped)
end_commit = "HEAD"
cd HOMEBREW_REPOSITORY do
start_commit = if (commit = T.let(args.commit, T.nilable(String)))
start_commit = if (commit = args.commit)
commit
elsif (date = args.before)
Utils.popen_read("git", "rev-list", "-n1", "--before=#{date}", "origin/master").chomp

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -30,7 +30,7 @@ module Homebrew
Homebrew.install_bundler!
ENV["BUNDLE_WITH"] = VALID_GEM_GROUPS.join(":")
ENV["BUNDLE_WITH"] = "sorbet"
# System Ruby does not pick up the correct SDK by default.
ENV["SDKROOT"] = MacOS.sdk_path if ENV["HOMEBREW_MACOS_SYSTEM_RUBY_NEW_ENOUGH"]

View File

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

View File

@ -1,18 +1,16 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
extend T::Sig
module_function
class << self
alias generic_git_tags git_tags
end
def git_tags
tags = generic_git_tags
tags = Utils.popen_read("git tag --list | sort -rV") if tags.blank?
tags
def git_tags
tags = generic_git_tags
tags = Utils.popen_read("git tag --list | sort -rV") if tags.blank?
tags
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "tempfile"
@ -46,7 +46,7 @@ module Homebrew
f.write "#!/bin/sh\n"
f.chmod 0700
f.close
return if system f.path
return if system T.must(f.path)
<<~EOS
The directory #{HOMEBREW_TEMP} does not permit executing
@ -56,12 +56,12 @@ module Homebrew
echo 'export HOMEBREW_TEMP=~/tmp' >> #{shell_profile}
EOS
ensure
f.unlink
f&.unlink
end
def check_xdg_data_dirs
return if ENV["XDG_DATA_DIRS"].blank?
return if ENV["XDG_DATA_DIRS"].split("/").include?(HOMEBREW_PREFIX/"share")
xdg_data_dirs = ENV.fetch("XDG_DATA_DIRS", nil)
return if xdg_data_dirs.blank? || xdg_data_dirs.split("/").include?(HOMEBREW_PREFIX/"share")
<<~EOS
Homebrew's share was not found in your XDG_DATA_DIRS but you have

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Hardware
@ -111,7 +111,10 @@ module Hardware
end
%w[aes altivec avx avx2 lm ssse3 sse4_2].each do |flag|
define_method("#{flag}?") { flags.include? flag }
define_method("#{flag}?") do
T.bind(self, T.class_of(Hardware::CPU))
flags.include? flag
end
end
def sse3?

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -379,7 +379,7 @@ module Homebrew
real_tmp = tmp.realpath.parent
where_tmp = volumes.which real_tmp
ensure
Dir.delete tmp
Dir.delete tmp.to_s
end
rescue
return

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "macho"
@ -114,7 +114,7 @@ module Hardware
end
end
def intel_family
def intel_family(_family = nil, _cpu_model = nil)
case sysctl_int("hw.cpufamily")
when 0x73d67300 # Yonah: Core Solo/Duo
:core

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
class Keg
@ -108,7 +108,7 @@ class Keg
# Needed to make symlink permissions consistent on macOS and Linux for
# reproducible bottles.
def consistent_reproducible_symlink_permissions!
find do |file|
path.find do |file|
File.lchmod 0777, file if file.symlink?
end
end

View File

@ -19,7 +19,7 @@ module Readall
# Fine to have missing URLs for unsupported macOS
macos_req = cask.depends_on.macos
next if macos_req&.version && Array(macos_req.version).none? do |macos_version|
current_macos_version.public_send(macos_req.comparator, macos_version)
current_macos_version.compare(macos_req.comparator, macos_version)
end
raise "Missing URL" if cask.url.nil?

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Predicable

View File

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

View File

@ -1,16 +1,17 @@
# typed: false
# typed: true
# frozen_string_literal: true
module TimeRemaining
refine Time do
def remaining
T.bind(self, Time)
[0, self - Time.now].max
end
def remaining!
r = remaining
raise Timeout::Error if r <= 0
Kernel.raise Timeout::Error if r <= 0
r
end

View File

@ -1152,7 +1152,7 @@ class Formula
ENV.activate_extensions!
etc_var_dirs = [bottle_prefix/"etc", bottle_prefix/"var"]
T.unsafe(Find).find(*etc_var_dirs.select(&:directory?)) do |path|
Find.find(*etc_var_dirs.select(&:directory?)) do |path|
path = Pathname.new(path)
path.extend(InstallRenamed)
path.cp_path_sub(bottle_prefix, HOMEBREW_PREFIX)
@ -2672,7 +2672,7 @@ class Formula
out.close
args.map!(&:to_s)
begin
T.unsafe(Kernel).exec(cmd, *args)
Kernel.exec(cmd, *args)
rescue
nil
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "warnings"
@ -30,7 +30,7 @@ module Ignorable
rescue Exception => e # rubocop:disable Lint/RescueException
unless e.is_a?(ScriptError)
e.extend(ExceptionMixin)
e.continuation = continuation
T.cast(e, ExceptionMixin).continuation = continuation
end
super(e)
end
@ -47,7 +47,6 @@ module Ignorable
def self.unhook_raise
Object.class_eval do
# False positive - https://github.com/rubocop/rubocop/issues/5022
alias_method :raise, :original_raise
alias_method :fail, :original_raise
undef :original_raise

View File

@ -0,0 +1,8 @@
# typed: strict
module Ignorable
include Kernel
# This is a workaround to enable `raise` to be aliased
# @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238
def self.raise(*); end
end

View File

@ -174,15 +174,15 @@ module Homebrew
The currently linked version is: #{formula.linked_version}
EOS
end
elsif only_dependencies
msg = nil
return true
elsif !formula.linked? || formula.keg_only?
msg = <<~EOS
#{msg}, it's just not linked.
To link this version, run:
brew link #{formula}
EOS
elsif only_dependencies
msg = nil
return true
else
msg = if quiet
nil

View File

@ -264,14 +264,14 @@ module Language
# Multiline strings are allowed and treated as though they represent
# the contents of a `requirements.txt`.
# @return [void]
def pip_install(targets)
def pip_install(targets, build_isolation: true)
targets = Array(targets)
targets.each do |t|
if t.respond_to? :stage
t.stage { do_install Pathname.pwd }
t.stage { do_install(Pathname.pwd, build_isolation: build_isolation) }
else
t = t.lines.map(&:strip) if t.respond_to?(:lines) && t.include?("\n")
do_install t
do_install(t, build_isolation: build_isolation)
end
end
end
@ -281,11 +281,11 @@ module Language
#
# @param (see #pip_install)
# @return (see #pip_install)
def pip_install_and_link(targets, link_manpages: false)
def pip_install_and_link(targets, link_manpages: false, build_isolation: true)
bin_before = Dir[@venv_root/"bin/*"].to_set
man_before = Dir[@venv_root/"share/man/man*/*"].to_set if link_manpages
pip_install(targets)
pip_install(targets, build_isolation: build_isolation)
bin_after = Dir[@venv_root/"bin/*"].to_set
bin_to_link = (bin_after - bin_before).to_a
@ -301,12 +301,15 @@ module Language
private
def do_install(targets)
def do_install(targets, build_isolation: true)
targets = Array(targets)
@formula.system @venv_root/"bin/pip", "install",
"-v", "--no-deps", "--no-binary", ":all:",
"--use-feature=no-binary-enable-wheel-cache",
"--ignore-installed", *targets
args = [
"-v", "--no-deps", "--no-binary", ":all:",
"--use-feature=no-binary-enable-wheel-cache",
"--ignore-installed"
]
args << "--no-build-isolation" unless build_isolation
@formula.system @venv_root/"bin/pip", "install", *args, *targets
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Homebrew
@ -161,7 +161,7 @@ module Homebrew
# specifies the strategy and contains a `strategy` block
next if (livecheck_strategy != strategy_symbol) || !block_provided
elsif strategy.const_defined?(:PRIORITY) &&
!strategy::PRIORITY.positive? &&
!strategy.const_get(:PRIORITY).positive? &&
livecheck_strategy != strategy_symbol
# Ignore strategies with a priority of 0 or lower, unless the
# strategy is specified in the `livecheck` block
@ -174,7 +174,7 @@ module Homebrew
# Sort usable strategies in descending order by priority, using the
# DEFAULT_PRIORITY when a strategy doesn't contain a PRIORITY constant
usable_strategies.sort_by do |strategy|
(strategy.const_defined?(:PRIORITY) ? -strategy::PRIORITY : -DEFAULT_PRIORITY)
(strategy.const_defined?(:PRIORITY) ? -strategy.const_get(:PRIORITY) : -DEFAULT_PRIORITY)
end
end
@ -216,7 +216,7 @@ module Homebrew
# @return [Hash]
sig { params(url: String, homebrew_curl: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
def self.page_content(url, homebrew_curl: false)
stderr = nil
stderr = T.let(nil, T.nilable(String))
[:default, :browser].each do |user_agent|
stdout, stderr, status = curl_with_workarounds(
*PAGE_CONTENT_CURL_ARGS, url,

View File

@ -93,13 +93,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -96,13 +96,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -80,13 +80,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -45,7 +45,7 @@ module Homebrew
regex: T.nilable(Regexp),
provided_content: T.nilable(String),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, provided_content: nil, **unused, &block)
@ -54,7 +54,7 @@ module Homebrew
"#{Utils.demodulize(T.must(name))} only supports a regex when using a `strategy` block"
end
T.unsafe(Yaml).find_versions(
Yaml.find_versions(
url: url,
regex: regex,
provided_content: provided_content,

View File

@ -64,7 +64,7 @@ module Homebrew
params(
items: T::Hash[String, Item],
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_items(items, regex = nil, &block)
@ -92,7 +92,7 @@ module Homebrew
url: T.nilable(String),
regex: T.nilable(Regexp),
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(cask:, url: nil, regex: nil, **_unused, &block)

View File

@ -85,7 +85,7 @@ module Homebrew
params(
tags: T::Array[String],
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_tags(tags, regex = nil, &block)
@ -125,7 +125,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **_unused, &block)

View File

@ -92,13 +92,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || DEFAULT_REGEX, **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || DEFAULT_REGEX, **unused, &block)
end
end
end

View File

@ -82,13 +82,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
version_data = T.unsafe(PageMatch).find_versions(
version_data = PageMatch.find_versions(
url: generated[:url],
regex: regex || generated[:regex],
**unused,

View File

@ -92,13 +92,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -78,13 +78,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -44,7 +44,7 @@ module Homebrew
params(
headers: T::Hash[String, String],
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_headers(headers, regex = nil, &block)
@ -79,7 +79,7 @@ module Homebrew
regex: T.nilable(Regexp),
homebrew_curl: T::Boolean,
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, homebrew_curl: false, **_unused, &block)

View File

@ -70,7 +70,7 @@ module Homebrew
params(
content: String,
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_content(content, regex = nil, &block)
@ -105,7 +105,7 @@ module Homebrew
provided_content: T.nilable(String),
homebrew_curl: T::Boolean,
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, provided_content: nil, homebrew_curl: false, **_unused, &block)

View File

@ -75,13 +75,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || DEFAULT_REGEX, **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || DEFAULT_REGEX, **unused, &block)
end
end
end

View File

@ -73,13 +73,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -51,7 +51,7 @@ module Homebrew
params(
content: String,
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_content(content, regex, &block)
@ -88,7 +88,7 @@ module Homebrew
provided_content: T.nilable(String),
homebrew_curl: T::Boolean,
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, provided_content: nil, homebrew_curl: false, **_unused, &block)

View File

@ -87,13 +87,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
PageMatch.find_versions(url: generated[:url], regex: regex || generated[:regex], **unused, &block)
end
end
end

View File

@ -92,13 +92,13 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
generated = generate_input_values(url)
T.unsafe(PageMatch).find_versions(
PageMatch.find_versions(
url: generated[:url] || url,
regex: regex || generated[:regex],
**unused,

View File

@ -146,7 +146,7 @@ module Homebrew
params(
content: String,
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_content(content, regex = nil, &block)
@ -181,7 +181,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **_unused, &block)

View File

@ -87,7 +87,7 @@ module Homebrew
params(
content: String,
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_content(content, regex = nil, &block)
@ -123,7 +123,7 @@ module Homebrew
provided_content: T.nilable(String),
homebrew_curl: T::Boolean,
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, provided_content: nil, homebrew_curl: false, **_unused, &block)

View File

@ -114,7 +114,7 @@ module Homebrew
url: String,
regex: T.nilable(Regexp),
unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, **unused, &block)
@ -123,7 +123,7 @@ module Homebrew
# Use the cached page content to avoid duplicate fetches
cached_content = @page_data[generated_url]
match_data = T.unsafe(PageMatch).find_versions(
match_data = PageMatch.find_versions(
url: generated_url,
regex: regex || generated[:regex],
provided_content: cached_content,

View File

@ -70,7 +70,7 @@ module Homebrew
params(
content: String,
regex: T.nilable(Regexp),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Array[String])
}
def self.versions_from_content(content, regex = nil, &block)
@ -105,7 +105,7 @@ module Homebrew
provided_content: T.nilable(String),
homebrew_curl: T::Boolean,
_unused: T.nilable(T::Hash[Symbol, T.untyped]),
block: T.untyped,
block: T.nilable(Proc),
).returns(T::Hash[Symbol, T.untyped])
}
def self.find_versions(url:, regex: nil, provided_content: nil, homebrew_curl: false, **_unused, &block)

View File

@ -56,7 +56,7 @@ class MacOSRequirement < Requirement
satisfy(build_env: false) do
T.bind(self, MacOSRequirement)
next Array(@version).any? { |v| OS::Mac.version.public_send(@comparator, v) } if OS.mac? && version_specified?
next Array(@version).any? { |v| OS::Mac.version.compare(@comparator, v) } if OS.mac? && version_specified?
next true if OS.mac?
next true if @version

View File

@ -24,6 +24,7 @@ require_relative "lines"
require_relative "livecheck"
require_relative "options"
require_relative "patches"
require_relative "service"
require_relative "text"
require_relative "urls"
require_relative "uses_from_macos"

View File

@ -8,13 +8,13 @@ module RuboCop
#
# @example
# # bad
# url "https://example.com/foo.dmg",
# verified: "https://example.com"
# url "https://example.com/download/foo.dmg",
# verified: "https://example.com/download"
#
#
# # good
# url "https://example.com/foo.dmg",
# verified: "example.com"
# url "https://example.com/download/foo.dmg",
# verified: "example.com/download/"
#
class Url < Base
extend AutoCorrector
@ -24,19 +24,35 @@ module RuboCop
def on_url_stanza(stanza)
return if stanza.stanza_node.block_type?
url_stanza = stanza.stanza_node.first_argument
hash_node = stanza.stanza_node.last_argument
return unless hash_node.hash_type?
hash_node.each_pair do |key_node, value_node|
next unless key_node.source == "verified"
next unless value_node.str_type?
next unless value_node.source.start_with?(%r{^"https?://})
if value_node.source.start_with?(%r{^"https?://})
add_offense(
value_node.source_range,
message: "Verified URL parameter value should not contain a URL scheme.",
) do |corrector|
corrector.replace(value_node.source_range, value_node.source.gsub(%r{^"https?://}, "\""))
end
end
# Skip if the URL and the verified value are the same.
next if value_node.source == url_stanza.source.gsub(%r{^"https?://}, "\"")
# Skip if the URL has two path components, eg: `https://github.com/google/fonts.git`.
next if url_stanza.source.gsub(%r{^"https?://}, "\"").count("/") == 2
# Skip if the verified value ends with a slash.
next if value_node.str_content.end_with?("/")
add_offense(
value_node.source_range,
message: "Verified URL parameter value should not start with https:// or http://.",
message: "Verified URL parameter value should end with a /.",
) do |corrector|
corrector.replace(value_node.source_range, value_node.source.gsub(%r{^"https?://}, "\""))
corrector.replace(value_node.source_range, value_node.source.gsub(/"$/, "/\""))
end
end
end

View File

@ -0,0 +1,31 @@
# typed: true
# frozen_string_literal: true
require "rubocops/extend/formula_cop"
module RuboCop
module Cop
module FormulaAudit
# This cop audits the service block.
#
# @api private
class Service < FormulaCop
extend AutoCorrector
def audit_formula(_node, _class_node, _parent_class_node, body_node)
service_node = find_block(body_node, :service)
return if service_node.blank?
# This check ensures that `bin` is not referenced because
# `opt_bin` is more portable and works with the API.
find_every_method_call_by_name(service_node, :bin).each do |bin_node|
offending_node(bin_node)
problem "Use `opt_bin` instead of `bin` in service blocks." do |corrector|
corrector.replace(bin_node.source_range, "opt_bin")
end
end
end
end
end
end
end

View File

@ -101,7 +101,7 @@ class Sandbox
begin
command = [SANDBOX_EXEC, "-f", seatbelt.path, *args]
# Start sandbox in a pseudoterminal to prevent access of the parent terminal.
T.unsafe(PTY).spawn(*command) do |r, w, pid|
PTY.spawn(*command) do |r, w, pid|
# Set the PTY's window size to match the parent terminal.
# Some formula tests are sensitive to the terminal size and fail if this is not set.
winch = proc do |_sig|

View File

@ -46,8 +46,11 @@ module Homebrew
}
def run(command = nil, macos: nil, linux: nil)
# Save parameters for serialization
@run_params ||= command
@run_params ||= { macos: macos, linux: linux }.compact
if command
@run_params = command
elsif macos || linux
@run_params = { macos: macos, linux: linux }.compact
end
command ||= on_system_conditional(macos: macos, linux: linux)
case T.unsafe(command)
@ -551,15 +554,22 @@ module Homebrew
hash = {}
hash[:run] =
case api_hash["run"]
when Hash
api_hash["run"].to_h do |key, array|
[
key.to_sym,
array.map(&method(:replace_placeholders)),
]
end
when String
replace_placeholders(api_hash["run"])
when Array
api_hash["run"].map(&method(:replace_placeholders))
when Hash
api_hash["run"].to_h do |key, elem|
run_cmd = if elem.is_a?(Array)
elem.map(&method(:replace_placeholders))
else
replace_placeholders(elem)
end
[key.to_sym, run_cmd]
end
else
raise ArgumentError, "Unexpected run command: #{api_hash["run"]}"
end
hash[:keep_alive] = api_hash["keep_alive"].transform_keys(&:to_sym) if api_hash.key?("keep_alive")

View File

@ -10,13 +10,9 @@ module Addressable::IDNA
class << self
def to_ascii(input); end
def to_unicode(input); end
def unicode_normalize_kc(input); end
private
def lookup_unicode_combining_class(codepoint); end
def lookup_unicode_compatibility(codepoint); end
def lookup_unicode_composition(unpacked); end
def lookup_unicode_lowercase(codepoint); end
def punycode_adapt(delta, numpoints, firsttime); end
def punycode_basic?(codepoint); end
@ -25,28 +21,13 @@ module Addressable::IDNA
def punycode_delimiter?(codepoint); end
def punycode_encode(unicode); end
def punycode_encode_digit(d); end
def ucs4_to_utf8(char, buffer); end
def unicode_compose(unpacked); end
def unicode_compose_pair(ch_one, ch_two); end
def unicode_decompose(unpacked); end
def unicode_decompose_hangul(codepoint); end
def unicode_downcase(input); end
def unicode_sort_canonical(unpacked); end
end
end
Addressable::IDNA::ACE_MAX_LENGTH = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::ACE_PREFIX = T.let(T.unsafe(nil), String)
Addressable::IDNA::COMPOSITION_TABLE = T.let(T.unsafe(nil), Hash)
Addressable::IDNA::HANGUL_LBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_LCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_NCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_SBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_SCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_TBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_TCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_VBASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::HANGUL_VCOUNT = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_BASE = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_DAMP = T.let(T.unsafe(nil), Integer)
Addressable::IDNA::PUNYCODE_DELIMITER = T.let(T.unsafe(nil), Integer)
@ -226,6 +207,10 @@ class Addressable::URI
def split_path(path); end
def validate; end
private
def reset_ivs; end
class << self
def convert_path(path); end
def encode(uri, return_type = T.unsafe(nil)); end
@ -265,6 +250,7 @@ Addressable::URI::CharacterClasses::SUB_DELIMS = T.let(T.unsafe(nil), String)
Addressable::URI::CharacterClasses::UNRESERVED = T.let(T.unsafe(nil), String)
Addressable::URI::EMPTY_STR = T.let(T.unsafe(nil), String)
class Addressable::URI::InvalidURIError < ::StandardError; end
Addressable::URI::NONE = T.let(T.unsafe(nil), Object)
Addressable::URI::NORMPATH = T.let(T.unsafe(nil), Regexp)
module Addressable::URI::NormalizeCharacterClasses; end
Addressable::URI::NormalizeCharacterClasses::FRAGMENT = T.let(T.unsafe(nil), Regexp)

View File

@ -5018,8 +5018,6 @@ end
class Net::HTTPAlreadyReported
end
Net::HTTPClientError::EXCEPTION_TYPE = Net::HTTPServerException
Net::HTTPClientErrorCode = Net::HTTPClientError
class Net::HTTPEarlyHints
@ -5031,9 +5029,13 @@ end
Net::HTTPFatalErrorCode = Net::HTTPClientError
Net::HTTPInformation::EXCEPTION_TYPE = Net::HTTPError
class Net::HTTPInformation
end
Net::HTTPInformationCode = Net::HTTPInformation
Net::HTTPInformationCode::EXCEPTION_TYPE = Net::HTTPError
class Net::HTTPInformation
end
class Net::HTTPLoopDetected
HAS_BODY = ::T.let(nil, ::T.untyped)
@ -5081,8 +5083,6 @@ end
class Net::HTTPRangeNotSatisfiable
end
Net::HTTPRedirection::EXCEPTION_TYPE = Net::HTTPRetriableError
Net::HTTPRedirectionCode = Net::HTTPRedirection
Net::HTTPRequestURITooLarge = Net::HTTPURITooLong
@ -5091,15 +5091,17 @@ Net::HTTPResponceReceiver = Net::HTTPResponse
Net::HTTPRetriableCode = Net::HTTPRedirection
Net::HTTPServerError::EXCEPTION_TYPE = Net::HTTPFatalError
Net::HTTPServerErrorCode = Net::HTTPServerError
Net::HTTPSession = Net::HTTP
Net::HTTPSuccess::EXCEPTION_TYPE = Net::HTTPError
class Net::HTTPSuccess
end
Net::HTTPSuccessCode = Net::HTTPSuccess
Net::HTTPSuccessCode::EXCEPTION_TYPE = Net::HTTPError
class Net::HTTPSuccess
end
class Net::HTTPURITooLong
HAS_BODY = ::T.let(nil, ::T.untyped)

View File

@ -0,0 +1,48 @@
# typed: strict
# This file contains temporary definitions for fixes that have
# been submitted upstream to https://github.com/sorbet/sorbet.
module FileUtils
# @see https://github.com/sorbet/sorbet/pull/6847
sig do
params(
src: T.any(String, Pathname),
dest: T.any(String, Pathname),
preserve: T.nilable(T::Boolean),
noop: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
dereference_root: T::Boolean,
remove_destination: T.nilable(T::Boolean)
).returns(T::Array[String])
end
module_function def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil, dereference_root: true, remove_destination: nil); end
end
module Kernel
# @see https://github.com/sorbet/sorbet/blob/a1e8389/rbi/core/kernel.rbi#L41-L46
sig do
type_parameters(:U).params(
block: T.proc.params(cont: Continuation).returns(T.type_parameter(:U))
).returns(T.type_parameter(:U))
end
def callcc(&block); end
# @see https://github.com/sorbet/sorbet/blob/a1e8389/rbi/core/kernel.rbi#L2348-L2363
sig do
params(
arg0: T.nilable(
T.proc.params(
event: String,
file: String,
line: Integer,
id: T.nilable(Symbol),
binding: T.nilable(Binding),
classname: Object,
).returns(T.untyped)
)
).void
end
sig { params(arg0: NilClass).returns(NilClass) }
def set_trace_func(arg0); end
end

View File

@ -199,8 +199,8 @@ class SystemCommand
pid = T.let(nil, T.nilable(Integer))
raw_stdin, raw_stdout, raw_stderr, raw_wait_thr = ignore_interrupts do
T.unsafe(Open3).popen3(env, [executable, executable], *args, **options)
.tap { |*, wait_thr| pid = wait_thr.pid }
Open3.popen3(env, [executable, executable], *args, **options)
.tap { |*, wait_thr| pid = wait_thr.pid }
end
write_input_to(raw_stdin)

View File

@ -870,7 +870,7 @@ class CoreTap < Tap
sig { returns(String) }
def remote
super if installed? || Homebrew::EnvConfig.no_install_from_api?
super if Homebrew::EnvConfig.no_install_from_api?
Homebrew::EnvConfig.core_git_remote
end
@ -988,7 +988,7 @@ class CoreTap < Tap
# @private
sig { returns(T::Array[String]) }
def aliases
return super if installed? || Homebrew::EnvConfig.no_install_from_api?
return super if Homebrew::EnvConfig.no_install_from_api?
Homebrew::API::Formula.all_aliases.keys
end
@ -996,7 +996,7 @@ class CoreTap < Tap
# @private
sig { returns(T::Array[String]) }
def formula_names
return super if installed? || Homebrew::EnvConfig.no_install_from_api?
return super if Homebrew::EnvConfig.no_install_from_api?
Homebrew::API::Formula.all_formulae.keys
end

View File

@ -20,10 +20,10 @@ describe Cask::DSL, :cask do
end
end
it "prints a warning that it has encountered an unexpected method" do
it "prints an error that it has encountered an unexpected method" do
expected = Regexp.compile(<<~EOS.lines.map(&:chomp).join)
(?m)
Warning:
Error:
.*
Unexpected method 'future_feature' called on Cask unexpected-method-cask\\.
.*

View File

@ -67,6 +67,14 @@ describe Language::Python::Virtualenv::Virtualenv, :needs_python do
virtualenv.pip_install res
end
it "works without build isolation" do
expect(formula).to receive(:system)
.with(dir/"bin/pip", "install", "-v", "--no-deps", "--no-binary", ":all:",
"--use-feature=no-binary-enable-wheel-cache", "--ignore-installed", "--no-build-isolation", "foo")
.and_return(true)
virtualenv.pip_install("foo", build_isolation: false)
end
end
describe "#pip_install_and_link" do
@ -86,7 +94,7 @@ describe Language::Python::Virtualenv::Virtualenv, :needs_python do
FileUtils.touch src_bin/"kilroy"
bin_after = Dir.glob(src_bin/"*")
expect(virtualenv).to receive(:pip_install).with("foo")
expect(virtualenv).to receive(:pip_install).with("foo", { build_isolation: true })
expect(Dir).to receive(:[]).with(src_bin/"*").twice.and_return(bin_before, bin_after)
virtualenv.pip_install_and_link "foo"
@ -115,7 +123,7 @@ describe Language::Python::Virtualenv::Virtualenv, :needs_python do
FileUtils.touch src_man/"man5/kilroy.5"
man_after = Dir.glob(src_man/"**/*")
expect(virtualenv).to receive(:pip_install).with("foo")
expect(virtualenv).to receive(:pip_install).with("foo", { build_isolation: true })
expect(Dir).to receive(:[]).with(src_bin/"*").and_return([])
expect(Dir).to receive(:[]).with(src_man/"man*/*").and_return(man_before)
expect(Dir).to receive(:[]).with(src_bin/"*").and_return([])

View File

@ -14,7 +14,7 @@ describe RuboCop::Cop::Cask::Url do
<<~CASK
cask "foo" do
url "https://example.com/download/foo-v1.2.0.dmg",
verified: "example.com"
verified: "example.com/download/"
end
CASK
end
@ -27,18 +27,18 @@ describe RuboCop::Cop::Cask::Url do
<<~CASK
cask "foo" do
url "https://example.com/download/foo-v1.2.0.dmg",
verified: "https://example.com"
verified: "https://example.com/download/"
end
CASK
end
let(:expected_offenses) do
[{
message: "Verified URL parameter value should not start with https:// or http://.",
message: "Verified URL parameter value should not contain a URL scheme.",
severity: :convention,
line: 3,
column: 14,
source: "\"https://example.com\"",
column: 16,
source: "\"https://example.com/download/\"",
}]
end
@ -46,7 +46,189 @@ describe RuboCop::Cop::Cask::Url do
<<~CASK
cask "foo" do
url "https://example.com/download/foo-v1.2.0.dmg",
verified: "example.com"
verified: "example.com/download/"
end
CASK
end
include_examples "reports offenses"
include_examples "autocorrects source"
end
context "when url 'verified' value does not have a path component" do
context "when the URL ends with a slash" do
let(:source) do
<<~CASK
cask "foo" do
url "https://example.org/",
verified: "example.org/"
end
CASK
end
include_examples "does not report any offenses"
end
context "when the URL does not end with a slash" do
let(:source) do
<<~CASK
cask "foo" do
url "https://example.org/",
verified: "example.org"
end
CASK
end
let(:expected_offenses) do
[{
message: "Verified URL parameter value should end with a /.",
severity: :convention,
line: 3,
column: 16,
source: "\"example.org\"",
}]
end
let(:correct_source) do
<<~CASK
cask "foo" do
url "https://example.org/",
verified: "example.org/"
end
CASK
end
include_examples "reports offenses"
include_examples "autocorrects source"
end
end
context "when the URL does not end with a slash" do
describe "and it has one path component" do
let(:source) do
<<~CASK
cask "foo" do
url "https://github.com/Foo",
verified: "github.com/Foo"
end
CASK
end
include_examples "does not report any offenses"
end
describe "and it has two path components" do
let(:source) do
<<~CASK
cask "foo" do
url "https://github.com/foo/foo.git",
verified: "github.com/foo/foo"
end
CASK
end
include_examples "does not report any offenses"
end
end
context "when the url ends with a / and the verified value does too" do
let(:source) do
<<~CASK
cask "foo" do
url "https://github.com/",
verified: "github.com/"
end
CASK
end
include_examples "does not report any offenses"
end
context "when the url ends with a / and the verified value does not" do
let(:source) do
<<~CASK
cask "foo" do
url "https://github.com/",
verified: "github.com"
end
CASK
end
let(:expected_offenses) do
[{
message: "Verified URL parameter value should end with a /.",
severity: :convention,
line: 3,
column: 16,
source: "\"github.com\"",
}]
end
let(:correct_source) do
<<~CASK
cask "foo" do
url "https://github.com/",
verified: "github.com/"
end
CASK
end
include_examples "reports offenses"
include_examples "autocorrects source"
end
context "when url 'verified' value has a path component that ends with a /" do
let(:source) do
<<~CASK
cask "foo" do
url "https://github.com/Foo/foo/releases/download/v1.2.0/foo-v1.2.0.dmg",
verified: "github.com/Foo/foo/"
end
CASK
end
include_examples "does not report any offenses"
end
context "when the url has interpolation in it and the verified url ends with a /" do
let(:source) do
<<~CASK
cask "foo" do
version "1.2.3"
url "https://example.com/download/foo-v\#{version}.dmg",
verified: "example.com/download/"
end
CASK
end
include_examples "does not report any offenses"
end
context "when the url 'verified' value has a path component that doesn't end with a /" do
let(:source) do
<<~CASK
cask "foo" do
url "https://github.com/Foo/foo/releases/download/v1.2.0/foo-v1.2.0.dmg",
verified: "github.com/Foo/foo"
end
CASK
end
let(:expected_offenses) do
[{
message: "Verified URL parameter value should end with a /.",
severity: :convention,
line: 3,
column: 16,
source: "\"github.com/Foo/foo\"",
}]
end
let(:correct_source) do
<<~CASK
cask "foo" do
url "https://github.com/Foo/foo/releases/download/v1.2.0/foo-v1.2.0.dmg",
verified: "github.com/Foo/foo/"
end
CASK
end

View File

@ -0,0 +1,43 @@
# typed: false
# frozen_string_literal: true
require "rubocops/service"
describe RuboCop::Cop::FormulaAudit::Service do
subject(:cop) { described_class.new }
it "reports an offense when a formula's service block uses `bin`" do
expect_offense(<<~RUBY)
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
service do
run [bin/"foo", "run", "-config", etc/"foo/config.json"]
^^^ Use `opt_bin` instead of `bin` in service blocks.
end
end
RUBY
expect_correction(<<~RUBY)
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
service do
run [opt_bin/"foo", "run", "-config", etc/"foo/config.json"]
end
end
RUBY
end
it "reports no offenses when a formula's service block uses `opt_bin`" do
expect_no_offenses(<<~RUBY)
class Bin < Formula
url "https://brew.sh/foo-1.0.tgz"
service do
run [opt_bin/"bin", "run", "-config", etc/"bin/config.json"]
end
end
RUBY
end
end

View File

@ -975,5 +975,37 @@ describe Homebrew::Service do
it "replaces placeholders with local paths" do
expect(described_class.deserialize(serialized_hash)).to eq(deserialized_hash)
end
describe "run command" do
it "handles String argument correctly" do
expect(described_class.deserialize({
"run" => "$HOMEBREW_PREFIX/opt/formula_name/bin/beanstalkd",
})).to eq({
run: "#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd",
})
end
it "handles Array argument correctly" do
expect(described_class.deserialize({
"run" => ["$HOMEBREW_PREFIX/opt/formula_name/bin/beanstalkd", "--option"],
})).to eq({
run: ["#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd", "--option"],
})
end
it "handles Hash argument correctly" do
expect(described_class.deserialize({
"run" => {
"linux" => "$HOMEBREW_PREFIX/opt/formula_name/bin/beanstalkd",
"macos" => ["$HOMEBREW_PREFIX/opt/formula_name/bin/beanstalkd", "--option"],
},
})).to eq({
run: {
linux: "#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd",
macos: ["#{HOMEBREW_PREFIX}/opt/formula_name/bin/beanstalkd", "--option"],
},
})
end
end
end
end

View File

@ -93,6 +93,26 @@ describe Version do
expect(described_class.create("1.2.3")).to be < described_class.create("1.2.3-p34")
end
specify "compare" do
expect(described_class.create("0.1").compare("==", described_class.create("0.1.0"))).to be true
expect(described_class.create("0.1").compare("<", described_class.create("0.2"))).to be true
expect(described_class.create("1.2.3").compare(">", described_class.create("1.2.2"))).to be true
expect(described_class.create("1.2.4").compare("<", described_class.create("1.2.4.1"))).to be true
expect(described_class.create("0.1").compare("!=", described_class.create("0.1.0"))).to be false
expect(described_class.create("0.1").compare(">=", described_class.create("0.2"))).to be false
expect(described_class.create("1.2.3").compare("<=", described_class.create("1.2.2"))).to be false
expect(described_class.create("1.2.4").compare(">=", described_class.create("1.2.4.1"))).to be false
expect(described_class.create("1.2.3").compare(">", described_class.create("1.2.3alpha4"))).to be true
expect(described_class.create("1.2.3").compare(">", described_class.create("1.2.3beta2"))).to be true
expect(described_class.create("1.2.3").compare(">", described_class.create("1.2.3rc3"))).to be true
expect(described_class.create("1.2.3").compare("<", described_class.create("1.2.3-p34"))).to be true
expect(described_class.create("1.2.3").compare("<=", described_class.create("1.2.3alpha4"))).to be false
expect(described_class.create("1.2.3").compare("<=", described_class.create("1.2.3beta2"))).to be false
expect(described_class.create("1.2.3").compare("<=", described_class.create("1.2.3rc3"))).to be false
expect(described_class.create("1.2.3").compare(">=", described_class.create("1.2.3-p34"))).to be false
end
specify "HEAD" do
expect(described_class.create("HEAD")).to be > described_class.create("1.2.3")
expect(described_class.create("HEAD-abcdef")).to be > described_class.create("1.2.3")

View File

@ -41,6 +41,16 @@ module Homebrew
@qlplugins ||= @cask.artifacts.select { |a| a.is_a?(Cask::Artifact::Qlplugin) }
end
sig { returns(T::Array[Cask::Artifact::Dictionary]) }
def dictionaries
@dictionaries ||= @cask.artifacts.select { |a| a.is_a?(Cask::Artifact::Dictionary) }
end
sig { returns(T::Array[Cask::Artifact::ScreenSaver]) }
def screen_savers
@screen_savers ||= @cask.artifacts.select { |a| a.is_a?(Cask::Artifact::ScreenSaver) }
end
sig { returns(T::Array[Cask::Artifact::Colorpicker]) }
def colorpickers
@colorpickers ||= @cask.artifacts.select { |a| a.is_a?(Cask::Artifact::Colorpicker) }
@ -113,11 +123,24 @@ module Homebrew
*keyboard_layouts,
*mdimporters,
*colorpickers,
*dictionaries,
*qlplugins,
*installers,
*screen_savers,
].flat_map do |artifact|
source = artifact.is_a?(Cask::Artifact::Installer) ? artifact.path : artifact.source.basename
top_level_info_plists(Pathname.glob(dir/"**"/source/"Contents"/"Info.plist")).sort
sources = if artifact.is_a?(Cask::Artifact::Installer)
# Installers are sometimes contained within an `.app`, so try both.
installer_path = artifact.path
installer_path.ascend
.select { |path| path == installer_path || path.extname == ".app" }
.sort
else
[artifact.source.basename]
end
sources.flat_map do |source|
top_level_info_plists(Pathname.glob(dir/"**"/source/"Contents"/"Info.plist")).sort
end
end
info_plist_paths.each(&parse_info_plist)

View File

@ -12,8 +12,6 @@ module Homebrew
# After updating this, run `brew vendor-gems --update=--bundler`.
HOMEBREW_BUNDLER_VERSION = "2.3.26"
VALID_GEM_GROUPS = ["sorbet", "prof"].freeze
module_function
def ruby_bindir
@ -136,9 +134,6 @@ module Homebrew
old_bundle_frozen = ENV.fetch("BUNDLE_FROZEN", nil)
old_sdkroot = ENV.fetch("SDKROOT", nil)
invalid_groups = groups - VALID_GEM_GROUPS
raise ArgumentError, "Invalid gem groups: #{invalid_groups.join(", ")}" unless invalid_groups.empty?
install_bundler!
require "settings"

View File

@ -418,6 +418,17 @@ module GitHub
result["organization"]["team"]["members"]["nodes"].to_h { |member| [member["login"], member["name"]] }
end
sig {
params(user: String)
.returns(
T::Array[{
closest_tier_monthly_amount: Integer,
login: String,
monthly_amount: Integer,
name: String,
}],
)
}
def self.sponsorships(user)
has_next_page = T.let(true, T::Boolean)
after = ""

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
# Helper functions for updating PyPI resources.
@ -7,8 +7,6 @@
module PyPI
extend T::Sig
module_function
PYTHONHOSTED_URL_PREFIX = "https://files.pythonhosted.org/packages/"
private_constant :PYTHONHOSTED_URL_PREFIX
@ -35,13 +33,16 @@ module PyPI
return
end
@name = package_string
@name, @version = @name.split("==") if @name.include? "=="
if package_string.include? "=="
@name, @version = package_string.split("==")
else
@name = package_string
end
return unless (match = @name.match(/^(.*?)\[(.+)\]$/))
return unless (match = T.must(@name).match(/^(.*?)\[(.+)\]$/))
@name = match[1]
@extras = match[2].split ","
@extras = T.must(match[2]).split ","
end
# Get name, URL, SHA-256 checksum, and latest version for a given PyPI package.
@ -87,7 +88,7 @@ module PyPI
sig { params(other: Package).returns(T::Boolean) }
def same_package?(other)
@name.tr("_", "-").casecmp(other.name.tr("_", "-")).zero?
T.must(@name.tr("_", "-").casecmp(other.name.tr("_", "-"))).zero?
end
# Compare only names so we can use .include? and .uniq on a Package array
@ -109,7 +110,7 @@ module PyPI
end
sig { params(url: String, version: T.any(String, Version)).returns(T.nilable(String)) }
def update_pypi_url(url, version)
def self.update_pypi_url(url, version)
package = Package.new url, is_url: true
return unless package.valid_pypi_package?
@ -133,8 +134,9 @@ module PyPI
ignore_non_pypi_packages: T.nilable(T::Boolean),
).returns(T.nilable(T::Boolean))
}
def update_python_resources!(formula, version: nil, package_name: nil, extra_packages: nil, exclude_packages: nil,
print_only: false, silent: false, ignore_non_pypi_packages: false)
def self.update_python_resources!(formula, version: nil, package_name: nil, extra_packages: nil,
exclude_packages: nil, print_only: false, silent: false,
ignore_non_pypi_packages: false)
auto_update_list = formula.tap&.pypi_formula_mappings
if auto_update_list.present? && auto_update_list.key?(formula.full_name) &&
@ -282,7 +284,7 @@ module PyPI
true
end
def json_to_packages(json_tree, main_package, exclude_packages)
def self.json_to_packages(json_tree, main_package, exclude_packages)
return [] if json_tree.blank?
json_tree.flat_map do |package_json|

View File

@ -30,7 +30,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/zeitwerk-2.6.7/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/activesupport-6.1.7.3/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/public_suffix-5.0.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/addressable-2.8.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/addressable-2.8.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ast-2.4.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bindata-2.4.15/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/msgpack-1.7.0")
@ -76,7 +76,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/mustache-1.1.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel-1.22.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parallel_tests-3.13.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parser-3.2.1.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parser-3.2.2.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rainbow-3.1.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/sorbet-runtime-0.5.10461/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/parlour-8.1.0/lib")

View File

@ -29,10 +29,6 @@ module Addressable
IDN::Punycode.decode(value.to_s)
end
def self.unicode_normalize_kc(value)
IDN::Stringprep.nfkc_normalize(value.to_s)
end
def self.to_ascii(value)
value.to_s.split('.', -1).map do |segment|
if segment.size > 0 && segment.size < 64

View File

@ -66,7 +66,7 @@ module Addressable
# domain name as described in RFC 3490.
def self.to_ascii(input)
input = input.to_s unless input.is_a?(String)
input = input.dup
input = input.dup.force_encoding(Encoding::UTF_8).unicode_normalize(:nfkc)
if input.respond_to?(:force_encoding)
input.force_encoding(Encoding::ASCII_8BIT)
end
@ -77,7 +77,7 @@ module Addressable
part.force_encoding(Encoding::ASCII_8BIT)
end
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
ACE_PREFIX + punycode_encode(part)
else
part
end
@ -112,15 +112,6 @@ module Addressable
output
end
# Unicode normalization form KC.
def self.unicode_normalize_kc(input)
input = input.to_s unless input.is_a?(String)
unpacked = input.unpack("U*")
unpacked =
unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
return unpacked.pack("U*")
end
##
# Unicode aware downcase method.
#
@ -136,164 +127,6 @@ module Addressable
end
private_class_method :unicode_downcase
def self.unicode_compose(unpacked)
unpacked_result = []
length = unpacked.length
return unpacked if length == 0
starter = unpacked[0]
starter_cc = lookup_unicode_combining_class(starter)
starter_cc = 256 if starter_cc != 0
for i in 1...length
ch = unpacked[i]
if (starter_cc == 0 &&
(composite = unicode_compose_pair(starter, ch)) != nil)
starter = composite
else
unpacked_result << starter
starter = ch
end
end
unpacked_result << starter
return unpacked_result
end
private_class_method :unicode_compose
def self.unicode_compose_pair(ch_one, ch_two)
if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
# Hangul L + V
return HANGUL_SBASE + (
(ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
) * HANGUL_TCOUNT
elsif ch_one >= HANGUL_SBASE &&
ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
(ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
# Hangul LV + T
return ch_one + (ch_two - HANGUL_TBASE)
end
p = []
ucs4_to_utf8(ch_one, p)
ucs4_to_utf8(ch_two, p)
return lookup_unicode_composition(p)
end
private_class_method :unicode_compose_pair
def self.ucs4_to_utf8(char, buffer)
if char < 128
buffer << char
elsif char < 2048
buffer << (char >> 6 | 192)
buffer << (char & 63 | 128)
elsif char < 0x10000
buffer << (char >> 12 | 224)
buffer << (char >> 6 & 63 | 128)
buffer << (char & 63 | 128)
elsif char < 0x200000
buffer << (char >> 18 | 240)
buffer << (char >> 12 & 63 | 128)
buffer << (char >> 6 & 63 | 128)
buffer << (char & 63 | 128)
elsif char < 0x4000000
buffer << (char >> 24 | 248)
buffer << (char >> 18 & 63 | 128)
buffer << (char >> 12 & 63 | 128)
buffer << (char >> 6 & 63 | 128)
buffer << (char & 63 | 128)
elsif char < 0x80000000
buffer << (char >> 30 | 252)
buffer << (char >> 24 & 63 | 128)
buffer << (char >> 18 & 63 | 128)
buffer << (char >> 12 & 63 | 128)
buffer << (char >> 6 & 63 | 128)
buffer << (char & 63 | 128)
end
end
private_class_method :ucs4_to_utf8
def self.unicode_sort_canonical(unpacked)
unpacked = unpacked.dup
i = 1
length = unpacked.length
return unpacked if length < 2
while i < length
last = unpacked[i-1]
ch = unpacked[i]
last_cc = lookup_unicode_combining_class(last)
cc = lookup_unicode_combining_class(ch)
if cc != 0 && last_cc != 0 && last_cc > cc
unpacked[i] = last
unpacked[i-1] = ch
i -= 1 if i > 1
else
i += 1
end
end
return unpacked
end
private_class_method :unicode_sort_canonical
def self.unicode_decompose(unpacked)
unpacked_result = []
for cp in unpacked
if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
l, v, t = unicode_decompose_hangul(cp)
unpacked_result << l
unpacked_result << v if v
unpacked_result << t if t
else
dc = lookup_unicode_compatibility(cp)
unless dc
unpacked_result << cp
else
unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
end
end
end
return unpacked_result
end
private_class_method :unicode_decompose
def self.unicode_decompose_hangul(codepoint)
sindex = codepoint - HANGUL_SBASE;
if sindex < 0 || sindex >= HANGUL_SCOUNT
l = codepoint
v = t = nil
return l, v, t
end
l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
if t == HANGUL_TBASE
t = nil
end
return l, v, t
end
private_class_method :unicode_decompose_hangul
def self.lookup_unicode_combining_class(codepoint)
codepoint_data = UNICODE_DATA[codepoint]
(codepoint_data ?
(codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
0)
end
private_class_method :lookup_unicode_combining_class
def self.lookup_unicode_compatibility(codepoint)
codepoint_data = UNICODE_DATA[codepoint]
(codepoint_data ?
codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
end
private_class_method :lookup_unicode_compatibility
def self.lookup_unicode_lowercase(codepoint)
codepoint_data = UNICODE_DATA[codepoint]
(codepoint_data ?
@ -302,21 +135,6 @@ module Addressable
end
private_class_method :lookup_unicode_lowercase
def self.lookup_unicode_composition(unpacked)
return COMPOSITION_TABLE[unpacked]
end
private_class_method :lookup_unicode_composition
HANGUL_SBASE = 0xac00
HANGUL_LBASE = 0x1100
HANGUL_LCOUNT = 19
HANGUL_VBASE = 0x1161
HANGUL_VCOUNT = 21
HANGUL_TBASE = 0x11a7
HANGUL_TCOUNT = 28
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
UNICODE_DATA_COMBINING_CLASS = 0
UNICODE_DATA_EXCLUSION = 1
UNICODE_DATA_CANONICAL = 2

View File

@ -892,7 +892,7 @@ module Addressable
# operator.
#
# @param [Hash, Array, String] value
# Normalizes keys and values with IDNA#unicode_normalize_kc
# Normalizes unicode keys and values with String#unicode_normalize (NFC)
#
# @return [Hash, Array, String] The normalized values
def normalize_value(value)
@ -902,15 +902,17 @@ module Addressable
# Handle unicode normalization
if value.kind_of?(Array)
value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
value.map! { |val| normalize_value(val) }
elsif value.kind_of?(Hash)
value = value.inject({}) { |acc, (k, v)|
acc[Addressable::IDNA.unicode_normalize_kc(k)] =
Addressable::IDNA.unicode_normalize_kc(v)
acc[normalize_value(k)] = normalize_value(v)
acc
}
else
value = Addressable::IDNA.unicode_normalize_kc(value)
if value.encoding != Encoding::UTF_8
value = value.dup.force_encoding(Encoding::UTF_8)
end
value = value.unicode_normalize(:nfc)
end
value
end

View File

@ -53,7 +53,7 @@ module Addressable
PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
AUTHORITY = (PCHAR + "\\[\\:\\]").freeze
AUTHORITY = (PCHAR + "\\[\\]").freeze
PATH = (PCHAR + "\\/").freeze
QUERY = (PCHAR + "\\/\\?").freeze
FRAGMENT = (PCHAR + "\\/\\?").freeze
@ -117,7 +117,7 @@ module Addressable
uri = uri.to_str
rescue TypeError, NoMethodError
raise TypeError, "Can't convert #{uri.class} into String."
end if not uri.is_a? String
end unless uri.is_a?(String)
# This Regexp supplied as an example in RFC 3986, and it works great.
scan = uri.scan(URIREGEX)
@ -138,15 +138,15 @@ module Addressable
user = userinfo.strip[/^([^:]*):?/, 1]
password = userinfo.strip[/:(.*)$/, 1]
end
host = authority.sub(
/^([^\[\]]*)@/, EMPTY_STR
).sub(
/:([^:@\[\]]*?)$/, EMPTY_STR
)
port = authority[/:([^:@\[\]]*?)$/, 1]
end
if port == EMPTY_STR
port = nil
port = nil if port == EMPTY_STR
end
return new(
@ -189,7 +189,7 @@ module Addressable
uri = uri.to_s
end
if !uri.respond_to?(:to_str)
unless uri.respond_to?(:to_str)
raise TypeError, "Can't convert #{uri.class} into String."
end
# Otherwise, convert to a String
@ -281,7 +281,7 @@ module Addressable
return nil unless path
# If a URI object is passed, just return itself.
return path if path.kind_of?(self)
if !path.respond_to?(:to_str)
unless path.respond_to?(:to_str)
raise TypeError, "Can't convert #{path.class} into String."
end
# Otherwise, convert to a String
@ -329,13 +329,13 @@ module Addressable
# #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
def self.join(*uris)
uri_objects = uris.collect do |uri|
if !uri.respond_to?(:to_str)
unless uri.respond_to?(:to_str)
raise TypeError, "Can't convert #{uri.class} into String."
end
uri.kind_of?(self) ? uri : self.parse(uri.to_str)
end
result = uri_objects.shift.dup
for uri in uri_objects
uri_objects.each do |uri|
result.join!(uri)
end
return result
@ -481,7 +481,7 @@ module Addressable
leave_encoded.include?(c) ? sequence : c
end
result.force_encoding("utf-8")
result.force_encoding(Encoding::UTF_8)
if return_type == String
return result
elsif return_type == ::Addressable::URI
@ -579,7 +579,7 @@ module Addressable
unencoded = self.unencode_component(component, String, leave_encoded)
begin
encoded = self.encode_component(
Addressable::IDNA.unicode_normalize_kc(unencoded),
unencoded.unicode_normalize(:nfc),
character_class,
leave_encoded
)
@ -687,8 +687,7 @@ module Addressable
components.each do |key, value|
if value != nil
begin
components[key] =
Addressable::IDNA.unicode_normalize_kc(value.to_str)
components[key] = value.to_str.unicode_normalize(:nfc)
rescue ArgumentError
# Likely a malformed UTF-8 character, skip unicode normalization
components[key] = value.to_str
@ -836,7 +835,9 @@ module Addressable
end
end
self.defer_validation do
reset_ivs
defer_validation do
# Bunch of crazy logic required because of the composite components
# like userinfo and authority.
self.scheme = options[:scheme] if options[:scheme]
@ -851,7 +852,8 @@ module Addressable
self.query_values = options[:query_values] if options[:query_values]
self.fragment = options[:fragment] if options[:fragment]
end
self.to_s
to_s # force path validation
end
##
@ -878,9 +880,7 @@ module Addressable
# The scheme component for this URI.
#
# @return [String] The scheme component.
def scheme
return defined?(@scheme) ? @scheme : nil
end
attr_reader :scheme
##
# The scheme component for this URI, normalized.
@ -888,8 +888,8 @@ module Addressable
# @return [String] The scheme component, normalized.
def normalized_scheme
return nil unless self.scheme
@normalized_scheme ||= begin
if self.scheme =~ /^\s*ssh\+svn\s*$/i
if @normalized_scheme == NONE
@normalized_scheme = if self.scheme =~ /^\s*ssh\+svn\s*$/i
"svn+ssh".dup
else
Addressable::URI.normalize_component(
@ -920,7 +920,7 @@ module Addressable
@scheme = nil if @scheme.to_s.strip.empty?
# Reset dependent values
remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
@normalized_scheme = NONE
remove_composite_values
# Ensure we haven't created an invalid URI
@ -931,9 +931,7 @@ module Addressable
# The user component for this URI.
#
# @return [String] The user component.
def user
return defined?(@user) ? @user : nil
end
attr_reader :user
##
# The user component for this URI, normalized.
@ -941,8 +939,8 @@ module Addressable
# @return [String] The user component, normalized.
def normalized_user
return nil unless self.user
return @normalized_user if defined?(@normalized_user)
@normalized_user ||= begin
return @normalized_user unless @normalized_user == NONE
@normalized_user = begin
if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
(!self.password || self.password.strip.empty?)
nil
@ -970,14 +968,14 @@ module Addressable
# You can't have a nil user with a non-nil password
if password != nil
@user = EMPTY_STR if @user.nil?
@user = EMPTY_STR unless user
end
# Reset dependent values
remove_instance_variable(:@userinfo) if defined?(@userinfo)
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
remove_instance_variable(:@authority) if defined?(@authority)
remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
@userinfo = nil
@normalized_userinfo = NONE
@authority = nil
@normalized_user = NONE
remove_composite_values
# Ensure we haven't created an invalid URI
@ -988,9 +986,7 @@ module Addressable
# The password component for this URI.
#
# @return [String] The password component.
def password
return defined?(@password) ? @password : nil
end
attr_reader :password
##
# The password component for this URI, normalized.
@ -998,8 +994,8 @@ module Addressable
# @return [String] The password component, normalized.
def normalized_password
return nil unless self.password
return @normalized_password if defined?(@normalized_password)
@normalized_password ||= begin
return @normalized_password unless @normalized_password == NONE
@normalized_password = begin
if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
(!self.user || self.user.strip.empty?)
nil
@ -1026,17 +1022,15 @@ module Addressable
@password = new_password ? new_password.to_str : nil
# You can't have a nil user with a non-nil password
@password ||= nil
@user ||= nil
if @password != nil
@user = EMPTY_STR if @user.nil?
self.user = EMPTY_STR if user.nil?
end
# Reset dependent values
remove_instance_variable(:@userinfo) if defined?(@userinfo)
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
remove_instance_variable(:@authority) if defined?(@authority)
remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
@userinfo = nil
@normalized_userinfo = NONE
@authority = nil
@normalized_password = NONE
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1066,8 +1060,8 @@ module Addressable
# @return [String] The userinfo component, normalized.
def normalized_userinfo
return nil unless self.userinfo
return @normalized_userinfo if defined?(@normalized_userinfo)
@normalized_userinfo ||= begin
return @normalized_userinfo unless @normalized_userinfo == NONE
@normalized_userinfo = begin
current_user = self.normalized_user
current_password = self.normalized_password
if !current_user && !current_password
@ -1105,7 +1099,7 @@ module Addressable
self.user = new_user
# Reset dependent values
remove_instance_variable(:@authority) if defined?(@authority)
@authority = nil
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1116,9 +1110,7 @@ module Addressable
# The host component for this URI.
#
# @return [String] The host component.
def host
return defined?(@host) ? @host : nil
end
attr_reader :host
##
# The host component for this URI, normalized.
@ -1161,8 +1153,8 @@ module Addressable
@host = new_host ? new_host.to_str : nil
# Reset dependent values
remove_instance_variable(:@authority) if defined?(@authority)
remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
@authority = nil
@normalized_host = nil
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1293,14 +1285,14 @@ module Addressable
end
# Password assigned first to ensure validity in case of nil
self.password = defined?(new_password) ? new_password : nil
self.user = defined?(new_user) ? new_user : nil
self.host = defined?(new_host) ? new_host : nil
self.port = defined?(new_port) ? new_port : nil
self.password = new_password
self.user = new_user
self.host = new_host
self.port = new_port
# Reset dependent values
remove_instance_variable(:@userinfo) if defined?(@userinfo)
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
@userinfo = nil
@normalized_userinfo = NONE
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1348,16 +1340,16 @@ module Addressable
new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
end
self.scheme = defined?(new_scheme) ? new_scheme : nil
self.host = defined?(new_host) ? new_host : nil
self.port = defined?(new_port) ? new_port : nil
self.scheme = new_scheme
self.host = new_host
self.port = new_port
self.userinfo = nil
# Reset dependent values
remove_instance_variable(:@userinfo) if defined?(@userinfo)
remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
remove_instance_variable(:@authority) if defined?(@authority)
remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
@userinfo = nil
@normalized_userinfo = NONE
@authority = nil
@normalized_authority = nil
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1384,9 +1376,7 @@ module Addressable
# infer port numbers from default values.
#
# @return [Integer] The port component.
def port
return defined?(@port) ? @port : nil
end
attr_reader :port
##
# The port component for this URI, normalized.
@ -1394,8 +1384,8 @@ module Addressable
# @return [Integer] The port component, normalized.
def normalized_port
return nil unless self.port
return @normalized_port if defined?(@normalized_port)
@normalized_port ||= begin
return @normalized_port unless @normalized_port == NONE
@normalized_port = begin
if URI.port_mapping[self.normalized_scheme] == self.port
nil
else
@ -1426,8 +1416,8 @@ module Addressable
@port = nil if @port == 0
# Reset dependent values
remove_instance_variable(:@authority) if defined?(@authority)
remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
@authority = nil
@normalized_port = NONE
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1528,9 +1518,7 @@ module Addressable
# The path component for this URI.
#
# @return [String] The path component.
def path
return defined?(@path) ? @path : EMPTY_STR
end
attr_reader :path
NORMPATH = /^(?!\/)[^\/:]*:.*$/
##
@ -1579,7 +1567,7 @@ module Addressable
end
# Reset dependent values
remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
@normalized_path = nil
remove_composite_values
# Ensure we haven't created an invalid URI
@ -1609,9 +1597,7 @@ module Addressable
# The query component for this URI.
#
# @return [String] The query component.
def query
return defined?(@query) ? @query : nil
end
attr_reader :query
##
# The query component for this URI, normalized.
@ -1619,8 +1605,8 @@ module Addressable
# @return [String] The query component, normalized.
def normalized_query(*flags)
return nil unless self.query
return @normalized_query if defined?(@normalized_query)
@normalized_query ||= begin
return @normalized_query unless @normalized_query == NONE
@normalized_query = begin
modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
# Make sure possible key-value pair delimiters are escaped.
modified_query_class.sub!("\\&", "").sub!("\\;", "")
@ -1652,7 +1638,7 @@ module Addressable
@query = new_query ? new_query.to_str : nil
# Reset dependent values
remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
@normalized_query = NONE
remove_composite_values
end
@ -1814,9 +1800,7 @@ module Addressable
# The fragment component for this URI.
#
# @return [String] The fragment component.
def fragment
return defined?(@fragment) ? @fragment : nil
end
attr_reader :fragment
##
# The fragment component for this URI, normalized.
@ -1824,8 +1808,8 @@ module Addressable
# @return [String] The fragment component, normalized.
def normalized_fragment
return nil unless self.fragment
return @normalized_fragment if defined?(@normalized_fragment)
@normalized_fragment ||= begin
return @normalized_fragment unless @normalized_fragment == NONE
@normalized_fragment = begin
component = Addressable::URI.normalize_component(
self.fragment,
Addressable::URI::NormalizeCharacterClasses::FRAGMENT
@ -1848,7 +1832,7 @@ module Addressable
@fragment = new_fragment ? new_fragment.to_str : nil
# Reset dependent values
remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
@normalized_fragment = NONE
remove_composite_values
# Ensure we haven't created an invalid URI
@ -2014,7 +1998,7 @@ module Addressable
#
# @see Hash#merge
def merge(hash)
if !hash.respond_to?(:to_hash)
unless hash.respond_to?(:to_hash)
raise TypeError, "Can't convert #{hash.class} into Hash."
end
hash = hash.to_hash
@ -2408,7 +2392,8 @@ module Addressable
yield
@validation_deferred = false
validate
return nil
ensure
@validation_deferred = false
end
protected
@ -2507,11 +2492,7 @@ module Addressable
# @return [Addressable::URI] <code>self</code>.
def replace_self(uri)
# Reset dependent values
instance_variables.each do |var|
if instance_variable_defined?(var) && var != :@validation_deferred
remove_instance_variable(var)
end
end
reset_ivs
@scheme = uri.scheme
@user = uri.user
@ -2543,8 +2524,8 @@ module Addressable
#
# @api private
def remove_composite_values
remove_instance_variable(:@uri_string) if defined?(@uri_string)
remove_instance_variable(:@hash) if defined?(@hash)
@uri_string = nil
@hash = nil
end
##
@ -2556,5 +2537,40 @@ module Addressable
str.force_encoding(Encoding::UTF_8)
end
end
private
##
# Resets instance variables
#
# @api private
def reset_ivs
@scheme = nil
@user = nil
@normalized_scheme = NONE
@normalized_user = NONE
@uri_string = nil
@hash = nil
@userinfo = nil
@normalized_userinfo = NONE
@authority = nil
@password = nil
@normalized_authority = nil
@port = nil
@normalized_password = NONE
@host = nil
@normalized_host = nil
@normalized_port = NONE
@path = EMPTY_STR
@normalized_path = nil
@normalized_query = NONE
@fragment = nil
@normalized_fragment = NONE
@query = nil
end
NONE = Object.new.freeze
private_constant :NONE
end
end

View File

@ -23,7 +23,7 @@ if !defined?(Addressable::VERSION)
module VERSION
MAJOR = 2
MINOR = 8
TINY = 1
TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

@ -2,6 +2,7 @@
# frozen_string_literal: true
require "pkg_version"
require "version/head"
require "version/null"
require "version/parser"
@ -532,6 +533,19 @@ class Version
false
end
sig { params(comparator: String, other: Version).returns(T::Boolean) }
def compare(comparator, other)
case comparator
when ">=" then self >= other
when ">" then self > other
when "<" then self < other
when "<=" then self <= other
when "==" then self == other
when "!=" then self != other
else raise ArgumentError, "Unknown comparator: #{comparator}"
end
end
sig { params(other: T.untyped).returns(T.nilable(Integer)) }
def <=>(other)
# Needed to retain API compatibility with older string comparisons
@ -663,34 +677,3 @@ class Version
version.scan(SCAN_PATTERN).map { |token| Token.create(T.cast(token, String)) }
end
end
# A formula's HEAD version.
# @see https://docs.brew.sh/Formula-Cookbook#unstable-versions-head Unstable versions (head)
#
# @api private
class HeadVersion < Version
extend T::Sig
sig { returns(T.nilable(String)) }
attr_reader :commit
def initialize(*)
super
@commit = @version[/^HEAD-(.+)$/, 1]
end
sig { params(commit: T.nilable(String)).void }
def update_commit(commit)
@commit = commit
@version = if commit
"HEAD-#{commit}"
else
"HEAD"
end
end
sig { returns(T::Boolean) }
def head?
true
end
end

View File

@ -0,0 +1,35 @@
# typed: true
# frozen_string_literal: true
class Version
# A formula's HEAD version.
# @see https://docs.brew.sh/Formula-Cookbook#unstable-versions-head Unstable versions (head)
#
# @api private
class HeadVersion < Version
extend T::Sig
sig { returns(T.nilable(String)) }
attr_reader :commit
def initialize(*)
super
@commit = @version[/^HEAD-(.+)$/, 1]
end
sig { params(commit: T.nilable(String)).void }
def update_commit(commit)
@commit = commit
@version = if commit
"HEAD-#{commit}"
else
"HEAD"
end
end
sig { returns(T::Boolean) }
def head?
true
end
end
end

View File

@ -882,7 +882,7 @@ There are two ways to add `launchd` plists and `systemd` services to a formula,
```ruby
service do
run bin/"script"
run opt_bin/"script"
end
```