mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 22:29:27 +04:00
Make tests use public APIs and enforce via RuboCop
- Tests reached into private APIs with `send` and poked internal state via `instance_variable_get`/`instance_variable_set`, hiding which interfaces specs really exercise and letting visibility rot. - Make every statically-poked method public and call it directly, keeping `public_send` only for dynamically-named public methods. - Read and write state through public `attr_*` accessors instead of instance variable reflection. - Add `Homebrew/NoSendInTests` and `Homebrew/NoInstanceVariableAccessInTests` cops scoped to `Library/Homebrew/test` so neither pattern creeps back in. - Keep rare justified disables, e.g. `Module#remove_const` is private core Ruby and raw memoisation state has no accessor.
This commit is contained in:
@@ -64,6 +64,6 @@ Do not use conventional commit prefixes such as `feat:`, `fix:`, `chore:`, `refa
|
||||
9. Inline new or existing methods as methods or local variables unless they are reused 2+ times or needed for unit tests.
|
||||
10. Avoid `T.must`, `T.cast`, `T.let`, `T.untyped` and `T.anything` where possible while maintaining `typed: strict`; prefer explicit nil checks, precise types and APIs that return non-nil values. If a generic top type is unavoidable, prefer `T.anything` over `T.untyped`.
|
||||
11. Avoid `T.unsafe(self)` whenever possible; prefer `requires_ancestor` or similar typed module patterns.
|
||||
12. Prefer `.public_send` over `.send` where possible; call methods directly when practical. Use `.send` only when a private API must be invoked.
|
||||
12. Prefer `.public_send` over `.send` where possible; call methods directly when practical. Use `.send` only when a private API must be invoked. In tests, never use `.send`: make the method public and call it directly (enforced by `Homebrew/NoSendInTests`), keeping `.public_send` only for dynamically-named public methods. Likewise in tests read and write state through public `attr_*` accessors rather than `instance_variable_get`/`instance_variable_set` (enforced by `Homebrew/NoInstanceVariableAccessInTests`).
|
||||
13. Keep `extend/os/*` prepends as thin as possible; put the `prepend` in the OS-specific `linux` or `macos` file rather than the shared `extend/os/*` loader with an inline `if`, and prefer putting substantive logic in shared code outside `extend/` when practical so it can be tested on all platforms instead of relying on `:needs_linux` or `:needs_macos` specs.
|
||||
14. When Bash logic mirrors Ruby logic, keep both implementations in sync and add two-way comments naming the matching Ruby and Bash locations; keep matching helper filenames aligned where practical.
|
||||
|
||||
@@ -94,6 +94,20 @@ Homebrew/NegateInclude:
|
||||
- "Homebrew/rubocops/**/*"
|
||||
- "Homebrew/sorbet/tapioca/**/*"
|
||||
|
||||
Homebrew/NoInstanceVariableAccessInTests:
|
||||
Description: "Use public `attr_*` accessors instead of `instance_variable_get`/`instance_variable_set` in tests."
|
||||
Include:
|
||||
- "Homebrew/test/**/*.rb"
|
||||
Exclude:
|
||||
- "Homebrew/test/support/fixtures/**/*"
|
||||
|
||||
Homebrew/NoSendInTests:
|
||||
Description: "Make the target method public and call it directly instead of using `send` in tests."
|
||||
Include:
|
||||
- "Homebrew/test/**/*.rb"
|
||||
Exclude:
|
||||
- "Homebrew/test/support/fixtures/**/*"
|
||||
|
||||
Homebrew/UnreferencedLet:
|
||||
Description: "Removes a lazy `let` whose name is never referenced (its block never runs)."
|
||||
Include:
|
||||
|
||||
@@ -147,7 +147,7 @@ module Homebrew
|
||||
|
||||
# Redact any token `add_auth_token_to_url!` embedded, so dry-run output doesn't leak it.
|
||||
sig { params(url: T.nilable(String)).returns(String) }
|
||||
private_class_method def self.redacted_url(url)
|
||||
def self.redacted_url(url)
|
||||
Formatter.redact_secrets(url.to_s, [GitHub::API.credentials].compact)
|
||||
end
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@ module Homebrew
|
||||
@formula_oldnames = T.let(nil, T.nilable(T::Hash[String, String]))
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formulae_by_name: T.nilable(T::Hash[String, T::Hash[Symbol, T.untyped]]))
|
||||
.returns(T.nilable(T::Hash[String, T::Hash[Symbol, T.untyped]]))
|
||||
}
|
||||
attr_writer :formulae_by_name
|
||||
|
||||
sig { override.params(name: String, no_upgrade: T::Boolean, verbose: T::Boolean, options: T.untyped).returns(T::Boolean) }
|
||||
def preinstall!(name, no_upgrade: false, verbose: false, **options)
|
||||
new(name, options).preinstall!(no_upgrade:, verbose:)
|
||||
|
||||
@@ -102,8 +102,6 @@ module Homebrew
|
||||
@pool.wait_for_termination
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(entries: T::Array[Installer::InstallableEntry]).returns(T::Hash[String, T::Set[String]]) }
|
||||
def build_dependency_map(entries)
|
||||
installed_taps = Homebrew::Bundle::Tap.installed_taps
|
||||
@@ -177,6 +175,21 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(message: String, stream: IO).void }
|
||||
def write_output(message, stream: $stdout)
|
||||
@output_mutex.synchronize do
|
||||
# Interactive installers can leave ONLCR disabled, so use CRLF to
|
||||
# ensure terminal status output returns to column 0.
|
||||
if stream.tty?
|
||||
stream.write(message, "\r\n")
|
||||
else
|
||||
stream.puts(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(name: String).returns(String) }
|
||||
def normalize_formula_name(name)
|
||||
Utils.name_from_full_name(name)
|
||||
@@ -286,19 +299,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(message: String, stream: IO).void }
|
||||
def write_output(message, stream: $stdout)
|
||||
@output_mutex.synchronize do
|
||||
# Interactive installers can leave ONLCR disabled, so use CRLF to
|
||||
# ensure terminal status output returns to column 0.
|
||||
if stream.tty?
|
||||
stream.write(message, "\r\n")
|
||||
else
|
||||
stream.puts(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def clear_tty_line
|
||||
File.open("/dev/tty", "w") do |f|
|
||||
|
||||
@@ -34,6 +34,15 @@ module Homebrew
|
||||
@failed_taps << tap_name
|
||||
end
|
||||
|
||||
sig { params(failed_taps: T.nilable(T::Array[String])).returns(T.nilable(T::Array[String])) }
|
||||
attr_writer :failed_taps
|
||||
|
||||
sig {
|
||||
params(skipped_entries: T.nilable(T::Hash[Symbol, T.nilable(T::Array[String])]))
|
||||
.returns(T.nilable(T::Hash[Symbol, T.nilable(T::Array[String])]))
|
||||
}
|
||||
attr_writer :skipped_entries
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T::Hash[Symbol, T.nilable(T::Array[String])]) }
|
||||
|
||||
@@ -133,6 +133,9 @@ class CacheStoreDatabase
|
||||
db.each_key(&block)
|
||||
end
|
||||
|
||||
sig { params(db: T.nilable(T::Hash[Key, Value])).void }
|
||||
attr_writer :db
|
||||
|
||||
private
|
||||
|
||||
# Lazily loaded database in read/write mode. If this method is called, a
|
||||
|
||||
@@ -76,6 +76,100 @@ module Cask
|
||||
@bundle_ids_to_reopen ||= T.let([], T.nilable(T::Array[String]))
|
||||
end
|
||||
|
||||
# :quit/:signal must come before :kext so the kext will not be in use by a running process
|
||||
sig {
|
||||
params(
|
||||
bundle_ids: String,
|
||||
command: T.nilable(T.class_of(SystemCommand)),
|
||||
upgrade: T::Boolean,
|
||||
_kwargs: T.anything,
|
||||
).void
|
||||
}
|
||||
def uninstall_quit(*bundle_ids, command: nil, upgrade: false, **_kwargs)
|
||||
bundle_ids.each do |bundle_id|
|
||||
next unless running?(bundle_id)
|
||||
|
||||
unless T.must(User.current).gui?
|
||||
opoo "Not logged into a GUI; skipping quitting application ID '#{bundle_id}'."
|
||||
next
|
||||
end
|
||||
|
||||
ohai "Quitting application '#{bundle_id}'..."
|
||||
|
||||
quit_succeeded = T.let(false, T::Boolean)
|
||||
begin
|
||||
Timeout.timeout(10) do
|
||||
Kernel.loop do
|
||||
next unless quit(bundle_id).success?
|
||||
|
||||
next if running?(bundle_id)
|
||||
|
||||
puts "Application '#{bundle_id}' quit successfully."
|
||||
quit_succeeded = true
|
||||
break
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
opoo "Application '#{bundle_id}' did not quit. #{automation_access_instructions}"
|
||||
end
|
||||
|
||||
bundle_ids_to_reopen << bundle_id if upgrade && quit_succeeded
|
||||
end
|
||||
end
|
||||
|
||||
# This returns T::Enumerable[[Pathname, T::Array[Pathname]]] when called without a block,
|
||||
# but sorbet doesn't support overloads.
|
||||
sig {
|
||||
params(
|
||||
action: Symbol,
|
||||
paths: T::Array[T.any(Pathname, String)],
|
||||
_block: T.nilable(T.proc.params(path: T.any(Pathname, String), resolved_paths: T::Array[Pathname]).void),
|
||||
).returns(T.untyped)
|
||||
}
|
||||
def each_resolved_path(action, paths, &_block)
|
||||
return enum_for(:each_resolved_path, action, paths) unless block_given?
|
||||
|
||||
paths.each do |path|
|
||||
resolved_path = Pathname.new(path.to_s.sub(%r{^~(?=(/|$))}, Dir.home))
|
||||
|
||||
if resolved_path.relative?
|
||||
opoo "Skipping #{Formatter.identifier(action)} for relative path '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
if resolved_path.each_filename.any? { |part| [".", ".."].include?(part) }
|
||||
opoo "Skipping #{Formatter.identifier(action)} for path with relative segments '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
begin
|
||||
resolved_paths = Pathname.glob(resolved_path).reject do |target|
|
||||
next false unless undeletable?(target)
|
||||
|
||||
opoo "Skipping #{Formatter.identifier(action)} for undeletable path '#{target}'."
|
||||
true
|
||||
end
|
||||
yield path, resolved_paths
|
||||
rescue Errno::EPERM
|
||||
raise if ::Cask::Utils.full_disk_access_enabled?
|
||||
|
||||
odie "Unable to remove some files. Please enable Full Disk Access for your terminal under " \
|
||||
"#{::Cask::Utils.privacy_security_preference_pane("Full Disk Access")}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(search: String).returns(T::Array[String]) }
|
||||
def find_launchctl_with_wildcard(search)
|
||||
regex = Regexp.escape(search).gsub("\\*", ".*")
|
||||
system_command!("/bin/launchctl", args: ["list"])
|
||||
.stdout.lines.drop(1) # skip stdout column headers
|
||||
.filter_map do |line|
|
||||
pid, _state, id = line.chomp.split(/\s+/)
|
||||
id if pid.to_i.nonzero? && T.must(id).match?(regex)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(options: DirectivesType).void }
|
||||
@@ -189,17 +283,6 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(search: String).returns(T::Array[String]) }
|
||||
def find_launchctl_with_wildcard(search)
|
||||
regex = Regexp.escape(search).gsub("\\*", ".*")
|
||||
system_command!("/bin/launchctl", args: ["list"])
|
||||
.stdout.lines.drop(1) # skip stdout column headers
|
||||
.filter_map do |line|
|
||||
pid, _state, id = line.chomp.split(/\s+/)
|
||||
id if pid.to_i.nonzero? && T.must(id).match?(regex)
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def automation_access_instructions
|
||||
<<~EOS
|
||||
@@ -209,47 +292,6 @@ module Cask
|
||||
EOS
|
||||
end
|
||||
|
||||
# :quit/:signal must come before :kext so the kext will not be in use by a running process
|
||||
sig {
|
||||
params(
|
||||
bundle_ids: String,
|
||||
command: T.nilable(T.class_of(SystemCommand)),
|
||||
upgrade: T::Boolean,
|
||||
_kwargs: T.anything,
|
||||
).void
|
||||
}
|
||||
def uninstall_quit(*bundle_ids, command: nil, upgrade: false, **_kwargs)
|
||||
bundle_ids.each do |bundle_id|
|
||||
next unless running?(bundle_id)
|
||||
|
||||
unless T.must(User.current).gui?
|
||||
opoo "Not logged into a GUI; skipping quitting application ID '#{bundle_id}'."
|
||||
next
|
||||
end
|
||||
|
||||
ohai "Quitting application '#{bundle_id}'..."
|
||||
|
||||
quit_succeeded = T.let(false, T::Boolean)
|
||||
begin
|
||||
Timeout.timeout(10) do
|
||||
Kernel.loop do
|
||||
next unless quit(bundle_id).success?
|
||||
|
||||
next if running?(bundle_id)
|
||||
|
||||
puts "Application '#{bundle_id}' quit successfully."
|
||||
quit_succeeded = true
|
||||
break
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
opoo "Application '#{bundle_id}' did not quit. #{automation_access_instructions}"
|
||||
end
|
||||
|
||||
bundle_ids_to_reopen << bundle_id if upgrade && quit_succeeded
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(bundle_id: String).returns(T::Boolean) }
|
||||
def running?(bundle_id)
|
||||
script = <<~JAVASCRIPT
|
||||
@@ -452,48 +494,6 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
# This returns T::Enumerable[[Pathname, T::Array[Pathname]]] when called without a block,
|
||||
# but sorbet doesn't support overloads.
|
||||
sig {
|
||||
params(
|
||||
action: Symbol,
|
||||
paths: T::Array[T.any(Pathname, String)],
|
||||
_block: T.nilable(T.proc.params(path: T.any(Pathname, String), resolved_paths: T::Array[Pathname]).void),
|
||||
).returns(T.untyped)
|
||||
}
|
||||
def each_resolved_path(action, paths, &_block)
|
||||
return enum_for(:each_resolved_path, action, paths) unless block_given?
|
||||
|
||||
paths.each do |path|
|
||||
resolved_path = Pathname.new(path.to_s.sub(%r{^~(?=(/|$))}, Dir.home))
|
||||
|
||||
if resolved_path.relative?
|
||||
opoo "Skipping #{Formatter.identifier(action)} for relative path '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
if resolved_path.each_filename.any? { |part| [".", ".."].include?(part) }
|
||||
opoo "Skipping #{Formatter.identifier(action)} for path with relative segments '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
begin
|
||||
resolved_paths = Pathname.glob(resolved_path).reject do |target|
|
||||
next false unless undeletable?(target)
|
||||
|
||||
opoo "Skipping #{Formatter.identifier(action)} for undeletable path '#{target}'."
|
||||
true
|
||||
end
|
||||
yield path, resolved_paths
|
||||
rescue Errno::EPERM
|
||||
raise if ::Cask::Utils.full_disk_access_enabled?
|
||||
|
||||
odie "Unable to remove some files. Please enable Full Disk Access for your terminal under " \
|
||||
"#{::Cask::Utils.privacy_security_preference_pane("Full Disk Access")}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(paths: T.any(Pathname, String), command: T.class_of(SystemCommand), _kwargs: T.anything).void }
|
||||
def uninstall_delete(*paths, command:, **_kwargs)
|
||||
return if paths.empty?
|
||||
|
||||
@@ -56,6 +56,11 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { overridable.params(target: Pathname, source: Pathname).returns(T::Array[T.any(String, Pathname)]) }
|
||||
def backup_copy_args(target, source)
|
||||
["-pR", target, source]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
@@ -259,11 +264,6 @@ module Cask
|
||||
def undeletable?(target)
|
||||
!target.parent.writable?
|
||||
end
|
||||
|
||||
sig { overridable.params(target: Pathname, source: Pathname).returns(T::Array[T.any(String, Pathname)]) }
|
||||
def backup_copy_args(target, source)
|
||||
["-pR", target, source]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,11 +80,6 @@ module Cask
|
||||
"#{@source_string}#{target_string}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
ALT_NAME_ATTRIBUTE = "com.apple.metadata:kMDItemAlternateNames"
|
||||
private_constant :ALT_NAME_ATTRIBUTE
|
||||
|
||||
# Try to make the asset searchable under the target name. Spotlight
|
||||
# respects this attribute for many filetypes, but ignores it for App
|
||||
# bundles. Alfred 2.2 respects it even for App bundles.
|
||||
@@ -112,6 +107,11 @@ module Cask
|
||||
sudo: !file.writable?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
ALT_NAME_ATTRIBUTE = "com.apple.metadata:kMDItemAlternateNames"
|
||||
private_constant :ALT_NAME_ATTRIBUTE
|
||||
|
||||
sig { returns(String) }
|
||||
def printable_target
|
||||
target.to_s.sub(/^#{Dir.home}(#{File::SEPARATOR}|$)/, "~/")
|
||||
|
||||
+124
-121
@@ -36,6 +36,9 @@ module Cask
|
||||
sig { returns(T.nilable(Download)) }
|
||||
attr_reader :download
|
||||
|
||||
sig { params(livecheck_result: T.nilable(T.any(T::Boolean, Symbol))).void }
|
||||
attr_writer :livecheck_result
|
||||
|
||||
sig {
|
||||
params(
|
||||
cask: ::Cask::Cask, download: T::Boolean, quarantine: T::Boolean,
|
||||
@@ -148,6 +151,127 @@ module Cask
|
||||
summary.join("\n")
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
include_manual_installers: T::Boolean,
|
||||
_block: T.nilable(T.proc.params(
|
||||
arg0: T::Array[T.any(Artifact::Installer, Artifact::Pkg, Artifact::Relocated)],
|
||||
arg1: Pathname,
|
||||
).void),
|
||||
).void
|
||||
}
|
||||
def extract_artifacts(include_manual_installers: false, &_block)
|
||||
return unless online?
|
||||
return if (download = self.download).nil?
|
||||
|
||||
artifacts = cask.artifacts.select do |artifact|
|
||||
artifact.is_a?(Artifact::Pkg) ||
|
||||
artifact.is_a?(Artifact::App) ||
|
||||
artifact.is_a?(Artifact::Binary) ||
|
||||
(include_manual_installers &&
|
||||
artifact.is_a?(Artifact::Installer) &&
|
||||
artifact.manual_install &&
|
||||
[".app", ".pkg"].include?(artifact.path.extname.downcase))
|
||||
end
|
||||
|
||||
if @artifacts_extracted && @tmpdir
|
||||
yield artifacts, @tmpdir if block_given?
|
||||
return
|
||||
end
|
||||
|
||||
return if artifacts.empty?
|
||||
|
||||
@tmpdir ||= T.let(Pathname(Dir.mktmpdir("cask-audit", HOMEBREW_TEMP)), T.nilable(Pathname))
|
||||
|
||||
# Clean up tmp dir when @tmpdir object is destroyed
|
||||
ObjectSpace.define_finalizer(
|
||||
@tmpdir,
|
||||
proc { FileUtils.remove_entry(@tmpdir) },
|
||||
)
|
||||
|
||||
ohai "Downloading and extracting artifacts"
|
||||
|
||||
downloaded_path = download.fetch
|
||||
|
||||
primary_container = UnpackStrategy.detect(downloaded_path, type: @cask.container&.type, merge_xattrs: true)
|
||||
return if primary_container.nil?
|
||||
|
||||
# If the container has any dependencies we need to install them or unpacking will fail.
|
||||
if primary_container.dependencies.any?
|
||||
|
||||
install_options = {
|
||||
show_header: true,
|
||||
installed_on_request: false,
|
||||
verbose: false,
|
||||
}.compact
|
||||
|
||||
Homebrew::Install.perform_preinstall_checks_once
|
||||
formula_installers = primary_container.dependencies.filter_map do |dep|
|
||||
next unless dep.is_a?(Formula)
|
||||
next if dep.linked?
|
||||
|
||||
FormulaInstaller.new(
|
||||
dep,
|
||||
**install_options,
|
||||
)
|
||||
end
|
||||
valid_formula_installers = Homebrew::Install.fetch_formulae(formula_installers)
|
||||
|
||||
formula_installers.each do |fi|
|
||||
next unless valid_formula_installers.include?(fi)
|
||||
|
||||
fi.install
|
||||
fi.finish
|
||||
end
|
||||
end
|
||||
|
||||
# Extract the container to the temporary directory.
|
||||
primary_container.extract_nestedly(to: @tmpdir, basename: downloaded_path.basename, verbose: false)
|
||||
|
||||
if (nested_container = @cask.container&.nested)
|
||||
FileUtils.chmod_R "+rw", @tmpdir/nested_container, force: true, verbose: false
|
||||
UnpackStrategy.detect(@tmpdir/nested_container, merge_xattrs: true)
|
||||
.extract_nestedly(to: @tmpdir, verbose: false)
|
||||
end
|
||||
|
||||
# Propagate quarantine attributes from the downloaded file to extracted contents.
|
||||
# This is necessary because some extraction tools (like 7zr) don't preserve xattrs.
|
||||
if Quarantine.available? && Quarantine.detect(downloaded_path)
|
||||
Quarantine.propagate(from: downloaded_path, to: @tmpdir)
|
||||
end
|
||||
|
||||
# Process rename operations after extraction
|
||||
# Create a temporary installer to process renames in the audit directory
|
||||
temp_installer = Installer.new(@cask)
|
||||
temp_installer.process_rename_operations(target_dir: @tmpdir)
|
||||
|
||||
# Set the flag to indicate that extraction has occurred.
|
||||
@artifacts_extracted = T.let(true, T.nilable(TrueClass))
|
||||
|
||||
# Yield the artifacts and temp directory to the block if provided.
|
||||
yield artifacts, @tmpdir if block_given?
|
||||
end
|
||||
|
||||
sig { params(min_os: T.nilable(T.any(String, MacOSVersion))).returns(T.nilable(MacOSVersion)) }
|
||||
def normalize_min_os(min_os)
|
||||
return if min_os.nil?
|
||||
return if min_os.is_a?(String) && min_os.blank?
|
||||
|
||||
min_os = if min_os.is_a?(MacOSVersion)
|
||||
min_os.strip_patch
|
||||
else
|
||||
MacOSVersion.new(min_os).strip_patch
|
||||
end
|
||||
|
||||
# Big Sur is sometimes identified as 10.16, so we override it to the
|
||||
# expected macOS version (11).
|
||||
min_os = MacOSVersion.new("11") if min_os == "10.16"
|
||||
|
||||
min_os
|
||||
rescue MacOSVersion::Error
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
@@ -562,107 +686,6 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
include_manual_installers: T::Boolean,
|
||||
_block: T.nilable(T.proc.params(
|
||||
arg0: T::Array[T.any(Artifact::Installer, Artifact::Pkg, Artifact::Relocated)],
|
||||
arg1: Pathname,
|
||||
).void),
|
||||
).void
|
||||
}
|
||||
def extract_artifacts(include_manual_installers: false, &_block)
|
||||
return unless online?
|
||||
return if (download = self.download).nil?
|
||||
|
||||
artifacts = cask.artifacts.select do |artifact|
|
||||
artifact.is_a?(Artifact::Pkg) ||
|
||||
artifact.is_a?(Artifact::App) ||
|
||||
artifact.is_a?(Artifact::Binary) ||
|
||||
(include_manual_installers &&
|
||||
artifact.is_a?(Artifact::Installer) &&
|
||||
artifact.manual_install &&
|
||||
[".app", ".pkg"].include?(artifact.path.extname.downcase))
|
||||
end
|
||||
|
||||
if @artifacts_extracted && @tmpdir
|
||||
yield artifacts, @tmpdir if block_given?
|
||||
return
|
||||
end
|
||||
|
||||
return if artifacts.empty?
|
||||
|
||||
@tmpdir ||= T.let(Pathname(Dir.mktmpdir("cask-audit", HOMEBREW_TEMP)), T.nilable(Pathname))
|
||||
|
||||
# Clean up tmp dir when @tmpdir object is destroyed
|
||||
ObjectSpace.define_finalizer(
|
||||
@tmpdir,
|
||||
proc { FileUtils.remove_entry(@tmpdir) },
|
||||
)
|
||||
|
||||
ohai "Downloading and extracting artifacts"
|
||||
|
||||
downloaded_path = download.fetch
|
||||
|
||||
primary_container = UnpackStrategy.detect(downloaded_path, type: @cask.container&.type, merge_xattrs: true)
|
||||
return if primary_container.nil?
|
||||
|
||||
# If the container has any dependencies we need to install them or unpacking will fail.
|
||||
if primary_container.dependencies.any?
|
||||
|
||||
install_options = {
|
||||
show_header: true,
|
||||
installed_on_request: false,
|
||||
verbose: false,
|
||||
}.compact
|
||||
|
||||
Homebrew::Install.perform_preinstall_checks_once
|
||||
formula_installers = primary_container.dependencies.filter_map do |dep|
|
||||
next unless dep.is_a?(Formula)
|
||||
next if dep.linked?
|
||||
|
||||
FormulaInstaller.new(
|
||||
dep,
|
||||
**install_options,
|
||||
)
|
||||
end
|
||||
valid_formula_installers = Homebrew::Install.fetch_formulae(formula_installers)
|
||||
|
||||
formula_installers.each do |fi|
|
||||
next unless valid_formula_installers.include?(fi)
|
||||
|
||||
fi.install
|
||||
fi.finish
|
||||
end
|
||||
end
|
||||
|
||||
# Extract the container to the temporary directory.
|
||||
primary_container.extract_nestedly(to: @tmpdir, basename: downloaded_path.basename, verbose: false)
|
||||
|
||||
if (nested_container = @cask.container&.nested)
|
||||
FileUtils.chmod_R "+rw", @tmpdir/nested_container, force: true, verbose: false
|
||||
UnpackStrategy.detect(@tmpdir/nested_container, merge_xattrs: true)
|
||||
.extract_nestedly(to: @tmpdir, verbose: false)
|
||||
end
|
||||
|
||||
# Propagate quarantine attributes from the downloaded file to extracted contents.
|
||||
# This is necessary because some extraction tools (like 7zr) don't preserve xattrs.
|
||||
if Quarantine.available? && Quarantine.detect(downloaded_path)
|
||||
Quarantine.propagate(from: downloaded_path, to: @tmpdir)
|
||||
end
|
||||
|
||||
# Process rename operations after extraction
|
||||
# Create a temporary installer to process renames in the audit directory
|
||||
temp_installer = Installer.new(@cask)
|
||||
temp_installer.process_rename_operations(target_dir: @tmpdir)
|
||||
|
||||
# Set the flag to indicate that extraction has occurred.
|
||||
@artifacts_extracted = T.let(true, T.nilable(TrueClass))
|
||||
|
||||
# Yield the artifacts and temp directory to the block if provided.
|
||||
yield artifacts, @tmpdir if block_given?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def audit_rosetta
|
||||
return if (url = cask.url).nil?
|
||||
@@ -955,26 +978,6 @@ module Cask
|
||||
normalize_min_os(min_os)
|
||||
end
|
||||
|
||||
sig { params(min_os: T.nilable(T.any(String, MacOSVersion))).returns(T.nilable(MacOSVersion)) }
|
||||
def normalize_min_os(min_os)
|
||||
return if min_os.nil?
|
||||
return if min_os.is_a?(String) && min_os.blank?
|
||||
|
||||
min_os = if min_os.is_a?(MacOSVersion)
|
||||
min_os.strip_patch
|
||||
else
|
||||
MacOSVersion.new(min_os).strip_patch
|
||||
end
|
||||
|
||||
# Big Sur is sometimes identified as 10.16, so we override it to the
|
||||
# expected macOS version (11).
|
||||
min_os = MacOSVersion.new("11") if min_os == "10.16"
|
||||
|
||||
min_os
|
||||
rescue MacOSVersion::Error
|
||||
nil
|
||||
end
|
||||
|
||||
sig { params(path: Pathname).returns(T.nilable(String)) }
|
||||
def get_plist_main_binary(path)
|
||||
return unless online?
|
||||
|
||||
@@ -102,8 +102,6 @@ module Cask
|
||||
errors
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(audit: T.nilable(Audit)).returns(T::Boolean) }
|
||||
def output_summary?(audit = nil)
|
||||
return true if @any_named_args
|
||||
@@ -113,6 +111,8 @@ module Cask
|
||||
audit.errors?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(languages: T::Array[String]).returns(::Cask::Audit) }
|
||||
def audit_languages(languages)
|
||||
original_config = cask.config
|
||||
|
||||
@@ -147,6 +147,9 @@ module Cask
|
||||
sig { returns(Pathname) }
|
||||
attr_reader :path
|
||||
|
||||
sig { params(from_installed_caskfile: T::Boolean).void }
|
||||
attr_writer :from_installed_caskfile
|
||||
|
||||
sig { params(path: T.any(Pathname, String), token: String).void }
|
||||
def initialize(path, token: T.unsafe(nil))
|
||||
super()
|
||||
|
||||
@@ -205,6 +205,13 @@ module Cask
|
||||
path.stat.gid == group.gid
|
||||
end
|
||||
|
||||
@expected_caskroom_group = T.let(nil, T.nilable(String))
|
||||
|
||||
class << self
|
||||
sig { params(expected_caskroom_group: T.nilable(String)).void }
|
||||
attr_writer :expected_caskroom_group
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def self.expected_caskroom_group
|
||||
"admin"
|
||||
|
||||
@@ -229,6 +229,9 @@ module Cask
|
||||
sig { override.returns(String) }
|
||||
def download_queue_type = "Cask"
|
||||
|
||||
sig { override.returns(String) }
|
||||
def download_name = cask.token
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
@@ -280,8 +283,5 @@ module Cask
|
||||
def cache
|
||||
Cache.path
|
||||
end
|
||||
|
||||
sig { override.returns(String) }
|
||||
def download_name = cask.token
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,6 +56,9 @@ module Homebrew
|
||||
sig { returns(T::Array[Subcommand]) }
|
||||
attr_reader :subcommands
|
||||
|
||||
sig { returns(T.nilable(Integer)) }
|
||||
attr_reader :min_named_args
|
||||
|
||||
sig { params(cmd_path: Pathname).returns(T.nilable(CLI::Parser)) }
|
||||
def self.from_cmd_path(cmd_path)
|
||||
cmd_args_method_name = Commands.args_method_name(cmd_path)
|
||||
|
||||
@@ -172,6 +172,68 @@ module Homebrew
|
||||
download_queue.shutdown
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask).returns(T::Array[Cask::Download]) }
|
||||
def cask_downloads(cask)
|
||||
ref = cask.reloadable_ref
|
||||
|
||||
if args.all_platforms? && cask.loaded_from_api?
|
||||
opoo "Cask #{cask} was loaded from the API; cannot fetch all operating system and " \
|
||||
"architecture variants. Set `HOMEBREW_NO_INSTALL_FROM_API=1` to fetch them all."
|
||||
end
|
||||
|
||||
# With `--all-platforms`, a cask without `on_system` blocks resolves
|
||||
# identically everywhere, so one combination covers the whole matrix.
|
||||
cask_combinations = args.os_arch_combinations
|
||||
cask_combinations = cask_combinations.first(1) if args.all_platforms? && !cask.on_system_blocks_exist?
|
||||
|
||||
downloads = T.let([], T::Array[Cask::Download])
|
||||
enqueued_urls = Set.new
|
||||
|
||||
cask_combinations.each do |os, arch|
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
loaded_cask = begin
|
||||
Cask::CaskLoader.load(ref)
|
||||
rescue Cask::CaskInvalidError, Cask::CaskUnreadableError
|
||||
raise unless cask.on_system_blocks_exist?
|
||||
end
|
||||
if loaded_cask.nil? || loaded_cask.depends_on.arch&.none? { |dep_arch| dep_arch[:type] == arch }
|
||||
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
|
||||
next
|
||||
end
|
||||
|
||||
languages = (loaded_cask.languages if args.all_platforms?)
|
||||
languages = [nil] if languages.blank?
|
||||
|
||||
languages.each do |language|
|
||||
localized_cask = loaded_cask
|
||||
if language
|
||||
# Reload per language: `Cask::Download` reads `sha256`/`url`
|
||||
# lazily, so each download needs its own cask instance.
|
||||
localized_cask = Cask::CaskLoader.load(ref)
|
||||
localized_cask.config = localized_cask.config.merge(
|
||||
Cask::Config.new(explicit: { languages: [language] }),
|
||||
)
|
||||
end
|
||||
|
||||
if localized_cask.url.nil? || localized_cask.sha256.nil?
|
||||
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
|
||||
next
|
||||
end
|
||||
|
||||
next unless enqueued_urls.add?(localized_cask.url.to_s)
|
||||
|
||||
downloads << Cask::Download.new(
|
||||
localized_cask,
|
||||
quarantine: true,
|
||||
require_sha: Homebrew::EnvConfig.cask_opts_require_sha?,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
downloads
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@@ -288,68 +350,6 @@ module Homebrew
|
||||
names
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask).returns(T::Array[Cask::Download]) }
|
||||
def cask_downloads(cask)
|
||||
ref = cask.reloadable_ref
|
||||
|
||||
if args.all_platforms? && cask.loaded_from_api?
|
||||
opoo "Cask #{cask} was loaded from the API; cannot fetch all operating system and " \
|
||||
"architecture variants. Set `HOMEBREW_NO_INSTALL_FROM_API=1` to fetch them all."
|
||||
end
|
||||
|
||||
# With `--all-platforms`, a cask without `on_system` blocks resolves
|
||||
# identically everywhere, so one combination covers the whole matrix.
|
||||
cask_combinations = args.os_arch_combinations
|
||||
cask_combinations = cask_combinations.first(1) if args.all_platforms? && !cask.on_system_blocks_exist?
|
||||
|
||||
downloads = T.let([], T::Array[Cask::Download])
|
||||
enqueued_urls = Set.new
|
||||
|
||||
cask_combinations.each do |os, arch|
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
loaded_cask = begin
|
||||
Cask::CaskLoader.load(ref)
|
||||
rescue Cask::CaskInvalidError, Cask::CaskUnreadableError
|
||||
raise unless cask.on_system_blocks_exist?
|
||||
end
|
||||
if loaded_cask.nil? || loaded_cask.depends_on.arch&.none? { |dep_arch| dep_arch[:type] == arch }
|
||||
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
|
||||
next
|
||||
end
|
||||
|
||||
languages = (loaded_cask.languages if args.all_platforms?)
|
||||
languages = [nil] if languages.blank?
|
||||
|
||||
languages.each do |language|
|
||||
localized_cask = loaded_cask
|
||||
if language
|
||||
# Reload per language: `Cask::Download` reads `sha256`/`url`
|
||||
# lazily, so each download needs its own cask instance.
|
||||
localized_cask = Cask::CaskLoader.load(ref)
|
||||
localized_cask.config = localized_cask.config.merge(
|
||||
Cask::Config.new(explicit: { languages: [language] }),
|
||||
)
|
||||
end
|
||||
|
||||
if localized_cask.url.nil? || localized_cask.sha256.nil?
|
||||
opoo "Cask #{cask} is not supported on os #{os} and arch #{arch}"
|
||||
next
|
||||
end
|
||||
|
||||
next unless enqueued_urls.add?(localized_cask.url.to_s)
|
||||
|
||||
downloads << Cask::Download.new(
|
||||
localized_cask,
|
||||
quarantine: true,
|
||||
require_sha: Homebrew::EnvConfig.cask_opts_require_sha?,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
downloads
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def retries
|
||||
@retries ||= T.let(args.retry? ? FETCH_MAX_TRIES : 1, T.nilable(Integer))
|
||||
|
||||
+166
-166
@@ -349,39 +349,6 @@ module Homebrew
|
||||
private_class_method :formula_metadata_lines, :formatted_time, :pin_path_mtime,
|
||||
:formula_installs_from_source?, :cask_requirements_lines
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def print_statistics
|
||||
return unless HOMEBREW_CELLAR.exist?
|
||||
|
||||
count = Formula.racks.length
|
||||
puts "#{Utils.pluralize("keg", count, include_count: true)}, #{HOMEBREW_CELLAR.dup.abv}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def print_analytics
|
||||
if args.no_named?
|
||||
Utils::Analytics.output(args:)
|
||||
return
|
||||
end
|
||||
|
||||
args.named.to_formulae_and_casks_and_unavailable.each_with_index do |obj, i|
|
||||
puts unless i.zero?
|
||||
|
||||
case obj
|
||||
when Formula
|
||||
Utils::Analytics.formula_output(obj, args:) if obj.core_formula?
|
||||
when Cask::Cask
|
||||
Utils::Analytics.cask_output(obj, args:) if obj.tap&.core_cask_tap?
|
||||
when FormulaOrCaskUnavailableError
|
||||
Utils::Analytics.output(filter: obj.name, args:)
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(quiet: T::Boolean).void }
|
||||
def print_info(quiet: false)
|
||||
objects = args.named.to_formulae_and_casks_and_unavailable(uniq: false)
|
||||
@@ -411,25 +378,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(resolved: T::Array[[T.untyped, T.nilable(Tap)]]).returns(T::Array[[T.untyped, T.nilable(Tap)]])
|
||||
}
|
||||
def unique_by_display_name(resolved)
|
||||
resolved.uniq do |obj, _shadowed_by|
|
||||
case obj
|
||||
when Formula, Cask::Cask then obj.full_name
|
||||
else obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, user_qualified: T::Boolean).returns([Formula, T.nilable(Tap)]) }
|
||||
def display_resolution(formula, user_qualified:)
|
||||
return [formula, nil] if user_qualified
|
||||
|
||||
installed_resolution(formula)
|
||||
end
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), qualified_inputs: T::Set[String]).returns(T::Boolean) }
|
||||
def formula_qualified_by_user?(formula_or_cask, qualified_inputs)
|
||||
return false if qualified_inputs.empty?
|
||||
@@ -441,24 +389,6 @@ module Homebrew
|
||||
names.any? { |n| qualified_inputs.include?(n) }
|
||||
end
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), quiet: T::Boolean, shadowed_by: T.nilable(Tap)).void }
|
||||
def info_formula_or_cask(formula_or_cask, quiet:, shadowed_by: nil)
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
if quiet
|
||||
info_formula_summary(formula_or_cask)
|
||||
else
|
||||
info_formula(formula_or_cask, shadowed_by:)
|
||||
end
|
||||
when Cask::Cask
|
||||
if quiet
|
||||
info_cask_summary(formula_or_cask)
|
||||
else
|
||||
info_cask(formula_or_cask)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns([Formula, T.nilable(Tap)]) }
|
||||
def installed_resolution(formula)
|
||||
keg = formula.installed_kegs.last
|
||||
@@ -472,95 +402,6 @@ module Homebrew
|
||||
[formula, nil]
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T.nilable(Formula)) }
|
||||
def shadowing_installed_formula(formula)
|
||||
installed_formula, shadowed_by = installed_resolution(formula)
|
||||
installed_formula if shadowed_by
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, qualified_inputs: T::Set[String]).returns(Formula) }
|
||||
def swap_to_installed_formula(formula, qualified_inputs)
|
||||
return formula if formula_qualified_by_user?(formula, qualified_inputs)
|
||||
|
||||
installed_resolution(formula).first
|
||||
end
|
||||
|
||||
sig { params(version: T.any(T::Boolean, String)).returns(Symbol) }
|
||||
def json_version(version)
|
||||
version_hash = {
|
||||
true => :default,
|
||||
"v1" => :v1,
|
||||
"v2" => :v2,
|
||||
}
|
||||
|
||||
raise UsageError, "invalid JSON version: #{version}" unless version_hash.include?(version)
|
||||
|
||||
version_hash[version]
|
||||
end
|
||||
|
||||
sig { params(json: T.any(T::Boolean, String), eval_all: T::Boolean).void }
|
||||
def print_json(json, eval_all)
|
||||
raise FormulaOrCaskUnspecifiedError if !(eval_all || args.installed?) && args.no_named?
|
||||
|
||||
qualified_inputs = args.named.select { |name| name.include?("/") }.to_set
|
||||
|
||||
json = case json_version(json)
|
||||
when :v1, :default
|
||||
raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask?
|
||||
|
||||
formulae = if eval_all
|
||||
Formula.all(eval_all:).sort
|
||||
elsif args.installed?
|
||||
Formula.installed.sort
|
||||
else
|
||||
args.named.to_formulae.map { |f| swap_to_installed_formula(f, qualified_inputs) }
|
||||
end
|
||||
|
||||
if args.variations?
|
||||
formulae.map(&:to_hash_with_variations)
|
||||
else
|
||||
formulae.map(&:to_hash)
|
||||
end
|
||||
when :v2
|
||||
formulae, casks = T.let(
|
||||
if eval_all
|
||||
formulae = [] if args.cask?
|
||||
formulae ||= Formula.all(eval_all:).sort
|
||||
casks = [] if args.formula?
|
||||
casks ||= Cask::Cask.all(eval_all:).sort_by(&:full_name)
|
||||
[formulae, casks]
|
||||
elsif args.installed?
|
||||
formulae = [] if args.cask?
|
||||
formulae ||= Formula.installed.sort
|
||||
casks = [] if args.formula?
|
||||
casks ||= Cask::Caskroom.casks.sort_by(&:full_name)
|
||||
[formulae, casks]
|
||||
else
|
||||
named_formulae, named_casks = T.cast(
|
||||
args.named.to_formulae_to_casks, [T::Array[Formula], T::Array[Cask::Cask]]
|
||||
)
|
||||
[named_formulae.map { |f| swap_to_installed_formula(f, qualified_inputs) }, named_casks]
|
||||
end, [T::Array[Formula], T::Array[Cask::Cask]]
|
||||
)
|
||||
|
||||
if args.variations?
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash_with_variations),
|
||||
"casks" => casks.map(&:to_hash_with_variations),
|
||||
}
|
||||
else
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash),
|
||||
"casks" => casks.map(&:to_h),
|
||||
}
|
||||
end
|
||||
else
|
||||
raise
|
||||
end
|
||||
|
||||
puts JSON.pretty_generate(json)
|
||||
end
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(String) }
|
||||
def github_info(formula_or_cask)
|
||||
tap = T.let(nil, T.nilable(Tap))
|
||||
@@ -594,13 +435,6 @@ module Homebrew
|
||||
github_remote_path(remote, path.to_s)
|
||||
end
|
||||
|
||||
sig { params(name: String, description: T.nilable(String), installed: T::Boolean).returns(String) }
|
||||
def info_summary_title(name, description, installed:)
|
||||
name = pretty_installed(name) if installed
|
||||
|
||||
"#{name}#{": #{description}" if description.present?}"
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).void }
|
||||
def info_formula_summary(formula)
|
||||
kegs = formula.installed_kegs
|
||||
@@ -830,6 +664,172 @@ module Homebrew
|
||||
Utils::Analytics.formula_output(formula, args:)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def print_statistics
|
||||
return unless HOMEBREW_CELLAR.exist?
|
||||
|
||||
count = Formula.racks.length
|
||||
puts "#{Utils.pluralize("keg", count, include_count: true)}, #{HOMEBREW_CELLAR.dup.abv}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def print_analytics
|
||||
if args.no_named?
|
||||
Utils::Analytics.output(args:)
|
||||
return
|
||||
end
|
||||
|
||||
args.named.to_formulae_and_casks_and_unavailable.each_with_index do |obj, i|
|
||||
puts unless i.zero?
|
||||
|
||||
case obj
|
||||
when Formula
|
||||
Utils::Analytics.formula_output(obj, args:) if obj.core_formula?
|
||||
when Cask::Cask
|
||||
Utils::Analytics.cask_output(obj, args:) if obj.tap&.core_cask_tap?
|
||||
when FormulaOrCaskUnavailableError
|
||||
Utils::Analytics.output(filter: obj.name, args:)
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(resolved: T::Array[[T.untyped, T.nilable(Tap)]]).returns(T::Array[[T.untyped, T.nilable(Tap)]])
|
||||
}
|
||||
def unique_by_display_name(resolved)
|
||||
resolved.uniq do |obj, _shadowed_by|
|
||||
case obj
|
||||
when Formula, Cask::Cask then obj.full_name
|
||||
else obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, user_qualified: T::Boolean).returns([Formula, T.nilable(Tap)]) }
|
||||
def display_resolution(formula, user_qualified:)
|
||||
return [formula, nil] if user_qualified
|
||||
|
||||
installed_resolution(formula)
|
||||
end
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), quiet: T::Boolean, shadowed_by: T.nilable(Tap)).void }
|
||||
def info_formula_or_cask(formula_or_cask, quiet:, shadowed_by: nil)
|
||||
case formula_or_cask
|
||||
when Formula
|
||||
if quiet
|
||||
info_formula_summary(formula_or_cask)
|
||||
else
|
||||
info_formula(formula_or_cask, shadowed_by:)
|
||||
end
|
||||
when Cask::Cask
|
||||
if quiet
|
||||
info_cask_summary(formula_or_cask)
|
||||
else
|
||||
info_cask(formula_or_cask)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T.nilable(Formula)) }
|
||||
def shadowing_installed_formula(formula)
|
||||
installed_formula, shadowed_by = installed_resolution(formula)
|
||||
installed_formula if shadowed_by
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, qualified_inputs: T::Set[String]).returns(Formula) }
|
||||
def swap_to_installed_formula(formula, qualified_inputs)
|
||||
return formula if formula_qualified_by_user?(formula, qualified_inputs)
|
||||
|
||||
installed_resolution(formula).first
|
||||
end
|
||||
|
||||
sig { params(version: T.any(T::Boolean, String)).returns(Symbol) }
|
||||
def json_version(version)
|
||||
version_hash = {
|
||||
true => :default,
|
||||
"v1" => :v1,
|
||||
"v2" => :v2,
|
||||
}
|
||||
|
||||
raise UsageError, "invalid JSON version: #{version}" unless version_hash.include?(version)
|
||||
|
||||
version_hash[version]
|
||||
end
|
||||
|
||||
sig { params(json: T.any(T::Boolean, String), eval_all: T::Boolean).void }
|
||||
def print_json(json, eval_all)
|
||||
raise FormulaOrCaskUnspecifiedError if !(eval_all || args.installed?) && args.no_named?
|
||||
|
||||
qualified_inputs = args.named.select { |name| name.include?("/") }.to_set
|
||||
|
||||
json = case json_version(json)
|
||||
when :v1, :default
|
||||
raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask?
|
||||
|
||||
formulae = if eval_all
|
||||
Formula.all(eval_all:).sort
|
||||
elsif args.installed?
|
||||
Formula.installed.sort
|
||||
else
|
||||
args.named.to_formulae.map { |f| swap_to_installed_formula(f, qualified_inputs) }
|
||||
end
|
||||
|
||||
if args.variations?
|
||||
formulae.map(&:to_hash_with_variations)
|
||||
else
|
||||
formulae.map(&:to_hash)
|
||||
end
|
||||
when :v2
|
||||
formulae, casks = T.let(
|
||||
if eval_all
|
||||
formulae = [] if args.cask?
|
||||
formulae ||= Formula.all(eval_all:).sort
|
||||
casks = [] if args.formula?
|
||||
casks ||= Cask::Cask.all(eval_all:).sort_by(&:full_name)
|
||||
[formulae, casks]
|
||||
elsif args.installed?
|
||||
formulae = [] if args.cask?
|
||||
formulae ||= Formula.installed.sort
|
||||
casks = [] if args.formula?
|
||||
casks ||= Cask::Caskroom.casks.sort_by(&:full_name)
|
||||
[formulae, casks]
|
||||
else
|
||||
named_formulae, named_casks = T.cast(
|
||||
args.named.to_formulae_to_casks, [T::Array[Formula], T::Array[Cask::Cask]]
|
||||
)
|
||||
[named_formulae.map { |f| swap_to_installed_formula(f, qualified_inputs) }, named_casks]
|
||||
end, [T::Array[Formula], T::Array[Cask::Cask]]
|
||||
)
|
||||
|
||||
if args.variations?
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash_with_variations),
|
||||
"casks" => casks.map(&:to_hash_with_variations),
|
||||
}
|
||||
else
|
||||
{
|
||||
"formulae" => formulae.map(&:to_hash),
|
||||
"casks" => casks.map(&:to_h),
|
||||
}
|
||||
end
|
||||
else
|
||||
raise
|
||||
end
|
||||
|
||||
puts JSON.pretty_generate(json)
|
||||
end
|
||||
|
||||
sig { params(name: String, description: T.nilable(String), installed: T::Boolean).returns(String) }
|
||||
def info_summary_title(name, description, installed:)
|
||||
name = pretty_installed(name) if installed
|
||||
|
||||
"#{name}#{": #{description}" if description.present?}"
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, verbose: T::Boolean).returns(T::Array[String]) }
|
||||
def installed_section_lines(formula, verbose: false)
|
||||
siblings = formula.versioned_formulae
|
||||
|
||||
@@ -90,6 +90,31 @@ module Homebrew
|
||||
Homebrew.failed = args.named.present? && outdated.present?
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formulae_or_casks: T::Array[T.any(Formula, Cask::Cask)]).returns(T::Array[T.any(Formula, Cask::Cask)])
|
||||
}
|
||||
def select_outdated(formulae_or_casks)
|
||||
formulae_or_casks.select do |formula_or_cask|
|
||||
if formula_or_cask.is_a?(Formula)
|
||||
if minimum_version.present?
|
||||
formula_outdated_kegs(formula_or_cask).present?
|
||||
else
|
||||
formula_or_cask.outdated?(fetch_head: args.fetch_HEAD?)
|
||||
end
|
||||
else
|
||||
if minimum_version.present?
|
||||
next MinimumVersion.cask_installed_below?(formula_or_cask, T.must(minimum_version))
|
||||
end
|
||||
|
||||
cask_greedy = upgrade_greedy_cask?(args.greedy?, formula_or_cask)
|
||||
|
||||
formula_or_cask.outdated?(greedy: cask_greedy,
|
||||
greedy_latest: args.greedy_latest?,
|
||||
greedy_auto_updates: args.greedy_auto_updates?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(formulae_or_casks: T::Array[T.any(Formula, Cask::Cask)]).void }
|
||||
@@ -243,31 +268,6 @@ module Homebrew
|
||||
[select_outdated(formulae).sort, select_outdated(casks)]
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formulae_or_casks: T::Array[T.any(Formula, Cask::Cask)]).returns(T::Array[T.any(Formula, Cask::Cask)])
|
||||
}
|
||||
def select_outdated(formulae_or_casks)
|
||||
formulae_or_casks.select do |formula_or_cask|
|
||||
if formula_or_cask.is_a?(Formula)
|
||||
if minimum_version.present?
|
||||
formula_outdated_kegs(formula_or_cask).present?
|
||||
else
|
||||
formula_or_cask.outdated?(fetch_head: args.fetch_HEAD?)
|
||||
end
|
||||
else
|
||||
if minimum_version.present?
|
||||
next MinimumVersion.cask_installed_below?(formula_or_cask, T.must(minimum_version))
|
||||
end
|
||||
|
||||
cask_greedy = upgrade_greedy_cask?(args.greedy?, formula_or_cask)
|
||||
|
||||
formula_or_cask.outdated?(greedy: cask_greedy,
|
||||
greedy_latest: args.greedy_latest?,
|
||||
greedy_auto_updates: args.greedy_auto_updates?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Array[Keg]) }
|
||||
def formula_outdated_kegs(formula)
|
||||
MinimumVersion.formula_outdated_kegs(formula, minimum_version, fetch_head: args.fetch_HEAD?)
|
||||
|
||||
@@ -90,6 +90,21 @@ module Homebrew
|
||||
print_regex_help
|
||||
end
|
||||
|
||||
sig { params(query: String, found_matches: T::Boolean).void }
|
||||
def print_missing_formula_help(query, found_matches)
|
||||
return unless $stdout.tty?
|
||||
return if query.match?(Search::QUERY_REGEX)
|
||||
|
||||
reason = MissingFormula.reason(query, silent: true)
|
||||
return if reason.nil?
|
||||
|
||||
if found_matches
|
||||
puts
|
||||
puts "If you meant #{query.inspect} specifically:"
|
||||
end
|
||||
puts reason
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
@@ -154,21 +169,6 @@ module Homebrew
|
||||
|
||||
odie "No formulae or casks found for #{query.inspect}." if count.zero?
|
||||
end
|
||||
|
||||
sig { params(query: String, found_matches: T::Boolean).void }
|
||||
def print_missing_formula_help(query, found_matches)
|
||||
return unless $stdout.tty?
|
||||
return if query.match?(Search::QUERY_REGEX)
|
||||
|
||||
reason = MissingFormula.reason(query, silent: true)
|
||||
return if reason.nil?
|
||||
|
||||
if found_matches
|
||||
puts
|
||||
puts "If you meant #{query.inspect} specifically:"
|
||||
end
|
||||
puts reason
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,26 +45,6 @@ module Homebrew
|
||||
exec_browser(*repo_urls)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(formula: Formula).returns(T.nilable(String)) }
|
||||
def extract_repo_url(formula)
|
||||
urls_to_check = [
|
||||
formula.head&.url,
|
||||
formula.stable&.url,
|
||||
formula.homepage,
|
||||
]
|
||||
|
||||
urls_to_check.each do |url|
|
||||
next if url.nil?
|
||||
|
||||
repo_url = url_to_repo(url)
|
||||
return repo_url if repo_url
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
sig { params(url: String).returns(T.nilable(String)) }
|
||||
def url_to_repo(url)
|
||||
github_repo_url(url) ||
|
||||
@@ -207,6 +187,26 @@ module Homebrew
|
||||
rescue JSON::ParserError
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(formula: Formula).returns(T.nilable(String)) }
|
||||
def extract_repo_url(formula)
|
||||
urls_to_check = [
|
||||
formula.head&.url,
|
||||
formula.stable&.url,
|
||||
formula.homepage,
|
||||
]
|
||||
|
||||
urls_to_check.each do |url|
|
||||
next if url.nil?
|
||||
|
||||
repo_url = url_to_repo(url)
|
||||
return repo_url if repo_url
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,8 +40,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(taps: T::Array[Tap]).void }
|
||||
def print_tap_info(taps)
|
||||
if taps.none?
|
||||
@@ -95,9 +93,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
LISTING_LIMIT = 30
|
||||
private_constant :LISTING_LIMIT
|
||||
|
||||
sig { params(tap: Tap).void }
|
||||
def print_tap_listings(tap)
|
||||
commands = tap.command_files
|
||||
@@ -124,32 +119,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
tap: Tap,
|
||||
label: String,
|
||||
all: T::Array[String],
|
||||
installed: T::Array[String],
|
||||
min_width: Integer,
|
||||
block: T.proc.params(name: String).returns(String),
|
||||
).void
|
||||
}
|
||||
def print_section(tap, label, all, installed, min_width:, &block)
|
||||
return if all.none?
|
||||
|
||||
if all.size <= LISTING_LIMIT
|
||||
ohai label, Formatter.columns(all.map(&block), min_width:)
|
||||
elsif installed.any?
|
||||
ohai label
|
||||
opoo "Tap has more than #{LISTING_LIMIT} #{label.downcase}; showing only installed entries."
|
||||
puts Formatter.columns(installed.map(&block), min_width:)
|
||||
else
|
||||
ohai label
|
||||
opoo "Tap has more than #{LISTING_LIMIT} #{label.downcase} and none are installed."
|
||||
puts "See: #{tap.remote}" if tap.remote.present?
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, name: String, installed: T::Boolean).returns(String) }
|
||||
def decorate_formula(tap, name, installed:)
|
||||
formula = Formulary.factory("#{tap.name}/#{name}")
|
||||
@@ -187,6 +156,37 @@ module Homebrew
|
||||
|
||||
puts JSON.pretty_generate(hashes)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
LISTING_LIMIT = 30
|
||||
private_constant :LISTING_LIMIT
|
||||
|
||||
sig {
|
||||
params(
|
||||
tap: Tap,
|
||||
label: String,
|
||||
all: T::Array[String],
|
||||
installed: T::Array[String],
|
||||
min_width: Integer,
|
||||
block: T.proc.params(name: String).returns(String),
|
||||
).void
|
||||
}
|
||||
def print_section(tap, label, all, installed, min_width:, &block)
|
||||
return if all.none?
|
||||
|
||||
if all.size <= LISTING_LIMIT
|
||||
ohai label, Formatter.columns(all.map(&block), min_width:)
|
||||
elsif installed.any?
|
||||
ohai label
|
||||
opoo "Tap has more than #{LISTING_LIMIT} #{label.downcase}; showing only installed entries."
|
||||
puts Formatter.columns(installed.map(&block), min_width:)
|
||||
else
|
||||
ohai label
|
||||
opoo "Tap has more than #{LISTING_LIMIT} #{label.downcase} and none are installed."
|
||||
puts "See: #{tap.remote}" if tap.remote.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,6 +50,17 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def donation_message
|
||||
return if Settings.read("donationmessage") == "true"
|
||||
|
||||
ohai "Homebrew is run entirely by unpaid volunteers. Please consider donating:"
|
||||
puts " #{Formatter.url("https://github.com/Homebrew/brew#-donations")}\n\n"
|
||||
|
||||
# Consider the message possibly missed if not a TTY.
|
||||
Settings.write "donationmessage", true if $stdout.tty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
@@ -405,17 +416,6 @@ module Homebrew
|
||||
Utils::Analytics.messages_displayed! if $stdout.tty?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def donation_message
|
||||
return if Settings.read("donationmessage") == "true"
|
||||
|
||||
ohai "Homebrew is run entirely by unpaid volunteers. Please consider donating:"
|
||||
puts " #{Formatter.url("https://github.com/Homebrew/brew#-donations")}\n\n"
|
||||
|
||||
# Consider the message possibly missed if not a TTY.
|
||||
Settings.write "donationmessage", true if $stdout.tty?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def install_from_api_message
|
||||
return if Settings.read("installfromapimessage") == "true"
|
||||
|
||||
@@ -338,8 +338,6 @@ class Reporter
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(name: String, new_name: String, new_tap: Tap).returns(T::Boolean) }
|
||||
def ensure_trusted_tap_installed!(name, new_name, new_tap)
|
||||
return true if new_tap.installed?
|
||||
@@ -368,33 +366,6 @@ class Reporter
|
||||
true
|
||||
end
|
||||
|
||||
sig { returns(Tap) }
|
||||
attr_reader :tap
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :initial_revision
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :current_revision
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_names_txt
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_names_before_txt
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_dir_prefix
|
||||
|
||||
sig {
|
||||
params(api_names_txt: T.nilable(Pathname), api_names_before_txt: T.nilable(Pathname),
|
||||
api_dir_prefix: T.nilable(Pathname)).returns(T::Boolean)
|
||||
}
|
||||
def installed_from_api?(api_names_txt = @api_names_txt, api_names_before_txt = @api_names_before_txt,
|
||||
api_dir_prefix = @api_dir_prefix)
|
||||
!api_names_txt.nil? && !api_names_before_txt.nil? && !api_dir_prefix.nil?
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def diff
|
||||
@diff ||= T.let(nil, T.nilable(String))
|
||||
@@ -436,4 +407,33 @@ class Reporter
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(Tap) }
|
||||
attr_reader :tap
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :initial_revision
|
||||
|
||||
sig { returns(String) }
|
||||
attr_reader :current_revision
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_names_txt
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_names_before_txt
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :api_dir_prefix
|
||||
|
||||
sig {
|
||||
params(api_names_txt: T.nilable(Pathname), api_names_before_txt: T.nilable(Pathname),
|
||||
api_dir_prefix: T.nilable(Pathname)).returns(T::Boolean)
|
||||
}
|
||||
def installed_from_api?(api_names_txt = @api_names_txt, api_names_before_txt = @api_names_before_txt,
|
||||
api_dir_prefix = @api_dir_prefix)
|
||||
!api_names_txt.nil? && !api_names_before_txt.nil? && !api_dir_prefix.nil?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,6 +20,11 @@ class ReporterHub
|
||||
T.cast(@hash.fetch(key, []), T::Array[String])
|
||||
end
|
||||
|
||||
sig { returns(T::Array[[String, String]]) }
|
||||
def renamed_formulae
|
||||
T.cast(@hash.fetch(:R, []), T::Array[[String, String]])
|
||||
end
|
||||
|
||||
sig { params(reporter: Reporter, auto_update: T::Boolean).void }
|
||||
def add(reporter, auto_update: false)
|
||||
@reporters << reporter
|
||||
|
||||
@@ -336,51 +336,6 @@ module Homebrew
|
||||
show_final_upgrade_summary
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def minimum_version = args.minimum_version || args.min_version
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def formula_outdated?(formula)
|
||||
outdated = formula.outdated?(fetch_head: args.fetch_HEAD?)
|
||||
return false if outdated && fetched_head_formula_current?(formula)
|
||||
|
||||
version = minimum_version
|
||||
return outdated if version.blank?
|
||||
|
||||
outdated && MinimumVersion.formula_outdated_kegs(formula, version, fetch_head: args.fetch_HEAD?).present?
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def fetched_head_formula_current?(formula)
|
||||
return false unless args.fetch_HEAD?
|
||||
return false unless formula.head?
|
||||
return false unless formula.optlinked?
|
||||
|
||||
old_version = Keg.new(formula.opt_prefix).version
|
||||
return false unless old_version.head?
|
||||
|
||||
formula.latest_head_pkg_version(fetch_head: true).to_s == old_version.to_s
|
||||
end
|
||||
|
||||
sig { params(casks: T::Array[Cask::Cask], quiet: T::Boolean).returns(T::Array[Cask::Cask]) }
|
||||
def minimum_version_casks(casks, quiet: args.quiet?)
|
||||
version = minimum_version
|
||||
return casks if version.blank?
|
||||
|
||||
casks.select do |cask|
|
||||
if MinimumVersion.cask_installed_below?(cask, version)
|
||||
true
|
||||
else
|
||||
unless quiet
|
||||
opoo "Not upgrading #{cask.token}, the installed version is not below the minimum version #{version}"
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formulae: T::Array[Formula], show_upgrade_summary: T::Boolean,
|
||||
dry_run: T::Boolean).returns(T.nilable(FormulaeUpgradeContext))
|
||||
@@ -624,15 +579,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(title: String, items: T::Array[String]).void }
|
||||
def show_final_upgrade_summary_section(title, items)
|
||||
items = items.uniq
|
||||
return if items.empty?
|
||||
|
||||
oh1 title
|
||||
puts items.join("\n")
|
||||
end
|
||||
|
||||
sig { params(formulae: T::Array[Formula], include_sizes: T::Boolean).returns(T::Array[String]) }
|
||||
def formula_upgrade_descriptions(formulae, include_sizes: false)
|
||||
formulae.map do |formula|
|
||||
@@ -654,31 +600,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, old_version: PkgVersion).returns(String) }
|
||||
def formula_upgrade_display_version(formula, old_version)
|
||||
return formula.pkg_version.to_s if !old_version.head? || !formula.head?
|
||||
return formula.pkg_version.to_s if formula.pkg_version.to_s != old_version.to_s
|
||||
return "latest HEAD" unless args.fetch_HEAD?
|
||||
|
||||
latest_head_version = formula.latest_head_pkg_version(fetch_head: true)
|
||||
return "latest HEAD" if latest_head_version.to_s == old_version.to_s
|
||||
|
||||
latest_head_version.to_s
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(String) }
|
||||
def formula_upgrade_size(formula)
|
||||
return "" if args.build_from_source_formulae.include?(formula.name)
|
||||
|
||||
bottle = formula.bottle
|
||||
return "" unless bottle
|
||||
|
||||
bottle.fetch_tab(quiet: !args.debug?)
|
||||
return "" unless (download_size = bottle.bottle_size)
|
||||
|
||||
" (#{Formatter.disk_usage_readable(download_size.to_i)})"
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formulae: T::Array[Formula],
|
||||
@@ -919,6 +840,85 @@ module Homebrew
|
||||
ofail e
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def minimum_version = args.minimum_version || args.min_version
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def formula_outdated?(formula)
|
||||
outdated = formula.outdated?(fetch_head: args.fetch_HEAD?)
|
||||
return false if outdated && fetched_head_formula_current?(formula)
|
||||
|
||||
version = minimum_version
|
||||
return outdated if version.blank?
|
||||
|
||||
outdated && MinimumVersion.formula_outdated_kegs(formula, version, fetch_head: args.fetch_HEAD?).present?
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def fetched_head_formula_current?(formula)
|
||||
return false unless args.fetch_HEAD?
|
||||
return false unless formula.head?
|
||||
return false unless formula.optlinked?
|
||||
|
||||
old_version = Keg.new(formula.opt_prefix).version
|
||||
return false unless old_version.head?
|
||||
|
||||
formula.latest_head_pkg_version(fetch_head: true).to_s == old_version.to_s
|
||||
end
|
||||
|
||||
sig { params(casks: T::Array[Cask::Cask], quiet: T::Boolean).returns(T::Array[Cask::Cask]) }
|
||||
def minimum_version_casks(casks, quiet: args.quiet?)
|
||||
version = minimum_version
|
||||
return casks if version.blank?
|
||||
|
||||
casks.select do |cask|
|
||||
if MinimumVersion.cask_installed_below?(cask, version)
|
||||
true
|
||||
else
|
||||
unless quiet
|
||||
opoo "Not upgrading #{cask.token}, the installed version is not below the minimum version #{version}"
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(title: String, items: T::Array[String]).void }
|
||||
def show_final_upgrade_summary_section(title, items)
|
||||
items = items.uniq
|
||||
return if items.empty?
|
||||
|
||||
oh1 title
|
||||
puts items.join("\n")
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, old_version: PkgVersion).returns(String) }
|
||||
def formula_upgrade_display_version(formula, old_version)
|
||||
return formula.pkg_version.to_s if !old_version.head? || !formula.head?
|
||||
return formula.pkg_version.to_s if formula.pkg_version.to_s != old_version.to_s
|
||||
return "latest HEAD" unless args.fetch_HEAD?
|
||||
|
||||
latest_head_version = formula.latest_head_pkg_version(fetch_head: true)
|
||||
return "latest HEAD" if latest_head_version.to_s == old_version.to_s
|
||||
|
||||
latest_head_version.to_s
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(String) }
|
||||
def formula_upgrade_size(formula)
|
||||
return "" if args.build_from_source_formulae.include?(formula.name)
|
||||
|
||||
bottle = formula.bottle
|
||||
return "" unless bottle
|
||||
|
||||
bottle.fetch_tab(quiet: !args.debug?)
|
||||
return "" unless (download_size = bottle.bottle_size)
|
||||
|
||||
" (#{Formatter.disk_usage_readable(download_size.to_i)})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -163,6 +163,9 @@ class Dependency
|
||||
end
|
||||
|
||||
class << self
|
||||
sig { returns(T.nilable(T::Array[T.any(String, Symbol)])) }
|
||||
attr_reader :expand_stack
|
||||
|
||||
# Expand the dependencies of each dependent recursively, optionally yielding
|
||||
# `[dependent, dep]` pairs to allow callers to apply arbitrary filters to
|
||||
# the list.
|
||||
|
||||
@@ -216,17 +216,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(version: Cask::DSL::Version, cask: Cask::Cask).returns(Cask::DSL::Version) }
|
||||
def shortened_version(version, cask:)
|
||||
if version.before_comma == cask.version.before_comma
|
||||
version
|
||||
else
|
||||
version.before_comma
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, new_version: BumpVersionParser).returns(T::Array[[Symbol, Symbol]]) }
|
||||
def generate_system_options(cask, new_version)
|
||||
current_os = Homebrew::SimulateSystem.current_os
|
||||
@@ -375,6 +364,63 @@ module Homebrew
|
||||
contents
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
contents: String,
|
||||
name: Symbol,
|
||||
old_value: T.any(Numeric, String, Symbol),
|
||||
new_value: T.any(Numeric, String, Symbol),
|
||||
within: T.nilable(Symbol),
|
||||
).returns(String)
|
||||
}
|
||||
def replace_cask_stanza_value(contents, name, old_value, new_value, within: nil)
|
||||
return contents if old_value == new_value
|
||||
|
||||
cask_ast = Utils::AST::CaskAST.new(contents)
|
||||
replacement_count = cask_ast.replace_stanza_value(name, old_value, new_value, within:)
|
||||
if replacement_count.zero?
|
||||
# Treat an already-applied replacement as a successful no-op so the
|
||||
# per-(os, arch) loop in `replace_version_and_checksum` can yield the
|
||||
# same general version more than once without raising.
|
||||
return contents if cask_ast.replace_stanza_value(name, new_value, new_value, within:).positive?
|
||||
|
||||
raise "Could not find '#{name}' stanza with value #{old_value.inspect}!"
|
||||
end
|
||||
|
||||
cask_ast.process
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, new_version: BumpVersionParser).void }
|
||||
def check_throttle(cask, new_version:)
|
||||
return unless cask.tap
|
||||
|
||||
throttle_rate = cask.livecheck.throttle
|
||||
throttle_days = cask.livecheck.throttle_days
|
||||
return if throttle_rate.nil? && throttle_days.nil?
|
||||
|
||||
version = new_version.arm || new_version.intel || new_version.general
|
||||
return unless version.is_a?(Cask::DSL::Version)
|
||||
|
||||
return if Livecheck.throttle_allows_bump?(cask, version.to_s, throttle_rate:, throttle_days:)
|
||||
|
||||
throttle_items = []
|
||||
throttle_items << "#{throttle_rate} releases on multiples of #{throttle_rate}" if throttle_rate
|
||||
throttle_items << "#{throttle_days} #{Utils.pluralize("day", throttle_days)}" if throttle_days
|
||||
|
||||
odie "#{cask.token} should only be updated every #{throttle_items.join(" or ")}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(version: Cask::DSL::Version, cask: Cask::Cask).returns(Cask::DSL::Version) }
|
||||
def shortened_version(version, cask:)
|
||||
if version.before_comma == cask.version.before_comma
|
||||
version
|
||||
else
|
||||
version.before_comma
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
new_version: BumpVersionParser,
|
||||
@@ -434,52 +480,6 @@ module Homebrew
|
||||
nil
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
contents: String,
|
||||
name: Symbol,
|
||||
old_value: T.any(Numeric, String, Symbol),
|
||||
new_value: T.any(Numeric, String, Symbol),
|
||||
within: T.nilable(Symbol),
|
||||
).returns(String)
|
||||
}
|
||||
def replace_cask_stanza_value(contents, name, old_value, new_value, within: nil)
|
||||
return contents if old_value == new_value
|
||||
|
||||
cask_ast = Utils::AST::CaskAST.new(contents)
|
||||
replacement_count = cask_ast.replace_stanza_value(name, old_value, new_value, within:)
|
||||
if replacement_count.zero?
|
||||
# Treat an already-applied replacement as a successful no-op so the
|
||||
# per-(os, arch) loop in `replace_version_and_checksum` can yield the
|
||||
# same general version more than once without raising.
|
||||
return contents if cask_ast.replace_stanza_value(name, new_value, new_value, within:).positive?
|
||||
|
||||
raise "Could not find '#{name}' stanza with value #{old_value.inspect}!"
|
||||
end
|
||||
|
||||
cask_ast.process
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, new_version: BumpVersionParser).void }
|
||||
def check_throttle(cask, new_version:)
|
||||
return unless cask.tap
|
||||
|
||||
throttle_rate = cask.livecheck.throttle
|
||||
throttle_days = cask.livecheck.throttle_days
|
||||
return if throttle_rate.nil? && throttle_days.nil?
|
||||
|
||||
version = new_version.arm || new_version.intel || new_version.general
|
||||
return unless version.is_a?(Cask::DSL::Version)
|
||||
|
||||
return if Livecheck.throttle_allows_bump?(cask, version.to_s, throttle_rate:, throttle_days:)
|
||||
|
||||
throttle_items = []
|
||||
throttle_items << "#{throttle_rate} releases on multiples of #{throttle_rate}" if throttle_rate
|
||||
throttle_items << "#{throttle_days} #{Utils.pluralize("day", throttle_days)}" if throttle_days
|
||||
|
||||
odie "#{cask.token} should only be updated every #{throttle_items.join(" or ")}"
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, new_version: BumpVersionParser).void }
|
||||
def check_pull_requests(cask, new_version:)
|
||||
tap = cask.tap
|
||||
|
||||
@@ -504,6 +504,88 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, new_version: String).void }
|
||||
def check_throttle(formula, new_version)
|
||||
tap = formula.tap
|
||||
return if tap.nil?
|
||||
|
||||
throttle_rate = formula.livecheck.throttle
|
||||
throttle_days = formula.livecheck.throttle_days
|
||||
return if throttle_rate.nil? && throttle_days.nil?
|
||||
|
||||
return if Livecheck.throttle_allows_bump?(
|
||||
formula,
|
||||
new_version,
|
||||
throttle_rate: throttle_rate,
|
||||
throttle_days: throttle_days,
|
||||
)
|
||||
|
||||
throttle_items = []
|
||||
throttle_items << "#{throttle_rate} releases on multiples of #{throttle_rate}" if throttle_rate
|
||||
throttle_items << "#{throttle_days} #{Utils.pluralize("day", throttle_days)}" if throttle_days
|
||||
|
||||
odie "#{formula} should only be updated every #{throttle_items.join(" or ")}"
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
version: String,
|
||||
resource_versions: T.nilable(T::Hash[String, T::Hash[Symbol, T.nilable(String)]]),
|
||||
).returns(T::Hash[String, Symbol])
|
||||
}
|
||||
def update_matching_version_resources!(formula, version:, resource_versions: nil)
|
||||
resource_versions ||= {}
|
||||
formula.resources
|
||||
.select { |r| r.livecheck.formula == :parent && resource_versions[r.name].blank? }
|
||||
.to_h { |resource| [resource.name, update_resource_block!(formula, resource, version)] }
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
resource_versions: T::Hash[String, T::Hash[Symbol, T.nilable(String)]],
|
||||
).returns(T::Hash[String, Symbol])
|
||||
}
|
||||
def update_resources!(formula, resource_versions:)
|
||||
results = {}
|
||||
|
||||
formula.resources.each do |resource|
|
||||
version_data = resource_versions[resource.name]
|
||||
next if version_data.blank?
|
||||
|
||||
current_version = version_data[:current_version]
|
||||
latest_version = version_data[:latest_version]
|
||||
|
||||
if current_version.blank? || latest_version.blank?
|
||||
opoo "Could not determine versions for resource \"#{resource.name}\""
|
||||
results[resource.name] = :version_unknown
|
||||
next
|
||||
end
|
||||
|
||||
if current_version == latest_version
|
||||
results[resource.name] = :up_to_date
|
||||
next
|
||||
end
|
||||
|
||||
is_downgraded = Version.new(current_version) > Version.new(latest_version)
|
||||
|
||||
begin
|
||||
result = update_resource_block!(formula, resource, latest_version)
|
||||
results[resource.name] = if result == :success && is_downgraded
|
||||
:downgraded
|
||||
else
|
||||
result
|
||||
end
|
||||
rescue => e
|
||||
opoo "Failed to update resource \"#{resource.name}\": #{e}"
|
||||
results[resource.name] = :fetch_failed
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(url: String).returns(T.nilable(String)) }
|
||||
@@ -619,29 +701,6 @@ module Homebrew
|
||||
check_pull_requests(formula, tap_remote_repo, version:) unless args.write_only?
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, new_version: String).void }
|
||||
def check_throttle(formula, new_version)
|
||||
tap = formula.tap
|
||||
return if tap.nil?
|
||||
|
||||
throttle_rate = formula.livecheck.throttle
|
||||
throttle_days = formula.livecheck.throttle_days
|
||||
return if throttle_rate.nil? && throttle_days.nil?
|
||||
|
||||
return if Livecheck.throttle_allows_bump?(
|
||||
formula,
|
||||
new_version,
|
||||
throttle_rate: throttle_rate,
|
||||
throttle_days: throttle_days,
|
||||
)
|
||||
|
||||
throttle_items = []
|
||||
throttle_items << "#{throttle_rate} releases on multiples of #{throttle_rate}" if throttle_rate
|
||||
throttle_items << "#{throttle_days} #{Utils.pluralize("day", throttle_days)}" if throttle_days
|
||||
|
||||
odie "#{formula} should only be updated every #{throttle_items.join(" or ")}"
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, new_formula_version: Version).returns(T.nilable(T::Array[String])) }
|
||||
def alias_update_pair(formula, new_formula_version)
|
||||
versioned_alias = formula.aliases.grep(/^.*@\d+(\.\d+)?$/).first
|
||||
@@ -733,65 +792,6 @@ module Homebrew
|
||||
:success
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
version: String,
|
||||
resource_versions: T.nilable(T::Hash[String, T::Hash[Symbol, T.nilable(String)]]),
|
||||
).returns(T::Hash[String, Symbol])
|
||||
}
|
||||
def update_matching_version_resources!(formula, version:, resource_versions: nil)
|
||||
resource_versions ||= {}
|
||||
formula.resources
|
||||
.select { |r| r.livecheck.formula == :parent && resource_versions[r.name].blank? }
|
||||
.to_h { |resource| [resource.name, update_resource_block!(formula, resource, version)] }
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
resource_versions: T::Hash[String, T::Hash[Symbol, T.nilable(String)]],
|
||||
).returns(T::Hash[String, Symbol])
|
||||
}
|
||||
def update_resources!(formula, resource_versions:)
|
||||
results = {}
|
||||
|
||||
formula.resources.each do |resource|
|
||||
version_data = resource_versions[resource.name]
|
||||
next if version_data.blank?
|
||||
|
||||
current_version = version_data[:current_version]
|
||||
latest_version = version_data[:latest_version]
|
||||
|
||||
if current_version.blank? || latest_version.blank?
|
||||
opoo "Could not determine versions for resource \"#{resource.name}\""
|
||||
results[resource.name] = :version_unknown
|
||||
next
|
||||
end
|
||||
|
||||
if current_version == latest_version
|
||||
results[resource.name] = :up_to_date
|
||||
next
|
||||
end
|
||||
|
||||
is_downgraded = Version.new(current_version) > Version.new(latest_version)
|
||||
|
||||
begin
|
||||
result = update_resource_block!(formula, resource, latest_version)
|
||||
results[resource.name] = if result == :success && is_downgraded
|
||||
:downgraded
|
||||
else
|
||||
result
|
||||
end
|
||||
rescue => e
|
||||
opoo "Failed to update resource \"#{resource.name}\": #{e}"
|
||||
results[resource.name] = :fetch_failed
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formula: Formula, alias_rename: T.nilable(T::Array[String]),
|
||||
skip_synced_versions: T::Boolean).returns(T::Boolean)
|
||||
|
||||
+234
-234
@@ -184,62 +184,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T::Boolean) }
|
||||
def skip_repology?(formula_or_cask)
|
||||
return true unless args.repology?
|
||||
|
||||
(ENV["CI"].present? && args.open_pr? && formula_or_cask.livecheck_defined?) ||
|
||||
(formula_or_cask.is_a?(Formula) && formula_or_cask.versioned_formula?)
|
||||
end
|
||||
|
||||
sig { params(formulae_and_casks: T::Array[T.any(Formula, Cask::Cask)]).void }
|
||||
def handle_formulae_and_casks(formulae_and_casks)
|
||||
Livecheck.load_other_tap_strategies(formulae_and_casks)
|
||||
|
||||
ambiguous_casks = []
|
||||
if !args.formula? && !args.cask?
|
||||
ambiguous_casks = formulae_and_casks
|
||||
.group_by { |item| Livecheck.package_or_resource_name(item, full_name: true) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
.grep(Cask::Cask)
|
||||
end
|
||||
|
||||
ambiguous_names = []
|
||||
unless args.full_name?
|
||||
ambiguous_names = (formulae_and_casks - ambiguous_casks)
|
||||
.group_by { |item| Livecheck.package_or_resource_name(item) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
end
|
||||
|
||||
formulae_and_casks.each_with_index do |formula_or_cask, i|
|
||||
puts if i.positive?
|
||||
next if skip_ineligible_formulae!(formula_or_cask)
|
||||
|
||||
use_full_name = args.full_name? || ambiguous_names.include?(formula_or_cask)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
repository = if formula_or_cask.is_a?(Formula)
|
||||
Repology::HOMEBREW_CORE
|
||||
else
|
||||
Repology::HOMEBREW_CASK
|
||||
end
|
||||
|
||||
package_data = Repology.single_package_query(name, repository:) unless skip_repology?(formula_or_cask)
|
||||
|
||||
retrieve_and_display_info_and_open_pr(
|
||||
formula_or_cask,
|
||||
name,
|
||||
package_data&.values&.first || [],
|
||||
ambiguous_cask: ambiguous_casks.include?(formula_or_cask),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T::Boolean)
|
||||
}
|
||||
@@ -268,86 +212,6 @@ module Homebrew
|
||||
true
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
current: T.nilable(T.any(Version, Cask::DSL::Version)),
|
||||
).returns(T.any(Version, String))
|
||||
}
|
||||
def livecheck_result(formula_or_cask, current)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask)
|
||||
|
||||
referenced_formula_or_cask, = Livecheck.resolve_livecheck_reference(
|
||||
formula_or_cask,
|
||||
full_name: false,
|
||||
debug: false,
|
||||
)
|
||||
|
||||
# Check skip conditions for a referenced formula/cask
|
||||
if referenced_formula_or_cask
|
||||
skip_info = Livecheck::SkipConditions.referenced_skip_information(
|
||||
referenced_formula_or_cask,
|
||||
name,
|
||||
full_name: false,
|
||||
verbose: false,
|
||||
)
|
||||
end
|
||||
|
||||
skip_info ||= Livecheck::SkipConditions.skip_information(
|
||||
formula_or_cask,
|
||||
full_name: false,
|
||||
verbose: false,
|
||||
)
|
||||
|
||||
if skip_info.present?
|
||||
skip_status = skip_info[:status]
|
||||
skip_messages = skip_info[:messages]
|
||||
skip_message = skip_messages.join("; ") if skip_messages.present?
|
||||
return "error: #{skip_message}" if skip_status == "error" && skip_message
|
||||
|
||||
return "skipped - #{skip_message || skip_status}"
|
||||
end
|
||||
|
||||
version_info = Livecheck.latest_version(
|
||||
formula_or_cask,
|
||||
referenced_formula_or_cask:,
|
||||
json: true, full_name: false, verbose: true, debug: false
|
||||
)
|
||||
return "unable to get versions" if version_info.blank?
|
||||
|
||||
if !version_info.key?(:latest_throttled)
|
||||
version_with_cooldown(version_info, current) || Version.new(version_info[:latest])
|
||||
elsif version_info[:latest_throttled].nil?
|
||||
"unable to get throttled versions"
|
||||
else
|
||||
Version.new(version_info[:latest_throttled])
|
||||
end
|
||||
rescue => e
|
||||
"error: #{e}"
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
name: String,
|
||||
version: T.nilable(String),
|
||||
).returns T.nilable(T.any(T::Array[String], String))
|
||||
}
|
||||
def retrieve_pull_requests(formula_or_cask, name, version: nil)
|
||||
tap_remote_repo = formula_or_cask.tap&.remote_repository || formula_or_cask.tap&.full_name
|
||||
odie "unexpected nil tap remote repository" if tap_remote_repo.nil?
|
||||
|
||||
pull_requests = begin
|
||||
GitHub.fetch_pull_requests(name, tap_remote_repo, version:)
|
||||
rescue GitHub::API::ValidationFailedError => e
|
||||
odebug "Error fetching pull requests for #{formula_or_cask} #{name}: #{e}"
|
||||
nil
|
||||
end
|
||||
return if pull_requests.blank?
|
||||
|
||||
pull_requests.map { |pr| "#{pr["title"]} (#{Formatter.url(pr["html_url"])})" }.join(", ")
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
@@ -527,68 +391,6 @@ module Homebrew
|
||||
)
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
formula_latest_version: String,
|
||||
).returns(T::Array[ResourceVersionInfo])
|
||||
}
|
||||
def collect_resource_versions(formula, formula_latest_version)
|
||||
resource_versions = []
|
||||
|
||||
formula.resources.each do |resource|
|
||||
next unless resource.livecheck_defined?
|
||||
next if resource.livecheck.skip?
|
||||
|
||||
# Resources that reference :parent track the formula version directly
|
||||
if resource.livecheck.formula == :parent
|
||||
current = resource.version.to_s
|
||||
resource_versions << ResourceVersionInfo.new(
|
||||
name: resource.name,
|
||||
current_version: current,
|
||||
latest_version: formula_latest_version,
|
||||
outdated: Version.new(current) < Version.new(formula_latest_version),
|
||||
newer_than_upstream: Version.new(current) > Version.new(formula_latest_version),
|
||||
)
|
||||
next
|
||||
end
|
||||
|
||||
resource_info = Livecheck.resource_version(
|
||||
resource,
|
||||
formula_latest_version,
|
||||
json: true,
|
||||
full_name: false,
|
||||
debug: false,
|
||||
quiet: true,
|
||||
verbose: false,
|
||||
)
|
||||
|
||||
if resource_info.empty? || resource_info[:status] == "error"
|
||||
resource_versions << ResourceVersionInfo.new(
|
||||
name: resource.name,
|
||||
current_version: resource.version.to_s,
|
||||
latest_version: nil,
|
||||
outdated: false,
|
||||
newer_than_upstream: false,
|
||||
)
|
||||
next
|
||||
end
|
||||
|
||||
version_info = resource_info[:version]
|
||||
next if version_info.blank?
|
||||
|
||||
resource_versions << ResourceVersionInfo.new(
|
||||
name: resource.name,
|
||||
current_version: version_info[:current],
|
||||
latest_version: version_info[:latest],
|
||||
outdated: version_info[:outdated] == true,
|
||||
newer_than_upstream: version_info[:newer_than_upstream] == true,
|
||||
)
|
||||
end
|
||||
|
||||
resource_versions
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
@@ -876,42 +678,6 @@ module Homebrew
|
||||
value.match?(LIVECHECK_MESSAGE_REGEX)
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
new_version: T.nilable(T.any(Version, Cask::DSL::Version)),
|
||||
).returns(T::Array[String])
|
||||
}
|
||||
def synced_with(formula, new_version)
|
||||
synced_with = []
|
||||
|
||||
formula.tap&.synced_versions_formulae&.each do |synced_formulae|
|
||||
next unless synced_formulae.include?(formula.name)
|
||||
|
||||
synced_formulae.each do |synced_formula|
|
||||
synced_formula = Formulary.factory(synced_formula)
|
||||
next if synced_formula == formula.name
|
||||
|
||||
synced_with << synced_formula.name if synced_formula.version != new_version
|
||||
end
|
||||
end
|
||||
|
||||
synced_with
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, casks: T::Boolean).returns(T::Array[T.any(Formula, Cask::Cask)]) }
|
||||
def autobumped_formulae_or_casks(tap, casks: false)
|
||||
autobump_list = tap.autobump
|
||||
autobump_list.map do |name|
|
||||
qualified_name = "#{tap.name}/#{name}"
|
||||
if casks
|
||||
Cask::CaskLoader.load(qualified_name)
|
||||
else
|
||||
Formulary.factory(qualified_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Identifies the highest upstream version that has been released before
|
||||
# the cooldown interval.
|
||||
#
|
||||
@@ -1028,6 +794,240 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T::Boolean) }
|
||||
def skip_repology?(formula_or_cask)
|
||||
return true unless args.repology?
|
||||
|
||||
(ENV["CI"].present? && args.open_pr? && formula_or_cask.livecheck_defined?) ||
|
||||
(formula_or_cask.is_a?(Formula) && formula_or_cask.versioned_formula?)
|
||||
end
|
||||
|
||||
sig { params(formulae_and_casks: T::Array[T.any(Formula, Cask::Cask)]).void }
|
||||
def handle_formulae_and_casks(formulae_and_casks)
|
||||
Livecheck.load_other_tap_strategies(formulae_and_casks)
|
||||
|
||||
ambiguous_casks = []
|
||||
if !args.formula? && !args.cask?
|
||||
ambiguous_casks = formulae_and_casks
|
||||
.group_by { |item| Livecheck.package_or_resource_name(item, full_name: true) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
.grep(Cask::Cask)
|
||||
end
|
||||
|
||||
ambiguous_names = []
|
||||
unless args.full_name?
|
||||
ambiguous_names = (formulae_and_casks - ambiguous_casks)
|
||||
.group_by { |item| Livecheck.package_or_resource_name(item) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
end
|
||||
|
||||
formulae_and_casks.each_with_index do |formula_or_cask, i|
|
||||
puts if i.positive?
|
||||
next if skip_ineligible_formulae!(formula_or_cask)
|
||||
|
||||
use_full_name = args.full_name? || ambiguous_names.include?(formula_or_cask)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
repository = if formula_or_cask.is_a?(Formula)
|
||||
Repology::HOMEBREW_CORE
|
||||
else
|
||||
Repology::HOMEBREW_CASK
|
||||
end
|
||||
|
||||
package_data = Repology.single_package_query(name, repository:) unless skip_repology?(formula_or_cask)
|
||||
|
||||
retrieve_and_display_info_and_open_pr(
|
||||
formula_or_cask,
|
||||
name,
|
||||
package_data&.values&.first || [],
|
||||
ambiguous_cask: ambiguous_casks.include?(formula_or_cask),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
current: T.nilable(T.any(Version, Cask::DSL::Version)),
|
||||
).returns(T.any(Version, String))
|
||||
}
|
||||
def livecheck_result(formula_or_cask, current)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask)
|
||||
|
||||
referenced_formula_or_cask, = Livecheck.resolve_livecheck_reference(
|
||||
formula_or_cask,
|
||||
full_name: false,
|
||||
debug: false,
|
||||
)
|
||||
|
||||
# Check skip conditions for a referenced formula/cask
|
||||
if referenced_formula_or_cask
|
||||
skip_info = Livecheck::SkipConditions.referenced_skip_information(
|
||||
referenced_formula_or_cask,
|
||||
name,
|
||||
full_name: false,
|
||||
verbose: false,
|
||||
)
|
||||
end
|
||||
|
||||
skip_info ||= Livecheck::SkipConditions.skip_information(
|
||||
formula_or_cask,
|
||||
full_name: false,
|
||||
verbose: false,
|
||||
)
|
||||
|
||||
if skip_info.present?
|
||||
skip_status = skip_info[:status]
|
||||
skip_messages = skip_info[:messages]
|
||||
skip_message = skip_messages.join("; ") if skip_messages.present?
|
||||
return "error: #{skip_message}" if skip_status == "error" && skip_message
|
||||
|
||||
return "skipped - #{skip_message || skip_status}"
|
||||
end
|
||||
|
||||
version_info = Livecheck.latest_version(
|
||||
formula_or_cask,
|
||||
referenced_formula_or_cask:,
|
||||
json: true, full_name: false, verbose: true, debug: false
|
||||
)
|
||||
return "unable to get versions" if version_info.blank?
|
||||
|
||||
if !version_info.key?(:latest_throttled)
|
||||
version_with_cooldown(version_info, current) || Version.new(version_info[:latest])
|
||||
elsif version_info[:latest_throttled].nil?
|
||||
"unable to get throttled versions"
|
||||
else
|
||||
Version.new(version_info[:latest_throttled])
|
||||
end
|
||||
rescue => e
|
||||
"error: #{e}"
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
name: String,
|
||||
version: T.nilable(String),
|
||||
).returns T.nilable(T.any(T::Array[String], String))
|
||||
}
|
||||
def retrieve_pull_requests(formula_or_cask, name, version: nil)
|
||||
tap_remote_repo = formula_or_cask.tap&.remote_repository || formula_or_cask.tap&.full_name
|
||||
odie "unexpected nil tap remote repository" if tap_remote_repo.nil?
|
||||
|
||||
pull_requests = begin
|
||||
GitHub.fetch_pull_requests(name, tap_remote_repo, version:)
|
||||
rescue GitHub::API::ValidationFailedError => e
|
||||
odebug "Error fetching pull requests for #{formula_or_cask} #{name}: #{e}"
|
||||
nil
|
||||
end
|
||||
return if pull_requests.blank?
|
||||
|
||||
pull_requests.map { |pr| "#{pr["title"]} (#{Formatter.url(pr["html_url"])})" }.join(", ")
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
formula_latest_version: String,
|
||||
).returns(T::Array[ResourceVersionInfo])
|
||||
}
|
||||
def collect_resource_versions(formula, formula_latest_version)
|
||||
resource_versions = []
|
||||
|
||||
formula.resources.each do |resource|
|
||||
next unless resource.livecheck_defined?
|
||||
next if resource.livecheck.skip?
|
||||
|
||||
# Resources that reference :parent track the formula version directly
|
||||
if resource.livecheck.formula == :parent
|
||||
current = resource.version.to_s
|
||||
resource_versions << ResourceVersionInfo.new(
|
||||
name: resource.name,
|
||||
current_version: current,
|
||||
latest_version: formula_latest_version,
|
||||
outdated: Version.new(current) < Version.new(formula_latest_version),
|
||||
newer_than_upstream: Version.new(current) > Version.new(formula_latest_version),
|
||||
)
|
||||
next
|
||||
end
|
||||
|
||||
resource_info = Livecheck.resource_version(
|
||||
resource,
|
||||
formula_latest_version,
|
||||
json: true,
|
||||
full_name: false,
|
||||
debug: false,
|
||||
quiet: true,
|
||||
verbose: false,
|
||||
)
|
||||
|
||||
if resource_info.empty? || resource_info[:status] == "error"
|
||||
resource_versions << ResourceVersionInfo.new(
|
||||
name: resource.name,
|
||||
current_version: resource.version.to_s,
|
||||
latest_version: nil,
|
||||
outdated: false,
|
||||
newer_than_upstream: false,
|
||||
)
|
||||
next
|
||||
end
|
||||
|
||||
version_info = resource_info[:version]
|
||||
next if version_info.blank?
|
||||
|
||||
resource_versions << ResourceVersionInfo.new(
|
||||
name: resource.name,
|
||||
current_version: version_info[:current],
|
||||
latest_version: version_info[:latest],
|
||||
outdated: version_info[:outdated] == true,
|
||||
newer_than_upstream: version_info[:newer_than_upstream] == true,
|
||||
)
|
||||
end
|
||||
|
||||
resource_versions
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
new_version: T.nilable(T.any(Version, Cask::DSL::Version)),
|
||||
).returns(T::Array[String])
|
||||
}
|
||||
def synced_with(formula, new_version)
|
||||
synced_with = []
|
||||
|
||||
formula.tap&.synced_versions_formulae&.each do |synced_formulae|
|
||||
next unless synced_formulae.include?(formula.name)
|
||||
|
||||
synced_formulae.each do |synced_formula|
|
||||
synced_formula = Formulary.factory(synced_formula)
|
||||
next if synced_formula == formula.name
|
||||
|
||||
synced_with << synced_formula.name if synced_formula.version != new_version
|
||||
end
|
||||
end
|
||||
|
||||
synced_with
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, casks: T::Boolean).returns(T::Array[T.any(Formula, Cask::Cask)]) }
|
||||
def autobumped_formulae_or_casks(tap, casks: false)
|
||||
autobump_list = tap.autobump
|
||||
autobump_list.map do |name|
|
||||
qualified_name = "#{tap.name}/#{name}"
|
||||
if casks
|
||||
Cask::CaskLoader.load(qualified_name)
|
||||
else
|
||||
Formulary.factory(qualified_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -227,8 +227,6 @@ module Homebrew
|
||||
puts generate_csv(grand_totals)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
params(repository_refs: T::Hash[String, [Pathname, String]], to: String)
|
||||
.returns([T::Hash[String, String], T::Hash[String, T::Boolean], T::Hash[String, T.nilable(String)]])
|
||||
@@ -268,118 +266,6 @@ module Homebrew
|
||||
[user_names, lead_maintainers, maintainer_since_dates]
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
results: T::Hash[String, T::Hash[String, T::Hash[Symbol, Integer]]],
|
||||
grand_totals: T::Hash[String, T::Hash[Symbol, Integer]],
|
||||
user_names: T::Hash[String, String],
|
||||
lead_maintainers: T::Hash[String, T::Boolean],
|
||||
maintainer_since_dates: T::Hash[String, T.nilable(String)],
|
||||
to: String,
|
||||
).returns(String)
|
||||
}
|
||||
def generate_maintainer_report_csv(results, grand_totals, user_names, lead_maintainers, maintainer_since_dates,
|
||||
to)
|
||||
require "csv"
|
||||
|
||||
rows = results.sort_by do |user, _|
|
||||
qualifying_total = contribution_count(grand_totals.fetch(user).slice(*QUALIFYING_CONTRIBUTION_TYPES))
|
||||
[-qualifying_total, user.downcase]
|
||||
end
|
||||
rows.map! do |user, user_repositories|
|
||||
grand_total = grand_totals.fetch(user)
|
||||
repository_qualifying_totals = user_repositories.transform_values do |counts|
|
||||
contribution_count(counts.slice(*QUALIFYING_CONTRIBUTION_TYPES))
|
||||
end
|
||||
qualifying_total = contribution_count(grand_total.slice(*QUALIFYING_CONTRIBUTION_TYPES))
|
||||
maintainer_activity_met = qualifying_total >= MAINTAINER_ACTIVITY_THRESHOLD
|
||||
maintainer_since = maintainer_since_dates.fetch(user)
|
||||
maintainer_since_date = Date.iso8601(maintainer_since) if maintainer_since
|
||||
period_end = Date.iso8601(to)
|
||||
lead_maintainer = lead_maintainers.key?(user.downcase)
|
||||
lead_activity_met = lead_activity_met?(user_repositories)
|
||||
new_role = if lead_activity_met &&
|
||||
(lead_maintainer ||
|
||||
(maintainer_since_date && maintainer_since_date <= period_end.prev_year(3)))
|
||||
"Lead Maintainer"
|
||||
elsif maintainer_activity_met
|
||||
"Maintainer"
|
||||
else
|
||||
"None"
|
||||
end
|
||||
|
||||
capped = grand_total.fetch(:merged_pr_author_hit_cap, 0).positive? ||
|
||||
grand_total.fetch(:approved_pr_review_hit_cap, 0).positive?
|
||||
capped ||= user_repositories.any? do |_, counts|
|
||||
counts.fetch(:approved_pr_review) >= MAX_PR_SEARCH ||
|
||||
counts.except(:approved_pr_review).values.any? do |count|
|
||||
count >= MAX_CONTRIBUTIONS
|
||||
end
|
||||
end
|
||||
|
||||
[
|
||||
user,
|
||||
user_names.fetch(user),
|
||||
maintainer_since,
|
||||
maintainer_since_date ? [(period_end - maintainer_since_date).to_i, 0].max : nil,
|
||||
*PRIMARY_REPOS.flat_map do |repository|
|
||||
counts = user_repositories.fetch(repository)
|
||||
[*counts.values_at(*CONTRIBUTION_TYPES.keys), repository_qualifying_totals.fetch(repository)]
|
||||
end,
|
||||
qualifying_total,
|
||||
maintainer_activity_met,
|
||||
lead_activity_met,
|
||||
capped,
|
||||
lead_maintainer ? "Lead Maintainer" : "Maintainer",
|
||||
new_role,
|
||||
]
|
||||
end
|
||||
CSV.generate do |csv|
|
||||
csv << [
|
||||
"username", "name", "since", "tenure days",
|
||||
*PRIMARY_REPOS.flat_map do |repository|
|
||||
repository = repository.delete_prefix("Homebrew/").delete_prefix("homebrew-")
|
||||
[
|
||||
"#{repository} authored", "#{repository} merged", "#{repository} PRs",
|
||||
"#{repository} reviews", "#{repository} coauthored", "#{repository} total"
|
||||
]
|
||||
end,
|
||||
"total", "maintainer met", "lead met", "capped", "role", "new role"
|
||||
]
|
||||
rows.each { |row| csv << row }
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(repositories: T::Array[String], required: T::Boolean).returns(T::Hash[String, [Pathname, String]]) }
|
||||
def prepare_contribution_repositories(repositories, required:)
|
||||
require "utils/git"
|
||||
|
||||
repository_refs = T.let({}, T::Hash[String, [Pathname, String]])
|
||||
repositories.each do |repository|
|
||||
repository_path, tap = repository_path_and_tap(repository)
|
||||
if repository_path && tap && !repository_path.exist?
|
||||
opoo "Repository #{repository} not yet tapped! Tapping it now..."
|
||||
tap.install(force: true)
|
||||
end
|
||||
unless repository_path&.exist?
|
||||
odie "Could not find a local Git repository for #{repository}." if required
|
||||
next
|
||||
end
|
||||
|
||||
$stderr.puts "Fetching latest commits for #{repository}..."
|
||||
system_command!(Utils::Git.git,
|
||||
args: ["-C", repository_path, "fetch", "--quiet", "--force", "origin",
|
||||
"+refs/heads/*:refs/remotes/origin/*"],
|
||||
print_stderr: false)
|
||||
system_command!(Utils::Git.git,
|
||||
args: ["-C", repository_path, "remote", "set-head", "origin", "--auto"],
|
||||
print_stderr: false)
|
||||
|
||||
repository_refs[repository] = [repository_path, "origin/HEAD"]
|
||||
end
|
||||
repository_refs
|
||||
end
|
||||
|
||||
sig { params(repository_path: Pathname, ref: String, user: String, name: String).returns(T.nilable(String)) }
|
||||
def maintainer_since(repository_path, ref, user, name)
|
||||
require "utils/git"
|
||||
@@ -406,12 +292,6 @@ module Homebrew
|
||||
nil
|
||||
end
|
||||
|
||||
sig { params(readme: String, user: String, name: String).returns(T::Boolean) }
|
||||
def readme_mentions?(readme, user, name)
|
||||
readme = readme.dup.force_encoding(Encoding::UTF_8)
|
||||
readme.include?("https://github.com/#{user}") || readme.include?(name)
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
organisation: String,
|
||||
@@ -559,38 +439,6 @@ module Homebrew
|
||||
results
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
pull_request: T::Hash[String, T.untyped],
|
||||
authored_pull_requests: T::Set[String],
|
||||
merged_pull_requests: T::Set[String],
|
||||
).void
|
||||
}
|
||||
def add_merged_pull_request_id(pull_request, authored_pull_requests, merged_pull_requests)
|
||||
number = pull_request["number"]
|
||||
return unless number.is_a?(Integer)
|
||||
|
||||
pull_request_id = number.to_s
|
||||
authored_pull_requests << pull_request_id
|
||||
merged_pull_requests << pull_request_id
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
counts: T::Hash[Symbol, Integer],
|
||||
authored_pull_requests: T::Set[String],
|
||||
merged_pull_requests: T::Set[String],
|
||||
).void
|
||||
}
|
||||
def update_merged_pull_request_counts(counts, authored_pull_requests, merged_pull_requests)
|
||||
unless authored_pull_requests.empty?
|
||||
counts[:merged_pr_author] = [authored_pull_requests.length, MAX_CONTRIBUTIONS].min
|
||||
end
|
||||
return if merged_pull_requests.empty?
|
||||
|
||||
counts[:merged_pr] = [merged_pull_requests.length, MAX_CONTRIBUTIONS].min
|
||||
end
|
||||
|
||||
sig { params(user: String, to: String).returns(T.nilable(String)) }
|
||||
def github_username_for(user, to:)
|
||||
return user unless user.include?("@")
|
||||
@@ -731,6 +579,158 @@ module Homebrew
|
||||
counts
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
params(
|
||||
results: T::Hash[String, T::Hash[String, T::Hash[Symbol, Integer]]],
|
||||
grand_totals: T::Hash[String, T::Hash[Symbol, Integer]],
|
||||
user_names: T::Hash[String, String],
|
||||
lead_maintainers: T::Hash[String, T::Boolean],
|
||||
maintainer_since_dates: T::Hash[String, T.nilable(String)],
|
||||
to: String,
|
||||
).returns(String)
|
||||
}
|
||||
def generate_maintainer_report_csv(results, grand_totals, user_names, lead_maintainers, maintainer_since_dates,
|
||||
to)
|
||||
require "csv"
|
||||
|
||||
rows = results.sort_by do |user, _|
|
||||
qualifying_total = contribution_count(grand_totals.fetch(user).slice(*QUALIFYING_CONTRIBUTION_TYPES))
|
||||
[-qualifying_total, user.downcase]
|
||||
end
|
||||
rows.map! do |user, user_repositories|
|
||||
grand_total = grand_totals.fetch(user)
|
||||
repository_qualifying_totals = user_repositories.transform_values do |counts|
|
||||
contribution_count(counts.slice(*QUALIFYING_CONTRIBUTION_TYPES))
|
||||
end
|
||||
qualifying_total = contribution_count(grand_total.slice(*QUALIFYING_CONTRIBUTION_TYPES))
|
||||
maintainer_activity_met = qualifying_total >= MAINTAINER_ACTIVITY_THRESHOLD
|
||||
maintainer_since = maintainer_since_dates.fetch(user)
|
||||
maintainer_since_date = Date.iso8601(maintainer_since) if maintainer_since
|
||||
period_end = Date.iso8601(to)
|
||||
lead_maintainer = lead_maintainers.key?(user.downcase)
|
||||
lead_activity_met = lead_activity_met?(user_repositories)
|
||||
new_role = if lead_activity_met &&
|
||||
(lead_maintainer ||
|
||||
(maintainer_since_date && maintainer_since_date <= period_end.prev_year(3)))
|
||||
"Lead Maintainer"
|
||||
elsif maintainer_activity_met
|
||||
"Maintainer"
|
||||
else
|
||||
"None"
|
||||
end
|
||||
|
||||
capped = grand_total.fetch(:merged_pr_author_hit_cap, 0).positive? ||
|
||||
grand_total.fetch(:approved_pr_review_hit_cap, 0).positive?
|
||||
capped ||= user_repositories.any? do |_, counts|
|
||||
counts.fetch(:approved_pr_review) >= MAX_PR_SEARCH ||
|
||||
counts.except(:approved_pr_review).values.any? do |count|
|
||||
count >= MAX_CONTRIBUTIONS
|
||||
end
|
||||
end
|
||||
|
||||
[
|
||||
user,
|
||||
user_names.fetch(user),
|
||||
maintainer_since,
|
||||
maintainer_since_date ? [(period_end - maintainer_since_date).to_i, 0].max : nil,
|
||||
*PRIMARY_REPOS.flat_map do |repository|
|
||||
counts = user_repositories.fetch(repository)
|
||||
[*counts.values_at(*CONTRIBUTION_TYPES.keys), repository_qualifying_totals.fetch(repository)]
|
||||
end,
|
||||
qualifying_total,
|
||||
maintainer_activity_met,
|
||||
lead_activity_met,
|
||||
capped,
|
||||
lead_maintainer ? "Lead Maintainer" : "Maintainer",
|
||||
new_role,
|
||||
]
|
||||
end
|
||||
CSV.generate do |csv|
|
||||
csv << [
|
||||
"username", "name", "since", "tenure days",
|
||||
*PRIMARY_REPOS.flat_map do |repository|
|
||||
repository = repository.delete_prefix("Homebrew/").delete_prefix("homebrew-")
|
||||
[
|
||||
"#{repository} authored", "#{repository} merged", "#{repository} PRs",
|
||||
"#{repository} reviews", "#{repository} coauthored", "#{repository} total"
|
||||
]
|
||||
end,
|
||||
"total", "maintainer met", "lead met", "capped", "role", "new role"
|
||||
]
|
||||
rows.each { |row| csv << row }
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(repositories: T::Array[String], required: T::Boolean).returns(T::Hash[String, [Pathname, String]]) }
|
||||
def prepare_contribution_repositories(repositories, required:)
|
||||
require "utils/git"
|
||||
|
||||
repository_refs = T.let({}, T::Hash[String, [Pathname, String]])
|
||||
repositories.each do |repository|
|
||||
repository_path, tap = repository_path_and_tap(repository)
|
||||
if repository_path && tap && !repository_path.exist?
|
||||
opoo "Repository #{repository} not yet tapped! Tapping it now..."
|
||||
tap.install(force: true)
|
||||
end
|
||||
unless repository_path&.exist?
|
||||
odie "Could not find a local Git repository for #{repository}." if required
|
||||
next
|
||||
end
|
||||
|
||||
$stderr.puts "Fetching latest commits for #{repository}..."
|
||||
system_command!(Utils::Git.git,
|
||||
args: ["-C", repository_path, "fetch", "--quiet", "--force", "origin",
|
||||
"+refs/heads/*:refs/remotes/origin/*"],
|
||||
print_stderr: false)
|
||||
system_command!(Utils::Git.git,
|
||||
args: ["-C", repository_path, "remote", "set-head", "origin", "--auto"],
|
||||
print_stderr: false)
|
||||
|
||||
repository_refs[repository] = [repository_path, "origin/HEAD"]
|
||||
end
|
||||
repository_refs
|
||||
end
|
||||
|
||||
sig { params(readme: String, user: String, name: String).returns(T::Boolean) }
|
||||
def readme_mentions?(readme, user, name)
|
||||
readme = readme.dup.force_encoding(Encoding::UTF_8)
|
||||
readme.include?("https://github.com/#{user}") || readme.include?(name)
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
pull_request: T::Hash[String, T.untyped],
|
||||
authored_pull_requests: T::Set[String],
|
||||
merged_pull_requests: T::Set[String],
|
||||
).void
|
||||
}
|
||||
def add_merged_pull_request_id(pull_request, authored_pull_requests, merged_pull_requests)
|
||||
number = pull_request["number"]
|
||||
return unless number.is_a?(Integer)
|
||||
|
||||
pull_request_id = number.to_s
|
||||
authored_pull_requests << pull_request_id
|
||||
merged_pull_requests << pull_request_id
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
counts: T::Hash[Symbol, Integer],
|
||||
authored_pull_requests: T::Set[String],
|
||||
merged_pull_requests: T::Set[String],
|
||||
).void
|
||||
}
|
||||
def update_merged_pull_request_counts(counts, authored_pull_requests, merged_pull_requests)
|
||||
unless authored_pull_requests.empty?
|
||||
counts[:merged_pr_author] = [authored_pull_requests.length, MAX_CONTRIBUTIONS].min
|
||||
end
|
||||
return if merged_pull_requests.empty?
|
||||
|
||||
counts[:merged_pr] = [merged_pull_requests.length, MAX_CONTRIBUTIONS].min
|
||||
end
|
||||
|
||||
sig {
|
||||
params(name: String, email: String, identity_users: T::Hash[String, String]).returns(T.nilable(String))
|
||||
}
|
||||
|
||||
@@ -192,6 +192,28 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask).returns([T::Array[T::Hash[Symbol, T.any(Symbol, String)]], T::Boolean]) }
|
||||
def runners(cask:)
|
||||
filtered_runners = filter_runners(cask)
|
||||
|
||||
filtered_macos_found = filtered_runners.keys.any? do |runner|
|
||||
cask.to_hash_with_variations["variations"].key?(runner.fetch(:symbol).to_sym)
|
||||
end
|
||||
|
||||
if filtered_macos_found
|
||||
# If the cask varies on a MacOS version, test it on every possible macOS version.
|
||||
[filtered_runners.keys, true]
|
||||
else
|
||||
macos_runners, linux_runners = filtered_runners.partition do |runner, _|
|
||||
runner.fetch(:symbol) != :linux
|
||||
end
|
||||
selected_runners = macos_runners.group_by { |runner, _| runner.fetch(:arch) }.map do |_, runners|
|
||||
random_runner(runners.to_h)
|
||||
end + linux_runners.map(&:first)
|
||||
[selected_runners, false]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(cask: Cask::Cask, os: Symbol).returns(T::Array[Symbol]) }
|
||||
@@ -223,28 +245,6 @@ module Homebrew
|
||||
max_runner.first
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask).returns([T::Array[T::Hash[Symbol, T.any(Symbol, String)]], T::Boolean]) }
|
||||
def runners(cask:)
|
||||
filtered_runners = filter_runners(cask)
|
||||
|
||||
filtered_macos_found = filtered_runners.keys.any? do |runner|
|
||||
cask.to_hash_with_variations["variations"].key?(runner.fetch(:symbol).to_sym)
|
||||
end
|
||||
|
||||
if filtered_macos_found
|
||||
# If the cask varies on a MacOS version, test it on every possible macOS version.
|
||||
[filtered_runners.keys, true]
|
||||
else
|
||||
macos_runners, linux_runners = filtered_runners.partition do |runner, _|
|
||||
runner.fetch(:symbol) != :linux
|
||||
end
|
||||
selected_runners = macos_runners.group_by { |runner, _| runner.fetch(:arch) }.map do |_, runners|
|
||||
random_runner(runners.to_h)
|
||||
end + linux_runners.map(&:first)
|
||||
[selected_runners, false]
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(tap: T.nilable(Tap), labels: T::Array[String], cask_names: T::Array[String], skip_install: T::Boolean,
|
||||
new_cask: T::Boolean).returns(T::Array[T::Hash[Symbol,
|
||||
|
||||
@@ -129,8 +129,6 @@ module Homebrew
|
||||
puts format_stanza(trash: trash_paths, delete: delete_paths, rmdir: rmdir_paths)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(cask: Cask::Cask).returns(T::Array[String]) }
|
||||
def resolve_patterns_from_cask(cask)
|
||||
app_artifact = cask.artifacts.find { |a| a.is_a?(Cask::Artifact::App) }
|
||||
@@ -144,11 +142,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(patterns: T::Array[String]).returns(String) }
|
||||
def format_patterns(patterns)
|
||||
patterns.map { |pattern| "\"#{pattern}\"" }.to_sentence
|
||||
end
|
||||
|
||||
sig { params(app_artifact: Cask::Artifact::App).returns(T::Array[String]) }
|
||||
def bundle_identifiers(app_artifact)
|
||||
info_plist = app_artifact.target/"Contents/Info.plist"
|
||||
@@ -238,35 +231,6 @@ module Homebrew
|
||||
result.uniq.sort
|
||||
end
|
||||
|
||||
sig { params(basenames: T::Array[String]).returns(T::Array[String]) }
|
||||
def find_wildcard_groups(basenames)
|
||||
return basenames if basenames.size <= 1
|
||||
|
||||
used = Array.new(basenames.size, false)
|
||||
result = []
|
||||
|
||||
basenames.each_with_index do |name, i|
|
||||
next if used[i]
|
||||
|
||||
group_indices = [i]
|
||||
basenames.each_with_index do |other, j|
|
||||
next if i == j || used[j]
|
||||
next unless other.start_with?(name)
|
||||
|
||||
group_indices << j
|
||||
end
|
||||
|
||||
if group_indices.size > 1
|
||||
result << "#{name}*"
|
||||
group_indices.each { |idx| used[idx] = true }
|
||||
else
|
||||
result << name
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
sig { params(paths: T::Array[String]).returns(T::Array[String]) }
|
||||
def replace_uuids(paths)
|
||||
paths.map { |p| p.gsub(UUID_PATTERN, "*") }.uniq.sort
|
||||
@@ -321,6 +285,42 @@ module Homebrew
|
||||
.prepend("zap ")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(patterns: T::Array[String]).returns(String) }
|
||||
def format_patterns(patterns)
|
||||
patterns.map { |pattern| "\"#{pattern}\"" }.to_sentence
|
||||
end
|
||||
|
||||
sig { params(basenames: T::Array[String]).returns(T::Array[String]) }
|
||||
def find_wildcard_groups(basenames)
|
||||
return basenames if basenames.size <= 1
|
||||
|
||||
used = Array.new(basenames.size, false)
|
||||
result = []
|
||||
|
||||
basenames.each_with_index do |name, i|
|
||||
next if used[i]
|
||||
|
||||
group_indices = [i]
|
||||
basenames.each_with_index do |other, j|
|
||||
next if i == j || used[j]
|
||||
next unless other.start_with?(name)
|
||||
|
||||
group_indices << j
|
||||
end
|
||||
|
||||
if group_indices.size > 1
|
||||
result << "#{name}*"
|
||||
group_indices.each { |idx| used[idx] = true }
|
||||
else
|
||||
result << name
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
sig { params(key: String, paths: T::Array[String]).returns(String) }
|
||||
def format_directive(key, paths)
|
||||
if paths.size == 1
|
||||
|
||||
@@ -215,8 +215,6 @@ module Homebrew
|
||||
exec_browser release_url
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(name: String).returns(T::Array[T::Hash[String, T.untyped]]) }
|
||||
def matching_releases(name)
|
||||
releases_url = "#{GitHub::API_URL}/repos/Homebrew/brew/releases?per_page=#{GitHub::MAX_PER_PAGE}"
|
||||
@@ -240,6 +238,8 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(releases: T::Array[T::Hash[String, T.untyped]]).returns(T::Array[String]) }
|
||||
def release_urls(releases)
|
||||
releases.filter_map do |release|
|
||||
|
||||
@@ -264,47 +264,9 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
# for generic tests, remove macOS or Linux specific tests
|
||||
non_linux_bundle_args(non_macos_bundle_args(bundle_args))
|
||||
end
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_macos_bundle_args(bundle_args)
|
||||
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
|
||||
bundle_args << "--tag" << "~needs_svnadmin" unless args.online?
|
||||
bundle_args << "--tag" << "~needs_svn" unless args.online?
|
||||
|
||||
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
|
||||
end
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_linux_bundle_args(bundle_args)
|
||||
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_test_environment!; end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_files(files)
|
||||
# for generic tests, remove macOS or Linux specific files
|
||||
non_linux_files(non_macos_files(files))
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_macos_files(files)
|
||||
files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_linux_files(files)
|
||||
files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def changed_test_files
|
||||
changed_files = Utils::Git.changed_files(HOMEBREW_REPOSITORY)
|
||||
@@ -331,6 +293,44 @@ module Homebrew
|
||||
.map(&:to_s)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
# for generic tests, remove macOS or Linux specific tests
|
||||
non_linux_bundle_args(non_macos_bundle_args(bundle_args))
|
||||
end
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_macos_bundle_args(bundle_args)
|
||||
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
|
||||
bundle_args << "--tag" << "~needs_svnadmin" unless args.online?
|
||||
bundle_args << "--tag" << "~needs_svn" unless args.online?
|
||||
|
||||
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
|
||||
end
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_linux_bundle_args(bundle_args)
|
||||
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_files(files)
|
||||
# for generic tests, remove macOS or Linux specific files
|
||||
non_linux_files(non_macos_files(files))
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_macos_files(files)
|
||||
files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_linux_files(files)
|
||||
files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
sig { params(filestub: String).returns(T::Array[Pathname]) }
|
||||
def shared_context_test_files(filestub)
|
||||
case filestub
|
||||
|
||||
@@ -268,6 +268,11 @@ module Homebrew
|
||||
pool.wait_for_termination
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[Downloadable, Concurrent::Promises::Future]) }
|
||||
def downloads
|
||||
@downloads ||= T.let({}, T.nilable(T::Hash[Downloadable, Concurrent::Promises::Future]))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(downloadable: Downloadable, check_attestation: T::Boolean).void }
|
||||
@@ -344,11 +349,6 @@ module Homebrew
|
||||
tty && !@dumb_tty
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[Downloadable, Concurrent::Promises::Future]) }
|
||||
def downloads
|
||||
@downloads ||= T.let({}, T.nilable(T::Hash[Downloadable, Concurrent::Promises::Future]))
|
||||
end
|
||||
|
||||
sig { params(future: Concurrent::Promises::Future).returns(T.nilable(String)) }
|
||||
def status_from_future(future)
|
||||
case future.state
|
||||
|
||||
@@ -63,21 +63,6 @@ class AbstractFileDownloadStrategy < AbstractDownloadStrategy
|
||||
FileUtils.ln_s target_cached_location.relative_path_from(symlink_location.dirname), symlink_location, force: true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(String) }
|
||||
def resolved_basename
|
||||
_, resolved_basename = resolved_url_and_basename
|
||||
resolved_basename
|
||||
end
|
||||
|
||||
sig { returns([String, String]) }
|
||||
def resolved_url_and_basename
|
||||
return T.must(@resolved_url_and_basename) if defined?(@resolved_url_and_basename)
|
||||
|
||||
T.must(@resolved_url_and_basename = T.let([url, parse_basename(url)], T.nilable([String, String])))
|
||||
end
|
||||
|
||||
sig { params(url: String, search_query: T::Boolean).returns(String) }
|
||||
def parse_basename(url, search_query: true)
|
||||
components = { path: T.let([], T::Array[String]), query: T.let([], T::Array[String]) }
|
||||
@@ -125,4 +110,19 @@ class AbstractFileDownloadStrategy < AbstractDownloadStrategy
|
||||
|
||||
File.basename(filename)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(String) }
|
||||
def resolved_basename
|
||||
_, resolved_basename = resolved_url_and_basename
|
||||
resolved_basename
|
||||
end
|
||||
|
||||
sig { returns([String, String]) }
|
||||
def resolved_url_and_basename
|
||||
return T.must(@resolved_url_and_basename) if defined?(@resolved_url_and_basename)
|
||||
|
||||
T.must(@resolved_url_and_basename = T.let([url, parse_basename(url)], T.nilable([String, String])))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -146,6 +146,32 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
|
||||
[time, T.must(file_size)]
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def allow_deferred_environment_expansion!
|
||||
@expand_deferred_environment = true
|
||||
end
|
||||
|
||||
# Curl options to be always passed to curl,
|
||||
# with raw head calls (`curl --head`) or with actual `fetch`.
|
||||
sig { returns(T::Array[String]) }
|
||||
def _curl_args
|
||||
args = []
|
||||
|
||||
args += ["-b", meta.fetch(:cookies).map { |k, v| "#{k}=#{v}" }.join(";")] if meta.key?(:cookies)
|
||||
|
||||
args += ["-e", meta.fetch(:referer)] if meta.key?(:referer)
|
||||
|
||||
args += ["--user", meta.fetch(:user)] if meta.key?(:user)
|
||||
|
||||
if meta.fetch(:headers, []).any? { |header| header.include?(EnvSensitive::DEFERRED_PLACEHOLDER_PREFIX) }
|
||||
args += ["--max-redirs", "0"]
|
||||
end
|
||||
|
||||
args += expand_deferred_environment_args(meta.fetch(:headers, [])).flat_map { |h| ["--header", h.strip] }
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(timeout: T.nilable(T.any(Float, Integer))).returns([String, String]) }
|
||||
@@ -262,11 +288,6 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
|
||||
curl_download resolved_url, to:, try_partial: @try_partial, timeout:
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def allow_deferred_environment_expansion!
|
||||
@expand_deferred_environment = true
|
||||
end
|
||||
|
||||
sig { params(args: T::Array[String]).returns(T::Array[String]) }
|
||||
def expand_deferred_environment_args(args)
|
||||
return args unless @expand_deferred_environment
|
||||
@@ -276,27 +297,6 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
|
||||
end
|
||||
end
|
||||
|
||||
# Curl options to be always passed to curl,
|
||||
# with raw head calls (`curl --head`) or with actual `fetch`.
|
||||
sig { returns(T::Array[String]) }
|
||||
def _curl_args
|
||||
args = []
|
||||
|
||||
args += ["-b", meta.fetch(:cookies).map { |k, v| "#{k}=#{v}" }.join(";")] if meta.key?(:cookies)
|
||||
|
||||
args += ["-e", meta.fetch(:referer)] if meta.key?(:referer)
|
||||
|
||||
args += ["--user", meta.fetch(:user)] if meta.key?(:user)
|
||||
|
||||
if meta.fetch(:headers, []).any? { |header| header.include?(EnvSensitive::DEFERRED_PLACEHOLDER_PREFIX) }
|
||||
args += ["--max-redirs", "0"]
|
||||
end
|
||||
|
||||
args += expand_deferred_environment_args(meta.fetch(:headers, [])).flat_map { |h| ["--header", h.strip] }
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[Symbol, T.any(String, Symbol)]) }
|
||||
def _curl_opts
|
||||
meta.slice(:user_agent)
|
||||
|
||||
@@ -49,6 +49,30 @@ class GitDownloadStrategy < VCSDownloadStrategy
|
||||
@last_commit || ""
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ref?
|
||||
silent_command("git",
|
||||
args: ["--git-dir", git_dir, "rev-parse", "-q", "--verify", "--end-of-options",
|
||||
"#{@ref}^{commit}"])
|
||||
.success?
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def clone_args
|
||||
args = %w[clone]
|
||||
|
||||
case @ref_type
|
||||
when :branch, :tag
|
||||
args << "--branch" << @ref
|
||||
end
|
||||
|
||||
args << "--no-checkout" << "--filter=blob:none" if partial_clone_sparse_checkout?
|
||||
|
||||
args << "--config" << "advice.detachedHead=false" # Silences “detached head” warning.
|
||||
args << "--config" << "core.fsmonitor=false" # Prevent `fsmonitor` from watching this repository.
|
||||
args << "--end-of-options" << @url << cached_location.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Read user Git config so credential helpers work for private downloads,
|
||||
@@ -101,14 +125,6 @@ class GitDownloadStrategy < VCSDownloadStrategy
|
||||
cached_location/".git"
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ref?
|
||||
silent_command("git",
|
||||
args: ["--git-dir", git_dir, "rev-parse", "-q", "--verify", "--end-of-options",
|
||||
"#{@ref}^{commit}"])
|
||||
.success?
|
||||
end
|
||||
|
||||
sig { override.returns(String) }
|
||||
def current_revision
|
||||
system_command("git", args: ["--git-dir", git_dir, "rev-parse", "-q", "--verify", "HEAD"],
|
||||
@@ -133,22 +149,6 @@ class GitDownloadStrategy < VCSDownloadStrategy
|
||||
Utils::Git.supports_partial_clone_sparse_checkout?
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def clone_args
|
||||
args = %w[clone]
|
||||
|
||||
case @ref_type
|
||||
when :branch, :tag
|
||||
args << "--branch" << @ref
|
||||
end
|
||||
|
||||
args << "--no-checkout" << "--filter=blob:none" if partial_clone_sparse_checkout?
|
||||
|
||||
args << "--config" << "advice.detachedHead=false" # Silences “detached head” warning.
|
||||
args << "--config" << "core.fsmonitor=false" # Prevent `fsmonitor` from watching this repository.
|
||||
args << "--end-of-options" << @url << cached_location.to_s
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def refspec
|
||||
case @ref_type
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
#
|
||||
# @api public
|
||||
class GitHubGitDownloadStrategy < GitDownloadStrategy
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :user
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :repo
|
||||
|
||||
sig { params(url: String, name: String, version: T.nilable(Version), meta: T.untyped).void }
|
||||
def initialize(url, name, version, **meta)
|
||||
super
|
||||
|
||||
@@ -187,8 +187,9 @@ module Downloadable
|
||||
|
||||
download_strategy.new(primary_url, download_name, version,
|
||||
mirrors:, cache:, **T.must(@url).specs).tap do |downloader|
|
||||
if AbstractDownloadStrategy.expand_deferred_environment_for?(downloader)
|
||||
downloader.send(:allow_deferred_environment_expansion!)
|
||||
if downloader.is_a?(CurlDownloadStrategy) &&
|
||||
AbstractDownloadStrategy.expand_deferred_environment_for?(downloader)
|
||||
downloader.allow_deferred_environment_expansion!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,6 +41,11 @@ module OS
|
||||
Dependency.new(GLIBC, [:implicit])
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T::Set[String]]) }
|
||||
def global_dep_tree
|
||||
@@global_dep_tree
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
GLIBC = "glibc"
|
||||
@@ -114,11 +119,6 @@ module OS
|
||||
@@global_dep_tree = T.let({}, T::Hash[String, T::Set[String]])
|
||||
@@building_global_dep_tree = T.let(false, T::Boolean)
|
||||
|
||||
sig { returns(T::Hash[String, T::Set[String]]) }
|
||||
def global_dep_tree
|
||||
@@global_dep_tree
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def building_global_dep_tree!
|
||||
@@building_global_dep_tree = true
|
||||
|
||||
@@ -11,18 +11,6 @@ module OS
|
||||
|
||||
requires_ancestor { Homebrew::DevCmd::Tests }
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
non_macos_bundle_args(bundle_args)
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_files(files)
|
||||
non_macos_files(files)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_test_environment!
|
||||
super
|
||||
@@ -37,6 +25,18 @@ module OS
|
||||
end
|
||||
::Sandbox.ensure_sandbox_available!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
non_macos_bundle_args(bundle_args)
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_files(files)
|
||||
non_macos_files(files)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -176,13 +176,6 @@ module OS
|
||||
implementation.run { super }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(args: T::Array[T.any(String, ::Pathname)], tmpdir: String).returns(T::Array[T.any(String, ::Pathname)]) }
|
||||
def sandbox_command(args, tmpdir)
|
||||
implementation.command(args, tmpdir)
|
||||
end
|
||||
|
||||
sig { params(tmpdir: String).returns(T::Array[String]) }
|
||||
def bubblewrap_args(tmpdir)
|
||||
bubblewrap.arguments(tmpdir)
|
||||
@@ -193,6 +186,13 @@ module OS
|
||||
bubblewrap.writable_paths
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(args: T::Array[T.any(String, ::Pathname)], tmpdir: String).returns(T::Array[T.any(String, ::Pathname)]) }
|
||||
def sandbox_command(args, tmpdir)
|
||||
implementation.command(args, tmpdir)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def apply_sandbox
|
||||
sandbox = implementation
|
||||
|
||||
@@ -369,8 +369,6 @@ class Sandbox
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(abi: Integer).returns([String, Integer, Integer]) }
|
||||
def ruleset_attributes(abi)
|
||||
allowed_access_fs = WRITE_ACCESS_FS
|
||||
@@ -397,6 +395,17 @@ class Sandbox
|
||||
[attributes.pack("Q*"), handled_access_fs, allowed_access_fs]
|
||||
end
|
||||
|
||||
sig { params(denied_paths: T::Array[::Pathname]).returns(T::Array[String]) }
|
||||
def readable_paths(denied_paths)
|
||||
return [] if denied_paths.empty? || denied_paths.include?(root_path)
|
||||
|
||||
root_path.children.sort.each_with_object([]) do |path, paths|
|
||||
add_readable_path(path, denied_paths, paths)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(ruleset_fd: Integer, path: String, allowed_access: Integer).void }
|
||||
def add_path_rule(ruleset_fd, path, allowed_access)
|
||||
path_fd = open_path(path)
|
||||
@@ -424,15 +433,6 @@ class Sandbox
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(denied_paths: T::Array[::Pathname]).returns(T::Array[String]) }
|
||||
def readable_paths(denied_paths)
|
||||
return [] if denied_paths.empty? || denied_paths.include?(root_path)
|
||||
|
||||
root_path.children.sort.each_with_object([]) do |path, paths|
|
||||
add_readable_path(path, denied_paths, paths)
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(path: ::Pathname, denied_paths: T::Array[::Pathname], paths: T::Array[String]).void }
|
||||
def add_readable_path(path, denied_paths, paths)
|
||||
return if denied_paths.include?(path)
|
||||
|
||||
@@ -20,8 +20,6 @@ module OS
|
||||
|
||||
requires_ancestor { Utils::Bottles::Collector }
|
||||
|
||||
private
|
||||
|
||||
sig {
|
||||
params(tag: Utils::Bottles::Tag,
|
||||
no_older_versions: T::Boolean).returns(T.nilable(Utils::Bottles::Tag))
|
||||
@@ -38,6 +36,8 @@ module OS
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Find a bottle built for a previous version of macOS.
|
||||
sig { params(tag: Utils::Bottles::Tag).returns(T.nilable(Utils::Bottles::Tag)) }
|
||||
def find_older_compatible_tag(tag)
|
||||
|
||||
+26
-25
@@ -150,7 +150,7 @@ class Formula
|
||||
#
|
||||
# @api public
|
||||
sig { returns(T.nilable(Tap)) }
|
||||
attr_reader :tap
|
||||
attr_accessor :tap
|
||||
|
||||
# The stable (and default) {SoftwareSpec} for this {Formula}.
|
||||
# This contains all the attributes (e.g. URL, checksum) that apply to the
|
||||
@@ -176,8 +176,6 @@ class Formula
|
||||
sig { returns(SoftwareSpec) }
|
||||
attr_reader :active_spec
|
||||
|
||||
protected :active_spec
|
||||
|
||||
# A symbol to indicate currently active {SoftwareSpec}.
|
||||
# It's either `:stable` or `:head`.
|
||||
# @see #active_spec
|
||||
@@ -215,7 +213,7 @@ class Formula
|
||||
#
|
||||
# @api public
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :buildpath
|
||||
attr_accessor :buildpath
|
||||
|
||||
# The current working directory during tests.
|
||||
# Will only be non-`nil` inside {.test}.
|
||||
@@ -882,6 +880,9 @@ class Formula
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(oldnames: T.nilable(T::Array[String])).void }
|
||||
attr_writer :oldnames
|
||||
|
||||
# All aliases for the formula.
|
||||
#
|
||||
# @api internal
|
||||
@@ -3669,6 +3670,27 @@ class Formula
|
||||
T.must(bottle).tab_attributes
|
||||
end
|
||||
|
||||
# Common environment variables used by sandboxed build, test and postinstall phases.
|
||||
sig { params(home: Pathname).returns(T::Hash[Symbol, String]) }
|
||||
def common_sandbox_env(home)
|
||||
{
|
||||
_JAVA_OPTIONS: "-Duser.home=#{HOMEBREW_CACHE}/java_cache",
|
||||
GOCACHE: "#{HOMEBREW_CACHE}/go_cache",
|
||||
GIT_CONFIG_GLOBAL: Utils::Git.no_global_config_file,
|
||||
GIT_TERMINAL_PROMPT: "0",
|
||||
GOENV: "off",
|
||||
GOPATH: "#{HOMEBREW_CACHE}/go_mod_cache",
|
||||
CARGO_HOME: "#{HOMEBREW_CACHE}/cargo_cache",
|
||||
BUNDLE_COOLDOWN: Homebrew::RELEASE_COOLDOWN_DAYS.to_s,
|
||||
PIP_CACHE_DIR: "#{HOMEBREW_CACHE}/pip_cache",
|
||||
PIP_CONFIG_FILE: File::NULL,
|
||||
NPM_CONFIG_USERCONFIG: File::NULL,
|
||||
CURL_HOME: ENV.fetch("CURL_HOME") { home.to_s },
|
||||
PYTHONDONTWRITEBYTECODE: "1",
|
||||
XDG_CONFIG_HOME: "#{home}/.config",
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
@@ -3715,27 +3737,6 @@ class Formula
|
||||
exit! 1 # never gets here unless exec threw or failed
|
||||
end
|
||||
|
||||
# Common environment variables used by sandboxed build, test and postinstall phases.
|
||||
sig { params(home: Pathname).returns(T::Hash[Symbol, String]) }
|
||||
def common_sandbox_env(home)
|
||||
{
|
||||
_JAVA_OPTIONS: "-Duser.home=#{HOMEBREW_CACHE}/java_cache",
|
||||
GOCACHE: "#{HOMEBREW_CACHE}/go_cache",
|
||||
GIT_CONFIG_GLOBAL: Utils::Git.no_global_config_file,
|
||||
GIT_TERMINAL_PROMPT: "0",
|
||||
GOENV: "off",
|
||||
GOPATH: "#{HOMEBREW_CACHE}/go_mod_cache",
|
||||
CARGO_HOME: "#{HOMEBREW_CACHE}/cargo_cache",
|
||||
BUNDLE_COOLDOWN: Homebrew::RELEASE_COOLDOWN_DAYS.to_s,
|
||||
PIP_CACHE_DIR: "#{HOMEBREW_CACHE}/pip_cache",
|
||||
PIP_CONFIG_FILE: File::NULL,
|
||||
NPM_CONFIG_USERCONFIG: File::NULL,
|
||||
CURL_HOME: ENV.fetch("CURL_HOME") { home.to_s },
|
||||
PYTHONDONTWRITEBYTECODE: "1",
|
||||
XDG_CONFIG_HOME: "#{home}/.config",
|
||||
}
|
||||
end
|
||||
|
||||
sig { params(interactive: T::Boolean, debug_symbols: T::Boolean, _block: T.proc.params(arg0: Mktemp).void).void }
|
||||
def stage(interactive: false, debug_symbols: false, &_block)
|
||||
active_spec.stage(debug_symbols:) do |staging|
|
||||
|
||||
@@ -1236,57 +1236,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(message: String, location: T.nilable(Homebrew::SourceLocation), corrected: T::Boolean).void }
|
||||
def problem(message, location: nil, corrected: false)
|
||||
@problems << ({ message:, location:, corrected: })
|
||||
end
|
||||
|
||||
sig { params(message: String, location: T.nilable(Homebrew::SourceLocation), corrected: T::Boolean).void }
|
||||
def new_formula_problem(message, location: nil, corrected: false)
|
||||
@new_formula_problems << ({ message:, location:, corrected: })
|
||||
end
|
||||
|
||||
sig { params(repo_owner: String).returns(T::Boolean) }
|
||||
def self_submission?(repo_owner)
|
||||
return false if repo_owner.blank?
|
||||
|
||||
SharedAudits.self_submission_for_repo_owner?(repo_owner)
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def head_only?(formula)
|
||||
!!formula.head && formula.stable.nil?
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def linux_only_gcc_dep?(formula)
|
||||
odie "`#linux_only_gcc_dep?` works only on Linux!" if Homebrew::SimulateSystem.simulating_or_running_on_macos?
|
||||
return false if formula.deps.none? { |dep| dep.name == "gcc" && !dep.implicit? }
|
||||
|
||||
variations = formula.to_hash_with_variations["variations"]
|
||||
# The formula has no variations, so all OS-version-arch triples depend on GCC.
|
||||
return false if variations.blank?
|
||||
|
||||
MacOSVersion::SYMBOLS.keys.product(OnSystem::ARCH_OPTIONS).each do |os, arch|
|
||||
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless bottle_tag.valid_combination?
|
||||
|
||||
variation_dependencies = variations.dig(bottle_tag.to_sym, "dependencies")
|
||||
# This variation either:
|
||||
# 1. does not exist
|
||||
# 2. has no variation-specific dependencies
|
||||
# In either case, it matches Linux. We must check for `nil` because an empty
|
||||
# array indicates that this variation does not depend on GCC.
|
||||
return false if variation_dependencies.nil?
|
||||
# We found a non-Linux variation that depends on GCC.
|
||||
return false if variation_dependencies.include?("gcc")
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, only_names: T::Array[String]).returns(T::Array[Pathname]) }
|
||||
def changed_formulae_paths(tap, only_names: [].freeze)
|
||||
return [] unless tap.git?
|
||||
@@ -1369,6 +1318,57 @@ module Homebrew
|
||||
@committed_version_info_cache[formula.full_name] = [previous_version_info, base_ref_version_info]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(message: String, location: T.nilable(Homebrew::SourceLocation), corrected: T::Boolean).void }
|
||||
def problem(message, location: nil, corrected: false)
|
||||
@problems << ({ message:, location:, corrected: })
|
||||
end
|
||||
|
||||
sig { params(message: String, location: T.nilable(Homebrew::SourceLocation), corrected: T::Boolean).void }
|
||||
def new_formula_problem(message, location: nil, corrected: false)
|
||||
@new_formula_problems << ({ message:, location:, corrected: })
|
||||
end
|
||||
|
||||
sig { params(repo_owner: String).returns(T::Boolean) }
|
||||
def self_submission?(repo_owner)
|
||||
return false if repo_owner.blank?
|
||||
|
||||
SharedAudits.self_submission_for_repo_owner?(repo_owner)
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def head_only?(formula)
|
||||
!!formula.head && formula.stable.nil?
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Boolean) }
|
||||
def linux_only_gcc_dep?(formula)
|
||||
odie "`#linux_only_gcc_dep?` works only on Linux!" if Homebrew::SimulateSystem.simulating_or_running_on_macos?
|
||||
return false if formula.deps.none? { |dep| dep.name == "gcc" && !dep.implicit? }
|
||||
|
||||
variations = formula.to_hash_with_variations["variations"]
|
||||
# The formula has no variations, so all OS-version-arch triples depend on GCC.
|
||||
return false if variations.blank?
|
||||
|
||||
MacOSVersion::SYMBOLS.keys.product(OnSystem::ARCH_OPTIONS).each do |os, arch|
|
||||
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless bottle_tag.valid_combination?
|
||||
|
||||
variation_dependencies = variations.dig(bottle_tag.to_sym, "dependencies")
|
||||
# This variation either:
|
||||
# 1. does not exist
|
||||
# 2. has no variation-specific dependencies
|
||||
# In either case, it matches Linux. We must check for `nil` because an empty
|
||||
# array indicates that this variation does not depend on GCC.
|
||||
return false if variation_dependencies.nil?
|
||||
# We found a non-Linux variation that depends on GCC.
|
||||
return false if variation_dependencies.include?("gcc")
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
sig { params(tap: Tap).returns(String) }
|
||||
def git_audit_base_ref(tap)
|
||||
@git_audit_base_ref_cache ||= T.let({}, T.nilable(T::Hash[Pathname, T.nilable(String)]))
|
||||
|
||||
@@ -49,6 +49,9 @@ class FormulaInstaller
|
||||
sig { returns(Homebrew::DownloadQueue) }
|
||||
attr_accessor :download_queue
|
||||
|
||||
sig { params(ran_prelude: T::Boolean).void }
|
||||
attr_writer :ran_prelude
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula: Formula,
|
||||
|
||||
+143
-143
@@ -141,149 +141,6 @@ class GitHubPackages
|
||||
version_rebuild
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
IMAGE_CONFIG_SCHEMA_URI = "https://opencontainers.org/schema/image/config"
|
||||
IMAGE_INDEX_SCHEMA_URI = "https://opencontainers.org/schema/image/index"
|
||||
IMAGE_LAYOUT_SCHEMA_URI = "https://opencontainers.org/schema/image/layout"
|
||||
IMAGE_MANIFEST_SCHEMA_URI = "https://opencontainers.org/schema/image/manifest"
|
||||
|
||||
GITHUB_PACKAGE_TYPE = "homebrew_bottle"
|
||||
private_constant :IMAGE_CONFIG_SCHEMA_URI, :IMAGE_INDEX_SCHEMA_URI, :IMAGE_LAYOUT_SCHEMA_URI,
|
||||
:IMAGE_MANIFEST_SCHEMA_URI, :GITHUB_PACKAGE_TYPE
|
||||
|
||||
sig { void }
|
||||
def load_schemas!
|
||||
schema_uri("content-descriptor",
|
||||
"https://opencontainers.org/schema/image/content-descriptor.json")
|
||||
schema_uri("defs", %w[
|
||||
https://opencontainers.org/schema/defs.json
|
||||
https://opencontainers.org/schema/descriptor/defs.json
|
||||
https://opencontainers.org/schema/image/defs.json
|
||||
https://opencontainers.org/schema/image/descriptor/defs.json
|
||||
https://opencontainers.org/schema/image/index/defs.json
|
||||
https://opencontainers.org/schema/image/manifest/defs.json
|
||||
])
|
||||
schema_uri("defs-descriptor", %w[
|
||||
https://opencontainers.org/schema/descriptor.json
|
||||
https://opencontainers.org/schema/defs-descriptor.json
|
||||
https://opencontainers.org/schema/descriptor/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/descriptor/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/index/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/manifest/defs-descriptor.json
|
||||
https://opencontainers.org/schema/index/defs-descriptor.json
|
||||
])
|
||||
schema_uri("config-schema", IMAGE_CONFIG_SCHEMA_URI)
|
||||
schema_uri("image-index-schema", IMAGE_INDEX_SCHEMA_URI)
|
||||
schema_uri("image-layout-schema", IMAGE_LAYOUT_SCHEMA_URI)
|
||||
schema_uri("image-manifest-schema", IMAGE_MANIFEST_SCHEMA_URI)
|
||||
end
|
||||
|
||||
sig { params(basename: String, uris: T.any(String, T::Array[String])).void }
|
||||
def schema_uri(basename, uris)
|
||||
# The current `main` version has an invalid JSON schema.
|
||||
# Going forward, this should probably be pinned to tags.
|
||||
# We currently use features newer than the last one (v1.0.2).
|
||||
url = "https://raw.githubusercontent.com/opencontainers/image-spec/170393e57ed656f7f81c3070bfa8c3346eaa0a5a/schema/#{basename}.json"
|
||||
out = Utils::Curl.curl_output(url).stdout
|
||||
json = JSON.parse(out)
|
||||
|
||||
@schema_json ||= T.let({}, T.nilable(T::Hash[String, T::Hash[String, T.untyped]]))
|
||||
Array(uris).each do |uri|
|
||||
@schema_json[uri] = json
|
||||
end
|
||||
end
|
||||
|
||||
T::Sig::WithoutRuntime.sig { params(uri: URI::Generic).returns(T.nilable(T::Hash[String, T.untyped])) }
|
||||
def schema_resolver(uri)
|
||||
@schema_json&.fetch(uri.to_s.gsub(/#.*/, ""))
|
||||
end
|
||||
|
||||
sig { params(schema_uri: String, json: T::Hash[T.any(String, Symbol), T.untyped]).void }
|
||||
def validate_schema!(schema_uri, json)
|
||||
schema = JSONSchemer.schema(@schema_json&.fetch(schema_uri), ref_resolver: method(:schema_resolver))
|
||||
json = json.deep_stringify_keys
|
||||
return if schema.valid?(json)
|
||||
|
||||
puts
|
||||
ofail "#{Formatter.url(schema_uri)} JSON schema validation failed!"
|
||||
oh1 "Errors"
|
||||
puts schema.validate(json).to_a.inspect
|
||||
oh1 "JSON"
|
||||
puts json.inspect
|
||||
exit 1
|
||||
end
|
||||
|
||||
sig { params(user: String, token: String, skopeo: Pathname, image_uri: String, root: Pathname, dry_run: T::Boolean).void }
|
||||
def download(user, token, skopeo, image_uri, root, dry_run:)
|
||||
puts
|
||||
args = ["copy", "--all", image_uri.to_s, "oci:#{root}"]
|
||||
if dry_run
|
||||
puts "#{skopeo} #{args.join(" ")} --src-creds=#{user}:$HOMEBREW_GITHUB_PACKAGES_TOKEN"
|
||||
else
|
||||
args << "--src-creds=#{user}:#{token}"
|
||||
system_command!(skopeo, verbose: true, print_stdout: true, args:)
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
user: String, token: String, skopeo: Pathname, _formula_full_name: String,
|
||||
bottle_hash: T::Hash[String, T.untyped], keep_old: T::Boolean, dry_run: T::Boolean, warn_on_error: T::Boolean
|
||||
).returns(
|
||||
T.nilable([String, String, String, Version, Integer, String, String, String, T::Boolean]),
|
||||
)
|
||||
}
|
||||
def preupload_check(user, token, skopeo, _formula_full_name, bottle_hash, keep_old:, dry_run:, warn_on_error:)
|
||||
formula_name = bottle_hash["formula"]["name"]
|
||||
|
||||
_, org, repo, = *bottle_hash["bottle"]["root_url"].match(URL_REGEX)
|
||||
repo = "homebrew-#{repo}" unless repo.start_with?("homebrew-")
|
||||
|
||||
version = Version.new(bottle_hash["formula"]["pkg_version"])
|
||||
rebuild = bottle_hash["bottle"]["rebuild"].to_i
|
||||
version_rebuild = GitHubPackages.version_rebuild(version, rebuild)
|
||||
|
||||
image_name = GitHubPackages.image_formula_name(formula_name)
|
||||
image_tag = GitHubPackages.image_version_rebuild(version_rebuild)
|
||||
image_uri = "#{GitHubPackages.root_url(org, repo, DOCKER_PREFIX)}/#{image_name}:#{image_tag}"
|
||||
|
||||
puts
|
||||
inspect_args = ["inspect", "--raw", image_uri.to_s]
|
||||
if dry_run
|
||||
puts "#{skopeo} #{inspect_args.join(" ")} --creds=#{user}:$HOMEBREW_GITHUB_PACKAGES_TOKEN"
|
||||
else
|
||||
inspect_args << "--creds=#{user}:#{token}"
|
||||
inspect_result = system_command(skopeo, print_stderr: false, args: inspect_args)
|
||||
|
||||
# Order here is important.
|
||||
if !inspect_result.status.success? && !inspect_result.stderr.match?(/(name|manifest) unknown/)
|
||||
# We got an error and it was not about the tag or package being unknown.
|
||||
if warn_on_error
|
||||
opoo "#{image_uri} inspection returned an error, skipping upload!\n#{inspect_result.stderr}"
|
||||
return
|
||||
else
|
||||
odie "#{image_uri} inspection returned an error!\n#{inspect_result.stderr}"
|
||||
end
|
||||
elsif keep_old
|
||||
# If the tag doesn't exist, ignore `--keep-old`.
|
||||
keep_old = false unless inspect_result.status.success?
|
||||
# Otherwise, do nothing - the tag already existing is expected behaviour for --keep-old.
|
||||
elsif inspect_result.status.success?
|
||||
# The tag already exists and we are not passing `--keep-old`.
|
||||
if warn_on_error
|
||||
opoo "#{image_uri} already exists, skipping upload!"
|
||||
return
|
||||
else
|
||||
odie "#{image_uri} already exists!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[formula_name, org, repo, version, rebuild, version_rebuild, image_name, image_uri, keep_old]
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
user: String, token: String, skopeo: Pathname, formula_full_name: String,
|
||||
@@ -518,6 +375,149 @@ class GitHubPackages
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
IMAGE_CONFIG_SCHEMA_URI = "https://opencontainers.org/schema/image/config"
|
||||
IMAGE_INDEX_SCHEMA_URI = "https://opencontainers.org/schema/image/index"
|
||||
IMAGE_LAYOUT_SCHEMA_URI = "https://opencontainers.org/schema/image/layout"
|
||||
IMAGE_MANIFEST_SCHEMA_URI = "https://opencontainers.org/schema/image/manifest"
|
||||
|
||||
GITHUB_PACKAGE_TYPE = "homebrew_bottle"
|
||||
private_constant :IMAGE_CONFIG_SCHEMA_URI, :IMAGE_INDEX_SCHEMA_URI, :IMAGE_LAYOUT_SCHEMA_URI,
|
||||
:IMAGE_MANIFEST_SCHEMA_URI, :GITHUB_PACKAGE_TYPE
|
||||
|
||||
sig { void }
|
||||
def load_schemas!
|
||||
schema_uri("content-descriptor",
|
||||
"https://opencontainers.org/schema/image/content-descriptor.json")
|
||||
schema_uri("defs", %w[
|
||||
https://opencontainers.org/schema/defs.json
|
||||
https://opencontainers.org/schema/descriptor/defs.json
|
||||
https://opencontainers.org/schema/image/defs.json
|
||||
https://opencontainers.org/schema/image/descriptor/defs.json
|
||||
https://opencontainers.org/schema/image/index/defs.json
|
||||
https://opencontainers.org/schema/image/manifest/defs.json
|
||||
])
|
||||
schema_uri("defs-descriptor", %w[
|
||||
https://opencontainers.org/schema/descriptor.json
|
||||
https://opencontainers.org/schema/defs-descriptor.json
|
||||
https://opencontainers.org/schema/descriptor/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/descriptor/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/index/defs-descriptor.json
|
||||
https://opencontainers.org/schema/image/manifest/defs-descriptor.json
|
||||
https://opencontainers.org/schema/index/defs-descriptor.json
|
||||
])
|
||||
schema_uri("config-schema", IMAGE_CONFIG_SCHEMA_URI)
|
||||
schema_uri("image-index-schema", IMAGE_INDEX_SCHEMA_URI)
|
||||
schema_uri("image-layout-schema", IMAGE_LAYOUT_SCHEMA_URI)
|
||||
schema_uri("image-manifest-schema", IMAGE_MANIFEST_SCHEMA_URI)
|
||||
end
|
||||
|
||||
sig { params(basename: String, uris: T.any(String, T::Array[String])).void }
|
||||
def schema_uri(basename, uris)
|
||||
# The current `main` version has an invalid JSON schema.
|
||||
# Going forward, this should probably be pinned to tags.
|
||||
# We currently use features newer than the last one (v1.0.2).
|
||||
url = "https://raw.githubusercontent.com/opencontainers/image-spec/170393e57ed656f7f81c3070bfa8c3346eaa0a5a/schema/#{basename}.json"
|
||||
out = Utils::Curl.curl_output(url).stdout
|
||||
json = JSON.parse(out)
|
||||
|
||||
@schema_json ||= T.let({}, T.nilable(T::Hash[String, T::Hash[String, T.untyped]]))
|
||||
Array(uris).each do |uri|
|
||||
@schema_json[uri] = json
|
||||
end
|
||||
end
|
||||
|
||||
T::Sig::WithoutRuntime.sig { params(uri: URI::Generic).returns(T.nilable(T::Hash[String, T.untyped])) }
|
||||
def schema_resolver(uri)
|
||||
@schema_json&.fetch(uri.to_s.gsub(/#.*/, ""))
|
||||
end
|
||||
|
||||
sig { params(schema_uri: String, json: T::Hash[T.any(String, Symbol), T.untyped]).void }
|
||||
def validate_schema!(schema_uri, json)
|
||||
schema = JSONSchemer.schema(@schema_json&.fetch(schema_uri), ref_resolver: method(:schema_resolver))
|
||||
json = json.deep_stringify_keys
|
||||
return if schema.valid?(json)
|
||||
|
||||
puts
|
||||
ofail "#{Formatter.url(schema_uri)} JSON schema validation failed!"
|
||||
oh1 "Errors"
|
||||
puts schema.validate(json).to_a.inspect
|
||||
oh1 "JSON"
|
||||
puts json.inspect
|
||||
exit 1
|
||||
end
|
||||
|
||||
sig { params(user: String, token: String, skopeo: Pathname, image_uri: String, root: Pathname, dry_run: T::Boolean).void }
|
||||
def download(user, token, skopeo, image_uri, root, dry_run:)
|
||||
puts
|
||||
args = ["copy", "--all", image_uri.to_s, "oci:#{root}"]
|
||||
if dry_run
|
||||
puts "#{skopeo} #{args.join(" ")} --src-creds=#{user}:$HOMEBREW_GITHUB_PACKAGES_TOKEN"
|
||||
else
|
||||
args << "--src-creds=#{user}:#{token}"
|
||||
system_command!(skopeo, verbose: true, print_stdout: true, args:)
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
user: String, token: String, skopeo: Pathname, _formula_full_name: String,
|
||||
bottle_hash: T::Hash[String, T.untyped], keep_old: T::Boolean, dry_run: T::Boolean, warn_on_error: T::Boolean
|
||||
).returns(
|
||||
T.nilable([String, String, String, Version, Integer, String, String, String, T::Boolean]),
|
||||
)
|
||||
}
|
||||
def preupload_check(user, token, skopeo, _formula_full_name, bottle_hash, keep_old:, dry_run:, warn_on_error:)
|
||||
formula_name = bottle_hash["formula"]["name"]
|
||||
|
||||
_, org, repo, = *bottle_hash["bottle"]["root_url"].match(URL_REGEX)
|
||||
repo = "homebrew-#{repo}" unless repo.start_with?("homebrew-")
|
||||
|
||||
version = Version.new(bottle_hash["formula"]["pkg_version"])
|
||||
rebuild = bottle_hash["bottle"]["rebuild"].to_i
|
||||
version_rebuild = GitHubPackages.version_rebuild(version, rebuild)
|
||||
|
||||
image_name = GitHubPackages.image_formula_name(formula_name)
|
||||
image_tag = GitHubPackages.image_version_rebuild(version_rebuild)
|
||||
image_uri = "#{GitHubPackages.root_url(org, repo, DOCKER_PREFIX)}/#{image_name}:#{image_tag}"
|
||||
|
||||
puts
|
||||
inspect_args = ["inspect", "--raw", image_uri.to_s]
|
||||
if dry_run
|
||||
puts "#{skopeo} #{inspect_args.join(" ")} --creds=#{user}:$HOMEBREW_GITHUB_PACKAGES_TOKEN"
|
||||
else
|
||||
inspect_args << "--creds=#{user}:#{token}"
|
||||
inspect_result = system_command(skopeo, print_stderr: false, args: inspect_args)
|
||||
|
||||
# Order here is important.
|
||||
if !inspect_result.status.success? && !inspect_result.stderr.match?(/(name|manifest) unknown/)
|
||||
# We got an error and it was not about the tag or package being unknown.
|
||||
if warn_on_error
|
||||
opoo "#{image_uri} inspection returned an error, skipping upload!\n#{inspect_result.stderr}"
|
||||
return
|
||||
else
|
||||
odie "#{image_uri} inspection returned an error!\n#{inspect_result.stderr}"
|
||||
end
|
||||
elsif keep_old
|
||||
# If the tag doesn't exist, ignore `--keep-old`.
|
||||
keep_old = false unless inspect_result.status.success?
|
||||
# Otherwise, do nothing - the tag already existing is expected behaviour for --keep-old.
|
||||
elsif inspect_result.status.success?
|
||||
# The tag already exists and we are not passing `--keep-old`.
|
||||
if warn_on_error
|
||||
opoo "#{image_uri} already exists, skipping upload!"
|
||||
return
|
||||
else
|
||||
odie "#{image_uri} already exists!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[formula_name, org, repo, version, rebuild, version_rebuild, image_name, image_uri, keep_old]
|
||||
end
|
||||
|
||||
sig { params(root: Pathname).returns([String, Integer]) }
|
||||
def write_image_layout(root)
|
||||
image_layout = { imageLayoutVersion: "1.0.0" }
|
||||
|
||||
@@ -108,86 +108,6 @@ class GitHubRunnerMatrix
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# ARM macOS timeout, keep this under 1/2 of GitHub's job execution time limit for self-hosted runners.
|
||||
# https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits
|
||||
GITHUB_ACTIONS_LONG_TIMEOUT = 2160 # 36 hours
|
||||
GITHUB_ACTIONS_SHORT_TIMEOUT = 60
|
||||
private_constant :GITHUB_ACTIONS_LONG_TIMEOUT, :GITHUB_ACTIONS_SHORT_TIMEOUT
|
||||
|
||||
sig { params(arch: Symbol, self_hosted: T::Boolean).returns(LinuxRunnerSpec) }
|
||||
def linux_runner_spec(arch, self_hosted:)
|
||||
linux_runner = case arch
|
||||
when :arm64 then self_hosted ? "linux-arm64#{ephemeral_suffix}" : OS::LINUX_CI_ARM_RUNNER
|
||||
when :x86_64 then self_hosted ? "linux-x86_64#{ephemeral_suffix}" : "ubuntu-latest"
|
||||
else raise "Unknown Linux architecture: #{arch}"
|
||||
end
|
||||
|
||||
unless self_hosted
|
||||
container = {
|
||||
image: "ghcr.io/homebrew/brew:main",
|
||||
options: "--user linuxbrew --env HOMEBREW_SANDBOX_LINUX_LANDLOCK=1",
|
||||
}
|
||||
workdir = "/github/home"
|
||||
end
|
||||
|
||||
LinuxRunnerSpec.new(
|
||||
name: "Linux #{arch}",
|
||||
runner: linux_runner,
|
||||
container:,
|
||||
workdir:,
|
||||
timeout: GITHUB_ACTIONS_LONG_TIMEOUT,
|
||||
cleanup: false,
|
||||
)
|
||||
end
|
||||
|
||||
VALID_PLATFORMS = [:macos, :linux].freeze
|
||||
VALID_ARCHES = [:arm64, :x86_64].freeze
|
||||
private_constant :VALID_PLATFORMS, :VALID_ARCHES
|
||||
|
||||
sig {
|
||||
params(
|
||||
platform: Symbol,
|
||||
arch: Symbol,
|
||||
spec: RunnerSpec,
|
||||
macos_version: T.nilable(MacOSVersion),
|
||||
).returns(GitHubRunner)
|
||||
}
|
||||
def create_runner(platform, arch, spec, macos_version = nil)
|
||||
raise "Unexpected platform: #{platform}" if VALID_PLATFORMS.exclude?(platform)
|
||||
raise "Unexpected arch: #{arch}" if VALID_ARCHES.exclude?(arch)
|
||||
|
||||
runner = GitHubRunner.new(platform:, arch:, spec:, macos_version:)
|
||||
runner.spec.testing_formulae += testable_formulae(runner)
|
||||
runner.active = active_runner?(runner)
|
||||
runner.freeze
|
||||
end
|
||||
|
||||
sig { params(macos_version: MacOSVersion).returns(T::Boolean) }
|
||||
def runner_enabled?(macos_version)
|
||||
macos_version.between?(OLDEST_HOMEBREW_CORE_MACOS_RUNNER, NEWEST_HOMEBREW_CORE_MACOS_RUNNER)
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def ephemeral_suffix
|
||||
@ephemeral_suffix ||= T.let(begin
|
||||
suffix = "-#{@github_run_id}"
|
||||
suffix << "-deps" if @dependent_matrix
|
||||
suffix << "-long" if @runner_timeout == GITHUB_ACTIONS_LONG_TIMEOUT
|
||||
suffix.freeze
|
||||
end, T.nilable(String))
|
||||
end
|
||||
|
||||
NEWEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER = :ventura
|
||||
OLDEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER = :ventura
|
||||
NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER = :tahoe
|
||||
OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER = :sonoma
|
||||
GITHUB_ACTIONS_RUNNER_TIMEOUT = 360
|
||||
private_constant :NEWEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER, :OLDEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER,
|
||||
:NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER, :OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER,
|
||||
:GITHUB_ACTIONS_RUNNER_TIMEOUT
|
||||
|
||||
sig { void }
|
||||
def generate_runners!
|
||||
return if @runners.present?
|
||||
@@ -284,6 +204,86 @@ class GitHubRunnerMatrix
|
||||
@runners.freeze
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# ARM macOS timeout, keep this under 1/2 of GitHub's job execution time limit for self-hosted runners.
|
||||
# https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits
|
||||
GITHUB_ACTIONS_LONG_TIMEOUT = 2160 # 36 hours
|
||||
GITHUB_ACTIONS_SHORT_TIMEOUT = 60
|
||||
private_constant :GITHUB_ACTIONS_LONG_TIMEOUT, :GITHUB_ACTIONS_SHORT_TIMEOUT
|
||||
|
||||
sig { params(arch: Symbol, self_hosted: T::Boolean).returns(LinuxRunnerSpec) }
|
||||
def linux_runner_spec(arch, self_hosted:)
|
||||
linux_runner = case arch
|
||||
when :arm64 then self_hosted ? "linux-arm64#{ephemeral_suffix}" : OS::LINUX_CI_ARM_RUNNER
|
||||
when :x86_64 then self_hosted ? "linux-x86_64#{ephemeral_suffix}" : "ubuntu-latest"
|
||||
else raise "Unknown Linux architecture: #{arch}"
|
||||
end
|
||||
|
||||
unless self_hosted
|
||||
container = {
|
||||
image: "ghcr.io/homebrew/brew:main",
|
||||
options: "--user linuxbrew --env HOMEBREW_SANDBOX_LINUX_LANDLOCK=1",
|
||||
}
|
||||
workdir = "/github/home"
|
||||
end
|
||||
|
||||
LinuxRunnerSpec.new(
|
||||
name: "Linux #{arch}",
|
||||
runner: linux_runner,
|
||||
container:,
|
||||
workdir:,
|
||||
timeout: GITHUB_ACTIONS_LONG_TIMEOUT,
|
||||
cleanup: false,
|
||||
)
|
||||
end
|
||||
|
||||
VALID_PLATFORMS = [:macos, :linux].freeze
|
||||
VALID_ARCHES = [:arm64, :x86_64].freeze
|
||||
private_constant :VALID_PLATFORMS, :VALID_ARCHES
|
||||
|
||||
sig {
|
||||
params(
|
||||
platform: Symbol,
|
||||
arch: Symbol,
|
||||
spec: RunnerSpec,
|
||||
macos_version: T.nilable(MacOSVersion),
|
||||
).returns(GitHubRunner)
|
||||
}
|
||||
def create_runner(platform, arch, spec, macos_version = nil)
|
||||
raise "Unexpected platform: #{platform}" if VALID_PLATFORMS.exclude?(platform)
|
||||
raise "Unexpected arch: #{arch}" if VALID_ARCHES.exclude?(arch)
|
||||
|
||||
runner = GitHubRunner.new(platform:, arch:, spec:, macos_version:)
|
||||
runner.spec.testing_formulae += testable_formulae(runner)
|
||||
runner.active = active_runner?(runner)
|
||||
runner.freeze
|
||||
end
|
||||
|
||||
sig { params(macos_version: MacOSVersion).returns(T::Boolean) }
|
||||
def runner_enabled?(macos_version)
|
||||
macos_version.between?(OLDEST_HOMEBREW_CORE_MACOS_RUNNER, NEWEST_HOMEBREW_CORE_MACOS_RUNNER)
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def ephemeral_suffix
|
||||
@ephemeral_suffix ||= T.let(begin
|
||||
suffix = "-#{@github_run_id}"
|
||||
suffix << "-deps" if @dependent_matrix
|
||||
suffix << "-long" if @runner_timeout == GITHUB_ACTIONS_LONG_TIMEOUT
|
||||
suffix.freeze
|
||||
end, T.nilable(String))
|
||||
end
|
||||
|
||||
NEWEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER = :ventura
|
||||
OLDEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER = :ventura
|
||||
NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER = :tahoe
|
||||
OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER = :sonoma
|
||||
GITHUB_ACTIONS_RUNNER_TIMEOUT = 360
|
||||
private_constant :NEWEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER, :OLDEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER,
|
||||
:NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER, :OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER,
|
||||
:GITHUB_ACTIONS_RUNNER_TIMEOUT
|
||||
|
||||
sig { params(runner: GitHubRunner).returns(T::Array[String]) }
|
||||
def testable_formulae(runner)
|
||||
formulae = if @dependent_matrix
|
||||
|
||||
+10
-10
@@ -720,6 +720,16 @@ module Homebrew
|
||||
ask_input(action:)
|
||||
end
|
||||
|
||||
sig { params(all_fatal: T::Boolean).void }
|
||||
def perform_preinstall_checks(all_fatal: false)
|
||||
check_prefix
|
||||
check_cpu
|
||||
attempt_directory_creation
|
||||
Diagnostic.checks(:supported_configuration_checks, fatal: all_fatal)
|
||||
Diagnostic.checks(:preinstall_checks, fatal: false)
|
||||
Diagnostic.checks(:fatal_preinstall_checks)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(action: String).returns(String) }
|
||||
@@ -741,16 +751,6 @@ module Homebrew
|
||||
.map { |k| Keg.new(k.resolved_path) }
|
||||
end
|
||||
|
||||
sig { params(all_fatal: T::Boolean).void }
|
||||
def perform_preinstall_checks(all_fatal: false)
|
||||
check_prefix
|
||||
check_cpu
|
||||
attempt_directory_creation
|
||||
Diagnostic.checks(:supported_configuration_checks, fatal: all_fatal)
|
||||
Diagnostic.checks(:preinstall_checks, fatal: false)
|
||||
Diagnostic.checks(:fatal_preinstall_checks)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def attempt_directory_creation
|
||||
Keg.must_exist_directories.each do |dir|
|
||||
|
||||
@@ -12,6 +12,11 @@ module Language
|
||||
module Node
|
||||
extend ::Utils::Output::Mixin
|
||||
|
||||
class << self
|
||||
sig { returns(T.nilable(T::Boolean)) }
|
||||
attr_accessor :env_set
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def self.npm_cache_config
|
||||
"cache=#{HOMEBREW_CACHE}/npm_cache"
|
||||
|
||||
@@ -34,13 +34,13 @@ module Homebrew
|
||||
private_constant :UNSTABLE_VERSION_KEYWORDS
|
||||
|
||||
sig { params(strategy_class: T::Class[Strategic]).returns(String) }
|
||||
private_class_method def self.livecheck_strategy_names(strategy_class)
|
||||
def self.livecheck_strategy_names(strategy_class)
|
||||
@livecheck_strategy_names ||= T.let({}, T.nilable(T::Hash[T::Class[Strategic], String]))
|
||||
@livecheck_strategy_names[strategy_class] ||= Utils.demodulize(strategy_class.name)
|
||||
end
|
||||
|
||||
sig { params(strategy_class: T::Class[Strategic]).returns(T::Array[Symbol]) }
|
||||
private_class_method def self.livecheck_find_versions_parameters(strategy_class)
|
||||
def self.livecheck_find_versions_parameters(strategy_class)
|
||||
@livecheck_find_versions_parameters ||= T.let({}, T.nilable(T::Hash[T::Class[Strategic], T::Array[Symbol]]))
|
||||
@livecheck_find_versions_parameters[strategy_class] ||=
|
||||
(T::Utils.signature_for_method(strategy_class.method(:find_versions))&.parameters ||
|
||||
@@ -1133,7 +1133,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig { params(package_or_resource: T.any(Formula, Cask::Cask)).returns(T.nilable(Integer)) }
|
||||
private_class_method def self.formula_or_cask_last_updated_timestamp(package_or_resource)
|
||||
def self.formula_or_cask_last_updated_timestamp(package_or_resource)
|
||||
tap = package_or_resource.tap
|
||||
return if tap.nil?
|
||||
return unless tap.git?
|
||||
@@ -1261,7 +1261,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig { params(package_or_resource: T.any(Formula, Cask::Cask), days: Integer).returns(T::Boolean) }
|
||||
private_class_method def self.throttle_interval_elapsed?(package_or_resource, days)
|
||||
def self.throttle_interval_elapsed?(package_or_resource, days)
|
||||
return false if days <= 0
|
||||
|
||||
last_updated_timestamp = formula_or_cask_last_updated_timestamp(package_or_resource)
|
||||
|
||||
@@ -32,6 +32,11 @@ module Homebrew
|
||||
# Used to cache processed URLs, to avoid duplicating effort.
|
||||
@processed_urls = T.let({}, T::Hash[String, String])
|
||||
|
||||
class << self
|
||||
sig { params(processed_urls: T::Hash[String, String]).void }
|
||||
attr_writer :processed_urls
|
||||
end
|
||||
|
||||
# The priority of the strategy on an informal scale of 1 to 10 (from
|
||||
# lowest to highest).
|
||||
PRIORITY = 8
|
||||
|
||||
@@ -63,6 +63,11 @@ module Homebrew
|
||||
# repeatedly.
|
||||
@page_data = T.let({}, T::Hash[String, String])
|
||||
|
||||
class << self
|
||||
sig { params(page_data: T::Hash[String, String]).void }
|
||||
attr_writer :page_data
|
||||
end
|
||||
|
||||
# Whether the strategy can be applied to the provided URL.
|
||||
#
|
||||
# @param url [String] the URL to match against
|
||||
|
||||
@@ -54,6 +54,12 @@ class MacOSVersion < Version
|
||||
new(str)
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[T.untyped, T.nilable(Integer)]) }
|
||||
attr_reader :comparison_cache
|
||||
|
||||
sig { returns(T.nilable(Symbol)) }
|
||||
attr_reader :sym
|
||||
|
||||
sig { params(version: T.nilable(String)).void }
|
||||
def initialize(version)
|
||||
raise MacOSVersion::Error, version unless /\A\d{2,}(?:\.\d+){0,2}\z/.match?(version)
|
||||
|
||||
@@ -19,6 +19,11 @@ module OS
|
||||
/system/bin/linker
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
sig { params(system_ld_so: T.nilable(::Pathname)).returns(T.nilable(::Pathname)) }
|
||||
attr_writer :system_ld_so
|
||||
end
|
||||
|
||||
# The path to the system's dynamic linker or `nil` if not found
|
||||
sig { returns(T.nilable(::Pathname)) }
|
||||
def self.system_ld_so
|
||||
|
||||
@@ -10,6 +10,11 @@ module OS
|
||||
SOVERSION = 6
|
||||
SONAME = T.let("libstdc++.so.#{SOVERSION}".freeze, String)
|
||||
|
||||
class << self
|
||||
sig { params(system_version: T.nilable(Version)).returns(T.nilable(Version)) }
|
||||
attr_writer :system_version
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def self.below_ci_version?
|
||||
system_version < LINUX_LIBSTDCXX_CI_VERSION
|
||||
|
||||
@@ -116,8 +116,6 @@ module Homebrew
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(keg: Keg).void }
|
||||
def backup(keg)
|
||||
keg.unlink
|
||||
@@ -132,6 +130,8 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(keg: Keg, keg_was_linked: T::Boolean, verbose: T::Boolean).void }
|
||||
def restore_backup(keg, keg_was_linked, verbose:)
|
||||
path = backup_path(keg)
|
||||
|
||||
@@ -357,6 +357,9 @@ class Resource
|
||||
sig { returns(Bottle) }
|
||||
attr_reader :bottle
|
||||
|
||||
sig { params(manifest_annotations: T.nilable(T::Hash[String, String])).void }
|
||||
attr_writer :manifest_annotations
|
||||
|
||||
sig { params(bottle: Bottle).void }
|
||||
def initialize(bottle)
|
||||
super("#{bottle.name}_bottle_manifest")
|
||||
|
||||
@@ -14,6 +14,8 @@ require_relative "io_read"
|
||||
require_relative "move_to_extend_os"
|
||||
require_relative "negate_include"
|
||||
require_relative "no_fileutils_rmrf"
|
||||
require_relative "no_instance_variable_access_in_tests"
|
||||
require_relative "no_send_in_tests"
|
||||
require_relative "non_public_api_usage"
|
||||
require_relative "presence"
|
||||
require_relative "public_api_cookbook"
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Homebrew
|
||||
# Flags `instance_variable_get`/`instance_variable_set` in tests. Tests should read
|
||||
# and write object state through public accessors: add a public `attr_reader`/
|
||||
# `attr_writer` (or use an existing accessor) on the class instead of reaching into
|
||||
# its instance variables.
|
||||
#
|
||||
# ### Example
|
||||
#
|
||||
# ```ruby
|
||||
# # bad
|
||||
# formula.instance_variable_set(:@tap, CoreTap.instance)
|
||||
#
|
||||
# # good (with a public `attr_writer :tap`)
|
||||
# formula.tap = CoreTap.instance
|
||||
# ```
|
||||
class NoInstanceVariableAccessInTests < Base
|
||||
MSG = "Use a public `attr_reader`/`attr_writer` (or an existing accessor) instead of " \
|
||||
"`%<method>s` in tests."
|
||||
RESTRICT_ON_SEND = [:instance_variable_get, :instance_variable_set].freeze
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).void }
|
||||
def on_send(node)
|
||||
add_offense(node.loc.selector, message: format(MSG, method: node.method_name))
|
||||
end
|
||||
alias on_csend on_send
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,73 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Homebrew
|
||||
# Flags `send`-family dispatch in tests. Tests should exercise methods the way real
|
||||
# callers do: a private method poked via `send` should be made public and called
|
||||
# directly instead.
|
||||
#
|
||||
# - `send`/`__send__` are always flagged: with a static method name the call can be
|
||||
# written directly (after making the method public if needed); with a dynamic one
|
||||
# it must go through `public_send` so it cannot bypass method visibility.
|
||||
# - `public_send` is flagged only when the method name is a literal that could have
|
||||
# been written as a direct call. A dynamic name (`public_send(method_name)`,
|
||||
# `public_send(:"#{artifact_dsl_key}_phase")`) is the one legitimate use:
|
||||
# parameterised dispatch to public API. A literal name with no direct call syntax
|
||||
# (e.g. `:"gcc-9"`) is also allowed, as no direct call can spell it.
|
||||
#
|
||||
# ### Example
|
||||
#
|
||||
# ```ruby
|
||||
# # bad
|
||||
# formula.send(:active_spec)
|
||||
#
|
||||
# # good (with `active_spec` made public)
|
||||
# formula.active_spec
|
||||
#
|
||||
# # good (dynamic dispatch to public API in a parameterised example)
|
||||
# subject.public_send(:"#{artifact_dsl_key}_phase")
|
||||
# ```
|
||||
class NoSendInTests < Base
|
||||
MSG_SEND = "Make the method public and call it directly instead of using `%<method>s` in tests."
|
||||
MSG_SEND_DYNAMIC = "Use `public_send` instead of `%<method>s` in tests; " \
|
||||
"`%<method>s` bypasses method visibility."
|
||||
MSG_PUBLIC_SEND = "Call the method directly instead of using `public_send` with a static method name."
|
||||
RESTRICT_ON_SEND = [:send, :__send__, :public_send].freeze
|
||||
|
||||
# A literal method name that direct call syntax can spell, including setters
|
||||
# (`public_send(:foo=, value)` can be written `receiver.foo = value`).
|
||||
DIRECTLY_CALLABLE_NAME = /\A[a-zA-Z_][a-zA-Z0-9_]*[?!=]?\z/
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).void }
|
||||
def on_send(node)
|
||||
directly_callable = directly_callable_name?(node.first_argument)
|
||||
|
||||
message = if node.method_name == :public_send
|
||||
return unless directly_callable
|
||||
|
||||
MSG_PUBLIC_SEND
|
||||
elsif directly_callable
|
||||
format(MSG_SEND, method: node.method_name)
|
||||
else
|
||||
format(MSG_SEND_DYNAMIC, method: node.method_name)
|
||||
end
|
||||
|
||||
add_offense(node.loc.selector, message:)
|
||||
end
|
||||
alias on_csend on_send
|
||||
|
||||
private
|
||||
|
||||
sig { params(argument: T.nilable(RuboCop::AST::Node)).returns(T::Boolean) }
|
||||
def directly_callable_name?(argument)
|
||||
return false unless argument
|
||||
return false if !argument.sym_type? && !argument.str_type?
|
||||
|
||||
argument.children.first.to_s.match?(DIRECTLY_CALLABLE_NAME)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+10
-10
@@ -595,11 +595,19 @@ class Sandbox
|
||||
SandboxPathFilter.new(path: filter_path, type:)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(SandboxProfile) }
|
||||
attr_reader :profile
|
||||
|
||||
sig { params(controller: IO).void }
|
||||
def copy_pty_output(controller)
|
||||
controller.each_char { |c| print(c) }
|
||||
rescue Errno::EIO
|
||||
# Linux marks a PTY as an I/O error when its peer closes, so treat this as EOF:
|
||||
# https://github.com/torvalds/linux/blob/master/drivers/tty/pty.c
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
attr_reader :failed
|
||||
|
||||
@@ -630,14 +638,6 @@ class Sandbox
|
||||
sig { void }
|
||||
def apply_sandbox; end
|
||||
|
||||
sig { params(controller: IO).void }
|
||||
def copy_pty_output(controller)
|
||||
controller.each_char { |c| print(c) }
|
||||
rescue Errno::EIO
|
||||
# Linux marks a PTY as an I/O error when its peer closes, so treat this as EOF:
|
||||
# https://github.com/torvalds/linux/blob/master/drivers/tty/pty.c
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def record_sandbox_log; end
|
||||
|
||||
|
||||
@@ -613,7 +613,6 @@ class Tap
|
||||
|
||||
SystemCommand.run!("git", args:, chdir:, env: { "GIT_TERMINAL_PROMPT" => "0" }, print_stderr: true)
|
||||
end
|
||||
private :git_command!
|
||||
|
||||
# Install this {Tap}.
|
||||
#
|
||||
|
||||
@@ -58,8 +58,8 @@ RSpec.describe Homebrew::API::CaskStruct do
|
||||
)
|
||||
|
||||
Homebrew::API::CaskStruct::PREDICATES.each do |predicate|
|
||||
expect(struct.send(:"#{predicate}?")).to be(false),
|
||||
"expected #{predicate}? to default to false"
|
||||
expect(struct.public_send(:"#{predicate}?")).to be(false),
|
||||
"expected #{predicate}? to default to false"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,8 +76,8 @@ RSpec.describe Homebrew::API::CaskStruct do
|
||||
)
|
||||
|
||||
Homebrew::API::CaskStruct::PREDICATES.each do |predicate|
|
||||
expect(struct.send(:"#{predicate}?")).to be(true),
|
||||
"expected #{predicate}? to be true"
|
||||
expect(struct.public_send(:"#{predicate}?")).to be(true),
|
||||
"expected #{predicate}? to be true"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -232,7 +232,7 @@ RSpec.describe Homebrew::API::CaskStruct do
|
||||
struct = described_class.deserialize(hash)
|
||||
|
||||
Homebrew::API::CaskStruct::PREDICATES.each do |predicate|
|
||||
expect(struct.send(:"#{predicate}?")).to be false
|
||||
expect(struct.public_send(:"#{predicate}?")).to be false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -255,7 +255,7 @@ RSpec.describe Homebrew::API::CaskStruct do
|
||||
struct = described_class.deserialize(hash)
|
||||
|
||||
Homebrew::API::CaskStruct::PREDICATES.each do |predicate|
|
||||
expect(struct.send(:"#{predicate}?")).to be true
|
||||
expect(struct.public_send(:"#{predicate}?")).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -106,8 +106,8 @@ RSpec.describe Homebrew::API::FormulaStruct do
|
||||
)
|
||||
|
||||
Homebrew::API::FormulaStruct::PREDICATES.each do |predicate|
|
||||
expect(struct.send(:"#{predicate}?")).to be(false),
|
||||
"expected #{predicate}? to default to false"
|
||||
expect(struct.public_send(:"#{predicate}?")).to be(false),
|
||||
"expected #{predicate}? to default to false"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -126,8 +126,8 @@ RSpec.describe Homebrew::API::FormulaStruct do
|
||||
)
|
||||
|
||||
Homebrew::API::FormulaStruct::PREDICATES.each do |predicate|
|
||||
expect(struct.send(:"#{predicate}?")).to be(true),
|
||||
"expected #{predicate}? to be true"
|
||||
expect(struct.public_send(:"#{predicate}?")).to be(true),
|
||||
"expected #{predicate}? to be true"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,14 +7,15 @@ RSpec.describe Homebrew::Bump do
|
||||
describe "::redacted_url" do
|
||||
it "masks env-token credentials embedded in a push URL" do
|
||||
allow(GitHub::API).to receive(:credentials).and_return("ghp_secrettoken")
|
||||
expect(described_class.send(:redacted_url,
|
||||
"https://x-access-token:ghp_secrettoken@github.com/Homebrew/homebrew-core"))
|
||||
expect(described_class.redacted_url(
|
||||
"https://x-access-token:ghp_secrettoken@github.com/Homebrew/homebrew-core",
|
||||
))
|
||||
.to eq("https://x-access-token:******@github.com/Homebrew/homebrew-core")
|
||||
end
|
||||
|
||||
it "leaves a credential-free URL unchanged" do
|
||||
allow(GitHub::API).to receive(:credentials).and_return(nil)
|
||||
expect(described_class.send(:redacted_url, "https://github.com/Homebrew/homebrew-core"))
|
||||
expect(described_class.redacted_url("https://github.com/Homebrew/homebrew-core"))
|
||||
.to eq("https://github.com/Homebrew/homebrew-core")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -189,20 +189,19 @@ RSpec.describe Homebrew::Bundle::Dsl do
|
||||
end
|
||||
|
||||
it ".sanitize_brew_name" do
|
||||
expect(described_class.send(:sanitize_brew_name, "homebrew/homebrew/foo")).to eql("foo")
|
||||
expect(described_class.send(:sanitize_brew_name, "homebrew/homebrew-bar/foo")).to eql("homebrew/bar/foo")
|
||||
expect(described_class.send(:sanitize_brew_name, "homebrew/bar/foo")).to eql("homebrew/bar/foo")
|
||||
expect(described_class.send(:sanitize_brew_name, "foo")).to eql("foo")
|
||||
expect(described_class.sanitize_brew_name("homebrew/homebrew/foo")).to eql("foo")
|
||||
expect(described_class.sanitize_brew_name("homebrew/homebrew-bar/foo")).to eql("homebrew/bar/foo")
|
||||
expect(described_class.sanitize_brew_name("homebrew/bar/foo")).to eql("homebrew/bar/foo")
|
||||
expect(described_class.sanitize_brew_name("foo")).to eql("foo")
|
||||
end
|
||||
|
||||
it ".sanitize_tap_name" do
|
||||
expect(described_class.send(:sanitize_tap_name, "homebrew/homebrew-foo")).to eql("homebrew/foo")
|
||||
expect(described_class.send(:sanitize_tap_name, "homebrew/foo")).to eql("homebrew/foo")
|
||||
expect(described_class.sanitize_tap_name("homebrew/homebrew-foo")).to eql("homebrew/foo")
|
||||
expect(described_class.sanitize_tap_name("homebrew/foo")).to eql("homebrew/foo")
|
||||
end
|
||||
|
||||
it ".sanitize_cask_name" do
|
||||
expect(described_class.send(:sanitize_cask_name,
|
||||
"homebrew/cask-versions/adoptopenjdk8")).to eql("adoptopenjdk8")
|
||||
expect(described_class.send(:sanitize_cask_name, "adoptopenjdk8")).to eql("adoptopenjdk8")
|
||||
expect(described_class.sanitize_cask_name("homebrew/cask-versions/adoptopenjdk8")).to eql("adoptopenjdk8")
|
||||
expect(described_class.sanitize_cask_name("adoptopenjdk8")).to eql("adoptopenjdk8")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -280,7 +280,7 @@ RSpec.describe Homebrew::Bundle::Installer do
|
||||
output = IO.pipe do |reader, writer|
|
||||
allow(writer).to receive(:tty?).and_return(true)
|
||||
|
||||
parallel_installer.send(:write_output, "Installing alpha", stream: writer)
|
||||
parallel_installer.write_output("Installing alpha", stream: writer)
|
||||
writer.close
|
||||
|
||||
reader.read
|
||||
@@ -293,7 +293,7 @@ RSpec.describe Homebrew::Bundle::Installer do
|
||||
output = IO.pipe do |reader, writer|
|
||||
allow(writer).to receive(:tty?).and_return(false)
|
||||
|
||||
parallel_installer.send(:write_output, "Installing alpha", stream: writer)
|
||||
parallel_installer.write_output("Installing alpha", stream: writer)
|
||||
writer.close
|
||||
|
||||
reader.read
|
||||
@@ -417,7 +417,7 @@ RSpec.describe Homebrew::Bundle::Installer do
|
||||
dependency_map = Homebrew::Bundle::ParallelInstaller.new(
|
||||
entries,
|
||||
jobs: 2, no_upgrade: false, verbose: false, force: false, quiet: true,
|
||||
).send(:build_dependency_map, entries)
|
||||
).build_dependency_map(entries)
|
||||
|
||||
expect(dependency_map.fetch("beta")).to eq(Set["alpha"])
|
||||
end
|
||||
@@ -440,7 +440,7 @@ RSpec.describe Homebrew::Bundle::Installer do
|
||||
dependency_map = Homebrew::Bundle::ParallelInstaller.new(
|
||||
entries,
|
||||
jobs: 2, no_upgrade: false, verbose: false, force: false, quiet: true,
|
||||
).send(:build_dependency_map, entries)
|
||||
).build_dependency_map(entries)
|
||||
|
||||
expect(dependency_map.fetch("alpha")).to eq(Set["gh"])
|
||||
end
|
||||
|
||||
@@ -13,8 +13,8 @@ RSpec.describe Homebrew::Bundle::Skipper do
|
||||
allow(ENV).to receive(:[]).with("HOMEBREW_BUNDLE_BREW_SKIP").and_return("mysql")
|
||||
allow(ENV).to receive(:[]).with("HOMEBREW_BUNDLE_TAP_SKIP").and_return("org/repo")
|
||||
allow(Formatter).to receive(:warning)
|
||||
skipper.instance_variable_set(:@skipped_entries, nil)
|
||||
skipper.instance_variable_set(:@failed_taps, nil)
|
||||
skipper.skipped_entries = nil
|
||||
skipper.failed_taps = nil
|
||||
end
|
||||
|
||||
describe ".skip?" do
|
||||
|
||||
@@ -99,7 +99,7 @@ RSpec.describe CacheStoreDatabase do
|
||||
|
||||
context "without an open database" do
|
||||
before do
|
||||
sample_db.instance_variable_set(:@db, nil)
|
||||
sample_db.db = nil
|
||||
end
|
||||
|
||||
it "does not raise an error when `close` is called on the database" do
|
||||
|
||||
@@ -21,7 +21,7 @@ RSpec.describe Cask::Artifact::AbstractUninstall, :cask do
|
||||
|
||||
it "skips relative paths" do
|
||||
expect do
|
||||
expect(artifact.send(:each_resolved_path, :delete, ["relative/path"]).to_a).to be_empty
|
||||
expect(artifact.each_resolved_path(:delete, ["relative/path"]).to_a).to be_empty
|
||||
end.to output(%r{Skipping delete for relative path 'relative/path'\.}).to_stderr
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ RSpec.describe Cask::Artifact::AbstractUninstall, :cask do
|
||||
tmpdir/"nested/./#{valid_path.basename}",
|
||||
].each do |invalid_path|
|
||||
expect do
|
||||
expect(artifact.send(:each_resolved_path, :delete, [invalid_path.to_s]).to_a).to be_empty
|
||||
expect(artifact.each_resolved_path(:delete, [invalid_path.to_s]).to_a).to be_empty
|
||||
end.to output(
|
||||
/Skipping delete for path with relative segments '#{Regexp.escape(invalid_path.to_s)}'\./,
|
||||
).to_stderr
|
||||
@@ -49,7 +49,7 @@ RSpec.describe Cask::Artifact::AbstractUninstall, :cask do
|
||||
invalid_path = "~/../each_resolved_path_#{artifact_dsl_key}"
|
||||
|
||||
expect do
|
||||
expect(artifact.send(:each_resolved_path, :delete, [invalid_path]).to_a).to be_empty
|
||||
expect(artifact.each_resolved_path(:delete, [invalid_path]).to_a).to be_empty
|
||||
end.to output(
|
||||
/Skipping delete for path with relative segments '#{Regexp.escape(invalid_path)}'\./,
|
||||
).to_stderr
|
||||
@@ -66,7 +66,7 @@ RSpec.describe Cask::Artifact::AbstractUninstall, :cask do
|
||||
allow(artifact).to receive(:undeletable?) { |target| target == undeletable_path }
|
||||
|
||||
expect do
|
||||
expect(artifact.send(:each_resolved_path, :delete, ["#{glob_dir}/*.plist"]).to_a)
|
||||
expect(artifact.each_resolved_path(:delete, ["#{glob_dir}/*.plist"]).to_a)
|
||||
.to eq([["#{glob_dir}/*.plist", [safe_path]]])
|
||||
end.to output(
|
||||
/Skipping delete for undeletable path '#{Regexp.escape(undeletable_path.to_s)}'\./,
|
||||
@@ -81,7 +81,7 @@ RSpec.describe Cask::Artifact::AbstractUninstall, :cask do
|
||||
allow(MacOS).to receive(:version).and_return(MacOSVersion.from_symbol(:ventura))
|
||||
|
||||
expect do
|
||||
artifact.send(:each_resolved_path, :delete, ["/tmp/each_resolved_path_#{artifact_dsl_key}"]).to_a
|
||||
artifact.each_resolved_path(:delete, ["/tmp/each_resolved_path_#{artifact_dsl_key}"]).to_a
|
||||
end.to raise_error(SystemExit)
|
||||
.and output(/Full Disk Access/).to_stderr
|
||||
end
|
||||
|
||||
@@ -315,7 +315,7 @@ RSpec.describe Cask::Artifact::App, :cask do
|
||||
|
||||
allow(MacOS).to receive(:version).and_return(MacOSVersion.from_symbol(:sonoma))
|
||||
|
||||
expect(app.send(:backup_copy_args, target_path, source_path)).to eq(["-c", "-pR", target_path, source_path])
|
||||
expect(app.backup_copy_args(target_path, source_path)).to eq(["-c", "-pR", target_path, source_path])
|
||||
end
|
||||
|
||||
it "uses portable copy arguments on older macOS versions", :needs_macos do
|
||||
@@ -323,7 +323,7 @@ RSpec.describe Cask::Artifact::App, :cask do
|
||||
|
||||
allow(MacOS).to receive(:version).and_return(MacOSVersion.from_symbol(:ventura))
|
||||
|
||||
expect(app.send(:backup_copy_args, target_path, source_path)).to eq(["-pR", target_path, source_path])
|
||||
expect(app.backup_copy_args(target_path, source_path)).to eq(["-pR", target_path, source_path])
|
||||
end
|
||||
|
||||
it "uses portable copy arguments across filesystems", :needs_macos do
|
||||
@@ -335,7 +335,7 @@ RSpec.describe Cask::Artifact::App, :cask do
|
||||
allow(source_path).to receive(:dirname).and_return(source_dir)
|
||||
allow(source_dir).to receive(:stat).and_return(instance_double(File::Stat, dev: 2))
|
||||
|
||||
expect(app.send(:backup_copy_args, target_path, source_path)).to eq(["-pR", target_path, source_path])
|
||||
expect(app.backup_copy_args(target_path, source_path)).to eq(["-pR", target_path, source_path])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -5,13 +5,11 @@ RSpec.describe Cask::Artifact::GeneratedCompletion, :cask do
|
||||
let(:staged_path) { Pathname(Dir.mktmpdir) }
|
||||
|
||||
let(:cask) do
|
||||
tmp_staged = staged_path
|
||||
Cask::Cask.new("test-generated-completion") do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "file:///dev/null"
|
||||
generate_completions_from_executable "bin/foo", "completions"
|
||||
instance_variable_set(:@staged_path, tmp_staged)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,6 +18,7 @@ RSpec.describe Cask::Artifact::GeneratedCompletion, :cask do
|
||||
let(:fish_dir) { cask.config.fish_completion }
|
||||
|
||||
before do
|
||||
allow(cask).to receive(:staged_path).and_return(staged_path)
|
||||
(staged_path/"bin").mkpath
|
||||
(staged_path/"bin/foo").write("#!/bin/sh\necho \"$SHELL completion\"")
|
||||
(staged_path/"bin/foo").chmod(0755)
|
||||
@@ -134,14 +133,12 @@ RSpec.describe Cask::Artifact::GeneratedCompletion, :cask do
|
||||
|
||||
context "with specific shells and format" do
|
||||
let(:cask) do
|
||||
tmp_staged = staged_path
|
||||
Cask::Cask.new("test-generated-completion") do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "file:///dev/null"
|
||||
generate_completions_from_executable "bin/foo", "completions",
|
||||
shells: [:zsh], shell_parameter_format: :arg, base_name: "bar"
|
||||
instance_variable_set(:@staged_path, tmp_staged)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -175,14 +172,12 @@ RSpec.describe Cask::Artifact::GeneratedCompletion, :cask do
|
||||
|
||||
context "with string shells" do
|
||||
let(:cask) do
|
||||
tmp_staged = staged_path
|
||||
Cask::Cask.new("test-generated-completion") do
|
||||
version "1.0"
|
||||
sha256 :no_check
|
||||
url "file:///dev/null"
|
||||
generate_completions_from_executable "bin/foo", "completions",
|
||||
shells: %w[bash zsh fish pwsh]
|
||||
instance_variable_set(:@staged_path, tmp_staged)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ RSpec.describe Cask::Artifact::Relocated, :cask do
|
||||
expect(command).not_to receive(:run)
|
||||
expect(command).not_to receive(:run!)
|
||||
|
||||
artifact.send(:add_altname_metadata, file, altname, command: command)
|
||||
artifact.add_altname_metadata(file, altname, command: command)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,7 +46,7 @@ RSpec.describe Cask::Artifact::Relocated, :cask do
|
||||
print_stderr: false)
|
||||
expect(command).to receive(:run!).twice
|
||||
|
||||
artifact.send(:add_altname_metadata, file, altname, command: command)
|
||||
artifact.add_altname_metadata(file, altname, command: command)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -186,9 +186,8 @@ RSpec.shared_examples "#uninstall_phase or #zap_phase" do
|
||||
.with("/bin/launchctl", args: ["list"])
|
||||
.and_return(instance_double(SystemCommand::Result, stdout: launchctl_list))
|
||||
|
||||
expect(subject.send(:find_launchctl_with_wildcard,
|
||||
"my.fancy.package.service.*")).to eq(["my.fancy.package.service.12345",
|
||||
"my.fancy.package.service.test"])
|
||||
expect(subject.find_launchctl_with_wildcard("my.fancy.package.service.*"))
|
||||
.to eq(["my.fancy.package.service.12345", "my.fancy.package.service.test"])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ RSpec.describe Cask::Artifact::Uninstall, :cask do
|
||||
allow(artifact).to receive(:quit).with(bundle_id)
|
||||
.and_return(instance_double(SystemCommand::Result, success?: true))
|
||||
|
||||
artifact.send(:uninstall_quit, bundle_id, upgrade: true, command: fake_system_command)
|
||||
artifact.uninstall_quit(bundle_id, upgrade: true, command: fake_system_command)
|
||||
|
||||
expect(artifact.bundle_ids_to_reopen).to eq [bundle_id]
|
||||
end
|
||||
@@ -159,7 +159,7 @@ RSpec.describe Cask::Artifact::Uninstall, :cask do
|
||||
allow(artifact).to receive(:quit).with(bundle_id)
|
||||
.and_return(instance_double(SystemCommand::Result, success?: true))
|
||||
|
||||
artifact.send(:uninstall_quit, bundle_id, upgrade: false, command: fake_system_command)
|
||||
artifact.uninstall_quit(bundle_id, upgrade: false, command: fake_system_command)
|
||||
|
||||
expect(artifact.bundle_ids_to_reopen).to be_empty
|
||||
end
|
||||
@@ -171,7 +171,7 @@ RSpec.describe Cask::Artifact::Uninstall, :cask do
|
||||
allow(Timeout).to receive(:timeout).and_raise(Timeout::Error)
|
||||
|
||||
expect do
|
||||
artifact.send(:uninstall_quit, bundle_id, upgrade: true, command: fake_system_command)
|
||||
artifact.uninstall_quit(bundle_id, upgrade: true, command: fake_system_command)
|
||||
end.to output(/did not quit/).to_stderr
|
||||
|
||||
expect(artifact.bundle_ids_to_reopen).to be_empty
|
||||
|
||||
@@ -542,7 +542,7 @@ RSpec.describe Cask::Audit, :cask do
|
||||
allow(Cask::Quarantine).to receive(:available?).and_return(false)
|
||||
expect(Cask::Quarantine).not_to receive(:detect)
|
||||
|
||||
audit.send(:extract_artifacts)
|
||||
audit.extract_artifacts
|
||||
end
|
||||
end
|
||||
|
||||
@@ -555,9 +555,9 @@ RSpec.describe Cask::Audit, :cask do
|
||||
let(:cask_token) { "basic-cask" }
|
||||
|
||||
it "returns existing `@livecheck_result` value" do
|
||||
audit.instance_variable_set(:@livecheck_result, :auto_detected)
|
||||
audit.livecheck_result = :auto_detected
|
||||
expect(run).not_to error_with(message)
|
||||
audit.instance_variable_set(:@livecheck_result, nil)
|
||||
audit.livecheck_result = nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1237,7 +1237,7 @@ RSpec.describe Cask::Audit, :cask do
|
||||
end
|
||||
|
||||
it "normalizes 10.16.0 minimum macOS to Big Sur" do
|
||||
expect(audit.send(:normalize_min_os, "10.16.0")).to eq(MacOSVersion.from_symbol(:big_sur))
|
||||
expect(audit.normalize_min_os("10.16.0")).to eq(MacOSVersion.from_symbol(:big_sur))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -39,35 +39,31 @@ RSpec.describe Cask::Auditor, :cask do
|
||||
|
||||
it "returns true if @any_named_args is true" do
|
||||
auditor_obj = auditor.new(cask, any_named_args: true)
|
||||
expect(auditor_obj.send(:output_summary?)).to be(true)
|
||||
expect(auditor_obj.output_summary?).to be(true)
|
||||
end
|
||||
|
||||
it "returns true if @audit_strict is true" do
|
||||
auditor_obj = auditor.new(cask, audit_strict: true)
|
||||
expect(auditor_obj.send(:output_summary?)).to be(true)
|
||||
expect(auditor_obj.output_summary?).to be(true)
|
||||
end
|
||||
|
||||
it "returns false if the audit argument is nil" do
|
||||
auditor_obj = auditor.new(cask)
|
||||
expect(auditor_obj.send(:output_summary?)).to be(false)
|
||||
expect(auditor_obj.send(:output_summary?, nil)).to be(false)
|
||||
expect(auditor_obj.output_summary?).to be(false)
|
||||
expect(auditor_obj.output_summary?(nil)).to be(false)
|
||||
end
|
||||
|
||||
it "returns false if there are no audit errors" do
|
||||
auditor_obj = auditor.new(cask)
|
||||
audit = Cask::Audit.new(cask)
|
||||
expect(auditor_obj.send(:output_summary?, audit)).to be(false)
|
||||
expect(auditor_obj.output_summary?(audit)).to be(false)
|
||||
end
|
||||
|
||||
it "returns true if there are audit errors" do
|
||||
auditor_obj = auditor.new(cask)
|
||||
audit = Cask::Audit.new(cask)
|
||||
audit.instance_variable_set(:@errors, [{
|
||||
message: nil,
|
||||
location: nil,
|
||||
corrected: false,
|
||||
}])
|
||||
expect(auditor_obj.send(:output_summary?, audit)).to be(true)
|
||||
audit.add_error(nil)
|
||||
expect(auditor_obj.output_summary?(audit)).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -425,7 +425,7 @@ RSpec.describe Cask::CaskLoader, :cask do
|
||||
|
||||
it "raises CaskUnreadableError when loaded from installed caskfile" do
|
||||
loader = Cask::CaskLoader::FromPathLoader.new(cask_file)
|
||||
loader.instance_variable_set(:@from_installed_caskfile, true)
|
||||
loader.from_installed_caskfile = true
|
||||
expect { loader.load(config: nil) }.to raise_error(Cask::CaskUnreadableError, /appcast/)
|
||||
end
|
||||
end
|
||||
@@ -491,7 +491,7 @@ RSpec.describe Cask::CaskLoader, :cask do
|
||||
|
||||
it "raises CaskUnreadableError when loaded from installed caskfile" do
|
||||
loader = Cask::CaskLoader::FromPathLoader.new(cask_file)
|
||||
loader.instance_variable_set(:@from_installed_caskfile, true)
|
||||
loader.from_installed_caskfile = true
|
||||
expect { loader.load(config: nil) }.to raise_error(Cask::CaskUnreadableError, /Unknown key: :formula/)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
require "cask/caskroom"
|
||||
|
||||
RSpec.describe Cask::Caskroom do
|
||||
before { described_class.instance_variable_set(:@expected_caskroom_group, nil) }
|
||||
before { described_class.expected_caskroom_group = nil }
|
||||
|
||||
describe ".ensure_caskroom_exists" do
|
||||
it "changes the group when sudo is unnecessary and the group is wrong" do
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
RSpec.describe Cask::Download, :cask do
|
||||
describe "#download_name" do
|
||||
subject(:download_name) { described_class.new(cask).send(:download_name) }
|
||||
subject(:download_name) { described_class.new(cask).download_name }
|
||||
|
||||
let(:token) { "example-cask" }
|
||||
let(:full_token) { token }
|
||||
|
||||
@@ -73,7 +73,7 @@ RSpec.describe Cask::DSL::Version, :cask do
|
||||
end
|
||||
|
||||
shared_examples "version expectations hash" do |method, hash|
|
||||
subject { version.send(method) }
|
||||
subject { version.public_send(method) }
|
||||
|
||||
include_examples "expectations hash", :raw_version,
|
||||
{ :latest => "latest",
|
||||
|
||||
@@ -736,7 +736,7 @@ RSpec.describe Cask::DSL, :cask, :no_api do
|
||||
|
||||
it "allows installer manual to be specified" do
|
||||
installer = cask.artifacts.first
|
||||
expect(installer.instance_variable_get(:@manual_install)).to be true
|
||||
expect(installer.manual_install).to be true
|
||||
expect(installer.path).to eq(Pathname("Caffeine.app"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -921,7 +921,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
allow(cask).to receive(:staged_path).and_return(staged_path)
|
||||
|
||||
installer = described_class.new(cask)
|
||||
installer.send(:process_rename_operations)
|
||||
installer.process_rename_operations
|
||||
|
||||
expect(staged_path / "Renamed App.app").to be_a_directory
|
||||
expect(staged_path / "Original App.app").not_to exist
|
||||
@@ -941,7 +941,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
allow(cask).to receive(:staged_path).and_return(staged_path)
|
||||
|
||||
installer = described_class.new(cask)
|
||||
installer.send(:process_rename_operations)
|
||||
installer.process_rename_operations
|
||||
|
||||
expect(staged_path / "Final Name.app").to be_a_directory
|
||||
expect(staged_path / "Original.app").not_to exist
|
||||
@@ -961,7 +961,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
allow(cask).to receive(:staged_path).and_return(staged_path)
|
||||
|
||||
installer = described_class.new(cask)
|
||||
installer.send(:process_rename_operations)
|
||||
installer.process_rename_operations
|
||||
|
||||
expect(staged_path / "Test App.pkg").to be_a_file
|
||||
expect((staged_path / "Test App.pkg").read).to eq("test content")
|
||||
@@ -982,7 +982,7 @@ RSpec.describe Cask::Installer, :cask do
|
||||
|
||||
installer = described_class.new(cask)
|
||||
|
||||
expect { installer.send(:process_rename_operations) }.not_to raise_error
|
||||
expect { installer.process_rename_operations }.not_to raise_error
|
||||
expect(staged_path / "Different.app").to be_a_directory
|
||||
expect(staged_path / "Target.app").not_to exist
|
||||
end
|
||||
|
||||
@@ -102,7 +102,7 @@ RSpec.describe Homebrew::Cmd::Bundle::CheckSubcommand, :no_api do
|
||||
end
|
||||
|
||||
it "raises an error for an implicitly unlinked non-keg-only formula" do
|
||||
Homebrew::Bundle::Brew.instance_variable_set(:@formulae_by_name, { "abc" => { link?: false } })
|
||||
Homebrew::Bundle::Brew.formulae_by_name = { "abc" => { link?: false } }
|
||||
allow_any_instance_of(Pathname).to receive(:read).and_return("brew 'abc'")
|
||||
allow(Formula["abc"]).to receive(:linked?).and_return(false)
|
||||
|
||||
@@ -111,7 +111,7 @@ RSpec.describe Homebrew::Cmd::Bundle::CheckSubcommand, :no_api do
|
||||
end
|
||||
|
||||
it "does not raise an error when live link status satisfies an implicit check" do
|
||||
Homebrew::Bundle::Brew.instance_variable_set(:@formulae_by_name, { "abc" => { link?: false } })
|
||||
Homebrew::Bundle::Brew.formulae_by_name = { "abc" => { link?: false } }
|
||||
allow_any_instance_of(Pathname).to receive(:read).and_return("brew 'abc'")
|
||||
allow(Formula["abc"]).to receive(:linked?).and_return(true)
|
||||
|
||||
@@ -130,7 +130,7 @@ RSpec.describe Homebrew::Cmd::Bundle::CheckSubcommand, :no_api do
|
||||
end
|
||||
|
||||
it "outputs the implicit link status error" do
|
||||
Homebrew::Bundle::Brew.instance_variable_set(:@formulae_by_name, { "abc" => { link?: true } })
|
||||
Homebrew::Bundle::Brew.formulae_by_name = { "abc" => { link?: true } }
|
||||
allow_any_instance_of(Pathname).to receive(:read).and_return("brew 'abc'")
|
||||
allow(Formula["abc"]).to receive(:linked?).and_return(true)
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ RSpec.describe Homebrew::Cmd::FetchCmd do
|
||||
describe "#cask_downloads", :cask do
|
||||
it "collects one download per distinct URL across all platforms" do
|
||||
cmd = described_class.new(["--cask", "--all-platforms", "sha256-os"])
|
||||
basenames = cmd.send(:cask_downloads, Cask::CaskLoader.load("sha256-os"))
|
||||
basenames = cmd.cask_downloads(Cask::CaskLoader.load("sha256-os"))
|
||||
.map { |download| File.basename(download.url.to_s) }
|
||||
expect(basenames).to contain_exactly("caffeine-arm-darwin.zip", "caffeine-intel-darwin.zip",
|
||||
"caffeine-arm-linux.zip", "caffeine-intel-linux.zip")
|
||||
@@ -97,12 +97,12 @@ RSpec.describe Homebrew::Cmd::FetchCmd do
|
||||
|
||||
it "skips arches the cask's depends_on arch excludes" do
|
||||
cmd = described_class.new(["--cask", "--os=macos", "--arch=intel", "depends-on-arch-arm64"])
|
||||
expect(cmd.send(:cask_downloads, Cask::CaskLoader.load("depends-on-arch-arm64"))).to be_empty
|
||||
expect(cmd.cask_downloads(Cask::CaskLoader.load("depends-on-arch-arm64"))).to be_empty
|
||||
end
|
||||
|
||||
it "collapses to a single download for a cask without on_system blocks" do
|
||||
cmd = described_class.new(["--cask", "--all-platforms", "local-caffeine"])
|
||||
expect(cmd.send(:cask_downloads, Cask::CaskLoader.load("local-caffeine")).length).to eq(1)
|
||||
expect(cmd.cask_downloads(Cask::CaskLoader.load("local-caffeine")).length).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -256,7 +256,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
Formula from https://example.com/testball.rb
|
||||
Not installed
|
||||
EOS
|
||||
expect { info.send(:info_formula_summary, formula) }
|
||||
expect { info.info_formula_summary(formula) }
|
||||
.to output(expected_output).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -290,7 +290,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Installs from source: yes/).to_stdout
|
||||
.and not_to_output(/Metadata/).to_stdout
|
||||
.and not_to_output(/supports macOS and Linux/).to_stdout
|
||||
@@ -308,7 +308,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(other).to receive(:full_name).and_return("someuser/tap/other")
|
||||
allow(Formulary).to receive(:factory).with("other").and_return(other)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(%r{Conflicts with:\n someuser/tap/other}).to_stdout
|
||||
end
|
||||
|
||||
@@ -321,7 +321,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(Formulary).to receive(:factory).with("testball").and_return(formula)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.not_to output(/Conflicts with:/).to_stdout
|
||||
end
|
||||
|
||||
@@ -338,7 +338,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/==> .*testball.*\(deprecated\):/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -356,7 +356,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/==> .*testball.*\(disabled\):/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -379,7 +379,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
to_formulae_and_casks_and_unavailable: [core, core],
|
||||
)
|
||||
|
||||
expect { info.send(:print_info) }
|
||||
expect { info.print_info }
|
||||
.to output(%r{ataraxy-labs/tap/testball.*homebrew/core/testball.*Not installed}m).to_stdout
|
||||
end
|
||||
|
||||
@@ -391,7 +391,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
to_formulae_and_casks_and_unavailable: [error],
|
||||
)
|
||||
|
||||
expect { info.send(:print_info) }
|
||||
expect { info.print_info }
|
||||
.to output(/No available formula or cask with the name "nonexistent-formula"/).to_stderr
|
||||
end
|
||||
|
||||
@@ -409,7 +409,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(shadowing).to receive_messages(tap: Tap.fetch("ataraxy-labs/tap"), full_name: "ataraxy-labs/tap/testball")
|
||||
allow(Formulary).to receive(:factory).with("ataraxy-labs/tap/testball").and_return(shadowing)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(%r{homebrew/core/testball.*Not installed.*ataraxy-labs/tap/testball}m).to_stdout
|
||||
end
|
||||
|
||||
@@ -431,7 +431,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
end
|
||||
allow(Formulary).to receive(:factory).with("ataraxy-labs/tap/testball").and_return(keg_formula)
|
||||
|
||||
expect(info.send(:installed_resolution, formula)).to eq([keg_formula, shadowing_tap])
|
||||
expect(info.installed_resolution(formula)).to eq([keg_formula, shadowing_tap])
|
||||
end
|
||||
|
||||
it "resolves the keg's own name when it differs from the formula (installed via alias)" do
|
||||
@@ -443,7 +443,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
keg_formula = formula("stripe") { url "https://brew.sh/stripe-1.0.tar.gz" }
|
||||
allow(Formulary).to receive(:factory).with("stripe/stripe-cli/stripe").and_return(keg_formula)
|
||||
|
||||
expect(info.send(:installed_resolution, formula)).to eq([keg_formula, Tap.fetch("homebrew/core")])
|
||||
expect(info.installed_resolution(formula)).to eq([keg_formula, Tap.fetch("homebrew/core")])
|
||||
end
|
||||
|
||||
it "returns the original formula and no shadowing tap when the install receipt has no tap" do
|
||||
@@ -455,7 +455,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
tab.tabfile = keg_path/AbstractTab::FILENAME
|
||||
tab.write
|
||||
|
||||
expect(info.send(:installed_resolution, formula)).to eq([formula, nil])
|
||||
expect(info.installed_resolution(formula)).to eq([formula, nil])
|
||||
end
|
||||
|
||||
it "returns the original formula and no shadowing tap when the install receipt's tap matches" do
|
||||
@@ -469,7 +469,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
tab.write
|
||||
|
||||
allow(formula).to receive(:tap).and_return(Tap.fetch("homebrew/core"))
|
||||
expect(info.send(:installed_resolution, formula)).to eq([formula, nil])
|
||||
expect(info.installed_resolution(formula)).to eq([formula, nil])
|
||||
end
|
||||
|
||||
it "warns about a shadowing tap when info_formula is given one" do
|
||||
@@ -481,7 +481,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula, shadowed_by: Tap.fetch("homebrew/core")) }
|
||||
expect { info.info_formula(formula, shadowed_by: Tap.fetch("homebrew/core")) }
|
||||
.to output(%r{Warning: `testball` shadows `homebrew/core/testball`}).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -495,7 +495,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(formula).to receive(:tap).and_return(Tap.fetch("homebrew/core"))
|
||||
|
||||
qualified = Set["homebrew/core/testball"]
|
||||
expect(info.send(:formula_qualified_by_user?, formula, qualified)).to be(true)
|
||||
expect(info.formula_qualified_by_user?(formula, qualified)).to be(true)
|
||||
end
|
||||
|
||||
it "treats a bare unqualified input as not user-qualified" do
|
||||
@@ -505,7 +505,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
url "https://brew.sh/testball-0.1.tar.gz"
|
||||
end
|
||||
|
||||
expect(info.send(:formula_qualified_by_user?, formula, Set.new)).to be(false)
|
||||
expect(info.formula_qualified_by_user?(formula, Set.new)).to be(false)
|
||||
end
|
||||
|
||||
it "--json swaps an unqualified-input formula to its installed tap" do
|
||||
@@ -606,7 +606,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
"==> Dependencies\nRequired \\(1\\): .*bar.*\n" \
|
||||
"Recursive Runtime \\(2\\): 1 installed .*✔, 1 missing .*✘\nDependents: 1",
|
||||
)
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(expected_output).to_stdout
|
||||
.and not_to_output(/^Dependencies: /).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -643,7 +643,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/^Dependents \(2\): another-dependent, some-dependent$/).to_stdout
|
||||
.and not_to_output(/^Dependents: /).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -680,7 +680,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
allow(direct_dependency).to receive(:satisfied?).and_return(true)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Recursive Runtime \(1\): all installed .*✔/).to_stdout
|
||||
.and not_to_output(/missing/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -708,7 +708,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*✘/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -745,7 +745,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*✔/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -782,7 +782,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*↑/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -812,7 +812,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*✔/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -842,7 +842,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*↑/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -872,7 +872,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*pkg-config.*✔/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -892,7 +892,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): bar\n/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -919,7 +919,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*✘/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -952,7 +952,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Required \(1\): .*bar.*↑/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -981,7 +981,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
pour_bottle?: true,
|
||||
)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to not_to_output(/Build \(1\): .*bar.*/).to_stdout
|
||||
.and not_to_output(/==> Dependencies/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1004,7 +1004,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
tab.tabfile = keg_path/AbstractTab::FILENAME
|
||||
tab.write
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/\A==> testball: 0\.0\.1 → stable 0\.1\n/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1025,7 +1025,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
requirements: Requirements.new(LinuxRequirement.new),
|
||||
)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(/Requirements\nRequired: .*Linux/).to_stdout
|
||||
.and not_to_output(/supports Linux/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1048,7 +1048,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
requirements: Requirements.new(os_requirement),
|
||||
)
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to not_to_output(/Installs from source: yes/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1077,7 +1077,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(a_string_including("==> Binaries\nanother\ndaemon\ntestball\n")).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1101,7 +1101,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(formula).to receive_messages(bottle:, core_formula?: false)
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to output(a_string_including("==> Binaries\nanother\ndaemon\ntestball\n")).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1127,7 +1127,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to not_to_output(/==> Binaries/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1150,7 +1150,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(info).to receive(:github_info).with(formula).and_return("https://example.com/testball.rb")
|
||||
allow(formula).to receive_messages(core_formula?: false, missing_library_linkage: [[], Set.new])
|
||||
|
||||
expect { info.send(:info_formula, formula) }
|
||||
expect { info.info_formula(formula) }
|
||||
.to not_to_output(/==> Binaries/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1239,7 +1239,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive_messages(aliases: ["testball@1.0", "tball", "googleball"], oldnames: [])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/^Aliases: testball@1\.0, tball, googleball$/).to_stdout
|
||||
.and not_to_output(/^Old Names:/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1254,7 +1254,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive_messages(aliases: ["testball@1.0", "tball"], oldnames: ["foo", "bar"])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/^Aliases: testball@1\.0, tball\nOld Names: foo, bar$/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1268,7 +1268,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive_messages(aliases: [], oldnames: ["foo"])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/^Old Names: foo$/).to_stdout
|
||||
.and not_to_output(/^Aliases:/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1283,7 +1283,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive_messages(aliases: [], oldnames: [])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to not_to_output(/^Aliases:/).to_stdout
|
||||
.and not_to_output(/^Old Names:/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1318,7 +1318,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive(:versioned_formulae).and_return([versioned])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(Regexp.new(
|
||||
"==> Installed Versions\n" \
|
||||
".*testball\\b.*\\s+1\\.0\\s+\\(.*\\)\n" \
|
||||
@@ -1346,7 +1346,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive_messages(versioned_formulae: [], outdated?: true)
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/==> Installed Versions\n.*testball\b.*\s+1\.0 → 2\.0\s+\(/).to_stdout
|
||||
.and not_to_output(/0\.9 →/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1370,7 +1370,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive(:versioned_formulae).and_return([])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/==> Installed Versions\n.*testball\b.*\s+1\.0\s+\(/).to_stdout
|
||||
.and not_to_output(/\s+0\.9\s+\(/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1406,7 +1406,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
linked_version: PkgVersion.parse("1.0"))
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/.*testball\b.*\s+1\.0\s+\(.*\)\s+\[Linked\]/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1439,7 +1439,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(Formulary).to receive(:factory).with("testball").and_return(parent)
|
||||
allow(info).to receive(:github_info).with(versioned).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, versioned) }
|
||||
expect { info.info_formula(versioned) }
|
||||
.to output(Regexp.new(
|
||||
"==> Installed Versions\n" \
|
||||
".*testball\\b.*\\s+1\\.0\\s+\\(.*\\)\n" \
|
||||
@@ -1464,7 +1464,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive(:versioned_formulae).and_return([])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(/==> Installed Versions\n.*testball\b.*\s+1\.0\s+\(/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1491,7 +1491,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(Formulary).to receive(:factory).with("testball").and_return(parent)
|
||||
allow(info).to receive(:github_info).with(versioned).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, versioned) }
|
||||
expect { info.info_formula(versioned) }
|
||||
.to output(/==> Installed Versions\n.*testball\b.*\s+1\.0\s+\(/).to_stdout
|
||||
.and not_to_output(/testball@0\.9 \(0\.9\)/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
@@ -1515,7 +1515,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive(:versioned_formulae).and_return([])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to output(Regexp.new(
|
||||
"==> Installed Kegs and Versions\n" \
|
||||
".*testball\\b.*\\s+1\\.0\\b.*\\(.*\\)\n" \
|
||||
@@ -1536,7 +1536,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
allow(main_formula).to receive(:versioned_formulae).and_return([])
|
||||
allow(info).to receive(:github_info).with(main_formula).and_return("https://example.com/testball.rb")
|
||||
|
||||
expect { info.send(:info_formula, main_formula) }
|
||||
expect { info.info_formula(main_formula) }
|
||||
.to not_to_output(/==> Installed Versions\b/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
@@ -1554,7 +1554,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
url "https://brew.sh/testball-0.1.tar.gz"
|
||||
end
|
||||
|
||||
expect(described_class.new([]).send(:github_info, formula_instance))
|
||||
expect(described_class.new([]).github_info(formula_instance))
|
||||
.to eq(keg_formula_path.to_s)
|
||||
end
|
||||
|
||||
@@ -1565,7 +1565,7 @@ RSpec.describe Homebrew::Cmd::Info do
|
||||
url "https://brew.sh/testball-0.1.tar.gz"
|
||||
end
|
||||
|
||||
expect(described_class.new([]).send(:github_info, formula_instance))
|
||||
expect(described_class.new([]).github_info(formula_instance))
|
||||
.to eq("https://github.com/Homebrew/homebrew-core/blob/HEAD/" \
|
||||
"#{formula_path.relative_path_from(tap.path)}")
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ RSpec.describe Homebrew::Cmd::Outdated do
|
||||
expect(cask).to receive(:outdated?)
|
||||
.with(greedy: false, greedy_latest: false, greedy_auto_updates: false)
|
||||
.and_return(false)
|
||||
expect(cmd.send(:select_outdated, [cask])).to be_empty
|
||||
expect(cmd.select_outdated([cask])).to be_empty
|
||||
end
|
||||
|
||||
it "checks auto-updating casks with --greedy-auto-updates", :cask do
|
||||
@@ -58,7 +58,7 @@ RSpec.describe Homebrew::Cmd::Outdated do
|
||||
expect(cask).to receive(:outdated?)
|
||||
.with(greedy: false, greedy_latest: false, greedy_auto_updates: true)
|
||||
.and_return(true)
|
||||
expect(cmd.send(:select_outdated, [cask])).to eq([cask])
|
||||
expect(cmd.select_outdated([cask])).to eq([cask])
|
||||
end
|
||||
|
||||
it "excludes auto-updating casks when auto-update upgrades are disabled", :cask do
|
||||
|
||||
@@ -29,7 +29,7 @@ RSpec.describe Homebrew::Cmd::SearchCmd do
|
||||
before { allow_any_instance_of(StringIO).to receive(:tty?).and_return(false) }
|
||||
|
||||
it "skips" do
|
||||
expect { search_cmd.send(:print_missing_formula_help, "formula", false) }
|
||||
expect { search_cmd.print_missing_formula_help("formula", false) }
|
||||
.not_to output.to_stdout
|
||||
end
|
||||
end
|
||||
@@ -38,25 +38,25 @@ RSpec.describe Homebrew::Cmd::SearchCmd do
|
||||
before { allow_any_instance_of(StringIO).to receive(:tty?).and_return(true) }
|
||||
|
||||
it "skips a regex query" do
|
||||
expect { search_cmd.send(:print_missing_formula_help, "/formula/", false) }
|
||||
expect { search_cmd.print_missing_formula_help("/formula/", false) }
|
||||
.not_to output.to_stdout
|
||||
end
|
||||
|
||||
it "skips if there is not a reason" do
|
||||
allow(Homebrew::MissingFormula).to receive(:reason).and_return(nil)
|
||||
expect { search_cmd.send(:print_missing_formula_help, "formula", false) }
|
||||
expect { search_cmd.print_missing_formula_help("formula", false) }
|
||||
.not_to output.to_stdout
|
||||
end
|
||||
|
||||
it "prints additional output if `found_matches` is true" do
|
||||
allow(Homebrew::MissingFormula).to receive(:reason).and_return("Reason")
|
||||
expect { search_cmd.send(:print_missing_formula_help, "formula", true) }
|
||||
expect { search_cmd.print_missing_formula_help("formula", true) }
|
||||
.to output("\nIf you meant \"formula\" specifically:\nReason\n").to_stdout
|
||||
end
|
||||
|
||||
it "only prints reason if `found_matches` is false" do
|
||||
allow(Homebrew::MissingFormula).to receive(:reason).and_return("Reason")
|
||||
expect { search_cmd.send(:print_missing_formula_help, "formula", false) }
|
||||
expect { search_cmd.print_missing_formula_help("formula", false) }
|
||||
.to output("Reason\n").to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,7 +17,7 @@ RSpec.shared_examples "parseable arguments" do |command_name: nil|
|
||||
klass = Object.const_get(command)
|
||||
# rubocop:enable Sorbet/ConstantsFromStrings
|
||||
end
|
||||
argv = klass.parser.instance_variable_get(:@min_named_args)&.times&.map { "argument" } || []
|
||||
argv = klass.parser.min_named_args&.times&.map { "argument" } || []
|
||||
cmd = klass.new(argv)
|
||||
expect(cmd.args).to be_a Homebrew::CLI::Args
|
||||
end
|
||||
|
||||
@@ -16,85 +16,85 @@ RSpec.describe Homebrew::Cmd::Source do
|
||||
|
||||
describe "#github_repo_url" do
|
||||
it "extracts repository URL from GitHub URL" do
|
||||
expect(described_class.new([]).send(:github_repo_url, "https://github.com/Homebrew/brew.git"))
|
||||
expect(described_class.new([]).github_repo_url("https://github.com/Homebrew/brew.git"))
|
||||
.to eq("https://github.com/Homebrew/brew")
|
||||
end
|
||||
|
||||
it "handles GitHub archive URLs" do
|
||||
expect(described_class.new([]).send(:github_repo_url, "https://github.com/Homebrew/testball/archive/refs/tags/v0.1.tar.gz"))
|
||||
expect(described_class.new([]).github_repo_url("https://github.com/Homebrew/testball/archive/refs/tags/v0.1.tar.gz"))
|
||||
.to eq("https://github.com/Homebrew/testball")
|
||||
end
|
||||
|
||||
it "returns nil for non-GitHub URLs" do
|
||||
expect(described_class.new([]).send(:github_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).github_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#gitlab_repo_url" do
|
||||
it "extracts repository URL from GitLab URL with nested groups" do
|
||||
expect(described_class.new([]).send(:gitlab_repo_url, "https://gitlab.com/group/subgroup/project/-/archive/v1.0/project-v1.0.tar.gz"))
|
||||
expect(described_class.new([]).gitlab_repo_url("https://gitlab.com/group/subgroup/project/-/archive/v1.0/project-v1.0.tar.gz"))
|
||||
.to eq("https://gitlab.com/group/subgroup/project")
|
||||
end
|
||||
|
||||
it "handles GitLab .git URLs" do
|
||||
expect(described_class.new([]).send(:gitlab_repo_url, "https://gitlab.com/user/repo.git"))
|
||||
expect(described_class.new([]).gitlab_repo_url("https://gitlab.com/user/repo.git"))
|
||||
.to eq("https://gitlab.com/user/repo")
|
||||
end
|
||||
|
||||
it "returns nil for non-GitLab URLs" do
|
||||
expect(described_class.new([]).send(:gitlab_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).gitlab_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#bitbucket_repo_url" do
|
||||
it "extracts repository URL from Bitbucket URL" do
|
||||
expect(described_class.new([]).send(:bitbucket_repo_url, "https://bitbucket.org/user/repo/get/v1.0.tar.gz"))
|
||||
expect(described_class.new([]).bitbucket_repo_url("https://bitbucket.org/user/repo/get/v1.0.tar.gz"))
|
||||
.to eq("https://bitbucket.org/user/repo")
|
||||
end
|
||||
|
||||
it "handles Bitbucket .git URLs" do
|
||||
expect(described_class.new([]).send(:bitbucket_repo_url, "https://bitbucket.org/user/repo.git"))
|
||||
expect(described_class.new([]).bitbucket_repo_url("https://bitbucket.org/user/repo.git"))
|
||||
.to eq("https://bitbucket.org/user/repo")
|
||||
end
|
||||
|
||||
it "returns nil for non-Bitbucket URLs" do
|
||||
expect(described_class.new([]).send(:bitbucket_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).bitbucket_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#codeberg_repo_url" do
|
||||
it "extracts repository URL from Codeberg URL" do
|
||||
expect(described_class.new([]).send(:codeberg_repo_url, "https://codeberg.org/user/repo/archive/v1.0.tar.gz"))
|
||||
expect(described_class.new([]).codeberg_repo_url("https://codeberg.org/user/repo/archive/v1.0.tar.gz"))
|
||||
.to eq("https://codeberg.org/user/repo")
|
||||
end
|
||||
|
||||
it "handles Codeberg .git URLs" do
|
||||
expect(described_class.new([]).send(:codeberg_repo_url, "https://codeberg.org/user/repo.git"))
|
||||
expect(described_class.new([]).codeberg_repo_url("https://codeberg.org/user/repo.git"))
|
||||
.to eq("https://codeberg.org/user/repo")
|
||||
end
|
||||
|
||||
it "returns nil for non-Codeberg URLs" do
|
||||
expect(described_class.new([]).send(:codeberg_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).codeberg_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#sourcehut_repo_url" do
|
||||
it "extracts repository URL from SourceHut URL" do
|
||||
expect(described_class.new([]).send(:sourcehut_repo_url, "https://git.sr.ht/~user/repo/archive/v1.0.tar.gz"))
|
||||
expect(described_class.new([]).sourcehut_repo_url("https://git.sr.ht/~user/repo/archive/v1.0.tar.gz"))
|
||||
.to eq("https://sr.ht/~user/repo")
|
||||
end
|
||||
|
||||
it "handles sr.ht URLs without git subdomain" do
|
||||
expect(described_class.new([]).send(:sourcehut_repo_url, "https://sr.ht/~user/repo"))
|
||||
expect(described_class.new([]).sourcehut_repo_url("https://sr.ht/~user/repo"))
|
||||
.to eq("https://sr.ht/~user/repo")
|
||||
end
|
||||
|
||||
it "returns nil for non-SourceHut URLs" do
|
||||
expect(described_class.new([]).send(:sourcehut_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).sourcehut_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
@@ -118,8 +118,9 @@ RSpec.describe Homebrew::Cmd::Source do
|
||||
])
|
||||
|
||||
expect(described_class.new([])
|
||||
.send(:pypi_repo_url,
|
||||
"https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz"))
|
||||
.pypi_repo_url(
|
||||
"https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz",
|
||||
))
|
||||
.to eq("https://github.com/numpy/numpy")
|
||||
end
|
||||
|
||||
@@ -139,13 +140,14 @@ RSpec.describe Homebrew::Cmd::Source do
|
||||
])
|
||||
|
||||
expect(described_class.new([])
|
||||
.send(:pypi_repo_url,
|
||||
"https://files.pythonhosted.org/packages/00/00/000000000000000000000000000000000000000000000000000000000000/foobar-0.0.1.tar.gz"))
|
||||
.pypi_repo_url(
|
||||
"https://files.pythonhosted.org/packages/00/00/000000000000000000000000000000000000000000000000000000000000/foobar-0.0.1.tar.gz",
|
||||
))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
it "returns nil for non-PyPI URLs" do
|
||||
expect(described_class.new([]).send(:pypi_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).pypi_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
@@ -168,7 +170,7 @@ RSpec.describe Homebrew::Cmd::Source do
|
||||
instance_double(Process::Status, success?: true),
|
||||
])
|
||||
|
||||
expect(described_class.new([]).send(:npm_repo_url, "https://registry.npmjs.org/#{package}/-/vite-1.2.3.tgz"))
|
||||
expect(described_class.new([]).npm_repo_url("https://registry.npmjs.org/#{package}/-/vite-1.2.3.tgz"))
|
||||
.to eq("https://github.com/vitejs/vite.git")
|
||||
end
|
||||
end
|
||||
@@ -183,29 +185,29 @@ RSpec.describe Homebrew::Cmd::Source do
|
||||
])
|
||||
|
||||
expect(described_class.new([])
|
||||
.send(:npm_repo_url, "https://registry.npmjs.org/vite/-/vite-1.2.3.tgz"))
|
||||
.npm_repo_url("https://registry.npmjs.org/vite/-/vite-1.2.3.tgz"))
|
||||
.to be_nil
|
||||
end
|
||||
|
||||
it "returns nil for non-npm URLs" do
|
||||
expect(described_class.new([]).send(:npm_repo_url, "https://example.com/repo.git"))
|
||||
expect(described_class.new([]).npm_repo_url("https://example.com/repo.git"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#url_to_repo" do
|
||||
it "returns GitHub repo URL for GitHub URLs" do
|
||||
expect(described_class.new([]).send(:url_to_repo, "https://github.com/Homebrew/brew"))
|
||||
expect(described_class.new([]).url_to_repo("https://github.com/Homebrew/brew"))
|
||||
.to eq("https://github.com/Homebrew/brew")
|
||||
end
|
||||
|
||||
it "returns GitLab repo URL for GitLab URLs" do
|
||||
expect(described_class.new([]).send(:url_to_repo, "https://gitlab.com/user/repo.git"))
|
||||
expect(described_class.new([]).url_to_repo("https://gitlab.com/user/repo.git"))
|
||||
.to eq("https://gitlab.com/user/repo")
|
||||
end
|
||||
|
||||
it "returns nil for unsupported URLs" do
|
||||
expect(described_class.new([]).send(:url_to_repo, "https://example.com/repo.tar.gz"))
|
||||
expect(described_class.new([]).url_to_repo("https://example.com/repo.tar.gz"))
|
||||
.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
allow(Homebrew::Trust).to receive(:trusted_tap?).with(tap).and_return(true)
|
||||
|
||||
with_env(HOMEBREW_REQUIRE_TAP_TRUST: "1") do
|
||||
expect { tap_info.send(:print_tap_info, [tap]) }
|
||||
expect { tap_info.print_tap_info([tap]) }
|
||||
.to output(%r{thirdparty/foo: Installed\nTrusted\nNo commands/casks/formulae}).to_stdout
|
||||
end
|
||||
end
|
||||
@@ -76,7 +76,7 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
allow(Homebrew::Trust).to receive(:trusted_tap?).with(tap).and_return(false)
|
||||
|
||||
with_env(HOMEBREW_REQUIRE_TAP_TRUST: "1") do
|
||||
expect { tap_info.send(:print_tap_info, [tap]) }
|
||||
expect { tap_info.print_tap_info([tap]) }
|
||||
.to output(%r{thirdparty/foo: Installed\nUntrusted\nNo commands/casks/formulae}).to_stdout
|
||||
end
|
||||
end
|
||||
@@ -88,7 +88,7 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
instance_double(Tap, to_hash: { "name" => "user/b" }),
|
||||
]
|
||||
|
||||
expect { described_class.new([]).send(:print_tap_json, taps) }
|
||||
expect { described_class.new([]).print_tap_json(taps) }
|
||||
.to output(%r{"name":\s*"user/a".*"name":\s*"user/b"}m).to_stdout
|
||||
end
|
||||
end
|
||||
@@ -103,35 +103,35 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
end
|
||||
|
||||
it "does not mark an uninstalled formula" do
|
||||
expect(tap_info.send(:decorate_formula, tap, "missing", installed: false)).not_to include("✘")
|
||||
expect(tap_info.decorate_formula(tap, "missing", installed: false)).not_to include("✘")
|
||||
end
|
||||
|
||||
it "marks an installed formula as satisfied" do
|
||||
formula = instance_double(Formula, outdated?: false, deprecated?: false, disabled?: false)
|
||||
allow(Formulary).to receive(:factory).with("homebrew/foo/installed").and_return(formula)
|
||||
|
||||
expect(tap_info.send(:decorate_formula, tap, "installed", installed: true)).to match(/installed.*✔/)
|
||||
expect(tap_info.decorate_formula(tap, "installed", installed: true)).to match(/installed.*✔/)
|
||||
end
|
||||
|
||||
it "marks an outdated installed formula as upgradable" do
|
||||
formula = instance_double(Formula, outdated?: true, deprecated?: false, disabled?: false)
|
||||
allow(Formulary).to receive(:factory).with("homebrew/foo/outdated").and_return(formula)
|
||||
|
||||
expect(tap_info.send(:decorate_formula, tap, "outdated", installed: true)).to match(/outdated.*↑/)
|
||||
expect(tap_info.decorate_formula(tap, "outdated", installed: true)).to match(/outdated.*↑/)
|
||||
end
|
||||
|
||||
it "marks a deprecated formula with `(deprecated)`" do
|
||||
formula = instance_double(Formula, outdated?: false, deprecated?: true, disabled?: false)
|
||||
allow(Formulary).to receive(:factory).with("homebrew/foo/old").and_return(formula)
|
||||
|
||||
expect(tap_info.send(:decorate_formula, tap, "old", installed: false)).to match(/old.*\(deprecated\)/)
|
||||
expect(tap_info.decorate_formula(tap, "old", installed: false)).to match(/old.*\(deprecated\)/)
|
||||
end
|
||||
|
||||
it "marks a disabled formula with `(disabled)`" do
|
||||
formula = instance_double(Formula, outdated?: false, deprecated?: false, disabled?: true)
|
||||
allow(Formulary).to receive(:factory).with("homebrew/foo/gone").and_return(formula)
|
||||
|
||||
expect(tap_info.send(:decorate_formula, tap, "gone", installed: false)).to match(/gone.*\(disabled\)/)
|
||||
expect(tap_info.decorate_formula(tap, "gone", installed: false)).to match(/gone.*\(disabled\)/)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -145,35 +145,35 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
end
|
||||
|
||||
it "does not mark an uninstalled cask" do
|
||||
expect(tap_info.send(:decorate_cask, tap, "missing", installed: false)).not_to include("✘")
|
||||
expect(tap_info.decorate_cask(tap, "missing", installed: false)).not_to include("✘")
|
||||
end
|
||||
|
||||
it "marks an installed cask as satisfied" do
|
||||
cask = instance_double(Cask::Cask, outdated?: false, deprecated?: false, disabled?: false)
|
||||
allow(Cask::CaskLoader).to receive(:load).with("homebrew/foo/installed").and_return(cask)
|
||||
|
||||
expect(tap_info.send(:decorate_cask, tap, "installed", installed: true)).to match(/installed.*✔/)
|
||||
expect(tap_info.decorate_cask(tap, "installed", installed: true)).to match(/installed.*✔/)
|
||||
end
|
||||
|
||||
it "marks an outdated installed cask as upgradable" do
|
||||
cask = instance_double(Cask::Cask, outdated?: true, deprecated?: false, disabled?: false)
|
||||
allow(Cask::CaskLoader).to receive(:load).with("homebrew/foo/outdated").and_return(cask)
|
||||
|
||||
expect(tap_info.send(:decorate_cask, tap, "outdated", installed: true)).to match(/outdated.*↑/)
|
||||
expect(tap_info.decorate_cask(tap, "outdated", installed: true)).to match(/outdated.*↑/)
|
||||
end
|
||||
|
||||
it "marks a deprecated cask with `(deprecated)`" do
|
||||
cask = instance_double(Cask::Cask, outdated?: false, deprecated?: true, disabled?: false)
|
||||
allow(Cask::CaskLoader).to receive(:load).with("homebrew/foo/old").and_return(cask)
|
||||
|
||||
expect(tap_info.send(:decorate_cask, tap, "old", installed: false)).to match(/old.*\(deprecated\)/)
|
||||
expect(tap_info.decorate_cask(tap, "old", installed: false)).to match(/old.*\(deprecated\)/)
|
||||
end
|
||||
|
||||
it "marks a disabled cask with `(disabled)`" do
|
||||
cask = instance_double(Cask::Cask, outdated?: false, deprecated?: false, disabled?: true)
|
||||
allow(Cask::CaskLoader).to receive(:load).with("homebrew/foo/gone").and_return(cask)
|
||||
|
||||
expect(tap_info.send(:decorate_cask, tap, "gone", installed: false)).to match(/gone.*\(disabled\)/)
|
||||
expect(tap_info.decorate_cask(tap, "gone", installed: false)).to match(/gone.*\(disabled\)/)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -218,11 +218,11 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
end
|
||||
|
||||
it "lists every formula and cask, marking only the installed ones" do
|
||||
expect { tap_info.send(:print_tap_listings, tap) }
|
||||
expect { tap_info.print_tap_listings(tap) }
|
||||
.to output(
|
||||
/Commands.*mycmd.*==> Formulae.*foo.*✔.*uninstalled-formula.*==> Casks.*bar.*✔.*uninstalled-cask/m,
|
||||
).to_stdout
|
||||
expect { tap_info.send(:print_tap_listings, tap) }.not_to output(/✘/).to_stdout
|
||||
expect { tap_info.print_tap_listings(tap) }.not_to output(/✘/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
@@ -249,7 +249,7 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
end
|
||||
|
||||
it "warns about truncation and shows only installed entries under the standard header" do
|
||||
expect { tap_info.send(:print_tap_listings, tap) }
|
||||
expect { tap_info.print_tap_listings(tap) }
|
||||
.to output(/==> Formulae.*formula7.*✔/m).to_stdout
|
||||
.and output(/Tap has more than 30 formulae; showing only installed entries\./).to_stderr
|
||||
end
|
||||
@@ -273,9 +273,9 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
end
|
||||
|
||||
it "lists every formula and cask without uninstalled markers" do
|
||||
expect { tap_info.send(:print_tap_listings, tap) }
|
||||
expect { tap_info.print_tap_listings(tap) }
|
||||
.to output(/==> Formulae.*baz.*foo.*==> Casks.*bar/m).to_stdout
|
||||
expect { tap_info.send(:print_tap_listings, tap) }.not_to output(/✘/).to_stdout
|
||||
expect { tap_info.print_tap_listings(tap) }.not_to output(/✘/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
@@ -298,13 +298,13 @@ RSpec.describe Homebrew::Cmd::TapInfo do
|
||||
end
|
||||
|
||||
it "shows a link to the tap remote and warns when nothing is installed" do
|
||||
expect { tap_info.send(:print_tap_listings, tap) }
|
||||
expect { tap_info.print_tap_listings(tap) }
|
||||
.to output(%r{See: https://github.com/homebrew/homebrew-foo}).to_stdout
|
||||
.and output(/Tap has more than 30 formulae and none are installed\./).to_stderr
|
||||
end
|
||||
|
||||
it "does not list individual formula names" do
|
||||
expect { tap_info.send(:print_tap_listings, tap) }
|
||||
expect { tap_info.print_tap_listings(tap) }
|
||||
.not_to output(/formula1\b/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
it "links to the donations section" do
|
||||
allow(Homebrew::Settings).to receive(:read).with("donationmessage").and_return("false")
|
||||
|
||||
expect { described_class.new([]).send(:donation_message) }
|
||||
expect { described_class.new([]).donation_message }
|
||||
.to output(include("https://github.com/Homebrew/brew#-donations")).to_stdout
|
||||
end
|
||||
|
||||
@@ -471,7 +471,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
|
||||
expect(hub.select_formula_or_cask(:A)).to be_empty
|
||||
expect(hub.select_formula_or_cask(:D)).to be_empty
|
||||
expect(hub.instance_variable_get(:@hash)[:R]).to eq([["cv", "progress"]])
|
||||
expect(hub.renamed_formulae).to eq([["cv", "progress"]])
|
||||
end
|
||||
|
||||
context "when updating a Tap other than the core Tap" do
|
||||
@@ -490,7 +490,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
|
||||
expect(hub.select_formula_or_cask(:A)).to be_empty
|
||||
expect(hub.select_formula_or_cask(:D)).to be_empty
|
||||
expect(hub.instance_variable_get(:@hash)[:R]).to be_nil
|
||||
expect(hub.renamed_formulae).to be_empty
|
||||
end
|
||||
|
||||
specify "with renamed Formula and restructured Tap" do
|
||||
@@ -499,7 +499,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
|
||||
expect(hub.select_formula_or_cask(:A)).to be_empty
|
||||
expect(hub.select_formula_or_cask(:D)).to be_empty
|
||||
expect(hub.instance_variable_get(:@hash)[:R]).to eq([%w[foo/bar/xchat foo/bar/xchat2]])
|
||||
expect(hub.renamed_formulae).to eq([%w[foo/bar/xchat foo/bar/xchat2]])
|
||||
end
|
||||
|
||||
specify "with simulated 'homebrew/php' restructuring" do
|
||||
@@ -507,7 +507,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
|
||||
expect(hub.select_formula_or_cask(:A)).to be_empty
|
||||
expect(hub.select_formula_or_cask(:D)).to be_empty
|
||||
expect(hub.instance_variable_get(:@hash)[:R]).to be_nil
|
||||
expect(hub.renamed_formulae).to be_empty
|
||||
end
|
||||
|
||||
specify "with Formula changes" do
|
||||
@@ -515,7 +515,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
|
||||
expect(hub.select_formula_or_cask(:A)).to eq(%w[foo/bar/lua])
|
||||
expect(hub.select_formula_or_cask(:M)).to eq(%w[foo/bar/git])
|
||||
expect(hub.instance_variable_get(:@hash)[:R]).to be_nil
|
||||
expect(hub.renamed_formulae).to be_empty
|
||||
end
|
||||
|
||||
specify "with formula migrated to cask in same tap" do
|
||||
@@ -545,19 +545,19 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
|
||||
it "recommends trusting just the migrated package then migrating a rename" do
|
||||
expect(other_tap).not_to receive(:ensure_installed!)
|
||||
expect { reporter.send(:ensure_trusted_tap_installed!, "oldfoo", "newfoo", other_tap) }
|
||||
expect { reporter.ensure_trusted_tap_installed!("oldfoo", "newfoo", other_tap) }
|
||||
.to output(%r{brew trust foo/bar/newfoo.*brew migrate oldfoo}m).to_stderr
|
||||
end
|
||||
|
||||
it "recommends a reinstall for an unchanged-name tap migration" do
|
||||
expect { reporter.send(:ensure_trusted_tap_installed!, "foo", "foo", other_tap) }
|
||||
expect { reporter.ensure_trusted_tap_installed!("foo", "foo", other_tap) }
|
||||
.to output(/brew reinstall foo/).to_stderr
|
||||
end
|
||||
|
||||
it "taps a trusted tap" do
|
||||
allow(other_tap).to receive(:official?).and_return(true)
|
||||
expect(other_tap).to receive(:ensure_installed!)
|
||||
reporter.send(:ensure_trusted_tap_installed!, "foo", "foo", other_tap)
|
||||
reporter.ensure_trusted_tap_installed!("foo", "foo", other_tap)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -577,7 +577,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
-baz
|
||||
DIFF
|
||||
|
||||
expect(reporter.send(:diff)).to eq(<<~DIFF.strip)
|
||||
expect(reporter.diff).to eq(<<~DIFF.strip)
|
||||
A api/bar.rb
|
||||
D api/baz.rb
|
||||
DIFF
|
||||
@@ -593,7 +593,7 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
-baz
|
||||
DIFF
|
||||
|
||||
expect(reporter.send(:diff)).to eq(<<~DIFF.strip)
|
||||
expect(reporter.diff).to eq(<<~DIFF.strip)
|
||||
A api/baz.rb
|
||||
DIFF
|
||||
end
|
||||
@@ -698,13 +698,14 @@ RSpec.describe Homebrew::Cmd::UpdateReport do
|
||||
end
|
||||
|
||||
it "merges frozen report arrays" do
|
||||
allow(hub).to receive(:select_formula_or_cask).and_call_original
|
||||
first_reporter = instance_double(Reporter, report: { A: ["foo"].freeze })
|
||||
second_reporter = instance_double(Reporter, report: { A: ["bar"] })
|
||||
|
||||
hub.add(first_reporter)
|
||||
hub.add(second_reporter)
|
||||
|
||||
expect(hub.instance_variable_get(:@hash)[:A]).to eq(%w[foo bar])
|
||||
expect(hub.select_formula_or_cask(:A)).to eq(%w[foo bar])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -362,7 +362,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
allow(Cask::Upgrade).to receive(:upgrade_casks!).and_raise(Cask::CaskError.new("test cask error"))
|
||||
|
||||
cmd = described_class.new(["--cask"])
|
||||
expect { cmd.send(:upgrade_outdated_casks!, []) }
|
||||
expect { cmd.upgrade_outdated_casks!([]) }
|
||||
.to output(/test cask error/).to_stderr
|
||||
|
||||
expect(Homebrew).to have_failed
|
||||
@@ -374,7 +374,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
expect(Homebrew::Install).not_to receive(:ask_casks)
|
||||
expect(Cask::Upgrade).to receive(:upgrade_casks!).and_return(true)
|
||||
|
||||
cmd.send(:upgrade_outdated_casks!, [])
|
||||
cmd.upgrade_outdated_casks!([])
|
||||
end
|
||||
|
||||
it "passes --no-quit to cask upgrades" do
|
||||
@@ -385,7 +385,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
true
|
||||
end
|
||||
|
||||
cmd.send(:upgrade_outdated_casks!, [])
|
||||
cmd.upgrade_outdated_casks!([])
|
||||
end
|
||||
|
||||
it "passes HOMEBREW_NO_UPGRADE_QUIT_CASKS to cask upgrades" do
|
||||
@@ -397,7 +397,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
true
|
||||
end
|
||||
|
||||
cmd.send(:upgrade_outdated_casks!, [])
|
||||
cmd.upgrade_outdated_casks!([])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -409,7 +409,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
expect(cmd).to receive(:upgrade_outdated_formulae!)
|
||||
.with([], dry_run: true, show_upgrade_summary: false)
|
||||
.ordered do
|
||||
cmd.send(:final_upgrade_summary).version_changes << "testball 0.1 -> 0.2"
|
||||
cmd.final_upgrade_summary.version_changes << "testball 0.1 -> 0.2"
|
||||
true
|
||||
end
|
||||
expect(cmd).to receive(:upgrade_outdated_casks!)
|
||||
@@ -531,7 +531,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
expect(cmd).to receive(:upgrade_outdated_formulae!)
|
||||
.with([formula], dry_run: true, show_upgrade_summary: false)
|
||||
.ordered do
|
||||
cmd.send(:final_upgrade_summary).version_changes << "testball 0.1 -> 0.2"
|
||||
cmd.final_upgrade_summary.version_changes << "testball 0.1 -> 0.2"
|
||||
true
|
||||
end
|
||||
allow(cmd).to receive(:show_final_upgrade_summary).and_call_original
|
||||
@@ -560,7 +560,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
allow(formula).to receive_messages(optlinked?: true, opt_prefix: HOMEBREW_PREFIX/"opt/testball", bottle:)
|
||||
allow(Keg).to receive(:new).with(HOMEBREW_PREFIX/"opt/testball").and_return(keg)
|
||||
|
||||
expect(cmd.send(:formula_upgrade_descriptions, [formula], include_sizes: true))
|
||||
expect(cmd.formula_upgrade_descriptions([formula], include_sizes: true))
|
||||
.to eq(["testball 0.1 -> 0.2 (500B)"])
|
||||
end
|
||||
|
||||
@@ -578,7 +578,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
allow(Keg).to receive(:new).with(HOMEBREW_PREFIX/"opt/testball").and_return(keg)
|
||||
expect(bottle).not_to receive(:fetch_tab)
|
||||
|
||||
expect(cmd.send(:formula_upgrade_descriptions, [formula], include_sizes: true))
|
||||
expect(cmd.formula_upgrade_descriptions([formula], include_sizes: true))
|
||||
.to eq(["testball 0.1 -> 0.2"])
|
||||
end
|
||||
|
||||
@@ -697,7 +697,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
|
||||
allow(cmd).to receive(:final_upgrade_summary).and_return(summary)
|
||||
|
||||
expect { cmd.send(:show_final_upgrade_summary) }.to output(<<~EOS).to_stdout
|
||||
expect { cmd.show_final_upgrade_summary }.to output(<<~EOS).to_stdout
|
||||
==> Would upgrade 2 outdated packages
|
||||
testball 0.1 -> 0.2 (500B)
|
||||
codex 1.0 -> 2.0
|
||||
@@ -784,7 +784,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
use_prefetched: false, prefetch_names: nil,
|
||||
prefetch_upgrades: nil, **|
|
||||
if dry_run
|
||||
cmd.send(:final_upgrade_summary).version_changes << "deno 2.7.10 -> 2.7.11"
|
||||
cmd.final_upgrade_summary.version_changes << "deno 2.7.10 -> 2.7.11"
|
||||
elsif prefetch_only
|
||||
prefetch_names&.replace(["deno"])
|
||||
prefetch_upgrades&.replace(["deno 2.7.10 -> 2.7.11"])
|
||||
@@ -937,8 +937,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
expect(cmd).not_to receive(:ofail)
|
||||
|
||||
expect(
|
||||
cmd.send(
|
||||
:prefetch_outdated_casks!,
|
||||
cmd.prefetch_outdated_casks!(
|
||||
[],
|
||||
download_queue:,
|
||||
prefetch_names:,
|
||||
@@ -1019,7 +1018,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
allow(Homebrew::Upgrade).to receive(:formula_installers).and_return([])
|
||||
|
||||
expect do
|
||||
cmd.send(:formulae_upgrade_context, [formula], show_upgrade_summary: false)
|
||||
cmd.formulae_upgrade_context([formula], show_upgrade_summary: false)
|
||||
end.to output("==> Downloading bottle manifests\n").to_stdout
|
||||
end
|
||||
|
||||
@@ -1042,7 +1041,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
allow(Homebrew::Upgrade).to receive(:formula_installers).and_return([])
|
||||
|
||||
expect do
|
||||
cmd.send(:formulae_upgrade_context, [formula], show_upgrade_summary: false)
|
||||
cmd.formulae_upgrade_context([formula], show_upgrade_summary: false)
|
||||
end.not_to output(/Downloading bottle manifests/).to_stdout
|
||||
end
|
||||
|
||||
@@ -1116,7 +1115,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
|
||||
cmd = described_class.new(["--cask", "--dry-run"])
|
||||
|
||||
expect { cmd.send(:upgrade_outdated_casks!, []) }
|
||||
expect { cmd.upgrade_outdated_casks!([]) }
|
||||
.to not_to_output(/Unexpected method 'discontinued' called during caveats on Cask local-caffeine\./).to_stderr
|
||||
end
|
||||
|
||||
@@ -1133,7 +1132,7 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
|
||||
allow(cmd).to receive(:final_upgrade_summary).and_return(summary)
|
||||
|
||||
expect { cmd.send(:show_final_upgrade_summary) }.to output(<<~EOS).to_stdout
|
||||
expect { cmd.show_final_upgrade_summary }.to output(<<~EOS).to_stdout
|
||||
==> Upgraded 1 outdated package
|
||||
testball 0.1 -> 0.2
|
||||
==> 1 Pinned formula
|
||||
@@ -1188,8 +1187,8 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
pinned_formulae: [pinned],
|
||||
)
|
||||
|
||||
cmd.send(:record_formula_upgrade_summary, context)
|
||||
summary = cmd.send(:final_upgrade_summary)
|
||||
cmd.record_formula_upgrade_summary(context)
|
||||
summary = cmd.final_upgrade_summary
|
||||
|
||||
expect(summary.version_changes).to include("testball 0.1 -> 0.2")
|
||||
expect(summary.pinned_formulae).to include("pinnedball 1.0")
|
||||
@@ -1224,9 +1223,9 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
end
|
||||
allow(Homebrew::Upgrade).to receive(:upgrade_dependents)
|
||||
|
||||
cmd.send(:upgrade_outdated_formulae!, [])
|
||||
cmd.upgrade_outdated_formulae!([])
|
||||
|
||||
expect(cmd.send(:final_upgrade_summary).version_changes).to include("testball 0.1 -> 0.2")
|
||||
expect(cmd.final_upgrade_summary.version_changes).to include("testball 0.1 -> 0.2")
|
||||
end
|
||||
|
||||
it "omits failed formula version changes from the final summary" do
|
||||
@@ -1259,9 +1258,9 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
allow(Homebrew::Upgrade).to receive(:upgrade_formulae).and_return([successful_formula_installer])
|
||||
allow(Homebrew::Upgrade).to receive(:upgrade_dependents)
|
||||
|
||||
cmd.send(:upgrade_outdated_formulae!, [])
|
||||
cmd.upgrade_outdated_formulae!([])
|
||||
|
||||
expect(cmd.send(:final_upgrade_summary)).to have_attributes(
|
||||
expect(cmd.final_upgrade_summary).to have_attributes(
|
||||
version_changes: contain_exactly("testball 0.1 -> 0.2"),
|
||||
deprecated: contain_exactly("failball"),
|
||||
)
|
||||
|
||||
@@ -174,6 +174,6 @@ RSpec.describe Dependency do
|
||||
allow(foo).to receive(:to_formula).and_raise(FormulaUnavailableError, foo.name)
|
||||
f = instance_double(Formula, name: "f", deps: [foo])
|
||||
expect { described_class.expand(f) }.to raise_error(FormulaUnavailableError)
|
||||
expect(described_class.instance_variable_get(:@expand_stack)).to be_empty
|
||||
expect(described_class.expand_stack).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
@@ -85,12 +85,12 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
context "when cask does not have on_system blocks/calls or `depends_on arch`" do
|
||||
it "returns an array only including macOS/ARM" do
|
||||
Homebrew::SimulateSystem.with(os: :linux) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c, new_version))
|
||||
.to eq([[newest_macos, :arm]])
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.with(os: older_macos) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c, new_version))
|
||||
.to eq([[older_macos, :arm]])
|
||||
end
|
||||
end
|
||||
@@ -99,12 +99,12 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
context "when cask does not have on_system blocks/calls but has `depends_on arch`" do
|
||||
it "returns an array only including macOS/`depends_on arch` value" do
|
||||
Homebrew::SimulateSystem.with(os: :linux, arch: :arm) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_depends_on_intel, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c_depends_on_intel, new_version))
|
||||
.to eq([[newest_macos, :intel]])
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.with(os: older_macos, arch: :arm) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_depends_on_intel, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c_depends_on_intel, new_version))
|
||||
.to eq([[older_macos, :intel]])
|
||||
end
|
||||
end
|
||||
@@ -113,7 +113,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
context "when cask has on_system blocks/calls but does not have `depends_on arch`" do
|
||||
it "returns an array with combinations of `OnSystem::BASE_OS_OPTIONS` and `OnSystem::ARCH_OPTIONS`" do
|
||||
Homebrew::SimulateSystem.with(os: :linux) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_on_system, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c_on_system, new_version))
|
||||
.to eq([
|
||||
[newest_macos, :intel],
|
||||
[newest_macos, :arm],
|
||||
@@ -123,7 +123,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.with(os: older_macos) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_on_system, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c_on_system, new_version))
|
||||
.to eq([
|
||||
[older_macos, :intel],
|
||||
[older_macos, :arm],
|
||||
@@ -137,7 +137,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
context "when cask has on_system blocks/calls and `depends_on arch`" do
|
||||
it "returns an array with combinations of `OnSystem::BASE_OS_OPTIONS` and `OnSystem::ARCH_OPTIONS`" do
|
||||
Homebrew::SimulateSystem.with(os: :linux, arch: :arm) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_on_system_depends_on_intel, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c_on_system_depends_on_intel, new_version))
|
||||
.to eq([
|
||||
[newest_macos, :intel],
|
||||
[newest_macos, :arm],
|
||||
@@ -147,7 +147,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.with(os: older_macos, arch: :arm) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_on_system_depends_on_intel, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(c_on_system_depends_on_intel, new_version))
|
||||
.to eq([
|
||||
[older_macos, :intel],
|
||||
[older_macos, :arm],
|
||||
@@ -177,7 +177,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
end
|
||||
|
||||
expect(cask.depends_on.arch).to eq([{ type: :arm, bits: 64 }])
|
||||
expect(bump_cask_pr.send(:generate_system_options, cask, new_version))
|
||||
expect(bump_cask_pr.generate_system_options(cask, new_version))
|
||||
.to eq([
|
||||
[older_macos, :intel],
|
||||
[older_macos, :arm],
|
||||
@@ -196,24 +196,24 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
|
||||
it "returns an array only using archs of arch-specific versions" do
|
||||
Homebrew::SimulateSystem.with(os: :linux) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_arm))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_arm))
|
||||
.to eq([
|
||||
[newest_macos, :arm],
|
||||
[:linux, :arm],
|
||||
])
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_intel))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_intel))
|
||||
.to eq([
|
||||
[newest_macos, :intel],
|
||||
[:linux, :intel],
|
||||
])
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_arm_intel))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_arm_intel))
|
||||
.to eq([
|
||||
[newest_macos, :arm],
|
||||
[newest_macos, :intel],
|
||||
[:linux, :arm],
|
||||
[:linux, :intel],
|
||||
])
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_intel_arm))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_intel_arm))
|
||||
.to eq([
|
||||
[newest_macos, :intel],
|
||||
[newest_macos, :arm],
|
||||
@@ -223,24 +223,24 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.with(os: older_macos) do
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_arm))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_arm))
|
||||
.to eq([
|
||||
[older_macos, :arm],
|
||||
[:linux, :arm],
|
||||
])
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_intel))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_intel))
|
||||
.to eq([
|
||||
[older_macos, :intel],
|
||||
[:linux, :intel],
|
||||
])
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_arm_intel))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_arm_intel))
|
||||
.to eq([
|
||||
[older_macos, :arm],
|
||||
[older_macos, :intel],
|
||||
[:linux, :arm],
|
||||
[:linux, :intel],
|
||||
])
|
||||
expect(bump_cask_pr.send(:generate_system_options, c_arm_intel, new_version_intel_arm))
|
||||
expect(bump_cask_pr.generate_system_options(c_arm_intel, new_version_intel_arm))
|
||||
.to eq([
|
||||
[older_macos, :intel],
|
||||
[older_macos, :arm],
|
||||
@@ -274,14 +274,14 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
end
|
||||
|
||||
it "is idempotent when the replacement has already been applied" do
|
||||
bumped = bump_cask_pr.send(:replace_cask_stanza_value, contents, :version, "1.0", "2.0")
|
||||
bumped = bump_cask_pr.replace_cask_stanza_value(contents, :version, "1.0", "2.0")
|
||||
expect(bumped).to include('version "2.0"')
|
||||
expect { bump_cask_pr.send(:replace_cask_stanza_value, bumped, :version, "1.0", "2.0") }
|
||||
expect { bump_cask_pr.replace_cask_stanza_value(bumped, :version, "1.0", "2.0") }
|
||||
.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises when the stanza is missing entirely" do
|
||||
expect { bump_cask_pr.send(:replace_cask_stanza_value, contents, :version, "9.9", "2.0") }
|
||||
expect { bump_cask_pr.replace_cask_stanza_value(contents, :version, "9.9", "2.0") }
|
||||
.to raise_error(/Could not find 'version' stanza/)
|
||||
end
|
||||
end
|
||||
@@ -317,7 +317,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
cask = Homebrew::SimulateSystem.with(os: newest_macos, arch: :arm) { cask_from_contents(contents) }
|
||||
new_version = Homebrew::BumpVersionParser.new(arm: "2.0")
|
||||
|
||||
expect(bump_cask_pr.send(:replace_version_and_checksum, cask, new_hash, new_version, contents))
|
||||
expect(bump_cask_pr.replace_version_and_checksum(cask, new_hash, new_version, contents))
|
||||
.to eq <<~RUBY
|
||||
cask "foo" do
|
||||
on_arm do
|
||||
@@ -355,7 +355,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
cask = Homebrew::SimulateSystem.with(os: newest_macos, arch: :arm) { cask_from_contents(contents) }
|
||||
new_version = Homebrew::BumpVersionParser.new(arm: "2.0")
|
||||
|
||||
expect(bump_cask_pr.send(:replace_version_and_checksum, cask, new_hash, new_version, contents))
|
||||
expect(bump_cask_pr.replace_version_and_checksum(cask, new_hash, new_version, contents))
|
||||
.to eq <<~RUBY
|
||||
cask "foo" do
|
||||
arch arm: "arm", intel: "intel"
|
||||
@@ -388,7 +388,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
cask = cask_from_contents(contents)
|
||||
new_version = Homebrew::BumpVersionParser.new(arm: "2.0")
|
||||
|
||||
expect(bump_cask_pr.send(:replace_version_and_checksum, cask, new_hash, new_version, contents))
|
||||
expect(bump_cask_pr.replace_version_and_checksum(cask, new_hash, new_version, contents))
|
||||
.to eq <<~RUBY
|
||||
cask "foo" do
|
||||
on_arm do
|
||||
@@ -419,7 +419,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
new_version = Homebrew::BumpVersionParser.new(arm: "2.0", intel: "1.5")
|
||||
|
||||
expect(
|
||||
bump_cask_pr.send(:replace_version_and_checksum, cask, :no_check, new_version, contents),
|
||||
bump_cask_pr.replace_version_and_checksum(cask, :no_check, new_version, contents),
|
||||
).to eq <<~RUBY
|
||||
cask "foo" do
|
||||
on_arm do
|
||||
@@ -462,7 +462,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
new_version = Homebrew::BumpVersionParser.new(arm: "2.0")
|
||||
|
||||
expect(
|
||||
bump_cask_pr.send(:replace_version_and_checksum, cask, :no_check, new_version, contents),
|
||||
bump_cask_pr.replace_version_and_checksum(cask, :no_check, new_version, contents),
|
||||
).to eq <<~RUBY
|
||||
cask "foo" do
|
||||
on_arm do
|
||||
@@ -502,7 +502,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
new_version = Homebrew::BumpVersionParser.new(general: "2.0")
|
||||
|
||||
expect(
|
||||
bump_cask_pr.send(:replace_version_and_checksum, cask, new_hash, new_version, contents),
|
||||
bump_cask_pr.replace_version_and_checksum(cask, new_hash, new_version, contents),
|
||||
).to eq <<~RUBY
|
||||
cask "foo" do
|
||||
on_arm do
|
||||
@@ -544,7 +544,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
new_version = Homebrew::BumpVersionParser.new(arm: "2.0")
|
||||
|
||||
expect(
|
||||
bump_cask_pr.send(:replace_version_and_checksum, cask, :no_check, new_version, contents),
|
||||
bump_cask_pr.replace_version_and_checksum(cask, :no_check, new_version, contents),
|
||||
).to eq(contents)
|
||||
end
|
||||
end
|
||||
@@ -602,14 +602,14 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
|
||||
context "when cask is not in a tap" do
|
||||
it "outputs nothing" do
|
||||
expect { bump_cask_pr.send(:check_throttle, c, new_version:) }.not_to output.to_stderr
|
||||
expect { bump_cask_pr.check_throttle(c, new_version:) }.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
context "when a livecheck throttle value isn't present" do
|
||||
it "does not throttle" do
|
||||
allow(c).to receive(:tap).and_return(tap)
|
||||
expect { bump_cask_pr.send(:check_throttle, c, new_version:) }.not_to output.to_stderr
|
||||
expect { bump_cask_pr.check_throttle(c, new_version:) }.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -623,7 +623,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
it "does not throttle" do
|
||||
allow(c_throttle).to receive(:tap).and_return(tap)
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle, new_version: empty_version)
|
||||
bump_cask_pr.check_throttle(c_throttle, new_version: empty_version)
|
||||
end.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
@@ -632,7 +632,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
it "does not throttle" do
|
||||
allow(c_throttle).to receive(:tap).and_return(tap)
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle, new_version:)
|
||||
bump_cask_pr.check_throttle(c_throttle, new_version:)
|
||||
end.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
@@ -643,7 +643,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
it "throttles version" do
|
||||
allow(c_throttle).to receive(:tap).and_return(tap)
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle, new_version: new_version_indivisible)
|
||||
bump_cask_pr.check_throttle(c_throttle, new_version: new_version_indivisible)
|
||||
rescue SystemExit
|
||||
next
|
||||
end.to output(throttle_error).to_stderr
|
||||
@@ -661,7 +661,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(false)
|
||||
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle_rate_and_days, new_version: new_version_indivisible)
|
||||
bump_cask_pr.check_throttle(c_throttle_rate_and_days, new_version: new_version_indivisible)
|
||||
rescue SystemExit
|
||||
next
|
||||
end.to output(throttle_rate_days_error).to_stderr
|
||||
@@ -671,7 +671,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(true)
|
||||
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle_rate_and_days, new_version: new_version_indivisible)
|
||||
bump_cask_pr.check_throttle(c_throttle_rate_and_days, new_version: new_version_indivisible)
|
||||
end.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
@@ -685,7 +685,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(false)
|
||||
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle_days, new_version:)
|
||||
bump_cask_pr.check_throttle(c_throttle_days, new_version:)
|
||||
rescue SystemExit
|
||||
next
|
||||
end.to output(throttle_days_error).to_stderr
|
||||
@@ -695,7 +695,7 @@ RSpec.describe Homebrew::DevCmd::BumpCaskPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(true)
|
||||
|
||||
expect do
|
||||
bump_cask_pr.send(:check_throttle, c_throttle_days, new_version:)
|
||||
bump_cask_pr.check_throttle(c_throttle_days, new_version:)
|
||||
end.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -108,7 +108,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
it "outputs nothing" do
|
||||
allow(f).to receive(:tap).and_return(nil)
|
||||
|
||||
expect { bump_formula_pr.send(:check_throttle, f, "1.2.4") }.not_to output.to_stderr
|
||||
expect { bump_formula_pr.check_throttle(f, "1.2.4") }.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -116,7 +116,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
it "does not throttle" do
|
||||
allow(f).to receive(:tap).and_return(tap)
|
||||
|
||||
expect { bump_formula_pr.send(:check_throttle, f, "1.2.4") }.not_to output.to_stderr
|
||||
expect { bump_formula_pr.check_throttle(f, "1.2.4") }.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -124,7 +124,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
it "does not throttle" do
|
||||
allow(f_throttle).to receive(:tap).and_return(tap)
|
||||
|
||||
expect { bump_formula_pr.send(:check_throttle, f_throttle, "1.2.5") }.not_to output.to_stderr
|
||||
expect { bump_formula_pr.check_throttle(f_throttle, "1.2.5") }.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -133,7 +133,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
allow(f_throttle).to receive(:tap).and_return(tap)
|
||||
|
||||
expect do
|
||||
bump_formula_pr.send(:check_throttle, f_throttle, "1.2.4")
|
||||
bump_formula_pr.check_throttle(f_throttle, "1.2.4")
|
||||
rescue SystemExit
|
||||
nil
|
||||
end.to output(throttle_error).to_stderr
|
||||
@@ -149,7 +149,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(false)
|
||||
|
||||
expect do
|
||||
bump_formula_pr.send(:check_throttle, f_throttle_rate_and_days, "1.2.4")
|
||||
bump_formula_pr.check_throttle(f_throttle_rate_and_days, "1.2.4")
|
||||
rescue SystemExit
|
||||
nil
|
||||
end.to output(throttle_rate_days_error).to_stderr
|
||||
@@ -158,7 +158,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
it "does not throttle when throttle interval has elapsed" do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(true)
|
||||
|
||||
expect { bump_formula_pr.send(:check_throttle, f_throttle_rate_and_days, "1.2.4") }.not_to output.to_stderr
|
||||
expect { bump_formula_pr.check_throttle(f_throttle_rate_and_days, "1.2.4") }.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -171,7 +171,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(false)
|
||||
|
||||
expect do
|
||||
bump_formula_pr.send(:check_throttle, f_throttle_days, "1.2.4")
|
||||
bump_formula_pr.check_throttle(f_throttle_days, "1.2.4")
|
||||
rescue SystemExit
|
||||
next
|
||||
end.to output(throttle_days_error).to_stderr
|
||||
@@ -181,7 +181,7 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
allow(Homebrew::Livecheck).to receive(:throttle_interval_elapsed?).and_return(true)
|
||||
|
||||
expect do
|
||||
bump_formula_pr.send(:check_throttle, f_throttle_days, "1.2.4")
|
||||
bump_formula_pr.check_throttle(f_throttle_days, "1.2.4")
|
||||
end.not_to output.to_stderr
|
||||
end
|
||||
end
|
||||
@@ -210,13 +210,13 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
|
||||
it "only updates `:parent` resource" do
|
||||
expect(bump_formula_pr).to receive(:update_resource_block!).with(f, resource, version).and_return(:success)
|
||||
expect(bump_formula_pr.send(:update_matching_version_resources!, f, version:)).to eq({ "parent" => :success })
|
||||
expect(bump_formula_pr.update_matching_version_resources!(f, version:)).to eq({ "parent" => :success })
|
||||
end
|
||||
|
||||
it "does not update `:parent` resource if set in `--resource-versions`" do
|
||||
resource_versions = { "parent" => { current_version: "1.2.3", latest_version: version } }
|
||||
expect(bump_formula_pr).not_to receive(:update_resource_block!)
|
||||
expect(bump_formula_pr.send(:update_matching_version_resources!, f, version:, resource_versions:)).to eq({})
|
||||
expect(bump_formula_pr.update_matching_version_resources!(f, version:, resource_versions:)).to eq({})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -237,21 +237,21 @@ RSpec.describe Homebrew::DevCmd::BumpFormulaPr do
|
||||
version = "2.1.0"
|
||||
resource_versions = { "foo" => { current_version: "1.2.3", latest_version: version } }
|
||||
expect(bump_formula_pr).to receive(:update_resource_block!).with(f, r, version).and_return(:success)
|
||||
expect(bump_formula_pr.send(:update_resources!, f, resource_versions:)).to eq({ "foo" => :success })
|
||||
expect(bump_formula_pr.update_resources!(f, resource_versions:)).to eq({ "foo" => :success })
|
||||
end
|
||||
|
||||
it "downgrades to requested version" do
|
||||
version = "0.1.2"
|
||||
resource_versions = { "foo" => { current_version: "1.2.3", latest_version: version } }
|
||||
expect(bump_formula_pr).to receive(:update_resource_block!).with(f, r, version).and_return(:success)
|
||||
expect(bump_formula_pr.send(:update_resources!, f, resource_versions:)).to eq({ "foo" => :downgraded })
|
||||
expect(bump_formula_pr.update_resources!(f, resource_versions:)).to eq({ "foo" => :downgraded })
|
||||
end
|
||||
|
||||
it "returns update failures" do
|
||||
version = "0.1.2"
|
||||
resource_versions = { "foo" => { current_version: "1.2.3", latest_version: version } }
|
||||
expect(bump_formula_pr).to receive(:update_resource_block!).with(f, r, version).and_return(:url_unchanged)
|
||||
expect(bump_formula_pr.send(:update_resources!, f, resource_versions:)).to eq({ "foo" => :url_unchanged })
|
||||
expect(bump_formula_pr.update_resources!(f, resource_versions:)).to eq({ "foo" => :url_unchanged })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user