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 path: results.sarif
- name: Upload SARIF file - 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: with:
sarif_file: results.sarif sarif_file: results.sarif
category: zizmor category: zizmor

View File

@ -27,7 +27,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with: with:
languages: ruby languages: ruby
config: | config: |
@ -35,4 +35,4 @@ jobs:
- Library/Homebrew/vendor - Library/Homebrew/vendor
- name: Perform CodeQL Analysis - 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/ run: vale docs/
- name: Install Ruby - name: Install Ruby
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0 uses: ruby/setup-ruby@efbf473cab83af4468e8606cc33eca9281bb213f # v1.256.0
with: with:
bundler-cache: true bundler-cache: true
working-directory: docs working-directory: docs

View File

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

View File

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

View File

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

View File

@ -104,7 +104,7 @@ jobs:
- name: Cache style cache - name: Cache style cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with: with:
path: ~/Library/Caches/Homebrew/style path: ~/.cache/Homebrew/style
key: tap-syntax-style-cache-${{ github.sha }} key: tap-syntax-style-cache-${{ github.sha }}
restore-keys: tap-syntax-style-cache- restore-keys: tap-syntax-style-cache-
@ -331,7 +331,7 @@ jobs:
disable_search: true disable_search: true
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
- uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 - uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
with: with:
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
files: Library/Homebrew/test/coverage/coverage.xml files: Library/Homebrew/test/coverage/coverage.xml
@ -495,7 +495,7 @@ jobs:
uses: Homebrew/actions/setup-homebrew@main uses: Homebrew/actions/setup-homebrew@main
- name: Setup Python - name: Setup Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with: with:
python-version-file: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew/formula-analytics/.python-version 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) ast (2.4.3)
base64 (0.3.0) base64 (0.3.0)
benchmark (0.4.1) benchmark (0.4.1)
bigdecimal (3.2.3) bigdecimal (3.2.2)
bindata (2.5.1) bindata (2.5.1)
coderay (1.1.3) coderay (1.1.3)
concurrent-ruby (1.3.5) concurrent-ruby (1.3.5)
@ -57,7 +57,7 @@ GEM
redcarpet (3.6.1) redcarpet (3.6.1)
regexp_parser (2.11.2) regexp_parser (2.11.2)
require-hooks (0.2.2) require-hooks (0.2.2)
rexml (3.4.4) rexml (3.4.2)
rspec (3.13.1) rspec (3.13.1)
rspec-core (~> 3.13.0) rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0) rspec-expectations (~> 3.13.0)
@ -79,7 +79,7 @@ GEM
rspec-support (3.13.5) rspec-support (3.13.5)
rspec_junit_formatter (0.6.0) rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0) rspec-core (>= 2, < 4, != 2.12.0)
rubocop (1.80.2) rubocop (1.80.1)
json (~> 2.3) json (~> 2.3)
language_server-protocol (~> 3.17.0.2) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0) lint_roller (~> 1.1.0)
@ -96,11 +96,11 @@ GEM
rubocop-md (2.0.2) rubocop-md (2.0.2)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (>= 1.72.1) rubocop (>= 1.72.1)
rubocop-performance (1.26.0) rubocop-performance (1.25.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (>= 1.75.0, < 2.0) rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rspec (3.7.0) rubocop-rspec (3.6.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1) rubocop (~> 1.72, >= 1.72.1)
rubocop-sorbet (0.10.5) rubocop-sorbet (0.10.5)
@ -124,15 +124,15 @@ GEM
simplecov-html (0.13.2) simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
simpleidn (0.2.3) simpleidn (0.2.3)
sorbet (0.6.12521) sorbet (0.6.12466)
sorbet-static (= 0.6.12521) sorbet-static (= 0.6.12466)
sorbet-runtime (0.6.12521) sorbet-runtime (0.6.12466)
sorbet-static (0.6.12521-aarch64-linux) sorbet-static (0.6.12466-aarch64-linux)
sorbet-static (0.6.12521-universal-darwin) sorbet-static (0.6.12466-universal-darwin)
sorbet-static (0.6.12521-x86_64-linux) sorbet-static (0.6.12466-x86_64-linux)
sorbet-static-and-runtime (0.6.12521) sorbet-static-and-runtime (0.6.12466)
sorbet (= 0.6.12521) sorbet (= 0.6.12466)
sorbet-runtime (= 0.6.12521) sorbet-runtime (= 0.6.12466)
spoom (1.7.6) spoom (1.7.6)
erubi (>= 1.10.0) erubi (>= 1.10.0)
prism (>= 0.28.0) prism (>= 0.28.0)
@ -154,9 +154,9 @@ GEM
thor (>= 1.2.0) thor (>= 1.2.0)
yard-sorbet yard-sorbet
thor (1.4.0) thor (1.4.0)
unicode-display_width (3.2.0) unicode-display_width (3.1.5)
unicode-emoji (~> 4.1) unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.1.0) unicode-emoji (4.0.4)
vernier (1.8.0) vernier (1.8.0)
warning (1.5.0) warning (1.5.0)
yard (0.9.37) 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 = T.let((HOMEBREW_CACHE/"api").freeze, Pathname)
HOMEBREW_CACHE_API_SOURCE = T.let((HOMEBREW_CACHE/"api-source").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]) } sig { params(endpoint: String).returns(T::Hash[String, T.untyped]) }
def self.fetch(endpoint) def self.fetch(endpoint)
@ -37,25 +37,12 @@ module Homebrew
raise ArgumentError, "Invalid JSON file: #{Tty.underline}#{api_url}#{Tty.reset}" raise ArgumentError, "Invalid JSON file: #{Tty.underline}#{api_url}#{Tty.reset}"
end 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 { sig {
params( params(endpoint: String, target: Pathname, stale_seconds: Integer, download_queue: T.nilable(DownloadQueue))
endpoint: String, .returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
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])
} }
def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint, 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. # Lazy-load dependency.
require "development_tools" require "development_tools"
@ -76,7 +63,12 @@ module Homebrew
insecure_download = DevelopmentTools.ca_file_substitution_required? || insecure_download = DevelopmentTools.ca_file_substitution_required? ||
DevelopmentTools.curl_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 if download_queue
unless skip_download unless skip_download
@ -169,28 +161,17 @@ module Homebrew
require "download_queue" require "download_queue"
Homebrew::DownloadQueue.new Homebrew::DownloadQueue.new
end end
stale_seconds = 86400 # 1 day
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
if Homebrew::EnvConfig.use_internal_api? if Homebrew::EnvConfig.use_internal_api?
Homebrew::API::Internal.fetch_formula_api!(download_queue:, stale_seconds:) Homebrew::API::Internal.fetch_formula_api!(download_queue:, stale_seconds:)
Homebrew::API::Internal.fetch_cask_api!(download_queue:, stale_seconds:) Homebrew::API::Internal.fetch_cask_api!(download_queue:, stale_seconds:)
else else
Homebrew::API::Formula.fetch_api!(download_queue:, stale_seconds:) 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_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 end
ENV["HOMEBREW_API_UPDATED"] = "1"
return unless download_queue return unless download_queue
begin begin

View File

@ -75,18 +75,18 @@ module Homebrew
end end
sig { 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]) .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: Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:
end end
sig { 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]) .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: Homebrew::API.fetch_json_api_file "cask_tap_migrations.jws.json", stale_seconds:, download_queue:
end end

View File

@ -74,18 +74,18 @@ module Homebrew
end end
sig { 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]) .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: Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:
end end
sig { 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]) .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: Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json", stale_seconds:, download_queue:
end end

View File

@ -56,20 +56,20 @@ module Homebrew
end end
sig { 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]) .returns([T::Hash[String, T.untyped], T::Boolean])
} }
def self.fetch_formula_api!(download_queue: nil, stale_seconds: nil) 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:) 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] [T.cast(json_contents, T::Hash[String, T.untyped]), updated]
end end
sig { 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]) .returns([T::Hash[String, T.untyped], T::Boolean])
} }
def self.fetch_cask_api!(download_queue: nil, stale_seconds: nil) 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:) 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] [T.cast(json_contents, T::Hash[String, T.untyped]), updated]
end end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -172,7 +172,7 @@ module Cask
.stdout.lines.drop(1) # skip stdout column headers .stdout.lines.drop(1) # skip stdout column headers
.filter_map do |line| .filter_map do |line|
pid, _state, id = line.chomp.split(/\s+/) 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
end end
@ -460,9 +460,9 @@ module Cask
def trash_paths(*paths, command: nil, **_) def trash_paths(*paths, command: nil, **_)
return if paths.empty? 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, args: paths,
print_stderr: Homebrew::EnvConfig.developer?).stdout print_stderr: Homebrew::EnvConfig.developer?
trashed, _, untrashable = stdout.partition("\n") trashed, _, untrashable = stdout.partition("\n")
trashed = trashed.split(":") trashed = trashed.split(":")

View File

@ -79,9 +79,8 @@ module Cask
# Try to make the asset searchable under the target name. Spotlight # Try to make the asset searchable under the target name. Spotlight
# respects this attribute for many filetypes, but ignores it for App # respects this attribute for many filetypes, but ignores it for App
# bundles. Alfred 2.2 respects it even for App bundles. # 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: nil)
def add_altname_metadata(file, altname, command:) return if altname.to_s.casecmp(file.basename.to_s).zero?
return if altname.to_s.casecmp(file.basename.to_s)&.zero?
odebug "Adding #{ALT_NAME_ATTRIBUTE} metadata" odebug "Adding #{ALT_NAME_ATTRIBUTE} metadata"
altnames = command.run("/usr/bin/xattr", altnames = command.run("/usr/bin/xattr",
@ -109,5 +108,3 @@ module Cask
end end
end end
end end
require "extend/os/cask/artifact/relocated"

View File

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

View File

@ -6,7 +6,6 @@ require "cask/cask"
require "uri" require "uri"
require "utils/curl" require "utils/curl"
require "utils/output" require "utils/output"
require "utils/path"
require "extend/hash/keys" require "extend/hash/keys"
require "api" require "api"
@ -113,7 +112,9 @@ module Cask
return unless path.expand_path.exist? return unless path.expand_path.exist?
return if invalid_path?(path) 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) new(path)
end end

View File

@ -49,7 +49,7 @@ module Cask
SystemCommand.run("/bin/mkdir", args: ["-p", path], sudo:) SystemCommand.run("/bin/mkdir", args: ["-p", path], sudo:)
SystemCommand.run("/bin/chmod", args: ["g+rwx", 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:) SystemCommand.run("/usr/bin/chgrp", args: ["admin", path], sudo:)
end end

View File

@ -733,7 +733,7 @@ module Homebrew
formulae_names = removable_formulae.map(&:full_name).sort formulae_names = removable_formulae.map(&:full_name).sort
verb = dry_run ? "Would autoremove" : "Autoremoving" 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") puts formulae_names.join("\n")
return if dry_run return if dry_run

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ homebrew-shellenv() {
echo "setenv HOMEBREW_REPOSITORY ${HOMEBREW_REPOSITORY};" echo "setenv HOMEBREW_REPOSITORY ${HOMEBREW_REPOSITORY};"
if [[ -n "${PATH_HELPER_ROOT}" ]] if [[ -n "${PATH_HELPER_ROOT}" ]]
then 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 else
echo "setenv PATH ${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin:\$PATH;" echo "setenv PATH ${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin:\$PATH;"
fi fi
@ -75,7 +75,7 @@ homebrew-shellenv() {
fi fi
if [[ -n "${PATH_HELPER_ROOT}" ]] if [[ -n "${PATH_HELPER_ROOT}" ]]
then 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 else
echo "export PATH=\"${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin\${PATH+:\$PATH}\";" echo "export PATH=\"${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin\${PATH+:\$PATH}\";"
fi fi

View File

@ -57,7 +57,7 @@ module Homebrew
end end
info = Utils.pluralize("tap", tap_count, include_count: true) info = Utils.pluralize("tap", tap_count, include_count: true)
info += ", #{private_count} private" 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 += ", #{Utils.pluralize("command", command_count, include_count: true)}"
info += ", #{HOMEBREW_TAP_DIRECTORY.dup.abv}" if HOMEBREW_TAP_DIRECTORY.directory? info += ", #{HOMEBREW_TAP_DIRECTORY.dup.abv}" if HOMEBREW_TAP_DIRECTORY.directory?
puts info puts info

View File

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

View File

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

View File

@ -128,7 +128,7 @@ module Commands
OFFICIAL_CMD_TAPS.flat_map do |tap_name, cmds| OFFICIAL_CMD_TAPS.flat_map do |tap_name, cmds|
tap = Tap.fetch(tap_name) tap = Tap.fetch(tap_name)
tap.install(quiet:) unless tap.installed? 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
end end

View File

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

View File

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

View File

@ -93,11 +93,11 @@ module Homebrew
switch "--cask", "--casks", switch "--cask", "--casks",
description: "Treat all named arguments as casks." description: "Treat all named arguments as casks."
conflicts "--installed", "--eval-all"
conflicts "--only", "--except" conflicts "--only", "--except"
conflicts "--only-cops", "--except-cops", "--strict" conflicts "--only-cops", "--except-cops", "--strict"
conflicts "--only-cops", "--except-cops", "--only" conflicts "--only-cops", "--except-cops", "--only"
conflicts "--formula", "--cask" conflicts "--formula", "--cask"
conflicts "--installed", "--all"
named_args [:formula, :cask], without_api: true named_args [:formula, :cask], without_api: true
end end
@ -294,7 +294,9 @@ module Homebrew
errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true) errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true)
error_sources = [] 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("cask", cask_count, include_count: true) if cask_count.positive?
error_sources << Utils.pluralize("tap", tap_count, include_count: true) if tap_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 { sig {
params(old_keys: T::Array[String], old_bottle_spec: BottleSpecification, params(old_keys: T::Array[String], old_bottle_spec: BottleSpecification,
new_bottle_hash: T::Hash[String, T.untyped]) new_bottle_hash: T::Hash[String, T.untyped]).returns(T::Array[T::Array[String]])
.returns([T::Array[String], T::Array[T::Hash[Symbol, T.any(String, Symbol)]]])
} }
def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash) def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
mismatches = [] mismatches = []
@ -410,7 +409,7 @@ module Homebrew
bottle_tag, rebuild = if local_bottle_json bottle_tag, rebuild = if local_bottle_json
_, tag_string, rebuild_string = Utils::Bottles.extname_tag_rebuild(formula.local_bottle_path.to_s) _, 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 end
bottle_tag = if bottle_tag bottle_tag = if bottle_tag
@ -861,8 +860,8 @@ module Homebrew
end end
sig { sig {
params(formula: Formula, formula_ast: Utils::AST::FormulaAST, bottle_hash: T::Hash[String, T.untyped]) params(formula: Formula, formula_ast: Utils::AST::FormulaAST,
.returns(T.nilable(T::Array[T::Hash[Symbol, T.any(String, Symbol)]])) bottle_hash: T::Hash[String, T.untyped]).returns(T.nilable(T::Array[String]))
} }
def old_checksums(formula, formula_ast, bottle_hash) def old_checksums(formula, formula_ast, bottle_hash)
bottle_node = T.cast(formula_ast.bottle_block, T.nilable(RuboCop::AST::BlockNode)) 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." description: "Use the specified GitHub organization for forking."
conflicts "--dry-run", "--write" conflicts "--dry-run", "--write"
conflicts "--version", "--version-arm" conflicts "--version=", "--version-arm="
conflicts "--version", "--version-intel" conflicts "--version=", "--version-intel="
named_args :cask, number: 1, without_api: true named_args :cask, number: 1, without_api: true
end end

View File

@ -9,8 +9,6 @@ require "utils/repology"
module Homebrew module Homebrew
module DevCmd module DevCmd
class Bump < AbstractCommand class Bump < AbstractCommand
NEWER_THAN_UPSTREAM_MSG = " (newer than upstream)"
class VersionBumpInfo < T::Struct class VersionBumpInfo < T::Struct
const :type, Symbol const :type, Symbol
const :multiple_versions, T::Boolean const :multiple_versions, T::Boolean
@ -18,7 +16,6 @@ module Homebrew
const :current_version, BumpVersionParser const :current_version, BumpVersionParser
const :repology_latest, T.any(String, Version) const :repology_latest, T.any(String, Version)
const :new_version, BumpVersionParser 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 :duplicate_pull_requests, T.nilable(T.any(T::Array[String], String))
const :maybe_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 end
@ -60,10 +57,10 @@ module Homebrew
switch "--bump-synced", switch "--bump-synced",
description: "Bump additional formulae marked as synced with the given formulae." description: "Bump additional formulae marked as synced with the given formulae."
conflicts "--formula", "--cask" conflicts "--cask", "--formula"
conflicts "--tap", "--installed" conflicts "--tap=", "--installed"
conflicts "--tap", "--no-autobump" conflicts "--tap=", "--no-autobump"
conflicts "--installed", "--eval-all" conflicts "--eval-all", "--installed"
conflicts "--installed", "--auto" conflicts "--installed", "--auto"
conflicts "--no-pull-requests", "--open-pr" conflicts "--no-pull-requests", "--open-pr"
@ -318,7 +315,6 @@ module Homebrew
new_versions = {} new_versions = {}
repology_latest = repositories.present? ? Repology.latest_version(repositories) : "not found" 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. # 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] 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) loaded_formula_or_cask = Cask::CaskLoader.load(formula_or_cask.sourcefile_path)
current_version_value = Version.new(loaded_formula_or_cask.version) current_version_value = Version.new(loaded_formula_or_cask.version)
end end
formula_or_cask_has_livecheck = loaded_formula_or_cask.livecheck_defined?
livecheck_latest = livecheck_result(loaded_formula_or_cask) 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, livecheck_latest) >=
Livecheck::LivecheckVersion.create(formula_or_cask, current_version_value)) || Livecheck::LivecheckVersion.create(formula_or_cask, current_version_value)) ||
current_version_value == "latest" current_version_value == "latest"
livecheck_latest livecheck_latest
elsif livecheck_latest.is_a?(String) && livecheck_latest.start_with?("skipped") elsif livecheck_latest.is_a?(String) && livecheck_latest.start_with?("skipped")
"skipped" "skipped"
elsif repology_latest_is_a_version && elsif repology_latest.is_a?(Version) &&
!formula_or_cask_has_livecheck &&
repology_latest > current_version_value && repology_latest > current_version_value &&
!loaded_formula_or_cask.livecheck_defined? &&
current_version_value != "latest" current_version_value != "latest"
repology_latest repology_latest
end.presence 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 # Store old and new versions
old_versions[version_key] = current_version_value old_versions[version_key] = current_version_value
new_versions[version_key] = new_version_value new_versions[version_key] = new_version_value
@ -392,22 +380,10 @@ module Homebrew
new_version = BumpVersionParser.new(general: "unable to get versions") new_version = BumpVersionParser.new(general: "unable to get versions")
end 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? && if !args.no_pull_requests? &&
(new_version.general != "unable to get versions") && (new_version.general != "unable to get versions") &&
(new_version.general != "skipped") && (new_version.general != "skipped") &&
(new_version != current_version) && (new_version != current_version)
!newer_than_upstream.all? { |_k, v| v == true }
# We use the ARM version for the pull request version. This is # We use the ARM version for the pull request version. This is
# consistent with the behavior of bump-cask-pr. # consistent with the behavior of bump-cask-pr.
pull_request_version = if multiple_versions pull_request_version = if multiple_versions
@ -434,7 +410,6 @@ module Homebrew
current_version:, current_version:,
repology_latest:, repology_latest:,
new_version:, new_version:,
newer_than_upstream:,
duplicate_pull_requests:, duplicate_pull_requests:,
maybe_duplicate_pull_requests:, maybe_duplicate_pull_requests:,
) )
@ -457,8 +432,8 @@ module Homebrew
new_version = version_info.new_version new_version = version_info.new_version
repology_latest = version_info.repology_latest repology_latest = version_info.repology_latest
# Check if all versions are equal
versions_equal = (new_version == current_version) 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_name = ambiguous_cask ? "#{name} (cask)" : name
title = if (repology_latest == current_version.general || !repology_latest.is_a?(Version)) && versions_equal 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 # Conditionally format output based on type of formula_or_cask
current_versions = if version_info.multiple_versions current_versions = if version_info.multiple_versions
"arm: #{current_version.arm}" \ "arm: #{current_version.arm}
"#{NEWER_THAN_UPSTREAM_MSG if version_info.newer_than_upstream[:arm]}" \ intel: #{current_version.intel}"
"\n intel: #{current_version.intel}" \
"#{NEWER_THAN_UPSTREAM_MSG if version_info.newer_than_upstream[:intel]}"
else else
newer_than_upstream_general = version_info.newer_than_upstream[:general] current_version.general.to_s
"#{current_version.general}#{NEWER_THAN_UPSTREAM_MSG if newer_than_upstream_general}"
end end
current_versions << " (deprecated)" if formula_or_cask.deprecated? current_versions << " (deprecated)" if formula_or_cask.deprecated?
@ -510,8 +482,7 @@ module Homebrew
if !args.no_pull_requests? && if !args.no_pull_requests? &&
(new_version.general != "unable to get versions") && (new_version.general != "unable to get versions") &&
(new_version.general != "skipped") && (new_version.general != "skipped") &&
!versions_equal && !versions_equal
!all_newer_than_upstream
if duplicate_pull_requests if duplicate_pull_requests
duplicate_pull_requests_text = duplicate_pull_requests duplicate_pull_requests_text = duplicate_pull_requests
elsif maybe_duplicate_pull_requests elsif maybe_duplicate_pull_requests
@ -530,8 +501,7 @@ module Homebrew
if !args.open_pr? || if !args.open_pr? ||
(new_version.general == "unable to get versions") || (new_version.general == "unable to get versions") ||
(new_version.general == "skipped") || (new_version.general == "skipped")
all_newer_than_upstream
return return
end end

View File

@ -6,23 +6,13 @@ require "abstract_command"
module Homebrew module Homebrew
module DevCmd module DevCmd
class Contributions < AbstractCommand class Contributions < AbstractCommand
PRIMARY_REPOS = T.let(%w[ PRIMARY_REPOS = T.let(%w[brew core cask].freeze, T::Array[String])
Homebrew/brew SUPPORTED_REPOS = T.let([
Homebrew/homebrew-core PRIMARY_REPOS,
Homebrew/homebrew-cask OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") },
].freeze, T::Array[String]) OFFICIAL_CASK_TAPS.reject { |t| t == "cask" },
ALL_REPOS = T.let([ ].flatten.freeze, T::Array[String])
*PRIMARY_REPOS, MAX_REPO_COMMITS = 1000
*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)
cmd_args do cmd_args do
usage_banner "`contributions` [`--user=`] [`--repositories=`] [`--from=`] [`--to=`] [`--csv`]" usage_banner "`contributions` [`--user=`] [`--repositories=`] [`--from=`] [`--to=`] [`--csv`]"
@ -34,17 +24,10 @@ module Homebrew
"contributions from. Omitting this flag searches Homebrew maintainers." "contributions from. Omitting this flag searches Homebrew maintainers."
comma_array "--repositories", comma_array "--repositories",
description: "Specify a comma-separated list of repositories to search. " \ 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 " \ "Omitting this flag, or specifying `--repositories=primary`, searches only the " \
"main repositories: `Homebrew/brew`, `Homebrew/homebrew-core`, " \ "main repositories: `brew`, `core`, `cask`. " \
"`Homebrew/homebrew-cask`. Specifying `--repositories=all` searches all " \ "Specifying `--repositories=all` searches all repositories. "
"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."
flag "--from=", flag "--from=",
description: "Date (ISO 8601 format) to start searching contributions. " \ description: "Date (ISO 8601 format) to start searching contributions. " \
"Omitting this flag searches the past year." "Omitting this flag searches the past year."
@ -52,93 +35,54 @@ module Homebrew
description: "Date (ISO 8601 format) to stop searching contributions." description: "Date (ISO 8601 format) to stop searching contributions."
switch "--csv", switch "--csv",
description: "Print a CSV of contributions across repositories over the time period." description: "Print a CSV of contributions across repositories over the time period."
conflicts "--organisation", "--repositories"
conflicts "--organisation", "--team"
conflicts "--user", "--team"
end end
sig { override.void } sig { override.void }
def run 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? Homebrew.install_bundler_gems!(groups: ["contributions"]) if args.csv?
require "utils/github"
results = {} results = {}
grand_totals = {} grand_totals = {}
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) repos = T.must(
team_sections = team.split("/") if args.repositories.blank? || args.repositories&.include?("primary")
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 PRIMARY_REPOS
when "all" elsif args.repositories&.include?("all")
ALL_REPOS SUPPORTED_REPOS
else else
Array(first_repository) args.repositories
end,
)
repos.each do |repo|
if SUPPORTED_REPOS.exclude?(repo)
odie "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}."
end 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 end
organisation ||= T.must(repositories.fetch(0).split("/").first)
from = args.from.presence || Date.today.prev_year.iso8601
contribution_types = [:author, :committer, :coauthor, :review]
require "utils/github"
users = args.user.presence || GitHub.members_by_team("Homebrew", "maintainers").keys
users.each do |username| users.each do |username|
# TODO: Using the GitHub username to scan the `git log` undercounts some # TODO: Using the GitHub username to scan the `git log` undercounts some
# contributions as people might not always have configured their Git # contributions as people might not always have configured their Git
# committer details to match the ones on GitHub. # committer details to match the ones on GitHub.
# TODO: Switch to using the GitHub APIs instead of `git log` if # TODO: Switch to using the GitHub APIs instead of `git log` if
# they ever support trailers. # 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]) grand_totals[username] = total(results[username])
search_types = [:merged_pr_author, :approved_pr_review].freeze contributions = contribution_types.filter_map do |type|
greater_than_total = T.let(false, T::Boolean)
contributions = CONTRIBUTION_TYPES.keys.filter_map do |type|
type_count = grand_totals[username][type] type_count = grand_totals[username][type]
next if type_count.zero? next if type_count.to_i.zero?
count_prefix = "" "#{Utils.pluralize("time", type_count, include_count: true)} (#{type})"
if (search_types.include?(type) && type_count == MAX_PR_SEARCH) ||
(type == :committer && type_count == MAX_COMMITS)
greater_than_total ||= true
count_prefix = ">="
end
pretty_type = CONTRIBUTION_TYPES.fetch(type)
"#{count_prefix}#{Utils.pluralize("time", type_count, include_count: true)} (#{pretty_type})"
end end
total = Utils.pluralize("time", grand_totals[username].values.sum, include_count: true) contributions <<
total_prefix = ">=" if greater_than_total "#{Utils.pluralize("time", grand_totals[username].values.sum, include_count: true)} (total)"
contributions << "#{total_prefix}#{total} (total)"
contributions_string = [ contributions_string = [
"#{username} contributed", "#{username} contributed",
@ -160,16 +104,12 @@ module Homebrew
private private
sig { params(repository: String).returns([T.nilable(Pathname), T.nilable(Tap)]) } sig { params(repo: String).returns(Pathname) }
def repository_path_and_tap(repository) def find_repo_path_for_repo(repo)
return [HOMEBREW_REPOSITORY, nil] if repository == "Homebrew/brew" return HOMEBREW_REPOSITORY if repo == "brew"
return [nil, nil] if repository.exclude?("/homebrew-")
require "tap" require "tap"
tap = Tap.fetch(repository) Tap.fetch("homebrew", repo).path
return [nil, nil] if tap.user == "Homebrew" && DEPRECATED_OFFICIAL_TAPS.include?(tap.repository)
[tap.path, tap]
end end
sig { params(from: T.nilable(String), to: T.nilable(String)).returns(String) } sig { params(from: T.nilable(String), to: T.nilable(String)).returns(String) }
@ -190,7 +130,7 @@ module Homebrew
require "csv" require "csv"
CSV.generate do |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| totals.sort_by { |_, v| -v.values.sum }.each do |user, total|
csv << grand_total_row(user, total) csv << grand_total_row(user, total)
@ -198,67 +138,63 @@ module Homebrew
end end
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) 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 end
sig { sig {
params( params(
organisation: String, repos: T::Array[String],
repositories: T::Array[String], person: String,
person: String, from: String,
from: String,
to: String,
).returns(T::Hash[Symbol, T.untyped]) ).returns(T::Hash[Symbol, T.untyped])
} }
def scan_repositories(organisation, repositories, person, from:, to:) def scan_repositories(repos, person, from:)
data = {} data = {}
return data if repositories.blank? return data if repos.blank?
require "tap"
require "utils/github" require "utils/github"
repos.each do |repo|
max = MAX_COMMITS repo_path = find_repo_path_for_repo(repo)
verbose = args.verbose? tap = Tap.fetch("homebrew", repo)
unless repo_path.exist?
puts "Querying pull requests for #{person} in #{organisation}..." if args.verbose? opoo "Repository #{repo} not yet tapped! Tapping it now..."
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..."
tap.install tap.install
end end
repository_full_name = tap&.full_name repo_full_name = if repo == "brew"
repository_full_name ||= repository "homebrew/brew"
else
repository_api_url = "#{GitHub::API_URL}/repos/#{repository_full_name}" tap.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
end 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: } puts "Determining contributions for #{person} on #{repo_full_name}..." if args.verbose?
rescue GitHub::API::RateLimitExceededError => e
sleep_seconds = e.reset - Time.now.to_i author_commits, committer_commits = GitHub.count_repo_commits(repo_full_name, person,
opoo "GitHub rate limit exceeded, sleeping for #{sleep_seconds} seconds..." from:, to: args.to, max: MAX_REPO_COMMITS)
sleep sleep_seconds data[repo] = {
retry 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 end
data data
@ -266,17 +202,43 @@ module Homebrew
sig { params(results: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, Integer]) } sig { params(results: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, Integer]) }
def total(results) def total(results)
totals = {} totals = { author: 0, committer: 0, coauthor: 0, review: 0 }
results.each_value do |counts| results.each_value do |counts|
counts.each do |kind, count| counts.each do |kind, count|
totals[kind] ||= 0
totals[kind] += count totals[kind] += count
end end
end end
totals totals
end 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 end
end end

View File

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

View File

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

View File

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

View File

@ -579,7 +579,7 @@ class UnbottledError < RuntimeError
require "utils" require "utils"
msg = <<~EOS 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. built from source.
#{formulae.to_sentence} #{formulae.to_sentence}
EOS 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? @needs_libc_formula ||= OS::Linux::Glibc.below_ci_version?
end end
# Keep this method around for now to make it easier to add this functionality later.
# rubocop:disable Lint/UselessMethodDefinition
sig { returns(Pathname) } sig { returns(Pathname) }
def host_gcc_path def host_gcc_path
# Prioritise versioned path if installed # TODO: override this if/when we to pick the GCC based on e.g. the Ubuntu version.
path = Pathname.new("/usr/bin/#{OS::LINUX_PREFERRED_GCC_COMPILER_FORMULA.tr("@", "-")}")
return path if path.exist?
super super
end end
# rubocop:enable Lint/UselessMethodDefinition
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def needs_compiler_formula? def needs_compiler_formula?
@ -60,7 +60,12 @@ module OS
# Undocumented environment variable to make it easier to test compiler # Undocumented environment variable to make it easier to test compiler
# formula automatic installation. # formula automatic installation.
@needs_compiler_formula = true if ENV["HOMEBREW_FORCE_COMPILER_FORMULA"] @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 end
sig { returns(T::Hash[String, T.nilable(String)]) } sig { returns(T::Hash[String, T.nilable(String)]) }

View File

@ -40,8 +40,6 @@ module OS
self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula) self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula)
m4_path_deps = ["libtool", "bison"] m4_path_deps = ["libtool", "bison"]
self["M4"] = "#{HOMEBREW_PREFIX}/opt/m4/bin/m4" if deps.any? { m4_path_deps.include?(_1.name) } 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 # Pointer authentication and BTI are hardening techniques most distros
# use by default on their packages. arm64 Linux we're packaging # use by default on their packages. arm64 Linux we're packaging

View File

@ -1,24 +1,36 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "os/linux/ld"
require "os/linux/libstdcxx"
require "utils/output" require "utils/output"
module OS module OS
module Linux module Linux
module Install module Install
module ClassMethods 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, # 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 # 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 # as the other shared and static libraries are only used at build time where
# GCC can find its own libraries. # GCC can find its own libraries.
GCC_RUNTIME_LIBS = T.let(%W[ GCC_RUNTIME_LIBS = %w[
libatomic.so.1 libatomic.so.1
libgcc_s.so.1 libgcc_s.so.1
libgomp.so.1 libgomp.so.1
#{OS::Linux::Libstdcxx::SONAME} libstdc++.so.6
].freeze, T::Array[String]) ].freeze
sig { params(all_fatal: T::Boolean).void } sig { params(all_fatal: T::Boolean).void }
def perform_preinstall_checks(all_fatal: false) def perform_preinstall_checks(all_fatal: false)
@ -55,7 +67,7 @@ module OS
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so" ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
unless ld_so.readable? 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? if ld_so.blank?
::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable? ::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 # frozen_string_literal: true
require "compilers" require "compilers"
require "os/linux/libstdcxx"
module OS module OS
module Linux module Linux
module LinkageChecker module LinkageChecker
# Libraries provided by glibc and gcc. # Libraries provided by glibc and gcc.
SYSTEM_LIBRARY_ALLOWLIST = %W[ SYSTEM_LIBRARY_ALLOWLIST = %w[
ld-linux-x86-64.so.2 ld-linux-x86-64.so.2
ld-linux-aarch64.so.1 ld-linux-aarch64.so.1
libanl.so.1 libanl.so.1
@ -25,7 +24,7 @@ module OS
libutil.so.1 libutil.so.1
libgcc_s.so.1 libgcc_s.so.1
libgomp.so.1 libgomp.so.1
#{OS::Linux::Libstdcxx::SONAME} libstdc++.so.6
libquadmath.so.0 libquadmath.so.0
].freeze ].freeze

View File

@ -3,7 +3,6 @@
require "compilers" require "compilers"
require "os/linux/glibc" require "os/linux/glibc"
require "os/linux/libstdcxx"
require "system_command" require "system_command"
module OS module OS
@ -21,13 +20,6 @@ module OS
version version
end end
def host_libstdcxx_version
version = OS::Linux::Libstdcxx.system_version
return "N/A" if version.null?
version
end
def host_gcc_version def host_gcc_version
gcc = ::DevelopmentTools.host_gcc_path gcc = ::DevelopmentTools.host_gcc_path
return "N/A" unless gcc.executable? return "N/A" unless gcc.executable?
@ -44,7 +36,7 @@ module OS
end end
def host_ruby_version 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? return "N/A" unless status.success?
out out
@ -57,7 +49,6 @@ module OS
out.puts "OS: #{OS::Linux.os_version}" out.puts "OS: #{OS::Linux.os_version}"
out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl? out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl?
out.puts "Host glibc: #{host_glibc_version}" 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 "#{::DevelopmentTools.host_gcc_path}: #{host_gcc_version}"
out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH 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| ["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]) } sig { returns(T::Array[String]) }
def zipinfo def zipinfo
@zipinfo ||= T.let( @zipinfo ||= T.let(nil, T.nilable(String))
system_command("zipinfo", args: ["-1", self], print_stderr: false) @zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
.stdout .stdout
.encode(Encoding::UTF_8, invalid: :replace) .encode(Encoding::UTF_8, invalid: :replace)
.split("\n"), .split("\n")
T.nilable(T::Array[String]),
)
end end
private private

View File

@ -749,7 +749,7 @@ module Homebrew
end end
def audit_specs 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| %w[Stable HEAD].each do |name|
spec_name = name.downcase.to_sym spec_name = name.downcase.to_sym

View File

@ -609,8 +609,8 @@ on_request: installed_on_request?, options:)
clean clean
# Store the formula used to build the keg in the keg. # Store the formula used to build the keg in the keg.
formula_contents = if (local_bottle_path = formula.local_bottle_path) formula_contents = if formula.local_bottle_path
Utils::Bottles.formula_contents local_bottle_path, name: formula.name Utils::Bottles.formula_contents formula.local_bottle_path, name: formula.name
else else
formula.path.read formula.path.read
end end
@ -1156,17 +1156,6 @@ on_request: installed_on_request?, options:)
def link(keg) def link(keg)
Formula.clear_cache 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 unless link_keg
begin begin
keg.optlink(verbose: verbose?, overwrite: overwrite?) keg.optlink(verbose: verbose?, overwrite: overwrite?)
@ -1178,6 +1167,17 @@ on_request: installed_on_request?, options:)
return return
end 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? if keg.linked?
opoo "This keg was marked linked already, continuing anyway" opoo "This keg was marked linked already, continuing anyway"
keg.remove_linked_keg_record keg.remove_linked_keg_record

View File

@ -7,7 +7,6 @@ require "tab"
require "utils" require "utils"
require "utils/bottles" require "utils/bottles"
require "utils/output" require "utils/output"
require "utils/path"
require "service" require "service"
require "utils/curl" require "utils/curl"
require "deprecate_disable" require "deprecate_disable"
@ -728,7 +727,29 @@ module Formulary
end end
return unless path.expand_path.exist? 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)) if (tap = Tap.from_path(path))
# Only treat symlinks in taps as aliases. # 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. # on homebrew/core and tag the first commit with a bottle e.g.
# `git tag 15-sequoia f42c4a659e4da887fc714f8f41cc26794a4bb320` # `git tag 15-sequoia f42c4a659e4da887fc714f8f41cc26794a4bb320`
# to allow people to jump to specific commits based on their macOS version. # to allow people to jump to specific commits based on their macOS version.
NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :tahoe NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :sequoia
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :sonoma OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :ventura
NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER = :sonoma NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER = :sonoma
RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) } RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) }

View File

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

View File

@ -113,18 +113,6 @@ class Keg
old: "/usr/local/var/log/#{name}", old: "/usr/local/var/log/#{name}",
new: "#{PREFIX_PLACEHOLDER}/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 end

View File

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

View File

@ -129,9 +129,9 @@ module Homebrew
print_stderr: false, print_stderr: false,
debug: false, debug: false,
verbose: 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? tags_data[:messages] = stderr.split("\n") if stderr.present?
return tags_data if stdout.blank? 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. You can still access these formulae by using a special syntax, e.g.
`homebrew/dupes/vim` or `homebrew/versions/node4`. `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 ## SPECIFYING CASKS
Many Homebrew Cask commands accept one or more <cask> arguments. These can be 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 devel-only
dupes dupes
emacs emacs
formula-analytics
fuse fuse
games games
gui gui

View File

@ -50,7 +50,6 @@ module OS
LINUX_GLIBC_CI_VERSION = "2.35" LINUX_GLIBC_CI_VERSION = "2.35"
LINUX_GLIBC_NEXT_CI_VERSION = "2.39" LINUX_GLIBC_NEXT_CI_VERSION = "2.39"
LINUX_GCC_CI_VERSION = "11.0" 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_COMPILER_FORMULA = "gcc@11" # https://packages.ubuntu.com/jammy/gcc
LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc" LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc"

View File

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

View File

@ -5,69 +5,37 @@ module OS
module Linux module Linux
# Helper functions for querying `ld` information. # Helper functions for querying `ld` information.
module Ld module Ld
# This is a list of known paths to the host dynamic linker on Linux if sig { returns(String) }
# the host glibc is new enough. Brew will fail to create a symlink for def self.brewed_ld_so_diagnostics
# ld.so if the host linker cannot be found in this list. @brewed_ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
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
# The path to the system's dynamic linker or `nil` if not found brewed_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
sig { returns(T.nilable(Pathname)) } return "" unless brewed_ld_so.exist?
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
sig { params(brewed: T::Boolean).returns(String) } brewed_ld_so_target = brewed_ld_so.readlink
def self.ld_so_diagnostics(brewed: true) @brewed_ld_so_diagnostics[brewed_ld_so_target] ||= begin
@ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String])) ld_so_output = Utils.popen_read(brewed_ld_so, "--list-diagnostics")
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")
ld_so_output if $CHILD_STATUS.success? ld_so_output if $CHILD_STATUS.success?
end end
@ld_so_diagnostics[ld_so_target].to_s @brewed_ld_so_diagnostics[brewed_ld_so_target].to_s
end end
sig { params(brewed: T::Boolean).returns(String) } sig { returns(String) }
def self.sysconfdir(brewed: true) def self.sysconfdir
fallback_sysconfdir = "/etc" fallback_sysconfdir = "/etc"
match = ld_so_diagnostics(brewed:).match(/path.sysconfdir="(.+)"/) match = brewed_ld_so_diagnostics.match(/path.sysconfdir="(.+)"/)
return fallback_sysconfdir unless match return fallback_sysconfdir unless match
match.captures.compact.first || fallback_sysconfdir match.captures.compact.first || fallback_sysconfdir
end end
sig { params(brewed: T::Boolean).returns(T::Array[String]) } sig { returns(T::Array[String]) }
def self.system_dirs(brewed: true) def self.system_dirs
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.*\]="(.*)"/) match = line.match(/path.system_dirs\[0x.*\]="(.*)"/)
next unless match next unless match
@ -77,9 +45,9 @@ module OS
dirs dirs
end end
sig { params(conf_path: T.any(Pathname, String), brewed: T::Boolean).returns(T::Array[String]) } sig { params(conf_path: T.any(Pathname, String)).returns(T::Array[String]) }
def self.library_paths(conf_path = "ld.so.conf", brewed: true) def self.library_paths(conf_path = Pathname(sysconfdir)/"ld.so.conf")
conf_file = Pathname(sysconfdir(brewed:))/conf_path conf_file = Pathname(conf_path)
return [] unless conf_file.exist? return [] unless conf_file.exist?
return [] unless conf_file.file? return [] unless conf_file.file?
return [] unless conf_file.readable? return [] unless conf_file.readable?
@ -100,7 +68,8 @@ module OS
line.sub!(/\s*#.*$/, "") line.sub!(/\s*#.*$/, "")
if line.start_with?(/\s*include\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| Dir.glob(wildcard.to_s).each do |include_file|
paths += library_paths(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 def self.latest_sdk_version
# TODO: bump version when new Xcode macOS SDK is released # TODO: bump version when new Xcode macOS SDK is released
# NOTE: We only track the major version of the SDK. # NOTE: We only track the major version of the SDK.
::Version.new("26") ::Version.new("15")
end end
sig { returns(String) } sig { returns(String) }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -125,7 +125,7 @@ module Readall
sig { params(filename: Pathname).returns(T::Boolean) } sig { params(filename: Pathname).returns(T::Boolean) }
private_class_method def self.syntax_errors_or_warnings?(filename) private_class_method def self.syntax_errors_or_warnings?(filename)
# Retrieve messages about syntax errors/warnings printed to `$stderr`. # 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. # Ignore unnecessary warning about named capture conflicts.
# See https://bugs.ruby-lang.org/issues/12359. # See https://bugs.ruby-lang.org/issues/12359.

View File

@ -85,6 +85,9 @@ module Homebrew
end end
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) return if url_strategy != DownloadStrategyDetector.detect("", using)
problem "Redundant `using:` value in URL" problem "Redundant `using:` value in URL"

View File

@ -93,7 +93,7 @@ module Homebrew
wait = 2 ** @try wait = 2 ** @try
unless quiet 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)" ohai "Retrying download in #{wait}s... (#{tries_remaining} #{what} left)"
end end
sleep wait sleep wait

View File

@ -34,7 +34,7 @@ module Homebrew
unofficial = Tap.all.sum { |tap| tap.official? ? 0 : tap.formula_files.size } unofficial = Tap.all.sum { |tap| tap.official? ? 0 : tap.formula_files.size }
if unofficial.positive? if unofficial.positive?
opoo "Use `--eval-all` to search #{unofficial} additional " \ 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 end
descriptions = Homebrew::API::Formula.all_formulae.transform_values { |data| data["desc"] } 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 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:) private_class_method def self._run(*args, mode:)
require "system_command" require "system_command"
result = SystemCommand.run(T.must(executable), result = SystemCommand.run(executable,
args: [scope, *args.map(&:to_s)], args: [scope, *args.map(&:to_s)],
print_stdout: mode == :default, print_stdout: mode == :default,
print_stderr: mode == :default, print_stderr: mode == :default,

View File

@ -369,13 +369,9 @@ class Cmd
end end
args += rpath_flags("#{wl}-rpath=", rpath_paths) args += rpath_flags("#{wl}-rpath=", rpath_paths)
args += ["#{wl}--dynamic-linker=#{dynamic_linker_path}"] if dynamic_linker_path 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. # dependencies because -L will only handle direct dependencies.
if versioned_glibc_dep args << "#{wl}-rpath-link=#{@opt}/#{versioned_glibc_dep}/lib" if versioned_glibc_dep
args << "#{wl}-rpath-link=#{@opt}/#{versioned_glibc_dep}/lib"
else
args << "#{wl}-rpath-link=#{@opt}/glibc/lib"
end
args args
end end

View File

@ -17,21 +17,9 @@ class Homebrew::DevCmd::Contributions::Args < Homebrew::CLI::Args
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def from; end 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])) } sig { returns(T.nilable(T::Array[String])) }
def repositories; end def repositories; end
sig { returns(T.nilable(String)) }
def team; end
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def to; end def to; end

View File

@ -988,38 +988,38 @@ RuboCop::Cop::RSpec::DescribeSymbol::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Arr
# end # end
# 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 class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::ConfigurableEnforcedStyle include ::RuboCop::Cop::ConfigurableEnforcedStyle
include ::RuboCop::Cop::RSpec::Namespace include ::RuboCop::Cop::RSpec::Namespace
extend ::RuboCop::Cop::AutoCorrector 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 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 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 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 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 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 def scope_changing_syntax?(param0 = T.unsafe(nil)); end
private private
# @return [Boolean] # @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 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 def autocorrect(corrector, match); end
# @example # @example
@ -1033,7 +1033,7 @@ class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
# @param const [Array<Symbol>] # @param const [Array<Symbol>]
# @return [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 def collapse_namespace(namespace, const); end
# @example # @example
@ -1043,50 +1043,50 @@ class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
# @param node [RuboCop::AST::Node] # @param node [RuboCop::AST::Node]
# @return [Array<Symbol>] # @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 def const_name(node); end
# @yield [node] # @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 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 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 def message(offense); end
# @return [Boolean] # @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 def offensive?(node); end
# @return [Boolean] # @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 def offensive_described_class?(node); end
# @return [Boolean] # @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 def only_static_constants?; end
# @return [Boolean] # @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 def scope_change?(node); end
# @return [Boolean] # @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 def skippable_block?(node); end
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) 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) RuboCop::Cop::RSpec::DescribedClass::MSG = T.let(T.unsafe(nil), String)
# Avoid opening modules and defining specs within them. # 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! # - let, let!
# - subject, subject! # - subject, subject!
# - expect, is_expected, expect_any_instance_of # - expect, is_expected, expect_any_instance_of
# - raise_error, raise_exception
# #
# By default all of the RSpec methods and aliases are allowed. By setting # By default all of the RSpec methods and aliases are allowed. By setting
# a config like: # a config like:
@ -1170,19 +1169,19 @@ RuboCop::Cop::RSpec::DescribedClassModuleWrapping::MSG = T.let(T.unsafe(nil), St
# # ... # # ...
# end # 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 class RuboCop::Cop::RSpec::Dialect < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::MethodPreference include ::RuboCop::Cop::MethodPreference
extend ::RuboCop::Cop::AutoCorrector 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 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 def rspec_method?(param0 = T.unsafe(nil)); end
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) RuboCop::Cop::RSpec::Dialect::MSG = T.let(T.unsafe(nil), String)
# Avoid duplicated metadata. # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#60
def on_block(node); end 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 def on_numblock(node); end
private 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#30
def on_block(node); end 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 def on_numblock(node); end
private 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#78
def on_block(node); end 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 def on_numblock(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#69 # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#41
def on_block(node); end 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 def on_numblock(node); end
private private
@ -2839,18 +2838,18 @@ RuboCop::Cop::RSpec::ImplicitSubject::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Ar
# # good # # good
# it_behaves_like 'examples' # 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 class RuboCop::Cop::RSpec::IncludeExamples < ::RuboCop::Cop::RSpec::Base
extend ::RuboCop::Cop::AutoCorrector 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 def on_send(node); end
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) 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) 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`). # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#19
class RuboCop::Cop::RSpec::IteratedExpectation < ::RuboCop::Cop::RSpec::Base class RuboCop::Cop::RSpec::IteratedExpectation < ::RuboCop::Cop::RSpec::Base
extend ::RuboCop::Cop::AutoCorrector # source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#24
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#26
def each?(param0 = T.unsafe(nil)); end 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 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 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 def on_block(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#52 # 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 private
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#60
def check_offense(node, argument); end
# @return [Boolean] # @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 def only_expectations?(body, arg); end
# @return [Boolean] # @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 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 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) RuboCop::Cop::RSpec::IteratedExpectation::MSG = T.let(T.unsafe(nil), String)
# Enforce that subject is the first definition in the test. # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#101
def on_casgn(node); end 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 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 def on_module(node); end
private private
# @return [Boolean] # @return [Boolean]
# #
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#128 # source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#121
def explicit_namespace?(namespace); end
# @return [Boolean]
#
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#124
def inside_describe_block?(node); end def inside_describe_block?(node); end
end end
@ -3791,7 +3772,7 @@ module RuboCop::Cop::RSpec::Metadata
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#43 # source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#43
def on_metadata(_symbols, _hash); end 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 def on_numblock(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#21 # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#86
def expect?(param0 = T.unsafe(nil)); end 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 def max=(value); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#93 # 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 class RuboCop::Cop::RSpec::MultipleMemoizedHelpers < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::RSpec::Variable 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 def max=(value); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_memoized_helpers.rb#91 # 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 class RuboCop::Cop::RSpec::NestedGroups < ::RuboCop::Cop::RSpec::Base
include ::RuboCop::Cop::RSpec::TopLevelGroup 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 def max=(value); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/nested_groups.rb#107 # 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] # @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 def on_numblock(node); end
# @param node [RuboCop::AST::Node] # @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 # source://rubocop-rspec//lib/rubocop/cop/rspec/receive_counts.rb#30
RuboCop::Cop::RSpec::ReceiveCounts::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array) 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 # @example
# # bad # # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#23
def on_block(node); end 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 def on_numblock(node); end
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#32 # 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 # source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#29
def on_block(node); end 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 def on_numblock(node); end
private private
@ -7128,7 +7109,7 @@ class RuboCop::RSpec::Concept
# @return [Boolean] # @return [Boolean]
# #
# source://rubocop-rspec//lib/rubocop/rspec/concept.rb#18 # source://rubocop-rspec//lib/rubocop/rspec/concept.rb#14
def ==(other); end def ==(other); end
# @return [Boolean] # @return [Boolean]
@ -7375,137 +7356,129 @@ end
# This is used in Dialect and DescribeClass cops to detect RSpec blocks. # 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 module RuboCop::RSpec::Language::ALL
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#208 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#202
def all(element); end def all(element); end
end end
end end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#75 # 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 module RuboCop::RSpec::Language::ExampleGroups
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#83 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#77
def all(element); end 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 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 def regular(element); end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#97 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#91
def skipped(element); end def skipped(element); end
end end
end end
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#103 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#97
module RuboCop::RSpec::Language::Examples module RuboCop::RSpec::Language::Examples
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#105 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#99
def all(element); end 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 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 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 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 def skipped(element); end
end 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 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#130
module RuboCop::RSpec::Language::Expectations module RuboCop::RSpec::Language::Helpers
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#131 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#131
def all(element); end def all(element); end
end 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 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#136
module RuboCop::RSpec::Language::Helpers module RuboCop::RSpec::Language::Hooks
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#137 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#137
def all(element); end def all(element); end
end 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 # 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 module RuboCop::RSpec::Language::Includes
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#157 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#151
def all(element); end 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 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 def examples(element); end
end 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 module RuboCop::RSpec::Language::Runners
class << self 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 def all(element = T.unsafe(nil)); end
end 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) 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 module RuboCop::RSpec::Language::SharedGroups
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#185 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#179
def all(element); end 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 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 def examples(element); end
end 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 module RuboCop::RSpec::Language::Subjects
class << self class << self
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#201 # source://rubocop-rspec//lib/rubocop/rspec/language.rb#195
def all(element); end def all(element); end
end end
end end

View File

@ -49,7 +49,7 @@ module Homebrew
ruby_files = T.let([], T::Array[Pathname]) ruby_files = T.let([], T::Array[Pathname])
shell_files = T.let([], T::Array[Pathname]) shell_files = T.let([], T::Array[Pathname])
actionlint_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| .each do |path|
case path.extname case path.extname
when ".rb" when ".rb"

View File

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

View File

@ -102,7 +102,7 @@ module SystemConfig
sig { returns(String) } sig { returns(String) }
def describe_curl 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) match_data = /^curl (?<curl_version>[\d.]+)/.match(out)
if match_data if match_data

View File

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

View File

@ -45,9 +45,10 @@ begin
formula.extend(Debrew::Formula) formula.extend(Debrew::Formula)
end end
Pathname.prepend WriteMkpathExtension
ENV.extend(Stdenv) ENV.extend(Stdenv)
ENV.setup_build_environment(formula:, testing_formula: true) ENV.setup_build_environment(formula:, testing_formula: true)
Pathname.prepend WriteMkpathExtension
# tests can also return false to indicate failure # 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 } 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) expect(described_class.load_prefer_installed("test-cask").tap).to eq(foo_tap)
end end
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 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", "url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine/darwin/1.0.0/intel.zip",
"version": "1.0.0", "version": "1.0.0",
"sha256": "1866dfa833b123bb8fe7fa7185ebf24d28d300d0643d75798bc23730af734216" "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 JSON
@ -398,6 +418,22 @@ RSpec.describe Cask::Cask, :cask do
"catalina": { "catalina": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip", "url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" "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 JSON
@ -433,6 +469,22 @@ RSpec.describe Cask::Cask, :cask do
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip", "url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-darwin.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" "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": { "x86_64_linux": {
"url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-linux.zip", "url": "file://#{TEST_FIXTURE_DIR}/cask/caffeine-intel-linux.zip",
"sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b" "sha256": "8c62a2b791cf5f0da6066a0a4b6e85f62949cd60975da062df44adf887f4370b"

View File

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

View File

@ -4,61 +4,6 @@ require "os/linux/ld"
require "tmpdir" require "tmpdir"
RSpec.describe OS::Linux::Ld do 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 describe "::library_paths" do
ld_etc = Pathname("") ld_etc = Pathname("")
before do 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: "local-caffeine"
depends_on cask: "with-depends-on-cask" depends_on cask: "with-depends-on-cask"
depends_on formula: "unar" depends_on formula: "unar"
depends_on macos: ">= :catalina" depends_on macos: ">= :el_capitan"
app "Caffeine.app" app "Caffeine.app"
end end

View File

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

View File

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

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