Compare commits

..

No commits in common. "main" and "4.6.8" have entirely different histories.
main ... 4.6.8

219 changed files with 863 additions and 1735 deletions

View File

@ -93,7 +93,7 @@ jobs:
path: results.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
sarif_file: results.sarif
category: zizmor

View File

@ -27,7 +27,7 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
languages: ruby
config: |
@ -35,4 +35,4 @@ jobs:
- Library/Homebrew/vendor
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11

View File

@ -52,7 +52,7 @@ jobs:
run: vale docs/
- name: Install Ruby
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
uses: ruby/setup-ruby@efbf473cab83af4468e8606cc33eca9281bb213f # v1.256.0
with:
bundler-cache: true
working-directory: docs

View File

@ -32,7 +32,7 @@ jobs:
TEMPORARY_CERTIFICATE_FILE: 'homebrew_developer_id_installer_certificate.p12'
TEMPORARY_KEYCHAIN_FILE: 'homebrew_installer_signing.keychain-db'
# Set to the oldest supported version of macOS
HOMEBREW_MACOS_OLDEST_SUPPORTED: '14.0'
HOMEBREW_MACOS_OLDEST_SUPPORTED: '13.0'
permissions:
contents: read # for code access
attestations: write # for actions/attest-build-provenance
@ -152,6 +152,10 @@ jobs:
fail-fast: false
matrix:
include:
# Intel
- runner: macos-13
name: macos-13-x86_64
# Apple Silicon
- runner: macos-14
name: macos-14-arm64
- runner: macos-15

View File

@ -43,7 +43,7 @@ jobs:
persist-credentials: false
- name: Install Ruby
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
uses: ruby/setup-ruby@efbf473cab83af4468e8606cc33eca9281bb213f # v1.256.0
with:
bundler-cache: true
working-directory: rubydoc

View File

@ -38,7 +38,7 @@ jobs:
pull-requests: write
steps:
- name: Mark/Close Stale Issues and Pull Requests
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
@ -68,7 +68,7 @@ jobs:
pull-requests: write
steps:
- name: Mark/Close Stale `bump-formula-pr` and `bump-cask-pr` Pull Requests
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 2

View File

@ -104,7 +104,7 @@ jobs:
- name: Cache style cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ~/Library/Caches/Homebrew/style
path: ~/.cache/Homebrew/style
key: tap-syntax-style-cache-${{ github.sha }}
restore-keys: tap-syntax-style-cache-
@ -331,7 +331,7 @@ jobs:
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}
- uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
- uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
with:
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
files: Library/Homebrew/test/coverage/coverage.xml
@ -495,7 +495,7 @@ jobs:
uses: Homebrew/actions/setup-homebrew@main
- name: Setup Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew/formula-analytics/.python-version

View File

@ -6,7 +6,7 @@ GEM
ast (2.4.3)
base64 (0.3.0)
benchmark (0.4.1)
bigdecimal (3.2.3)
bigdecimal (3.2.2)
bindata (2.5.1)
coderay (1.1.3)
concurrent-ruby (1.3.5)
@ -57,7 +57,7 @@ GEM
redcarpet (3.6.1)
regexp_parser (2.11.2)
require-hooks (0.2.2)
rexml (3.4.4)
rexml (3.4.2)
rspec (3.13.1)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
@ -79,7 +79,7 @@ GEM
rspec-support (3.13.5)
rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0)
rubocop (1.80.2)
rubocop (1.80.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@ -96,11 +96,11 @@ GEM
rubocop-md (2.0.2)
lint_roller (~> 1.1)
rubocop (>= 1.72.1)
rubocop-performance (1.26.0)
rubocop-performance (1.25.0)
lint_roller (~> 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rspec (3.7.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rspec (3.6.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-sorbet (0.10.5)
@ -124,15 +124,15 @@ GEM
simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4)
simpleidn (0.2.3)
sorbet (0.6.12521)
sorbet-static (= 0.6.12521)
sorbet-runtime (0.6.12521)
sorbet-static (0.6.12521-aarch64-linux)
sorbet-static (0.6.12521-universal-darwin)
sorbet-static (0.6.12521-x86_64-linux)
sorbet-static-and-runtime (0.6.12521)
sorbet (= 0.6.12521)
sorbet-runtime (= 0.6.12521)
sorbet (0.6.12466)
sorbet-static (= 0.6.12466)
sorbet-runtime (0.6.12466)
sorbet-static (0.6.12466-aarch64-linux)
sorbet-static (0.6.12466-universal-darwin)
sorbet-static (0.6.12466-x86_64-linux)
sorbet-static-and-runtime (0.6.12466)
sorbet (= 0.6.12466)
sorbet-runtime (= 0.6.12466)
spoom (1.7.6)
erubi (>= 1.10.0)
prism (>= 0.28.0)
@ -154,9 +154,9 @@ GEM
thor (>= 1.2.0)
yard-sorbet
thor (1.4.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.1.0)
unicode-display_width (3.1.5)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
vernier (1.8.0)
warning (1.5.0)
yard (0.9.37)

View File

@ -17,7 +17,7 @@ module Homebrew
HOMEBREW_CACHE_API = T.let((HOMEBREW_CACHE/"api").freeze, Pathname)
HOMEBREW_CACHE_API_SOURCE = T.let((HOMEBREW_CACHE/"api-source").freeze, Pathname)
DEFAULT_API_STALE_SECONDS = T.let(86400, Integer) # 1 day
TAP_MIGRATIONS_STALE_SECONDS = T.let(86400, Integer) # 1 day
sig { params(endpoint: String).returns(T::Hash[String, T.untyped]) }
def self.fetch(endpoint)
@ -37,25 +37,12 @@ module Homebrew
raise ArgumentError, "Invalid JSON file: #{Tty.underline}#{api_url}#{Tty.reset}"
end
sig { params(target: Pathname, stale_seconds: T.nilable(Integer)).returns(T::Boolean) }
def self.skip_download?(target:, stale_seconds:)
return true if Homebrew.running_as_root_but_not_owned_by_root?
return false if !target.exist? || target.empty?
return true unless stale_seconds
(Time.now - stale_seconds) < target.mtime
end
sig {
params(
endpoint: String,
target: Pathname,
stale_seconds: T.nilable(Integer),
download_queue: T.nilable(DownloadQueue),
).returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
params(endpoint: String, target: Pathname, stale_seconds: Integer, download_queue: T.nilable(DownloadQueue))
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
}
def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint,
stale_seconds: nil, download_queue: nil)
stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i, download_queue: nil)
# Lazy-load dependency.
require "development_tools"
@ -76,7 +63,12 @@ module Homebrew
insecure_download = DevelopmentTools.ca_file_substitution_required? ||
DevelopmentTools.curl_substitution_required?
skip_download = skip_download?(target:, stale_seconds:)
skip_download = target.exist? &&
!target.empty? &&
(!Homebrew.auto_update_command? ||
(Homebrew::EnvConfig.no_auto_update? && !Homebrew::EnvConfig.force_api_auto_update?) ||
((Time.now - stale_seconds) < target.mtime))
skip_download ||= Homebrew.running_as_root_but_not_owned_by_root?
if download_queue
unless skip_download
@ -169,28 +161,17 @@ module Homebrew
require "download_queue"
Homebrew::DownloadQueue.new
end
stale_seconds = if ENV["HOMEBREW_API_UPDATED"].present? ||
(Homebrew::EnvConfig.no_auto_update? && !Homebrew::EnvConfig.force_api_auto_update?)
nil
elsif Homebrew.auto_update_command?
Homebrew::EnvConfig.api_auto_update_secs.to_i
else
DEFAULT_API_STALE_SECONDS
end
stale_seconds = 86400 # 1 day
if Homebrew::EnvConfig.use_internal_api?
Homebrew::API::Internal.fetch_formula_api!(download_queue:, stale_seconds:)
Homebrew::API::Internal.fetch_cask_api!(download_queue:, stale_seconds:)
else
Homebrew::API::Formula.fetch_api!(download_queue:, stale_seconds:)
Homebrew::API::Formula.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS)
Homebrew::API::Formula.fetch_tap_migrations!(download_queue:, stale_seconds:)
Homebrew::API::Cask.fetch_api!(download_queue:, stale_seconds:)
Homebrew::API::Cask.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS)
Homebrew::API::Cask.fetch_tap_migrations!(download_queue:, stale_seconds:)
end
ENV["HOMEBREW_API_UPDATED"] = "1"
return unless download_queue
begin

View File

@ -75,18 +75,18 @@ module Homebrew
end
sig {
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: Integer)
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
}
def self.fetch_api!(download_queue: nil, stale_seconds: nil)
def self.fetch_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:
end
sig {
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: Integer)
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
}
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: nil)
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: Homebrew::API::TAP_MIGRATIONS_STALE_SECONDS)
Homebrew::API.fetch_json_api_file "cask_tap_migrations.jws.json", stale_seconds:, download_queue:
end

View File

@ -74,18 +74,18 @@ module Homebrew
end
sig {
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
}
def self.fetch_api!(download_queue: nil, stale_seconds: nil)
def self.fetch_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:
end
sig {
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
}
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: nil)
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: Homebrew::API::TAP_MIGRATIONS_STALE_SECONDS)
Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json", stale_seconds:, download_queue:
end

View File

@ -56,20 +56,20 @@ module Homebrew
end
sig {
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
.returns([T::Hash[String, T.untyped], T::Boolean])
}
def self.fetch_formula_api!(download_queue: nil, stale_seconds: nil)
json_contents, updated = Homebrew::API.fetch_json_api_file(formula_endpoint, stale_seconds:, download_queue:)
def self.fetch_formula_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
json_contents, updated = (Homebrew::API.fetch_json_api_file formula_endpoint, stale_seconds:, download_queue:)
[T.cast(json_contents, T::Hash[String, T.untyped]), updated]
end
sig {
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
.returns([T::Hash[String, T.untyped], T::Boolean])
}
def self.fetch_cask_api!(download_queue: nil, stale_seconds: nil)
json_contents, updated = Homebrew::API.fetch_json_api_file(cask_endpoint, stale_seconds:, download_queue:)
def self.fetch_cask_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
json_contents, updated = (Homebrew::API.fetch_json_api_file cask_endpoint, stale_seconds:, download_queue:)
[T.cast(json_contents, T::Hash[String, T.untyped]), updated]
end

View File

@ -10,13 +10,13 @@ module Homebrew
def initialize(url, name, version, **meta)
super
@target = T.let(meta.fetch(:target), Pathname)
@stale_seconds = T.let(meta[:stale_seconds], T.nilable(Integer))
@stale_seconds = T.let(meta.fetch(:stale_seconds), Integer)
end
sig { override.params(timeout: T.nilable(T.any(Integer, Float))).returns(Pathname) }
def fetch(timeout: nil)
with_context quiet: quiet? do
Homebrew::API.fetch_json_api_file(url, target: cached_location, stale_seconds: meta[:stale_seconds])
Homebrew::API.fetch_json_api_file(url, target: cached_location, stale_seconds: meta.fetch(:stale_seconds))
end
cached_location
end
@ -30,7 +30,7 @@ module Homebrew
class JSONDownload
include Downloadable
sig { params(url: String, target: Pathname, stale_seconds: T.nilable(Integer)).void }
sig { params(url: String, target: Pathname, stale_seconds: Integer).void }
def initialize(url, target:, stale_seconds:)
super()
@url = T.let(URL.new(url, using: API::JSONDownloadStrategy, target:, stale_seconds:), URL)

View File

@ -576,19 +576,18 @@ esac
# - docs/Installation.md
# - https://github.com/Homebrew/install/blob/HEAD/install.sh
# - Library/Homebrew/os/mac.rb (latest_sdk_version)
# - Library/Homebrew/os/mac/xcode.rb (latest_version), (minimum_version)
# and, if needed:
# - MacOSVersion::SYMBOLS
HOMEBREW_MACOS_NEWEST_UNSUPPORTED="27"
HOMEBREW_MACOS_NEWEST_UNSUPPORTED="16"
# TODO: bump version when new macOS is released
HOMEBREW_MACOS_NEWEST_SUPPORTED="26"
HOMEBREW_MACOS_NEWEST_SUPPORTED="15"
# TODO: bump version when new macOS is released and update references in:
# - docs/Installation.md
# - HOMEBREW_MACOS_OLDEST_SUPPORTED in .github/workflows/pkg-installer.yml
# - `os-version min` in package/Distribution.xml
# - https://github.com/Homebrew/install/blob/HEAD/install.sh
HOMEBREW_MACOS_OLDEST_SUPPORTED="14"
HOMEBREW_MACOS_OLDEST_ALLOWED="10.15"
HOMEBREW_MACOS_OLDEST_SUPPORTED="13"
HOMEBREW_MACOS_OLDEST_ALLOWED="10.11"
if [[ -n "${HOMEBREW_MACOS}" ]]
then

View File

@ -1,4 +1,4 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
# This script is loaded by formula_installer as a separate instance.
@ -23,40 +23,28 @@ require "extend/pathname/write_mkpath_extension"
class Build
include Utils::Output::Mixin
sig { returns(Formula) }
attr_reader :formula
attr_reader :formula, :deps, :reqs, :args
sig { returns(T::Array[Dependency]) }
attr_reader :deps
sig { returns(Requirements) }
attr_reader :reqs
sig { returns(Homebrew::Cmd::InstallCmd::Args) }
attr_reader :args
sig { params(formula: Formula, options: Options, args: Homebrew::Cmd::InstallCmd::Args).void }
def initialize(formula, options, args:)
@formula = formula
@formula.build = BuildOptions.new(options, formula.options)
@args = T.let(args, Homebrew::Cmd::InstallCmd::Args)
@deps = T.let([], T::Array[Dependency])
@reqs = T.let(Requirements.new, Requirements)
return if args.ignore_dependencies?
@args = args
if args.ignore_dependencies?
@deps = []
@reqs = []
else
@deps = expand_deps
@reqs = expand_reqs
end
end
sig { params(dependent: Formula).returns(BuildOptions) }
def effective_build_options_for(dependent)
args = dependent.build.used_options
args |= Tab.for_formula(dependent).used_options
BuildOptions.new(args, dependent.options)
end
sig { returns(Requirements) }
def expand_reqs
formula.recursive_requirements do |dependent, req|
build = effective_build_options_for(dependent)
@ -66,7 +54,6 @@ class Build
end
end
sig { returns(T::Array[Dependency]) }
def expand_deps
formula.recursive_dependencies do |dependent, dep|
build = effective_build_options_for(dependent)
@ -80,7 +67,6 @@ class Build
end
end
sig { void }
def install
formula_deps = deps.map(&:to_formula)
keg_only_deps = formula_deps.select(&:keg_only?)
@ -93,7 +79,7 @@ class Build
ENV.activate_extensions!(env: args.env)
if superenv?(args.env)
superenv = ENV
superenv = T.cast(ENV, Superenv)
superenv.keg_only_deps = keg_only_deps
superenv.deps = formula_deps
superenv.run_time_deps = run_time_deps
@ -197,8 +183,6 @@ class Build
(formula.logs/"00.options.out").write \
"#{formula.full_name} #{formula.build.used_options.sort.join(" ")}".strip
Pathname.prepend WriteMkpathExtension
formula.install
stdlibs = detect_stdlibs
@ -206,7 +190,7 @@ class Build
tab.write
# Find and link metafiles
formula.prefix.install_metafiles T.must(formula.buildpath)
formula.prefix.install_metafiles formula.buildpath
formula.prefix.install_metafiles formula.libexec if formula.libexec.exist?
normalize_pod2man_outputs!(formula)
@ -216,7 +200,6 @@ class Build
end
end
sig { returns(T::Array[Symbol]) }
def detect_stdlibs
keg = Keg.new(formula.prefix)
@ -226,15 +209,13 @@ class Build
keg.detect_cxx_stdlibs(skip_executables: true)
end
sig { params(formula: Formula).void }
def fixopt(formula)
path = if formula.linked_keg.directory? && formula.linked_keg.symlink?
formula.linked_keg.resolved_path
elsif formula.prefix.directory?
formula.prefix
elsif (children = formula.rack.children.presence) && children.size == 1 &&
(first_child = children.first.presence) && first_child.directory?
first_child
elsif (kids = formula.rack.children).size == 1 && kids.first.directory?
kids.first
else
raise
end
@ -243,7 +224,6 @@ class Build
raise "#{formula.opt_prefix} not present or broken\nPlease reinstall #{formula.full_name}. Sorry :("
end
sig { params(formula: Formula).void }
def normalize_pod2man_outputs!(formula)
keg = Keg.new(formula.prefix)
keg.normalize_pod2man_outputs!
@ -263,10 +243,12 @@ begin
trap("INT", old_trap)
formula = args.named.to_formulae.fetch(0)
formula = args.named.to_formulae.first
options = Options.create(args.flags_only)
build = Build.new(formula, options, args:)
Pathname.prepend WriteMkpathExtension
build.install
# Any exception means the build did not complete.
# The `case` for what to do per-exception class is further down.

View File

@ -1,11 +1,11 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
# Settings for the build environment.
class BuildEnvironment
sig { params(settings: Symbol).void }
def initialize(*settings)
@settings = T.let(Set.new(settings), T::Set[Symbol])
@settings = Set.new(settings)
end
sig { params(args: T::Enumerable[Symbol]).returns(T.self_type) }
@ -29,17 +29,16 @@ class BuildEnvironment
module DSL
# Initialise @env for each class which may use this DSL (e.g. each formula subclass).
# `env` may never be called and it needs to be initialised before the class is frozen.
sig { params(child: T.untyped).void }
def inherited(child)
super
child.instance_eval do
@env = T.let(BuildEnvironment.new, T.nilable(BuildEnvironment))
@env = BuildEnvironment.new
end
end
sig { params(settings: Symbol).returns(BuildEnvironment) }
def env(*settings)
T.must(@env).merge(settings)
@env.merge(settings)
end
end

View File

@ -1,12 +1,11 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
# Options for a formula build.
class BuildOptions
sig { params(args: Options, options: Options).void }
def initialize(args, options)
@args = T.let(args, Options)
@options = T.let(options, Options)
@args = args
@options = options
end
# True if a {Formula} is being built with a specific option.
@ -30,13 +29,8 @@ class BuildOptions
# args << "--with-example1"
# end
# ```
sig { params(val: T.any(String, Requirement, Dependency)).returns(T::Boolean) }
def with?(val)
option_names = if val.is_a?(String)
[val]
else
val.option_names
end
option_names = val.respond_to?(:option_names) ? val.option_names : [val]
option_names.any? do |name|
if option_defined? "with-#{name}"
@ -56,13 +50,11 @@ class BuildOptions
# ```ruby
# args << "--no-spam-plz" if build.without? "spam"
# ```
sig { params(val: T.any(String, Requirement, Dependency)).returns(T::Boolean) }
def without?(val)
!with?(val)
end
# True if a {Formula} is being built as a bottle (i.e. binary package).
sig { returns(T::Boolean) }
def bottle?
include? "build-bottle"
end
@ -83,7 +75,6 @@ class BuildOptions
# args << "--and-a-cold-beer" if build.with? "cold-beer"
# end
# ```
sig { returns(T::Boolean) }
def head?
include? "HEAD"
end
@ -96,35 +87,29 @@ class BuildOptions
# ```ruby
# args << "--some-feature" if build.stable?
# ```
sig { returns(T::Boolean) }
def stable?
!head?
end
# True if the build has any arguments or options specified.
sig { returns(T::Boolean) }
def any_args_or_options?
!@args.empty? || !@options.empty?
end
sig { returns(Options) }
def used_options
@options & @args
end
sig { returns(Options) }
def unused_options
@options - @args
end
private
sig { params(name: String).returns(T::Boolean) }
def include?(name)
@args.include?("--#{name}")
end
sig { params(name: String).returns(T::Boolean) }
def option_defined?(name)
@options.include? name
end

View File

@ -4,8 +4,6 @@
module Homebrew
# Class handling architecture-specific version information.
class BumpVersionParser
VERSION_SYMBOLS = [:general, :arm, :intel].freeze
sig { returns(T.nilable(T.any(Version, Cask::DSL::Version))) }
attr_reader :arm, :general, :intel

View File

@ -183,7 +183,7 @@ module Homebrew
require "bundle/tap_dumper"
@dsl ||= Brewfile.read(global:, file:)
kept_formulae = self.kept_formulae(global:, file:).filter_map { lookup_formula(_1) }
kept_formulae = self.kept_formulae(global:, file:).filter_map(&method(:lookup_formula))
kept_taps = @dsl.entries.select { |e| e.type == :tap }.map(&:name)
kept_taps += kept_formulae.filter_map(&:tap).map(&:name)
current_taps = Homebrew::Bundle::TapDumper.tap_names

View File

@ -40,7 +40,7 @@ module Homebrew
@formulae_by_full_name ||= {}
if name.nil?
formulae = Formula.installed.map { add_formula(_1) }
formulae = Formula.installed.map(&method(:add_formula))
sort!(formulae)
return @formulae_by_full_name
end

View File

@ -172,7 +172,7 @@ module Cask
.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)
id if pid.to_i.nonzero? && id.match?(regex)
end
end
@ -460,9 +460,9 @@ module Cask
def trash_paths(*paths, command: nil, **_)
return if paths.empty?
stdout = system_command(HOMEBREW_LIBRARY_PATH/"cask/utils/trash.swift",
stdout, = system_command HOMEBREW_LIBRARY_PATH/"cask/utils/trash.swift",
args: paths,
print_stderr: Homebrew::EnvConfig.developer?).stdout
print_stderr: Homebrew::EnvConfig.developer?
trashed, _, untrashable = stdout.partition("\n")
trashed = trashed.split(":")

View File

@ -79,9 +79,8 @@ module Cask
# 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.
sig { params(file: Pathname, altname: Pathname, command: T.class_of(SystemCommand)).returns(T.nilable(SystemCommand::Result)) }
def add_altname_metadata(file, altname, command:)
return if altname.to_s.casecmp(file.basename.to_s)&.zero?
def add_altname_metadata(file, altname, command: nil)
return if altname.to_s.casecmp(file.basename.to_s).zero?
odebug "Adding #{ALT_NAME_ATTRIBUTE} metadata"
altnames = command.run("/usr/bin/xattr",
@ -109,5 +108,3 @@ module Cask
end
end
end
require "extend/os/cask/artifact/relocated"

View File

@ -543,7 +543,6 @@ module Cask
print_stderr: false)
else
add_error "Unknown artifact type: #{artifact.class}", location: url.location
next
end
next false if result.success?
@ -623,17 +622,15 @@ module Cask
}.compact
Homebrew::Install.perform_preinstall_checks_once
formula_installers = primary_container.dependencies.map do |dep|
FormulaInstaller.new(
valid_formula_installers = Homebrew::Install.fetch_formulae(primary_container.dependencies)
primary_container.dependencies.each do |dep|
next unless valid_formula_installers.include?(dep)
fi = 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
@ -698,7 +695,7 @@ module Cask
add_error "No binaries in App: #{artifact.source}", location: url.location if files.empty?
main_binary = get_plist_main_binary(path)
main_binary ||= files.fetch(0)
main_binary ||= files.first
system_command("lipo", args: ["-archs", main_binary], print_stderr: false)
when Artifact::Binary
@ -756,9 +753,9 @@ module Cask
latest_version = Homebrew::Livecheck.latest_version(
cask,
referenced_formula_or_cask: referenced_cask,
)&.fetch(:latest, nil)
)&.fetch(:latest)
return :auto_detected if latest_version && (cask.version.to_s == latest_version.to_s)
return :auto_detected if cask.version.to_s == latest_version.to_s
add_error "Version '#{cask.version}' differs from '#{latest_version}' retrieved by livecheck."

View File

@ -6,7 +6,6 @@ require "cask/cask"
require "uri"
require "utils/curl"
require "utils/output"
require "utils/path"
require "extend/hash/keys"
require "api"
@ -113,7 +112,9 @@ module Cask
return unless path.expand_path.exist?
return if invalid_path?(path)
return unless ::Utils::Path.loadable_package_path?(path, :cask)
return if Homebrew::EnvConfig.forbid_packages_from_paths? &&
!path.realpath.to_s.start_with?("#{Caskroom.path}/", "#{HOMEBREW_LIBRARY}/Taps/")
new(path)
end

View File

@ -49,7 +49,7 @@ module Cask
SystemCommand.run("/bin/mkdir", args: ["-p", path], sudo:)
SystemCommand.run("/bin/chmod", args: ["g+rwx", path], sudo:)
SystemCommand.run("/usr/sbin/chown", args: [User.current.to_s, path], sudo:)
SystemCommand.run("/usr/sbin/chown", args: [User.current, path], sudo:)
SystemCommand.run("/usr/bin/chgrp", args: ["admin", path], sudo:)
end

View File

@ -733,7 +733,7 @@ module Homebrew
formulae_names = removable_formulae.map(&:full_name).sort
verb = dry_run ? "Would autoremove" : "Autoremoving"
oh1 "#{verb} #{formulae_names.count} unneeded #{Utils.pluralize("formula", formulae_names.count)}:"
oh1 "#{verb} #{formulae_names.count} unneeded #{Utils.pluralize("formula", formulae_names.count, plural: "e")}:"
puts formulae_names.join("\n")
return if dry_run

View File

@ -127,7 +127,7 @@ module Homebrew
conflicts "--all", "--no-vscode"
conflicts "--vscode", "--no-vscode"
conflicts "--install", "--upgrade"
conflicts "--file", "--global"
conflicts "--file=", "--global"
named_args %w[install dump cleanup check exec list sh env edit]
end

View File

@ -77,6 +77,7 @@ module Homebrew
description: "Show the size of installed formulae and casks."
conflicts "--installed", "--eval-all"
conflicts "--installed", "--all"
conflicts "--formula", "--cask"
conflicts "--fetch-manifest", "--cask"
conflicts "--fetch-manifest", "--json"

View File

@ -233,8 +233,8 @@ module Homebrew
.map(&:name)
next if dep_names.blank?
ohai "Would install #{::Utils.pluralize("dependency", dep_names.count, include_count: true)} " \
"for #{cask.full_name}:"
ohai "Would install #{::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y",
include_count: true)} for #{cask.full_name}:"
puts dep_names.join(" ")
end
return

View File

@ -23,7 +23,7 @@ module Homebrew
flag "--command=",
description: "Show options for the specified <command>."
conflicts "--command", "--installed", "--eval-all"
conflicts "--installed", "--all", "--command"
named_args :formula
end
@ -47,7 +47,7 @@ module Homebrew
puts
end
elsif args.no_named?
raise UsageError, "`brew options` needs a formula or `--eval-all` passed or `HOMEBREW_EVAL_ALL=1` set!"
raise FormulaUnspecifiedError
else
puts_options args.named.to_formulae
end

View File

@ -37,7 +37,8 @@ module Homebrew
description: "Search for casks."
switch "--desc",
description: "Search for formulae with a description matching <text> and casks with " \
"a name or description matching <text>."
"a name or description matching <text>.",
depends_on: "--eval-all"
switch "--eval-all",
description: "Evaluate all available formulae and casks, whether installed or not, to search their " \
"descriptions.",

View File

@ -68,7 +68,7 @@ module Homebrew
description: "Output as JSON."
conflicts "--all", "--file"
conflicts "--max-wait", "--no-wait"
conflicts "--max-wait=", "--no-wait"
named_args %w[list info run start stop kill restart cleanup]
end

View File

@ -50,7 +50,7 @@ homebrew-shellenv() {
echo "setenv HOMEBREW_REPOSITORY ${HOMEBREW_REPOSITORY};"
if [[ -n "${PATH_HELPER_ROOT}" ]]
then
echo "eval \`PATH_HELPER_ROOT=\"${PATH_HELPER_ROOT}\" /usr/libexec/path_helper -c\`;"
PATH_HELPER_ROOT="${PATH_HELPER_ROOT}" PATH="${HOMEBREW_PATH}" /usr/libexec/path_helper -c
else
echo "setenv PATH ${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin:\$PATH;"
fi
@ -75,7 +75,7 @@ homebrew-shellenv() {
fi
if [[ -n "${PATH_HELPER_ROOT}" ]]
then
echo "eval \"\$(PATH_HELPER_ROOT=\"${PATH_HELPER_ROOT}\" /usr/libexec/path_helper -s)\""
PATH_HELPER_ROOT="${PATH_HELPER_ROOT}" PATH="${HOMEBREW_PATH}" /usr/libexec/path_helper -s
else
echo "export PATH=\"${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin\${PATH+:\$PATH}\";"
fi

View File

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

View File

@ -842,7 +842,7 @@ class ReporterHub
msg = ""
if outdated_formulae.positive?
noun = Utils.pluralize("formula", outdated_formulae)
noun = Utils.pluralize("formula", outdated_formulae, plural: "e")
msg += "#{Tty.bold}#{outdated_formulae}#{Tty.reset} outdated #{noun}"
end
@ -974,11 +974,7 @@ class ReporterHub
# Skip non-homebrew/core formulae for security.
return if formula.include?("/")
begin
Formula[formula].desc&.presence
rescue FormulaUnavailableError
nil
end
else
all_formula_json.find { |f| f["name"] == formula }
&.fetch("desc", nil)
@ -992,11 +988,7 @@ class ReporterHub
# Skip non-homebrew/cask formulae for security.
return if cask.include?("/")
begin
Cask::CaskLoader.load(cask).desc&.presence
rescue Cask::CaskError
nil
end
else
all_cask_json.find { |f| f["token"] == cask }
&.fetch("desc", nil)

View File

@ -55,7 +55,7 @@ module Homebrew
description: "Include only casks."
conflicts "--formula", "--cask"
conflicts "--installed", "--eval-all"
conflicts "--installed", "--all"
conflicts "--missing", "--installed"
named_args :formula, min: 1

View File

@ -128,7 +128,7 @@ module Commands
OFFICIAL_CMD_TAPS.flat_map do |tap_name, cmds|
tap = Tap.fetch(tap_name)
tap.install(quiet:) unless tap.installed?
cmds.map { external_ruby_v2_cmd_path(_1) }.compact
cmds.map(&method(:external_ruby_v2_cmd_path)).compact
end
end

View File

@ -28,7 +28,6 @@ module DeprecateDisable
no_longer_meets_criteria: "no longer meets the criteria for acceptable casks",
unmaintained: "is not maintained upstream",
fails_gatekeeper_check: "does not pass the macOS Gatekeeper check",
unreachable: "is no longer reliably reachable upstream",
# odeprecate: remove the unsigned reason in a future release
unsigned: "is unsigned or does not meet signature requirements",
}.freeze, T::Hash[Symbol, String])

View File

@ -83,7 +83,7 @@ class DescriptionCacheStore < CacheStore
def delete_from_formula_names!(formula_names)
return if database.empty?
formula_names.each { delete!(_1) }
formula_names.each(&method(:delete!))
end
alias delete_from_cask_tokens! delete_from_formula_names!

View File

@ -93,11 +93,11 @@ module Homebrew
switch "--cask", "--casks",
description: "Treat all named arguments as casks."
conflicts "--installed", "--eval-all"
conflicts "--only", "--except"
conflicts "--only-cops", "--except-cops", "--strict"
conflicts "--only-cops", "--except-cops", "--only"
conflicts "--formula", "--cask"
conflicts "--installed", "--all"
named_args [:formula, :cask], without_api: true
end
@ -294,7 +294,9 @@ module Homebrew
errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true)
error_sources = []
error_sources << Utils.pluralize("formula", formula_count, include_count: true) if formula_count.positive?
if formula_count.positive?
error_sources << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
end
error_sources << Utils.pluralize("cask", cask_count, include_count: true) if cask_count.positive?
error_sources << Utils.pluralize("tap", tap_count, include_count: true) if tap_count.positive?

View File

@ -182,8 +182,7 @@ module Homebrew
sig {
params(old_keys: T::Array[String], old_bottle_spec: BottleSpecification,
new_bottle_hash: T::Hash[String, T.untyped])
.returns([T::Array[String], T::Array[T::Hash[Symbol, T.any(String, Symbol)]]])
new_bottle_hash: T::Hash[String, T.untyped]).returns(T::Array[T::Array[String]])
}
def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
mismatches = []
@ -410,7 +409,7 @@ module Homebrew
bottle_tag, rebuild = if local_bottle_json
_, tag_string, rebuild_string = Utils::Bottles.extname_tag_rebuild(formula.local_bottle_path.to_s)
[T.must(tag_string).to_sym, rebuild_string.to_i]
[tag_string.to_sym, rebuild_string.to_i]
end
bottle_tag = if bottle_tag
@ -861,8 +860,8 @@ module Homebrew
end
sig {
params(formula: Formula, formula_ast: Utils::AST::FormulaAST, bottle_hash: T::Hash[String, T.untyped])
.returns(T.nilable(T::Array[T::Hash[Symbol, T.any(String, Symbol)]]))
params(formula: Formula, formula_ast: Utils::AST::FormulaAST,
bottle_hash: T::Hash[String, T.untyped]).returns(T.nilable(T::Array[String]))
}
def old_checksums(formula, formula_ast, bottle_hash)
bottle_node = T.cast(formula_ast.bottle_block, T.nilable(RuboCop::AST::BlockNode))

View File

@ -49,8 +49,8 @@ module Homebrew
description: "Use the specified GitHub organization for forking."
conflicts "--dry-run", "--write"
conflicts "--version", "--version-arm"
conflicts "--version", "--version-intel"
conflicts "--version=", "--version-arm="
conflicts "--version=", "--version-intel="
named_args :cask, number: 1, without_api: true
end

View File

@ -9,8 +9,6 @@ require "utils/repology"
module Homebrew
module DevCmd
class Bump < AbstractCommand
NEWER_THAN_UPSTREAM_MSG = " (newer than upstream)"
class VersionBumpInfo < T::Struct
const :type, Symbol
const :multiple_versions, T::Boolean
@ -18,7 +16,6 @@ module Homebrew
const :current_version, BumpVersionParser
const :repology_latest, T.any(String, Version)
const :new_version, BumpVersionParser
const :newer_than_upstream, T::Hash[Symbol, T::Boolean], default: {}
const :duplicate_pull_requests, T.nilable(T.any(T::Array[String], String))
const :maybe_duplicate_pull_requests, T.nilable(T.any(T::Array[String], String))
end
@ -60,10 +57,10 @@ module Homebrew
switch "--bump-synced",
description: "Bump additional formulae marked as synced with the given formulae."
conflicts "--formula", "--cask"
conflicts "--tap", "--installed"
conflicts "--tap", "--no-autobump"
conflicts "--installed", "--eval-all"
conflicts "--cask", "--formula"
conflicts "--tap=", "--installed"
conflicts "--tap=", "--no-autobump"
conflicts "--eval-all", "--installed"
conflicts "--installed", "--auto"
conflicts "--no-pull-requests", "--open-pr"
@ -318,7 +315,6 @@ module Homebrew
new_versions = {}
repology_latest = repositories.present? ? Repology.latest_version(repositories) : "not found"
repology_latest_is_a_version = repology_latest.is_a?(Version)
# When blocks are absent, arch is not relevant. For consistency, we simulate the arm architecture.
arch_options = is_cask_with_blocks ? OnSystem::ARCH_OPTIONS : [:arm]
@ -335,31 +331,23 @@ module Homebrew
loaded_formula_or_cask = Cask::CaskLoader.load(formula_or_cask.sourcefile_path)
current_version_value = Version.new(loaded_formula_or_cask.version)
end
formula_or_cask_has_livecheck = loaded_formula_or_cask.livecheck_defined?
livecheck_latest = livecheck_result(loaded_formula_or_cask)
livecheck_latest_is_a_version = livecheck_latest.is_a?(Version)
new_version_value = if (livecheck_latest_is_a_version &&
new_version_value = if (livecheck_latest.is_a?(Version) &&
Livecheck::LivecheckVersion.create(formula_or_cask, livecheck_latest) >=
Livecheck::LivecheckVersion.create(formula_or_cask, current_version_value)) ||
current_version_value == "latest"
livecheck_latest
elsif livecheck_latest.is_a?(String) && livecheck_latest.start_with?("skipped")
"skipped"
elsif repology_latest_is_a_version &&
!formula_or_cask_has_livecheck &&
elsif repology_latest.is_a?(Version) &&
repology_latest > current_version_value &&
!loaded_formula_or_cask.livecheck_defined? &&
current_version_value != "latest"
repology_latest
end.presence
# Fall back to the upstream version if there isn't a new version
# value at this point, as this will allow us to surface an upstream
# version that's lower than the current version.
new_version_value ||= livecheck_latest if livecheck_latest_is_a_version
new_version_value ||= repology_latest if repology_latest_is_a_version && !formula_or_cask_has_livecheck
# Store old and new versions
old_versions[version_key] = current_version_value
new_versions[version_key] = new_version_value
@ -392,22 +380,10 @@ module Homebrew
new_version = BumpVersionParser.new(general: "unable to get versions")
end
newer_than_upstream = {}
BumpVersionParser::VERSION_SYMBOLS.each do |version_type|
new_version_value = new_version.send(version_type)
next unless new_version_value.is_a?(Version)
newer_than_upstream[version_type] =
(current_version_value = current_version.send(version_type)).is_a?(Version) &&
(Livecheck::LivecheckVersion.create(formula_or_cask, current_version_value) >
Livecheck::LivecheckVersion.create(formula_or_cask, new_version_value))
end
if !args.no_pull_requests? &&
(new_version.general != "unable to get versions") &&
(new_version.general != "skipped") &&
(new_version != current_version) &&
!newer_than_upstream.all? { |_k, v| v == true }
(new_version != current_version)
# We use the ARM version for the pull request version. This is
# consistent with the behavior of bump-cask-pr.
pull_request_version = if multiple_versions
@ -434,7 +410,6 @@ module Homebrew
current_version:,
repology_latest:,
new_version:,
newer_than_upstream:,
duplicate_pull_requests:,
maybe_duplicate_pull_requests:,
)
@ -457,8 +432,8 @@ module Homebrew
new_version = version_info.new_version
repology_latest = version_info.repology_latest
# Check if all versions are equal
versions_equal = (new_version == current_version)
all_newer_than_upstream = version_info.newer_than_upstream.all? { |_k, v| v == true }
title_name = ambiguous_cask ? "#{name} (cask)" : name
title = if (repology_latest == current_version.general || !repology_latest.is_a?(Version)) && versions_equal
@ -469,13 +444,10 @@ module Homebrew
# Conditionally format output based on type of formula_or_cask
current_versions = if version_info.multiple_versions
"arm: #{current_version.arm}" \
"#{NEWER_THAN_UPSTREAM_MSG if version_info.newer_than_upstream[:arm]}" \
"\n intel: #{current_version.intel}" \
"#{NEWER_THAN_UPSTREAM_MSG if version_info.newer_than_upstream[:intel]}"
"arm: #{current_version.arm}
intel: #{current_version.intel}"
else
newer_than_upstream_general = version_info.newer_than_upstream[:general]
"#{current_version.general}#{NEWER_THAN_UPSTREAM_MSG if newer_than_upstream_general}"
current_version.general.to_s
end
current_versions << " (deprecated)" if formula_or_cask.deprecated?
@ -510,8 +482,7 @@ module Homebrew
if !args.no_pull_requests? &&
(new_version.general != "unable to get versions") &&
(new_version.general != "skipped") &&
!versions_equal &&
!all_newer_than_upstream
!versions_equal
if duplicate_pull_requests
duplicate_pull_requests_text = duplicate_pull_requests
elsif maybe_duplicate_pull_requests
@ -530,8 +501,7 @@ module Homebrew
if !args.open_pr? ||
(new_version.general == "unable to get versions") ||
(new_version.general == "skipped") ||
all_newer_than_upstream
(new_version.general == "skipped")
return
end

View File

@ -6,23 +6,13 @@ require "abstract_command"
module Homebrew
module DevCmd
class Contributions < AbstractCommand
PRIMARY_REPOS = T.let(%w[
Homebrew/brew
Homebrew/homebrew-core
Homebrew/homebrew-cask
].freeze, T::Array[String])
ALL_REPOS = T.let([
*PRIMARY_REPOS,
*OFFICIAL_CMD_TAPS.keys,
].freeze, T::Array[String])
CONTRIBUTION_TYPES = T.let({
merged_pr_author: "merged PR author",
approved_pr_review: "approved PR reviewer",
committer: "commit author or committer",
coauthor: "commit coauthor",
}.freeze, T::Hash[Symbol, String])
MAX_COMMITS = T.let(1000, Integer)
MAX_PR_SEARCH = T.let(100, Integer)
PRIMARY_REPOS = T.let(%w[brew core cask].freeze, T::Array[String])
SUPPORTED_REPOS = T.let([
PRIMARY_REPOS,
OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") },
OFFICIAL_CASK_TAPS.reject { |t| t == "cask" },
].flatten.freeze, T::Array[String])
MAX_REPO_COMMITS = 1000
cmd_args do
usage_banner "`contributions` [`--user=`] [`--repositories=`] [`--from=`] [`--to=`] [`--csv`]"
@ -34,17 +24,10 @@ module Homebrew
"contributions from. Omitting this flag searches Homebrew maintainers."
comma_array "--repositories",
description: "Specify a comma-separated list of repositories to search. " \
"All repositories must be under the same user or organisation. " \
"Supported repositories: #{SUPPORTED_REPOS.map { |t| "`#{t}`" }.to_sentence}. " \
"Omitting this flag, or specifying `--repositories=primary`, searches only the " \
"main repositories: `Homebrew/brew`, `Homebrew/homebrew-core`, " \
"`Homebrew/homebrew-cask`. Specifying `--repositories=all` searches all " \
"non-deprecated Homebrew repositories. "
flag "--organisation=", "--organization=", "--org=",
description: "Specify the organisation to populate sources repositories from. " \
"Omitting this flag searches the Homebrew primary repositories."
flag "--team=",
description: "Specify the team to populate users from. " \
"The first part of the team name will be used as the organisation."
"main repositories: `brew`, `core`, `cask`. " \
"Specifying `--repositories=all` searches all repositories. "
flag "--from=",
description: "Date (ISO 8601 format) to start searching contributions. " \
"Omitting this flag searches the past year."
@ -52,93 +35,54 @@ module Homebrew
description: "Date (ISO 8601 format) to stop searching contributions."
switch "--csv",
description: "Print a CSV of contributions across repositories over the time period."
conflicts "--organisation", "--repositories"
conflicts "--organisation", "--team"
conflicts "--user", "--team"
end
sig { override.void }
def run
odie "Cannot get contributions as `$HOMEBREW_NO_GITHUB_API` is set!" if Homebrew::EnvConfig.no_github_api?
Homebrew.install_bundler_gems!(groups: ["contributions"]) if args.csv?
require "utils/github"
results = {}
grand_totals = {}
repos = T.must(
if args.repositories.blank? || args.repositories&.include?("primary")
PRIMARY_REPOS
elsif args.repositories&.include?("all")
SUPPORTED_REPOS
else
args.repositories
end,
)
repos.each do |repo|
if SUPPORTED_REPOS.exclude?(repo)
odie "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}."
end
end
from = args.from.presence || Date.today.prev_year.iso8601
to = args.to.presence || (Date.today + 1).iso8601
organisation = nil
users = if (team = args.team.presence)
team_sections = team.split("/")
organisation = team_sections.first.presence
team_name = team_sections.last.presence
if team_sections.length != 2 || organisation.nil? || team_name.nil?
odie "Team must be in the format `organisation/team`!"
end
puts "Getting members for #{organisation}/#{team_name}..." if args.verbose?
GitHub.members_by_team(organisation, team_name).keys
elsif (users = args.user.presence)
users
else
puts "Getting members for Homebrew/maintainers..." if args.verbose?
GitHub.members_by_team("Homebrew", "maintainers").keys
end
repositories = if (org = organisation.presence) || (org = args.organisation.presence)
organisation = org
puts "Getting repositories for #{organisation}..." if args.verbose?
GitHub.organisation_repositories(organisation, from, to, args.verbose?)
elsif (repos = args.repositories.presence) && repos.length == 1 && (first_repository = repos.first)
case first_repository
when "primary"
PRIMARY_REPOS
when "all"
ALL_REPOS
else
Array(first_repository)
end
elsif (repos = args.repositories.presence)
organisations = repos.map { |repository| repository.split("/").first }.uniq
odie "All repositories must be under the same user or organisation!" if organisations.length > 1
repos
else
PRIMARY_REPOS
end
organisation ||= T.must(repositories.fetch(0).split("/").first)
contribution_types = [:author, :committer, :coauthor, :review]
require "utils/github"
users = args.user.presence || GitHub.members_by_team("Homebrew", "maintainers").keys
users.each do |username|
# TODO: Using the GitHub username to scan the `git log` undercounts some
# contributions as people might not always have configured their Git
# committer details to match the ones on GitHub.
# TODO: Switch to using the GitHub APIs instead of `git log` if
# they ever support trailers.
results[username] = scan_repositories(organisation, repositories, username, from:, to:)
results[username] = scan_repositories(repos, username, from:)
grand_totals[username] = total(results[username])
search_types = [:merged_pr_author, :approved_pr_review].freeze
greater_than_total = T.let(false, T::Boolean)
contributions = CONTRIBUTION_TYPES.keys.filter_map do |type|
contributions = contribution_types.filter_map do |type|
type_count = grand_totals[username][type]
next if type_count.zero?
next if type_count.to_i.zero?
count_prefix = ""
if (search_types.include?(type) && type_count == MAX_PR_SEARCH) ||
(type == :committer && type_count == MAX_COMMITS)
greater_than_total ||= true
count_prefix = ">="
"#{Utils.pluralize("time", type_count, include_count: true)} (#{type})"
end
pretty_type = CONTRIBUTION_TYPES.fetch(type)
"#{count_prefix}#{Utils.pluralize("time", type_count, include_count: true)} (#{pretty_type})"
end
total = Utils.pluralize("time", grand_totals[username].values.sum, include_count: true)
total_prefix = ">=" if greater_than_total
contributions << "#{total_prefix}#{total} (total)"
contributions <<
"#{Utils.pluralize("time", grand_totals[username].values.sum, include_count: true)} (total)"
contributions_string = [
"#{username} contributed",
@ -160,16 +104,12 @@ module Homebrew
private
sig { params(repository: String).returns([T.nilable(Pathname), T.nilable(Tap)]) }
def repository_path_and_tap(repository)
return [HOMEBREW_REPOSITORY, nil] if repository == "Homebrew/brew"
return [nil, nil] if repository.exclude?("/homebrew-")
sig { params(repo: String).returns(Pathname) }
def find_repo_path_for_repo(repo)
return HOMEBREW_REPOSITORY if repo == "brew"
require "tap"
tap = Tap.fetch(repository)
return [nil, nil] if tap.user == "Homebrew" && DEPRECATED_OFFICIAL_TAPS.include?(tap.repository)
[tap.path, tap]
Tap.fetch("homebrew", repo).path
end
sig { params(from: T.nilable(String), to: T.nilable(String)).returns(String) }
@ -190,7 +130,7 @@ module Homebrew
require "csv"
CSV.generate do |csv|
csv << ["user", "repository", *CONTRIBUTION_TYPES.keys, "total"]
csv << %w[user repo author committer coauthor review total]
totals.sort_by { |_, v| -v.values.sum }.each do |user, total|
csv << grand_total_row(user, total)
@ -198,67 +138,63 @@ module Homebrew
end
end
sig { params(user: String, grand_total: T::Hash[Symbol, Integer]).returns(T::Array[T.any(String, T.nilable(Integer))]) }
sig {
params(
user: String,
grand_total: T::Hash[Symbol, Integer],
).returns(
[String, String, T.nilable(Integer), T.nilable(Integer), T.nilable(Integer), T.nilable(Integer), Integer],
)
}
def grand_total_row(user, grand_total)
grand_totals = grand_total.slice(*CONTRIBUTION_TYPES.keys).values
[user, "all", *grand_totals, grand_totals.sum]
[
user,
"all",
grand_total[:author],
grand_total[:committer],
grand_total[:coauthor],
grand_total[:review],
grand_total.values.sum,
]
end
sig {
params(
organisation: String,
repositories: T::Array[String],
repos: T::Array[String],
person: String,
from: String,
to: String,
).returns(T::Hash[Symbol, T.untyped])
}
def scan_repositories(organisation, repositories, person, from:, to:)
def scan_repositories(repos, person, from:)
data = {}
return data if repositories.blank?
return data if repos.blank?
require "tap"
require "utils/github"
max = MAX_COMMITS
verbose = args.verbose?
puts "Querying pull requests for #{person} in #{organisation}..." if args.verbose?
organisation_merged_prs = \
GitHub.search_merged_pull_requests_in_user_or_organisation(organisation, person, from:, to:)
organisation_approved_reviews = \
GitHub.search_approved_pull_requests_in_user_or_organisation(organisation, person, from:, to:)
require "utils/git"
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..."
repos.each do |repo|
repo_path = find_repo_path_for_repo(repo)
tap = Tap.fetch("homebrew", repo)
unless repo_path.exist?
opoo "Repository #{repo} not yet tapped! Tapping it now..."
tap.install
end
repository_full_name = tap&.full_name
repository_full_name ||= repository
repository_api_url = "#{GitHub::API_URL}/repos/#{repository_full_name}"
puts "Determining contributions for #{person} on #{repository_full_name}..." if args.verbose?
merged_pr_author = organisation_merged_prs.count do |pr|
pr.fetch("repository_url") == repository_api_url
repo_full_name = if repo == "brew"
"homebrew/brew"
else
tap.full_name
end
approved_pr_review = organisation_approved_reviews.count do |pr|
pr.fetch("repository_url") == repository_api_url
end
committer = GitHub.count_repository_commits(repository_full_name, person, max:, verbose:, from:, to:)
coauthor = Utils::Git.count_coauthors(repository_path, person, from:, to:)
data[repository] = { merged_pr_author:, approved_pr_review:, committer:, coauthor: }
rescue GitHub::API::RateLimitExceededError => e
sleep_seconds = e.reset - Time.now.to_i
opoo "GitHub rate limit exceeded, sleeping for #{sleep_seconds} seconds..."
sleep sleep_seconds
retry
puts "Determining contributions for #{person} on #{repo_full_name}..." if args.verbose?
author_commits, committer_commits = GitHub.count_repo_commits(repo_full_name, person,
from:, to: args.to, max: MAX_REPO_COMMITS)
data[repo] = {
author: author_commits,
committer: committer_commits,
coauthor: git_log_trailers_cmd(repo_path, person, "Co-authored-by", from:, to: args.to),
review: count_reviews(repo_full_name, person, from:, to: args.to),
}
end
data
@ -266,17 +202,43 @@ module Homebrew
sig { params(results: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, Integer]) }
def total(results)
totals = {}
totals = { author: 0, committer: 0, coauthor: 0, review: 0 }
results.each_value do |counts|
counts.each do |kind, count|
totals[kind] ||= 0
totals[kind] += count
end
end
totals
end
sig {
params(repo_path: Pathname, person: String, trailer: String, from: T.nilable(String),
to: T.nilable(String)).returns(Integer)
}
def git_log_trailers_cmd(repo_path, person, trailer, from:, to:)
cmd = ["git", "-C", repo_path, "log", "--oneline"]
cmd << "--format='%(trailers:key=#{trailer}:)'"
cmd << "--before=#{to}" if to
cmd << "--after=#{from}" if from
Utils.safe_popen_read(*cmd).lines.count { |l| l.include?(person) }
end
sig {
params(repo_full_name: String, person: String, from: T.nilable(String),
to: T.nilable(String)).returns(Integer)
}
def count_reviews(repo_full_name, person, from:, to:)
require "utils/github"
GitHub.count_issues("", is: "pr", repo: repo_full_name, reviewed_by: person, review: "approved", from:, to:)
rescue GitHub::API::ValidationFailedError
if args.verbose?
onoe "Couldn't search GitHub for PRs by #{person}. Their profile might be private. Defaulting to 0."
end
0 # Users who have made their contributions private are not searchable to determine counts.
end
end
end
end

View File

@ -41,9 +41,9 @@ module Homebrew
switch "--autobump",
description: "Include packages that are autobumped by BrewTestBot. By default these are skipped."
conflicts "--tap", "--installed", "--eval-all"
conflicts "--json", "--debug"
conflicts "--formula", "--cask"
conflicts "--debug", "--json"
conflicts "--tap=", "--eval-all", "--installed"
conflicts "--cask", "--formula"
conflicts "--formula", "--extract-plist"
named_args [:formula, :cask], without_api: true
@ -78,15 +78,9 @@ module Homebrew
formulae + casks
elsif File.exist?(watchlist_path)
begin
# This removes blank lines, comment lines, and trailing comments
names = Pathname.new(watchlist_path).read.lines
.filter_map do |line|
comment_index = line.index("#")
next if comment_index&.zero?
line = line[0...comment_index] if comment_index
line&.strip.presence
end
.reject { |line| line.start_with?("#") || line.blank? }
.map(&:strip)
named_args = CLI::NamedArgs.new(*names, parent: args)
named_args.to_formulae_and_casks(ignore_unavailable: true)

View File

@ -208,7 +208,8 @@ module Homebrew
if pull_request
# This is a tap pull request and approving reviewers should also sign-off.
tap = T.must(Tap.from_path(git_repo.pathname))
review_trailers = GitHub.repository_approved_reviews(tap.user, tap.full_repository, pull_request).map do |r|
review_trailers = GitHub.approved_reviews(tap.user, tap.full_name.split("/").last,
pull_request).map do |r|
"Signed-off-by: #{r["name"]} <#{r["email"]}>"
end
trailers = trailers.lines.concat(review_trailers).map(&:strip).uniq.join("\n")

View File

@ -114,9 +114,9 @@ class DevelopmentTools
# Get the GCC version.
#
# @api public
# @api internal
sig { params(cc: String).returns(Version) }
def gcc_version(cc = host_gcc_path.to_s)
def gcc_version(cc)
(@gcc_version ||= T.let({}, T.nilable(T::Hash[String, Version]))).fetch(cc) do
path = HOMEBREW_PREFIX/"opt/#{CompilerSelector.preferred_gcc}/bin"/cc
path = locate(cc) unless path.exist?

View File

@ -579,7 +579,7 @@ class UnbottledError < RuntimeError
require "utils"
msg = <<~EOS
The following #{Utils.pluralize("formula", formulae.count)} cannot be installed from #{Utils.pluralize("bottle", formulae.count)} and must be
The following #{Utils.pluralize("formula", formulae.count, plural: "e")} cannot be installed from #{Utils.pluralize("bottle", formulae.count)} and must be
built from source.
#{formulae.to_sentence}
EOS

View File

@ -1,4 +0,0 @@
# typed: strict
# frozen_string_literal: true
require "extend/os/linux/cask/artifact/relocated" if OS.linux?

View File

@ -1,23 +0,0 @@
# typed: strict
# frozen_string_literal: true
module OS
module Linux
module Cask
module Artifact
module Relocated
extend T::Helpers
requires_ancestor { ::Cask::Artifact::Relocated }
sig { params(file: Pathname, altname: Pathname, command: T.class_of(SystemCommand)).returns(T.nilable(SystemCommand::Result)) }
def add_altname_metadata(file, altname, command:)
# no-op on Linux: /usr/bin/xattr for setting extended attributes is not available there.
end
end
end
end
end
end
Cask::Artifact::Relocated.prepend(OS::Linux::Cask::Artifact::Relocated)

View File

@ -42,14 +42,14 @@ module OS
@needs_libc_formula ||= OS::Linux::Glibc.below_ci_version?
end
# Keep this method around for now to make it easier to add this functionality later.
# rubocop:disable Lint/UselessMethodDefinition
sig { returns(Pathname) }
def host_gcc_path
# Prioritise versioned path if installed
path = Pathname.new("/usr/bin/#{OS::LINUX_PREFERRED_GCC_COMPILER_FORMULA.tr("@", "-")}")
return path if path.exist?
# TODO: override this if/when we to pick the GCC based on e.g. the Ubuntu version.
super
end
# rubocop:enable Lint/UselessMethodDefinition
sig { returns(T::Boolean) }
def needs_compiler_formula?
@ -60,7 +60,12 @@ module OS
# Undocumented environment variable to make it easier to test compiler
# formula automatic installation.
@needs_compiler_formula = true if ENV["HOMEBREW_FORCE_COMPILER_FORMULA"]
@needs_compiler_formula ||= OS::Linux::Libstdcxx.below_ci_version?
@needs_compiler_formula ||= if host_gcc_path.exist?
::DevelopmentTools.gcc_version(host_gcc_path.to_s) < OS::LINUX_GCC_CI_VERSION
else
true
end
end
sig { returns(T::Hash[String, T.nilable(String)]) }

View File

@ -40,8 +40,6 @@ module OS
self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula)
m4_path_deps = ["libtool", "bison"]
self["M4"] = "#{HOMEBREW_PREFIX}/opt/m4/bin/m4" if deps.any? { m4_path_deps.include?(_1.name) }
# Build jemalloc-sys rust crate on ARM64/AArch64 with support for page sizes up to 64K.
self["JEMALLOC_SYS_WITH_LG_PAGE"] = "16" if ::Hardware::CPU.arch == :arm64
# Pointer authentication and BTI are hardening techniques most distros
# use by default on their packages. arm64 Linux we're packaging

View File

@ -1,24 +1,36 @@
# typed: strict
# frozen_string_literal: true
require "os/linux/ld"
require "os/linux/libstdcxx"
require "utils/output"
module OS
module Linux
module Install
module ClassMethods
# This is a list of known paths to the host dynamic linker on Linux if
# the host glibc is new enough. The symlink_ld_so method will fail if
# the host linker cannot be found in this list.
DYNAMIC_LINKERS = %w[
/lib64/ld-linux-x86-64.so.2
/lib64/ld64.so.2
/lib/ld-linux.so.3
/lib/ld-linux.so.2
/lib/ld-linux-aarch64.so.1
/lib/ld-linux-armhf.so.3
/system/bin/linker64
/system/bin/linker
].freeze
# We link GCC runtime libraries that are not specifically used for Fortran,
# which are linked by the GCC formula. We only use the versioned shared libraries
# as the other shared and static libraries are only used at build time where
# GCC can find its own libraries.
GCC_RUNTIME_LIBS = T.let(%W[
GCC_RUNTIME_LIBS = %w[
libatomic.so.1
libgcc_s.so.1
libgomp.so.1
#{OS::Linux::Libstdcxx::SONAME}
].freeze, T::Array[String])
libstdc++.so.6
].freeze
sig { params(all_fatal: T::Boolean).void }
def perform_preinstall_checks(all_fatal: false)
@ -55,7 +67,7 @@ module OS
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
unless ld_so.readable?
ld_so = OS::Linux::Ld.system_ld_so
ld_so = DYNAMIC_LINKERS.find { |s| File.executable? s }
if ld_so.blank?
::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?

View File

@ -2,13 +2,12 @@
# frozen_string_literal: true
require "compilers"
require "os/linux/libstdcxx"
module OS
module Linux
module LinkageChecker
# Libraries provided by glibc and gcc.
SYSTEM_LIBRARY_ALLOWLIST = %W[
SYSTEM_LIBRARY_ALLOWLIST = %w[
ld-linux-x86-64.so.2
ld-linux-aarch64.so.1
libanl.so.1
@ -25,7 +24,7 @@ module OS
libutil.so.1
libgcc_s.so.1
libgomp.so.1
#{OS::Linux::Libstdcxx::SONAME}
libstdc++.so.6
libquadmath.so.0
].freeze

View File

@ -3,7 +3,6 @@
require "compilers"
require "os/linux/glibc"
require "os/linux/libstdcxx"
require "system_command"
module OS
@ -21,13 +20,6 @@ module OS
version
end
def host_libstdcxx_version
version = OS::Linux::Libstdcxx.system_version
return "N/A" if version.null?
version
end
def host_gcc_version
gcc = ::DevelopmentTools.host_gcc_path
return "N/A" unless gcc.executable?
@ -44,7 +36,7 @@ module OS
end
def host_ruby_version
out, _, status = system_command(HOST_RUBY_PATH, args: ["-e", "puts RUBY_VERSION"], print_stderr: false).to_a
out, _, status = system_command(HOST_RUBY_PATH, args: ["-e", "puts RUBY_VERSION"], print_stderr: false)
return "N/A" unless status.success?
out
@ -57,7 +49,6 @@ module OS
out.puts "OS: #{OS::Linux.os_version}"
out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl?
out.puts "Host glibc: #{host_glibc_version}"
out.puts "Host libstdc++: #{host_libstdcxx_version}"
out.puts "#{::DevelopmentTools.host_gcc_path}: #{host_gcc_version}"
out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH
["glibc", ::CompilerSelector.preferred_gcc, OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA, "xorg"].each do |f|

View File

@ -422,13 +422,11 @@ class Pathname
sig { returns(T::Array[String]) }
def zipinfo
@zipinfo ||= T.let(
system_command("zipinfo", args: ["-1", self], print_stderr: false)
@zipinfo ||= T.let(nil, T.nilable(String))
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
.stdout
.encode(Encoding::UTF_8, invalid: :replace)
.split("\n"),
T.nilable(T::Array[String]),
)
.split("\n")
end
private

View File

@ -749,7 +749,7 @@ module Homebrew
end
def audit_specs
problem "HEAD-only (no stable download)" if head_only?(formula) && @core_tap
problem "HEAD-only (no stable download)" if head_only?(formula)
%w[Stable HEAD].each do |name|
spec_name = name.downcase.to_sym

View File

@ -609,8 +609,8 @@ on_request: installed_on_request?, options:)
clean
# Store the formula used to build the keg in the keg.
formula_contents = if (local_bottle_path = formula.local_bottle_path)
Utils::Bottles.formula_contents local_bottle_path, name: formula.name
formula_contents = if formula.local_bottle_path
Utils::Bottles.formula_contents formula.local_bottle_path, name: formula.name
else
formula.path.read
end
@ -1156,17 +1156,6 @@ on_request: installed_on_request?, options:)
def link(keg)
Formula.clear_cache
cask_installed_with_formula_name = begin
Cask::CaskLoader.load(formula.name, warn: false).installed?
rescue Cask::CaskUnavailableError, Cask::CaskInvalidError
false
end
if cask_installed_with_formula_name
ohai "#{formula.name} cask is installed, skipping link."
@link_keg = false
end
unless link_keg
begin
keg.optlink(verbose: verbose?, overwrite: overwrite?)
@ -1178,6 +1167,17 @@ on_request: installed_on_request?, options:)
return
end
cask_installed_with_formula_name = begin
Cask::CaskLoader.load(formula.name, warn: false).installed?
rescue Cask::CaskUnavailableError, Cask::CaskInvalidError
false
end
if cask_installed_with_formula_name
ohai "#{formula.name} cask is installed, skipping link."
return
end
if keg.linked?
opoo "This keg was marked linked already, continuing anyway"
keg.remove_linked_keg_record

View File

@ -7,7 +7,6 @@ require "tab"
require "utils"
require "utils/bottles"
require "utils/output"
require "utils/path"
require "service"
require "utils/curl"
require "deprecate_disable"
@ -728,7 +727,29 @@ module Formulary
end
return unless path.expand_path.exist?
return unless ::Utils::Path.loadable_package_path?(path, :formula)
if Homebrew::EnvConfig.forbid_packages_from_paths?
path_realpath = path.realpath.to_s
path_string = path.to_s
if (path_realpath.end_with?(".rb") || path_string.end_with?(".rb")) &&
!path_realpath.start_with?("#{HOMEBREW_CELLAR}/", "#{HOMEBREW_LIBRARY}/Taps/") &&
!path_string.start_with?("#{HOMEBREW_CELLAR}/", "#{HOMEBREW_LIBRARY}/Taps/")
if path_string.include?("./") || path_string.end_with?(".rb") || path_string.count("/") != 2
raise <<~WARNING
Homebrew requires formulae to be in a tap, rejecting:
#{path_string} (#{path_realpath})
To create a tap, run e.g.
brew tap-new <user|org>/<repository>
To create a formula in a tap run e.g.
brew create <url> --tap=<user|org>/<repository>
WARNING
elsif path_string.count("/") == 2
# Looks like a tap, let's quietly return but not error.
return
end
end
end
if (tap = Tap.from_path(path))
# Only treat symlinks in taps as aliases.

View File

@ -9,8 +9,8 @@ class GitHubRunnerMatrix
# on homebrew/core and tag the first commit with a bottle e.g.
# `git tag 15-sequoia f42c4a659e4da887fc714f8f41cc26794a4bb320`
# to allow people to jump to specific commits based on their macOS version.
NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :tahoe
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :sonoma
NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :sequoia
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :ventura
NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER = :sonoma
RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) }

View File

@ -405,7 +405,8 @@ module Homebrew
return if formulae_names_to_install.empty?
if dry_run
ohai "Would install #{Utils.pluralize("formula", formulae_names_to_install.count, include_count: true)}:"
ohai "Would install #{Utils.pluralize("formula", formulae_names_to_install.count,
plural: "e", include_count: true)}:"
puts formulae_names_to_install.join(" ")
formula_installers.each do |fi|
@ -428,8 +429,8 @@ module Homebrew
def print_dry_run_dependencies(formula, dependencies)
return if dependencies.empty?
ohai "Would install #{Utils.pluralize("dependency", dependencies.count, include_count: true)} " \
"for #{formula.name}:"
ohai "Would install #{Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y",
include_count: true)} for #{formula.name}:"
formula_names = dependencies.map { |(dep, _options)| yield dep.to_formula }
puts formula_names.join(" ")
end
@ -445,7 +446,7 @@ module Homebrew
sizes = compute_total_sizes(formulae, debug: args.debug?)
puts "#{::Utils.pluralize("Formula", formulae.count)} \
puts "#{::Utils.pluralize("Formula", formulae.count, plural: "e")} \
(#{formulae.count}): #{formulae.join(", ")}\n\n"
puts "Download Size: #{disk_usage_readable(sizes.fetch(:download))}"
puts "Install Size: #{disk_usage_readable(sizes.fetch(:installed))}"

View File

@ -113,18 +113,6 @@ class Keg
old: "/usr/local/var/log/#{name}",
new: "#{PREFIX_PLACEHOLDER}/var/log/#{name}",
},
var_run_name: {
old: "/usr/local/var/run/#{name}",
new: "#{PREFIX_PLACEHOLDER}/var/run/#{name}",
},
var_db_name: {
old: "/usr/local/var/db/#{name}",
new: "#{PREFIX_PLACEHOLDER}/var/db/#{name}",
},
share_name: {
old: "/usr/local/share/#{name}",
new: "#{PREFIX_PLACEHOLDER}/share/#{name}",
},
}
end

View File

@ -198,8 +198,8 @@ module Homebrew
FORMULA_CHECKS = T.let([
:package_or_resource_skip,
:formula_head_only,
:formula_disabled,
:formula_deprecated,
:formula_disabled,
:formula_versioned,
].freeze, T::Array[Symbol])
private_constant :FORMULA_CHECKS
@ -207,8 +207,8 @@ module Homebrew
# Skip conditions for casks.
CASK_CHECKS = T.let([
:package_or_resource_skip,
:cask_disabled,
:cask_deprecated,
:cask_disabled,
:cask_extract_plist,
:cask_version_latest,
:cask_url_unversioned,

View File

@ -129,9 +129,9 @@ module Homebrew
print_stderr: false,
debug: false,
verbose: false,
).to_a
)
tags_data = { tags: T.let([], T::Array[String]) }
tags_data = { tags: [] }
tags_data[:messages] = stderr.split("\n") if stderr.present?
return tags_data if stdout.blank?

View File

@ -154,6 +154,12 @@ can take several different forms:
You can still access these formulae by using a special syntax, e.g.
`homebrew/dupes/vim` or `homebrew/versions/node4`.
* An arbitrary file:
Homebrew can install formulae from a local path. It can point to either a
formula file or a bottle.
Prefix relative paths with `./` to prevent them from being interpreted as a
formula or tap name.
## SPECIFYING CASKS
Many Homebrew Cask commands accept one or more <cask> arguments. These can be

View File

@ -23,7 +23,6 @@ DEPRECATED_OFFICIAL_TAPS = %w[
devel-only
dupes
emacs
formula-analytics
fuse
games
gui

View File

@ -50,7 +50,6 @@ module OS
LINUX_GLIBC_CI_VERSION = "2.35"
LINUX_GLIBC_NEXT_CI_VERSION = "2.39"
LINUX_GCC_CI_VERSION = "11.0"
LINUX_LIBSTDCXX_CI_VERSION = "6.0.30" # https://packages.ubuntu.com/jammy/libstdc++6
LINUX_PREFERRED_GCC_COMPILER_FORMULA = "gcc@11" # https://packages.ubuntu.com/jammy/gcc
LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc"

View File

@ -29,6 +29,8 @@ module OS
else
"#{description} (#{codename})"
end
elsif (redhat_release = Pathname.new("/etc/redhat-release")).readable?
redhat_release.read.chomp
elsif ::OS_VERSION.present?
::OS_VERSION
else

View File

@ -5,69 +5,37 @@ module OS
module Linux
# Helper functions for querying `ld` information.
module Ld
# This is a list of known paths to the host dynamic linker on Linux if
# the host glibc is new enough. Brew will fail to create a symlink for
# ld.so if the host linker cannot be found in this list.
DYNAMIC_LINKERS = %w[
/lib64/ld-linux-x86-64.so.2
/lib64/ld64.so.2
/lib/ld-linux.so.3
/lib/ld-linux.so.2
/lib/ld-linux-aarch64.so.1
/lib/ld-linux-armhf.so.3
/system/bin/linker64
/system/bin/linker
].freeze
sig { returns(String) }
def self.brewed_ld_so_diagnostics
@brewed_ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
# The path to the system's dynamic linker or `nil` if not found
sig { returns(T.nilable(Pathname)) }
def self.system_ld_so
@system_ld_so ||= T.let(nil, T.nilable(Pathname))
@system_ld_so ||= begin
linker = DYNAMIC_LINKERS.find { |s| File.executable? s }
Pathname(linker) if linker
end
end
brewed_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
return "" unless brewed_ld_so.exist?
sig { params(brewed: T::Boolean).returns(String) }
def self.ld_so_diagnostics(brewed: true)
@ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
ld_so_target = if brewed
ld_so = HOMEBREW_PREFIX/"lib/ld.so"
return "" unless ld_so.exist?
ld_so.readlink
else
ld_so = system_ld_so
return "" unless ld_so&.exist?
ld_so
end
@ld_so_diagnostics[ld_so_target] ||= begin
ld_so_output = Utils.popen_read(ld_so, "--list-diagnostics")
brewed_ld_so_target = brewed_ld_so.readlink
@brewed_ld_so_diagnostics[brewed_ld_so_target] ||= begin
ld_so_output = Utils.popen_read(brewed_ld_so, "--list-diagnostics")
ld_so_output if $CHILD_STATUS.success?
end
@ld_so_diagnostics[ld_so_target].to_s
@brewed_ld_so_diagnostics[brewed_ld_so_target].to_s
end
sig { params(brewed: T::Boolean).returns(String) }
def self.sysconfdir(brewed: true)
sig { returns(String) }
def self.sysconfdir
fallback_sysconfdir = "/etc"
match = ld_so_diagnostics(brewed:).match(/path.sysconfdir="(.+)"/)
match = brewed_ld_so_diagnostics.match(/path.sysconfdir="(.+)"/)
return fallback_sysconfdir unless match
match.captures.compact.first || fallback_sysconfdir
end
sig { params(brewed: T::Boolean).returns(T::Array[String]) }
def self.system_dirs(brewed: true)
sig { returns(T::Array[String]) }
def self.system_dirs
dirs = []
ld_so_diagnostics(brewed:).split("\n").each do |line|
brewed_ld_so_diagnostics.split("\n").each do |line|
match = line.match(/path.system_dirs\[0x.*\]="(.*)"/)
next unless match
@ -77,9 +45,9 @@ module OS
dirs
end
sig { params(conf_path: T.any(Pathname, String), brewed: T::Boolean).returns(T::Array[String]) }
def self.library_paths(conf_path = "ld.so.conf", brewed: true)
conf_file = Pathname(sysconfdir(brewed:))/conf_path
sig { params(conf_path: T.any(Pathname, String)).returns(T::Array[String]) }
def self.library_paths(conf_path = Pathname(sysconfdir)/"ld.so.conf")
conf_file = Pathname(conf_path)
return [] unless conf_file.exist?
return [] unless conf_file.file?
return [] unless conf_file.readable?
@ -100,7 +68,8 @@ module OS
line.sub!(/\s*#.*$/, "")
if line.start_with?(/\s*include\s+/)
wildcard = Pathname(line.sub(/^\s*include\s+/, "")).expand_path(directory)
include_path = Pathname(line.sub(/^\s*include\s+/, "")).expand_path
wildcard = include_path.absolute? ? include_path : directory/include_path
Dir.glob(wildcard.to_s).each do |include_file|
paths += library_paths(include_file)

View File

@ -1,47 +0,0 @@
# typed: strict
# frozen_string_literal: true
require "os/linux/ld"
module OS
module Linux
# Helper functions for querying `libstdc++` information.
module Libstdcxx
SOVERSION = 6
SONAME = T.let("libstdc++.so.#{SOVERSION}".freeze, String)
sig { returns(T::Boolean) }
def self.below_ci_version?
system_version < LINUX_LIBSTDCXX_CI_VERSION
end
sig { returns(Version) }
def self.system_version
@system_version ||= T.let(nil, T.nilable(Version))
@system_version ||= if (path = system_path)
Version.new("#{SOVERSION}#{path.realpath.basename.to_s.delete_prefix!(SONAME)}")
else
Version::NULL
end
end
sig { returns(T.nilable(Pathname)) }
def self.system_path
@system_path ||= T.let(nil, T.nilable(Pathname))
@system_path ||= find_library(OS::Linux::Ld.library_paths(brewed: false))
@system_path ||= find_library(OS::Linux::Ld.system_dirs(brewed: false))
end
sig { params(paths: T::Array[String]).returns(T.nilable(Pathname)) }
private_class_method def self.find_library(paths)
paths.each do |path|
next if path.start_with?(HOMEBREW_PREFIX)
candidate = Pathname(path)/SONAME
return candidate if candidate.exist? && candidate.elf?
end
nil
end
end
end
end

View File

@ -56,7 +56,7 @@ module OS
def self.latest_sdk_version
# TODO: bump version when new Xcode macOS SDK is released
# NOTE: We only track the major version of the SDK.
::Version.new("26")
::Version.new("15")
end
sig { returns(String) }

View File

@ -1,14 +1,7 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
class Keg
sig { params(path: Pathname).void }
def initialize(path)
super
@require_relocation = T.let(false, T::Boolean)
end
sig { params(id: String, file: Pathname).returns(T::Boolean) }
def change_dylib_id(id, file)
return false if file.dylib_id == id
@ -43,7 +36,6 @@ class Keg
raise
end
sig { params(old: String, new: String, file: Pathname).returns(T::Boolean) }
def change_rpath(old, new, file)
return false if old == new

View File

@ -1,4 +1,4 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "macho"
@ -12,28 +12,25 @@ module MachOShim
delegate [:dylib_id] => :macho
sig { params(args: T.untyped).void }
def initialize(*args)
@macho = T.let(nil, T.nilable(T.any(MachO::MachOFile, MachO::FatFile)))
@mach_data = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, Symbol]]))
@macho = T.let(nil, T.nilable(MachO::MachOFile))
@mach_data = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, T.untyped]]))
super
end
sig { returns(T.any(MachO::MachOFile, MachO::FatFile)) }
def macho
@macho ||= MachO.open(to_s)
end
private :macho
sig { returns(T::Array[T::Hash[Symbol, Symbol]]) }
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
def mach_data
@mach_data ||= begin
machos = []
mach_data = []
case (macho = self.macho)
when MachO::FatFile
if MachO::Utils.fat_magic?(macho.magic)
machos = macho.machos
else
machos << macho
@ -71,38 +68,34 @@ module MachOShim
# TODO: See if the `#write!` call can be delayed until
# we know we're not making any changes to the rpaths.
sig { params(rpath: String, strict: T::Boolean).void }
def delete_rpath(rpath, strict: true)
def delete_rpath(rpath, **options)
candidates = rpaths(resolve_variable_references: false).select do |r|
resolve_variable_name(r) == resolve_variable_name(rpath)
end
# Delete the last instance to avoid changing the order in which rpaths are searched.
rpath_to_delete = candidates.last
options[:last] = true
macho.delete_rpath(rpath_to_delete, { last: true, strict: })
macho.delete_rpath(rpath_to_delete, options)
macho.write!
end
sig { params(old: String, new: String, uniq: T::Boolean, last: T::Boolean, strict: T::Boolean).void }
def change_rpath(old, new, uniq: false, last: false, strict: true)
macho.change_rpath(old, new, { uniq:, last:, strict: })
def change_rpath(old, new, **options)
macho.change_rpath(old, new, options)
macho.write!
end
sig { params(id: String, strict: T::Boolean).void }
def change_dylib_id(id, strict: true)
macho.change_dylib_id(id, { strict: })
def change_dylib_id(id, **options)
macho.change_dylib_id(id, options)
macho.write!
end
sig { params(old: String, new: String, strict: T::Boolean).void }
def change_install_name(old, new, strict: true)
macho.change_install_name(old, new, { strict: })
def change_install_name(old, new, **options)
macho.change_install_name(old, new, options)
macho.write!
end
sig { params(except: Symbol, resolve_variable_references: T::Boolean).returns(T::Array[String]) }
def dynamically_linked_libraries(except: :none, resolve_variable_references: true)
lcs = macho.dylib_load_commands
lcs.reject! { |lc| lc.flag?(except) } if except != :none
@ -112,7 +105,6 @@ module MachOShim
names
end
sig { params(resolve_variable_references: T::Boolean).returns(T::Array[String]) }
def rpaths(resolve_variable_references: true)
names = macho.rpaths
# Don't recursively resolve rpaths to avoid infinite loops.
@ -121,12 +113,11 @@ module MachOShim
names
end
sig { params(name: String, resolve_rpaths: T::Boolean).returns(String) }
def resolve_variable_name(name, resolve_rpaths: true)
if name.start_with? "@loader_path"
Pathname(name.sub("@loader_path", dirname.to_s)).cleanpath.to_s
Pathname(name.sub("@loader_path", dirname)).cleanpath.to_s
elsif name.start_with?("@executable_path") && binary_executable?
Pathname(name.sub("@executable_path", dirname.to_s)).cleanpath.to_s
Pathname(name.sub("@executable_path", dirname)).cleanpath.to_s
elsif resolve_rpaths && name.start_with?("@rpath") && (target = resolve_rpath(name)).present?
target
else
@ -134,7 +125,6 @@ module MachOShim
end
end
sig { params(name: String).returns(T.nilable(String)) }
def resolve_rpath(name)
target = T.let(nil, T.nilable(String))
return unless rpaths(resolve_variable_references: true).find do |rpath|
@ -144,58 +134,48 @@ module MachOShim
target
end
sig { returns(T::Array[Symbol]) }
def archs
mach_data.map { |m| m.fetch :arch }
end
sig { returns(Symbol) }
def arch
case archs.length
when 0 then :dunno
when 1 then archs.fetch(0)
when 1 then archs.first
else :universal
end
end
sig { returns(T::Boolean) }
def universal?
arch == :universal
end
sig { returns(T::Boolean) }
def i386?
arch == :i386
end
sig { returns(T::Boolean) }
def x86_64?
arch == :x86_64
end
sig { returns(T::Boolean) }
def ppc7400?
arch == :ppc7400
end
sig { returns(T::Boolean) }
def ppc64?
arch == :ppc64
end
sig { returns(T::Boolean) }
def dylib?
mach_data.any? { |m| m.fetch(:type) == :dylib }
end
sig { returns(T::Boolean) }
def mach_o_executable?
mach_data.any? { |m| m.fetch(:type) == :executable }
end
alias binary_executable? mach_o_executable?
sig { returns(T::Boolean) }
def mach_o_bundle?
mach_data.any? { |m| m.fetch(:type) == :bundle }
end

View File

@ -6,7 +6,7 @@ includedir=${prefix}/include
Name: SQLite
Description: SQL database engine
Version: 3.51.0
Version: 3.48.0
Libs: -L${libdir} -lsqlite3
Libs.private:
Cflags:

View File

@ -1,4 +1,4 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "system_command"
@ -22,7 +22,7 @@ module OS
sig { params(version: MacOSVersion, path: T.any(String, Pathname), source: Symbol).void }
def initialize(version, path, source)
@version = version
@path = T.let(Pathname(path), Pathname)
@path = Pathname.new(path)
@source = source
end
end
@ -36,12 +36,6 @@ module OS
class NoSDKError < StandardError; end
sig { void }
def initialize
@all_sdks = T.let(nil, T.nilable(T::Array[SDK]))
@sdk_prefix = T.let(nil, T.nilable(String))
end
sig { params(version: MacOSVersion).returns(SDK) }
def sdk_for(version)
sdk = all_sdks.find { |s| s.version == version }

View File

@ -1,11 +1,11 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
module OS
module Mac
# Helper module for querying Xcode information.
module Xcode
DEFAULT_BUNDLE_PATH = T.let(Pathname("/Applications/Xcode.app").freeze, Pathname)
DEFAULT_BUNDLE_PATH = Pathname("/Applications/Xcode.app").freeze
BUNDLE_ID = "com.apple.dt.Xcode"
OLD_BUNDLE_ID = "com.apple.Xcode"
APPLE_DEVELOPER_DOWNLOAD_URL = "https://developer.apple.com/download/all/"
@ -17,7 +17,6 @@ module OS
def self.latest_version(macos: MacOS.version)
macos = macos.strip_patch
case macos
when "26" then "26.0"
when "15" then "16.4"
when "14" then "16.2"
when "13" then "15.2"
@ -44,7 +43,6 @@ module OS
def self.minimum_version
macos = MacOS.version
case macos
when "26" then "26.0"
when "15" then "16.0"
when "14" then "15.0"
when "13" then "14.1"
@ -100,7 +98,7 @@ module OS
# directory or nil if Xcode.app is not installed.
sig { returns(T.nilable(Pathname)) }
def self.prefix
@prefix ||= T.let(begin
@prefix ||= begin
dir = MacOS.active_developer_dir
if dir.empty? || dir == CLT::PKG_PATH || !File.directory?(dir)
@ -110,7 +108,7 @@ module OS
# Use cleanpath to avoid pathological trailing slash
Pathname.new(dir).cleanpath
end
end, T.nilable(Pathname))
end
end
sig { returns(Pathname) }
@ -136,7 +134,7 @@ module OS
sig { returns(XcodeSDKLocator) }
def self.sdk_locator
@sdk_locator ||= T.let(XcodeSDKLocator.new, T.nilable(OS::Mac::XcodeSDKLocator))
@sdk_locator ||= XcodeSDKLocator.new
end
sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) }
@ -185,7 +183,7 @@ module OS
# may return a version string
# that is guessed based on the compiler, so do not
# use it in order to check if Xcode is installed.
if @version ||= T.let(detect_version, T.nilable(String))
if @version ||= detect_version
::Version.new @version
else
::Version::NULL
@ -295,7 +293,7 @@ module OS
sig { returns(CLTSDKLocator) }
def self.sdk_locator
@sdk_locator ||= T.let(CLTSDKLocator.new, T.nilable(OS::Mac::CLTSDKLocator))
@sdk_locator ||= CLTSDKLocator.new
end
sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) }
@ -374,7 +372,7 @@ module OS
sig { returns(String) }
def self.latest_clang_version
case MacOS.version
when "26" then "1700.3.19.1"
when "26" then "1700.3.9.908"
when "15" then "1700.0.13.5"
when "14" then "1600.0.26.6"
when "13" then "1500.1.0.2.5"
@ -446,7 +444,7 @@ module OS
# @api internal
sig { returns(::Version) }
def self.version
if @version ||= T.let(detect_version, T.nilable(String))
if @version ||= detect_version
::Version.new @version
else
::Version::NULL

View File

@ -32,8 +32,8 @@ begin
end
Pathname.prepend WriteMkpathExtension
formula.run_post_install
formula.run_post_install
# Handle all possible exceptions.
rescue Exception => e # rubocop:disable Lint/RescueException
error_pipe&.puts e.to_json

View File

@ -125,7 +125,7 @@ module Readall
sig { params(filename: Pathname).returns(T::Boolean) }
private_class_method def self.syntax_errors_or_warnings?(filename)
# Retrieve messages about syntax errors/warnings printed to `$stderr`.
_, err, status = system_command(RUBY_PATH, args: ["-c", "-w", filename], print_stderr: false).to_a
_, err, status = system_command(RUBY_PATH, args: ["-c", "-w", filename], print_stderr: false)
# Ignore unnecessary warning about named capture conflicts.
# See https://bugs.ruby-lang.org/issues/12359.

View File

@ -85,6 +85,9 @@ module Homebrew
end
end
# TODO: Remove this exception for `lsr` after support for tangled.sh
# Git URLs is available in a brew release.
return if name == "lsr"
return if url_strategy != DownloadStrategyDetector.detect("", using)
problem "Redundant `using:` value in URL"

View File

@ -93,7 +93,7 @@ module Homebrew
wait = 2 ** @try
unless quiet
what = Utils.pluralize("try", tries_remaining)
what = Utils.pluralize("tr", tries_remaining, plural: "ies", singular: "y")
ohai "Retrying download in #{wait}s... (#{tries_remaining} #{what} left)"
end
sleep wait

View File

@ -34,7 +34,7 @@ module Homebrew
unofficial = Tap.all.sum { |tap| tap.official? ? 0 : tap.formula_files.size }
if unofficial.positive?
opoo "Use `--eval-all` to search #{unofficial} additional " \
"#{Utils.pluralize("formula", unofficial)} in third party taps."
"#{Utils.pluralize("formula", unofficial, plural: "e")} in third party taps."
end
descriptions = Homebrew::API::Formula.all_formulae.transform_values { |data| data["desc"] }
Descriptions.search(string_or_regex, search_type, descriptions, eval_all, cache_store_hash: true).print

View File

@ -34,7 +34,7 @@ module Homebrew
private_class_method def self._run(*args, mode:)
require "system_command"
result = SystemCommand.run(T.must(executable),
result = SystemCommand.run(executable,
args: [scope, *args.map(&:to_s)],
print_stdout: mode == :default,
print_stderr: mode == :default,

View File

@ -369,13 +369,9 @@ class Cmd
end
args += rpath_flags("#{wl}-rpath=", rpath_paths)
args += ["#{wl}--dynamic-linker=#{dynamic_linker_path}"] if dynamic_linker_path
# Use -rpath-link to make sure linker uses brew glibc rather than the system glibc for indirect
# Use -rpath-link to make sure linker uses versioned glibc rather than the system glibc for indirect
# dependencies because -L will only handle direct dependencies.
if versioned_glibc_dep
args << "#{wl}-rpath-link=#{@opt}/#{versioned_glibc_dep}/lib"
else
args << "#{wl}-rpath-link=#{@opt}/glibc/lib"
end
args << "#{wl}-rpath-link=#{@opt}/#{versioned_glibc_dep}/lib" if versioned_glibc_dep
args
end

View File

@ -17,21 +17,9 @@ class Homebrew::DevCmd::Contributions::Args < Homebrew::CLI::Args
sig { returns(T.nilable(String)) }
def from; end
sig { returns(T.nilable(String)) }
def org; end
sig { returns(T.nilable(String)) }
def organisation; end
sig { returns(T.nilable(String)) }
def organization; end
sig { returns(T.nilable(T::Array[String])) }
def repositories; end
sig { returns(T.nilable(String)) }
def team; end
sig { returns(T.nilable(String)) }
def to; end

View File

@ -988,38 +988,38 @@ RuboCop::Cop::RSpec::DescribeSymbol::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Arr
# end
# end
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#76
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#71
class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::ConfigurableEnforcedStyle
include ::RuboCop::Cop::RSpec::Namespace
extend ::RuboCop::Cop::AutoCorrector
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#85
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#80
def common_instance_exec_closure?(param0 = T.unsafe(nil)); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#108
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#97
def contains_described_class?(param0); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#103
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#92
def described_constant(param0 = T.unsafe(nil)); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#111
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#100
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#96
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#85
def rspec_block?(param0 = T.unsafe(nil)); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#100
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#89
def scope_changing_syntax?(param0 = T.unsafe(nil)); end
private
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#147
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#136
def allowed?(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#128
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#117
def autocorrect(corrector, match); end
# @example
@ -1033,7 +1033,7 @@ class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
# @param const [Array<Symbol>]
# @return [Array<Symbol>]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#213
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#202
def collapse_namespace(namespace, const); end
# @example
@ -1043,50 +1043,50 @@ class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
# @param node [RuboCop::AST::Node]
# @return [Array<Symbol>]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#230
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#219
def const_name(node); end
# @yield [node]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#138
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#127
def find_usage(node, &block); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#198
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#187
def full_const_name(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#151
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#140
def message(offense); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#176
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#165
def offensive?(node); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#184
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#173
def offensive_described_class?(node); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#172
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#161
def only_static_constants?; end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#160
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#149
def scope_change?(node); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#166
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#155
def skippable_block?(node); end
end
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#81
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#76
RuboCop::Cop::RSpec::DescribedClass::DESCRIBED_CLASS = T.let(T.unsafe(nil), String)
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#82
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#77
RuboCop::Cop::RSpec::DescribedClass::MSG = T.let(T.unsafe(nil), String)
# Avoid opening modules and defining specs within them.
@ -1135,7 +1135,6 @@ RuboCop::Cop::RSpec::DescribedClassModuleWrapping::MSG = T.let(T.unsafe(nil), St
# - let, let!
# - subject, subject!
# - expect, is_expected, expect_any_instance_of
# - raise_error, raise_exception
#
# By default all of the RSpec methods and aliases are allowed. By setting
# a config like:
@ -1170,19 +1169,19 @@ RuboCop::Cop::RSpec::DescribedClassModuleWrapping::MSG = T.let(T.unsafe(nil), St
# # ...
# end
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#59
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#58
class RuboCop::Cop::RSpec::Dialect < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::MethodPreference
extend ::RuboCop::Cop::AutoCorrector
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#68
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#67
def on_send(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#66
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#65
def rspec_method?(param0 = T.unsafe(nil)); end
end
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#63
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#62
RuboCop::Cop::RSpec::Dialect::MSG = T.let(T.unsafe(nil), String)
# Avoid duplicated metadata.
@ -1579,7 +1578,7 @@ class RuboCop::Cop::RSpec::EmptyLineAfterHook < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#60
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#70
# source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#60
def on_numblock(node); end
private
@ -2190,7 +2189,7 @@ class RuboCop::Cop::RSpec::ExpectInHook < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#30
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#40
# source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#30
def on_numblock(node); end
private
@ -2483,7 +2482,7 @@ class RuboCop::Cop::RSpec::HookArgument < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#78
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#91
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#78
def on_numblock(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#69
@ -2547,7 +2546,7 @@ class RuboCop::Cop::RSpec::HooksBeforeExamples < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#41
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#47
# source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#41
def on_numblock(node); end
private
@ -2839,18 +2838,18 @@ RuboCop::Cop::RSpec::ImplicitSubject::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Ar
# # good
# it_behaves_like 'examples'
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#73
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#22
class RuboCop::Cop::RSpec::IncludeExamples < ::RuboCop::Cop::RSpec::Base
extend ::RuboCop::Cop::AutoCorrector
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#80
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#29
def on_send(node); end
end
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#76
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#25
RuboCop::Cop::RSpec::IncludeExamples::MSG = T.let(T.unsafe(nil), String)
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#78
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#27
RuboCop::Cop::RSpec::IncludeExamples::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
# Do not set up test data using indexes (e.g., `item_1`, `item_2`).
@ -3212,18 +3211,16 @@ RuboCop::Cop::RSpec::ItBehavesLike::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Arra
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#19
class RuboCop::Cop::RSpec::IteratedExpectation < ::RuboCop::Cop::RSpec::Base
extend ::RuboCop::Cop::AutoCorrector
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#26
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#24
def each?(param0 = T.unsafe(nil)); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#35
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#33
def each_numblock?(param0 = T.unsafe(nil)); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#42
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#40
def expectation?(param0 = T.unsafe(nil), param1); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#46
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#44
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#52
@ -3231,29 +3228,18 @@ class RuboCop::Cop::RSpec::IteratedExpectation < ::RuboCop::Cop::RSpec::Base
private
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#60
def check_offense(node, argument); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#88
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#66
def only_expectations?(body, arg); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#84
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#62
def single_expectation?(body, arg); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#73
def single_expectation_replacement(node); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#80
def uses_argument_in_matcher?(node, argument); end
end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#22
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#20
RuboCop::Cop::RSpec::IteratedExpectation::MSG = T.let(T.unsafe(nil), String)
# Enforce that subject is the first definition in the test.
@ -3407,22 +3393,17 @@ class RuboCop::Cop::RSpec::LeakyConstantDeclaration < ::RuboCop::Cop::RSpec::Bas
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#101
def on_casgn(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#108
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#107
def on_class(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#115
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#113
def on_module(node); end
private
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#128
def explicit_namespace?(namespace); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#124
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#121
def inside_describe_block?(node); end
end
@ -3791,7 +3772,7 @@ module RuboCop::Cop::RSpec::Metadata
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#43
def on_metadata(_symbols, _hash); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#41
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#30
def on_numblock(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#21
@ -4065,7 +4046,7 @@ class RuboCop::Cop::RSpec::MultipleExpectations < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#86
def expect?(param0 = T.unsafe(nil)); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#75
# source://rubocop/1.75.6/lib/rubocop/cop/exclude_limit.rb#11
def max=(value); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#93
@ -4180,7 +4161,7 @@ RuboCop::Cop::RSpec::MultipleExpectations::TRUE_NODE = T.let(T.unsafe(nil), Proc
class RuboCop::Cop::RSpec::MultipleMemoizedHelpers < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::RSpec::Variable
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_memoized_helpers.rb#89
# source://rubocop/1.75.6/lib/rubocop/cop/exclude_limit.rb#11
def max=(value); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_memoized_helpers.rb#91
@ -4522,7 +4503,7 @@ end
class RuboCop::Cop::RSpec::NestedGroups < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::RSpec::TopLevelGroup
# source://rubocop-rspec//lib/rubocop/cop/rspec/nested_groups.rb#105
# source://rubocop/1.75.6/lib/rubocop/cop/exclude_limit.rb#11
def max=(value); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/nested_groups.rb#107
@ -4636,7 +4617,7 @@ class RuboCop::Cop::RSpec::NoExpectationExample < ::RuboCop::Cop::RSpec::Base
# @param node [RuboCop::AST::BlockNode]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/no_expectation_example.rb#98
# source://rubocop-rspec//lib/rubocop/cop/rspec/no_expectation_example.rb#89
def on_numblock(node); end
# @param node [RuboCop::AST::Node]
@ -5016,7 +4997,7 @@ RuboCop::Cop::RSpec::ReceiveCounts::MSG = T.let(T.unsafe(nil), String)
# source://rubocop-rspec//lib/rubocop/cop/rspec/receive_counts.rb#30
RuboCop::Cop::RSpec::ReceiveCounts::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
# Prefer `receive_messages` over multiple `receive`s on the same object.
# Checks for multiple messages stubbed on the same object.
#
# @example
# # bad
@ -5159,7 +5140,7 @@ class RuboCop::Cop::RSpec::RedundantAround < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#23
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#30
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#23
def on_numblock(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#32
@ -6145,7 +6126,7 @@ class RuboCop::Cop::RSpec::SkipBlockInsideExample < ::RuboCop::Cop::RSpec::Base
# source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#29
def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#36
# source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#29
def on_numblock(node); end
private
@ -7128,7 +7109,7 @@ class RuboCop::RSpec::Concept
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/rspec/concept.rb#18
# source://rubocop-rspec//lib/rubocop/rspec/concept.rb#14
def ==(other); end
# @return [Boolean]
@ -7375,137 +7356,129 @@ end
# This is used in Dialect and DescribeClass cops to detect RSpec blocks.
#
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#207
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#201
module RuboCop::RSpec::Language::ALL
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#208
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#202
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#75
module RuboCop::RSpec::Language::ErrorMatchers
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#76
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#81
module RuboCop::RSpec::Language::ExampleGroups
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#83
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#77
def all(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#93
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#87
def focused(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#89
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#83
def regular(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#91
def skipped(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#97
def skipped(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#103
module RuboCop::RSpec::Language::Examples
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#105
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#99
def all(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#116
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#110
def focused(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#124
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#118
def pending(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#112
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#106
def regular(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#120
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#114
def skipped(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#124
module RuboCop::RSpec::Language::Expectations
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#125
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#130
module RuboCop::RSpec::Language::Expectations
module RuboCop::RSpec::Language::Helpers
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#131
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#142
module RuboCop::RSpec::Language::HookScopes
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#144
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#143
RuboCop::RSpec::Language::HookScopes::ALL = T.let(T.unsafe(nil), Array)
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#136
module RuboCop::RSpec::Language::Helpers
module RuboCop::RSpec::Language::Hooks
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#137
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#148
module RuboCop::RSpec::Language::HookScopes
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#150
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#149
RuboCop::RSpec::Language::HookScopes::ALL = T.let(T.unsafe(nil), Array)
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#142
module RuboCop::RSpec::Language::Hooks
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#143
def all(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#155
module RuboCop::RSpec::Language::Includes
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#157
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#151
def all(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#166
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#160
def context(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#162
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#156
def examples(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#172
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#166
module RuboCop::RSpec::Language::Runners
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#175
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#169
def all(element = T.unsafe(nil)); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#173
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#167
RuboCop::RSpec::Language::Runners::ALL = T.let(T.unsafe(nil), Array)
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#183
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#177
module RuboCop::RSpec::Language::SharedGroups
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#185
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#179
def all(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#194
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#188
def context(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#190
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#184
def examples(element); end
end
end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#200
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#194
module RuboCop::RSpec::Language::Subjects
class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#201
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#195
def all(element); end
end
end

View File

@ -49,7 +49,7 @@ module Homebrew
ruby_files = T.let([], T::Array[Pathname])
shell_files = T.let([], T::Array[Pathname])
actionlint_files = T.let([], T::Array[Pathname])
Array(files).map { Pathname(_1) }
Array(files).map(&method(:Pathname))
.each do |path|
case path.extname
when ".rb"

View File

@ -1,4 +1,4 @@
# typed: strict
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "plist"
@ -21,121 +21,33 @@ class SystemCommand
# Run a fallible system command.
#
# @api internal
sig {
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
sudo: T::Boolean,
sudo_as_root: T::Boolean,
env: T::Hash[String, String],
input: T.any(String, T::Array[String]),
must_succeed: T::Boolean,
print_stdout: T.any(T::Boolean, Symbol),
print_stderr: T.any(T::Boolean, Symbol),
debug: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
secrets: T.any(String, T::Array[String]),
chdir: T.any(String, Pathname),
reset_uid: T::Boolean,
timeout: T.nilable(T.any(Integer, Float)),
).returns(SystemCommand::Result)
}
def system_command(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [],
must_succeed: false, print_stdout: false, print_stderr: true, debug: nil, verbose: nil,
secrets: [], chdir: T.unsafe(nil), reset_uid: false, timeout: nil)
SystemCommand.run(executable, args:, sudo:, sudo_as_root:, env:, input:, must_succeed:, print_stdout:,
print_stderr:, debug:, verbose:, secrets:, chdir:, reset_uid:, timeout:)
def system_command(executable, **options)
SystemCommand.run(executable, **options)
end
# Run an infallible system command.
#
# @api internal
sig {
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
sudo: T::Boolean,
sudo_as_root: T::Boolean,
env: T::Hash[String, String],
input: T.any(String, T::Array[String]),
print_stdout: T.any(T::Boolean, Symbol),
print_stderr: T.any(T::Boolean, Symbol),
debug: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
secrets: T.any(String, T::Array[String]),
chdir: T.any(String, Pathname),
reset_uid: T::Boolean,
timeout: T.nilable(T.any(Integer, Float)),
).returns(SystemCommand::Result)
}
def system_command!(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [],
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [],
chdir: T.unsafe(nil), reset_uid: false, timeout: nil)
SystemCommand.run!(executable, args:, sudo:, sudo_as_root:, env:, input:, print_stdout:,
print_stderr:, debug:, verbose:, secrets:, chdir:, reset_uid:, timeout:)
def system_command!(command, **options)
SystemCommand.run!(command, **options)
end
end
include Context
sig {
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
sudo: T::Boolean,
sudo_as_root: T::Boolean,
env: T::Hash[String, String],
input: T.any(String, T::Array[String]),
must_succeed: T::Boolean,
print_stdout: T.any(T::Boolean, Symbol),
print_stderr: T.any(T::Boolean, Symbol),
debug: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
secrets: T.any(String, T::Array[String]),
chdir: T.any(NilClass, String, Pathname),
reset_uid: T::Boolean,
timeout: T.nilable(T.any(Integer, Float)),
).returns(SystemCommand::Result)
}
def self.run(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [], must_succeed: false,
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [], chdir: nil,
reset_uid: false, timeout: nil)
new(executable, args:, sudo:, sudo_as_root:, env:, input:, must_succeed:, print_stdout:, print_stderr:, debug:,
verbose:, secrets:, chdir:, reset_uid:, timeout:).run!
def self.run(executable, **options)
new(executable, **options).run!
end
sig {
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
sudo: T::Boolean,
sudo_as_root: T::Boolean,
env: T::Hash[String, String],
input: T.any(String, T::Array[String]),
must_succeed: T::Boolean,
print_stdout: T.any(T::Boolean, Symbol),
print_stderr: T.any(T::Boolean, Symbol),
debug: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
secrets: T.any(String, T::Array[String]),
chdir: T.any(NilClass, String, Pathname),
reset_uid: T::Boolean,
timeout: T.nilable(T.any(Integer, Float)),
).returns(SystemCommand::Result)
}
def self.run!(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [], must_succeed: true,
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [], chdir: nil,
reset_uid: false, timeout: nil)
run(executable, args:, sudo:, sudo_as_root:, env:, input:, must_succeed:, print_stdout:, print_stderr:,
debug:, verbose:, secrets:, chdir:, reset_uid:, timeout:)
def self.run!(command, **options)
run(command, **options, must_succeed: true)
end
sig { returns(SystemCommand::Result) }
def run!
$stderr.puts redact_secrets(command.shelljoin.gsub('\=', "="), @secrets) if verbose? && debug?
@output = T.let([], T.nilable(T::Array[[Symbol, String]]))
@output = T.must(@output)
@output = []
each_output_line do |type, line|
case type
@ -158,7 +70,7 @@ class SystemCommand
end
end
result = Result.new(command, @output, T.must(@status), secrets: @secrets)
result = Result.new(command, @output, @status, secrets: @secrets)
result.assert_success! if must_succeed?
result
end
@ -166,7 +78,7 @@ class SystemCommand
sig {
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
args: T::Array[T.any(String, Integer, Float, URI::Generic)],
sudo: T::Boolean,
sudo_as_root: T::Boolean,
env: T::Hash[String, String],
@ -177,14 +89,28 @@ class SystemCommand
debug: T.nilable(T::Boolean),
verbose: T.nilable(T::Boolean),
secrets: T.any(String, T::Array[String]),
chdir: T.any(NilClass, String, Pathname),
chdir: T.any(String, Pathname),
reset_uid: T::Boolean,
timeout: T.nilable(T.any(Integer, Float)),
).void
}
def initialize(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [], must_succeed: false,
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [], chdir: nil,
reset_uid: false, timeout: nil)
def initialize(
executable,
args: [],
sudo: false,
sudo_as_root: false,
env: {},
input: [],
must_succeed: false,
print_stdout: false,
print_stderr: true,
debug: nil,
verbose: T.unsafe(nil),
secrets: [],
chdir: T.unsafe(nil),
reset_uid: false,
timeout: nil
)
require "extend/ENV"
@executable = executable
@args = args
@ -206,13 +132,13 @@ class SystemCommand
raise ArgumentError, "Invalid variable name: #{name}"
end
@env = env
@input = T.let(Array(input), T::Array[String])
@input = Array(input)
@must_succeed = must_succeed
@print_stdout = print_stdout
@print_stderr = print_stderr
@debug = debug
@verbose = verbose
@secrets = T.let((Array(secrets) + ENV.sensitive_environment.values).uniq, T::Array[String])
@secrets = (Array(secrets) + ENV.sensitive_environment.values).uniq
@chdir = chdir
@reset_uid = reset_uid
@timeout = timeout
@ -225,20 +151,7 @@ class SystemCommand
private
sig { returns(T.any(Pathname, String)) }
attr_reader :executable
sig { returns(T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)]) }
attr_reader :args
sig { returns(T::Array[String]) }
attr_reader :input
sig { returns(T.any(NilClass, String, Pathname)) }
attr_reader :chdir
sig { returns(T::Hash[String, String]) }
attr_reader :env
attr_reader :executable, :args, :input, :chdir, :env
sig { returns(T::Boolean) }
def must_succeed? = @must_succeed
@ -314,13 +227,15 @@ class SystemCommand
sig { returns(T::Array[String]) }
def expanded_args
@expanded_args ||= T.let(args.map do |arg|
if arg.is_a?(Pathname)
@expanded_args ||= args.map do |arg|
if arg.respond_to?(:to_path)
File.absolute_path(arg)
else
elsif arg.is_a?(Integer) || arg.is_a?(Float) || arg.is_a?(URI::Generic)
arg.to_s
else
arg.to_str
end
end
end, T.nilable(T::Array[String]))
end
class ProcessTerminatedInterrupt < StandardError; end
@ -360,7 +275,7 @@ class SystemCommand
end_time = Time.now + @timeout if @timeout
raise Timeout::Error if raw_wait_thr.join(Utils::Timer.remaining(end_time)).nil?
@status = T.let(raw_wait_thr.value, T.nilable(Process::Status))
@status = raw_wait_thr.value
rescue Interrupt
Process.kill("INT", raw_wait_thr.pid) if raw_wait_thr && !sudo?
raise Interrupt
@ -468,14 +383,7 @@ class SystemCommand
include Context
include Utils::Output::Mixin
sig { returns(T::Array[String]) }
attr_accessor :command
sig { returns(Process::Status) }
attr_accessor :status
sig { returns(T.nilable(Integer)) }
attr_accessor :exit_status
attr_accessor :command, :status, :exit_status
sig {
params(
@ -489,7 +397,7 @@ class SystemCommand
@command = command
@output = output
@status = status
@exit_status = T.let(status.exitstatus, T.nilable(Integer))
@exit_status = status.exitstatus
@secrets = secrets
end
@ -502,21 +410,22 @@ class SystemCommand
sig { returns(String) }
def stdout
@stdout ||= T.let(@output.select { |type,| type == :stdout }
@stdout ||= @output.select { |type,| type == :stdout }
.map { |_, line| line }
.join, T.nilable(String))
.join
end
sig { returns(String) }
def stderr
@stderr ||= T.let(@output.select { |type,| type == :stderr }
@stderr ||= @output.select { |type,| type == :stderr }
.map { |_, line| line }
.join, T.nilable(String))
.join
end
sig { returns(String) }
def merged_output
@merged_output ||= T.let(@output.map { |_, line| line }.join, T.nilable(String))
@merged_output ||= @output.map { |_, line| line }
.join
end
sig { returns(T::Boolean) }
@ -530,11 +439,10 @@ class SystemCommand
def to_ary
[stdout, stderr, status]
end
alias to_a to_ary
sig { returns(T.untyped) }
sig { returns(T.nilable(T.any(Array, Hash))) }
def plist
@plist ||= T.let(begin
@plist ||= begin
output = stdout
output = output.sub(/\A(.*?)(\s*<\?\s*xml)/m) do
@ -548,7 +456,7 @@ class SystemCommand
end
Plist.parse_xml(output, marshal: false)
end, T.untyped)
end
end
sig { params(garbage: String).void }

View File

@ -102,7 +102,7 @@ module SystemConfig
sig { returns(String) }
def describe_curl
out = system_command(Utils::Curl.curl_executable, args: ["--version"], verbose: false).stdout
out, = system_command(Utils::Curl.curl_executable, args: ["--version"], verbose: false)
match_data = /^curl (?<curl_version>[\d.]+)/.match(out)
if match_data

View File

@ -169,12 +169,6 @@ class Tap
sig { returns(String) }
attr_reader :repository
# The repository name of this {Tap} including the leading `homebrew-`.
#
# @api public
sig { returns(String) }
attr_reader :full_repository
# The name of this {Tap}. It combines {#user} and {#repository} with a slash.
# {#name} is always in lowercase.
# e.g. `user/repository`
@ -216,8 +210,7 @@ class Tap
@user = user
@repository = repository
@name = T.let("#{@user}/#{@repository}".downcase, String)
@full_repository = T.let("homebrew-#{@repository}", String)
@full_name = T.let("#{@user}/#{@full_repository}", String)
@full_name = T.let("#{@user}/homebrew-#{@repository}", String)
@path = T.let(HOMEBREW_TAP_DIRECTORY/@full_name.downcase, Pathname)
@git_repository = T.let(GitRepository.new(@path), GitRepository)
end
@ -738,7 +731,7 @@ class Tap
end
if (formula_count = formula_files.count).positive?
contents << Utils.pluralize("formula", formula_count, include_count: true)
contents << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
end
contents

View File

@ -45,9 +45,10 @@ begin
formula.extend(Debrew::Formula)
end
Pathname.prepend WriteMkpathExtension
ENV.extend(Stdenv)
ENV.setup_build_environment(formula:, testing_formula: true)
Pathname.prepend WriteMkpathExtension
# tests can also return false to indicate failure
run_test = proc { |_ = nil| raise "test returned false" if formula.run_test(keep_tmp: args.keep_tmp?) == false }

View File

@ -1,52 +0,0 @@
# frozen_string_literal: true
require "cask/artifact/relocated"
RSpec.describe Cask::Artifact::Relocated, :cask do
let(:cask) do
Cask::Cask.new("test-cask") do
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
homepage "https://brew.sh/"
version "1.0"
sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94"
end
end
let(:command) { NeverSudoSystemCommand }
let(:artifact) { described_class.new(cask, "test_file.txt") }
describe "#add_altname_metadata" do
let(:file) { Pathname("/tmp/test_file.txt") }
let(:altname) { Pathname("alternate_name.txt") }
before do
allow(file).to receive_messages(basename: Pathname("test_file.txt"), writable?: true, realpath: file)
end
context "when running on Linux", :needs_linux do
it "is a no-op and does not call xattr commands" do
expect(command).not_to receive(:run)
expect(command).not_to receive(:run!)
artifact.send(:add_altname_metadata, file, altname, command: command)
end
end
context "when running on macOS", :needs_macos do
before do
stdout_double = instance_double(SystemCommand::Result, stdout: "")
allow(command).to receive(:run).and_return(stdout_double)
allow(command).to receive(:run!)
end
it "calls xattr commands to set metadata" do
expect(command).to receive(:run).with("/usr/bin/xattr",
args: ["-p", "com.apple.metadata:kMDItemAlternateNames", file],
print_stderr: false)
expect(command).to receive(:run!).twice
artifact.send(:add_altname_metadata, file, altname, command: command)
end
end
end
end

View File

@ -236,73 +236,4 @@ RSpec.describe Cask::CaskLoader, :cask do
expect(described_class.load_prefer_installed("test-cask").tap).to eq(foo_tap)
end
end
describe "FromPathLoader with symlinked taps" do
let(:cask_token) { "testcask" }
let(:tmpdir) { mktmpdir }
let(:real_tap_path) { tmpdir / "real_tap" }
let(:homebrew_prefix) { tmpdir / "homebrew" }
let(:taps_dir) { homebrew_prefix / "Library" / "Taps" / "testuser" }
let(:symlinked_tap_path) { taps_dir / "homebrew-testtap" }
let(:cask_file_path) { symlinked_tap_path / "Casks" / "#{cask_token}.rb" }
let(:cask_content) do
<<~RUBY
cask "#{cask_token}" do
version "1.0.0"
sha256 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
url "https://example.com/#{cask_token}-\#{version}.dmg"
name "Test Cask"
desc "A test cask for symlink testing"
homepage "https://example.com"
app "TestCask.app"
end
RUBY
end
after do
tmpdir.rmtree if tmpdir.exist?
end
before do
# Create real tap directory structure
(real_tap_path / "Casks").mkpath
(real_tap_path / "Casks" / "#{cask_token}.rb").write(cask_content)
# Create homebrew prefix structure
taps_dir.mkpath
# Create symlink to the tap (this simulates what setup-homebrew does)
symlinked_tap_path.make_symlink(real_tap_path)
# Set HOMEBREW_LIBRARY to our test prefix for the security check
stub_const("HOMEBREW_LIBRARY", homebrew_prefix / "Library")
allow(Homebrew::EnvConfig).to receive(:forbid_packages_from_paths?).and_return(true)
end
context "when HOMEBREW_FORBID_PACKAGES_FROM_PATHS is enabled" do
it "allows loading casks from symlinked taps" do
loader = Cask::CaskLoader::FromPathLoader.try_new(cask_file_path)
expect(loader).not_to be_nil
expect(loader).to be_a(Cask::CaskLoader::FromPathLoader)
cask = loader.load(config: nil)
expect(cask.token).to eq(cask_token)
expect(cask.version).to eq(Version.new("1.0.0"))
end
end
context "when HOMEBREW_FORBID_PACKAGES_FROM_PATHS is disabled" do
before do
allow(Homebrew::EnvConfig).to receive(:forbid_packages_from_paths?).and_return(false)
end
it "allows loading casks from symlinked taps" do
loader = Cask::CaskLoader::FromPathLoader.try_new(cask_file_path)
expect(loader).not_to be_nil
expect(loader).to be_a(Cask::CaskLoader::FromPathLoader)
end
end
end
end

View File

@ -364,6 +364,26 @@ RSpec.describe Cask::Cask, :cask do
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
},
"mojave": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
},
"high_sierra": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
},
"sierra": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
},
"el_capitan": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
"version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216"
}
}
JSON
@ -398,6 +418,22 @@ RSpec.describe Cask::Cask, :cask do
"catalina": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"mojave": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"high_sierra": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"sierra": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"el_capitan": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
}
}
JSON
@ -433,6 +469,22 @@ RSpec.describe Cask::Cask, :cask do
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"mojave": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"high_sierra": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"sierra": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"el_capitan": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"
},
"x86_64_linux": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-linux.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"

View File

@ -175,7 +175,7 @@ RSpec.describe Homebrew::CLI::Parser do
flag "--flag2=", depends_on: "--flag1="
flag "--flag3="
conflicts "--flag1", "--flag3"
conflicts "--flag1=", "--flag3="
end
end
@ -204,8 +204,7 @@ RSpec.describe Homebrew::CLI::Parser do
described_class.new(Cmd) do
flag "--flag1="
flag "--flag2=", depends_on: "--flag1="
conflicts "--flag1", "--flag2"
conflicts "--flag1=", "--flag2="
end
end

View File

@ -4,61 +4,6 @@ require "os/linux/ld"
require "tmpdir"
RSpec.describe OS::Linux::Ld do
let(:diagnostics) do
<<~EOS
path.prefix="/usr"
path.sysconfdir="/usr/local/etc"
path.system_dirs[0x0]="/lib64"
path.system_dirs[0x1]="/var/lib"
EOS
end
describe "::system_ld_so" do
let(:ld_so) { "/lib/ld-linux.so.3" }
before do
allow(File).to receive(:executable?).and_return(false)
described_class.instance_variable_set(:@system_ld_so, nil)
end
it "returns the path to a known dynamic linker" do
allow(File).to receive(:executable?).with(ld_so).and_return(true)
expect(described_class.system_ld_so).to eq(Pathname(ld_so))
end
it "returns nil when there is no known dynamic linker" do
expect(described_class.system_ld_so).to be_nil
end
end
describe "::sysconfdir" do
it "returns path.sysconfdir" do
allow(described_class).to receive(:ld_so_diagnostics).and_return(diagnostics)
expect(described_class.sysconfdir).to eq("/usr/local/etc")
expect(described_class.sysconfdir(brewed: false)).to eq("/usr/local/etc")
end
it "returns fallback on blank diagnostics" do
allow(described_class).to receive(:ld_so_diagnostics).and_return("")
expect(described_class.sysconfdir).to eq("/etc")
expect(described_class.sysconfdir(brewed: false)).to eq("/etc")
end
end
describe "::system_dirs" do
it "returns all path.system_dirs" do
allow(described_class).to receive(:ld_so_diagnostics).and_return(diagnostics)
expect(described_class.system_dirs).to eq(["/lib64", "/var/lib"])
expect(described_class.system_dirs(brewed: false)).to eq(["/lib64", "/var/lib"])
end
it "returns an empty array on blank diagnostics" do
allow(described_class).to receive(:ld_so_diagnostics).and_return("")
expect(described_class.system_dirs).to eq([])
expect(described_class.system_dirs(brewed: false)).to eq([])
end
end
describe "::library_paths" do
ld_etc = Pathname("")
before do

View File

@ -1,58 +0,0 @@
# frozen_string_literal: true
require "os/linux/libstdcxx"
RSpec.describe OS::Linux::Libstdcxx do
describe "::below_ci_version?" do
it "returns false when system version matches CI version" do
allow(described_class).to receive(:system_version).and_return(Version.new(OS::LINUX_LIBSTDCXX_CI_VERSION))
expect(described_class.below_ci_version?).to be false
end
it "returns true when system version cannot be detected" do
allow(described_class).to receive(:system_version).and_return(Version::NULL)
expect(described_class.below_ci_version?).to be true
end
end
describe "::system_version" do
let(:tmpdir) { mktmpdir }
let(:libstdcxx) { tmpdir/described_class::SONAME }
let(:soversion) { Version.new(described_class::SOVERSION.to_s) }
before do
tmpdir.mkpath
described_class.instance_variable_set(:@system_version, nil)
allow(described_class).to receive(:system_path).and_return(libstdcxx)
end
after do
FileUtils.rm_rf(tmpdir)
end
it "returns NULL when unable to find system path" do
allow(described_class).to receive(:system_path).and_return(nil)
expect(described_class.system_version).to be Version::NULL
end
it "returns full version from filename" do
full_version = Version.new("#{soversion}.0.999")
libstdcxx_real = libstdcxx.sub_ext(".#{full_version}")
FileUtils.touch libstdcxx_real
FileUtils.ln_s libstdcxx_real, libstdcxx
expect(described_class.system_version).to eq full_version
end
it "returns major version when non-standard libstdc++ filename without full version" do
FileUtils.touch libstdcxx
expect(described_class.system_version).to eq soversion
end
it "returns major version when non-standard libstdc++ filename with unexpected realpath" do
libstdcxx_real = tmpdir/"libstdc++.so.real"
FileUtils.touch libstdcxx_real
FileUtils.ln_s libstdcxx_real, libstdcxx
expect(described_class.system_version).to eq soversion
end
end
end

View File

@ -9,7 +9,7 @@ cask "with-depends-on-everything" do
depends_on cask: "local-caffeine"
depends_on cask: "with-depends-on-cask"
depends_on formula: "unar"
depends_on macos: ">= :catalina"
depends_on macos: ">= :el_capitan"
app "Caffeine.app"
end

View File

@ -7,7 +7,7 @@ cask "with-depends-on-macos-comparison" do
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
homepage "https://brew.sh/with-depends-on-macos-comparison"
depends_on macos: ">= :catalina"
depends_on macos: ">= :el_capitan"
app "Caffeine.app"
end

View File

@ -82,7 +82,7 @@
],
"macos": {
">=": [
"10.15"
"10.11"
]
}
},

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