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