Compare commits
121 Commits
6418ac7b4d
...
cd4fc80ec1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cd4fc80ec1 | ||
![]() |
f5c11fa342 | ||
![]() |
eda9e78529 | ||
![]() |
5f4e42a2c8 | ||
![]() |
852574dedf | ||
![]() |
3e413b4521 | ||
![]() |
2d9e9ce5d1 | ||
![]() |
2fe1076281 | ||
![]() |
05985fabc3 | ||
![]() |
6297f98d06 | ||
![]() |
07091cfbea | ||
![]() |
1019f9ef21 | ||
![]() |
e88ceb8178 | ||
![]() |
2d8ba1e209 | ||
![]() |
675a588f6c | ||
![]() |
0a7a60f506 | ||
![]() |
9b364ad25b | ||
![]() |
613d6466a9 | ||
![]() |
99456ee150 | ||
![]() |
29270a8858 | ||
![]() |
3fb55a1577 | ||
![]() |
3d80dfadd1 | ||
![]() |
f233244ab7 | ||
![]() |
e4ac3bfeed | ||
![]() |
610c67b715 | ||
![]() |
6d548f784b | ||
![]() |
b2539d37fe | ||
![]() |
1cc4d0bc25 | ||
![]() |
89d36e0dd5 | ||
![]() |
deb6666f32 | ||
![]() |
66f8afa734 | ||
![]() |
34be1486e2 | ||
![]() |
ccda7a5de1 | ||
![]() |
0d8da983d8 | ||
![]() |
3023e6dcad | ||
![]() |
843fc7c97a | ||
![]() |
a31fb2a6c2 | ||
![]() |
8ef7a9dbd4 | ||
![]() |
67f4be419b | ||
![]() |
51a98eb950 | ||
![]() |
055c87e47e | ||
![]() |
bc2c12c742 | ||
![]() |
0adf85970d | ||
![]() |
28761d8756 | ||
![]() |
289fed93b4 | ||
![]() |
744e2452d8 | ||
![]() |
80f9e6714f | ||
![]() |
971ca17180 | ||
![]() |
4d36861ad0 | ||
![]() |
14d7942c95 | ||
![]() |
5453c0a74c | ||
![]() |
6e6c06f5a9 | ||
![]() |
b54695d6e5 | ||
![]() |
b827a1337a | ||
![]() |
2f827242aa | ||
![]() |
f68908d492 | ||
![]() |
3541b4989f | ||
![]() |
afcaae3be8 | ||
![]() |
c5b8cc906e | ||
![]() |
78d34b45af | ||
![]() |
efc036f75a | ||
![]() |
31fb2adc84 | ||
![]() |
bbca610601 | ||
![]() |
3f11702fb1 | ||
![]() |
e2f02bcfbf | ||
![]() |
b22d2328b7 | ||
![]() |
1da17b8fde | ||
![]() |
83c1ed68f3 | ||
![]() |
62a3b5e43e | ||
![]() |
896edb4451 | ||
![]() |
5b003154e5 | ||
![]() |
74095ab480 | ||
![]() |
0e81158e3a | ||
![]() |
ffc3c4bd6a | ||
![]() |
f26e296cbb | ||
![]() |
0264a1e2ee | ||
![]() |
faf936a589 | ||
![]() |
1c4cb6936e | ||
![]() |
09452ed3c7 | ||
![]() |
cba6e40e77 | ||
![]() |
24e7d8f0e0 | ||
![]() |
32a5e9f779 | ||
![]() |
6e9da0ba4c | ||
![]() |
c73538ac03 | ||
![]() |
5fb96d9d8a | ||
![]() |
08181692f3 | ||
![]() |
1c959c22ce | ||
![]() |
00f960174f | ||
![]() |
bd57ad891d | ||
![]() |
f23b84897c | ||
![]() |
dce220e518 | ||
![]() |
4343324468 | ||
![]() |
31cf8b43a9 | ||
![]() |
6c98d2bf1b | ||
![]() |
de8ff312e6 | ||
![]() |
ce8ae46054 | ||
![]() |
fb6c51da07 | ||
![]() |
ab980dc3b7 | ||
![]() |
89f3c76cd7 | ||
![]() |
e28fe444a5 | ||
![]() |
45a642c363 | ||
![]() |
75350b48a3 | ||
![]() |
bfb0a55347 | ||
![]() |
9da27e1ac2 | ||
![]() |
aa40480c46 | ||
![]() |
dc28719c8e | ||
![]() |
48170b8957 | ||
![]() |
25ed81f14b | ||
![]() |
12ea343c9f | ||
![]() |
a8b4d115f2 | ||
![]() |
f973bea35c | ||
![]() |
ac6f770cdc | ||
![]() |
45a2ef9159 | ||
![]() |
6be546a5bc | ||
![]() |
88b7d2900a | ||
![]() |
a9cd0c4b5d | ||
![]() |
63fee348f7 | ||
![]() |
e6711c5b5f | ||
![]() |
244c3e0e71 | ||
![]() |
58e1f13842 | ||
![]() |
3eb57bc41d |
2
.github/workflows/actionlint.yml
vendored
2
.github/workflows/actionlint.yml
vendored
@ -93,7 +93,7 @@ jobs:
|
||||
path: results.sarif
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
|
||||
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
|
||||
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
with:
|
||||
languages: ruby
|
||||
config: |
|
||||
@ -35,4 +35,4 @@ jobs:
|
||||
- Library/Homebrew/vendor
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
|
||||
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
|
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@ -52,7 +52,7 @@ jobs:
|
||||
run: vale docs/
|
||||
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@efbf473cab83af4468e8606cc33eca9281bb213f # v1.256.0
|
||||
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
|
||||
with:
|
||||
bundler-cache: true
|
||||
working-directory: docs
|
||||
|
2
.github/workflows/rubydoc.yml
vendored
2
.github/workflows/rubydoc.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Ruby
|
||||
uses: ruby/setup-ruby@efbf473cab83af4468e8606cc33eca9281bb213f # v1.256.0
|
||||
uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
|
||||
with:
|
||||
bundler-cache: true
|
||||
working-directory: rubydoc
|
||||
|
4
.github/workflows/stale-issues.yml
vendored
4
.github/workflows/stale-issues.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests
|
||||
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
|
||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 21
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale `bump-formula-pr` and `bump-cask-pr` Pull Requests
|
||||
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
|
||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 2
|
||||
|
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
@ -104,7 +104,7 @@ jobs:
|
||||
- name: Cache style cache
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ~/.cache/Homebrew/style
|
||||
path: ~/Library/Caches/Homebrew/style
|
||||
key: tap-syntax-style-cache-${{ github.sha }}
|
||||
restore-keys: tap-syntax-style-cache-
|
||||
|
||||
@ -331,7 +331,7 @@ jobs:
|
||||
disable_search: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
|
||||
- uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||
with:
|
||||
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
|
||||
files: Library/Homebrew/test/coverage/coverage.xml
|
||||
@ -495,7 +495,7 @@ jobs:
|
||||
uses: Homebrew/actions/setup-homebrew@main
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
with:
|
||||
python-version-file: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew/formula-analytics/.python-version
|
||||
|
||||
|
@ -45,6 +45,7 @@ RUN apt-get update \
|
||||
tzdata \
|
||||
jq \
|
||||
&& if [ "$(. /etc/lsb-release; echo "${DISTRIB_RELEASE}" | cut -d. -f1)" -ge 22 ]; then apt-get install -y --no-install-recommends skopeo; fi \
|
||||
&& if [ "$(. /etc/lsb-release; echo "${DISTRIB_RELEASE}" | cut -d. -f1)" -eq 22 ]; then apt-get install -y --no-install-recommends g++-12; fi \
|
||||
&& mkdir -p /etc/apt/keyrings \
|
||||
&& chmod 0755 /etc /etc/apt /etc/apt/keyrings \
|
||||
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null \
|
||||
|
@ -6,7 +6,7 @@ GEM
|
||||
ast (2.4.3)
|
||||
base64 (0.3.0)
|
||||
benchmark (0.4.1)
|
||||
bigdecimal (3.2.2)
|
||||
bigdecimal (3.2.3)
|
||||
bindata (2.5.1)
|
||||
coderay (1.1.3)
|
||||
concurrent-ruby (1.3.5)
|
||||
@ -57,7 +57,7 @@ GEM
|
||||
redcarpet (3.6.1)
|
||||
regexp_parser (2.11.2)
|
||||
require-hooks (0.2.2)
|
||||
rexml (3.4.2)
|
||||
rexml (3.4.4)
|
||||
rspec (3.13.1)
|
||||
rspec-core (~> 3.13.0)
|
||||
rspec-expectations (~> 3.13.0)
|
||||
@ -79,7 +79,7 @@ GEM
|
||||
rspec-support (3.13.5)
|
||||
rspec_junit_formatter (0.6.0)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (1.80.1)
|
||||
rubocop (1.80.2)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@ -96,11 +96,11 @@ GEM
|
||||
rubocop-md (2.0.2)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.72.1)
|
||||
rubocop-performance (1.25.0)
|
||||
rubocop-performance (1.26.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (>= 1.75.0, < 2.0)
|
||||
rubocop-ast (>= 1.38.0, < 2.0)
|
||||
rubocop-rspec (3.6.0)
|
||||
rubocop-ast (>= 1.44.0, < 2.0)
|
||||
rubocop-rspec (3.7.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop (~> 1.72, >= 1.72.1)
|
||||
rubocop-sorbet (0.10.5)
|
||||
@ -124,15 +124,15 @@ GEM
|
||||
simplecov-html (0.13.2)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
simpleidn (0.2.3)
|
||||
sorbet (0.6.12466)
|
||||
sorbet-static (= 0.6.12466)
|
||||
sorbet-runtime (0.6.12466)
|
||||
sorbet-static (0.6.12466-aarch64-linux)
|
||||
sorbet-static (0.6.12466-universal-darwin)
|
||||
sorbet-static (0.6.12466-x86_64-linux)
|
||||
sorbet-static-and-runtime (0.6.12466)
|
||||
sorbet (= 0.6.12466)
|
||||
sorbet-runtime (= 0.6.12466)
|
||||
sorbet (0.6.12521)
|
||||
sorbet-static (= 0.6.12521)
|
||||
sorbet-runtime (0.6.12521)
|
||||
sorbet-static (0.6.12521-aarch64-linux)
|
||||
sorbet-static (0.6.12521-universal-darwin)
|
||||
sorbet-static (0.6.12521-x86_64-linux)
|
||||
sorbet-static-and-runtime (0.6.12521)
|
||||
sorbet (= 0.6.12521)
|
||||
sorbet-runtime (= 0.6.12521)
|
||||
spoom (1.7.6)
|
||||
erubi (>= 1.10.0)
|
||||
prism (>= 0.28.0)
|
||||
@ -154,9 +154,9 @@ GEM
|
||||
thor (>= 1.2.0)
|
||||
yard-sorbet
|
||||
thor (1.4.0)
|
||||
unicode-display_width (3.1.5)
|
||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
||||
unicode-emoji (4.0.4)
|
||||
unicode-display_width (3.2.0)
|
||||
unicode-emoji (~> 4.1)
|
||||
unicode-emoji (4.1.0)
|
||||
vernier (1.8.0)
|
||||
warning (1.5.0)
|
||||
yard (0.9.37)
|
||||
|
@ -17,7 +17,7 @@ module Homebrew
|
||||
|
||||
HOMEBREW_CACHE_API = T.let((HOMEBREW_CACHE/"api").freeze, Pathname)
|
||||
HOMEBREW_CACHE_API_SOURCE = T.let((HOMEBREW_CACHE/"api-source").freeze, Pathname)
|
||||
TAP_MIGRATIONS_STALE_SECONDS = T.let(86400, Integer) # 1 day
|
||||
DEFAULT_API_STALE_SECONDS = T.let(86400, Integer) # 1 day
|
||||
|
||||
sig { params(endpoint: String).returns(T::Hash[String, T.untyped]) }
|
||||
def self.fetch(endpoint)
|
||||
@ -37,12 +37,25 @@ module Homebrew
|
||||
raise ArgumentError, "Invalid JSON file: #{Tty.underline}#{api_url}#{Tty.reset}"
|
||||
end
|
||||
|
||||
sig { params(target: Pathname, stale_seconds: T.nilable(Integer)).returns(T::Boolean) }
|
||||
def self.skip_download?(target:, stale_seconds:)
|
||||
return true if Homebrew.running_as_root_but_not_owned_by_root?
|
||||
return false if !target.exist? || target.empty?
|
||||
return true unless stale_seconds
|
||||
|
||||
(Time.now - stale_seconds) < target.mtime
|
||||
end
|
||||
|
||||
sig {
|
||||
params(endpoint: String, target: Pathname, stale_seconds: Integer, download_queue: T.nilable(DownloadQueue))
|
||||
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
params(
|
||||
endpoint: String,
|
||||
target: Pathname,
|
||||
stale_seconds: T.nilable(Integer),
|
||||
download_queue: T.nilable(DownloadQueue),
|
||||
).returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
}
|
||||
def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint,
|
||||
stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i, download_queue: nil)
|
||||
stale_seconds: nil, download_queue: nil)
|
||||
# Lazy-load dependency.
|
||||
require "development_tools"
|
||||
|
||||
@ -63,12 +76,7 @@ module Homebrew
|
||||
|
||||
insecure_download = DevelopmentTools.ca_file_substitution_required? ||
|
||||
DevelopmentTools.curl_substitution_required?
|
||||
skip_download = target.exist? &&
|
||||
!target.empty? &&
|
||||
(!Homebrew.auto_update_command? ||
|
||||
(Homebrew::EnvConfig.no_auto_update? && !Homebrew::EnvConfig.force_api_auto_update?) ||
|
||||
((Time.now - stale_seconds) < target.mtime))
|
||||
skip_download ||= Homebrew.running_as_root_but_not_owned_by_root?
|
||||
skip_download = skip_download?(target:, stale_seconds:)
|
||||
|
||||
if download_queue
|
||||
unless skip_download
|
||||
@ -161,17 +169,28 @@ module Homebrew
|
||||
require "download_queue"
|
||||
Homebrew::DownloadQueue.new
|
||||
end
|
||||
stale_seconds = 86400 # 1 day
|
||||
|
||||
stale_seconds = if ENV["HOMEBREW_API_UPDATED"].present? ||
|
||||
(Homebrew::EnvConfig.no_auto_update? && !Homebrew::EnvConfig.force_api_auto_update?)
|
||||
nil
|
||||
elsif Homebrew.auto_update_command?
|
||||
Homebrew::EnvConfig.api_auto_update_secs.to_i
|
||||
else
|
||||
DEFAULT_API_STALE_SECONDS
|
||||
end
|
||||
|
||||
if Homebrew::EnvConfig.use_internal_api?
|
||||
Homebrew::API::Internal.fetch_formula_api!(download_queue:, stale_seconds:)
|
||||
Homebrew::API::Internal.fetch_cask_api!(download_queue:, stale_seconds:)
|
||||
else
|
||||
Homebrew::API::Formula.fetch_api!(download_queue:, stale_seconds:)
|
||||
Homebrew::API::Formula.fetch_tap_migrations!(download_queue:, stale_seconds:)
|
||||
Homebrew::API::Formula.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS)
|
||||
Homebrew::API::Cask.fetch_api!(download_queue:, stale_seconds:)
|
||||
Homebrew::API::Cask.fetch_tap_migrations!(download_queue:, stale_seconds:)
|
||||
Homebrew::API::Cask.fetch_tap_migrations!(download_queue:, stale_seconds: DEFAULT_API_STALE_SECONDS)
|
||||
end
|
||||
|
||||
ENV["HOMEBREW_API_UPDATED"] = "1"
|
||||
|
||||
return unless download_queue
|
||||
|
||||
begin
|
||||
@ -196,6 +215,7 @@ module Homebrew
|
||||
def self.write_names_file!(names, type, regenerate:)
|
||||
names_path = HOMEBREW_CACHE_API/"#{type}_names.txt"
|
||||
if !names_path.exist? || regenerate
|
||||
names_path.unlink if names_path.exist?
|
||||
names_path.write(names.join("\n"))
|
||||
return true
|
||||
end
|
||||
@ -210,6 +230,7 @@ module Homebrew
|
||||
aliases_text = aliases.map do |alias_name, real_name|
|
||||
"#{alias_name}|#{real_name}"
|
||||
end
|
||||
aliases_path.unlink if aliases_path.exist?
|
||||
aliases_path.write(aliases_text.join("\n"))
|
||||
return true
|
||||
end
|
||||
|
@ -75,18 +75,18 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig {
|
||||
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: Integer)
|
||||
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
|
||||
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
}
|
||||
def self.fetch_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
|
||||
def self.fetch_api!(download_queue: nil, stale_seconds: nil)
|
||||
Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:
|
||||
end
|
||||
|
||||
sig {
|
||||
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: Integer)
|
||||
params(download_queue: T.nilable(::Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
|
||||
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
}
|
||||
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: Homebrew::API::TAP_MIGRATIONS_STALE_SECONDS)
|
||||
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: nil)
|
||||
Homebrew::API.fetch_json_api_file "cask_tap_migrations.jws.json", stale_seconds:, download_queue:
|
||||
end
|
||||
|
||||
|
@ -74,18 +74,18 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig {
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
|
||||
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
}
|
||||
def self.fetch_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
|
||||
def self.fetch_api!(download_queue: nil, stale_seconds: nil)
|
||||
Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME, stale_seconds:, download_queue:
|
||||
end
|
||||
|
||||
sig {
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
|
||||
.returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
|
||||
}
|
||||
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: Homebrew::API::TAP_MIGRATIONS_STALE_SECONDS)
|
||||
def self.fetch_tap_migrations!(download_queue: nil, stale_seconds: nil)
|
||||
Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json", stale_seconds:, download_queue:
|
||||
end
|
||||
|
||||
|
@ -56,20 +56,20 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig {
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
|
||||
.returns([T::Hash[String, T.untyped], T::Boolean])
|
||||
}
|
||||
def self.fetch_formula_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
|
||||
json_contents, updated = (Homebrew::API.fetch_json_api_file formula_endpoint, stale_seconds:, download_queue:)
|
||||
def self.fetch_formula_api!(download_queue: nil, stale_seconds: nil)
|
||||
json_contents, updated = Homebrew::API.fetch_json_api_file(formula_endpoint, stale_seconds:, download_queue:)
|
||||
[T.cast(json_contents, T::Hash[String, T.untyped]), updated]
|
||||
end
|
||||
|
||||
sig {
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: Integer)
|
||||
params(download_queue: T.nilable(Homebrew::DownloadQueue), stale_seconds: T.nilable(Integer))
|
||||
.returns([T::Hash[String, T.untyped], T::Boolean])
|
||||
}
|
||||
def self.fetch_cask_api!(download_queue: nil, stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
|
||||
json_contents, updated = (Homebrew::API.fetch_json_api_file cask_endpoint, stale_seconds:, download_queue:)
|
||||
def self.fetch_cask_api!(download_queue: nil, stale_seconds: nil)
|
||||
json_contents, updated = Homebrew::API.fetch_json_api_file(cask_endpoint, stale_seconds:, download_queue:)
|
||||
[T.cast(json_contents, T::Hash[String, T.untyped]), updated]
|
||||
end
|
||||
|
||||
|
@ -10,13 +10,13 @@ module Homebrew
|
||||
def initialize(url, name, version, **meta)
|
||||
super
|
||||
@target = T.let(meta.fetch(:target), Pathname)
|
||||
@stale_seconds = T.let(meta.fetch(:stale_seconds), Integer)
|
||||
@stale_seconds = T.let(meta[:stale_seconds], T.nilable(Integer))
|
||||
end
|
||||
|
||||
sig { override.params(timeout: T.nilable(T.any(Integer, Float))).returns(Pathname) }
|
||||
def fetch(timeout: nil)
|
||||
with_context quiet: quiet? do
|
||||
Homebrew::API.fetch_json_api_file(url, target: cached_location, stale_seconds: meta.fetch(:stale_seconds))
|
||||
Homebrew::API.fetch_json_api_file(url, target: cached_location, stale_seconds: meta[:stale_seconds])
|
||||
end
|
||||
cached_location
|
||||
end
|
||||
@ -30,7 +30,7 @@ module Homebrew
|
||||
class JSONDownload
|
||||
include Downloadable
|
||||
|
||||
sig { params(url: String, target: Pathname, stale_seconds: Integer).void }
|
||||
sig { params(url: String, target: Pathname, stale_seconds: T.nilable(Integer)).void }
|
||||
def initialize(url, target:, stale_seconds:)
|
||||
super()
|
||||
@url = T.let(URL.new(url, using: API::JSONDownloadStrategy, target:, stale_seconds:), URL)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This script is loaded by formula_installer as a separate instance.
|
||||
@ -23,28 +23,40 @@ require "extend/pathname/write_mkpath_extension"
|
||||
class Build
|
||||
include Utils::Output::Mixin
|
||||
|
||||
attr_reader :formula, :deps, :reqs, :args
|
||||
sig { returns(Formula) }
|
||||
attr_reader :formula
|
||||
|
||||
sig { returns(T::Array[Dependency]) }
|
||||
attr_reader :deps
|
||||
|
||||
sig { returns(Requirements) }
|
||||
attr_reader :reqs
|
||||
|
||||
sig { returns(Homebrew::Cmd::InstallCmd::Args) }
|
||||
attr_reader :args
|
||||
|
||||
sig { params(formula: Formula, options: Options, args: Homebrew::Cmd::InstallCmd::Args).void }
|
||||
def initialize(formula, options, args:)
|
||||
@formula = formula
|
||||
@formula.build = BuildOptions.new(options, formula.options)
|
||||
@args = args
|
||||
@args = T.let(args, Homebrew::Cmd::InstallCmd::Args)
|
||||
@deps = T.let([], T::Array[Dependency])
|
||||
@reqs = T.let(Requirements.new, Requirements)
|
||||
|
||||
if args.ignore_dependencies?
|
||||
@deps = []
|
||||
@reqs = []
|
||||
else
|
||||
@deps = expand_deps
|
||||
@reqs = expand_reqs
|
||||
end
|
||||
return if args.ignore_dependencies?
|
||||
|
||||
@deps = expand_deps
|
||||
@reqs = expand_reqs
|
||||
end
|
||||
|
||||
sig { params(dependent: Formula).returns(BuildOptions) }
|
||||
def effective_build_options_for(dependent)
|
||||
args = dependent.build.used_options
|
||||
args |= Tab.for_formula(dependent).used_options
|
||||
BuildOptions.new(args, dependent.options)
|
||||
end
|
||||
|
||||
sig { returns(Requirements) }
|
||||
def expand_reqs
|
||||
formula.recursive_requirements do |dependent, req|
|
||||
build = effective_build_options_for(dependent)
|
||||
@ -54,6 +66,7 @@ class Build
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[Dependency]) }
|
||||
def expand_deps
|
||||
formula.recursive_dependencies do |dependent, dep|
|
||||
build = effective_build_options_for(dependent)
|
||||
@ -67,6 +80,7 @@ class Build
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def install
|
||||
formula_deps = deps.map(&:to_formula)
|
||||
keg_only_deps = formula_deps.select(&:keg_only?)
|
||||
@ -79,7 +93,7 @@ class Build
|
||||
ENV.activate_extensions!(env: args.env)
|
||||
|
||||
if superenv?(args.env)
|
||||
superenv = T.cast(ENV, Superenv)
|
||||
superenv = ENV
|
||||
superenv.keg_only_deps = keg_only_deps
|
||||
superenv.deps = formula_deps
|
||||
superenv.run_time_deps = run_time_deps
|
||||
@ -183,6 +197,8 @@ class Build
|
||||
|
||||
(formula.logs/"00.options.out").write \
|
||||
"#{formula.full_name} #{formula.build.used_options.sort.join(" ")}".strip
|
||||
|
||||
Pathname.prepend WriteMkpathExtension
|
||||
formula.install
|
||||
|
||||
stdlibs = detect_stdlibs
|
||||
@ -190,7 +206,7 @@ class Build
|
||||
tab.write
|
||||
|
||||
# Find and link metafiles
|
||||
formula.prefix.install_metafiles formula.buildpath
|
||||
formula.prefix.install_metafiles T.must(formula.buildpath)
|
||||
formula.prefix.install_metafiles formula.libexec if formula.libexec.exist?
|
||||
|
||||
normalize_pod2man_outputs!(formula)
|
||||
@ -200,6 +216,7 @@ class Build
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[Symbol]) }
|
||||
def detect_stdlibs
|
||||
keg = Keg.new(formula.prefix)
|
||||
|
||||
@ -209,13 +226,15 @@ class Build
|
||||
keg.detect_cxx_stdlibs(skip_executables: true)
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).void }
|
||||
def fixopt(formula)
|
||||
path = if formula.linked_keg.directory? && formula.linked_keg.symlink?
|
||||
formula.linked_keg.resolved_path
|
||||
elsif formula.prefix.directory?
|
||||
formula.prefix
|
||||
elsif (kids = formula.rack.children).size == 1 && kids.first.directory?
|
||||
kids.first
|
||||
elsif (children = formula.rack.children.presence) && children.size == 1 &&
|
||||
(first_child = children.first.presence) && first_child.directory?
|
||||
first_child
|
||||
else
|
||||
raise
|
||||
end
|
||||
@ -224,6 +243,7 @@ class Build
|
||||
raise "#{formula.opt_prefix} not present or broken\nPlease reinstall #{formula.full_name}. Sorry :("
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).void }
|
||||
def normalize_pod2man_outputs!(formula)
|
||||
keg = Keg.new(formula.prefix)
|
||||
keg.normalize_pod2man_outputs!
|
||||
@ -243,12 +263,10 @@ begin
|
||||
|
||||
trap("INT", old_trap)
|
||||
|
||||
formula = args.named.to_formulae.first
|
||||
formula = args.named.to_formulae.fetch(0)
|
||||
options = Options.create(args.flags_only)
|
||||
build = Build.new(formula, options, args:)
|
||||
|
||||
Pathname.prepend WriteMkpathExtension
|
||||
|
||||
build.install
|
||||
# Any exception means the build did not complete.
|
||||
# The `case` for what to do per-exception class is further down.
|
||||
|
@ -1,11 +1,11 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Settings for the build environment.
|
||||
class BuildEnvironment
|
||||
sig { params(settings: Symbol).void }
|
||||
def initialize(*settings)
|
||||
@settings = Set.new(settings)
|
||||
@settings = T.let(Set.new(settings), T::Set[Symbol])
|
||||
end
|
||||
|
||||
sig { params(args: T::Enumerable[Symbol]).returns(T.self_type) }
|
||||
@ -29,16 +29,17 @@ class BuildEnvironment
|
||||
module DSL
|
||||
# Initialise @env for each class which may use this DSL (e.g. each formula subclass).
|
||||
# `env` may never be called and it needs to be initialised before the class is frozen.
|
||||
sig { params(child: T.untyped).void }
|
||||
def inherited(child)
|
||||
super
|
||||
child.instance_eval do
|
||||
@env = BuildEnvironment.new
|
||||
@env = T.let(BuildEnvironment.new, T.nilable(BuildEnvironment))
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(settings: Symbol).returns(BuildEnvironment) }
|
||||
def env(*settings)
|
||||
@env.merge(settings)
|
||||
T.must(@env).merge(settings)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Options for a formula build.
|
||||
class BuildOptions
|
||||
sig { params(args: Options, options: Options).void }
|
||||
def initialize(args, options)
|
||||
@args = args
|
||||
@options = options
|
||||
@args = T.let(args, Options)
|
||||
@options = T.let(options, Options)
|
||||
end
|
||||
|
||||
# True if a {Formula} is being built with a specific option.
|
||||
@ -29,8 +30,13 @@ class BuildOptions
|
||||
# args << "--with-example1"
|
||||
# end
|
||||
# ```
|
||||
sig { params(val: T.any(String, Requirement, Dependency)).returns(T::Boolean) }
|
||||
def with?(val)
|
||||
option_names = val.respond_to?(:option_names) ? val.option_names : [val]
|
||||
option_names = if val.is_a?(String)
|
||||
[val]
|
||||
else
|
||||
val.option_names
|
||||
end
|
||||
|
||||
option_names.any? do |name|
|
||||
if option_defined? "with-#{name}"
|
||||
@ -50,11 +56,13 @@ class BuildOptions
|
||||
# ```ruby
|
||||
# args << "--no-spam-plz" if build.without? "spam"
|
||||
# ```
|
||||
sig { params(val: T.any(String, Requirement, Dependency)).returns(T::Boolean) }
|
||||
def without?(val)
|
||||
!with?(val)
|
||||
end
|
||||
|
||||
# True if a {Formula} is being built as a bottle (i.e. binary package).
|
||||
sig { returns(T::Boolean) }
|
||||
def bottle?
|
||||
include? "build-bottle"
|
||||
end
|
||||
@ -75,6 +83,7 @@ class BuildOptions
|
||||
# args << "--and-a-cold-beer" if build.with? "cold-beer"
|
||||
# end
|
||||
# ```
|
||||
sig { returns(T::Boolean) }
|
||||
def head?
|
||||
include? "HEAD"
|
||||
end
|
||||
@ -87,29 +96,35 @@ class BuildOptions
|
||||
# ```ruby
|
||||
# args << "--some-feature" if build.stable?
|
||||
# ```
|
||||
sig { returns(T::Boolean) }
|
||||
def stable?
|
||||
!head?
|
||||
end
|
||||
|
||||
# True if the build has any arguments or options specified.
|
||||
sig { returns(T::Boolean) }
|
||||
def any_args_or_options?
|
||||
!@args.empty? || !@options.empty?
|
||||
end
|
||||
|
||||
sig { returns(Options) }
|
||||
def used_options
|
||||
@options & @args
|
||||
end
|
||||
|
||||
sig { returns(Options) }
|
||||
def unused_options
|
||||
@options - @args
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(name: String).returns(T::Boolean) }
|
||||
def include?(name)
|
||||
@args.include?("--#{name}")
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T::Boolean) }
|
||||
def option_defined?(name)
|
||||
@options.include? name
|
||||
end
|
||||
|
@ -4,6 +4,8 @@
|
||||
module Homebrew
|
||||
# Class handling architecture-specific version information.
|
||||
class BumpVersionParser
|
||||
VERSION_SYMBOLS = [:general, :arm, :intel].freeze
|
||||
|
||||
sig { returns(T.nilable(T.any(Version, Cask::DSL::Version))) }
|
||||
attr_reader :arm, :general, :intel
|
||||
|
||||
|
@ -183,7 +183,7 @@ module Homebrew
|
||||
require "bundle/tap_dumper"
|
||||
|
||||
@dsl ||= Brewfile.read(global:, file:)
|
||||
kept_formulae = self.kept_formulae(global:, file:).filter_map(&method(:lookup_formula))
|
||||
kept_formulae = self.kept_formulae(global:, file:).filter_map { lookup_formula(_1) }
|
||||
kept_taps = @dsl.entries.select { |e| e.type == :tap }.map(&:name)
|
||||
kept_taps += kept_formulae.filter_map(&:tap).map(&:name)
|
||||
current_taps = Homebrew::Bundle::TapDumper.tap_names
|
||||
|
@ -40,7 +40,7 @@ module Homebrew
|
||||
@formulae_by_full_name ||= {}
|
||||
|
||||
if name.nil?
|
||||
formulae = Formula.installed.map(&method(:add_formula))
|
||||
formulae = Formula.installed.map { add_formula(_1) }
|
||||
sort!(formulae)
|
||||
return @formulae_by_full_name
|
||||
end
|
||||
|
@ -172,7 +172,7 @@ module Cask
|
||||
.stdout.lines.drop(1) # skip stdout column headers
|
||||
.filter_map do |line|
|
||||
pid, _state, id = line.chomp.split(/\s+/)
|
||||
id if pid.to_i.nonzero? && id.match?(regex)
|
||||
id if pid.to_i.nonzero? && T.must(id).match?(regex)
|
||||
end
|
||||
end
|
||||
|
||||
@ -460,9 +460,9 @@ module Cask
|
||||
def trash_paths(*paths, command: nil, **_)
|
||||
return if paths.empty?
|
||||
|
||||
stdout, = system_command HOMEBREW_LIBRARY_PATH/"cask/utils/trash.swift",
|
||||
args: paths,
|
||||
print_stderr: Homebrew::EnvConfig.developer?
|
||||
stdout = system_command(HOMEBREW_LIBRARY_PATH/"cask/utils/trash.swift",
|
||||
args: paths,
|
||||
print_stderr: Homebrew::EnvConfig.developer?).stdout
|
||||
|
||||
trashed, _, untrashable = stdout.partition("\n")
|
||||
trashed = trashed.split(":")
|
||||
|
@ -79,8 +79,9 @@ module Cask
|
||||
# Try to make the asset searchable under the target name. Spotlight
|
||||
# respects this attribute for many filetypes, but ignores it for App
|
||||
# bundles. Alfred 2.2 respects it even for App bundles.
|
||||
def add_altname_metadata(file, altname, command: nil)
|
||||
return if altname.to_s.casecmp(file.basename.to_s).zero?
|
||||
sig { params(file: Pathname, altname: Pathname, command: T.class_of(SystemCommand)).returns(T.nilable(SystemCommand::Result)) }
|
||||
def add_altname_metadata(file, altname, command:)
|
||||
return if altname.to_s.casecmp(file.basename.to_s)&.zero?
|
||||
|
||||
odebug "Adding #{ALT_NAME_ATTRIBUTE} metadata"
|
||||
altnames = command.run("/usr/bin/xattr",
|
||||
@ -108,3 +109,5 @@ module Cask
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "extend/os/cask/artifact/relocated"
|
||||
|
@ -543,6 +543,7 @@ module Cask
|
||||
print_stderr: false)
|
||||
else
|
||||
add_error "Unknown artifact type: #{artifact.class}", location: url.location
|
||||
next
|
||||
end
|
||||
|
||||
next false if result.success?
|
||||
@ -622,15 +623,17 @@ module Cask
|
||||
}.compact
|
||||
|
||||
Homebrew::Install.perform_preinstall_checks_once
|
||||
valid_formula_installers = Homebrew::Install.fetch_formulae(primary_container.dependencies)
|
||||
|
||||
primary_container.dependencies.each do |dep|
|
||||
next unless valid_formula_installers.include?(dep)
|
||||
|
||||
fi = FormulaInstaller.new(
|
||||
formula_installers = primary_container.dependencies.map do |dep|
|
||||
FormulaInstaller.new(
|
||||
dep,
|
||||
**install_options,
|
||||
)
|
||||
end
|
||||
valid_formula_installers = Homebrew::Install.fetch_formulae(formula_installers)
|
||||
|
||||
formula_installers.each do |fi|
|
||||
next unless valid_formula_installers.include?(fi)
|
||||
|
||||
fi.install
|
||||
fi.finish
|
||||
end
|
||||
@ -695,7 +698,7 @@ module Cask
|
||||
add_error "No binaries in App: #{artifact.source}", location: url.location if files.empty?
|
||||
|
||||
main_binary = get_plist_main_binary(path)
|
||||
main_binary ||= files.first
|
||||
main_binary ||= files.fetch(0)
|
||||
|
||||
system_command("lipo", args: ["-archs", main_binary], print_stderr: false)
|
||||
when Artifact::Binary
|
||||
@ -753,9 +756,9 @@ module Cask
|
||||
latest_version = Homebrew::Livecheck.latest_version(
|
||||
cask,
|
||||
referenced_formula_or_cask: referenced_cask,
|
||||
)&.fetch(:latest)
|
||||
)&.fetch(:latest, nil)
|
||||
|
||||
return :auto_detected if cask.version.to_s == latest_version.to_s
|
||||
return :auto_detected if latest_version && (cask.version.to_s == latest_version.to_s)
|
||||
|
||||
add_error "Version '#{cask.version}' differs from '#{latest_version}' retrieved by livecheck."
|
||||
|
||||
|
@ -6,6 +6,7 @@ require "cask/cask"
|
||||
require "uri"
|
||||
require "utils/curl"
|
||||
require "utils/output"
|
||||
require "utils/path"
|
||||
require "extend/hash/keys"
|
||||
require "api"
|
||||
|
||||
@ -112,9 +113,7 @@ module Cask
|
||||
|
||||
return unless path.expand_path.exist?
|
||||
return if invalid_path?(path)
|
||||
|
||||
return if Homebrew::EnvConfig.forbid_packages_from_paths? &&
|
||||
!path.realpath.to_s.start_with?("#{Caskroom.path}/", "#{HOMEBREW_LIBRARY}/Taps/")
|
||||
return unless ::Utils::Path.loadable_package_path?(path, :cask)
|
||||
|
||||
new(path)
|
||||
end
|
||||
|
@ -49,7 +49,7 @@ module Cask
|
||||
|
||||
SystemCommand.run("/bin/mkdir", args: ["-p", path], sudo:)
|
||||
SystemCommand.run("/bin/chmod", args: ["g+rwx", path], sudo:)
|
||||
SystemCommand.run("/usr/sbin/chown", args: [User.current, path], sudo:)
|
||||
SystemCommand.run("/usr/sbin/chown", args: [User.current.to_s, path], sudo:)
|
||||
SystemCommand.run("/usr/bin/chgrp", args: ["admin", path], sudo:)
|
||||
end
|
||||
|
||||
|
@ -733,7 +733,7 @@ module Homebrew
|
||||
formulae_names = removable_formulae.map(&:full_name).sort
|
||||
|
||||
verb = dry_run ? "Would autoremove" : "Autoremoving"
|
||||
oh1 "#{verb} #{formulae_names.count} unneeded #{Utils.pluralize("formula", formulae_names.count, plural: "e")}:"
|
||||
oh1 "#{verb} #{formulae_names.count} unneeded #{Utils.pluralize("formula", formulae_names.count)}:"
|
||||
puts formulae_names.join("\n")
|
||||
return if dry_run
|
||||
|
||||
|
@ -127,7 +127,7 @@ module Homebrew
|
||||
conflicts "--all", "--no-vscode"
|
||||
conflicts "--vscode", "--no-vscode"
|
||||
conflicts "--install", "--upgrade"
|
||||
conflicts "--file=", "--global"
|
||||
conflicts "--file", "--global"
|
||||
|
||||
named_args %w[install dump cleanup check exec list sh env edit]
|
||||
end
|
||||
|
@ -77,7 +77,6 @@ module Homebrew
|
||||
description: "Show the size of installed formulae and casks."
|
||||
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--fetch-manifest", "--cask"
|
||||
conflicts "--fetch-manifest", "--json"
|
||||
|
@ -233,8 +233,8 @@ module Homebrew
|
||||
.map(&:name)
|
||||
next if dep_names.blank?
|
||||
|
||||
ohai "Would install #{::Utils.pluralize("dependenc", dep_names.count, plural: "ies", singular: "y",
|
||||
include_count: true)} for #{cask.full_name}:"
|
||||
ohai "Would install #{::Utils.pluralize("dependency", dep_names.count, include_count: true)} " \
|
||||
"for #{cask.full_name}:"
|
||||
puts dep_names.join(" ")
|
||||
end
|
||||
return
|
||||
|
@ -23,7 +23,7 @@ module Homebrew
|
||||
flag "--command=",
|
||||
description: "Show options for the specified <command>."
|
||||
|
||||
conflicts "--installed", "--all", "--command"
|
||||
conflicts "--command", "--installed", "--eval-all"
|
||||
|
||||
named_args :formula
|
||||
end
|
||||
@ -47,7 +47,7 @@ module Homebrew
|
||||
puts
|
||||
end
|
||||
elsif args.no_named?
|
||||
raise FormulaUnspecifiedError
|
||||
raise UsageError, "`brew options` needs a formula or `--eval-all` passed or `HOMEBREW_EVAL_ALL=1` set!"
|
||||
else
|
||||
puts_options args.named.to_formulae
|
||||
end
|
||||
|
@ -37,8 +37,7 @@ module Homebrew
|
||||
description: "Search for casks."
|
||||
switch "--desc",
|
||||
description: "Search for formulae with a description matching <text> and casks with " \
|
||||
"a name or description matching <text>.",
|
||||
depends_on: "--eval-all"
|
||||
"a name or description matching <text>."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to search their " \
|
||||
"descriptions.",
|
||||
|
@ -68,7 +68,7 @@ module Homebrew
|
||||
description: "Output as JSON."
|
||||
|
||||
conflicts "--all", "--file"
|
||||
conflicts "--max-wait=", "--no-wait"
|
||||
conflicts "--max-wait", "--no-wait"
|
||||
|
||||
named_args %w[list info run start stop kill restart cleanup]
|
||||
end
|
||||
|
@ -57,7 +57,7 @@ module Homebrew
|
||||
end
|
||||
info = Utils.pluralize("tap", tap_count, include_count: true)
|
||||
info += ", #{private_count} private"
|
||||
info += ", #{Utils.pluralize("formula", formula_count, plural: "e", include_count: true)}"
|
||||
info += ", #{Utils.pluralize("formula", formula_count, include_count: true)}"
|
||||
info += ", #{Utils.pluralize("command", command_count, include_count: true)}"
|
||||
info += ", #{HOMEBREW_TAP_DIRECTORY.dup.abv}" if HOMEBREW_TAP_DIRECTORY.directory?
|
||||
puts info
|
||||
|
@ -842,7 +842,7 @@ class ReporterHub
|
||||
msg = ""
|
||||
|
||||
if outdated_formulae.positive?
|
||||
noun = Utils.pluralize("formula", outdated_formulae, plural: "e")
|
||||
noun = Utils.pluralize("formula", outdated_formulae)
|
||||
msg += "#{Tty.bold}#{outdated_formulae}#{Tty.reset} outdated #{noun}"
|
||||
end
|
||||
|
||||
|
@ -55,7 +55,7 @@ module Homebrew
|
||||
description: "Include only casks."
|
||||
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--missing", "--installed"
|
||||
|
||||
named_args :formula, min: 1
|
||||
|
@ -128,7 +128,7 @@ module Commands
|
||||
OFFICIAL_CMD_TAPS.flat_map do |tap_name, cmds|
|
||||
tap = Tap.fetch(tap_name)
|
||||
tap.install(quiet:) unless tap.installed?
|
||||
cmds.map(&method(:external_ruby_v2_cmd_path)).compact
|
||||
cmds.map { external_ruby_v2_cmd_path(_1) }.compact
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,6 +28,7 @@ module DeprecateDisable
|
||||
no_longer_meets_criteria: "no longer meets the criteria for acceptable casks",
|
||||
unmaintained: "is not maintained upstream",
|
||||
fails_gatekeeper_check: "does not pass the macOS Gatekeeper check",
|
||||
unreachable: "is no longer reliably reachable upstream",
|
||||
# odeprecate: remove the unsigned reason in a future release
|
||||
unsigned: "is unsigned or does not meet signature requirements",
|
||||
}.freeze, T::Hash[Symbol, String])
|
||||
|
@ -83,7 +83,7 @@ class DescriptionCacheStore < CacheStore
|
||||
def delete_from_formula_names!(formula_names)
|
||||
return if database.empty?
|
||||
|
||||
formula_names.each(&method(:delete!))
|
||||
formula_names.each { delete!(_1) }
|
||||
end
|
||||
alias delete_from_cask_tokens! delete_from_formula_names!
|
||||
|
||||
|
@ -93,11 +93,11 @@ module Homebrew
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--only", "--except"
|
||||
conflicts "--only-cops", "--except-cops", "--strict"
|
||||
conflicts "--only-cops", "--except-cops", "--only"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--installed", "--all"
|
||||
|
||||
named_args [:formula, :cask], without_api: true
|
||||
end
|
||||
@ -294,9 +294,7 @@ module Homebrew
|
||||
errors_summary = Utils.pluralize("problem", total_problems_count, include_count: true)
|
||||
|
||||
error_sources = []
|
||||
if formula_count.positive?
|
||||
error_sources << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
|
||||
end
|
||||
error_sources << Utils.pluralize("formula", formula_count, include_count: true) if formula_count.positive?
|
||||
error_sources << Utils.pluralize("cask", cask_count, include_count: true) if cask_count.positive?
|
||||
error_sources << Utils.pluralize("tap", tap_count, include_count: true) if tap_count.positive?
|
||||
|
||||
|
@ -182,7 +182,8 @@ module Homebrew
|
||||
|
||||
sig {
|
||||
params(old_keys: T::Array[String], old_bottle_spec: BottleSpecification,
|
||||
new_bottle_hash: T::Hash[String, T.untyped]).returns(T::Array[T::Array[String]])
|
||||
new_bottle_hash: T::Hash[String, T.untyped])
|
||||
.returns([T::Array[String], T::Array[T::Hash[Symbol, T.any(String, Symbol)]]])
|
||||
}
|
||||
def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
|
||||
mismatches = []
|
||||
@ -409,7 +410,7 @@ module Homebrew
|
||||
|
||||
bottle_tag, rebuild = if local_bottle_json
|
||||
_, tag_string, rebuild_string = Utils::Bottles.extname_tag_rebuild(formula.local_bottle_path.to_s)
|
||||
[tag_string.to_sym, rebuild_string.to_i]
|
||||
[T.must(tag_string).to_sym, rebuild_string.to_i]
|
||||
end
|
||||
|
||||
bottle_tag = if bottle_tag
|
||||
@ -860,8 +861,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig {
|
||||
params(formula: Formula, formula_ast: Utils::AST::FormulaAST,
|
||||
bottle_hash: T::Hash[String, T.untyped]).returns(T.nilable(T::Array[String]))
|
||||
params(formula: Formula, formula_ast: Utils::AST::FormulaAST, bottle_hash: T::Hash[String, T.untyped])
|
||||
.returns(T.nilable(T::Array[T::Hash[Symbol, T.any(String, Symbol)]]))
|
||||
}
|
||||
def old_checksums(formula, formula_ast, bottle_hash)
|
||||
bottle_node = T.cast(formula_ast.bottle_block, T.nilable(RuboCop::AST::BlockNode))
|
||||
|
@ -49,8 +49,8 @@ module Homebrew
|
||||
description: "Use the specified GitHub organization for forking."
|
||||
|
||||
conflicts "--dry-run", "--write"
|
||||
conflicts "--version=", "--version-arm="
|
||||
conflicts "--version=", "--version-intel="
|
||||
conflicts "--version", "--version-arm"
|
||||
conflicts "--version", "--version-intel"
|
||||
|
||||
named_args :cask, number: 1, without_api: true
|
||||
end
|
||||
|
@ -9,6 +9,8 @@ require "utils/repology"
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
class Bump < AbstractCommand
|
||||
NEWER_THAN_UPSTREAM_MSG = " (newer than upstream)"
|
||||
|
||||
class VersionBumpInfo < T::Struct
|
||||
const :type, Symbol
|
||||
const :multiple_versions, T::Boolean
|
||||
@ -16,6 +18,7 @@ module Homebrew
|
||||
const :current_version, BumpVersionParser
|
||||
const :repology_latest, T.any(String, Version)
|
||||
const :new_version, BumpVersionParser
|
||||
const :newer_than_upstream, T::Hash[Symbol, T::Boolean], default: {}
|
||||
const :duplicate_pull_requests, T.nilable(T.any(T::Array[String], String))
|
||||
const :maybe_duplicate_pull_requests, T.nilable(T.any(T::Array[String], String))
|
||||
end
|
||||
@ -57,10 +60,10 @@ module Homebrew
|
||||
switch "--bump-synced",
|
||||
description: "Bump additional formulae marked as synced with the given formulae."
|
||||
|
||||
conflicts "--cask", "--formula"
|
||||
conflicts "--tap=", "--installed"
|
||||
conflicts "--tap=", "--no-autobump"
|
||||
conflicts "--eval-all", "--installed"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--tap", "--installed"
|
||||
conflicts "--tap", "--no-autobump"
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--installed", "--auto"
|
||||
conflicts "--no-pull-requests", "--open-pr"
|
||||
|
||||
@ -315,6 +318,7 @@ module Homebrew
|
||||
new_versions = {}
|
||||
|
||||
repology_latest = repositories.present? ? Repology.latest_version(repositories) : "not found"
|
||||
repology_latest_is_a_version = repology_latest.is_a?(Version)
|
||||
|
||||
# When blocks are absent, arch is not relevant. For consistency, we simulate the arm architecture.
|
||||
arch_options = is_cask_with_blocks ? OnSystem::ARCH_OPTIONS : [:arm]
|
||||
@ -331,23 +335,31 @@ module Homebrew
|
||||
loaded_formula_or_cask = Cask::CaskLoader.load(formula_or_cask.sourcefile_path)
|
||||
current_version_value = Version.new(loaded_formula_or_cask.version)
|
||||
end
|
||||
formula_or_cask_has_livecheck = loaded_formula_or_cask.livecheck_defined?
|
||||
|
||||
livecheck_latest = livecheck_result(loaded_formula_or_cask)
|
||||
livecheck_latest_is_a_version = livecheck_latest.is_a?(Version)
|
||||
|
||||
new_version_value = if (livecheck_latest.is_a?(Version) &&
|
||||
new_version_value = if (livecheck_latest_is_a_version &&
|
||||
Livecheck::LivecheckVersion.create(formula_or_cask, livecheck_latest) >=
|
||||
Livecheck::LivecheckVersion.create(formula_or_cask, current_version_value)) ||
|
||||
current_version_value == "latest"
|
||||
livecheck_latest
|
||||
elsif livecheck_latest.is_a?(String) && livecheck_latest.start_with?("skipped")
|
||||
"skipped"
|
||||
elsif repology_latest.is_a?(Version) &&
|
||||
elsif repology_latest_is_a_version &&
|
||||
!formula_or_cask_has_livecheck &&
|
||||
repology_latest > current_version_value &&
|
||||
!loaded_formula_or_cask.livecheck_defined? &&
|
||||
current_version_value != "latest"
|
||||
repology_latest
|
||||
end.presence
|
||||
|
||||
# Fall back to the upstream version if there isn't a new version
|
||||
# value at this point, as this will allow us to surface an upstream
|
||||
# version that's lower than the current version.
|
||||
new_version_value ||= livecheck_latest if livecheck_latest_is_a_version
|
||||
new_version_value ||= repology_latest if repology_latest_is_a_version && !formula_or_cask_has_livecheck
|
||||
|
||||
# Store old and new versions
|
||||
old_versions[version_key] = current_version_value
|
||||
new_versions[version_key] = new_version_value
|
||||
@ -380,10 +392,22 @@ module Homebrew
|
||||
new_version = BumpVersionParser.new(general: "unable to get versions")
|
||||
end
|
||||
|
||||
newer_than_upstream = {}
|
||||
BumpVersionParser::VERSION_SYMBOLS.each do |version_type|
|
||||
new_version_value = new_version.send(version_type)
|
||||
next unless new_version_value.is_a?(Version)
|
||||
|
||||
newer_than_upstream[version_type] =
|
||||
(current_version_value = current_version.send(version_type)).is_a?(Version) &&
|
||||
(Livecheck::LivecheckVersion.create(formula_or_cask, current_version_value) >
|
||||
Livecheck::LivecheckVersion.create(formula_or_cask, new_version_value))
|
||||
end
|
||||
|
||||
if !args.no_pull_requests? &&
|
||||
(new_version.general != "unable to get versions") &&
|
||||
(new_version.general != "skipped") &&
|
||||
(new_version != current_version)
|
||||
(new_version != current_version) &&
|
||||
!newer_than_upstream.all? { |_k, v| v == true }
|
||||
# We use the ARM version for the pull request version. This is
|
||||
# consistent with the behavior of bump-cask-pr.
|
||||
pull_request_version = if multiple_versions
|
||||
@ -410,6 +434,7 @@ module Homebrew
|
||||
current_version:,
|
||||
repology_latest:,
|
||||
new_version:,
|
||||
newer_than_upstream:,
|
||||
duplicate_pull_requests:,
|
||||
maybe_duplicate_pull_requests:,
|
||||
)
|
||||
@ -432,8 +457,8 @@ module Homebrew
|
||||
new_version = version_info.new_version
|
||||
repology_latest = version_info.repology_latest
|
||||
|
||||
# Check if all versions are equal
|
||||
versions_equal = (new_version == current_version)
|
||||
all_newer_than_upstream = version_info.newer_than_upstream.all? { |_k, v| v == true }
|
||||
|
||||
title_name = ambiguous_cask ? "#{name} (cask)" : name
|
||||
title = if (repology_latest == current_version.general || !repology_latest.is_a?(Version)) && versions_equal
|
||||
@ -444,10 +469,13 @@ module Homebrew
|
||||
|
||||
# Conditionally format output based on type of formula_or_cask
|
||||
current_versions = if version_info.multiple_versions
|
||||
"arm: #{current_version.arm}
|
||||
intel: #{current_version.intel}"
|
||||
"arm: #{current_version.arm}" \
|
||||
"#{NEWER_THAN_UPSTREAM_MSG if version_info.newer_than_upstream[:arm]}" \
|
||||
"\n intel: #{current_version.intel}" \
|
||||
"#{NEWER_THAN_UPSTREAM_MSG if version_info.newer_than_upstream[:intel]}"
|
||||
else
|
||||
current_version.general.to_s
|
||||
newer_than_upstream_general = version_info.newer_than_upstream[:general]
|
||||
"#{current_version.general}#{NEWER_THAN_UPSTREAM_MSG if newer_than_upstream_general}"
|
||||
end
|
||||
current_versions << " (deprecated)" if formula_or_cask.deprecated?
|
||||
|
||||
@ -482,7 +510,8 @@ module Homebrew
|
||||
if !args.no_pull_requests? &&
|
||||
(new_version.general != "unable to get versions") &&
|
||||
(new_version.general != "skipped") &&
|
||||
!versions_equal
|
||||
!versions_equal &&
|
||||
!all_newer_than_upstream
|
||||
if duplicate_pull_requests
|
||||
duplicate_pull_requests_text = duplicate_pull_requests
|
||||
elsif maybe_duplicate_pull_requests
|
||||
@ -501,7 +530,8 @@ module Homebrew
|
||||
|
||||
if !args.open_pr? ||
|
||||
(new_version.general == "unable to get versions") ||
|
||||
(new_version.general == "skipped")
|
||||
(new_version.general == "skipped") ||
|
||||
all_newer_than_upstream
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -6,13 +6,23 @@ require "abstract_command"
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
class Contributions < AbstractCommand
|
||||
PRIMARY_REPOS = T.let(%w[brew core cask].freeze, T::Array[String])
|
||||
SUPPORTED_REPOS = T.let([
|
||||
PRIMARY_REPOS,
|
||||
OFFICIAL_CMD_TAPS.keys.map { |t| t.delete_prefix("homebrew/") },
|
||||
OFFICIAL_CASK_TAPS.reject { |t| t == "cask" },
|
||||
].flatten.freeze, T::Array[String])
|
||||
MAX_REPO_COMMITS = 1000
|
||||
PRIMARY_REPOS = T.let(%w[
|
||||
Homebrew/brew
|
||||
Homebrew/homebrew-core
|
||||
Homebrew/homebrew-cask
|
||||
].freeze, T::Array[String])
|
||||
ALL_REPOS = T.let([
|
||||
*PRIMARY_REPOS,
|
||||
*OFFICIAL_CMD_TAPS.keys,
|
||||
].freeze, T::Array[String])
|
||||
CONTRIBUTION_TYPES = T.let({
|
||||
merged_pr_author: "merged PR author",
|
||||
approved_pr_review: "approved PR reviewer",
|
||||
committer: "commit author or committer",
|
||||
coauthor: "commit coauthor",
|
||||
}.freeze, T::Hash[Symbol, String])
|
||||
MAX_COMMITS = T.let(1000, Integer)
|
||||
MAX_PR_SEARCH = T.let(100, Integer)
|
||||
|
||||
cmd_args do
|
||||
usage_banner "`contributions` [`--user=`] [`--repositories=`] [`--from=`] [`--to=`] [`--csv`]"
|
||||
@ -24,10 +34,17 @@ module Homebrew
|
||||
"contributions from. Omitting this flag searches Homebrew maintainers."
|
||||
comma_array "--repositories",
|
||||
description: "Specify a comma-separated list of repositories to search. " \
|
||||
"Supported repositories: #{SUPPORTED_REPOS.map { |t| "`#{t}`" }.to_sentence}. " \
|
||||
"All repositories must be under the same user or organisation. " \
|
||||
"Omitting this flag, or specifying `--repositories=primary`, searches only the " \
|
||||
"main repositories: `brew`, `core`, `cask`. " \
|
||||
"Specifying `--repositories=all` searches all repositories. "
|
||||
"main repositories: `Homebrew/brew`, `Homebrew/homebrew-core`, " \
|
||||
"`Homebrew/homebrew-cask`. Specifying `--repositories=all` searches all " \
|
||||
"non-deprecated Homebrew repositories. "
|
||||
flag "--organisation=", "--organization=", "--org=",
|
||||
description: "Specify the organisation to populate sources repositories from. " \
|
||||
"Omitting this flag searches the Homebrew primary repositories."
|
||||
flag "--team=",
|
||||
description: "Specify the team to populate users from. " \
|
||||
"The first part of the team name will be used as the organisation."
|
||||
flag "--from=",
|
||||
description: "Date (ISO 8601 format) to start searching contributions. " \
|
||||
"Omitting this flag searches the past year."
|
||||
@ -35,54 +52,93 @@ module Homebrew
|
||||
description: "Date (ISO 8601 format) to stop searching contributions."
|
||||
switch "--csv",
|
||||
description: "Print a CSV of contributions across repositories over the time period."
|
||||
conflicts "--organisation", "--repositories"
|
||||
conflicts "--organisation", "--team"
|
||||
conflicts "--user", "--team"
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
odie "Cannot get contributions as `$HOMEBREW_NO_GITHUB_API` is set!" if Homebrew::EnvConfig.no_github_api?
|
||||
|
||||
Homebrew.install_bundler_gems!(groups: ["contributions"]) if args.csv?
|
||||
|
||||
require "utils/github"
|
||||
|
||||
results = {}
|
||||
grand_totals = {}
|
||||
from = args.from.presence || Date.today.prev_year.iso8601
|
||||
to = args.to.presence || (Date.today + 1).iso8601
|
||||
organisation = nil
|
||||
|
||||
repos = T.must(
|
||||
if args.repositories.blank? || args.repositories&.include?("primary")
|
||||
PRIMARY_REPOS
|
||||
elsif args.repositories&.include?("all")
|
||||
SUPPORTED_REPOS
|
||||
else
|
||||
args.repositories
|
||||
end,
|
||||
)
|
||||
|
||||
repos.each do |repo|
|
||||
if SUPPORTED_REPOS.exclude?(repo)
|
||||
odie "Unsupported repository: #{repo}. Try one of #{SUPPORTED_REPOS.join(", ")}."
|
||||
users = if (team = args.team.presence)
|
||||
team_sections = team.split("/")
|
||||
organisation = team_sections.first.presence
|
||||
team_name = team_sections.last.presence
|
||||
if team_sections.length != 2 || organisation.nil? || team_name.nil?
|
||||
odie "Team must be in the format `organisation/team`!"
|
||||
end
|
||||
|
||||
puts "Getting members for #{organisation}/#{team_name}..." if args.verbose?
|
||||
GitHub.members_by_team(organisation, team_name).keys
|
||||
elsif (users = args.user.presence)
|
||||
users
|
||||
else
|
||||
puts "Getting members for Homebrew/maintainers..." if args.verbose?
|
||||
GitHub.members_by_team("Homebrew", "maintainers").keys
|
||||
end
|
||||
|
||||
from = args.from.presence || Date.today.prev_year.iso8601
|
||||
repositories = if (org = organisation.presence) || (org = args.organisation.presence)
|
||||
organisation = org
|
||||
puts "Getting repositories for #{organisation}..." if args.verbose?
|
||||
GitHub.organisation_repositories(organisation, from, to, args.verbose?)
|
||||
elsif (repos = args.repositories.presence) && repos.length == 1 && (first_repository = repos.first)
|
||||
case first_repository
|
||||
when "primary"
|
||||
PRIMARY_REPOS
|
||||
when "all"
|
||||
ALL_REPOS
|
||||
else
|
||||
Array(first_repository)
|
||||
end
|
||||
elsif (repos = args.repositories.presence)
|
||||
organisations = repos.map { |repository| repository.split("/").first }.uniq
|
||||
odie "All repositories must be under the same user or organisation!" if organisations.length > 1
|
||||
|
||||
contribution_types = [:author, :committer, :coauthor, :review]
|
||||
repos
|
||||
else
|
||||
PRIMARY_REPOS
|
||||
end
|
||||
organisation ||= T.must(repositories.fetch(0).split("/").first)
|
||||
|
||||
require "utils/github"
|
||||
users = args.user.presence || GitHub.members_by_team("Homebrew", "maintainers").keys
|
||||
users.each do |username|
|
||||
# TODO: Using the GitHub username to scan the `git log` undercounts some
|
||||
# contributions as people might not always have configured their Git
|
||||
# committer details to match the ones on GitHub.
|
||||
# TODO: Switch to using the GitHub APIs instead of `git log` if
|
||||
# they ever support trailers.
|
||||
results[username] = scan_repositories(repos, username, from:)
|
||||
results[username] = scan_repositories(organisation, repositories, username, from:, to:)
|
||||
grand_totals[username] = total(results[username])
|
||||
|
||||
contributions = contribution_types.filter_map do |type|
|
||||
search_types = [:merged_pr_author, :approved_pr_review].freeze
|
||||
greater_than_total = T.let(false, T::Boolean)
|
||||
contributions = CONTRIBUTION_TYPES.keys.filter_map do |type|
|
||||
type_count = grand_totals[username][type]
|
||||
next if type_count.to_i.zero?
|
||||
next if type_count.zero?
|
||||
|
||||
"#{Utils.pluralize("time", type_count, include_count: true)} (#{type})"
|
||||
count_prefix = ""
|
||||
if (search_types.include?(type) && type_count == MAX_PR_SEARCH) ||
|
||||
(type == :committer && type_count == MAX_COMMITS)
|
||||
greater_than_total ||= true
|
||||
count_prefix = ">="
|
||||
end
|
||||
|
||||
pretty_type = CONTRIBUTION_TYPES.fetch(type)
|
||||
"#{count_prefix}#{Utils.pluralize("time", type_count, include_count: true)} (#{pretty_type})"
|
||||
end
|
||||
contributions <<
|
||||
"#{Utils.pluralize("time", grand_totals[username].values.sum, include_count: true)} (total)"
|
||||
total = Utils.pluralize("time", grand_totals[username].values.sum, include_count: true)
|
||||
total_prefix = ">=" if greater_than_total
|
||||
contributions << "#{total_prefix}#{total} (total)"
|
||||
|
||||
contributions_string = [
|
||||
"#{username} contributed",
|
||||
@ -104,12 +160,16 @@ module Homebrew
|
||||
|
||||
private
|
||||
|
||||
sig { params(repo: String).returns(Pathname) }
|
||||
def find_repo_path_for_repo(repo)
|
||||
return HOMEBREW_REPOSITORY if repo == "brew"
|
||||
sig { params(repository: String).returns([T.nilable(Pathname), T.nilable(Tap)]) }
|
||||
def repository_path_and_tap(repository)
|
||||
return [HOMEBREW_REPOSITORY, nil] if repository == "Homebrew/brew"
|
||||
return [nil, nil] if repository.exclude?("/homebrew-")
|
||||
|
||||
require "tap"
|
||||
Tap.fetch("homebrew", repo).path
|
||||
tap = Tap.fetch(repository)
|
||||
return [nil, nil] if tap.user == "Homebrew" && DEPRECATED_OFFICIAL_TAPS.include?(tap.repository)
|
||||
|
||||
[tap.path, tap]
|
||||
end
|
||||
|
||||
sig { params(from: T.nilable(String), to: T.nilable(String)).returns(String) }
|
||||
@ -130,7 +190,7 @@ module Homebrew
|
||||
require "csv"
|
||||
|
||||
CSV.generate do |csv|
|
||||
csv << %w[user repo author committer coauthor review total]
|
||||
csv << ["user", "repository", *CONTRIBUTION_TYPES.keys, "total"]
|
||||
|
||||
totals.sort_by { |_, v| -v.values.sum }.each do |user, total|
|
||||
csv << grand_total_row(user, total)
|
||||
@ -138,63 +198,67 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
user: String,
|
||||
grand_total: T::Hash[Symbol, Integer],
|
||||
).returns(
|
||||
[String, String, T.nilable(Integer), T.nilable(Integer), T.nilable(Integer), T.nilable(Integer), Integer],
|
||||
)
|
||||
}
|
||||
sig { params(user: String, grand_total: T::Hash[Symbol, Integer]).returns(T::Array[T.any(String, T.nilable(Integer))]) }
|
||||
def grand_total_row(user, grand_total)
|
||||
[
|
||||
user,
|
||||
"all",
|
||||
grand_total[:author],
|
||||
grand_total[:committer],
|
||||
grand_total[:coauthor],
|
||||
grand_total[:review],
|
||||
grand_total.values.sum,
|
||||
]
|
||||
grand_totals = grand_total.slice(*CONTRIBUTION_TYPES.keys).values
|
||||
[user, "all", *grand_totals, grand_totals.sum]
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
repos: T::Array[String],
|
||||
person: String,
|
||||
from: String,
|
||||
organisation: String,
|
||||
repositories: T::Array[String],
|
||||
person: String,
|
||||
from: String,
|
||||
to: String,
|
||||
).returns(T::Hash[Symbol, T.untyped])
|
||||
}
|
||||
def scan_repositories(repos, person, from:)
|
||||
def scan_repositories(organisation, repositories, person, from:, to:)
|
||||
data = {}
|
||||
return data if repos.blank?
|
||||
return data if repositories.blank?
|
||||
|
||||
require "tap"
|
||||
require "utils/github"
|
||||
repos.each do |repo|
|
||||
repo_path = find_repo_path_for_repo(repo)
|
||||
tap = Tap.fetch("homebrew", repo)
|
||||
unless repo_path.exist?
|
||||
opoo "Repository #{repo} not yet tapped! Tapping it now..."
|
||||
|
||||
max = MAX_COMMITS
|
||||
verbose = args.verbose?
|
||||
|
||||
puts "Querying pull requests for #{person} in #{organisation}..." if args.verbose?
|
||||
organisation_merged_prs = \
|
||||
GitHub.search_merged_pull_requests_in_user_or_organisation(organisation, person, from:, to:)
|
||||
organisation_approved_reviews = \
|
||||
GitHub.search_approved_pull_requests_in_user_or_organisation(organisation, person, from:, to:)
|
||||
|
||||
require "utils/git"
|
||||
|
||||
repositories.each do |repository|
|
||||
repository_path, tap = repository_path_and_tap(repository)
|
||||
if repository_path && tap && !repository_path.exist?
|
||||
opoo "Repository #{repository} not yet tapped! Tapping it now..."
|
||||
tap.install
|
||||
end
|
||||
|
||||
repo_full_name = if repo == "brew"
|
||||
"homebrew/brew"
|
||||
else
|
||||
tap.full_name
|
||||
repository_full_name = tap&.full_name
|
||||
repository_full_name ||= repository
|
||||
|
||||
repository_api_url = "#{GitHub::API_URL}/repos/#{repository_full_name}"
|
||||
|
||||
puts "Determining contributions for #{person} on #{repository_full_name}..." if args.verbose?
|
||||
|
||||
merged_pr_author = organisation_merged_prs.count do |pr|
|
||||
pr.fetch("repository_url") == repository_api_url
|
||||
end
|
||||
approved_pr_review = organisation_approved_reviews.count do |pr|
|
||||
pr.fetch("repository_url") == repository_api_url
|
||||
end
|
||||
committer = GitHub.count_repository_commits(repository_full_name, person, max:, verbose:, from:, to:)
|
||||
coauthor = Utils::Git.count_coauthors(repository_path, person, from:, to:)
|
||||
|
||||
puts "Determining contributions for #{person} on #{repo_full_name}..." if args.verbose?
|
||||
|
||||
author_commits, committer_commits = GitHub.count_repo_commits(repo_full_name, person,
|
||||
from:, to: args.to, max: MAX_REPO_COMMITS)
|
||||
data[repo] = {
|
||||
author: author_commits,
|
||||
committer: committer_commits,
|
||||
coauthor: git_log_trailers_cmd(repo_path, person, "Co-authored-by", from:, to: args.to),
|
||||
review: count_reviews(repo_full_name, person, from:, to: args.to),
|
||||
}
|
||||
data[repository] = { merged_pr_author:, approved_pr_review:, committer:, coauthor: }
|
||||
rescue GitHub::API::RateLimitExceededError => e
|
||||
sleep_seconds = e.reset - Time.now.to_i
|
||||
opoo "GitHub rate limit exceeded, sleeping for #{sleep_seconds} seconds..."
|
||||
sleep sleep_seconds
|
||||
retry
|
||||
end
|
||||
|
||||
data
|
||||
@ -202,43 +266,17 @@ module Homebrew
|
||||
|
||||
sig { params(results: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, Integer]) }
|
||||
def total(results)
|
||||
totals = { author: 0, committer: 0, coauthor: 0, review: 0 }
|
||||
totals = {}
|
||||
|
||||
results.each_value do |counts|
|
||||
counts.each do |kind, count|
|
||||
totals[kind] ||= 0
|
||||
totals[kind] += count
|
||||
end
|
||||
end
|
||||
|
||||
totals
|
||||
end
|
||||
|
||||
sig {
|
||||
params(repo_path: Pathname, person: String, trailer: String, from: T.nilable(String),
|
||||
to: T.nilable(String)).returns(Integer)
|
||||
}
|
||||
def git_log_trailers_cmd(repo_path, person, trailer, from:, to:)
|
||||
cmd = ["git", "-C", repo_path, "log", "--oneline"]
|
||||
cmd << "--format='%(trailers:key=#{trailer}:)'"
|
||||
cmd << "--before=#{to}" if to
|
||||
cmd << "--after=#{from}" if from
|
||||
|
||||
Utils.safe_popen_read(*cmd).lines.count { |l| l.include?(person) }
|
||||
end
|
||||
|
||||
sig {
|
||||
params(repo_full_name: String, person: String, from: T.nilable(String),
|
||||
to: T.nilable(String)).returns(Integer)
|
||||
}
|
||||
def count_reviews(repo_full_name, person, from:, to:)
|
||||
require "utils/github"
|
||||
GitHub.count_issues("", is: "pr", repo: repo_full_name, reviewed_by: person, review: "approved", from:, to:)
|
||||
rescue GitHub::API::ValidationFailedError
|
||||
if args.verbose?
|
||||
onoe "Couldn't search GitHub for PRs by #{person}. Their profile might be private. Defaulting to 0."
|
||||
end
|
||||
0 # Users who have made their contributions private are not searchable to determine counts.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -41,9 +41,9 @@ module Homebrew
|
||||
switch "--autobump",
|
||||
description: "Include packages that are autobumped by BrewTestBot. By default these are skipped."
|
||||
|
||||
conflicts "--debug", "--json"
|
||||
conflicts "--tap=", "--eval-all", "--installed"
|
||||
conflicts "--cask", "--formula"
|
||||
conflicts "--tap", "--installed", "--eval-all"
|
||||
conflicts "--json", "--debug"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--formula", "--extract-plist"
|
||||
|
||||
named_args [:formula, :cask], without_api: true
|
||||
@ -78,9 +78,15 @@ module Homebrew
|
||||
formulae + casks
|
||||
elsif File.exist?(watchlist_path)
|
||||
begin
|
||||
# This removes blank lines, comment lines, and trailing comments
|
||||
names = Pathname.new(watchlist_path).read.lines
|
||||
.reject { |line| line.start_with?("#") || line.blank? }
|
||||
.map(&:strip)
|
||||
.filter_map do |line|
|
||||
comment_index = line.index("#")
|
||||
next if comment_index&.zero?
|
||||
|
||||
line = line[0...comment_index] if comment_index
|
||||
line&.strip.presence
|
||||
end
|
||||
|
||||
named_args = CLI::NamedArgs.new(*names, parent: args)
|
||||
named_args.to_formulae_and_casks(ignore_unavailable: true)
|
||||
|
@ -208,8 +208,7 @@ module Homebrew
|
||||
if pull_request
|
||||
# This is a tap pull request and approving reviewers should also sign-off.
|
||||
tap = T.must(Tap.from_path(git_repo.pathname))
|
||||
review_trailers = GitHub.approved_reviews(tap.user, tap.full_name.split("/").last,
|
||||
pull_request).map do |r|
|
||||
review_trailers = GitHub.repository_approved_reviews(tap.user, tap.full_repository, pull_request).map do |r|
|
||||
"Signed-off-by: #{r["name"]} <#{r["email"]}>"
|
||||
end
|
||||
trailers = trailers.lines.concat(review_trailers).map(&:strip).uniq.join("\n")
|
||||
|
@ -5,6 +5,8 @@ require "abstract_command"
|
||||
require "fileutils"
|
||||
require "stringio"
|
||||
require "formula"
|
||||
require "cask/download"
|
||||
require "unpack_strategy"
|
||||
|
||||
module Homebrew
|
||||
module DevCmd
|
||||
@ -13,7 +15,7 @@ module Homebrew
|
||||
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Unpack the source files for <formula> into subdirectories of the current
|
||||
Unpack the files for the <formula> or <cask> into subdirectories of the current
|
||||
working directory.
|
||||
EOS
|
||||
flag "--destdir=",
|
||||
@ -25,15 +27,28 @@ module Homebrew
|
||||
"patches for the software."
|
||||
switch "-f", "--force",
|
||||
description: "Overwrite the destination directory if it already exists."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
conflicts "--git", "--patch"
|
||||
conflicts "--formula", "--cask"
|
||||
conflicts "--cask", "--patch"
|
||||
conflicts "--cask", "--git"
|
||||
|
||||
named_args :formula, min: 1
|
||||
named_args [:formula, :cask], min: 1
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
formulae = args.named.to_formulae
|
||||
formulae_and_casks = if args.casks?
|
||||
args.named.to_formulae_and_casks(only: :cask)
|
||||
elsif args.formulae?
|
||||
args.named.to_formulae_and_casks(only: :formula)
|
||||
else
|
||||
args.named.to_formulae_and_casks
|
||||
end
|
||||
|
||||
if (dir = args.destdir)
|
||||
unpack_dir = Pathname.new(dir).expand_path
|
||||
@ -44,35 +59,70 @@ module Homebrew
|
||||
|
||||
odie "Cannot write to #{unpack_dir}" unless unpack_dir.writable?
|
||||
|
||||
formulae.each do |f|
|
||||
stage_dir = unpack_dir/"#{f.name}-#{f.version}"
|
||||
|
||||
if stage_dir.exist?
|
||||
odie "Destination #{stage_dir} already exists!" unless args.force?
|
||||
|
||||
rm_rf stage_dir
|
||||
end
|
||||
|
||||
oh1 "Unpacking #{Formatter.identifier(f.full_name)} to: #{stage_dir}"
|
||||
|
||||
# show messages about tar
|
||||
with_env VERBOSE: "1" do
|
||||
f.brew do
|
||||
f.patch if args.patch?
|
||||
cp_r getwd, stage_dir, preserve: true
|
||||
end
|
||||
end
|
||||
|
||||
next unless args.git?
|
||||
|
||||
ohai "Setting up Git repository"
|
||||
cd(stage_dir) do
|
||||
system "git", "init", "-q"
|
||||
system "git", "add", "-A"
|
||||
system "git", "commit", "-q", "-m", "brew-unpack"
|
||||
formulae_and_casks.each do |formula_or_cask|
|
||||
if formula_or_cask.is_a?(Cask::Cask)
|
||||
unpack_cask(formula_or_cask, unpack_dir)
|
||||
elsif (formula = T.cast(formula_or_cask, Formula))
|
||||
unpack_formula(formula, unpack_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(formula: Formula, unpack_dir: Pathname).void }
|
||||
def unpack_formula(formula, unpack_dir)
|
||||
stage_dir = unpack_dir/"#{formula.name}-#{formula.version}"
|
||||
|
||||
if stage_dir.exist?
|
||||
odie "Destination #{stage_dir} already exists!" unless args.force?
|
||||
|
||||
rm_rf stage_dir
|
||||
end
|
||||
|
||||
oh1 "Unpacking #{Formatter.identifier(formula.full_name)} to: #{stage_dir}"
|
||||
|
||||
# show messages about tar
|
||||
with_env VERBOSE: "1" do
|
||||
formula.brew do
|
||||
formula.patch if args.patch?
|
||||
cp_r getwd, stage_dir, preserve: true
|
||||
end
|
||||
end
|
||||
|
||||
return unless args.git?
|
||||
|
||||
ohai "Setting up Git repository"
|
||||
cd(stage_dir) do
|
||||
system "git", "init", "-q"
|
||||
system "git", "add", "-A"
|
||||
system "git", "commit", "-q", "-m", "brew-unpack"
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, unpack_dir: Pathname).void }
|
||||
def unpack_cask(cask, unpack_dir)
|
||||
stage_dir = unpack_dir/"#{cask.token}-#{cask.version}"
|
||||
|
||||
if stage_dir.exist?
|
||||
odie "Destination #{stage_dir} already exists!" unless args.force?
|
||||
|
||||
rm_rf stage_dir
|
||||
end
|
||||
|
||||
oh1 "Unpacking #{Formatter.identifier(cask.full_name)} to: #{stage_dir}"
|
||||
|
||||
download = Cask::Download.new(cask, quarantine: true)
|
||||
|
||||
downloaded_path = if download.downloaded?
|
||||
download.cached_download
|
||||
else
|
||||
download.fetch(quiet: false)
|
||||
end
|
||||
|
||||
stage_dir.mkpath
|
||||
UnpackStrategy.detect(downloaded_path).extract_nestedly(to: stage_dir, verbose: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -114,9 +114,9 @@ class DevelopmentTools
|
||||
|
||||
# Get the GCC version.
|
||||
#
|
||||
# @api internal
|
||||
# @api public
|
||||
sig { params(cc: String).returns(Version) }
|
||||
def gcc_version(cc)
|
||||
def gcc_version(cc = host_gcc_path.to_s)
|
||||
(@gcc_version ||= T.let({}, T.nilable(T::Hash[String, Version]))).fetch(cc) do
|
||||
path = HOMEBREW_PREFIX/"opt/#{CompilerSelector.preferred_gcc}/bin"/cc
|
||||
path = locate(cc) unless path.exist?
|
||||
|
@ -579,7 +579,7 @@ class UnbottledError < RuntimeError
|
||||
require "utils"
|
||||
|
||||
msg = <<~EOS
|
||||
The following #{Utils.pluralize("formula", formulae.count, plural: "e")} cannot be installed from #{Utils.pluralize("bottle", formulae.count)} and must be
|
||||
The following #{Utils.pluralize("formula", formulae.count)} cannot be installed from #{Utils.pluralize("bottle", formulae.count)} and must be
|
||||
built from source.
|
||||
#{formulae.to_sentence}
|
||||
EOS
|
||||
|
4
Library/Homebrew/extend/os/cask/artifact/relocated.rb
Normal file
4
Library/Homebrew/extend/os/cask/artifact/relocated.rb
Normal file
@ -0,0 +1,4 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/os/linux/cask/artifact/relocated" if OS.linux?
|
23
Library/Homebrew/extend/os/linux/cask/artifact/relocated.rb
Normal file
23
Library/Homebrew/extend/os/linux/cask/artifact/relocated.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module Cask
|
||||
module Artifact
|
||||
module Relocated
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { ::Cask::Artifact::Relocated }
|
||||
|
||||
sig { params(file: Pathname, altname: Pathname, command: T.class_of(SystemCommand)).returns(T.nilable(SystemCommand::Result)) }
|
||||
def add_altname_metadata(file, altname, command:)
|
||||
# no-op on Linux: /usr/bin/xattr for setting extended attributes is not available there.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Cask::Artifact::Relocated.prepend(OS::Linux::Cask::Artifact::Relocated)
|
@ -42,14 +42,14 @@ module OS
|
||||
@needs_libc_formula ||= OS::Linux::Glibc.below_ci_version?
|
||||
end
|
||||
|
||||
# Keep this method around for now to make it easier to add this functionality later.
|
||||
# rubocop:disable Lint/UselessMethodDefinition
|
||||
sig { returns(Pathname) }
|
||||
def host_gcc_path
|
||||
# TODO: override this if/when we to pick the GCC based on e.g. the Ubuntu version.
|
||||
# Prioritise versioned path if installed
|
||||
path = Pathname.new("/usr/bin/#{OS::LINUX_PREFERRED_GCC_COMPILER_FORMULA.tr("@", "-")}")
|
||||
return path if path.exist?
|
||||
|
||||
super
|
||||
end
|
||||
# rubocop:enable Lint/UselessMethodDefinition
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def needs_compiler_formula?
|
||||
@ -60,12 +60,7 @@ module OS
|
||||
# Undocumented environment variable to make it easier to test compiler
|
||||
# formula automatic installation.
|
||||
@needs_compiler_formula = true if ENV["HOMEBREW_FORCE_COMPILER_FORMULA"]
|
||||
|
||||
@needs_compiler_formula ||= if host_gcc_path.exist?
|
||||
::DevelopmentTools.gcc_version(host_gcc_path.to_s) < OS::LINUX_GCC_CI_VERSION
|
||||
else
|
||||
true
|
||||
end
|
||||
@needs_compiler_formula ||= OS::Linux::Libstdcxx.below_ci_version?
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.nilable(String)]) }
|
||||
|
@ -40,6 +40,8 @@ module OS
|
||||
self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula)
|
||||
m4_path_deps = ["libtool", "bison"]
|
||||
self["M4"] = "#{HOMEBREW_PREFIX}/opt/m4/bin/m4" if deps.any? { m4_path_deps.include?(_1.name) }
|
||||
# Build jemalloc-sys rust crate on ARM64/AArch64 with support for page sizes up to 64K.
|
||||
self["JEMALLOC_SYS_WITH_LG_PAGE"] = "16" if ::Hardware::CPU.arch == :arm64
|
||||
|
||||
# Pointer authentication and BTI are hardening techniques most distros
|
||||
# use by default on their packages. arm64 Linux we're packaging
|
||||
|
@ -1,36 +1,24 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/ld"
|
||||
require "os/linux/libstdcxx"
|
||||
require "utils/output"
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module Install
|
||||
module ClassMethods
|
||||
# This is a list of known paths to the host dynamic linker on Linux if
|
||||
# the host glibc is new enough. The symlink_ld_so method will fail if
|
||||
# the host linker cannot be found in this list.
|
||||
DYNAMIC_LINKERS = %w[
|
||||
/lib64/ld-linux-x86-64.so.2
|
||||
/lib64/ld64.so.2
|
||||
/lib/ld-linux.so.3
|
||||
/lib/ld-linux.so.2
|
||||
/lib/ld-linux-aarch64.so.1
|
||||
/lib/ld-linux-armhf.so.3
|
||||
/system/bin/linker64
|
||||
/system/bin/linker
|
||||
].freeze
|
||||
|
||||
# We link GCC runtime libraries that are not specifically used for Fortran,
|
||||
# which are linked by the GCC formula. We only use the versioned shared libraries
|
||||
# as the other shared and static libraries are only used at build time where
|
||||
# GCC can find its own libraries.
|
||||
GCC_RUNTIME_LIBS = %w[
|
||||
GCC_RUNTIME_LIBS = T.let(%W[
|
||||
libatomic.so.1
|
||||
libgcc_s.so.1
|
||||
libgomp.so.1
|
||||
libstdc++.so.6
|
||||
].freeze
|
||||
#{OS::Linux::Libstdcxx::SONAME}
|
||||
].freeze, T::Array[String])
|
||||
|
||||
sig { params(all_fatal: T::Boolean).void }
|
||||
def perform_preinstall_checks(all_fatal: false)
|
||||
@ -67,7 +55,7 @@ module OS
|
||||
|
||||
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
|
||||
unless ld_so.readable?
|
||||
ld_so = DYNAMIC_LINKERS.find { |s| File.executable? s }
|
||||
ld_so = OS::Linux::Ld.system_ld_so
|
||||
if ld_so.blank?
|
||||
::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?
|
||||
|
||||
|
@ -2,12 +2,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "compilers"
|
||||
require "os/linux/libstdcxx"
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module LinkageChecker
|
||||
# Libraries provided by glibc and gcc.
|
||||
SYSTEM_LIBRARY_ALLOWLIST = %w[
|
||||
SYSTEM_LIBRARY_ALLOWLIST = %W[
|
||||
ld-linux-x86-64.so.2
|
||||
ld-linux-aarch64.so.1
|
||||
libanl.so.1
|
||||
@ -24,7 +25,7 @@ module OS
|
||||
libutil.so.1
|
||||
libgcc_s.so.1
|
||||
libgomp.so.1
|
||||
libstdc++.so.6
|
||||
#{OS::Linux::Libstdcxx::SONAME}
|
||||
libquadmath.so.0
|
||||
].freeze
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
require "compilers"
|
||||
require "os/linux/glibc"
|
||||
require "os/linux/libstdcxx"
|
||||
require "system_command"
|
||||
|
||||
module OS
|
||||
@ -20,6 +21,13 @@ module OS
|
||||
version
|
||||
end
|
||||
|
||||
def host_libstdcxx_version
|
||||
version = OS::Linux::Libstdcxx.system_version
|
||||
return "N/A" if version.null?
|
||||
|
||||
version
|
||||
end
|
||||
|
||||
def host_gcc_version
|
||||
gcc = ::DevelopmentTools.host_gcc_path
|
||||
return "N/A" unless gcc.executable?
|
||||
@ -36,7 +44,7 @@ module OS
|
||||
end
|
||||
|
||||
def host_ruby_version
|
||||
out, _, status = system_command(HOST_RUBY_PATH, args: ["-e", "puts RUBY_VERSION"], print_stderr: false)
|
||||
out, _, status = system_command(HOST_RUBY_PATH, args: ["-e", "puts RUBY_VERSION"], print_stderr: false).to_a
|
||||
return "N/A" unless status.success?
|
||||
|
||||
out
|
||||
@ -49,6 +57,7 @@ module OS
|
||||
out.puts "OS: #{OS::Linux.os_version}"
|
||||
out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl?
|
||||
out.puts "Host glibc: #{host_glibc_version}"
|
||||
out.puts "Host libstdc++: #{host_libstdcxx_version}"
|
||||
out.puts "#{::DevelopmentTools.host_gcc_path}: #{host_gcc_version}"
|
||||
out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH
|
||||
["glibc", ::CompilerSelector.preferred_gcc, OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA, "xorg"].each do |f|
|
||||
|
@ -422,11 +422,13 @@ class Pathname
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def zipinfo
|
||||
@zipinfo ||= T.let(nil, T.nilable(String))
|
||||
@zipinfo ||= system_command("zipinfo", args: ["-1", self], print_stderr: false)
|
||||
.stdout
|
||||
.encode(Encoding::UTF_8, invalid: :replace)
|
||||
.split("\n")
|
||||
@zipinfo ||= T.let(
|
||||
system_command("zipinfo", args: ["-1", self], print_stderr: false)
|
||||
.stdout
|
||||
.encode(Encoding::UTF_8, invalid: :replace)
|
||||
.split("\n"),
|
||||
T.nilable(T::Array[String]),
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -749,7 +749,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
def audit_specs
|
||||
problem "HEAD-only (no stable download)" if head_only?(formula)
|
||||
problem "HEAD-only (no stable download)" if head_only?(formula) && @core_tap
|
||||
|
||||
%w[Stable HEAD].each do |name|
|
||||
spec_name = name.downcase.to_sym
|
||||
|
@ -609,8 +609,8 @@ on_request: installed_on_request?, options:)
|
||||
clean
|
||||
|
||||
# Store the formula used to build the keg in the keg.
|
||||
formula_contents = if formula.local_bottle_path
|
||||
Utils::Bottles.formula_contents formula.local_bottle_path, name: formula.name
|
||||
formula_contents = if (local_bottle_path = formula.local_bottle_path)
|
||||
Utils::Bottles.formula_contents local_bottle_path, name: formula.name
|
||||
else
|
||||
formula.path.read
|
||||
end
|
||||
|
@ -7,6 +7,7 @@ require "tab"
|
||||
require "utils"
|
||||
require "utils/bottles"
|
||||
require "utils/output"
|
||||
require "utils/path"
|
||||
require "service"
|
||||
require "utils/curl"
|
||||
require "deprecate_disable"
|
||||
@ -727,29 +728,7 @@ module Formulary
|
||||
end
|
||||
|
||||
return unless path.expand_path.exist?
|
||||
|
||||
if Homebrew::EnvConfig.forbid_packages_from_paths?
|
||||
path_realpath = path.realpath.to_s
|
||||
path_string = path.to_s
|
||||
if (path_realpath.end_with?(".rb") || path_string.end_with?(".rb")) &&
|
||||
!path_realpath.start_with?("#{HOMEBREW_CELLAR}/", "#{HOMEBREW_LIBRARY}/Taps/") &&
|
||||
!path_string.start_with?("#{HOMEBREW_CELLAR}/", "#{HOMEBREW_LIBRARY}/Taps/")
|
||||
if path_string.include?("./") || path_string.end_with?(".rb") || path_string.count("/") != 2
|
||||
raise <<~WARNING
|
||||
Homebrew requires formulae to be in a tap, rejecting:
|
||||
#{path_string} (#{path_realpath})
|
||||
|
||||
To create a tap, run e.g.
|
||||
brew tap-new <user|org>/<repository>
|
||||
To create a formula in a tap run e.g.
|
||||
brew create <url> --tap=<user|org>/<repository>
|
||||
WARNING
|
||||
elsif path_string.count("/") == 2
|
||||
# Looks like a tap, let's quietly return but not error.
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
return unless ::Utils::Path.loadable_package_path?(path, :formula)
|
||||
|
||||
if (tap = Tap.from_path(path))
|
||||
# Only treat symlinks in taps as aliases.
|
||||
|
@ -10,7 +10,7 @@ class GitHubRunnerMatrix
|
||||
# `git tag 15-sequoia f42c4a659e4da887fc714f8f41cc26794a4bb320`
|
||||
# to allow people to jump to specific commits based on their macOS version.
|
||||
NEWEST_HOMEBREW_CORE_MACOS_RUNNER = :sequoia
|
||||
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :ventura
|
||||
OLDEST_HOMEBREW_CORE_MACOS_RUNNER = :sonoma
|
||||
NEWEST_HOMEBREW_CORE_INTEL_MACOS_RUNNER = :sonoma
|
||||
|
||||
RunnerSpec = T.type_alias { T.any(LinuxRunnerSpec, MacOSRunnerSpec) }
|
||||
|
@ -405,8 +405,7 @@ module Homebrew
|
||||
return if formulae_names_to_install.empty?
|
||||
|
||||
if dry_run
|
||||
ohai "Would install #{Utils.pluralize("formula", formulae_names_to_install.count,
|
||||
plural: "e", include_count: true)}:"
|
||||
ohai "Would install #{Utils.pluralize("formula", formulae_names_to_install.count, include_count: true)}:"
|
||||
puts formulae_names_to_install.join(" ")
|
||||
|
||||
formula_installers.each do |fi|
|
||||
@ -429,8 +428,8 @@ module Homebrew
|
||||
def print_dry_run_dependencies(formula, dependencies)
|
||||
return if dependencies.empty?
|
||||
|
||||
ohai "Would install #{Utils.pluralize("dependenc", dependencies.count, plural: "ies", singular: "y",
|
||||
include_count: true)} for #{formula.name}:"
|
||||
ohai "Would install #{Utils.pluralize("dependency", dependencies.count, include_count: true)} " \
|
||||
"for #{formula.name}:"
|
||||
formula_names = dependencies.map { |(dep, _options)| yield dep.to_formula }
|
||||
puts formula_names.join(" ")
|
||||
end
|
||||
@ -446,7 +445,7 @@ module Homebrew
|
||||
|
||||
sizes = compute_total_sizes(formulae, debug: args.debug?)
|
||||
|
||||
puts "#{::Utils.pluralize("Formula", formulae.count, plural: "e")} \
|
||||
puts "#{::Utils.pluralize("Formula", formulae.count)} \
|
||||
(#{formulae.count}): #{formulae.join(", ")}\n\n"
|
||||
puts "Download Size: #{disk_usage_readable(sizes.fetch(:download))}"
|
||||
puts "Install Size: #{disk_usage_readable(sizes.fetch(:installed))}"
|
||||
|
@ -113,6 +113,18 @@ class Keg
|
||||
old: "/usr/local/var/log/#{name}",
|
||||
new: "#{PREFIX_PLACEHOLDER}/var/log/#{name}",
|
||||
},
|
||||
var_run_name: {
|
||||
old: "/usr/local/var/run/#{name}",
|
||||
new: "#{PREFIX_PLACEHOLDER}/var/run/#{name}",
|
||||
},
|
||||
var_db_name: {
|
||||
old: "/usr/local/var/db/#{name}",
|
||||
new: "#{PREFIX_PLACEHOLDER}/var/db/#{name}",
|
||||
},
|
||||
share_name: {
|
||||
old: "/usr/local/share/#{name}",
|
||||
new: "#{PREFIX_PLACEHOLDER}/share/#{name}",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -198,8 +198,8 @@ module Homebrew
|
||||
FORMULA_CHECKS = T.let([
|
||||
:package_or_resource_skip,
|
||||
:formula_head_only,
|
||||
:formula_deprecated,
|
||||
:formula_disabled,
|
||||
:formula_deprecated,
|
||||
:formula_versioned,
|
||||
].freeze, T::Array[Symbol])
|
||||
private_constant :FORMULA_CHECKS
|
||||
@ -207,8 +207,8 @@ module Homebrew
|
||||
# Skip conditions for casks.
|
||||
CASK_CHECKS = T.let([
|
||||
:package_or_resource_skip,
|
||||
:cask_deprecated,
|
||||
:cask_disabled,
|
||||
:cask_deprecated,
|
||||
:cask_extract_plist,
|
||||
:cask_version_latest,
|
||||
:cask_url_unversioned,
|
||||
|
@ -129,9 +129,9 @@ module Homebrew
|
||||
print_stderr: false,
|
||||
debug: false,
|
||||
verbose: false,
|
||||
)
|
||||
).to_a
|
||||
|
||||
tags_data = { tags: [] }
|
||||
tags_data = { tags: T.let([], T::Array[String]) }
|
||||
tags_data[:messages] = stderr.split("\n") if stderr.present?
|
||||
return tags_data if stdout.blank?
|
||||
|
||||
|
@ -154,12 +154,6 @@ can take several different forms:
|
||||
You can still access these formulae by using a special syntax, e.g.
|
||||
`homebrew/dupes/vim` or `homebrew/versions/node4`.
|
||||
|
||||
* An arbitrary file:
|
||||
Homebrew can install formulae from a local path. It can point to either a
|
||||
formula file or a bottle.
|
||||
Prefix relative paths with `./` to prevent them from being interpreted as a
|
||||
formula or tap name.
|
||||
|
||||
## SPECIFYING CASKS
|
||||
|
||||
Many Homebrew Cask commands accept one or more <cask> arguments. These can be
|
||||
|
@ -23,6 +23,7 @@ DEPRECATED_OFFICIAL_TAPS = %w[
|
||||
devel-only
|
||||
dupes
|
||||
emacs
|
||||
formula-analytics
|
||||
fuse
|
||||
games
|
||||
gui
|
||||
|
@ -49,8 +49,9 @@ module OS
|
||||
LINUX_CI_OS_VERSION = "Ubuntu 22.04"
|
||||
LINUX_GLIBC_CI_VERSION = "2.35"
|
||||
LINUX_GLIBC_NEXT_CI_VERSION = "2.39"
|
||||
LINUX_GCC_CI_VERSION = "11.0"
|
||||
LINUX_PREFERRED_GCC_COMPILER_FORMULA = "gcc@11" # https://packages.ubuntu.com/jammy/gcc
|
||||
LINUX_GCC_CI_VERSION = "12" # https://packages.ubuntu.com/jammy/gcc-12
|
||||
LINUX_LIBSTDCXX_CI_VERSION = "6.0.30" # https://packages.ubuntu.com/jammy/libstdc++6
|
||||
LINUX_PREFERRED_GCC_COMPILER_FORMULA = T.let("gcc@#{LINUX_GCC_CI_VERSION}".freeze, String)
|
||||
LINUX_PREFERRED_GCC_RUNTIME_FORMULA = "gcc"
|
||||
|
||||
if OS.mac?
|
||||
|
@ -29,8 +29,6 @@ module OS
|
||||
else
|
||||
"#{description} (#{codename})"
|
||||
end
|
||||
elsif (redhat_release = Pathname.new("/etc/redhat-release")).readable?
|
||||
redhat_release.read.chomp
|
||||
elsif ::OS_VERSION.present?
|
||||
::OS_VERSION
|
||||
else
|
||||
|
@ -5,37 +5,69 @@ module OS
|
||||
module Linux
|
||||
# Helper functions for querying `ld` information.
|
||||
module Ld
|
||||
sig { returns(String) }
|
||||
def self.brewed_ld_so_diagnostics
|
||||
@brewed_ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
|
||||
# This is a list of known paths to the host dynamic linker on Linux if
|
||||
# the host glibc is new enough. Brew will fail to create a symlink for
|
||||
# ld.so if the host linker cannot be found in this list.
|
||||
DYNAMIC_LINKERS = %w[
|
||||
/lib64/ld-linux-x86-64.so.2
|
||||
/lib64/ld64.so.2
|
||||
/lib/ld-linux.so.3
|
||||
/lib/ld-linux.so.2
|
||||
/lib/ld-linux-aarch64.so.1
|
||||
/lib/ld-linux-armhf.so.3
|
||||
/system/bin/linker64
|
||||
/system/bin/linker
|
||||
].freeze
|
||||
|
||||
brewed_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
|
||||
return "" unless brewed_ld_so.exist?
|
||||
# The path to the system's dynamic linker or `nil` if not found
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
def self.system_ld_so
|
||||
@system_ld_so ||= T.let(nil, T.nilable(Pathname))
|
||||
@system_ld_so ||= begin
|
||||
linker = DYNAMIC_LINKERS.find { |s| File.executable? s }
|
||||
Pathname(linker) if linker
|
||||
end
|
||||
end
|
||||
|
||||
brewed_ld_so_target = brewed_ld_so.readlink
|
||||
@brewed_ld_so_diagnostics[brewed_ld_so_target] ||= begin
|
||||
ld_so_output = Utils.popen_read(brewed_ld_so, "--list-diagnostics")
|
||||
sig { params(brewed: T::Boolean).returns(String) }
|
||||
def self.ld_so_diagnostics(brewed: true)
|
||||
@ld_so_diagnostics ||= T.let({}, T.nilable(T::Hash[Pathname, String]))
|
||||
|
||||
ld_so_target = if brewed
|
||||
ld_so = HOMEBREW_PREFIX/"lib/ld.so"
|
||||
return "" unless ld_so.exist?
|
||||
|
||||
ld_so.readlink
|
||||
else
|
||||
ld_so = system_ld_so
|
||||
return "" unless ld_so&.exist?
|
||||
|
||||
ld_so
|
||||
end
|
||||
|
||||
@ld_so_diagnostics[ld_so_target] ||= begin
|
||||
ld_so_output = Utils.popen_read(ld_so, "--list-diagnostics")
|
||||
ld_so_output if $CHILD_STATUS.success?
|
||||
end
|
||||
|
||||
@brewed_ld_so_diagnostics[brewed_ld_so_target].to_s
|
||||
@ld_so_diagnostics[ld_so_target].to_s
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def self.sysconfdir
|
||||
sig { params(brewed: T::Boolean).returns(String) }
|
||||
def self.sysconfdir(brewed: true)
|
||||
fallback_sysconfdir = "/etc"
|
||||
|
||||
match = brewed_ld_so_diagnostics.match(/path.sysconfdir="(.+)"/)
|
||||
match = ld_so_diagnostics(brewed:).match(/path.sysconfdir="(.+)"/)
|
||||
return fallback_sysconfdir unless match
|
||||
|
||||
match.captures.compact.first || fallback_sysconfdir
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def self.system_dirs
|
||||
sig { params(brewed: T::Boolean).returns(T::Array[String]) }
|
||||
def self.system_dirs(brewed: true)
|
||||
dirs = []
|
||||
|
||||
brewed_ld_so_diagnostics.split("\n").each do |line|
|
||||
ld_so_diagnostics(brewed:).split("\n").each do |line|
|
||||
match = line.match(/path.system_dirs\[0x.*\]="(.*)"/)
|
||||
next unless match
|
||||
|
||||
@ -45,9 +77,9 @@ module OS
|
||||
dirs
|
||||
end
|
||||
|
||||
sig { params(conf_path: T.any(Pathname, String)).returns(T::Array[String]) }
|
||||
def self.library_paths(conf_path = Pathname(sysconfdir)/"ld.so.conf")
|
||||
conf_file = Pathname(conf_path)
|
||||
sig { params(conf_path: T.any(Pathname, String), brewed: T::Boolean).returns(T::Array[String]) }
|
||||
def self.library_paths(conf_path = "ld.so.conf", brewed: true)
|
||||
conf_file = Pathname(sysconfdir(brewed:))/conf_path
|
||||
return [] unless conf_file.exist?
|
||||
return [] unless conf_file.file?
|
||||
return [] unless conf_file.readable?
|
||||
@ -68,8 +100,7 @@ module OS
|
||||
line.sub!(/\s*#.*$/, "")
|
||||
|
||||
if line.start_with?(/\s*include\s+/)
|
||||
include_path = Pathname(line.sub(/^\s*include\s+/, "")).expand_path
|
||||
wildcard = include_path.absolute? ? include_path : directory/include_path
|
||||
wildcard = Pathname(line.sub(/^\s*include\s+/, "")).expand_path(directory)
|
||||
|
||||
Dir.glob(wildcard.to_s).each do |include_file|
|
||||
paths += library_paths(include_file)
|
||||
|
47
Library/Homebrew/os/linux/libstdcxx.rb
Normal file
47
Library/Homebrew/os/linux/libstdcxx.rb
Normal file
@ -0,0 +1,47 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/ld"
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
# Helper functions for querying `libstdc++` information.
|
||||
module Libstdcxx
|
||||
SOVERSION = 6
|
||||
SONAME = T.let("libstdc++.so.#{SOVERSION}".freeze, String)
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def self.below_ci_version?
|
||||
system_version < LINUX_LIBSTDCXX_CI_VERSION
|
||||
end
|
||||
|
||||
sig { returns(Version) }
|
||||
def self.system_version
|
||||
@system_version ||= T.let(nil, T.nilable(Version))
|
||||
@system_version ||= if (path = system_path)
|
||||
Version.new("#{SOVERSION}#{path.realpath.basename.to_s.delete_prefix!(SONAME)}")
|
||||
else
|
||||
Version::NULL
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
def self.system_path
|
||||
@system_path ||= T.let(nil, T.nilable(Pathname))
|
||||
@system_path ||= find_library(OS::Linux::Ld.library_paths(brewed: false))
|
||||
@system_path ||= find_library(OS::Linux::Ld.system_dirs(brewed: false))
|
||||
end
|
||||
|
||||
sig { params(paths: T::Array[String]).returns(T.nilable(Pathname)) }
|
||||
private_class_method def self.find_library(paths)
|
||||
paths.each do |path|
|
||||
next if path.start_with?(HOMEBREW_PREFIX)
|
||||
|
||||
candidate = Pathname(path)/SONAME
|
||||
return candidate if candidate.exist? && candidate.elf?
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,7 +1,14 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Keg
|
||||
sig { params(path: Pathname).void }
|
||||
def initialize(path)
|
||||
super
|
||||
|
||||
@require_relocation = T.let(false, T::Boolean)
|
||||
end
|
||||
|
||||
sig { params(id: String, file: Pathname).returns(T::Boolean) }
|
||||
def change_dylib_id(id, file)
|
||||
return false if file.dylib_id == id
|
||||
@ -36,6 +43,7 @@ class Keg
|
||||
raise
|
||||
end
|
||||
|
||||
sig { params(old: String, new: String, file: Pathname).returns(T::Boolean) }
|
||||
def change_rpath(old, new, file)
|
||||
return false if old == new
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "macho"
|
||||
@ -12,25 +12,28 @@ module MachOShim
|
||||
|
||||
delegate [:dylib_id] => :macho
|
||||
|
||||
sig { params(args: T.untyped).void }
|
||||
def initialize(*args)
|
||||
@macho = T.let(nil, T.nilable(MachO::MachOFile))
|
||||
@mach_data = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, T.untyped]]))
|
||||
@macho = T.let(nil, T.nilable(T.any(MachO::MachOFile, MachO::FatFile)))
|
||||
@mach_data = T.let(nil, T.nilable(T::Array[T::Hash[Symbol, Symbol]]))
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
sig { returns(T.any(MachO::MachOFile, MachO::FatFile)) }
|
||||
def macho
|
||||
@macho ||= MachO.open(to_s)
|
||||
end
|
||||
private :macho
|
||||
|
||||
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
||||
sig { returns(T::Array[T::Hash[Symbol, Symbol]]) }
|
||||
def mach_data
|
||||
@mach_data ||= begin
|
||||
machos = []
|
||||
mach_data = []
|
||||
|
||||
if MachO::Utils.fat_magic?(macho.magic)
|
||||
case (macho = self.macho)
|
||||
when MachO::FatFile
|
||||
machos = macho.machos
|
||||
else
|
||||
machos << macho
|
||||
@ -68,34 +71,38 @@ module MachOShim
|
||||
|
||||
# TODO: See if the `#write!` call can be delayed until
|
||||
# we know we're not making any changes to the rpaths.
|
||||
def delete_rpath(rpath, **options)
|
||||
sig { params(rpath: String, strict: T::Boolean).void }
|
||||
def delete_rpath(rpath, strict: true)
|
||||
candidates = rpaths(resolve_variable_references: false).select do |r|
|
||||
resolve_variable_name(r) == resolve_variable_name(rpath)
|
||||
end
|
||||
|
||||
# Delete the last instance to avoid changing the order in which rpaths are searched.
|
||||
rpath_to_delete = candidates.last
|
||||
options[:last] = true
|
||||
|
||||
macho.delete_rpath(rpath_to_delete, options)
|
||||
macho.delete_rpath(rpath_to_delete, { last: true, strict: })
|
||||
macho.write!
|
||||
end
|
||||
|
||||
def change_rpath(old, new, **options)
|
||||
macho.change_rpath(old, new, options)
|
||||
sig { params(old: String, new: String, uniq: T::Boolean, last: T::Boolean, strict: T::Boolean).void }
|
||||
def change_rpath(old, new, uniq: false, last: false, strict: true)
|
||||
macho.change_rpath(old, new, { uniq:, last:, strict: })
|
||||
macho.write!
|
||||
end
|
||||
|
||||
def change_dylib_id(id, **options)
|
||||
macho.change_dylib_id(id, options)
|
||||
sig { params(id: String, strict: T::Boolean).void }
|
||||
def change_dylib_id(id, strict: true)
|
||||
macho.change_dylib_id(id, { strict: })
|
||||
macho.write!
|
||||
end
|
||||
|
||||
def change_install_name(old, new, **options)
|
||||
macho.change_install_name(old, new, options)
|
||||
sig { params(old: String, new: String, strict: T::Boolean).void }
|
||||
def change_install_name(old, new, strict: true)
|
||||
macho.change_install_name(old, new, { strict: })
|
||||
macho.write!
|
||||
end
|
||||
|
||||
sig { params(except: Symbol, resolve_variable_references: T::Boolean).returns(T::Array[String]) }
|
||||
def dynamically_linked_libraries(except: :none, resolve_variable_references: true)
|
||||
lcs = macho.dylib_load_commands
|
||||
lcs.reject! { |lc| lc.flag?(except) } if except != :none
|
||||
@ -105,6 +112,7 @@ module MachOShim
|
||||
names
|
||||
end
|
||||
|
||||
sig { params(resolve_variable_references: T::Boolean).returns(T::Array[String]) }
|
||||
def rpaths(resolve_variable_references: true)
|
||||
names = macho.rpaths
|
||||
# Don't recursively resolve rpaths to avoid infinite loops.
|
||||
@ -113,11 +121,12 @@ module MachOShim
|
||||
names
|
||||
end
|
||||
|
||||
sig { params(name: String, resolve_rpaths: T::Boolean).returns(String) }
|
||||
def resolve_variable_name(name, resolve_rpaths: true)
|
||||
if name.start_with? "@loader_path"
|
||||
Pathname(name.sub("@loader_path", dirname)).cleanpath.to_s
|
||||
Pathname(name.sub("@loader_path", dirname.to_s)).cleanpath.to_s
|
||||
elsif name.start_with?("@executable_path") && binary_executable?
|
||||
Pathname(name.sub("@executable_path", dirname)).cleanpath.to_s
|
||||
Pathname(name.sub("@executable_path", dirname.to_s)).cleanpath.to_s
|
||||
elsif resolve_rpaths && name.start_with?("@rpath") && (target = resolve_rpath(name)).present?
|
||||
target
|
||||
else
|
||||
@ -125,6 +134,7 @@ module MachOShim
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T.nilable(String)) }
|
||||
def resolve_rpath(name)
|
||||
target = T.let(nil, T.nilable(String))
|
||||
return unless rpaths(resolve_variable_references: true).find do |rpath|
|
||||
@ -134,48 +144,58 @@ module MachOShim
|
||||
target
|
||||
end
|
||||
|
||||
sig { returns(T::Array[Symbol]) }
|
||||
def archs
|
||||
mach_data.map { |m| m.fetch :arch }
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def arch
|
||||
case archs.length
|
||||
when 0 then :dunno
|
||||
when 1 then archs.first
|
||||
when 1 then archs.fetch(0)
|
||||
else :universal
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def universal?
|
||||
arch == :universal
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def i386?
|
||||
arch == :i386
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def x86_64?
|
||||
arch == :x86_64
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ppc7400?
|
||||
arch == :ppc7400
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ppc64?
|
||||
arch == :ppc64
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def dylib?
|
||||
mach_data.any? { |m| m.fetch(:type) == :dylib }
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def mach_o_executable?
|
||||
mach_data.any? { |m| m.fetch(:type) == :executable }
|
||||
end
|
||||
|
||||
alias binary_executable? mach_o_executable?
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def mach_o_bundle?
|
||||
mach_data.any? { |m| m.fetch(:type) == :bundle }
|
||||
end
|
||||
|
@ -6,7 +6,7 @@ includedir=${prefix}/include
|
||||
|
||||
Name: SQLite
|
||||
Description: SQL database engine
|
||||
Version: 3.48.0
|
||||
Version: 3.51.0
|
||||
Libs: -L${libdir} -lsqlite3
|
||||
Libs.private:
|
||||
Cflags:
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "system_command"
|
||||
@ -22,7 +22,7 @@ module OS
|
||||
sig { params(version: MacOSVersion, path: T.any(String, Pathname), source: Symbol).void }
|
||||
def initialize(version, path, source)
|
||||
@version = version
|
||||
@path = Pathname.new(path)
|
||||
@path = T.let(Pathname(path), Pathname)
|
||||
@source = source
|
||||
end
|
||||
end
|
||||
@ -36,6 +36,12 @@ module OS
|
||||
|
||||
class NoSDKError < StandardError; end
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
@all_sdks = T.let(nil, T.nilable(T::Array[SDK]))
|
||||
@sdk_prefix = T.let(nil, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { params(version: MacOSVersion).returns(SDK) }
|
||||
def sdk_for(version)
|
||||
sdk = all_sdks.find { |s| s.version == version }
|
||||
|
@ -1,11 +1,11 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OS
|
||||
module Mac
|
||||
# Helper module for querying Xcode information.
|
||||
module Xcode
|
||||
DEFAULT_BUNDLE_PATH = Pathname("/Applications/Xcode.app").freeze
|
||||
DEFAULT_BUNDLE_PATH = T.let(Pathname("/Applications/Xcode.app").freeze, Pathname)
|
||||
BUNDLE_ID = "com.apple.dt.Xcode"
|
||||
OLD_BUNDLE_ID = "com.apple.Xcode"
|
||||
APPLE_DEVELOPER_DOWNLOAD_URL = "https://developer.apple.com/download/all/"
|
||||
@ -98,7 +98,7 @@ module OS
|
||||
# directory or nil if Xcode.app is not installed.
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
def self.prefix
|
||||
@prefix ||= begin
|
||||
@prefix ||= T.let(begin
|
||||
dir = MacOS.active_developer_dir
|
||||
|
||||
if dir.empty? || dir == CLT::PKG_PATH || !File.directory?(dir)
|
||||
@ -108,7 +108,7 @@ module OS
|
||||
# Use cleanpath to avoid pathological trailing slash
|
||||
Pathname.new(dir).cleanpath
|
||||
end
|
||||
end
|
||||
end, T.nilable(Pathname))
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
@ -134,7 +134,7 @@ module OS
|
||||
|
||||
sig { returns(XcodeSDKLocator) }
|
||||
def self.sdk_locator
|
||||
@sdk_locator ||= XcodeSDKLocator.new
|
||||
@sdk_locator ||= T.let(XcodeSDKLocator.new, T.nilable(OS::Mac::XcodeSDKLocator))
|
||||
end
|
||||
|
||||
sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) }
|
||||
@ -183,7 +183,7 @@ module OS
|
||||
# may return a version string
|
||||
# that is guessed based on the compiler, so do not
|
||||
# use it in order to check if Xcode is installed.
|
||||
if @version ||= detect_version
|
||||
if @version ||= T.let(detect_version, T.nilable(String))
|
||||
::Version.new @version
|
||||
else
|
||||
::Version::NULL
|
||||
@ -293,7 +293,7 @@ module OS
|
||||
|
||||
sig { returns(CLTSDKLocator) }
|
||||
def self.sdk_locator
|
||||
@sdk_locator ||= CLTSDKLocator.new
|
||||
@sdk_locator ||= T.let(CLTSDKLocator.new, T.nilable(OS::Mac::CLTSDKLocator))
|
||||
end
|
||||
|
||||
sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) }
|
||||
@ -372,7 +372,7 @@ module OS
|
||||
sig { returns(String) }
|
||||
def self.latest_clang_version
|
||||
case MacOS.version
|
||||
when "26" then "1700.3.9.908"
|
||||
when "26" then "1700.3.19.1"
|
||||
when "15" then "1700.0.13.5"
|
||||
when "14" then "1600.0.26.6"
|
||||
when "13" then "1500.1.0.2.5"
|
||||
@ -444,7 +444,7 @@ module OS
|
||||
# @api internal
|
||||
sig { returns(::Version) }
|
||||
def self.version
|
||||
if @version ||= detect_version
|
||||
if @version ||= T.let(detect_version, T.nilable(String))
|
||||
::Version.new @version
|
||||
else
|
||||
::Version::NULL
|
||||
|
@ -32,8 +32,8 @@ begin
|
||||
end
|
||||
|
||||
Pathname.prepend WriteMkpathExtension
|
||||
|
||||
formula.run_post_install
|
||||
|
||||
# Handle all possible exceptions.
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
error_pipe&.puts e.to_json
|
||||
|
@ -125,7 +125,7 @@ module Readall
|
||||
sig { params(filename: Pathname).returns(T::Boolean) }
|
||||
private_class_method def self.syntax_errors_or_warnings?(filename)
|
||||
# Retrieve messages about syntax errors/warnings printed to `$stderr`.
|
||||
_, err, status = system_command(RUBY_PATH, args: ["-c", "-w", filename], print_stderr: false)
|
||||
_, err, status = system_command(RUBY_PATH, args: ["-c", "-w", filename], print_stderr: false).to_a
|
||||
|
||||
# Ignore unnecessary warning about named capture conflicts.
|
||||
# See https://bugs.ruby-lang.org/issues/12359.
|
||||
|
@ -85,9 +85,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Remove this exception for `lsr` after support for tangled.sh
|
||||
# Git URLs is available in a brew release.
|
||||
return if name == "lsr"
|
||||
return if url_strategy != DownloadStrategyDetector.detect("", using)
|
||||
|
||||
problem "Redundant `using:` value in URL"
|
||||
|
@ -93,7 +93,7 @@ module Homebrew
|
||||
|
||||
wait = 2 ** @try
|
||||
unless quiet
|
||||
what = Utils.pluralize("tr", tries_remaining, plural: "ies", singular: "y")
|
||||
what = Utils.pluralize("try", tries_remaining)
|
||||
ohai "Retrying download in #{wait}s... (#{tries_remaining} #{what} left)"
|
||||
end
|
||||
sleep wait
|
||||
|
@ -34,7 +34,7 @@ module Homebrew
|
||||
unofficial = Tap.all.sum { |tap| tap.official? ? 0 : tap.formula_files.size }
|
||||
if unofficial.positive?
|
||||
opoo "Use `--eval-all` to search #{unofficial} additional " \
|
||||
"#{Utils.pluralize("formula", unofficial, plural: "e")} in third party taps."
|
||||
"#{Utils.pluralize("formula", unofficial)} in third party taps."
|
||||
end
|
||||
descriptions = Homebrew::API::Formula.all_formulae.transform_values { |data| data["desc"] }
|
||||
Descriptions.search(string_or_regex, search_type, descriptions, eval_all, cache_store_hash: true).print
|
||||
|
@ -34,7 +34,7 @@ module Homebrew
|
||||
|
||||
private_class_method def self._run(*args, mode:)
|
||||
require "system_command"
|
||||
result = SystemCommand.run(executable,
|
||||
result = SystemCommand.run(T.must(executable),
|
||||
args: [scope, *args.map(&:to_s)],
|
||||
print_stdout: mode == :default,
|
||||
print_stderr: mode == :default,
|
||||
|
@ -369,9 +369,13 @@ class Cmd
|
||||
end
|
||||
args += rpath_flags("#{wl}-rpath=", rpath_paths)
|
||||
args += ["#{wl}--dynamic-linker=#{dynamic_linker_path}"] if dynamic_linker_path
|
||||
# Use -rpath-link to make sure linker uses versioned glibc rather than the system glibc for indirect
|
||||
# Use -rpath-link to make sure linker uses brew glibc rather than the system glibc for indirect
|
||||
# dependencies because -L will only handle direct dependencies.
|
||||
args << "#{wl}-rpath-link=#{@opt}/#{versioned_glibc_dep}/lib" if versioned_glibc_dep
|
||||
if versioned_glibc_dep
|
||||
args << "#{wl}-rpath-link=#{@opt}/#{versioned_glibc_dep}/lib"
|
||||
else
|
||||
args << "#{wl}-rpath-link=#{@opt}/glibc/lib"
|
||||
end
|
||||
args
|
||||
end
|
||||
|
||||
|
@ -17,9 +17,21 @@ class Homebrew::DevCmd::Contributions::Args < Homebrew::CLI::Args
|
||||
sig { returns(T.nilable(String)) }
|
||||
def from; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def org; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def organisation; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def organization; end
|
||||
|
||||
sig { returns(T.nilable(T::Array[String])) }
|
||||
def repositories; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def team; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def to; end
|
||||
|
||||
|
@ -11,6 +11,12 @@ class Homebrew::DevCmd::Unpack
|
||||
end
|
||||
|
||||
class Homebrew::DevCmd::Unpack::Args < Homebrew::CLI::Args
|
||||
sig { returns(T::Boolean) }
|
||||
def cask?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def casks?; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def destdir; end
|
||||
|
||||
@ -20,6 +26,12 @@ class Homebrew::DevCmd::Unpack::Args < Homebrew::CLI::Args
|
||||
sig { returns(T::Boolean) }
|
||||
def force?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def formula?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def formulae?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def g?; end
|
||||
|
||||
|
@ -988,38 +988,38 @@ RuboCop::Cop::RSpec::DescribeSymbol::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Arr
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#71
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#76
|
||||
class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
|
||||
include ::RuboCop::Cop::ConfigurableEnforcedStyle
|
||||
include ::RuboCop::Cop::RSpec::Namespace
|
||||
extend ::RuboCop::Cop::AutoCorrector
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#80
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#85
|
||||
def common_instance_exec_closure?(param0 = T.unsafe(nil)); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#97
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#108
|
||||
def contains_described_class?(param0); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#92
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#103
|
||||
def described_constant(param0 = T.unsafe(nil)); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#100
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#111
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#85
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#96
|
||||
def rspec_block?(param0 = T.unsafe(nil)); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#89
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#100
|
||||
def scope_changing_syntax?(param0 = T.unsafe(nil)); end
|
||||
|
||||
private
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#136
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#147
|
||||
def allowed?(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#117
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#128
|
||||
def autocorrect(corrector, match); end
|
||||
|
||||
# @example
|
||||
@ -1033,7 +1033,7 @@ class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
|
||||
# @param const [Array<Symbol>]
|
||||
# @return [Array<Symbol>]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#202
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#213
|
||||
def collapse_namespace(namespace, const); end
|
||||
|
||||
# @example
|
||||
@ -1043,50 +1043,50 @@ class RuboCop::Cop::RSpec::DescribedClass < ::RuboCop::Cop::RSpec::Base
|
||||
# @param node [RuboCop::AST::Node]
|
||||
# @return [Array<Symbol>]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#219
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#230
|
||||
def const_name(node); end
|
||||
|
||||
# @yield [node]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#127
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#138
|
||||
def find_usage(node, &block); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#187
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#198
|
||||
def full_const_name(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#140
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#151
|
||||
def message(offense); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#165
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#176
|
||||
def offensive?(node); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#173
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#184
|
||||
def offensive_described_class?(node); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#161
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#172
|
||||
def only_static_constants?; end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#149
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#160
|
||||
def scope_change?(node); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#155
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#166
|
||||
def skippable_block?(node); end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#76
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#81
|
||||
RuboCop::Cop::RSpec::DescribedClass::DESCRIBED_CLASS = T.let(T.unsafe(nil), String)
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#77
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/described_class.rb#82
|
||||
RuboCop::Cop::RSpec::DescribedClass::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
# Avoid opening modules and defining specs within them.
|
||||
@ -1135,6 +1135,7 @@ RuboCop::Cop::RSpec::DescribedClassModuleWrapping::MSG = T.let(T.unsafe(nil), St
|
||||
# - let, let!
|
||||
# - subject, subject!
|
||||
# - expect, is_expected, expect_any_instance_of
|
||||
# - raise_error, raise_exception
|
||||
#
|
||||
# By default all of the RSpec methods and aliases are allowed. By setting
|
||||
# a config like:
|
||||
@ -1169,19 +1170,19 @@ RuboCop::Cop::RSpec::DescribedClassModuleWrapping::MSG = T.let(T.unsafe(nil), St
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#58
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#59
|
||||
class RuboCop::Cop::RSpec::Dialect < ::RuboCop::Cop::RSpec::Base
|
||||
include ::RuboCop::Cop::MethodPreference
|
||||
extend ::RuboCop::Cop::AutoCorrector
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#67
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#68
|
||||
def on_send(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#65
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#66
|
||||
def rspec_method?(param0 = T.unsafe(nil)); end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#62
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/dialect.rb#63
|
||||
RuboCop::Cop::RSpec::Dialect::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
# Avoid duplicated metadata.
|
||||
@ -1578,7 +1579,7 @@ class RuboCop::Cop::RSpec::EmptyLineAfterHook < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#60
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#60
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/empty_line_after_hook.rb#70
|
||||
def on_numblock(node); end
|
||||
|
||||
private
|
||||
@ -2189,7 +2190,7 @@ class RuboCop::Cop::RSpec::ExpectInHook < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#30
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#30
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/expect_in_hook.rb#40
|
||||
def on_numblock(node); end
|
||||
|
||||
private
|
||||
@ -2482,7 +2483,7 @@ class RuboCop::Cop::RSpec::HookArgument < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#78
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#78
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#91
|
||||
def on_numblock(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hook_argument.rb#69
|
||||
@ -2546,7 +2547,7 @@ class RuboCop::Cop::RSpec::HooksBeforeExamples < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#41
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#41
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/hooks_before_examples.rb#47
|
||||
def on_numblock(node); end
|
||||
|
||||
private
|
||||
@ -2838,18 +2839,18 @@ RuboCop::Cop::RSpec::ImplicitSubject::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Ar
|
||||
# # good
|
||||
# it_behaves_like 'examples'
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#22
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#73
|
||||
class RuboCop::Cop::RSpec::IncludeExamples < ::RuboCop::Cop::RSpec::Base
|
||||
extend ::RuboCop::Cop::AutoCorrector
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#29
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#80
|
||||
def on_send(node); end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#25
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#76
|
||||
RuboCop::Cop::RSpec::IncludeExamples::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#27
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/include_examples.rb#78
|
||||
RuboCop::Cop::RSpec::IncludeExamples::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
|
||||
|
||||
# Do not set up test data using indexes (e.g., `item_1`, `item_2`).
|
||||
@ -3211,16 +3212,18 @@ RuboCop::Cop::RSpec::ItBehavesLike::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Arra
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#19
|
||||
class RuboCop::Cop::RSpec::IteratedExpectation < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#24
|
||||
extend ::RuboCop::Cop::AutoCorrector
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#26
|
||||
def each?(param0 = T.unsafe(nil)); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#33
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#35
|
||||
def each_numblock?(param0 = T.unsafe(nil)); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#40
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#42
|
||||
def expectation?(param0 = T.unsafe(nil), param1); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#44
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#46
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#52
|
||||
@ -3228,18 +3231,29 @@ class RuboCop::Cop::RSpec::IteratedExpectation < ::RuboCop::Cop::RSpec::Base
|
||||
|
||||
private
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#60
|
||||
def check_offense(node, argument); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#66
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#88
|
||||
def only_expectations?(body, arg); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#62
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#84
|
||||
def single_expectation?(body, arg); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#73
|
||||
def single_expectation_replacement(node); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#80
|
||||
def uses_argument_in_matcher?(node, argument); end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#20
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/iterated_expectation.rb#22
|
||||
RuboCop::Cop::RSpec::IteratedExpectation::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
# Enforce that subject is the first definition in the test.
|
||||
@ -3393,17 +3407,22 @@ class RuboCop::Cop::RSpec::LeakyConstantDeclaration < ::RuboCop::Cop::RSpec::Bas
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#101
|
||||
def on_casgn(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#107
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#108
|
||||
def on_class(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#113
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#115
|
||||
def on_module(node); end
|
||||
|
||||
private
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#121
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#128
|
||||
def explicit_namespace?(namespace); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/leaky_constant_declaration.rb#124
|
||||
def inside_describe_block?(node); end
|
||||
end
|
||||
|
||||
@ -3772,7 +3791,7 @@ module RuboCop::Cop::RSpec::Metadata
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#43
|
||||
def on_metadata(_symbols, _hash); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#30
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#41
|
||||
def on_numblock(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/mixin/metadata.rb#21
|
||||
@ -4046,7 +4065,7 @@ class RuboCop::Cop::RSpec::MultipleExpectations < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#86
|
||||
def expect?(param0 = T.unsafe(nil)); end
|
||||
|
||||
# source://rubocop/1.75.6/lib/rubocop/cop/exclude_limit.rb#11
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#75
|
||||
def max=(value); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_expectations.rb#93
|
||||
@ -4161,7 +4180,7 @@ RuboCop::Cop::RSpec::MultipleExpectations::TRUE_NODE = T.let(T.unsafe(nil), Proc
|
||||
class RuboCop::Cop::RSpec::MultipleMemoizedHelpers < ::RuboCop::Cop::RSpec::Base
|
||||
include ::RuboCop::Cop::RSpec::Variable
|
||||
|
||||
# source://rubocop/1.75.6/lib/rubocop/cop/exclude_limit.rb#11
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_memoized_helpers.rb#89
|
||||
def max=(value); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/multiple_memoized_helpers.rb#91
|
||||
@ -4503,7 +4522,7 @@ end
|
||||
class RuboCop::Cop::RSpec::NestedGroups < ::RuboCop::Cop::RSpec::Base
|
||||
include ::RuboCop::Cop::RSpec::TopLevelGroup
|
||||
|
||||
# source://rubocop/1.75.6/lib/rubocop/cop/exclude_limit.rb#11
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/nested_groups.rb#105
|
||||
def max=(value); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/nested_groups.rb#107
|
||||
@ -4617,7 +4636,7 @@ class RuboCop::Cop::RSpec::NoExpectationExample < ::RuboCop::Cop::RSpec::Base
|
||||
|
||||
# @param node [RuboCop::AST::BlockNode]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/no_expectation_example.rb#89
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/no_expectation_example.rb#98
|
||||
def on_numblock(node); end
|
||||
|
||||
# @param node [RuboCop::AST::Node]
|
||||
@ -4997,7 +5016,7 @@ RuboCop::Cop::RSpec::ReceiveCounts::MSG = T.let(T.unsafe(nil), String)
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/receive_counts.rb#30
|
||||
RuboCop::Cop::RSpec::ReceiveCounts::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
|
||||
|
||||
# Checks for multiple messages stubbed on the same object.
|
||||
# Prefer `receive_messages` over multiple `receive`s on the same object.
|
||||
#
|
||||
# @example
|
||||
# # bad
|
||||
@ -5140,7 +5159,7 @@ class RuboCop::Cop::RSpec::RedundantAround < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#23
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#23
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#30
|
||||
def on_numblock(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/redundant_around.rb#32
|
||||
@ -6126,7 +6145,7 @@ class RuboCop::Cop::RSpec::SkipBlockInsideExample < ::RuboCop::Cop::RSpec::Base
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#29
|
||||
def on_block(node); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#29
|
||||
# source://rubocop-rspec//lib/rubocop/cop/rspec/skip_block_inside_example.rb#36
|
||||
def on_numblock(node); end
|
||||
|
||||
private
|
||||
@ -7109,7 +7128,7 @@ class RuboCop::RSpec::Concept
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/concept.rb#14
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/concept.rb#18
|
||||
def ==(other); end
|
||||
|
||||
# @return [Boolean]
|
||||
@ -7356,129 +7375,137 @@ end
|
||||
|
||||
# This is used in Dialect and DescribeClass cops to detect RSpec blocks.
|
||||
#
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#201
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#207
|
||||
module RuboCop::RSpec::Language::ALL
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#202
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#208
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#75
|
||||
module RuboCop::RSpec::Language::ErrorMatchers
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#76
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#81
|
||||
module RuboCop::RSpec::Language::ExampleGroups
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#77
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#83
|
||||
def all(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#87
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#93
|
||||
def focused(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#83
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#89
|
||||
def regular(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#91
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#97
|
||||
def skipped(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#97
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#103
|
||||
module RuboCop::RSpec::Language::Examples
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#99
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#105
|
||||
def all(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#110
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#116
|
||||
def focused(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#118
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#124
|
||||
def pending(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#106
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#112
|
||||
def regular(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#114
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#120
|
||||
def skipped(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#124
|
||||
module RuboCop::RSpec::Language::Expectations
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#125
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#130
|
||||
module RuboCop::RSpec::Language::Helpers
|
||||
module RuboCop::RSpec::Language::Expectations
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#131
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#142
|
||||
module RuboCop::RSpec::Language::HookScopes
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#144
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#143
|
||||
RuboCop::RSpec::Language::HookScopes::ALL = T.let(T.unsafe(nil), Array)
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#136
|
||||
module RuboCop::RSpec::Language::Hooks
|
||||
module RuboCop::RSpec::Language::Helpers
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#137
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#148
|
||||
module RuboCop::RSpec::Language::HookScopes
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#150
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#149
|
||||
RuboCop::RSpec::Language::HookScopes::ALL = T.let(T.unsafe(nil), Array)
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#142
|
||||
module RuboCop::RSpec::Language::Hooks
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#143
|
||||
def all(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#155
|
||||
module RuboCop::RSpec::Language::Includes
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#151
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#157
|
||||
def all(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#160
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#166
|
||||
def context(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#156
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#162
|
||||
def examples(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#166
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#172
|
||||
module RuboCop::RSpec::Language::Runners
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#169
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#175
|
||||
def all(element = T.unsafe(nil)); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#167
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#173
|
||||
RuboCop::RSpec::Language::Runners::ALL = T.let(T.unsafe(nil), Array)
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#177
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#183
|
||||
module RuboCop::RSpec::Language::SharedGroups
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#179
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#185
|
||||
def all(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#188
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#194
|
||||
def context(element); end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#184
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#190
|
||||
def examples(element); end
|
||||
end
|
||||
end
|
||||
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#194
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#200
|
||||
module RuboCop::RSpec::Language::Subjects
|
||||
class << self
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#195
|
||||
# source://rubocop-rspec//lib/rubocop/rspec/language.rb#201
|
||||
def all(element); end
|
||||
end
|
||||
end
|
@ -49,7 +49,7 @@ module Homebrew
|
||||
ruby_files = T.let([], T::Array[Pathname])
|
||||
shell_files = T.let([], T::Array[Pathname])
|
||||
actionlint_files = T.let([], T::Array[Pathname])
|
||||
Array(files).map(&method(:Pathname))
|
||||
Array(files).map { Pathname(_1) }
|
||||
.each do |path|
|
||||
case path.extname
|
||||
when ".rb"
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "plist"
|
||||
@ -21,33 +21,121 @@ class SystemCommand
|
||||
# Run a fallible system command.
|
||||
#
|
||||
# @api internal
|
||||
def system_command(executable, **options)
|
||||
SystemCommand.run(executable, **options)
|
||||
sig {
|
||||
params(
|
||||
executable: T.any(String, Pathname),
|
||||
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
|
||||
sudo: T::Boolean,
|
||||
sudo_as_root: T::Boolean,
|
||||
env: T::Hash[String, String],
|
||||
input: T.any(String, T::Array[String]),
|
||||
must_succeed: T::Boolean,
|
||||
print_stdout: T.any(T::Boolean, Symbol),
|
||||
print_stderr: T.any(T::Boolean, Symbol),
|
||||
debug: T.nilable(T::Boolean),
|
||||
verbose: T.nilable(T::Boolean),
|
||||
secrets: T.any(String, T::Array[String]),
|
||||
chdir: T.any(String, Pathname),
|
||||
reset_uid: T::Boolean,
|
||||
timeout: T.nilable(T.any(Integer, Float)),
|
||||
).returns(SystemCommand::Result)
|
||||
}
|
||||
def system_command(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [],
|
||||
must_succeed: false, print_stdout: false, print_stderr: true, debug: nil, verbose: nil,
|
||||
secrets: [], chdir: T.unsafe(nil), reset_uid: false, timeout: nil)
|
||||
SystemCommand.run(executable, args:, sudo:, sudo_as_root:, env:, input:, must_succeed:, print_stdout:,
|
||||
print_stderr:, debug:, verbose:, secrets:, chdir:, reset_uid:, timeout:)
|
||||
end
|
||||
|
||||
# Run an infallible system command.
|
||||
#
|
||||
# @api internal
|
||||
def system_command!(command, **options)
|
||||
SystemCommand.run!(command, **options)
|
||||
sig {
|
||||
params(
|
||||
executable: T.any(String, Pathname),
|
||||
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
|
||||
sudo: T::Boolean,
|
||||
sudo_as_root: T::Boolean,
|
||||
env: T::Hash[String, String],
|
||||
input: T.any(String, T::Array[String]),
|
||||
print_stdout: T.any(T::Boolean, Symbol),
|
||||
print_stderr: T.any(T::Boolean, Symbol),
|
||||
debug: T.nilable(T::Boolean),
|
||||
verbose: T.nilable(T::Boolean),
|
||||
secrets: T.any(String, T::Array[String]),
|
||||
chdir: T.any(String, Pathname),
|
||||
reset_uid: T::Boolean,
|
||||
timeout: T.nilable(T.any(Integer, Float)),
|
||||
).returns(SystemCommand::Result)
|
||||
}
|
||||
def system_command!(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [],
|
||||
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [],
|
||||
chdir: T.unsafe(nil), reset_uid: false, timeout: nil)
|
||||
SystemCommand.run!(executable, args:, sudo:, sudo_as_root:, env:, input:, print_stdout:,
|
||||
print_stderr:, debug:, verbose:, secrets:, chdir:, reset_uid:, timeout:)
|
||||
end
|
||||
end
|
||||
|
||||
include Context
|
||||
|
||||
def self.run(executable, **options)
|
||||
new(executable, **options).run!
|
||||
sig {
|
||||
params(
|
||||
executable: T.any(String, Pathname),
|
||||
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
|
||||
sudo: T::Boolean,
|
||||
sudo_as_root: T::Boolean,
|
||||
env: T::Hash[String, String],
|
||||
input: T.any(String, T::Array[String]),
|
||||
must_succeed: T::Boolean,
|
||||
print_stdout: T.any(T::Boolean, Symbol),
|
||||
print_stderr: T.any(T::Boolean, Symbol),
|
||||
debug: T.nilable(T::Boolean),
|
||||
verbose: T.nilable(T::Boolean),
|
||||
secrets: T.any(String, T::Array[String]),
|
||||
chdir: T.any(NilClass, String, Pathname),
|
||||
reset_uid: T::Boolean,
|
||||
timeout: T.nilable(T.any(Integer, Float)),
|
||||
).returns(SystemCommand::Result)
|
||||
}
|
||||
def self.run(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [], must_succeed: false,
|
||||
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [], chdir: nil,
|
||||
reset_uid: false, timeout: nil)
|
||||
new(executable, args:, sudo:, sudo_as_root:, env:, input:, must_succeed:, print_stdout:, print_stderr:, debug:,
|
||||
verbose:, secrets:, chdir:, reset_uid:, timeout:).run!
|
||||
end
|
||||
|
||||
def self.run!(command, **options)
|
||||
run(command, **options, must_succeed: true)
|
||||
sig {
|
||||
params(
|
||||
executable: T.any(String, Pathname),
|
||||
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
|
||||
sudo: T::Boolean,
|
||||
sudo_as_root: T::Boolean,
|
||||
env: T::Hash[String, String],
|
||||
input: T.any(String, T::Array[String]),
|
||||
must_succeed: T::Boolean,
|
||||
print_stdout: T.any(T::Boolean, Symbol),
|
||||
print_stderr: T.any(T::Boolean, Symbol),
|
||||
debug: T.nilable(T::Boolean),
|
||||
verbose: T.nilable(T::Boolean),
|
||||
secrets: T.any(String, T::Array[String]),
|
||||
chdir: T.any(NilClass, String, Pathname),
|
||||
reset_uid: T::Boolean,
|
||||
timeout: T.nilable(T.any(Integer, Float)),
|
||||
).returns(SystemCommand::Result)
|
||||
}
|
||||
def self.run!(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [], must_succeed: true,
|
||||
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [], chdir: nil,
|
||||
reset_uid: false, timeout: nil)
|
||||
run(executable, args:, sudo:, sudo_as_root:, env:, input:, must_succeed:, print_stdout:, print_stderr:,
|
||||
debug:, verbose:, secrets:, chdir:, reset_uid:, timeout:)
|
||||
end
|
||||
|
||||
sig { returns(SystemCommand::Result) }
|
||||
def run!
|
||||
$stderr.puts redact_secrets(command.shelljoin.gsub('\=', "="), @secrets) if verbose? && debug?
|
||||
|
||||
@output = []
|
||||
@output = T.let([], T.nilable(T::Array[[Symbol, String]]))
|
||||
@output = T.must(@output)
|
||||
|
||||
each_output_line do |type, line|
|
||||
case type
|
||||
@ -70,7 +158,7 @@ class SystemCommand
|
||||
end
|
||||
end
|
||||
|
||||
result = Result.new(command, @output, @status, secrets: @secrets)
|
||||
result = Result.new(command, @output, T.must(@status), secrets: @secrets)
|
||||
result.assert_success! if must_succeed?
|
||||
result
|
||||
end
|
||||
@ -78,7 +166,7 @@ class SystemCommand
|
||||
sig {
|
||||
params(
|
||||
executable: T.any(String, Pathname),
|
||||
args: T::Array[T.any(String, Integer, Float, URI::Generic)],
|
||||
args: T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)],
|
||||
sudo: T::Boolean,
|
||||
sudo_as_root: T::Boolean,
|
||||
env: T::Hash[String, String],
|
||||
@ -89,28 +177,14 @@ class SystemCommand
|
||||
debug: T.nilable(T::Boolean),
|
||||
verbose: T.nilable(T::Boolean),
|
||||
secrets: T.any(String, T::Array[String]),
|
||||
chdir: T.any(String, Pathname),
|
||||
chdir: T.any(NilClass, String, Pathname),
|
||||
reset_uid: T::Boolean,
|
||||
timeout: T.nilable(T.any(Integer, Float)),
|
||||
).void
|
||||
}
|
||||
def initialize(
|
||||
executable,
|
||||
args: [],
|
||||
sudo: false,
|
||||
sudo_as_root: false,
|
||||
env: {},
|
||||
input: [],
|
||||
must_succeed: false,
|
||||
print_stdout: false,
|
||||
print_stderr: true,
|
||||
debug: nil,
|
||||
verbose: T.unsafe(nil),
|
||||
secrets: [],
|
||||
chdir: T.unsafe(nil),
|
||||
reset_uid: false,
|
||||
timeout: nil
|
||||
)
|
||||
def initialize(executable, args: [], sudo: false, sudo_as_root: false, env: {}, input: [], must_succeed: false,
|
||||
print_stdout: false, print_stderr: true, debug: nil, verbose: nil, secrets: [], chdir: nil,
|
||||
reset_uid: false, timeout: nil)
|
||||
require "extend/ENV"
|
||||
@executable = executable
|
||||
@args = args
|
||||
@ -132,13 +206,13 @@ class SystemCommand
|
||||
raise ArgumentError, "Invalid variable name: #{name}"
|
||||
end
|
||||
@env = env
|
||||
@input = Array(input)
|
||||
@input = T.let(Array(input), T::Array[String])
|
||||
@must_succeed = must_succeed
|
||||
@print_stdout = print_stdout
|
||||
@print_stderr = print_stderr
|
||||
@debug = debug
|
||||
@verbose = verbose
|
||||
@secrets = (Array(secrets) + ENV.sensitive_environment.values).uniq
|
||||
@secrets = T.let((Array(secrets) + ENV.sensitive_environment.values).uniq, T::Array[String])
|
||||
@chdir = chdir
|
||||
@reset_uid = reset_uid
|
||||
@timeout = timeout
|
||||
@ -151,7 +225,20 @@ class SystemCommand
|
||||
|
||||
private
|
||||
|
||||
attr_reader :executable, :args, :input, :chdir, :env
|
||||
sig { returns(T.any(Pathname, String)) }
|
||||
attr_reader :executable
|
||||
|
||||
sig { returns(T::Array[T.any(String, Integer, Float, Pathname, URI::Generic)]) }
|
||||
attr_reader :args
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
attr_reader :input
|
||||
|
||||
sig { returns(T.any(NilClass, String, Pathname)) }
|
||||
attr_reader :chdir
|
||||
|
||||
sig { returns(T::Hash[String, String]) }
|
||||
attr_reader :env
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def must_succeed? = @must_succeed
|
||||
@ -227,15 +314,13 @@ class SystemCommand
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def expanded_args
|
||||
@expanded_args ||= args.map do |arg|
|
||||
if arg.respond_to?(:to_path)
|
||||
@expanded_args ||= T.let(args.map do |arg|
|
||||
if arg.is_a?(Pathname)
|
||||
File.absolute_path(arg)
|
||||
elsif arg.is_a?(Integer) || arg.is_a?(Float) || arg.is_a?(URI::Generic)
|
||||
arg.to_s
|
||||
else
|
||||
arg.to_str
|
||||
arg.to_s
|
||||
end
|
||||
end
|
||||
end, T.nilable(T::Array[String]))
|
||||
end
|
||||
|
||||
class ProcessTerminatedInterrupt < StandardError; end
|
||||
@ -275,7 +360,7 @@ class SystemCommand
|
||||
end_time = Time.now + @timeout if @timeout
|
||||
raise Timeout::Error if raw_wait_thr.join(Utils::Timer.remaining(end_time)).nil?
|
||||
|
||||
@status = raw_wait_thr.value
|
||||
@status = T.let(raw_wait_thr.value, T.nilable(Process::Status))
|
||||
rescue Interrupt
|
||||
Process.kill("INT", raw_wait_thr.pid) if raw_wait_thr && !sudo?
|
||||
raise Interrupt
|
||||
@ -383,7 +468,14 @@ class SystemCommand
|
||||
include Context
|
||||
include Utils::Output::Mixin
|
||||
|
||||
attr_accessor :command, :status, :exit_status
|
||||
sig { returns(T::Array[String]) }
|
||||
attr_accessor :command
|
||||
|
||||
sig { returns(Process::Status) }
|
||||
attr_accessor :status
|
||||
|
||||
sig { returns(T.nilable(Integer)) }
|
||||
attr_accessor :exit_status
|
||||
|
||||
sig {
|
||||
params(
|
||||
@ -397,7 +489,7 @@ class SystemCommand
|
||||
@command = command
|
||||
@output = output
|
||||
@status = status
|
||||
@exit_status = status.exitstatus
|
||||
@exit_status = T.let(status.exitstatus, T.nilable(Integer))
|
||||
@secrets = secrets
|
||||
end
|
||||
|
||||
@ -410,22 +502,21 @@ class SystemCommand
|
||||
|
||||
sig { returns(String) }
|
||||
def stdout
|
||||
@stdout ||= @output.select { |type,| type == :stdout }
|
||||
.map { |_, line| line }
|
||||
.join
|
||||
@stdout ||= T.let(@output.select { |type,| type == :stdout }
|
||||
.map { |_, line| line }
|
||||
.join, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def stderr
|
||||
@stderr ||= @output.select { |type,| type == :stderr }
|
||||
.map { |_, line| line }
|
||||
.join
|
||||
@stderr ||= T.let(@output.select { |type,| type == :stderr }
|
||||
.map { |_, line| line }
|
||||
.join, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def merged_output
|
||||
@merged_output ||= @output.map { |_, line| line }
|
||||
.join
|
||||
@merged_output ||= T.let(@output.map { |_, line| line }.join, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@ -439,10 +530,11 @@ class SystemCommand
|
||||
def to_ary
|
||||
[stdout, stderr, status]
|
||||
end
|
||||
alias to_a to_ary
|
||||
|
||||
sig { returns(T.nilable(T.any(Array, Hash))) }
|
||||
sig { returns(T.untyped) }
|
||||
def plist
|
||||
@plist ||= begin
|
||||
@plist ||= T.let(begin
|
||||
output = stdout
|
||||
|
||||
output = output.sub(/\A(.*?)(\s*<\?\s*xml)/m) do
|
||||
@ -456,7 +548,7 @@ class SystemCommand
|
||||
end
|
||||
|
||||
Plist.parse_xml(output, marshal: false)
|
||||
end
|
||||
end, T.untyped)
|
||||
end
|
||||
|
||||
sig { params(garbage: String).void }
|
||||
|
@ -102,7 +102,7 @@ module SystemConfig
|
||||
|
||||
sig { returns(String) }
|
||||
def describe_curl
|
||||
out, = system_command(Utils::Curl.curl_executable, args: ["--version"], verbose: false)
|
||||
out = system_command(Utils::Curl.curl_executable, args: ["--version"], verbose: false).stdout
|
||||
|
||||
match_data = /^curl (?<curl_version>[\d.]+)/.match(out)
|
||||
if match_data
|
||||
|
@ -169,6 +169,12 @@ class Tap
|
||||
sig { returns(String) }
|
||||
attr_reader :repository
|
||||
|
||||
# The repository name of this {Tap} including the leading `homebrew-`.
|
||||
#
|
||||
# @api public
|
||||
sig { returns(String) }
|
||||
attr_reader :full_repository
|
||||
|
||||
# The name of this {Tap}. It combines {#user} and {#repository} with a slash.
|
||||
# {#name} is always in lowercase.
|
||||
# e.g. `user/repository`
|
||||
@ -210,7 +216,8 @@ class Tap
|
||||
@user = user
|
||||
@repository = repository
|
||||
@name = T.let("#{@user}/#{@repository}".downcase, String)
|
||||
@full_name = T.let("#{@user}/homebrew-#{@repository}", String)
|
||||
@full_repository = T.let("homebrew-#{@repository}", String)
|
||||
@full_name = T.let("#{@user}/#{@full_repository}", String)
|
||||
@path = T.let(HOMEBREW_TAP_DIRECTORY/@full_name.downcase, Pathname)
|
||||
@git_repository = T.let(GitRepository.new(@path), GitRepository)
|
||||
end
|
||||
@ -731,7 +738,7 @@ class Tap
|
||||
end
|
||||
|
||||
if (formula_count = formula_files.count).positive?
|
||||
contents << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
|
||||
contents << Utils.pluralize("formula", formula_count, include_count: true)
|
||||
end
|
||||
|
||||
contents
|
||||
|
@ -45,10 +45,9 @@ begin
|
||||
formula.extend(Debrew::Formula)
|
||||
end
|
||||
|
||||
Pathname.prepend WriteMkpathExtension
|
||||
|
||||
ENV.extend(Stdenv)
|
||||
ENV.setup_build_environment(formula:, testing_formula: true)
|
||||
Pathname.prepend WriteMkpathExtension
|
||||
|
||||
# tests can also return false to indicate failure
|
||||
run_test = proc { |_ = nil| raise "test returned false" if formula.run_test(keep_tmp: args.keep_tmp?) == false }
|
||||
|
52
Library/Homebrew/test/cask/artifact/relocated_spec.rb
Normal file
52
Library/Homebrew/test/cask/artifact/relocated_spec.rb
Normal file
@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/artifact/relocated"
|
||||
|
||||
RSpec.describe Cask::Artifact::Relocated, :cask do
|
||||
let(:cask) do
|
||||
Cask::Cask.new("test-cask") do
|
||||
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
|
||||
homepage "https://brew.sh/"
|
||||
version "1.0"
|
||||
sha256 "67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94"
|
||||
end
|
||||
end
|
||||
|
||||
let(:command) { NeverSudoSystemCommand }
|
||||
let(:artifact) { described_class.new(cask, "test_file.txt") }
|
||||
|
||||
describe "#add_altname_metadata" do
|
||||
let(:file) { Pathname("/tmp/test_file.txt") }
|
||||
let(:altname) { Pathname("alternate_name.txt") }
|
||||
|
||||
before do
|
||||
allow(file).to receive_messages(basename: Pathname("test_file.txt"), writable?: true, realpath: file)
|
||||
end
|
||||
|
||||
context "when running on Linux", :needs_linux do
|
||||
it "is a no-op and does not call xattr commands" do
|
||||
expect(command).not_to receive(:run)
|
||||
expect(command).not_to receive(:run!)
|
||||
|
||||
artifact.send(:add_altname_metadata, file, altname, command: command)
|
||||
end
|
||||
end
|
||||
|
||||
context "when running on macOS", :needs_macos do
|
||||
before do
|
||||
stdout_double = instance_double(SystemCommand::Result, stdout: "")
|
||||
allow(command).to receive(:run).and_return(stdout_double)
|
||||
allow(command).to receive(:run!)
|
||||
end
|
||||
|
||||
it "calls xattr commands to set metadata" do
|
||||
expect(command).to receive(:run).with("/usr/bin/xattr",
|
||||
args: ["-p", "com.apple.metadata:kMDItemAlternateNames", file],
|
||||
print_stderr: false)
|
||||
expect(command).to receive(:run!).twice
|
||||
|
||||
artifact.send(:add_altname_metadata, file, altname, command: command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -236,4 +236,73 @@ RSpec.describe Cask::CaskLoader, :cask do
|
||||
expect(described_class.load_prefer_installed("test-cask").tap).to eq(foo_tap)
|
||||
end
|
||||
end
|
||||
|
||||
describe "FromPathLoader with symlinked taps" do
|
||||
let(:cask_token) { "testcask" }
|
||||
let(:tmpdir) { mktmpdir }
|
||||
let(:real_tap_path) { tmpdir / "real_tap" }
|
||||
let(:homebrew_prefix) { tmpdir / "homebrew" }
|
||||
let(:taps_dir) { homebrew_prefix / "Library" / "Taps" / "testuser" }
|
||||
let(:symlinked_tap_path) { taps_dir / "homebrew-testtap" }
|
||||
let(:cask_file_path) { symlinked_tap_path / "Casks" / "#{cask_token}.rb" }
|
||||
let(:cask_content) do
|
||||
<<~RUBY
|
||||
cask "#{cask_token}" do
|
||||
version "1.0.0"
|
||||
sha256 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
|
||||
|
||||
url "https://example.com/#{cask_token}-\#{version}.dmg"
|
||||
name "Test Cask"
|
||||
desc "A test cask for symlink testing"
|
||||
homepage "https://example.com"
|
||||
|
||||
app "TestCask.app"
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
after do
|
||||
tmpdir.rmtree if tmpdir.exist?
|
||||
end
|
||||
|
||||
before do
|
||||
# Create real tap directory structure
|
||||
(real_tap_path / "Casks").mkpath
|
||||
(real_tap_path / "Casks" / "#{cask_token}.rb").write(cask_content)
|
||||
|
||||
# Create homebrew prefix structure
|
||||
taps_dir.mkpath
|
||||
|
||||
# Create symlink to the tap (this simulates what setup-homebrew does)
|
||||
symlinked_tap_path.make_symlink(real_tap_path)
|
||||
|
||||
# Set HOMEBREW_LIBRARY to our test prefix for the security check
|
||||
stub_const("HOMEBREW_LIBRARY", homebrew_prefix / "Library")
|
||||
allow(Homebrew::EnvConfig).to receive(:forbid_packages_from_paths?).and_return(true)
|
||||
end
|
||||
|
||||
context "when HOMEBREW_FORBID_PACKAGES_FROM_PATHS is enabled" do
|
||||
it "allows loading casks from symlinked taps" do
|
||||
loader = Cask::CaskLoader::FromPathLoader.try_new(cask_file_path)
|
||||
expect(loader).not_to be_nil
|
||||
expect(loader).to be_a(Cask::CaskLoader::FromPathLoader)
|
||||
|
||||
cask = loader.load(config: nil)
|
||||
expect(cask.token).to eq(cask_token)
|
||||
expect(cask.version).to eq(Version.new("1.0.0"))
|
||||
end
|
||||
end
|
||||
|
||||
context "when HOMEBREW_FORBID_PACKAGES_FROM_PATHS is disabled" do
|
||||
before do
|
||||
allow(Homebrew::EnvConfig).to receive(:forbid_packages_from_paths?).and_return(false)
|
||||
end
|
||||
|
||||
it "allows loading casks from symlinked taps" do
|
||||
loader = Cask::CaskLoader::FromPathLoader.try_new(cask_file_path)
|
||||
expect(loader).not_to be_nil
|
||||
expect(loader).to be_a(Cask::CaskLoader::FromPathLoader)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -175,7 +175,7 @@ RSpec.describe Homebrew::CLI::Parser do
|
||||
flag "--flag2=", depends_on: "--flag1="
|
||||
flag "--flag3="
|
||||
|
||||
conflicts "--flag1=", "--flag3="
|
||||
conflicts "--flag1", "--flag3"
|
||||
end
|
||||
end
|
||||
|
||||
@ -204,7 +204,8 @@ RSpec.describe Homebrew::CLI::Parser do
|
||||
described_class.new(Cmd) do
|
||||
flag "--flag1="
|
||||
flag "--flag2=", depends_on: "--flag1="
|
||||
conflicts "--flag1=", "--flag2="
|
||||
|
||||
conflicts "--flag1", "--flag2"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -16,4 +16,15 @@ RSpec.describe Homebrew::DevCmd::Unpack do
|
||||
expect(path/"testball-0.1").to be_a_directory
|
||||
end
|
||||
end
|
||||
|
||||
it "unpacks a given Cask's archive", :integration_test do
|
||||
caffeine_cask = Cask::CaskLoader.load(cask_path("local-caffeine"))
|
||||
|
||||
mktmpdir do |path|
|
||||
expect { brew "unpack", cask_path("local-caffeine"), "--destdir=#{path}" }
|
||||
.to be_a_success
|
||||
|
||||
expect(path/"local-caffeine-#{caffeine_cask.version}").to be_a_directory
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,6 +4,61 @@ require "os/linux/ld"
|
||||
require "tmpdir"
|
||||
|
||||
RSpec.describe OS::Linux::Ld do
|
||||
let(:diagnostics) do
|
||||
<<~EOS
|
||||
path.prefix="/usr"
|
||||
path.sysconfdir="/usr/local/etc"
|
||||
path.system_dirs[0x0]="/lib64"
|
||||
path.system_dirs[0x1]="/var/lib"
|
||||
EOS
|
||||
end
|
||||
|
||||
describe "::system_ld_so" do
|
||||
let(:ld_so) { "/lib/ld-linux.so.3" }
|
||||
|
||||
before do
|
||||
allow(File).to receive(:executable?).and_return(false)
|
||||
described_class.instance_variable_set(:@system_ld_so, nil)
|
||||
end
|
||||
|
||||
it "returns the path to a known dynamic linker" do
|
||||
allow(File).to receive(:executable?).with(ld_so).and_return(true)
|
||||
expect(described_class.system_ld_so).to eq(Pathname(ld_so))
|
||||
end
|
||||
|
||||
it "returns nil when there is no known dynamic linker" do
|
||||
expect(described_class.system_ld_so).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "::sysconfdir" do
|
||||
it "returns path.sysconfdir" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return(diagnostics)
|
||||
expect(described_class.sysconfdir).to eq("/usr/local/etc")
|
||||
expect(described_class.sysconfdir(brewed: false)).to eq("/usr/local/etc")
|
||||
end
|
||||
|
||||
it "returns fallback on blank diagnostics" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return("")
|
||||
expect(described_class.sysconfdir).to eq("/etc")
|
||||
expect(described_class.sysconfdir(brewed: false)).to eq("/etc")
|
||||
end
|
||||
end
|
||||
|
||||
describe "::system_dirs" do
|
||||
it "returns all path.system_dirs" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return(diagnostics)
|
||||
expect(described_class.system_dirs).to eq(["/lib64", "/var/lib"])
|
||||
expect(described_class.system_dirs(brewed: false)).to eq(["/lib64", "/var/lib"])
|
||||
end
|
||||
|
||||
it "returns an empty array on blank diagnostics" do
|
||||
allow(described_class).to receive(:ld_so_diagnostics).and_return("")
|
||||
expect(described_class.system_dirs).to eq([])
|
||||
expect(described_class.system_dirs(brewed: false)).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "::library_paths" do
|
||||
ld_etc = Pathname("")
|
||||
before do
|
||||
|
58
Library/Homebrew/test/os/linux/libstdcxx_spec.rb
Normal file
58
Library/Homebrew/test/os/linux/libstdcxx_spec.rb
Normal file
@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/libstdcxx"
|
||||
|
||||
RSpec.describe OS::Linux::Libstdcxx do
|
||||
describe "::below_ci_version?" do
|
||||
it "returns false when system version matches CI version" do
|
||||
allow(described_class).to receive(:system_version).and_return(Version.new(OS::LINUX_LIBSTDCXX_CI_VERSION))
|
||||
expect(described_class.below_ci_version?).to be false
|
||||
end
|
||||
|
||||
it "returns true when system version cannot be detected" do
|
||||
allow(described_class).to receive(:system_version).and_return(Version::NULL)
|
||||
expect(described_class.below_ci_version?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe "::system_version" do
|
||||
let(:tmpdir) { mktmpdir }
|
||||
let(:libstdcxx) { tmpdir/described_class::SONAME }
|
||||
let(:soversion) { Version.new(described_class::SOVERSION.to_s) }
|
||||
|
||||
before do
|
||||
tmpdir.mkpath
|
||||
described_class.instance_variable_set(:@system_version, nil)
|
||||
allow(described_class).to receive(:system_path).and_return(libstdcxx)
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_rf(tmpdir)
|
||||
end
|
||||
|
||||
it "returns NULL when unable to find system path" do
|
||||
allow(described_class).to receive(:system_path).and_return(nil)
|
||||
expect(described_class.system_version).to be Version::NULL
|
||||
end
|
||||
|
||||
it "returns full version from filename" do
|
||||
full_version = Version.new("#{soversion}.0.999")
|
||||
libstdcxx_real = libstdcxx.sub_ext(".#{full_version}")
|
||||
FileUtils.touch libstdcxx_real
|
||||
FileUtils.ln_s libstdcxx_real, libstdcxx
|
||||
expect(described_class.system_version).to eq full_version
|
||||
end
|
||||
|
||||
it "returns major version when non-standard libstdc++ filename without full version" do
|
||||
FileUtils.touch libstdcxx
|
||||
expect(described_class.system_version).to eq soversion
|
||||
end
|
||||
|
||||
it "returns major version when non-standard libstdc++ filename with unexpected realpath" do
|
||||
libstdcxx_real = tmpdir/"libstdc++.so.real"
|
||||
FileUtils.touch libstdcxx_real
|
||||
FileUtils.ln_s libstdcxx_real, libstdcxx
|
||||
expect(described_class.system_version).to eq soversion
|
||||
end
|
||||
end
|
||||
end
|
@ -164,8 +164,8 @@ RSpec.describe Utils::Git do
|
||||
end
|
||||
|
||||
describe "::ensure_installed!" do
|
||||
it "returns nil if git already available" do
|
||||
expect(described_class.ensure_installed!).to be_nil
|
||||
it "doesn't fail if git already available" do
|
||||
expect { described_class.ensure_installed! }.not_to raise_error
|
||||
end
|
||||
|
||||
context "when git is not already available" do
|
||||
|
@ -32,9 +32,9 @@ RSpec.describe GitHub do
|
||||
end
|
||||
end
|
||||
|
||||
describe "::approved_reviews", :needs_network do
|
||||
describe "::repository_approved_reviews", :needs_network do
|
||||
it "can get reviews for a pull request" do
|
||||
reviews = described_class.approved_reviews("Homebrew", "homebrew-core", 1, commit: "deadbeef")
|
||||
reviews = described_class.repository_approved_reviews("Homebrew", "homebrew-core", 1, commit: "deadbeef")
|
||||
expect(reviews).to eq([])
|
||||
end
|
||||
end
|
||||
@ -88,51 +88,60 @@ RSpec.describe GitHub do
|
||||
describe "::count_repo_commits" do
|
||||
let(:five_shas) { %w[abcdef ghjkl mnop qrst uvwxyz] }
|
||||
let(:ten_shas) { %w[abcdef ghjkl mnop qrst uvwxyz fedcba lkjhg ponm tsrq zyxwvu] }
|
||||
let(:max) { 1000 }
|
||||
let(:verbose) { false }
|
||||
let(:from) { nil }
|
||||
let(:to) { nil }
|
||||
|
||||
it "counts commits authored by a user" do
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/cask", "user1", "author", nil, nil, nil).and_return(five_shas)
|
||||
.with("homebrew/cask", "user1", "author", nil, nil, max, verbose).and_return(five_shas)
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/cask", "user1", "committer", nil, nil, nil).and_return([])
|
||||
.with("homebrew/cask", "user1", "committer", nil, nil, max, verbose).and_return([])
|
||||
|
||||
expect(described_class.count_repo_commits("homebrew/cask", "user1")).to eq([5, 0])
|
||||
expect(described_class.count_repository_commits("homebrew/cask", "user1", max:, verbose:, from:,
|
||||
to:)).to eq(5)
|
||||
end
|
||||
|
||||
it "counts commits committed by a user" do
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/core", "user1", "author", nil, nil, nil).and_return([])
|
||||
.with("homebrew/core", "user1", "author", nil, nil, max, verbose).and_return([])
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/core", "user1", "committer", nil, nil, nil).and_return(five_shas)
|
||||
.with("homebrew/core", "user1", "committer", nil, nil, max, verbose).and_return(five_shas)
|
||||
|
||||
expect(described_class.count_repo_commits("homebrew/core", "user1")).to eq([0, 5])
|
||||
expect(described_class.count_repository_commits("homebrew/core", "user1", max:, verbose:, from:,
|
||||
to:)).to eq(5)
|
||||
end
|
||||
|
||||
it "calculates correctly when authored > committed with different shas" do
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/cask", "user1", "author", nil, nil, nil).and_return(ten_shas)
|
||||
.with("homebrew/cask", "user1", "author", nil, nil, max, verbose).and_return(ten_shas)
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/cask", "user1", "committer", nil, nil, nil).and_return(%w[1 2 3 4 5])
|
||||
.with("homebrew/cask", "user1", "committer", nil, nil, max, verbose).and_return(%w[1 2 3 4 5])
|
||||
|
||||
expect(described_class.count_repo_commits("homebrew/cask", "user1")).to eq([10, 5])
|
||||
expect(described_class.count_repository_commits("homebrew/cask", "user1", max:, verbose:, from:,
|
||||
to:)).to eq(15)
|
||||
end
|
||||
|
||||
it "calculates correctly when committed > authored" do
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/cask", "user1", "author", nil, nil, nil).and_return(five_shas)
|
||||
.with("homebrew/cask", "user1", "author", nil, nil, max, verbose).and_return(five_shas)
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/cask", "user1", "committer", nil, nil, nil).and_return(ten_shas)
|
||||
.with("homebrew/cask", "user1", "committer", nil, nil, max, verbose).and_return(ten_shas)
|
||||
|
||||
expect(described_class.count_repo_commits("homebrew/cask", "user1")).to eq([5, 5])
|
||||
expect(described_class.count_repository_commits("homebrew/cask", "user1", max:, verbose:, from:,
|
||||
to:)).to eq(10)
|
||||
end
|
||||
|
||||
it "deduplicates commits authored and committed by the same user" do
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/core", "user1", "author", nil, nil, nil).and_return(five_shas)
|
||||
.with("homebrew/core", "user1", "author", nil, nil, max, verbose).and_return(five_shas)
|
||||
allow(described_class).to receive(:repo_commits_for_user)
|
||||
.with("homebrew/core", "user1", "committer", nil, nil, nil).and_return(five_shas)
|
||||
.with("homebrew/core", "user1", "committer", nil, nil, max, verbose).and_return(five_shas)
|
||||
|
||||
# Because user1 authored and committed the same 5 commits.
|
||||
expect(described_class.count_repo_commits("homebrew/core", "user1")).to eq([5, 0])
|
||||
expect(described_class.count_repository_commits("homebrew/core", "user1", max:, verbose:, from:,
|
||||
to:)).to eq(5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -11,7 +11,8 @@ RSpec.describe User do
|
||||
before do
|
||||
allow(SystemCommand).to receive(:run)
|
||||
.with("who", any_args)
|
||||
.and_return([who_output, "", instance_double(Process::Status, success?: true)])
|
||||
.and_return(instance_double(SystemCommand::Result,
|
||||
to_a: [who_output, "", instance_double(Process::Status, success?: true)]))
|
||||
end
|
||||
|
||||
context "when the current user is in a console session" do
|
||||
|
@ -155,7 +155,7 @@ module UnpackStrategy
|
||||
filelist.close
|
||||
|
||||
system_command! "mkbom",
|
||||
args: ["-s", "-i", filelist.path, "--", bomfile.path],
|
||||
args: ["-s", "-i", T.must(filelist.path), "--", T.must(bomfile.path)],
|
||||
verbose:
|
||||
end
|
||||
|
||||
@ -179,8 +179,8 @@ module UnpackStrategy
|
||||
|
||||
sig { override.params(path: Pathname).returns(T::Boolean) }
|
||||
def self.can_extract?(path)
|
||||
stdout, _, status = system_command("hdiutil", args: ["imageinfo", "-format", path], print_stderr: false)
|
||||
status.success? && !stdout.empty?
|
||||
stdout, _, status = system_command("hdiutil", args: ["imageinfo", "-format", path], print_stderr: false).to_a
|
||||
(status.success? && !stdout.empty?) || false
|
||||
end
|
||||
|
||||
private
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user