Merge remote-tracking branch 'origin/master' into docker-image-arm64

This commit is contained in:
Carlo Cabrera 2025-03-13 17:27:23 +08:00 committed by Carlo Cabrera
commit 3246549edd
No known key found for this signature in database
GPG Key ID: C74D447FC549A1D0
343 changed files with 19246 additions and 4902 deletions

View File

@ -1,6 +1,6 @@
name: New issue for Reproducible Bug
description: "If you're sure it's reproducible and not just your machine: submit an issue so we can investigate."
labels: [bug]
type: "Bug"
body:
- type: markdown
attributes:

View File

@ -1,6 +1,6 @@
name: New issue for Feature Suggestion
description: Request our thoughts on your suggestion for a new feature for Homebrew.
labels: features
type: "Feature"
body:
- type: markdown
attributes:

View File

@ -14,6 +14,7 @@ updates:
artifacts:
patterns:
- actions/*-artifact
open-pull-requests-limit: 10
- package-ecosystem: bundler
directory: /Library/Homebrew
@ -25,6 +26,7 @@ updates:
sorbet:
patterns:
- "sorbet*"
open-pull-requests-limit: 10
- package-ecosystem: npm
directory: /
@ -32,6 +34,7 @@ updates:
interval: daily
allow:
- dependency-type: all
open-pull-requests-limit: 10
- package-ecosystem: docker
directory: /
@ -39,6 +42,7 @@ updates:
interval: daily
allow:
- dependency-type: all
open-pull-requests-limit: 10
- package-ecosystem: devcontainers
directory: /
@ -46,6 +50,7 @@ updates:
interval: daily
allow:
- dependency-type: all
open-pull-requests-limit: 10
- package-ecosystem: pip
directory: /
@ -53,6 +58,7 @@ updates:
interval: daily
allow:
- dependency-type: all
open-pull-requests-limit: 10
- package-ecosystem: pip
directory: /Library/Homebrew/formula-analytics/
@ -60,3 +66,4 @@ updates:
interval: daily
allow:
- dependency-type: all
open-pull-requests-limit: 10

View File

@ -57,7 +57,7 @@ jobs:
zizmor --format sarif . > results.sarif || true
- name: Upload SARIF file
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with:
name: results.sarif
path: results.sarif
@ -72,13 +72,13 @@ jobs:
security-events: write
steps:
- name: Download SARIF file
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
name: results.sarif
path: results.sarif
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
with:
sarif_file: results.sarif
category: zizmor

View File

@ -34,7 +34,7 @@ jobs:
test-bot: true
- name: Cache Bundler RubyGems
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}

View File

@ -28,7 +28,7 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
with:
languages: ruby
config: |
@ -36,4 +36,4 @@ jobs:
- Library/Homebrew/vendor
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11

View File

@ -45,7 +45,7 @@ jobs:
run: git fetch origin master
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
with:
cache-binary: false
@ -120,7 +120,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build Docker image
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
with:
context: .
load: true

View File

@ -53,7 +53,7 @@ jobs:
run: vale docs/
- name: Install Ruby
uses: ruby/setup-ruby@d781c1b4ed31764801bfae177617bb0446f5ef8d # v1.218.0
uses: ruby/setup-ruby@277ba2a127aba66d45bad0fa2dc56f80dbfedffa # v1.222.0
with:
bundler-cache: true
working-directory: docs

View File

@ -133,12 +133,12 @@ jobs:
fi
- name: Generate build provenance
uses: actions/attest-build-provenance@520d128f165991a6c774bcb264f323e3d70747f4 # v2.2.0
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
with:
subject-path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
- name: Upload installer to GitHub Actions
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
with:
name: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
@ -160,7 +160,7 @@ jobs:
name: macos-15-arm64
steps:
- name: Download installer from GitHub Actions
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
name: "${{ needs.build.outputs.installer_path }}"
@ -213,7 +213,7 @@ jobs:
contents: write
steps:
- name: Download installer from GitHub Actions
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
with:
name: "${{ needs.build.outputs.installer_path }}"

View File

@ -43,7 +43,7 @@ jobs:
persist-credentials: false
- name: Install Ruby
uses: ruby/setup-ruby@d781c1b4ed31764801bfae177617bb0446f5ef8d # v1.218.0
uses: ruby/setup-ruby@277ba2a127aba66d45bad0fa2dc56f80dbfedffa # v1.222.0
with:
bundler-cache: true
working-directory: rubydoc

View File

@ -50,7 +50,7 @@ jobs:
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
- name: Cache Bundler RubyGems
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}

View File

@ -40,7 +40,7 @@ jobs:
test-bot: false
- name: Cache Bundler RubyGems
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-syntax-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -53,7 +53,7 @@ jobs:
run: brew install shellcheck shfmt
- name: Cache style cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ~/.cache/Homebrew/style
key: syntax-style-cache-${{ github.sha }}
@ -92,7 +92,7 @@ jobs:
test-bot: true
- name: Cache Bundler RubyGems
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-tap-syntax-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -102,7 +102,7 @@ jobs:
run: brew install-bundler-gems --groups=style
- name: Cache style cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ~/.cache/Homebrew/style
key: tap-syntax-style-cache-${{ github.sha }}
@ -242,6 +242,7 @@ jobs:
name: ${{ matrix.name }}
needs: syntax
runs-on: ${{ matrix.runs-on }}
container: ${{ matrix.container }}
strategy:
matrix:
include:
@ -259,7 +260,8 @@ jobs:
runs-on: ubuntu-22.04
- name: tests (Ubuntu 20.04)
test-flags: --coverage
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
container: ghcr.io/homebrew/ubuntu20.04:latest
- name: tests (macOS 13 x86_64)
test-flags: --coverage
runs-on: macos-13
@ -279,7 +281,7 @@ jobs:
test-bot: false
- name: Cache Bundler RubyGems
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ matrix.runs-on }}-tests-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
@ -294,7 +296,7 @@ jobs:
run: mkdir tests
- name: Cache parallel tests log
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: tests
key: ${{ runner.os }}-${{ matrix.test-flags }}-parallel_runtime_rspec-${{ github.sha }}
@ -341,14 +343,14 @@ jobs:
filenames=$(find Library/Homebrew/test/junit -name 'rspec*.xml' -print | tr '\n' ',')
echo "filenames=${filenames%,}" >> "$GITHUB_OUTPUT"
- uses: codecov/test-results-action@44ecb3a270cd942bdf0fa8f2ce14cb32493e810a # v1.0.3
- uses: codecov/test-results-action@5c441a7bcc06f8706cde90192857d337c5dab8a6 # v1.0.4
with:
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
files: ${{ steps.junit_xml.outputs.filenames }}
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}
- uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
- uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0
with:
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
files: Library/Homebrew/test/coverage/coverage.xml
@ -364,12 +366,15 @@ jobs:
strategy:
matrix:
include:
- name: test default formula (Ubuntu 24.04)
runs-on: ubuntu-latest
container: ghcr.io/homebrew/ubuntu24.04:latest
- name: test default formula (Ubuntu 22.04)
runs-on: ubuntu-latest
container: ghcr.io/homebrew/ubuntu22.04:master
- name: test default formula (Ubuntu 20.04)
runs-on: ubuntu-latest
container: ghcr.io/homebrew/ubuntu20.04
container: ghcr.io/homebrew/ubuntu20.04:latest
- name: test default formula (macOS 13 x86_64)
runs-on: macos-13
- name: test default formula (macOS 15 arm64)
@ -415,7 +420,7 @@ jobs:
- name: Cache Homebrew Bundler RubyGems
id: cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2
with:
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}

View File

@ -92,7 +92,7 @@ jobs:
fi
- name: Generate push token
uses: actions/create-github-app-token@67e27a7eb7db372a1c61a7f9bdab8699e9ee57f7 # v1.11.3
uses: actions/create-github-app-token@21cfef2b496dd8ef5b904c159339626a10ad380e # v1.11.6
id: app-token
if: github.event_name == 'workflow_dispatch'
with:

1
.gitignore vendored
View File

@ -90,6 +90,7 @@
**/vendor/bundle/ruby/*/gems/json_schemer-*/
**/vendor/bundle/ruby/*/gems/kramdown-*/
**/vendor/bundle/ruby/*/gems/language_server-protocol-*/
**/vendor/bundle/ruby/*/gems/lint_roller-*/
**/vendor/bundle/ruby/*/gems/logger-*/
**/vendor/bundle/ruby/*/gems/method_source-*/
**/vendor/bundle/ruby/*/gems/mini_portile2-*/

View File

@ -1,9 +1,14 @@
---
plugins:
- rubocop-md:
plugin_class_name: RuboCop::Markdown::Plugin
- rubocop-performance:
plugin_class_name: RuboCop::Performance::Plugin
- rubocop-rspec:
plugin_class_name: RuboCop::RSpec::Plugin
require:
- ./Homebrew/rubocops.rb
- rubocop-md
- rubocop-performance
- rubocop-rspec
- rubocop-sorbet
inherit_mode:
@ -255,6 +260,11 @@ RSpec/Focus:
RSpec/MessageSpies:
EnforcedStyle: receive
# These are legacy violations that we should try to fix.
Sorbet/AllowIncompatibleOverride:
Exclude:
- "Homebrew/livecheck/strategy/*.rb"
# Try getting rid of these.
Sorbet/ConstantsFromStrings:
Enabled: false

View File

@ -10,13 +10,13 @@ GEM
bindata (2.5.0)
coderay (1.1.3)
concurrent-ruby (1.3.5)
diff-lcs (1.5.1)
diff-lcs (1.6.0)
docile (1.4.1)
elftools (1.3.1)
bindata (~> 2)
erubi (1.13.1)
hana (1.3.7)
json (2.9.1)
json (2.10.2)
json_schemer (2.4.0)
bigdecimal
hana (~> 1.3)
@ -25,12 +25,13 @@ GEM
kramdown (2.5.1)
rexml (>= 3.3.9)
language_server-protocol (3.17.0.4)
logger (1.6.5)
lint_roller (1.1.0)
logger (1.6.6)
method_source (1.1.0)
minitest (5.25.4)
netrc (0.11.0)
parallel (1.26.3)
parallel_tests (4.9.0)
parallel_tests (5.1.0)
parallel
parser (3.3.7.1)
ast (~> 2.4.1)
@ -46,14 +47,15 @@ GEM
pycall (1.5.2)
racc (1.8.1)
rainbow (3.1.1)
rbi (0.2.4)
rbi (0.3.0)
prism (~> 1.0)
rbs (>= 3.4.4)
sorbet-runtime (>= 0.5.9204)
rbs (3.8.1)
logger
redcarpet (3.6.0)
redcarpet (3.6.1)
regexp_parser (2.10.0)
rexml (3.4.0)
rexml (3.4.1)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
@ -75,9 +77,10 @@ GEM
rspec-support (3.13.2)
rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0)
rubocop (1.71.2)
rubocop (1.73.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
@ -85,18 +88,22 @@ GEM
rubocop-ast (>= 1.38.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.38.0)
rubocop-ast (1.38.1)
parser (>= 3.3.1.0)
rubocop-md (1.2.4)
rubocop (>= 1.45)
rubocop-performance (1.23.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (3.4.0)
rubocop (~> 1.61)
rubocop-sorbet (0.8.7)
rubocop-md (2.0.0)
lint_roller (~> 1.1)
rubocop (>= 1.72.1)
rubocop-performance (1.24.0)
lint_roller (~> 1.1)
rubocop (>= 1.72.1, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rspec (3.5.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-sorbet (0.9.0)
lint_roller (~> 1.1)
rubocop (>= 1)
ruby-lsp (0.23.9)
ruby-lsp (0.23.11)
language_server-protocol (~> 3.17.0)
prism (>= 1.2, < 2.0)
rbs (>= 3, < 4)
@ -114,23 +121,23 @@ GEM
simplecov-html (0.13.1)
simplecov_json_formatter (0.1.4)
simpleidn (0.2.3)
sorbet (0.5.11812)
sorbet-static (= 0.5.11812)
sorbet-runtime (0.5.11812)
sorbet-static (0.5.11812-aarch64-linux)
sorbet-static (0.5.11812-universal-darwin)
sorbet-static (0.5.11812-x86_64-linux)
sorbet-static-and-runtime (0.5.11812)
sorbet (= 0.5.11812)
sorbet-runtime (= 0.5.11812)
spoom (1.5.3)
sorbet (0.5.11915)
sorbet-static (= 0.5.11915)
sorbet-runtime (0.5.11915)
sorbet-static (0.5.11915-aarch64-linux)
sorbet-static (0.5.11915-universal-darwin)
sorbet-static (0.5.11915-x86_64-linux)
sorbet-static-and-runtime (0.5.11915)
sorbet (= 0.5.11915)
sorbet-runtime (= 0.5.11915)
spoom (1.6.0)
erubi (>= 1.10.0)
prism (>= 0.28.0)
rbi (>= 0.2.3)
sorbet-static-and-runtime (>= 0.5.10187)
thor (>= 0.19.2)
stackprof (0.2.27)
tapioca (0.16.9)
tapioca (0.16.11)
benchmark
bundler (>= 2.2.25)
netrc (>= 0.11.0)

View File

@ -1,30 +0,0 @@
# typed: strict
# frozen_string_literal: true
# This module provides methods to define specialized attributes.
# Method stubs are generated by the {Tapioca::Compilers::Attrables} compiler.
# @note The compiler is fragile, and must be updated if the filename changes, if methods are added or removed,
# or if a method's arity changes.
module Attrable
extend T::Helpers
requires_ancestor { Module }
sig { params(attrs: Symbol).void }
def attr_predicate(*attrs)
attrs.each do |attr|
define_method attr do
instance_variable_get("@#{attr.to_s.sub(/\?$/, "")}") == true
end
end
end
sig { params(attrs: Symbol).void }
def attr_rw(*attrs)
attrs.each do |attr|
define_method attr do |val = nil|
val.nil? ? instance_variable_get(:"@#{attr}") : instance_variable_set(:"@#{attr}", val)
end
end
end
end

View File

@ -2,10 +2,8 @@
# frozen_string_literal: true
class BottleSpecification
extend Attrable
RELOCATABLE_CELLARS = [:any, :any_skip_relocation].freeze
attr_rw :rebuild
attr_accessor :tap
attr_reader :collector, :root_url_specs, :repository
@ -17,6 +15,11 @@ class BottleSpecification
@root_url_specs = {}
end
sig { params(val: Integer).returns(T.nilable(Integer)) }
def rebuild(val = T.unsafe(nil))
val.nil? ? @rebuild : @rebuild = val
end
def root_url(var = nil, specs = {})
if var.nil?
@root_url ||= if (github_packages_url = GitHubPackages.root_url_if_match(Homebrew::EnvConfig.bottle_domain))

View File

@ -969,13 +969,6 @@ then
export HOMEBREW_DEVELOPER_COMMAND="1"
fi
# Provide a (temporary, undocumented) way to disable Sorbet globally if needed
# to avoid reverting the above.
if [[ -n "${HOMEBREW_NO_SORBET_RUNTIME}" ]]
then
unset HOMEBREW_SORBET_RUNTIME
fi
if [[ -n "${HOMEBREW_DEVELOPER_COMMAND}" && -z "${HOMEBREW_DEVELOPER}" ]]
then
if [[ -z "${HOMEBREW_DEV_CMD_RUN}" ]]
@ -999,6 +992,13 @@ then
export HOMEBREW_SORBET_RUNTIME="1"
fi
# Provide a (temporary, undocumented) way to disable Sorbet globally if needed
# to avoid reverting the above.
if [[ -n "${HOMEBREW_NO_SORBET_RUNTIME}" ]]
then
unset HOMEBREW_SORBET_RUNTIME
fi
if [[ -f "${HOMEBREW_LIBRARY}/Homebrew/cmd/${HOMEBREW_COMMAND}.sh" ]]
then
HOMEBREW_BASH_COMMAND="${HOMEBREW_LIBRARY}/Homebrew/cmd/${HOMEBREW_COMMAND}.sh"

View File

@ -148,12 +148,15 @@ class Build
# https://github.com/Homebrew/homebrew-core/pull/87470
TZ: "UTC0",
) do
formula.patch
if args.git?
formula.selective_patch(is_data: false)
system "git", "init"
system "git", "add", "-A"
formula.selective_patch(is_data: true)
else
formula.patch
end
if args.interactive?
ohai "Entering interactive mode..."
puts <<~EOS
@ -185,6 +188,8 @@ class Build
# Find and link metafiles
formula.prefix.install_metafiles formula.buildpath
formula.prefix.install_metafiles formula.libexec if formula.libexec.exist?
normalize_pod2man_outputs!(formula)
end
end
end
@ -214,6 +219,11 @@ class Build
rescue
raise "#{formula.opt_prefix} not present or broken\nPlease reinstall #{formula.full_name}. Sorry :("
end
def normalize_pod2man_outputs!(formula)
keg = Keg.new(formula.prefix)
keg.normalize_pod2man_outputs!
end
end
begin

View File

@ -22,6 +22,9 @@ require "cask/artifact/prefpane"
require "cask/artifact/qlplugin"
require "cask/artifact/mdimporter"
require "cask/artifact/screen_saver"
require "cask/artifact/bashcompletion"
require "cask/artifact/fishcompletion"
require "cask/artifact/zshcompletion"
require "cask/artifact/service"
require "cask/artifact/stage_only"
require "cask/artifact/suite"

View File

@ -1,7 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
require "extend/object/deep_dup"
module Cask

View File

@ -411,7 +411,7 @@ module Cask
next
end
if MacOS.undeletable?(resolved_path)
if undeletable?(resolved_path)
opoo "Skipping #{Formatter.identifier(action)} for undeletable path '#{path}'."
next
end
@ -538,6 +538,10 @@ module Cask
recursive_rmdir(*resolved_paths, **kwargs)
end
end
def undeletable?(target); end
end
end
end
require "extend/os/cask/artifact/abstract_uninstall"

View File

@ -0,0 +1,25 @@
# typed: strict
# frozen_string_literal: true
require "cask/artifact/shellcompletion"
module Cask
module Artifact
# Artifact corresponding to the `bash_completion` stanza.
class BashCompletion < ShellCompletion
sig { params(target: T.any(String, Pathname)).returns(Pathname) }
def resolve_target(target)
name = if File.extname(target).nil?
target
else
new_name = File.basename(target, File.extname(target))
odebug "Renaming completion #{target} to #{new_name}"
new_name
end
config.bash_completion/name
end
end
end
end

View File

@ -0,0 +1,25 @@
# typed: strict
# frozen_string_literal: true
require "cask/artifact/shellcompletion"
module Cask
module Artifact
# Artifact corresponding to the `fish_completion` stanza.
class FishCompletion < ShellCompletion
sig { params(target: T.any(String, Pathname)).returns(Pathname) }
def resolve_target(target)
name = if target.to_s.end_with? ".fish"
target
else
new_name = "#{File.basename(target, File.extname(target))}.fish"
odebug "Renaming completion #{target} to #{new_name}"
new_name
end
config.fish_completion/name
end
end
end
end

View File

@ -72,6 +72,7 @@ module Cask
private
ALT_NAME_ATTRIBUTE = "com.apple.metadata:kMDItemAlternateNames"
private_constant :ALT_NAME_ATTRIBUTE
# Try to make the asset searchable under the target name. Spotlight
# respects this attribute for many filetypes, but ignores it for App

View File

@ -0,0 +1,20 @@
# typed: strict
# frozen_string_literal: true
require "cask/artifact/symlinked"
module Cask
module Artifact
class ShellCompletion < Symlinked
sig { params(cask: Cask, source: T.any(String, Pathname)).returns(ShellCompletion) }
def self.from_args(cask, source)
new(cask, source)
end
sig { params(_: T.any(String, Pathname)).returns(Pathname) }
def resolve_target(_)
raise CaskInvalidError, "Shell completion without shell info"
end
end
end
end

View File

@ -62,7 +62,7 @@ module Cask
end
ohai "Linking #{self.class.english_name} '#{source.basename}' to '#{target}'"
create_filesystem_link(command:)
create_filesystem_link(command)
end
def unlink(command: nil, **)
@ -72,14 +72,10 @@ module Cask
Utils.gain_permissions_remove(target, command:)
end
def create_filesystem_link(command: nil)
Utils.gain_permissions_mkpath(target.dirname, command:)
command.run! "/bin/ln", args: ["-h", "-f", "-s", "--", source, target],
sudo: !target.dirname.writable?
add_altname_metadata(source, target.basename, command:)
end
sig { params(command: T.class_of(SystemCommand)).void }
def create_filesystem_link(command); end
end
end
end
require "extend/os/cask/artifact/symlinked"

View File

@ -0,0 +1,25 @@
# typed: strict
# frozen_string_literal: true
require "cask/artifact/shellcompletion"
module Cask
module Artifact
# Artifact corresponding to the `zsh_completion` stanza.
class ZshCompletion < ShellCompletion
sig { params(target: T.any(String, Pathname)).returns(Pathname) }
def resolve_target(target)
name = if target.to_s.start_with? "_"
target
else
new_name = "_#{File.basename(target, File.extname(target))}"
odebug "Renaming completion #{target} to #{new_name}"
new_name
end
config.zsh_completion/name
end
end
end
end

View File

@ -1,7 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
require "cask/denylist"
require "cask/download"
require "digest"
@ -19,7 +18,6 @@ module Cask
class Audit
include SystemCommand::Mixin
include ::Utils::Curl
extend Attrable
sig { returns(Cask) }
attr_reader :cask
@ -27,11 +25,16 @@ module Cask
sig { returns(T.nilable(Download)) }
attr_reader :download
attr_predicate :new_cask?, :strict?, :signing?, :online?, :token_conflicts?
sig {
params(
cask: ::Cask::Cask, download: T::Boolean, quarantine: T::Boolean, token_conflicts: T.nilable(T::Boolean),
online: T.nilable(T::Boolean), strict: T.nilable(T::Boolean), signing: T.nilable(T::Boolean),
new_cask: T.nilable(T::Boolean), only: T::Array[String], except: T::Array[String]
).void
}
def initialize(
cask,
download: nil, quarantine: nil,
download: false, quarantine: false,
token_conflicts: nil, online: nil, strict: nil, signing: nil,
new_cask: nil, only: [], except: []
)
@ -42,7 +45,7 @@ module Cask
token_conflicts = new_cask if token_conflicts.nil?
# `online` and `signing` imply `download`
download = online || signing if download.nil?
download ||= online || signing
@cask = cask
@download = Download.new(cask, quarantine:) if download
@ -51,18 +54,34 @@ module Cask
@signing = signing
@new_cask = new_cask
@token_conflicts = token_conflicts
@only = only || []
@except = except || []
@only = only
@except = except
end
sig { returns(T::Boolean) }
def new_cask? = !!@new_cask
sig { returns(T::Boolean) }
def online? =!!@online
sig { returns(T::Boolean) }
def signing? = !!@signing
sig { returns(T::Boolean) }
def strict? = !!@strict
sig { returns(T::Boolean) }
def token_conflicts? = !!@token_conflicts
sig { returns(::Cask::Audit) }
def run!
only_audits = @only
except_audits = @except
private_methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name|
name = audit_method_name.delete_prefix("audit_")
next if !only_audits.empty? && only_audits&.exclude?(name)
next if except_audits&.include?(name)
next if !only_audits.empty? && only_audits.exclude?(name)
next if except_audits.include?(name)
send(audit_method_name)
end
@ -292,6 +311,7 @@ module Cask
end
LIVECHECK_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#stanza-livecheck"
private_constant :LIVECHECK_REFERENCE_URL
sig { params(livecheck_result: T.any(NilClass, T::Boolean, Symbol)).void }
def audit_hosting_with_livecheck(livecheck_result: audit_livecheck_version)
@ -317,6 +337,7 @@ module Cask
end
SOURCEFORGE_OSDN_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#sourceforgeosdn-urls"
private_constant :SOURCEFORGE_OSDN_REFERENCE_URL
sig { void }
def audit_download_url_format
@ -339,6 +360,7 @@ module Cask
end
VERIFIED_URL_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#when-url-and-homepage-domains-differ-add-verified"
private_constant :VERIFIED_URL_REFERENCE_URL
sig { void }
def audit_unnecessary_verified
@ -928,6 +950,9 @@ module Cask
return unless (url = cask.livecheck.url)
return if url.is_a?(Symbol)
options = cask.livecheck.options
return if options.post_form || options.post_json
validate_url_for_https_availability(
url, "livecheck URL",
check_content: true,

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true
require "cask/audit"
@ -6,22 +6,50 @@ require "cask/audit"
module Cask
# Helper class for auditing all available languages of a cask.
class Auditor
def self.audit(cask, **options)
new(cask, **options).audit
# TODO: use argument forwarding (...) when Sorbet supports it in strict mode
sig {
params(
cask: ::Cask::Cask, audit_download: T::Boolean, audit_online: T.nilable(T::Boolean),
audit_strict: T.nilable(T::Boolean), audit_signing: T.nilable(T::Boolean),
audit_token_conflicts: T.nilable(T::Boolean), audit_new_cask: T.nilable(T::Boolean), quarantine: T::Boolean,
any_named_args: T::Boolean, language: T.nilable(String), only: T::Array[String], except: T::Array[String]
).returns(T::Set[String])
}
def self.audit(
cask, audit_download: false, audit_online: nil, audit_strict: nil, audit_signing: nil,
audit_token_conflicts: nil, audit_new_cask: nil, quarantine: false, any_named_args: false, language: nil,
only: [], except: []
)
new(
cask, audit_download:, audit_online:, audit_strict:, audit_signing:, audit_token_conflicts:,
audit_new_cask:, quarantine:, any_named_args:, language:, only:, except:
).audit
end
attr_reader :cask, :language
sig { returns(::Cask::Cask) }
attr_reader :cask
sig { returns(T.nilable(String)) }
attr_reader :language
sig {
params(
cask: ::Cask::Cask, audit_download: T::Boolean, audit_online: T.nilable(T::Boolean),
audit_strict: T.nilable(T::Boolean), audit_signing: T.nilable(T::Boolean),
audit_token_conflicts: T.nilable(T::Boolean), audit_new_cask: T.nilable(T::Boolean), quarantine: T::Boolean,
any_named_args: T::Boolean, language: T.nilable(String), only: T::Array[String], except: T::Array[String]
).void
}
def initialize(
cask,
audit_download: nil,
audit_download: false,
audit_online: nil,
audit_strict: nil,
audit_signing: nil,
audit_token_conflicts: nil,
audit_new_cask: nil,
quarantine: nil,
any_named_args: nil,
quarantine: false,
any_named_args: false,
language: nil,
only: [],
except: []
@ -42,17 +70,18 @@ module Cask
LANGUAGE_BLOCK_LIMIT = 10
sig { returns(T::Set[String]) }
def audit
errors = Set.new
if !language && language_blocks
sample_languages = if language_blocks.length > LANGUAGE_BLOCK_LIMIT && !@audit_new_cask
sample_keys = language_blocks.keys.sample(LANGUAGE_BLOCK_LIMIT)
if !language && (blocks = language_blocks)
sample_languages = if blocks.length > LANGUAGE_BLOCK_LIMIT && !@audit_new_cask
sample_keys = T.must(blocks.keys.sample(LANGUAGE_BLOCK_LIMIT))
ohai "Auditing a sample of available languages for #{cask}: " \
"#{sample_keys.map { |lang| lang[0].to_s }.to_sentence}"
language_blocks.select { |k| sample_keys.include?(k) }
blocks.select { |k| sample_keys.include?(k) }
else
language_blocks
blocks
end
sample_languages.each_key do |l|
@ -74,14 +103,16 @@ module Cask
private
sig { params(audit: T.nilable(Audit)).returns(T::Boolean) }
def output_summary?(audit = nil)
return true if @any_named_args.present?
return true if @audit_strict.present?
return false if audit.blank?
return true if @any_named_args
return true if @audit_strict
return false if audit.nil?
audit.errors?
end
sig { params(languages: T::Array[String]).returns(::Cask::Audit) }
def audit_languages(languages)
original_config = cask.config
localized_config = original_config.merge(Config.new(explicit: { languages: }))
@ -92,6 +123,7 @@ module Cask
cask.config = original_config
end
sig { params(cask: ::Cask::Cask).returns(::Cask::Audit) }
def audit_cask_instance(cask)
audit = Audit.new(
cask,
@ -108,6 +140,7 @@ module Cask
audit.run!
end
sig { returns(T.nilable(T::Hash[T::Array[String], T.proc.returns(T.untyped)])) }
def language_blocks
cask.instance_variable_get(:@dsl).instance_variable_get(:@language_blocks)
end

View File

@ -1,7 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
require "bundle_version"
require "cask/cask_loader"
require "cask/config"
@ -15,7 +14,6 @@ module Cask
# An instance of a cask.
class Cask
extend Forwardable
extend Attrable
extend APIHashable
include Metadata
@ -32,8 +30,6 @@ module Cask
attr_reader :sourcefile_path, :source, :default_config, :loader
attr_accessor :download, :allow_reassignment
attr_predicate :loaded_from_api?
def self.all(eval_all: false)
if !eval_all && !Homebrew::EnvConfig.eval_all?
raise ArgumentError, "Cask::Cask#all cannot be used without `--eval-all` or HOMEBREW_EVAL_ALL"
@ -92,6 +88,9 @@ module Cask
end
end
sig { returns(T::Boolean) }
def loaded_from_api? = @loaded_from_api
# An old name for the cask.
sig { returns(T::Array[String]) }
def old_tokens
@ -114,12 +113,11 @@ module Cask
return unless @block
@dsl.instance_eval(&@block)
@dsl.add_implicit_macos_dependency
@dsl.language_eval
end
::Cask::DSL::DSL_METHODS.each do |method_name|
define_method(method_name) { |*args, &block| @dsl.send(method_name, *args, &block) }
end
def_delegators :@dsl, *::Cask::DSL::DSL_METHODS
sig { params(caskroom_path: Pathname).returns(T::Array[[String, String]]) }
def timestamped_versions(caskroom_path: self.caskroom_path)

View File

@ -1,67 +0,0 @@
# typed: strict
module Cask
class Cask
def appdir; end
def artifacts; end
def auto_updates; end
def caveats; end
def conflicts_with; end
def container; end
def depends_on; end
def desc; end
def discontinued?; end
def deprecated?; end
def deprecation_date; end
def deprecation_reason; end
def deprecation_replacement; end
def disabled?; end
def disable_date; end
def disable_reason; end
def disable_replacement; end
def homepage; end
def language; end
def languages; end
def livecheck; end
def livecheck_defined?; end
def livecheckable?; end
def name; end
def on_system_blocks_exist?; end
sig { returns(T.nilable(MacOSVersion)) }
def on_system_block_min_os; end
def sha256; end
def staged_path; end
sig { returns(T.nilable(::Cask::URL)) }
def url; end
def version; end
end
end

View File

@ -176,6 +176,21 @@ module Cask
@manpagedir ||= T.let(HOMEBREW_PREFIX/"share/man", T.nilable(Pathname))
end
sig { returns(Pathname) }
def bash_completion
@bash_completion ||= T.let(HOMEBREW_PREFIX/"etc/bash_completion.d", T.nilable(Pathname))
end
sig { returns(Pathname) }
def zsh_completion
@zsh_completion ||= T.let(HOMEBREW_PREFIX/"share/zsh/site-functions", T.nilable(Pathname))
end
sig { returns(Pathname) }
def fish_completion
@fish_completion ||= T.let(HOMEBREW_PREFIX/"share/fish/vendor_completions.d", T.nilable(Pathname))
end
sig { returns(T::Array[String]) }
def languages
[

View File

@ -1,7 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
require "locale"
require "lazy_object"
require "livecheck"
@ -54,6 +53,9 @@ module Cask
Artifact::Suite,
Artifact::VstPlugin,
Artifact::Vst3Plugin,
Artifact::ZshCompletion,
Artifact::FishCompletion,
Artifact::BashCompletion,
Artifact::Uninstall,
Artifact::Zap,
].freeze
@ -105,19 +107,36 @@ module Cask
*ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
]).freeze
extend Attrable
include OnSystem::MacOSOnly
include OnSystem::MacOSAndLinux
attr_reader :cask, :token, :deprecation_date, :deprecation_reason, :deprecation_replacement, :disable_date,
:disable_reason, :disable_replacement, :on_system_block_min_os
attr_predicate :deprecated?, :disabled?, :livecheck_defined?, :on_system_blocks_exist?, :depends_on_set_in_block?
def initialize(cask)
@cask = cask
@depends_on_set_in_block = T.let(false, T::Boolean)
@deprecated = T.let(false, T::Boolean)
@disabled = T.let(false, T::Boolean)
@livecheck_defined = T.let(false, T::Boolean)
@on_system_blocks_exist = T.let(false, T::Boolean)
@token = cask.token
end
sig { returns(T::Boolean) }
def depends_on_set_in_block? = @depends_on_set_in_block
sig { returns(T::Boolean) }
def deprecated? = @deprecated
sig { returns(T::Boolean) }
def disabled? = @disabled
sig { returns(T::Boolean) }
def livecheck_defined? = @livecheck_defined
sig { returns(T::Boolean) }
def on_system_blocks_exist? = @on_system_blocks_exist
# Specifies the cask's name.
#
# NOTE: Multiple names can be specified.
@ -287,6 +306,7 @@ module Cask
#
# @see DSL::Version
# @api public
sig { params(arg: T.nilable(T.any(String, Symbol))).returns(T.nilable(DSL::Version)) }
def version(arg = nil)
set_unique_stanza(:version, arg.nil?) do
if !arg.is_a?(String) && arg != :latest
@ -310,18 +330,36 @@ module Cask
# For architecture-dependent downloads:
#
# ```ruby
# sha256 arm: "7bdb497080ffafdfd8cc94d8c62b004af1be9599e865e5555e456e2681e150ca",
# intel: "b3c1c2442480a0219b9e05cf91d03385858c20f04b764ec08a3fa83d1b27e7b2"
# sha256 arm: "7bdb497080ffafdfd8cc94d8c62b004af1be9599e865e5555e456e2681e150ca",
# x86_64: "b3c1c2442480a0219b9e05cf91d03385858c20f04b764ec08a3fa83d1b27e7b2"
# x86_64_linux: "1a2aee7f1ddc999993d4d7d42a150c5e602bc17281678050b8ed79a0500cc90f"
# arm64_linux: "bd766af7e692afceb727a6f88e24e6e68d9882aeb3e8348412f6c03d96537c75"
# ```
#
# @api public
def sha256(arg = nil, arm: nil, intel: nil)
should_return = arg.nil? && arm.nil? && intel.nil?
sig {
params(
arg: T.nilable(T.any(String, Symbol)),
arm: T.nilable(String),
intel: T.nilable(String),
x86_64: T.nilable(String),
x86_64_linux: T.nilable(String),
arm64_linux: T.nilable(String),
).returns(T.nilable(T.any(Symbol, Checksum)))
}
def sha256(arg = nil, arm: nil, intel: nil, x86_64: nil, x86_64_linux: nil, arm64_linux: nil)
should_return = arg.nil? && arm.nil? && (intel.nil? || x86_64.nil?) && x86_64_linux.nil? && arm64_linux.nil?
x86_64 ||= intel if intel.present? && x86_64.nil?
set_unique_stanza(:sha256, should_return) do
@on_system_blocks_exist = true if arm.present? || intel.present?
if arm.present? || x86_64.present? || x86_64_linux.present? || arm64_linux.present?
@on_system_blocks_exist = true
end
val = arg || on_arch_conditional(arm:, intel:)
val = arg || on_system_conditional(
macos: on_arch_conditional(arm:, intel: x86_64),
linux: on_arch_conditional(arm: arm64_linux, intel: x86_64_linux),
)
case val
when :no_check
val
@ -352,6 +390,31 @@ module Cask
end
end
# Sets the cask's os strings.
#
# ### Example
#
# ```ruby
# os macos: "darwin", linux: "tux"
# ```
#
# @api public
sig {
params(
macos: T.nilable(String),
linux: T.nilable(String),
).returns(T.nilable(String))
}
def os(macos: nil, linux: nil)
should_return = macos.nil? && linux.nil?
set_unique_stanza(:os, should_return) do
@on_system_blocks_exist = true
on_system_conditional(macos:, linux:)
end
end
# Declare dependencies and requirements for a cask.
#
# NOTE: Multiple dependencies can be specified.
@ -370,6 +433,13 @@ module Cask
@depends_on
end
# @api private
def add_implicit_macos_dependency
return if @depends_on.present? && @depends_on.macos.present?
depends_on macos: ">= :#{MacOSVersion::SYMBOLS.key MacOSVersion::SYMBOLS.values.min}"
end
# Declare conflicts that keep a cask from installing or working correctly.
#
# @api public
@ -382,6 +452,7 @@ module Cask
@artifacts ||= ArtifactSet.new
end
sig { returns(Pathname) }
def caskroom_path
cask.caskroom_path
end
@ -389,6 +460,7 @@ module Cask
# The staged location for this cask, including version number.
#
# @api public
sig { returns(Pathname) }
def staged_path
return @staged_path if @staged_path
@ -520,9 +592,15 @@ module Cask
true
end
sig { returns(T.nilable(MacOSVersion)) }
def os_version
nil
end
# The directory `app`s are installed into.
#
# @api public
sig { returns(T.any(Pathname, String)) }
def appdir
return HOMEBREW_CASK_APPDIR_PLACEHOLDER if Cask.generating_hash?

View File

@ -1,8 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
module Cask
class DSL
# Class corresponding to the `caveats` stanza.
@ -15,10 +13,6 @@ module Cask
# to the output by the caller, but that feature is only for the
# convenience of cask authors.
class Caveats < Base
extend Attrable
attr_predicate :discontinued?
def initialize(*args)
super
@built_in_caveats = {}
@ -37,6 +31,9 @@ module Cask
private_class_method :caveat
sig { returns(T::Boolean) }
def discontinued? = @discontinued
sig { returns(String) }
def to_s
(@custom_caveats + @built_in_caveats.values).join("\n")

View File

@ -1,7 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
require "formula_installer"
require "unpack_strategy"
require "utils/topological_hash"
@ -17,8 +16,15 @@ require "cgi"
module Cask
# Installer for a {Cask}.
class Installer
extend Attrable
sig {
params(
cask: ::Cask::Cask, command: T::Class[SystemCommand], force: T::Boolean, adopt: T::Boolean,
skip_cask_deps: T::Boolean, binaries: T::Boolean, verbose: T::Boolean, zap: T::Boolean,
require_sha: T::Boolean, upgrade: T::Boolean, reinstall: T::Boolean, installed_as_dependency: T::Boolean,
installed_on_request: T::Boolean, quarantine: T::Boolean, verify_download_integrity: T::Boolean,
quiet: T::Boolean
).void
}
def initialize(cask, command: SystemCommand, force: false, adopt: false,
skip_cask_deps: false, binaries: true, verbose: false,
zap: false, require_sha: false, upgrade: false, reinstall: false,
@ -42,9 +48,44 @@ module Cask
@quiet = quiet
end
attr_predicate :binaries?, :force?, :adopt?, :skip_cask_deps?, :require_sha?,
:reinstall?, :upgrade?, :verbose?, :zap?, :installed_as_dependency?, :installed_on_request?,
:quarantine?, :quiet?
sig { returns(T::Boolean) }
def adopt? = @adopt
sig { returns(T::Boolean) }
def binaries? = @binaries
sig { returns(T::Boolean) }
def force? = @force
sig { returns(T::Boolean) }
def installed_as_dependency? = @installed_as_dependency
sig { returns(T::Boolean) }
def installed_on_request? = @installed_on_request
sig { returns(T::Boolean) }
def quarantine? = @quarantine
sig { returns(T::Boolean) }
def quiet? = @quiet
sig { returns(T::Boolean) }
def reinstall? = @reinstall
sig { returns(T::Boolean) }
def require_sha? = @require_sha
sig { returns(T::Boolean) }
def skip_cask_deps? = @skip_cask_deps
sig { returns(T::Boolean) }
def upgrade? = @upgrade
sig { returns(T::Boolean) }
def verbose? = @verbose
sig { returns(T::Boolean) }
def zap? = @zap
def self.caveats(cask)
odebug "Printing caveats"
@ -383,10 +424,13 @@ on_request: true)
cask_or_formula,
adopt: adopt?,
binaries: binaries?,
verbose: verbose?,
force: false,
installed_as_dependency: true,
installed_on_request: false,
force: false,
quarantine: quarantine?,
quiet: quiet?,
require_sha: require_sha?,
verbose: verbose?,
).install
else
Homebrew::Install.perform_preinstall_checks_once

View File

@ -1,11 +1,9 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true
module OS
module Mac
module_function
SYSTEM_DIRS = [
SYSTEM_DIRS = T.let([
"/",
"/Applications",
"/Applications/Utilities",
@ -234,14 +232,13 @@ module OS
"/var/spool/mail",
"/var/tmp",
]
.map(&method(:Pathname))
.to_set
.freeze
.to_set { Pathname(_1) }
.freeze, T::Set[Pathname])
private_constant :SYSTEM_DIRS
# TODO: There should be a way to specify a containing
# directory under which nothing can be deleted.
UNDELETABLE_PATHS = [
UNDELETABLE_PATHS = T.let([
"~/",
"~/Applications",
"~/Applications/.localized",
@ -378,14 +375,16 @@ module OS
]
.to_set { |path| Pathname(path.sub(%r{^~(?=(/|$))}, Dir.home)).expand_path }
.union(SYSTEM_DIRS)
.freeze
.freeze, T::Set[Pathname])
private_constant :UNDELETABLE_PATHS
def system_dir?(dir)
sig { params(dir: T.any(Pathname, String)).returns(T::Boolean) }
def self.system_dir?(dir)
SYSTEM_DIRS.include?(Pathname.new(dir).expand_path)
end
def undeletable?(path)
sig { params(path: T.any(Pathname, String)).returns(T::Boolean) }
def self.undeletable?(path)
UNDELETABLE_PATHS.include?(Pathname.new(path).expand_path)
end
end

View File

@ -1,32 +1,32 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true
module Cask
class Reinstall
sig {
params(
casks: ::Cask::Cask, verbose: T::Boolean, force: T::Boolean, skip_cask_deps: T::Boolean, binaries: T::Boolean,
require_sha: T::Boolean, quarantine: T::Boolean, zap: T::Boolean
).void
}
def self.reinstall_casks(
*casks,
verbose: nil,
force: nil,
skip_cask_deps: nil,
binaries: nil,
require_sha: nil,
quarantine: nil,
zap: nil
verbose: false,
force: false,
skip_cask_deps: false,
binaries: false,
require_sha: false,
quarantine: false,
zap: false
)
require "cask/installer"
quarantine = true if quarantine.nil?
casks.each do |cask|
Installer.new(cask,
binaries:,
verbose:,
force:,
skip_cask_deps:,
require_sha:,
reinstall: true,
quarantine:,
zap:).install
Installer
.new(cask, binaries:, verbose:, force:, skip_cask_deps:, require_sha:, reinstall: true, quarantine:, zap:)
.install
end
end
end

View File

@ -8,7 +8,7 @@ module Cask
module Staged
extend T::Helpers
requires_ancestor { Kernel }
requires_ancestor { ::Cask::DSL::Base }
Paths = T.type_alias { T.any(String, Pathname, T::Array[T.any(String, Pathname)]) }
sig { params(paths: Paths, permissions_str: String).void }

View File

@ -1,9 +1,10 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true
module Cask
class Uninstall
def self.uninstall_casks(*casks, binaries: nil, force: false, verbose: false)
sig { params(casks: ::Cask::Cask, binaries: T::Boolean, force: T::Boolean, verbose: T::Boolean).void }
def self.uninstall_casks(*casks, binaries: false, force: false, verbose: false)
require "cask/installer"
casks.each do |cask|

View File

@ -42,9 +42,16 @@ module Cask
greedy = true if Homebrew::EnvConfig.upgrade_greedy?
greedy_casks = if (upgrade_greedy_casks = Homebrew::EnvConfig.upgrade_greedy_casks.presence)
upgrade_greedy_casks.split
else
[]
end
outdated_casks = if casks.empty?
Caskroom.casks(config: Config.from_args(args)).select do |cask|
cask.outdated?(greedy:, greedy_latest:,
cask_greedy = greedy || greedy_casks.include?(cask.token)
cask.outdated?(greedy: cask_greedy, greedy_latest:,
greedy_auto_updates:)
end
else
@ -78,7 +85,7 @@ module Cask
return false if outdated_casks.empty?
if casks.empty? && !greedy
if casks.empty? && !greedy && greedy_casks.empty?
if !greedy_auto_updates && !greedy_latest
ohai "Casks with 'auto_updates true' or 'version :latest' " \
"will not be upgraded; pass `--greedy` to upgrade them."

View File

@ -267,6 +267,7 @@ module Cask
return false unless interpolated_url
interpolated_url = interpolated_url.gsub(/\#{\s*arch\s*}/, "")
interpolated_url = interpolated_url.gsub(/\#{\s*version\s*\.major\s*}/, "") if ignore_major_version
interpolated_url.exclude?('#{')

View File

@ -111,9 +111,8 @@ module Cask
def self.token_from(name)
name.downcase
.gsub("+", "-plus-")
.gsub("@", "-at-")
.gsub(/[ _·•]/, "-")
.gsub(/[^\w-]/, "")
.gsub(/[^\w@-]/, "")
.gsub(/--+/, "-")
.delete_prefix("-")
.delete_suffix("-")

View File

@ -26,7 +26,7 @@ do
# Some packages leave broken symlinks around; we clean them out before
# attempting to `rmdir` to prevent extra cruft from accumulating.
/usr/bin/find -f "${path}" -- -mindepth 1 -maxdepth 1 -type l ! -exec /bin/test -e {} \; -delete
/usr/bin/find -f "${path}" -- -mindepth 1 -maxdepth 1 -type l ! -exec /bin/test -e {} \; -delete || true
elif ! ${symlink} && [[ ! -e "${path}" ]]
then
# Skip paths that don't exists and aren't a broken symlink.

View File

@ -19,8 +19,8 @@ class Caveats
sig { returns(String) }
def caveats
caveats = []
build = formula.build
begin
build = formula.build
formula.build = Tab.for_formula(formula)
string = formula.caveats.to_s
caveats << "#{string.chomp}\n" unless string.empty?
@ -29,7 +29,7 @@ class Caveats
end
caveats << keg_only_text
valid_shells = [:bash, :zsh, :fish].freeze
valid_shells = [:bash, :zsh, :fish, :pwsh].freeze
current_shell = Utils::Shell.preferred || Utils::Shell.parent
shells = if current_shell.present? &&
(shell_sym = current_shell.to_sym) &&
@ -143,6 +143,11 @@ class Caveats
zsh #{installed.join(" and ")} have been installed to:
#{root_dir}/share/zsh/site-functions
EOS
when :pwsh
<<~EOS
PowerShell completion has been installed to:
#{root_dir}/share/pwsh/completions
EOS
end
end

View File

@ -114,6 +114,7 @@ class Cleaner
# Arch & MacPorts amongst other packagers as well. The files are
# created as part of installing any Perl module.
PERL_BASENAMES = T.let(Set.new(%w[perllocal.pod .packlist]).freeze, T::Set[String])
private_constant :PERL_BASENAMES
# Clean a top-level (`bin`, `sbin`, `lib`) directory, recursively, by fixing file
# permissions and removing .la files, unless the files (or parent

View File

@ -3,7 +3,6 @@
require "utils/bottles"
require "attrable"
require "formula"
require "cask/cask_loader"
@ -209,11 +208,8 @@ module Homebrew
end
end
extend Attrable
PERIODIC_CLEAN_FILE = (HOMEBREW_CACHE/".cleaned").freeze
attr_predicate :dry_run?, :scrub?, :prune?
attr_reader :args, :days, :cache, :disk_cleanup_size
def initialize(*args, dry_run: false, scrub: false, days: nil, cache: HOMEBREW_CACHE)
@ -227,6 +223,15 @@ module Homebrew
@cleaned_up_paths = Set.new
end
sig { returns(T::Boolean) }
def dry_run? = @dry_run
sig { returns(T::Boolean) }
def prune? = @prune
sig { returns(T::Boolean) }
def scrub? = @scrub
def self.install_formula_clean!(formula, dry_run: false)
return if Homebrew::EnvConfig.no_install_cleanup?
return unless formula.latest_version_installed?

View File

@ -221,8 +221,8 @@ module Homebrew
deps = dependency.runtime_dependencies if @use_runtime_dependencies
if recursive
deps ||= recursive_includes(Dependency, dependency, includes, ignores)
reqs = recursive_includes(Requirement, dependency, includes, ignores)
deps ||= recursive_dep_includes(dependency, includes, ignores)
reqs = recursive_req_includes(dependency, includes, ignores)
else
deps ||= select_includes(dependency.deps, ignores, includes)
reqs = select_includes(dependency.requirements, ignores, includes)

View File

@ -209,16 +209,18 @@ module Homebrew
formulae.map(&:to_hash)
end
when :v2
formulae, casks = if all
[
Formula.all(eval_all: args.eval_all?).sort,
Cask::Cask.all(eval_all: args.eval_all?).sort_by(&:full_name),
]
elsif args.installed?
[Formula.installed.sort, Cask::Caskroom.casks.sort_by(&:full_name)]
else
args.named.to_formulae_to_casks
end
formulae, casks = T.let(
if all
[
Formula.all(eval_all: args.eval_all?).sort,
Cask::Cask.all(eval_all: args.eval_all?).sort_by(&:full_name),
]
elsif args.installed?
[Formula.installed.sort, Cask::Caskroom.casks.sort_by(&:full_name)]
else
T.cast(args.named.to_formulae_to_casks, [T::Array[Formula], T::Array[Cask::Cask]])
end, [T::Array[Formula], T::Array[Cask::Cask]]
)
if args.variations?
{

View File

@ -120,6 +120,11 @@ module Homebrew
[:switch, "--overwrite", {
description: "Delete files that already exist in the prefix while linking.",
}],
[:switch, "--ask", {
description: "Ask for confirmation before downloading and installing formulae. " \
"Print bottles and dependencies download size and install size.",
env: :ask,
}],
].each do |args|
options = args.pop
send(*args, **options)
@ -237,14 +242,14 @@ module Homebrew
new_casks.each do |cask|
Cask::Installer.new(
cask,
binaries: args.binaries?,
verbose: args.verbose?,
force: args.force?,
adopt: args.adopt?,
require_sha: args.require_sha?,
skip_cask_deps: args.skip_cask_deps?,
binaries: args.binaries?,
force: args.force?,
quarantine: args.quarantine?,
quiet: args.quiet?,
require_sha: args.require_sha?,
skip_cask_deps: args.skip_cask_deps?,
verbose: args.verbose?,
).install
end
@ -302,6 +307,8 @@ module Homebrew
Install.perform_preinstall_checks_once
Install.check_cc_argv(args.cc)
Install.ask(formulae, args: args) if args.ask?
Install.install_formulae(
installed_formulae,
build_bottle: args.build_bottle?,

View File

@ -123,7 +123,8 @@ module Homebrew
raise UsageError, "Cannot use #{flags.join(", ")} with formula arguments." unless args.no_named?
formulae = if args.t?
Formula.installed.sort_by { |formula| test("M", formula.rack) }.reverse!
# See https://ruby-doc.org/3.2/Kernel.html#method-i-test
Formula.installed.sort_by { |formula| T.cast(test("M", formula.rack.to_s), Time) }.reverse!
elsif args.full_name?
Formula.installed.sort { |a, b| tap_and_name_comparison.call(a.full_name, b.full_name) }
else

View File

@ -33,7 +33,7 @@ module Homebrew
end
ff.each do |f|
missing = f.missing_dependencies(hide: args.hide)
missing = f.missing_dependencies(hide: args.hide || [])
next if missing.empty?
Homebrew.failed = true

View File

@ -63,6 +63,11 @@ module Homebrew
[:switch, "-g", "--git", {
description: "Create a Git repository, useful for creating patches to the software.",
}],
[:switch, "--ask", {
description: "Ask for confirmation before downloading and upgrading formulae. " \
"Print bottles and dependencies download size, install and net install size.",
env: :ask,
}],
].each do |args|
options = args.pop
send(*args, **options)
@ -126,6 +131,9 @@ module Homebrew
unless formulae.empty?
Install.perform_preinstall_checks_once
# If asking the user is enabled, show dependency and size information.
Install.ask(formulae, args: args) if args.ask?
formulae.each do |formula|
if formula.pinned?
onoe "#{formula.full_name} is pinned. You must unpin it to reinstall."

View File

@ -59,14 +59,14 @@ module Homebrew
elsif args.no_named?
puts Tap.installed.sort_by(&:name)
else
tap = Tap.fetch(args.named.fetch(0))
begin
tap = Tap.fetch(args.named.fetch(0))
tap.install clone_target: args.named.second,
custom_remote: args.custom_remote?,
quiet: args.quiet?,
verify: args.eval_all? || Homebrew::EnvConfig.eval_all?,
force: args.force?
rescue TapRemoteMismatchError, TapNoCustomRemoteError => e
rescue Tap::InvalidNameError, TapRemoteMismatchError, TapNoCustomRemoteError => e
odie e
rescue TapAlreadyTappedError
nil

View File

@ -161,7 +161,7 @@ module Homebrew
end
next unless formula.any_version_installed?
keg = formula.installed_kegs.last
keg = formula.installed_kegs.fetch(-1)
tab = keg.tab
# force a `brew upgrade` from the linuxbrew-core version to the homebrew-core version (even if lower)
tab.source["versions"]["version_scheme"] = -1
@ -277,7 +277,7 @@ module Homebrew
puts
new_major_version, new_minor_version, new_patch_version = new_tag.split(".").map(&:to_i)
old_major_version, old_minor_version = (old_tag.split(".")[0, 2]).map(&:to_i) if old_tag.present?
old_major_version, old_minor_version = old_tag.split(".")[0, 2].map(&:to_i) if old_tag.present?
if old_tag.blank? || new_major_version > old_major_version || new_minor_version > old_minor_version
puts <<~EOS
The #{new_major_version}.#{new_minor_version}.0 release notes are available on the Homebrew Blog:

View File

@ -71,6 +71,11 @@ module Homebrew
[:switch, "--overwrite", {
description: "Delete files that already exist in the prefix while linking.",
}],
[:switch, "--ask", {
description: "Ask for confirmation before downloading and upgrading formulae. " \
"Print bottles and dependencies download size, install and net install size.",
env: :ask,
}],
].each do |args|
options = args.pop
send(*args, **options)
@ -211,11 +216,14 @@ module Homebrew
"#{f.full_specified_name} #{f.pkg_version}"
end
end
puts formulae_upgrades.join("\n")
puts formulae_upgrades.join("\n") unless args.ask?
end
Install.perform_preinstall_checks_once
# Main block: if asking the user is enabled, show dependency and size information.
Install.ask(formulae_to_install, args: args) if args.ask?
Upgrade.upgrade_formulae(
formulae_to_install,
flags: args.flags_only,

View File

@ -94,7 +94,7 @@ module Homebrew
sig {
params(use_runtime_dependents: T::Boolean, used_formulae: T::Array[T.any(Formula, UnavailableFormula)])
.returns(T::Array[Formula])
.returns(T::Array[T.any(Formula, CaskDependent)])
}
def intersection_of_dependents(use_runtime_dependents, used_formulae)
recursive = args.recursive?
@ -106,9 +106,9 @@ module Homebrew
# We can only get here if `used_formulae_missing` is false, thus there are no UnavailableFormula.
used_formulae = T.cast(used_formulae, T::Array[Formula])
if show_formulae_and_casks || args.formula?
deps += used_formulae.map(&:runtime_installed_formula_dependents)
.reduce(&:&)
.select(&:any_version_installed?)
deps += T.must(used_formulae.map(&:runtime_installed_formula_dependents)
.reduce(&:&))
.select(&:any_version_installed?)
end
if show_formulae_and_casks || args.cask?
deps += select_used_dependents(
@ -150,26 +150,30 @@ module Homebrew
sig {
params(
dependents: T::Array[Formula], used_formulae: T::Array[T.any(Formula, UnavailableFormula)],
recursive: T::Boolean, includes: T::Array[Symbol], ignores: T::Array[Symbol]
).returns(
T::Array[Formula],
)
dependents: T::Array[T.any(Formula, CaskDependent)],
used_formulae: T::Array[T.any(Formula, UnavailableFormula)],
recursive: T::Boolean,
includes: T::Array[Symbol],
ignores: T::Array[Symbol],
).returns(T::Array[T.any(Formula, CaskDependent)])
}
def select_used_dependents(dependents, used_formulae, recursive, includes, ignores)
dependents.select do |d|
deps = if recursive
recursive_includes(Dependency, d, includes, ignores)
recursive_dep_includes(d, includes, ignores)
else
select_includes(d.deps, ignores, includes)
end
used_formulae.all? do |ff|
deps.any? do |dep|
match = begin
match = case dep
when Dependency
dep.to_formula.full_name == ff.full_name if dep.name.include?("/")
rescue
when Requirement
nil
else
T.absurd(dep)
end
next match unless match.nil?

View File

@ -92,6 +92,7 @@ class CompilerFailure
create(:clang),
],
}.freeze
private_constant :COLLECTIONS
end
# Class for selecting a compiler for a formula.

View File

@ -1,7 +1,6 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
require "attrable"
require "mutex_m"
require "ignorable"
@ -73,9 +72,10 @@ module Debrew
@debugged_exceptions = Set.new
class << self
extend Attrable
attr_predicate :active?
attr_reader :debugged_exceptions
sig { returns(T::Boolean) }
def active? = @active
end
def self.debrew

View File

@ -39,11 +39,6 @@ class Dependencies < SimpleDelegator
def inspect
"#<#{self.class.name}: #{__getobj__}>"
end
sig { returns(T::Array[Dependency]) }
def to_a
__getobj__.to_a
end
end
# A collection of requirements.

View File

@ -1,13 +1,32 @@
# typed: strict
class Dependencies < SimpleDelegator
include Enumerable
include Kernel
# This is a workaround to enable `alias eql? ==`
# @see https://github.com/sorbet/sorbet/issues/2378#issuecomment-569474238
sig { params(other: BasicObject).returns(T::Boolean) }
def ==(other); end
sig { params(blk: T.proc.params(arg0: Dependency).void).returns(T.self_type) }
sig { returns(T::Enumerator[Dependency]) }
def each(&blk); end
sig { params(blk: T.proc.params(arg0: Dependency).returns(T::Boolean)).returns(T::Array[Dependency]) }
sig { returns(T::Enumerator[Dependency]) }
def select(&blk); end
end
class Requirements < SimpleDelegator
include Enumerable
include Kernel
sig { params(blk: T.proc.params(arg0: Requirement).void).returns(T.self_type) }
sig { returns(T::Enumerator[Requirement]) }
def each(&blk); end
sig { params(blk: T.proc.params(arg0: Requirement).returns(T::Boolean)).returns(T::Array[Requirement]) }
sig { returns(T::Enumerator[Requirement]) }
def select(&blk); end
end

View File

@ -1,14 +1,10 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true
require "cask_dependent"
# Helper functions for dependencies.
module DependenciesHelpers
extend T::Helpers
requires_ancestor { Kernel }
def args_includes_ignores(args)
includes = [:required?, :recommended?] # included by default
includes << :implicit? if args.include_implicit?
@ -23,9 +19,35 @@ module DependenciesHelpers
[includes, ignores]
end
def recursive_includes(klass, root_dependent, includes, ignores)
raise ArgumentError, "Invalid class argument: #{klass}" if klass != Dependency && klass != Requirement
sig {
params(root_dependent: T.any(Formula, CaskDependent), includes: T::Array[Symbol], ignores: T::Array[Symbol])
.returns(T::Array[Dependency])
}
def recursive_dep_includes(root_dependent, includes, ignores)
# The use of T.unsafe is recommended by the Sorbet docs:
# https://sorbet.org/docs/overloads#multiple-methods-but-sharing-a-common-implementation
T.unsafe(recursive_includes(Dependency, root_dependent, includes, ignores))
end
sig {
params(root_dependent: T.any(Formula, CaskDependent), includes: T::Array[Symbol], ignores: T::Array[Symbol])
.returns(Requirements)
}
def recursive_req_includes(root_dependent, includes, ignores)
# The use of T.unsafe is recommended by the Sorbet docs:
# https://sorbet.org/docs/overloads#multiple-methods-but-sharing-a-common-implementation
T.unsafe(recursive_includes(Requirement, root_dependent, includes, ignores))
end
sig {
params(
klass: T.any(T.class_of(Dependency), T.class_of(Requirement)),
root_dependent: T.any(Formula, CaskDependent),
includes: T::Array[Symbol],
ignores: T::Array[Symbol],
).returns(T.any(T::Array[Dependency], Requirements))
}
def recursive_includes(klass, root_dependent, includes, ignores)
cache_key = "recursive_includes_#{includes}_#{ignores}"
klass.expand(root_dependent, cache_key:) do |dependent, dep|
@ -43,6 +65,13 @@ module DependenciesHelpers
end
end
sig {
params(
dependables: T.any(Dependencies, Requirements, T::Array[Dependency], T::Array[Requirement]),
ignores: T::Array[Symbol],
includes: T::Array[Symbol],
).returns(T::Array[T.any(Dependency, Requirement)])
}
def select_includes(dependables, ignores, includes)
dependables.select do |dep|
next false if ignores.any? { |ignore| dep.public_send(ignore) }
@ -51,14 +80,18 @@ module DependenciesHelpers
end
end
sig {
params(formulae_or_casks: T::Array[T.any(Formula, Keg, Cask::Cask)])
.returns(T::Array[T.any(Formula, CaskDependent)])
}
def dependents(formulae_or_casks)
formulae_or_casks.map do |formula_or_cask|
if formula_or_cask.is_a?(Formula)
formula_or_cask
case formula_or_cask
when Formula then formula_or_cask
when Cask::Cask then CaskDependent.new(formula_or_cask)
else
CaskDependent.new(formula_or_cask)
raise TypeError, "Unsupported type: #{formula_or_cask.class}"
end
end
end
module_function :dependents
end

View File

@ -0,0 +1,12 @@
# typed: strict
module DependenciesHelpers
include Kernel
# This sig is in an RBI to avoid both circular dependencies and unnecessary requires
sig {
params(args: T.any(Homebrew::Cmd::Deps::Args, Homebrew::Cmd::Uses::Args))
.returns([T::Array[Symbol], T::Array[Symbol]])
}
def args_includes_ignores(args); end
end

View File

@ -261,8 +261,8 @@ module Homebrew
audit_token_conflicts: args.token_conflicts? || nil,
quarantine: true,
any_named_args: !no_named_args,
only: args.only,
except: args.except,
only: args.only || [],
except: args.except || [],
).to_a
end
end.uniq

View File

@ -559,7 +559,7 @@ module Homebrew
ohai "Detecting if #{local_filename} is relocatable..." if bottle_path.size > 1 * 1024 * 1024
prefix_check = if Homebrew.default_prefix?(prefix)
prefix_check = if prefix == HOMEBREW_DEFAULT_PREFIX
File.join(prefix, "opt")
else
prefix

View File

@ -115,7 +115,7 @@ module Homebrew
end
end
formulae_and_casks = formulae_and_casks&.sort_by do |formula_or_cask|
formulae_and_casks = formulae_and_casks.sort_by do |formula_or_cask|
formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
end

View File

@ -40,6 +40,8 @@ module Homebrew
description: "Create a basic template for a Ruby build."
switch "--rust",
description: "Create a basic template for a Rust build."
switch "--zig",
description: "Create a basic template for a Zig build."
switch "--no-fetch",
description: "Homebrew will not download <URL> to the cache and will thus not add its SHA-256 " \
"to the formula for you, nor will it check the GitHub API for GitHub projects " \
@ -58,7 +60,7 @@ module Homebrew
description: "Ignore errors for disallowed formula names and names that shadow aliases."
conflicts "--autotools", "--cmake", "--crystal", "--go", "--meson", "--node",
"--perl", "--python", "--ruby", "--rust", "--cask"
"--perl", "--python", "--ruby", "--rust", "--zig", "--cask"
conflicts "--cask", "--HEAD"
conflicts "--cask", "--set-license"
@ -173,6 +175,8 @@ module Homebrew
:ruby
elsif args.rust?
:rust
elsif args.zig?
:zig
end
fc = FormulaCreator.new(
@ -220,6 +224,7 @@ module Homebrew
path = fc.write_formula!
formula = Homebrew.with_no_api_env do
CoreTap.instance.clear_cache
Formula[fc.name]
end
PyPI.update_python_resources! formula, ignore_non_pypi_packages: true if args.python?

View File

@ -229,7 +229,7 @@ module Homebrew
if record["prefix"] == "custom-prefix"
"#{record["prefix"]} (#{record["os"]} #{record["arch"]})"
else
(record["prefix"]).to_s
record["prefix"].to_s
end
when :os_versions
format_os_version_dimension(record["os_name_and_version"])

View File

@ -39,7 +39,7 @@ module Homebrew
sig { override.void }
def run
args.named.to_formulae.each do |formula|
ignore_errors = if T.must(formula.tap).name == "homebrew/core"
ignore_errors = if formula.tap&.official?
false
else
args.ignore_errors?

View File

@ -16,10 +16,10 @@ require "system_command"
module Homebrew
# Module containing diagnostic checks.
module Diagnostic
def self.missing_deps(formulae, hide = nil)
def self.missing_deps(formulae, hide = [])
missing = {}
formulae.each do |f|
missing_dependencies = f.missing_dependencies(hide:)
missing_dependencies = f.missing_dependencies(hide: hide)
next if missing_dependencies.empty?
yield f.full_name, missing_dependencies if block_given?
@ -63,7 +63,7 @@ module Homebrew
end
end
sig { params(list: T::Array[String], string: String).returns(String) }
sig { params(list: T::Array[T.any(Formula, Pathname, String)], string: String).returns(String) }
def inject_file_list(list, string)
list.reduce(string.dup) { |acc, elem| acc << " #{elem}\n" }
.freeze
@ -426,10 +426,12 @@ module Homebrew
end
end
@user_path_1_done = true
message unless message.empty?
end
def check_user_path_2
check_user_path_1 unless defined?(@user_path_1_done)
return if @seen_prefix_bin
<<~EOS
@ -440,6 +442,7 @@ module Homebrew
end
def check_user_path_3
check_user_path_1 unless defined?(@user_path_1_done)
return if @seen_prefix_sbin
# Don't complain about sbin not being in the path if it doesn't exist
@ -748,14 +751,12 @@ module Homebrew
def check_for_unlinked_but_not_keg_only
unlinked = Formula.racks.reject do |rack|
if (HOMEBREW_LINKED_KEGS/rack.basename).directory?
true
else
begin
Formulary.from_rack(rack).keg_only?
rescue FormulaUnavailableError, TapFormulaAmbiguityError
false
end
next true if (HOMEBREW_LINKED_KEGS/rack.basename).directory?
begin
Formulary.from_rack(rack).keg_only?
rescue FormulaUnavailableError, TapFormulaAmbiguityError
false
end
end.map(&:basename)
return if unlinked.empty?
@ -1040,6 +1041,64 @@ module Homebrew
end
end
def non_core_taps
@non_core_taps ||= Tap.installed.reject(&:core_tap?).reject(&:core_cask_tap?)
end
def check_for_duplicate_formulae
return if ENV["HOMEBREW_TEST_BOT"].present?
core_formula_names = CoreTap.instance.formula_names
shadowed_formula_full_names = non_core_taps.flat_map do |tap|
tap_formula_names = tap.formula_names.map { |s| s.delete_prefix("#{tap.name}/") }
(core_formula_names & tap_formula_names).map { |f| "#{tap.name}/#{f}" }
end.compact.sort
return if shadowed_formula_full_names.empty?
installed_formula_tap_names = Formula.installed.filter_map(&:tap).uniq.reject(&:official?).map(&:name)
shadowed_formula_tap_names = shadowed_formula_full_names.map { |s| s.rpartition("/").first }.uniq
unused_shadowed_formula_tap_names = (shadowed_formula_tap_names - installed_formula_tap_names).sort
resolution = if unused_shadowed_formula_tap_names.empty?
"Their taps are in use, so you must use these full names throughout Homebrew."
else
"Some of these can be resolved with:\n brew untap #{unused_shadowed_formula_tap_names.join(" ")}"
end
<<~EOS
The following formulae have the same name as core formulae:
#{shadowed_formula_full_names.join("\n ")}
#{resolution}
EOS
end
def check_for_duplicate_casks
return if ENV["HOMEBREW_TEST_BOT"].present?
core_cask_names = CoreCaskTap.instance.cask_tokens
shadowed_cask_full_names = non_core_taps.flat_map do |tap|
tap_cask_names = tap.cask_tokens.map { |s| s.delete_prefix("#{tap.name}/") }
(core_cask_names & tap_cask_names).map { |f| "#{tap.name}/#{f}" }
end.compact.sort
return if shadowed_cask_full_names.empty?
installed_cask_tap_names = Cask::Caskroom.casks.filter_map(&:tap).uniq.reject(&:official?).map(&:name)
shadowed_cask_tap_names = shadowed_cask_full_names.map { |s| s.rpartition("/").first }.uniq
unused_shadowed_cask_tap_names = (shadowed_cask_tap_names - installed_cask_tap_names).sort
resolution = if unused_shadowed_cask_tap_names.empty?
"Their taps are in use, so you must use these full names throughout Homebrew."
else
"Some of these can be resolved with:\n brew untap #{unused_shadowed_cask_tap_names.join(" ")}"
end
<<~EOS
The following casks have the same name as core casks:
#{shadowed_cask_full_names.join("\n ")}
#{resolution}
EOS
end
def all
methods.map(&:to_s).grep(/^check_/).sort
end

View File

@ -48,6 +48,11 @@ module Homebrew
"trying any other/default URLs.",
boolean: true,
},
HOMEBREW_ASK: {
description: "If set, pass `--ask`to all formulae `brew install`, `brew upgrade` and `brew reinstall` " \
"commands.",
boolean: true,
},
HOMEBREW_AUTO_UPDATE_SECS: {
description: "Run `brew update` once every `$HOMEBREW_AUTO_UPDATE_SECS` seconds before some commands, " \
"e.g. `brew install`, `brew upgrade` and `brew tap`. Alternatively, " \
@ -486,6 +491,10 @@ module Homebrew
description: "If set, pass `--greedy` to all cask upgrade commands.",
boolean: true,
},
HOMEBREW_UPGRADE_GREEDY_CASKS: {
description: "A space-separated list of casks. Homebrew will act as " \
"if `--greedy` was passed when upgrading any cask on this list.",
},
HOMEBREW_VERBOSE: {
description: "If set, always assume `--verbose` when running commands.",
boolean: true,

View File

@ -459,13 +459,13 @@ module Kernel
end
def disk_usage_readable(size_in_bytes)
if size_in_bytes >= 1_073_741_824
if size_in_bytes.abs >= 1_073_741_824
size = size_in_bytes.to_f / 1_073_741_824
unit = "GB"
elsif size_in_bytes >= 1_048_576
elsif size_in_bytes.abs >= 1_048_576
size = size_in_bytes.to_f / 1_048_576
unit = "MB"
elsif size_in_bytes >= 1_024
elsif size_in_bytes.abs >= 1_024
size = size_in_bytes.to_f / 1_024
unit = "KB"
else

View File

@ -4,3 +4,23 @@ module OnSystem::MacOSOnly
sig { params(arm: T.nilable(String), intel: T.nilable(String)).returns(T.nilable(String)) }
def on_arch_conditional(arm: nil, intel: nil); end
end
module OnSystem::MacOSAndLinux
sig {
params(
macos: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
linux: T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)),
).returns(T.nilable(T.any(T::Array[T.any(String, Pathname)], String, Pathname)))
}
def on_system_conditional(macos: nil, linux: nil); end
sig {
type_parameters(:U)
.params(block: T.proc.returns(T.type_parameter(:U)))
.returns(T.type_parameter(:U))
}
def on_macos(&block); end
sig { params(arm: T.nilable(String), intel: T.nilable(String)).returns(T.nilable(String)) }
def on_arch_conditional(arm: nil, intel: nil); end
end

View File

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

View File

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

View File

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

View File

@ -0,0 +1,23 @@
# typed: strict
# frozen_string_literal: true
module OS
module Linux
module Cask
module Artifact
module AbstractUninstall
extend T::Helpers
requires_ancestor { ::Cask::Artifact::AbstractUninstall }
sig { params(target: Pathname).returns(T::Boolean) }
def undeletable?(target)
!target.parent.writable?
end
end
end
end
end
end
Cask::Artifact::AbstractUninstall.prepend(OS::Linux::Cask::Artifact::AbstractUninstall)

View File

@ -20,4 +20,4 @@ module OS
end
end
Cask::Artifact::Moved.prepend(OS::Linux::Cask::Config)
Cask::Artifact::Moved.prepend(OS::Linux::Cask::Artifact::Moved)

View File

@ -0,0 +1,26 @@
# typed: strict
# frozen_string_literal: true
module OS
module Linux
module Cask
module Artifact
module Symlinked
extend T::Helpers
requires_ancestor { ::Cask::Artifact::Symlinked }
sig { params(command: T.class_of(SystemCommand)).void }
def create_filesystem_link(command)
::Cask::Utils.gain_permissions_mkpath(target.dirname, command:)
command.run! "/bin/ln", args: ["--no-dereference", "--force", "--symbolic", source, target],
sudo: !target.dirname.writable?
end
end
end
end
end
end
Cask::Artifact::Symlinked.prepend(OS::Linux::Cask::Artifact::Symlinked)

View File

@ -5,15 +5,37 @@ module OS
module Linux
module Cask
module Installer
private
extend T::Helpers
requires_ancestor { ::Cask::Installer }
LINUX_INVALID_ARTIFACTS = [
::Cask::Artifact::App,
::Cask::Artifact::AudioUnitPlugin,
::Cask::Artifact::Colorpicker,
::Cask::Artifact::Dictionary,
::Cask::Artifact::InputMethod,
::Cask::Artifact::Installer,
::Cask::Artifact::InternetPlugin,
::Cask::Artifact::KeyboardLayout,
::Cask::Artifact::Mdimporter,
::Cask::Artifact::Pkg,
::Cask::Artifact::Prefpane,
::Cask::Artifact::Qlplugin,
::Cask::Artifact::ScreenSaver,
::Cask::Artifact::Service,
::Cask::Artifact::Suite,
::Cask::Artifact::VstPlugin,
::Cask::Artifact::Vst3Plugin,
].freeze
private
sig { void }
def check_stanza_os_requirements
return if artifacts.all?(::Cask::Artifact::Font)
return unless artifacts.any? do |artifact|
LINUX_INVALID_ARTIFACTS.include?(artifact.class)
end
raise ::Cask::CaskError, "macOS is required for this software."
end

View File

@ -24,7 +24,7 @@ module Homebrew
end
next unless recursive_runtime_dependencies.map(&:name).include? "gcc"
keg = formula.installed_kegs.last
keg = formula.installed_kegs.fetch(-1)
tab = keg.tab
# Force reinstallation upon `brew upgrade` to fix the bottle RPATH.
tab.source["versions"]["version_scheme"] = -1

View File

@ -33,6 +33,7 @@ module OS
GLIBC = "glibc"
GCC = OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA
private_constant :GLIBC, :GCC
sig { void }
def init_global_dep_tree_if_needed!
@ -47,9 +48,9 @@ module OS
built_global_dep_tree!
end
sig { params(name: String).returns(T.nilable(Formula)) }
sig { params(name: String).returns(T.nilable(::Formula)) }
def formula_for(name)
@formula_for ||= T.let({}, T.nilable(T::Hash[String, Formula]))
@formula_for ||= T.let({}, T.nilable(T::Hash[String, ::Formula]))
@formula_for[name] ||= ::Formula[name]
rescue FormulaUnavailableError
nil

View File

@ -0,0 +1,25 @@
# typed: strict
# frozen_string_literal: true
require "cask/macos"
module OS
module Mac
module Cask
module Artifact
module AbstractUninstall
extend T::Helpers
requires_ancestor { ::Cask::Artifact::AbstractUninstall }
sig { params(target: Pathname).returns(T::Boolean) }
def undeletable?(target)
MacOS.undeletable?(target)
end
end
end
end
end
end
Cask::Artifact::AbstractUninstall.prepend(OS::Mac::Cask::Artifact::AbstractUninstall)

View File

@ -0,0 +1,30 @@
# typed: strict
# frozen_string_literal: true
require "cask/macos"
module OS
module Mac
module Cask
module Artifact
module Symlinked
extend T::Helpers
requires_ancestor { ::Cask::Artifact::Symlinked }
sig { params(command: T.class_of(SystemCommand)).void }
def create_filesystem_link(command)
::Cask::Utils.gain_permissions_mkpath(target.dirname, command:)
command.run! "/bin/ln", args: ["-h", "-f", "-s", "--", source, target],
sudo: !target.dirname.writable?
add_altname_metadata(source, target.basename, command:)
end
end
end
end
end
end
Cask::Artifact::Symlinked.prepend(OS::Mac::Cask::Artifact::Symlinked)

View File

@ -0,0 +1,23 @@
# typed: strict
# frozen_string_literal: true
require "cask/macos"
module OS
module Mac
module Cask
module DSL
extend T::Helpers
requires_ancestor { ::Cask::DSL }
sig { returns(T.nilable(MacOSVersion)) }
def os_version
MacOS.full_version
end
end
end
end
end
Cask::DSL.prepend(OS::Mac::Cask::DSL)

View File

@ -32,6 +32,18 @@ module OS
args
end
sig {
params(
prefix: T.any(String, Pathname),
release_mode: Symbol,
).returns(T::Array[String])
}
def std_zig_args(prefix: self.prefix, release_mode: :fast)
args = super
args << "-fno-rosetta" if ::Hardware::CPU.arm?
args
end
end
end
end

View File

@ -240,6 +240,7 @@ module OS
private
CELLAR_RX = %r{\A#{HOMEBREW_CELLAR}/(?<formula_name>[^/]+)/[^/]+}
private_constant :CELLAR_RX
# Replace HOMEBREW_CELLAR references with HOMEBREW_PREFIX/opt references
# if the Cellar reference is to a different keg.

View File

@ -8,53 +8,53 @@ certifi==2025.1.31 \
--hash=sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651 \
--hash=sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe
# via influxdb3-python
influxdb3-python==0.10.0 \
--hash=sha256:d279e5f8a597d49b44035263b1cf1472a3861ceba930fd08e1e3b1721a07d3cf \
--hash=sha256:f3d44dff4c4bbfdcb1fa1c4013ccfa317fbbd7df5812eb46395421166ffb385a
influxdb3-python==0.11.0 \
--hash=sha256:07bea8e1150be9707f818cda9634600a42487ee14802208f3f0357af2847f6e3 \
--hash=sha256:dd5a1197f776f9836935d797be2dbc9e09f75465188492409e0b5931e6a033c4
# via -r requirements.in
pyarrow==19.0.0 \
--hash=sha256:239ca66d9a05844bdf5af128861af525e14df3c9591bcc05bac25918e650d3a2 \
--hash=sha256:2795064647add0f16563e57e3d294dbfc067b723f0fd82ecd80af56dad15f503 \
--hash=sha256:29cd86c8001a94f768f79440bf83fee23963af5e7bc68ce3a7e5f120e17edf89 \
--hash=sha256:2a0144a712d990d60f7f42b7a31f0acaccf4c1e43e957f7b1ad58150d6f639c1 \
--hash=sha256:2a1a109dfda558eb011e5f6385837daffd920d54ca00669f7a11132d0b1e6042 \
--hash=sha256:2b6d3ce4288793350dc2d08d1e184fd70631ea22a4ff9ea5c4ff182130249d9b \
--hash=sha256:2f672f5364b2d7829ef7c94be199bb88bf5661dd485e21d2d37de12ccb78a136 \
--hash=sha256:3c1c162c4660e0978411a4761f91113dde8da3433683efa473501254563dcbe8 \
--hash=sha256:450a7d27e840e4d9a384b5c77199d489b401529e75a3b7a3799d4cd7957f2f9c \
--hash=sha256:4624c89d6f777c580e8732c27bb8e77fd1433b89707f17c04af7635dd9638351 \
--hash=sha256:4d8b0c0de0a73df1f1bf439af1b60f273d719d70648e898bc077547649bb8352 \
--hash=sha256:5418d4d0fab3a0ed497bad21d17a7973aad336d66ad4932a3f5f7480d4ca0c04 \
--hash=sha256:597360ffc71fc8cceea1aec1fb60cb510571a744fffc87db33d551d5de919bec \
--hash=sha256:5e8a28b918e2e878c918f6d89137386c06fe577cd08d73a6be8dafb317dc2d73 \
--hash=sha256:62ef8360ff256e960f57ce0299090fb86423afed5e46f18f1225f960e05aae3d \
--hash=sha256:66732e39eaa2247996a6b04c8aa33e3503d351831424cdf8d2e9a0582ac54b34 \
--hash=sha256:718947fb6d82409013a74b176bf93e0f49ef952d8a2ecd068fecd192a97885b7 \
--hash=sha256:8d47c691765cf497aaeed4954d226568563f1b3b74ff61139f2d77876717084b \
--hash=sha256:8e3a839bf36ec03b4315dc924d36dcde5444a50066f1c10f8290293c0427b46a \
--hash=sha256:9348a0137568c45601b031a8d118275069435f151cbb77e6a08a27e8125f59d4 \
--hash=sha256:a08e2a8a039a3f72afb67a6668180f09fddaa38fe0d21f13212b4aba4b5d2451 \
--hash=sha256:a218670b26fb1bc74796458d97bcab072765f9b524f95b2fccad70158feb8b17 \
--hash=sha256:a22a4bc0937856263df8b94f2f2781b33dd7f876f787ed746608e06902d691a5 \
--hash=sha256:a7bbe7109ab6198688b7079cbad5a8c22de4d47c4880d8e4847520a83b0d1b68 \
--hash=sha256:a92aff08e23d281c69835e4a47b80569242a504095ef6a6223c1f6bb8883431d \
--hash=sha256:b34d3bde38eba66190b215bae441646330f8e9da05c29e4b5dd3e41bde701098 \
--hash=sha256:b903afaa5df66d50fc38672ad095806443b05f202c792694f3a604ead7c6ea6e \
--hash=sha256:be686bf625aa7b9bada18defb3a3ea3981c1099697239788ff111d87f04cd263 \
--hash=sha256:c0423393e4a07ff6fea08feb44153302dd261d0551cc3b538ea7a5dc853af43a \
--hash=sha256:c318eda14f6627966997a7d8c374a87d084a94e4e38e9abbe97395c215830e0c \
--hash=sha256:c3b78eff5968a1889a0f3bc81ca57e1e19b75f664d9c61a42a604bf9d8402aae \
--hash=sha256:c73268cf557e688efb60f1ccbc7376f7e18cd8e2acae9e663e98b194c40c1a2d \
--hash=sha256:c751c1c93955b7a84c06794df46f1cec93e18610dcd5ab7d08e89a81df70a849 \
--hash=sha256:ce42275097512d9e4e4a39aade58ef2b3798a93aa3026566b7892177c266f735 \
--hash=sha256:cf3bf0ce511b833f7bc5f5bb3127ba731e97222023a444b7359f3a22e2a3b463 \
--hash=sha256:da410b70a7ab8eb524112f037a7a35da7128b33d484f7671a264a4c224ac131d \
--hash=sha256:e675a3ad4732b92d72e4d24009707e923cab76b0d088e5054914f11a797ebe44 \
--hash=sha256:e82c3d5e44e969c217827b780ed8faf7ac4c53f934ae9238872e749fa531f7c9 \
--hash=sha256:edfe6d3916e915ada9acc4e48f6dafca7efdbad2e6283db6fd9385a1b23055f1 \
--hash=sha256:f094742275586cdd6b1a03655ccff3b24b2610c3af76f810356c4c71d24a2a6c \
--hash=sha256:f208c3b58a6df3b239e0bb130e13bc7487ed14f39a9ff357b6415e3f6339b560 \
--hash=sha256:f43f5aef2a13d4d56adadae5720d1fed4c1356c993eda8b59dace4b5983843c1
pyarrow==19.0.1 \
--hash=sha256:008a4009efdb4ea3d2e18f05cd31f9d43c388aad29c636112c2966605ba33466 \
--hash=sha256:0148bb4fc158bfbc3d6dfe5001d93ebeed253793fff4435167f6ce1dc4bddeae \
--hash=sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136 \
--hash=sha256:1c7556165bd38cf0cd992df2636f8bcdd2d4b26916c6b7e646101aff3c16f76f \
--hash=sha256:335d170e050bcc7da867a1ed8ffb8b44c57aaa6e0843b156a501298657b1e972 \
--hash=sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e \
--hash=sha256:41f9706fbe505e0abc10e84bf3a906a1338905cbbcf1177b71486b03e6ea6608 \
--hash=sha256:4982f8e2b7afd6dae8608d70ba5bd91699077323f812a0448d8b7abdff6cb5d3 \
--hash=sha256:49a3aecb62c1be1d822f8bf629226d4a96418228a42f5b40835c1f10d42e4db6 \
--hash=sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14 \
--hash=sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8 \
--hash=sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6 \
--hash=sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960 \
--hash=sha256:65cf9feebab489b19cdfcfe4aa82f62147218558d8d3f0fc1e9dea0ab8e7905a \
--hash=sha256:699799f9c80bebcf1da0983ba86d7f289c5a2a5c04b945e2f2bcf7e874a91911 \
--hash=sha256:6c5941c1aac89a6c2f2b16cd64fe76bcdb94b2b1e99ca6459de4e6f07638d755 \
--hash=sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4 \
--hash=sha256:7a544ec12de66769612b2d6988c36adc96fb9767ecc8ee0a4d270b10b1c51e00 \
--hash=sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a \
--hash=sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b \
--hash=sha256:8464c9fbe6d94a7fe1599e7e8965f350fd233532868232ab2596a71586c5a429 \
--hash=sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3 \
--hash=sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9 \
--hash=sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6 \
--hash=sha256:ad76aef7f5f7e4a757fddcdcf010a8290958f09e3470ea458c80d26f4316ae89 \
--hash=sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832 \
--hash=sha256:b9766a47a9cb56fefe95cb27f535038b5a195707a08bf61b180e642324963b46 \
--hash=sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0 \
--hash=sha256:c6cb2335a411b713fdf1e82a752162f72d4a7b5dbc588e32aa18383318b05866 \
--hash=sha256:cc55d71898ea30dc95900297d191377caba257612f384207fe9f8293b5850f90 \
--hash=sha256:d03c9d6f2a3dffbd62671ca070f13fc527bb1867b4ec2b98c7eeed381d4f389a \
--hash=sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6 \
--hash=sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef \
--hash=sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae \
--hash=sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c \
--hash=sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294 \
--hash=sha256:f24faab6ed18f216a37870d8c5623f9c044566d75ec586ef884e13a02a9d62c5 \
--hash=sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2 \
--hash=sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34 \
--hash=sha256:fc28912a2dc924dddc2087679cc8b7263accc71b9ff025a1362b004711661a69 \
--hash=sha256:fca15aabbe9b8355800d923cc2e82c8ef514af321e18b437c3d782aa884eaeec \
--hash=sha256:fd44d66093a239358d07c42a91eebf5015aa54fccba959db899f932218ac9cc8
# via influxdb3-python
python-dateutil==2.9.0.post0 \
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
@ -78,7 +78,7 @@ urllib3==2.3.0 \
# via influxdb3-python
# The following packages are considered to be unsafe in a requirements file:
setuptools==75.8.0 \
--hash=sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6 \
--hash=sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3
setuptools==76.0.0 \
--hash=sha256:199466a166ff664970d0ee145839f5582cb9bca7a0a3a2e795b6a9cb2308e9c6 \
--hash=sha256:43b4ee60e10b0d0ee98ad11918e114c70701bc6051662a9a675a0496c1a158f4
# via influxdb3-python

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
# typed: strict
class Formula
# This method is included by `OnSystem`
def self.on_macos(&block); end
end

View File

@ -307,7 +307,7 @@ module FormulaCellarChecks
return unless formula.service?
return unless formula.service.command?
"Service command does not exist" unless File.exist?(formula.service.command.first)
"Service command does not exist" unless File.exist?(T.must(formula.service.command).first)
end
sig { params(formula: Formula).returns(T.nilable(String)) }

View File

@ -100,9 +100,9 @@ module Homebrew
sig { params(name: String).returns(String) }
def latest_versioned_formula(name)
name_prefix = "#{name}@"
Tap.fetch("homebrew/core").formula_names
.select { |f| f.start_with?(name_prefix) }
.max_by { |v| Gem::Version.new(v.sub(name_prefix, "")) } || "python"
CoreTap.instance.formula_names
.select { |f| f.start_with?(name_prefix) }
.max_by { |v| Gem::Version.new(v.sub(name_prefix, "")) } || "python"
end
sig { returns(String) }
@ -151,6 +151,8 @@ module Homebrew
uses_from_macos "ruby"
<% elsif @mode == :rust %>
depends_on "rust" => :build
<% elsif @mode == :zig %>
depends_on "zig" => :build
<% elsif @mode.nil? %>
# depends_on "cmake" => :build
<% end %>
@ -217,6 +219,8 @@ module Homebrew
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV["GEM_HOME"])
<% elsif @mode == :rust %>
system "cargo", "install", *std_cargo_args
<% elsif @mode == :zig %>
system "zig", "build", *std_zig_args
<% else %>
# Remove unrecognized options if they cause configure to fail
# https://rubydoc.brew.sh/Formula.html#std_configure_args-instance_method

View File

@ -29,7 +29,6 @@ require "utils/fork"
# Installer for a formula.
class FormulaInstaller
include FormulaCellarChecks
extend Attrable
ETC_VAR_DIRS = T.let([HOMEBREW_PREFIX/"etc", HOMEBREW_PREFIX/"var"].freeze, T::Array[Pathname])
@ -45,12 +44,6 @@ class FormulaInstaller
sig { returns(T::Boolean) }
attr_accessor :link_keg
attr_predicate :installed_as_dependency?, :installed_on_request?
attr_predicate :show_summary_heading?, :show_header?
attr_predicate :force_bottle?, :ignore_deps?, :only_deps?, :interactive?, :git?, :force?, :overwrite?, :keep_tmp?
attr_predicate :debug_symbols?
attr_predicate :verbose?, :debug?, :quiet?
sig {
params(
formula: Formula,
@ -148,6 +141,54 @@ class FormulaInstaller
@formula = T.let(T.must(previously_fetched_formula), Formula) if previously_fetched_formula
end
sig { returns(T::Boolean) }
def debug? = @debug
sig { returns(T::Boolean) }
def debug_symbols? = @debug_symbols
sig { returns(T::Boolean) }
def force? = @force
sig { returns(T::Boolean) }
def force_bottle? = @force_bottle
sig { returns(T::Boolean) }
def git? = @git
sig { returns(T::Boolean) }
def ignore_deps? = @ignore_deps
sig { returns(T::Boolean) }
def installed_as_dependency? = @installed_as_dependency
sig { returns(T::Boolean) }
def installed_on_request? = @installed_on_request
sig { returns(T::Boolean) }
def interactive? = @interactive
sig { returns(T::Boolean) }
def keep_tmp? = @keep_tmp
sig { returns(T::Boolean) }
def only_deps? = @only_deps
sig { returns(T::Boolean) }
def overwrite? = @overwrite
sig { returns(T::Boolean) }
def quiet? = @quiet
sig { returns(T::Boolean) }
def show_header? = @show_header
sig { returns(T::Boolean) }
def show_summary_heading? = @show_summary_heading
sig { returns(T::Boolean) }
def verbose? = @verbose
sig { returns(T::Set[Formula]) }
def self.attempted
@attempted ||= T.let(Set.new, T.nilable(T::Set[Formula]))
@ -809,11 +850,8 @@ on_request: installed_on_request?, options:)
options |= inherited_options
options &= df.options
installed_on_request = if df.any_version_installed? && tab.present? && tab.installed_on_request
true
else
false
end
installed_on_request = df.any_version_installed? && tab.present? && tab.installed_on_request
installed_on_request ||= false
fi = FormulaInstaller.new(
df,
@ -1230,7 +1268,7 @@ on_request: installed_on_request?, options:)
return keg_formula_path if formula.loaded_from_api?
return keg_formula_path if formula.local_bottle_path.present?
tap_formula_path = formula.specified_path
tap_formula_path = T.must(formula.specified_path)
return keg_formula_path unless tap_formula_path.exist?
begin

View File

@ -136,6 +136,8 @@ class GitHubPackages
IMAGE_MANIFEST_SCHEMA_URI = "https://opencontainers.org/schema/image/manifest"
GITHUB_PACKAGE_TYPE = "homebrew_bottle"
private_constant :IMAGE_CONFIG_SCHEMA_URI, :IMAGE_INDEX_SCHEMA_URI, :IMAGE_LAYOUT_SCHEMA_URI,
:IMAGE_MANIFEST_SCHEMA_URI, :GITHUB_PACKAGE_TYPE
def load_schemas!
schema_uri("content-descriptor",

View File

@ -5,6 +5,10 @@ require "test_runner_formula"
require "github_runner"
class GitHubRunnerMatrix
NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :sequoia
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :ventura
NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER = :sonoma
RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) }
private_constant :RunnerSpec
@ -77,6 +81,7 @@ class GitHubRunnerMatrix
# https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits
GITHUB_ACTIONS_LONG_TIMEOUT = 2160 # 36 hours
GITHUB_ACTIONS_SHORT_TIMEOUT = 60
private_constant :SELF_HOSTED_LINUX_RUNNER, :GITHUB_ACTIONS_LONG_TIMEOUT, :GITHUB_ACTIONS_SHORT_TIMEOUT
sig { returns(LinuxRunnerSpec) }
def linux_runner_spec
@ -97,6 +102,7 @@ class GitHubRunnerMatrix
VALID_PLATFORMS = T.let([:macos, :linux].freeze, T::Array[Symbol])
VALID_ARCHES = T.let([:arm64, :x86_64].freeze, T::Array[Symbol])
private_constant :VALID_PLATFORMS, :VALID_ARCHES
sig {
params(
@ -116,10 +122,6 @@ class GitHubRunnerMatrix
runner.freeze
end
NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :sequoia
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :ventura
NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER = :sonoma
sig { params(macos_version: MacOSVersion).returns(T::Boolean) }
def runner_enabled?(macos_version)
macos_version <= NEWEST_HOMEBREW_CORE_MACOS_RUNNER && macos_version >= OLDEST_HOMEBREW_CORE_MACOS_RUNNER
@ -130,6 +132,9 @@ class GitHubRunnerMatrix
NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER = :sequoia
OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER = :sonoma
GITHUB_ACTIONS_RUNNER_TIMEOUT = 360
private_constant :NEWEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER, :OLDEST_GITHUB_ACTIONS_INTEL_MACOS_RUNNER,
:NEWEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER, :OLDEST_GITHUB_ACTIONS_ARM_MACOS_RUNNER,
:GITHUB_ACTIONS_RUNNER_TIMEOUT
sig { void }
def generate_runners!

View File

@ -9,6 +9,7 @@ HOMEBREW_API_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_API_DEFAULT_DOMAIN").freeze
HOMEBREW_BOTTLE_DEFAULT_DOMAIN = ENV.fetch("HOMEBREW_BOTTLE_DEFAULT_DOMAIN").freeze
HOMEBREW_BREW_DEFAULT_GIT_REMOTE = ENV.fetch("HOMEBREW_BREW_DEFAULT_GIT_REMOTE").freeze
HOMEBREW_CORE_DEFAULT_GIT_REMOTE = ENV.fetch("HOMEBREW_CORE_DEFAULT_GIT_REMOTE").freeze
HOMEBREW_DEFAULT_CACHE = ENV.fetch("HOMEBREW_DEFAULT_CACHE").freeze
HOMEBREW_DEFAULT_LOGS = ENV.fetch("HOMEBREW_DEFAULT_LOGS").freeze
HOMEBREW_DEFAULT_TEMP = ENV.fetch("HOMEBREW_DEFAULT_TEMP").freeze
@ -16,9 +17,11 @@ HOMEBREW_REQUIRED_RUBY_VERSION = ENV.fetch("HOMEBREW_REQUIRED_RUBY_VERSION").fre
HOMEBREW_PRODUCT = ENV.fetch("HOMEBREW_PRODUCT").freeze
HOMEBREW_VERSION = ENV.fetch("HOMEBREW_VERSION").freeze
HOMEBREW_WWW = "https://brew.sh"
HOMEBREW_API_WWW = "https://formulae.brew.sh"
HOMEBREW_DOCS_WWW = "https://docs.brew.sh"
HOMEBREW_SYSTEM = ENV.fetch("HOMEBREW_SYSTEM").freeze
HOMEBREW_PROCESSOR = ENV.fetch("HOMEBREW_PROCESSOR").freeze
HOMEBREW_PHYSICAL_PROCESSOR = ENV.fetch("HOMEBREW_PHYSICAL_PROCESSOR").freeze
@ -33,13 +36,14 @@ HOMEBREW_USER_AGENT_FAKE_SAFARI =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 " \
"(KHTML, like Gecko) Version/17.0 Safari/605.1.15"
HOMEBREW_GITHUB_PACKAGES_AUTH = ENV.fetch("HOMEBREW_GITHUB_PACKAGES_AUTH").freeze
HOMEBREW_DEFAULT_PREFIX = ENV.fetch("HOMEBREW_GENERIC_DEFAULT_PREFIX").freeze
HOMEBREW_DEFAULT_REPOSITORY = ENV.fetch("HOMEBREW_GENERIC_DEFAULT_REPOSITORY").freeze
HOMEBREW_MACOS_ARM_DEFAULT_PREFIX = ENV.fetch("HOMEBREW_MACOS_ARM_DEFAULT_PREFIX").freeze
HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY = ENV.fetch("HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY").freeze
HOMEBREW_LINUX_DEFAULT_PREFIX = ENV.fetch("HOMEBREW_LINUX_DEFAULT_PREFIX").freeze
HOMEBREW_LINUX_DEFAULT_REPOSITORY = ENV.fetch("HOMEBREW_LINUX_DEFAULT_REPOSITORY").freeze
HOMEBREW_MACOS_ARM_DEFAULT_PREFIX = ENV.delete("HOMEBREW_MACOS_ARM_DEFAULT_PREFIX").freeze
HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY = ENV.delete("HOMEBREW_MACOS_ARM_DEFAULT_REPOSITORY").freeze
HOMEBREW_LINUX_DEFAULT_PREFIX = ENV.delete("HOMEBREW_LINUX_DEFAULT_PREFIX").freeze
HOMEBREW_LINUX_DEFAULT_REPOSITORY = ENV.delete("HOMEBREW_LINUX_DEFAULT_REPOSITORY").freeze
HOMEBREW_PREFIX_PLACEHOLDER = "$HOMEBREW_PREFIX"
HOMEBREW_CELLAR_PLACEHOLDER = "$HOMEBREW_CELLAR"
# Needs a leading slash to avoid `File.expand.path` complaining about non-absolute home.

View File

@ -327,6 +327,22 @@ module Homebrew
puts formula_names.join(" ")
end
# If asking the user is enabled, show dependency and size information.
def ask(formulae, args:)
ohai "Looking for bottles..."
sized_formulae = compute_sized_formulae(formulae, args: args)
sizes = compute_total_sizes(sized_formulae, debug: args.debug?)
puts "#{::Utils.pluralize("Formula", sized_formulae.count, plural: "e")} \
(#{sized_formulae.count}): #{sized_formulae.join(", ")}\n\n"
puts "Download Size: #{disk_usage_readable(sizes[:download])}"
puts "Install Size: #{disk_usage_readable(sizes[:installed])}"
puts "Net Install Size: #{disk_usage_readable(sizes[:net])}" if sizes[:net] != 0
ask_input
end
private
def perform_preinstall_checks(all_fatal: false)
@ -363,6 +379,86 @@ module Homebrew
Upgrade.install_formula(formula_installer, upgrade:)
end
def ask_input
ohai "Do you want to proceed with the installation? [Y/y/yes/N/n/no]"
accepted_inputs = %w[y yes]
declined_inputs = %w[n no]
loop do
result = $stdin.gets
return unless result
result = result.chomp.strip.downcase
if accepted_inputs.include?(result)
break
elsif declined_inputs.include?(result)
exit 1
else
puts "Invalid input. Please enter 'Y', 'y', or 'yes' to proceed, or 'N' to abort."
end
end
end
# Build a unique list of formulae to size by including:
# 1. The original formulae to install.
# 2. Their outdated dependents (subject to pruning criteria).
# 3. Optionally, any installed formula that depends on one of these and is outdated.
def compute_sized_formulae(formulae, args:)
sized_formulae = formulae.flat_map do |formula|
# Always include the formula itself.
formula_list = [formula]
deps = args.build_from_source? ? formula.deps.build : formula.deps.required
outdated_dependents = deps.map(&:to_formula).reject(&:pinned?).select do |dep|
dep.installed_kegs.empty? || (dep.bottled? && dep.outdated?)
end
deps.map(&:to_formula).each do |f|
outdated_dependents.concat(f.recursive_dependencies.map(&:to_formula).reject(&:pinned?).select do |dep|
dep.installed_kegs.empty? || (dep.bottled? && dep.outdated?)
end)
end
formula_list.concat(outdated_dependents)
formula_list
end
# Add any installed formula that depends on one of the sized formulae and is outdated.
unless Homebrew::EnvConfig.no_installed_dependents_check?
sized_formulae.concat(Formula.installed.select do |installed_formula|
installed_formula.bottled? && installed_formula.outdated? &&
installed_formula.deps.required.map(&:to_formula).intersect?(sized_formulae)
end)
end
sized_formulae.uniq(&:to_s).compact
end
# Compute the total sizes (download, installed, and net) for the given formulae.
def compute_total_sizes(sized_formulae, debug: false)
total_download_size = 0
total_installed_size = 0
total_net_size = 0
sized_formulae.select(&:bottle).each do |formula|
bottle = formula.bottle
# Fetch additional bottle metadata (if necessary).
bottle.fetch_tab(quiet: !debug)
total_download_size += bottle.bottle_size.to_i if bottle.bottle_size
total_installed_size += bottle.installed_size.to_i if bottle.installed_size
# Sum disk usage for all installed kegs of the formula.
next if formula.installed_kegs.none?
kegs_dep_size = formula.installed_kegs.sum { |keg| keg.disk_usage.to_i }
total_net_size += bottle.installed_size.to_i - kegs_dep_size if bottle.installed_size
end
{ download: total_download_size,
installed: total_installed_size,
net: total_net_size }
end
end
end
end

View File

@ -147,6 +147,7 @@ class Keg
share/man/man1 share/man/man2 share/man/man3 share/man/man4
share/man/man5 share/man/man6 share/man/man7 share/man/man8
share/zsh share/zsh/site-functions
share/pwsh share/pwsh/completions
var/log
].map { |dir| HOMEBREW_PREFIX/dir } + must_exist_subdirectories + [
HOMEBREW_CACHE,
@ -354,6 +355,7 @@ class Keg
when :zsh
dir = path/"share/zsh/site-functions"
dir if dir.directory? && dir.children.any? { |f| f.basename.to_s.start_with?("_") }
when :pwsh then path/"share/pwsh/completions"
end
dir&.directory? && !dir.children.empty?
end
@ -388,6 +390,7 @@ class Keg
(path/"share/emacs/site-lisp"/name).children.any? { |f| ELISP_EXTENSIONS.include? f.extname }
end
sig { returns(PkgVersion) }
def version
require "pkg_version"
PkgVersion.parse(path.basename.to_s)
@ -554,6 +557,41 @@ class Keg
path.find { |pn| FileUtils.rm_rf pn if pn.basename.to_s == "__pycache__" }
end
def normalize_pod2man_outputs!
# Only process uncompressed manpages, which end in a digit
manpages = Dir[path/"share/man/*/*.[1-9]"]
generated_regex = /^\.\\"\s*Automatically generated by .*\n/
manpages.each do |f|
manpage = Pathname.new(f)
next unless manpage.file?
content = manpage.read
unless content.valid_encoding?
# Occasionally, a manpage might not be encoded as UTF-8. ISO-8859-1 is a
# common alternative that's worth trying in this case.
content = File.read(manpage, encoding: "ISO-8859-1")
# If the encoding is still invalid, we can't do anything about it.
next unless content.valid_encoding?
end
content = content.gsub(generated_regex, "")
content = content.lines.map do |line|
next line unless line.start_with?(".TH")
# Split the line by spaces, but preserve quoted strings
parts = line.split(/\s(?=(?:[^"]|"[^"]*")*$)/)
next line if parts.length != 6
# pod2man embeds the perl version used into the 5th field of the footer
T.must(parts[4]).gsub!(/^"perl v.*"$/, "\"\"")
"#{parts.join(" ")}\n"
end.join
manpage.atomic_write(content)
end
end
def binary_executable_or_library_files
[]
end

View File

@ -16,7 +16,7 @@ module Language
next false unless f.any_version_installed?
unless version.zero?
major = f.any_installed_version.major
major = T.must(f.any_installed_version).major
next false if major < version
next false if major > version && !can_be_newer
end

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