Merge branch 'master' into codecov-action
This commit is contained in:
		
						commit
						ff7c05a285
					
				
							
								
								
									
										17
									
								
								.github/ISSUE_TEMPLATE/bug.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.github/ISSUE_TEMPLATE/bug.yml
									
									
									
									
										vendored
									
									
								
							@ -1,38 +1,47 @@
 | 
			
		||||
name: New issue for Reproducible Bug
 | 
			
		||||
about: "If you're sure it's reproducible and not just your machine: submit an issue so we can investigate."
 | 
			
		||||
description: "If you're sure it's reproducible and not just your machine: submit an issue so we can investigate."
 | 
			
		||||
labels: bug
 | 
			
		||||
issue_body: false
 | 
			
		||||
inputs:
 | 
			
		||||
  - type: description
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: Please note we will close your issue without comment if you do not fill out the issue checklist below and provide ALL the requested information. If you repeatedly fail to use the issue template, we will block you from ever submitting issues to Homebrew again.
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      render: shell
 | 
			
		||||
      label: "`brew config` output"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      render: shell
 | 
			
		||||
      label: "`brew doctor` output"
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: checkboxes
 | 
			
		||||
    attributes:
 | 
			
		||||
      description: Please verify that you've followed these steps
 | 
			
		||||
      choices:
 | 
			
		||||
      options:
 | 
			
		||||
        - label: The `brew doctor` above contains no "Warning" lines.
 | 
			
		||||
          required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: What were you trying to do (and why)?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: What happened (include all command output)?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: What did you expect to happen?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      render: shell
 | 
			
		||||
      label: Step-by-step reproduction instructions (by running `brew` commands)
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							@ -9,7 +9,7 @@ contact_links:
 | 
			
		||||
    about: On macOS/Mac OS X? Having a `brew` problem with a `brew install` or `brew upgrade` of a single formula/package? Report it to Homebrew/homebrew-core (the macOS core tap/repository).
 | 
			
		||||
  - name: New issue on Homebrew/homebrew-cask
 | 
			
		||||
    url: https://github.com/Homebrew/homebrew-cask/issues/new/choose
 | 
			
		||||
    about: Having a `brew cask` problem? Report it to Homebrew/homebrew-cask (the cask tap/repository).
 | 
			
		||||
    about: Having a `brew --cask` problem? Report it to Homebrew/homebrew-cask (the cask tap/repository).
 | 
			
		||||
  - name: New issue on Homebrew/linuxbrew-core
 | 
			
		||||
    url: https://github.com/Homebrew/linuxbrew-core/issues/new/choose
 | 
			
		||||
    about: On Linux? Having a `brew` problem with a `brew install` or `brew upgrade` of a single formula/package? Report it to Homebrew/linuxbrew-core (the Linux core tap/repository).
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								.github/ISSUE_TEMPLATE/feature.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/ISSUE_TEMPLATE/feature.yml
									
									
									
									
										vendored
									
									
								
							@ -1,27 +1,31 @@
 | 
			
		||||
name: New issue for Feature Suggestion
 | 
			
		||||
about: Request our thoughts on your suggestion for a new feature for Homebrew.
 | 
			
		||||
description: Request our thoughts on your suggestion for a new feature for Homebrew.
 | 
			
		||||
labels: features
 | 
			
		||||
issue_body: false
 | 
			
		||||
inputs:
 | 
			
		||||
  - type: description
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: Please note we will close your issue without comment if you do not fill out the issue checklist below and provide ALL the requested information. If you repeatedly fail to use the issue template, we will block you from ever submitting issues to Homebrew again.
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Provide a detailed description of the proposed feature
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: What is the motivation for the feature?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: How will the feature be relevant to at least 90% of Homebrew users?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: What alternatives to the feature have been considered?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: description
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: We will close this issue or ask you to create a pull-request if it's something the maintainers are not actively planning to work on.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							@ -5,6 +5,5 @@
 | 
			
		||||
- [ ] Have you successfully run `brew style` with your changes locally?
 | 
			
		||||
- [ ] Have you successfully run `brew typecheck` with your changes locally?
 | 
			
		||||
- [ ] Have you successfully run `brew tests` with your changes locally?
 | 
			
		||||
- [ ] Have you successfully run `brew man` locally and committed any changes?
 | 
			
		||||
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@ -10,10 +10,14 @@ updates:
 | 
			
		||||
    directory: /docs
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: weekly
 | 
			
		||||
    allow:
 | 
			
		||||
      - dependency-type: all
 | 
			
		||||
 | 
			
		||||
  - package-ecosystem: bundler
 | 
			
		||||
    directory: /Library/Homebrew
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: daily
 | 
			
		||||
    allow:
 | 
			
		||||
      - dependency-type: all
 | 
			
		||||
    ignore:
 | 
			
		||||
      - dependency-name: sorbet-runtime
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/doctor.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/doctor.yml
									
									
									
									
										vendored
									
									
								
							@ -16,7 +16,7 @@ jobs:
 | 
			
		||||
  tests:
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        version: ['11-arm', '11.0', '10.15', '10.14']
 | 
			
		||||
        version: ["11-arm", "11.0", "10.15", "10.14"]
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
    runs-on: ${{ matrix.version }}
 | 
			
		||||
    env:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										78
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							@ -31,8 +31,6 @@ jobs:
 | 
			
		||||
 | 
			
		||||
      - run: brew style --display-cop-names
 | 
			
		||||
 | 
			
		||||
      - run: brew man --fail-if-changed
 | 
			
		||||
 | 
			
		||||
      - run: brew typecheck
 | 
			
		||||
 | 
			
		||||
      - name: Run vale for docs linting
 | 
			
		||||
@ -81,6 +79,58 @@ jobs:
 | 
			
		||||
      - name: Run brew audit --skip-style on all taps
 | 
			
		||||
        run: brew audit --skip-style
 | 
			
		||||
 | 
			
		||||
      - name: Set up all Homebrew taps
 | 
			
		||||
        run: |
 | 
			
		||||
          HOMEBREW_REPOSITORY="$(brew --repo)"
 | 
			
		||||
          HOMEBREW_CORE_REPOSITORY="$HOMEBREW_REPOSITORY/Library/Taps/homebrew/homebrew-core"
 | 
			
		||||
          git -C "$HOMEBREW_CORE_REPOSITORY" remote add homebrew_core https://github.com/Homebrew/homebrew-core
 | 
			
		||||
          git -C "$HOMEBREW_CORE_REPOSITORY" fetch homebrew_core || git -C "$HOMEBREW_CORE_REPOSITORY" fetch homebrew_core
 | 
			
		||||
          git -C "$HOMEBREW_CORE_REPOSITORY" checkout --force -B master homebrew_core/master
 | 
			
		||||
 | 
			
		||||
          brew tap homebrew/aliases
 | 
			
		||||
          brew tap homebrew/bundle
 | 
			
		||||
          brew tap homebrew/cask
 | 
			
		||||
          brew tap homebrew/cask-drivers
 | 
			
		||||
          brew tap homebrew/cask-fonts
 | 
			
		||||
          brew tap homebrew/cask-versions
 | 
			
		||||
          brew tap homebrew/command-not-found
 | 
			
		||||
          brew tap homebrew/formula-analytics
 | 
			
		||||
          brew tap homebrew/linux-dev
 | 
			
		||||
          brew tap homebrew/portable-ruby
 | 
			
		||||
          brew tap homebrew/services
 | 
			
		||||
 | 
			
		||||
          brew update-reset Library/Taps/homebrew/homebrew-bundle
 | 
			
		||||
 | 
			
		||||
          # brew style doesn't like world writable directories
 | 
			
		||||
          sudo chmod -R g-w,o-w "$HOMEBREW_REPOSITORY/Library/Taps"
 | 
			
		||||
 | 
			
		||||
      - name: Run brew style on homebrew-core
 | 
			
		||||
        run: brew style --display-cop-names homebrew/core
 | 
			
		||||
 | 
			
		||||
      - name: Run brew audit --skip-style on homebrew-core
 | 
			
		||||
        run: brew audit --skip-style --tap=homebrew/core
 | 
			
		||||
        env:
 | 
			
		||||
          HOMEBREW_SIMULATE_MACOS_ON_LINUX: 1
 | 
			
		||||
 | 
			
		||||
      - name: Run brew style on official taps
 | 
			
		||||
        run: |
 | 
			
		||||
          brew style --display-cop-names homebrew/bundle \
 | 
			
		||||
                                         homebrew/services \
 | 
			
		||||
                                         homebrew/test-bot
 | 
			
		||||
 | 
			
		||||
          brew style --display-cop-names homebrew/aliases\
 | 
			
		||||
                                         homebrew/command-not-found \
 | 
			
		||||
                                         homebrew/formula-analytics \
 | 
			
		||||
                                         homebrew/linux-dev \
 | 
			
		||||
                                         homebrew/portable-ruby
 | 
			
		||||
 | 
			
		||||
      - name: Run brew style on cask taps
 | 
			
		||||
        run: |
 | 
			
		||||
          brew style --display-cop-names homebrew/cask \
 | 
			
		||||
                                         homebrew/cask-drivers \
 | 
			
		||||
                                         homebrew/cask-fonts \
 | 
			
		||||
                                         homebrew/cask-versions
 | 
			
		||||
 | 
			
		||||
  vendored-gems:
 | 
			
		||||
    name: vendored gems (Linux)
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
@ -231,29 +281,17 @@ jobs:
 | 
			
		||||
      - name: Run brew readall on all taps
 | 
			
		||||
        run: brew readall --aliases
 | 
			
		||||
 | 
			
		||||
      - name: Run brew style on homebrew-core
 | 
			
		||||
        run: brew style --display-cop-names homebrew/core
 | 
			
		||||
 | 
			
		||||
      - name: Run brew style on official taps
 | 
			
		||||
      - name: Run brew audit --skip-style on Cask taps
 | 
			
		||||
        run: |
 | 
			
		||||
          brew style --display-cop-names homebrew/bundle \
 | 
			
		||||
                                         homebrew/services \
 | 
			
		||||
                                         homebrew/test-bot
 | 
			
		||||
 | 
			
		||||
      - name: Run brew style on cask taps
 | 
			
		||||
        run: |
 | 
			
		||||
          brew style --display-cop-names homebrew/cask \
 | 
			
		||||
                                         homebrew/cask-drivers \
 | 
			
		||||
                                         homebrew/cask-fonts \
 | 
			
		||||
                                         homebrew/cask-versions
 | 
			
		||||
 | 
			
		||||
      - name: Run brew audit --skip-style on all taps
 | 
			
		||||
        run: brew audit --skip-style
 | 
			
		||||
          brew audit --skip-style --tap=homebrew/cask
 | 
			
		||||
          brew audit --skip-style --tap=homebrew/cask-drivers
 | 
			
		||||
          brew audit --skip-style --tap=homebrew/cask-fonts
 | 
			
		||||
          brew audit --skip-style --tap=homebrew/cask-versions
 | 
			
		||||
 | 
			
		||||
      - name: Install brew tests dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
          brew install subversion
 | 
			
		||||
          Library/Homebrew/shims/scm/svn --homebrew=print-path
 | 
			
		||||
          brew sh -c "svn --homebrew=print-path"
 | 
			
		||||
          which svn
 | 
			
		||||
          which svnadmin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/triage.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/triage.yml
									
									
									
									
										vendored
									
									
								
							@ -19,7 +19,7 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Re-run this workflow
 | 
			
		||||
        if: github.event_name == 'schedule' || github.event.action == 'closed'
 | 
			
		||||
        uses: reitermarkus/rerun-workflow@e2647e8885422412d5acbd61f9add5b1e77ad3ab
 | 
			
		||||
        uses: reitermarkus/rerun-workflow@64cba9e834916060e77b7dad424d086837fdd0a6
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
 | 
			
		||||
          continuous-label: waiting for feedback
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										82
									
								
								.github/workflows/update-manpage.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								.github/workflows/update-manpage.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
			
		||||
name: Update maintainers, manpage and completions
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
      - .github/workflows/update-manpage.yml
 | 
			
		||||
      - README.md
 | 
			
		||||
      - Library/Homebrew/cmd/**
 | 
			
		||||
      - Library/Homebrew/dev-cmd/**
 | 
			
		||||
      - Library/Homebrew/completions/**
 | 
			
		||||
      - Library/Homebrew/manpages/**
 | 
			
		||||
      - Library/Homebrew/cli/parser.rb
 | 
			
		||||
      - Library/Homebrew/completions.rb
 | 
			
		||||
      - Library/Homebrew/env_config.rb
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: "0 0 * * *"
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  update-manpage:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    if: github.repository == 'Homebrew/brew'
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Setup Homebrew
 | 
			
		||||
        uses: Homebrew/actions/setup-homebrew@master
 | 
			
		||||
 | 
			
		||||
      - name: Configure Git user
 | 
			
		||||
        uses: Homebrew/actions/git-user-config@master
 | 
			
		||||
        with:
 | 
			
		||||
          username: BrewTestBot
 | 
			
		||||
 | 
			
		||||
      - name: Update maintainers, manpage and completions
 | 
			
		||||
        id: update
 | 
			
		||||
        run: |
 | 
			
		||||
          git fetch origin
 | 
			
		||||
 | 
			
		||||
          BRANCH=update-manpage
 | 
			
		||||
          echo "::set-output name=branch::${BRANCH}"
 | 
			
		||||
 | 
			
		||||
          if git ls-remote --exit-code --heads origin "$BRANCH"; then
 | 
			
		||||
            git checkout "$BRANCH"
 | 
			
		||||
            git reset --hard origin/master
 | 
			
		||||
          else
 | 
			
		||||
            git checkout -B "$BRANCH" origin/master
 | 
			
		||||
            BRANCH_EXISTS="1"
 | 
			
		||||
          fi
 | 
			
		||||
 | 
			
		||||
          if [ "${{github.event_name}}" != "push" ]; then
 | 
			
		||||
            brew update-maintainers
 | 
			
		||||
          fi
 | 
			
		||||
 | 
			
		||||
          if brew man --fail-if-not-changed; then
 | 
			
		||||
            git add "$GITHUB_WORKSPACE/README.md" \
 | 
			
		||||
                    "$GITHUB_WORKSPACE/docs/Manpage.md" \
 | 
			
		||||
                    "$GITHUB_WORKSPACE/manpages/brew.1" \
 | 
			
		||||
                    "$GITHUB_WORKSPACE/completions"
 | 
			
		||||
            git commit -m "Update maintainers, manpage and completions." \
 | 
			
		||||
                       -m "Autogenerated by the [update-manpage](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/update-manpage.yml) workflow."
 | 
			
		||||
            echo "::set-output name=committed::true"
 | 
			
		||||
            if [ -n "$BRANCH_EXISTS" ]; then
 | 
			
		||||
              echo "::set-output name=pull_request::true"
 | 
			
		||||
            fi
 | 
			
		||||
          fi
 | 
			
		||||
        env:
 | 
			
		||||
          HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Push commits
 | 
			
		||||
        if: steps.update.outputs.committed == 'true'
 | 
			
		||||
        uses: Homebrew/actions/git-try-push@master
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
 | 
			
		||||
          branch: ${{ steps.update.outputs.branch }}
 | 
			
		||||
          force: true
 | 
			
		||||
          origin_branch: "master"
 | 
			
		||||
 | 
			
		||||
      - name: Open a pull request
 | 
			
		||||
        if: steps.update.outputs.pull_request == 'true'
 | 
			
		||||
        run: hub pull-request --no-edit
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
 | 
			
		||||
@ -14,6 +14,7 @@ RUN apt-get update \
 | 
			
		||||
    file \
 | 
			
		||||
    fonts-dejavu-core \
 | 
			
		||||
    g++ \
 | 
			
		||||
    gawk \
 | 
			
		||||
    git \
 | 
			
		||||
    less \
 | 
			
		||||
    libz-dev \
 | 
			
		||||
 | 
			
		||||
@ -240,6 +240,12 @@ Layout/SpaceAroundOperators:
 | 
			
		||||
Layout/RescueEnsureAlignment:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
 | 
			
		||||
# significantly less indentation involved; more consistent
 | 
			
		||||
Layout/FirstArrayElementIndentation:
 | 
			
		||||
  EnforcedStyle: consistent
 | 
			
		||||
Layout/FirstHashElementIndentation:
 | 
			
		||||
  EnforcedStyle: consistent
 | 
			
		||||
 | 
			
		||||
# favour parens-less DSL-style arguments
 | 
			
		||||
Lint/AmbiguousBlockAssociation:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
@ -255,14 +261,6 @@ Naming/MemoizedInstanceVariableName:
 | 
			
		||||
  Exclude:
 | 
			
		||||
    - "Homebrew/lazy_object.rb"
 | 
			
		||||
 | 
			
		||||
# so many of these in formulae and can't be autocorrected
 | 
			
		||||
# TODO: fix these as `ruby -w` complains about them.
 | 
			
		||||
Lint/AmbiguousRegexpLiteral:
 | 
			
		||||
  Exclude:
 | 
			
		||||
    - "Taps/*/*/*.rb"
 | 
			
		||||
    - "/**/Formula/*.rb"
 | 
			
		||||
    - "**/Formula/*.rb"
 | 
			
		||||
 | 
			
		||||
# useful for metaprogramming in RSpec
 | 
			
		||||
Lint/ConstantDefinitionInBlock:
 | 
			
		||||
  Exclude:
 | 
			
		||||
@ -462,6 +460,11 @@ Style/StringLiteralsInInterpolation:
 | 
			
		||||
Style/TernaryParentheses:
 | 
			
		||||
  EnforcedStyle: require_parentheses_when_complex
 | 
			
		||||
 | 
			
		||||
# `unless ... ||` and `unless ... &&` are hard to mentally parse
 | 
			
		||||
Style/UnlessLogicalOperators:
 | 
			
		||||
  Enabled: true
 | 
			
		||||
  EnforcedStyle: forbid_logical_operators
 | 
			
		||||
 | 
			
		||||
# a bit confusing to non-Rubyists but useful for longer arrays
 | 
			
		||||
Style/WordArray:
 | 
			
		||||
  MinSize: 4
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,6 @@ RSpec/SubjectStub:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
 | 
			
		||||
# TODO: try to enable these
 | 
			
		||||
RSpec/ContextWording:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
RSpec/DescribeClass:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
RSpec/LeakyConstantDeclaration:
 | 
			
		||||
@ -21,8 +19,6 @@ RSpec/MessageSpies:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
RSpec/RepeatedDescription:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
RSpec/RepeatedExampleGroupDescription:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
RSpec/StubbedMock:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,10 +7,6 @@ Layout/MultilineMethodCallIndentation:
 | 
			
		||||
  Exclude:
 | 
			
		||||
    - "**/*_spec.rb"
 | 
			
		||||
 | 
			
		||||
# TODO: add parentheses for these and remove
 | 
			
		||||
Lint/AssignmentInCondition:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
 | 
			
		||||
# `formula do` uses nested method definitions
 | 
			
		||||
Lint/NestedMethodDefinition:
 | 
			
		||||
  Exclude:
 | 
			
		||||
@ -36,9 +32,8 @@ Metrics/PerceivedComplexity:
 | 
			
		||||
  Max: 90
 | 
			
		||||
Metrics/MethodLength:
 | 
			
		||||
  Max: 260
 | 
			
		||||
# TODO: Reduce to 600 after refactoring utils/github
 | 
			
		||||
Metrics/ModuleLength:
 | 
			
		||||
  Max: 620
 | 
			
		||||
  Max: 600
 | 
			
		||||
  Exclude:
 | 
			
		||||
    - "test/**/*"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ source "https://rubygems.org"
 | 
			
		||||
# installed gems (should all be require: false)
 | 
			
		||||
gem "bootsnap", require: false
 | 
			
		||||
gem "byebug", require: false
 | 
			
		||||
gem "minitest", require: false
 | 
			
		||||
gem "nokogiri", require: false
 | 
			
		||||
gem "parallel_tests", require: false
 | 
			
		||||
gem "ronn", require: false
 | 
			
		||||
@ -20,6 +21,7 @@ gem "simplecov", require: false
 | 
			
		||||
gem "sorbet", require: false
 | 
			
		||||
gem "sorbet-runtime", require: false
 | 
			
		||||
gem "tapioca", require: false
 | 
			
		||||
gem "warning", require: false
 | 
			
		||||
 | 
			
		||||
# vendored gems
 | 
			
		||||
gem "activesupport"
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
GEM
 | 
			
		||||
  remote: https://rubygems.org/
 | 
			
		||||
  specs:
 | 
			
		||||
    activesupport (6.1.2)
 | 
			
		||||
    activesupport (6.1.3)
 | 
			
		||||
      concurrent-ruby (~> 1.0, >= 1.0.2)
 | 
			
		||||
      i18n (>= 1.6, < 2)
 | 
			
		||||
      minitest (>= 5.1)
 | 
			
		||||
@ -28,7 +28,7 @@ GEM
 | 
			
		||||
    hpricot (0.8.6)
 | 
			
		||||
    http-cookie (1.0.3)
 | 
			
		||||
      domain_name (~> 0.5)
 | 
			
		||||
    i18n (1.8.8)
 | 
			
		||||
    i18n (1.8.9)
 | 
			
		||||
      concurrent-ruby (~> 1.0)
 | 
			
		||||
    mechanize (2.7.7)
 | 
			
		||||
      domain_name (~> 0.5, >= 0.5.1)
 | 
			
		||||
@ -43,22 +43,22 @@ GEM
 | 
			
		||||
    method_source (1.0.0)
 | 
			
		||||
    mime-types (3.3.1)
 | 
			
		||||
      mime-types-data (~> 3.2015)
 | 
			
		||||
    mime-types-data (3.2020.1104)
 | 
			
		||||
    mime-types-data (3.2021.0212)
 | 
			
		||||
    mini_portile2 (2.5.0)
 | 
			
		||||
    minitest (5.14.3)
 | 
			
		||||
    minitest (5.14.4)
 | 
			
		||||
    msgpack (1.4.2)
 | 
			
		||||
    mustache (1.1.1)
 | 
			
		||||
    net-http-digest_auth (1.4.1)
 | 
			
		||||
    net-http-persistent (4.0.1)
 | 
			
		||||
      connection_pool (~> 2.2)
 | 
			
		||||
    nokogiri (1.11.1)
 | 
			
		||||
    nokogiri (1.11.2)
 | 
			
		||||
      mini_portile2 (~> 2.5.0)
 | 
			
		||||
      racc (~> 1.4)
 | 
			
		||||
    ntlm-http (0.1.1)
 | 
			
		||||
    parallel (1.20.1)
 | 
			
		||||
    parallel_tests (3.4.0)
 | 
			
		||||
    parallel_tests (3.5.2)
 | 
			
		||||
      parallel
 | 
			
		||||
    parlour (5.0.0)
 | 
			
		||||
    parlour (6.0.0)
 | 
			
		||||
      commander (~> 4.5)
 | 
			
		||||
      parser
 | 
			
		||||
      rainbow (~> 3.0)
 | 
			
		||||
@ -68,14 +68,14 @@ GEM
 | 
			
		||||
    patchelf (1.3.0)
 | 
			
		||||
      elftools (>= 1.1.3)
 | 
			
		||||
    plist (3.6.0)
 | 
			
		||||
    pry (0.13.1)
 | 
			
		||||
    pry (0.14.0)
 | 
			
		||||
      coderay (~> 1.1)
 | 
			
		||||
      method_source (~> 1.0)
 | 
			
		||||
    racc (1.5.2)
 | 
			
		||||
    rack (2.2.3)
 | 
			
		||||
    rainbow (3.0.0)
 | 
			
		||||
    rdiscount (2.2.0.2)
 | 
			
		||||
    regexp_parser (2.0.3)
 | 
			
		||||
    regexp_parser (2.1.1)
 | 
			
		||||
    rexml (3.2.4)
 | 
			
		||||
    ronn (0.7.3)
 | 
			
		||||
      hpricot (>= 0.8.2)
 | 
			
		||||
@ -85,9 +85,9 @@ GEM
 | 
			
		||||
      rspec-core (~> 3.10.0)
 | 
			
		||||
      rspec-expectations (~> 3.10.0)
 | 
			
		||||
      rspec-mocks (~> 3.10.0)
 | 
			
		||||
    rspec-core (3.10.0)
 | 
			
		||||
    rspec-core (3.10.1)
 | 
			
		||||
      rspec-support (~> 3.10.0)
 | 
			
		||||
    rspec-expectations (3.10.0)
 | 
			
		||||
    rspec-expectations (3.10.1)
 | 
			
		||||
      diff-lcs (>= 1.2.0, < 2.0)
 | 
			
		||||
      rspec-support (~> 3.10.0)
 | 
			
		||||
    rspec-github (2.3.1)
 | 
			
		||||
@ -95,7 +95,7 @@ GEM
 | 
			
		||||
    rspec-its (1.3.0)
 | 
			
		||||
      rspec-core (>= 3.0.0)
 | 
			
		||||
      rspec-expectations (>= 3.0.0)
 | 
			
		||||
    rspec-mocks (3.10.0)
 | 
			
		||||
    rspec-mocks (3.10.2)
 | 
			
		||||
      diff-lcs (>= 1.2.0, < 2.0)
 | 
			
		||||
      rspec-support (~> 3.10.0)
 | 
			
		||||
    rspec-retry (0.6.2)
 | 
			
		||||
@ -103,10 +103,10 @@ GEM
 | 
			
		||||
    rspec-sorbet (1.8.0)
 | 
			
		||||
      sorbet
 | 
			
		||||
      sorbet-runtime
 | 
			
		||||
    rspec-support (3.10.0)
 | 
			
		||||
    rspec-support (3.10.2)
 | 
			
		||||
    rspec-wait (0.0.9)
 | 
			
		||||
      rspec (>= 3, < 4)
 | 
			
		||||
    rubocop (1.9.1)
 | 
			
		||||
    rubocop (1.11.0)
 | 
			
		||||
      parallel (~> 1.10)
 | 
			
		||||
      parser (>= 3.0.0.0)
 | 
			
		||||
      rainbow (>= 2.2.2, < 4.0)
 | 
			
		||||
@ -117,7 +117,7 @@ GEM
 | 
			
		||||
      unicode-display_width (>= 1.4.0, < 3.0)
 | 
			
		||||
    rubocop-ast (1.4.1)
 | 
			
		||||
      parser (>= 2.7.1.5)
 | 
			
		||||
    rubocop-performance (1.9.2)
 | 
			
		||||
    rubocop-performance (1.10.1)
 | 
			
		||||
      rubocop (>= 0.90.0, < 2.0)
 | 
			
		||||
      rubocop-ast (>= 0.4.0)
 | 
			
		||||
    rubocop-rails (2.9.1)
 | 
			
		||||
@ -127,7 +127,7 @@ GEM
 | 
			
		||||
    rubocop-rspec (2.2.0)
 | 
			
		||||
      rubocop (~> 1.0)
 | 
			
		||||
      rubocop-ast (>= 1.1.0)
 | 
			
		||||
    rubocop-sorbet (0.5.1)
 | 
			
		||||
    rubocop-sorbet (0.6.1)
 | 
			
		||||
      rubocop
 | 
			
		||||
    ruby-macho (2.5.0)
 | 
			
		||||
    ruby-progressbar (1.11.0)
 | 
			
		||||
@ -139,28 +139,30 @@ GEM
 | 
			
		||||
    simplecov_json_formatter (0.1.2)
 | 
			
		||||
    sorbet (0.5.6274)
 | 
			
		||||
      sorbet-static (= 0.5.6274)
 | 
			
		||||
    sorbet-runtime (0.5.6267)
 | 
			
		||||
    sorbet-runtime (0.5.6274)
 | 
			
		||||
    sorbet-runtime-stub (0.2.0)
 | 
			
		||||
    sorbet-static (0.5.6274-universal-darwin-14)
 | 
			
		||||
    spoom (1.0.7)
 | 
			
		||||
    spoom (1.0.9)
 | 
			
		||||
      colorize
 | 
			
		||||
      sorbet (~> 0.5.5)
 | 
			
		||||
      sorbet-runtime
 | 
			
		||||
      thor (>= 0.19.2)
 | 
			
		||||
    tapioca (0.4.13)
 | 
			
		||||
    tapioca (0.4.17)
 | 
			
		||||
      bundler (>= 1.17.3)
 | 
			
		||||
      parlour (>= 2.1.0)
 | 
			
		||||
      pry (>= 0.12.2)
 | 
			
		||||
      sorbet-runtime
 | 
			
		||||
      sorbet-static (>= 0.4.4471)
 | 
			
		||||
      spoom
 | 
			
		||||
      thor (>= 0.19.2)
 | 
			
		||||
    thor (1.0.1)
 | 
			
		||||
    thor (1.1.0)
 | 
			
		||||
    tzinfo (2.0.4)
 | 
			
		||||
      concurrent-ruby (~> 1.0)
 | 
			
		||||
    unf (0.1.4)
 | 
			
		||||
      unf_ext
 | 
			
		||||
    unf_ext (0.0.7.7)
 | 
			
		||||
    unicode-display_width (2.0.0)
 | 
			
		||||
    warning (1.2.0)
 | 
			
		||||
    webrick (1.7.0)
 | 
			
		||||
    webrobots (0.1.2)
 | 
			
		||||
    zeitwerk (2.4.2)
 | 
			
		||||
@ -174,6 +176,7 @@ DEPENDENCIES
 | 
			
		||||
  byebug
 | 
			
		||||
  concurrent-ruby
 | 
			
		||||
  mechanize
 | 
			
		||||
  minitest
 | 
			
		||||
  nokogiri
 | 
			
		||||
  parallel_tests
 | 
			
		||||
  patchelf
 | 
			
		||||
@ -197,6 +200,7 @@ DEPENDENCIES
 | 
			
		||||
  sorbet-runtime
 | 
			
		||||
  sorbet-runtime-stub
 | 
			
		||||
  tapioca
 | 
			
		||||
  warning
 | 
			
		||||
 | 
			
		||||
BUNDLED WITH
 | 
			
		||||
   1.17.3
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										180
									
								
								Library/Homebrew/archive.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								Library/Homebrew/archive.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,180 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "digest/md5"
 | 
			
		||||
require "utils/curl"
 | 
			
		||||
 | 
			
		||||
# The Internet Archive API client.
 | 
			
		||||
#
 | 
			
		||||
# @api private
 | 
			
		||||
class Archive
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
 | 
			
		||||
  include Context
 | 
			
		||||
  include Utils::Curl
 | 
			
		||||
 | 
			
		||||
  class Error < RuntimeError
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  URL_PREFIX = "https://archive.org"
 | 
			
		||||
  S3_DOMAIN = "s3.us.archive.org"
 | 
			
		||||
 | 
			
		||||
  sig { returns(String) }
 | 
			
		||||
  def inspect
 | 
			
		||||
    "#<Archive: item=#{@archive_item}>"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(item: T.nilable(String)).void }
 | 
			
		||||
  def initialize(item: "homebrew")
 | 
			
		||||
    raise UsageError, "Must set the Archive item!" unless item
 | 
			
		||||
 | 
			
		||||
    @archive_item = item
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def open_api(url, *args, auth: true)
 | 
			
		||||
    if auth
 | 
			
		||||
      key = Homebrew::EnvConfig.internet_archive_key
 | 
			
		||||
      raise UsageError, "HOMEBREW_INTERNET_ARCHIVE_KEY is unset." if key.blank?
 | 
			
		||||
 | 
			
		||||
      if key.exclude?(":")
 | 
			
		||||
        raise UsageError, "Use HOMEBREW_INTERNET_ARCHIVE_KEY=access:secret. See #{URL_PREFIX}/account/s3.php"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      args += ["--header", "Authorization: AWS #{key}"]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    curl(*args, url, print_stdout: false, secrets: key)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig {
 | 
			
		||||
    params(local_file:    String,
 | 
			
		||||
           directory:     String,
 | 
			
		||||
           remote_file:   String,
 | 
			
		||||
           warn_on_error: T.nilable(T::Boolean)).void
 | 
			
		||||
  }
 | 
			
		||||
  def upload(local_file, directory:, remote_file:, warn_on_error: false)
 | 
			
		||||
    local_file = Pathname.new(local_file)
 | 
			
		||||
    unless local_file.exist?
 | 
			
		||||
      msg = "#{local_file} for upload doesn't exist!"
 | 
			
		||||
      raise Error, msg unless warn_on_error
 | 
			
		||||
 | 
			
		||||
      # Warn and return early here since we know this upload is going to fail.
 | 
			
		||||
      opoo msg
 | 
			
		||||
      return
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    md5_base64 = Digest::MD5.base64digest(local_file.read)
 | 
			
		||||
    url = "https://#{@archive_item}.#{S3_DOMAIN}/#{directory}/#{remote_file}"
 | 
			
		||||
    args = ["--upload-file", local_file, "--header", "Content-MD5: #{md5_base64}"]
 | 
			
		||||
    args << "--fail" unless warn_on_error
 | 
			
		||||
    result = T.unsafe(self).open_api(url, *args)
 | 
			
		||||
    return if result.success? && result.stdout.exclude?("Error")
 | 
			
		||||
 | 
			
		||||
    msg = "Bottle upload failed: #{result.stdout}"
 | 
			
		||||
    raise msg unless warn_on_error
 | 
			
		||||
 | 
			
		||||
    opoo msg
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig {
 | 
			
		||||
    params(formula:       Formula,
 | 
			
		||||
           directory:     String,
 | 
			
		||||
           warn_on_error: T::Boolean).returns(String)
 | 
			
		||||
  }
 | 
			
		||||
  def mirror_formula(formula, directory: "mirror", warn_on_error: false)
 | 
			
		||||
    formula.downloader.fetch
 | 
			
		||||
 | 
			
		||||
    filename = ERB::Util.url_encode(formula.downloader.basename)
 | 
			
		||||
    destination_url = "#{URL_PREFIX}/download/#{@archive_item}/#{directory}/#{filename}"
 | 
			
		||||
 | 
			
		||||
    odebug "Uploading to #{destination_url}"
 | 
			
		||||
 | 
			
		||||
    upload(
 | 
			
		||||
      formula.downloader.cached_location,
 | 
			
		||||
      directory:     directory,
 | 
			
		||||
      remote_file:   filename,
 | 
			
		||||
      warn_on_error: warn_on_error,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    destination_url
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Gets the MD5 hash of the specified remote file.
 | 
			
		||||
  #
 | 
			
		||||
  # @return the hash, the empty string (if the file doesn't have a hash), nil (if the file doesn't exist)
 | 
			
		||||
  sig { params(directory: String, remote_file: String).returns(T.nilable(String)) }
 | 
			
		||||
  def remote_md5(directory:, remote_file:)
 | 
			
		||||
    url = "https://#{@archive_item}.#{S3_DOMAIN}/#{directory}/#{remote_file}"
 | 
			
		||||
    result = curl_output "--fail", "--silent", "--head", "--location", url
 | 
			
		||||
    if result.success?
 | 
			
		||||
      result.stdout.match(/^ETag: "(\h{32})"/)&.values_at(1)&.first || ""
 | 
			
		||||
    else
 | 
			
		||||
      raise Error if result.status.exitstatus != 22 && result.stderr.exclude?("404 Not Found")
 | 
			
		||||
 | 
			
		||||
      nil
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(directory: String, filename: String).returns(String) }
 | 
			
		||||
  def file_delete_instructions(directory, filename)
 | 
			
		||||
    <<~EOS
 | 
			
		||||
      Run:
 | 
			
		||||
        curl -X DELETE -H "Authorization: AWS $HOMEBREW_INTERNET_ARCHIVE_KEY" https://#{@archive_item}.#{S3_DOMAIN}/#{directory}/#{filename}
 | 
			
		||||
      Or run:
 | 
			
		||||
        ia delete #{@archive_item} #{directory}/#{filename}
 | 
			
		||||
    EOS
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig {
 | 
			
		||||
    params(bottles_hash:  T::Hash[String, T.untyped],
 | 
			
		||||
           warn_on_error: T.nilable(T::Boolean)).void
 | 
			
		||||
  }
 | 
			
		||||
  def upload_bottles(bottles_hash, warn_on_error: false)
 | 
			
		||||
    bottles_hash.each do |_formula_name, bottle_hash|
 | 
			
		||||
      directory = bottle_hash["bintray"]["repository"]
 | 
			
		||||
      bottle_count = bottle_hash["bottle"]["tags"].length
 | 
			
		||||
 | 
			
		||||
      bottle_hash["bottle"]["tags"].each_value do |tag_hash|
 | 
			
		||||
        filename = tag_hash["filename"] # URL encoded in Bottle::Filename#archive
 | 
			
		||||
        delete_instructions = file_delete_instructions(directory, filename)
 | 
			
		||||
 | 
			
		||||
        local_filename = tag_hash["local_filename"]
 | 
			
		||||
        md5 = Digest::MD5.hexdigest(File.read(local_filename))
 | 
			
		||||
 | 
			
		||||
        odebug "Checking remote file #{@archive_item}/#{directory}/#{filename}"
 | 
			
		||||
        result = remote_md5(directory: directory, remote_file: filename)
 | 
			
		||||
        case result
 | 
			
		||||
        when nil
 | 
			
		||||
          # File doesn't exist.
 | 
			
		||||
          odebug "Uploading #{@archive_item}/#{directory}/#{filename}"
 | 
			
		||||
          upload(local_filename,
 | 
			
		||||
                 directory:     directory,
 | 
			
		||||
                 remote_file:   filename,
 | 
			
		||||
                 warn_on_error: warn_on_error)
 | 
			
		||||
        when md5
 | 
			
		||||
          # File exists, hash matches.
 | 
			
		||||
          odebug "#{filename} is already published with matching hash."
 | 
			
		||||
          bottle_count -= 1
 | 
			
		||||
        when ""
 | 
			
		||||
          # File exists, but can't find hash
 | 
			
		||||
          failed_message = "#{filename} is already published!"
 | 
			
		||||
          raise Error, "#{failed_message}\n#{delete_instructions}" unless warn_on_error
 | 
			
		||||
 | 
			
		||||
          opoo failed_message
 | 
			
		||||
        else
 | 
			
		||||
          # File exists, but hash either doesn't exist or is mismatched.
 | 
			
		||||
          failed_message = <<~EOS
 | 
			
		||||
            #{filename} is already published with a mismatched hash!
 | 
			
		||||
              Expected: #{md5}
 | 
			
		||||
              Actual:   #{result}
 | 
			
		||||
          EOS
 | 
			
		||||
          raise Error, "#{failed_message}#{delete_instructions}" unless warn_on_error
 | 
			
		||||
 | 
			
		||||
          opoo failed_message
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      odebug "Uploaded #{bottle_count} bottles"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -14,6 +14,7 @@ class Bintray
 | 
			
		||||
  include Utils::Curl
 | 
			
		||||
 | 
			
		||||
  API_URL = "https://api.bintray.com"
 | 
			
		||||
  URL_REGEX = %r{^https://[\w-]+\.bintray\.com/}.freeze
 | 
			
		||||
 | 
			
		||||
  class Error < RuntimeError
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,6 @@ HOMEBREW_TEMP="${HOMEBREW_TEMP:-${HOMEBREW_DEFAULT_TEMP}}"
 | 
			
		||||
# Don't need shellcheck to follow these `source`.
 | 
			
		||||
# shellcheck disable=SC1090
 | 
			
		||||
case "$*" in
 | 
			
		||||
  --prefix)            echo "$HOMEBREW_PREFIX"; exit 0 ;;
 | 
			
		||||
  --cellar)            echo "$HOMEBREW_CELLAR"; exit 0 ;;
 | 
			
		||||
  --repository|--repo) echo "$HOMEBREW_REPOSITORY"; exit 0 ;;
 | 
			
		||||
  --caskroom)          echo "$HOMEBREW_PREFIX/Caskroom"; exit 0 ;;
 | 
			
		||||
@ -56,6 +55,8 @@ case "$*" in
 | 
			
		||||
  shellenv)            source "$HOMEBREW_LIBRARY/Homebrew/cmd/shellenv.sh"; homebrew-shellenv; exit 0 ;;
 | 
			
		||||
  formulae)            source "$HOMEBREW_LIBRARY/Homebrew/cmd/formulae.sh"; homebrew-formulae; exit 0 ;;
 | 
			
		||||
  casks)               source "$HOMEBREW_LIBRARY/Homebrew/cmd/casks.sh"; homebrew-casks; exit 0 ;;
 | 
			
		||||
  # falls back to cmd/prefix.rb on a non-zero return
 | 
			
		||||
  --prefix*)           source "$HOMEBREW_LIBRARY/Homebrew/prefix.sh"; homebrew-prefix "$@" && exit 0 ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
#####
 | 
			
		||||
@ -187,9 +188,9 @@ update-preinstall() {
 | 
			
		||||
    # last $HOMEBREW_AUTO_UPDATE_SECS.
 | 
			
		||||
    if [[ "$HOMEBREW_COMMAND" = "cask" ]]
 | 
			
		||||
    then
 | 
			
		||||
      tap_fetch_head="$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-cask/.git/FETCH_HEAD"
 | 
			
		||||
      tap_fetch_head="$HOMEBREW_CASK_REPOSITORY/.git/FETCH_HEAD"
 | 
			
		||||
    else
 | 
			
		||||
      tap_fetch_head="$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-core/.git/FETCH_HEAD"
 | 
			
		||||
      tap_fetch_head="$HOMEBREW_CORE_REPOSITORY/.git/FETCH_HEAD"
 | 
			
		||||
    fi
 | 
			
		||||
    if [[ -f "$tap_fetch_head" &&
 | 
			
		||||
          -n "$(find "$tap_fetch_head" -type f -mtime -"${HOMEBREW_AUTO_UPDATE_SECS}"s 2>/dev/null)" ]]
 | 
			
		||||
@ -313,6 +314,20 @@ then
 | 
			
		||||
  HOMEBREW_USER_AGENT_VERSION="2.X.Y"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
HOMEBREW_CASK_REPOSITORY="$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-cask"
 | 
			
		||||
HOMEBREW_CORE_REPOSITORY="$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-core"
 | 
			
		||||
 | 
			
		||||
# Don't need shellcheck to follow these `source`.
 | 
			
		||||
# shellcheck disable=SC1090
 | 
			
		||||
case "$*" in
 | 
			
		||||
  --version|-v) source "$HOMEBREW_LIBRARY/Homebrew/cmd/--version.sh"; homebrew-version; exit 0 ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
if [[ -n "$HOMEBREW_SIMULATE_MACOS_ON_LINUX" ]]
 | 
			
		||||
then
 | 
			
		||||
  export HOMEBREW_FORCE_HOMEBREW_ON_LINUX="1"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ -n "$HOMEBREW_MACOS" ]]
 | 
			
		||||
then
 | 
			
		||||
  HOMEBREW_PRODUCT="Homebrew"
 | 
			
		||||
@ -439,13 +454,6 @@ curl_version_output="$("$HOMEBREW_CURL" --version 2>/dev/null)"
 | 
			
		||||
curl_name_and_version="${curl_version_output%% (*}"
 | 
			
		||||
HOMEBREW_USER_AGENT_CURL="$HOMEBREW_USER_AGENT ${curl_name_and_version// //}"
 | 
			
		||||
 | 
			
		||||
# Declared in bin/brew
 | 
			
		||||
export HOMEBREW_BREW_FILE
 | 
			
		||||
export HOMEBREW_PREFIX
 | 
			
		||||
export HOMEBREW_REPOSITORY
 | 
			
		||||
export HOMEBREW_LIBRARY
 | 
			
		||||
 | 
			
		||||
# Declared in brew.sh
 | 
			
		||||
export HOMEBREW_VERSION
 | 
			
		||||
export HOMEBREW_DEFAULT_CACHE
 | 
			
		||||
export HOMEBREW_CACHE
 | 
			
		||||
@ -567,16 +575,16 @@ then
 | 
			
		||||
  # Don't allow non-developers to customise Ruby warnings.
 | 
			
		||||
  unset HOMEBREW_RUBY_WARNINGS
 | 
			
		||||
 | 
			
		||||
  # Disable Ruby options we don't need. RubyGems provides a decent speedup.
 | 
			
		||||
  RUBY_DISABLE_OPTIONS="--disable=gems,did_you_mean,rubyopt"
 | 
			
		||||
  # Disable Ruby options we don't need.
 | 
			
		||||
  RUBY_DISABLE_OPTIONS="--disable=did_you_mean,rubyopt"
 | 
			
		||||
else
 | 
			
		||||
  # Don't disable did_you_mean for developers as it's useful.
 | 
			
		||||
  RUBY_DISABLE_OPTIONS="--disable=gems,rubyopt"
 | 
			
		||||
  RUBY_DISABLE_OPTIONS="--disable=rubyopt"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ -z "$HOMEBREW_RUBY_WARNINGS" ]]
 | 
			
		||||
then
 | 
			
		||||
  export HOMEBREW_RUBY_WARNINGS="-W0"
 | 
			
		||||
  export HOMEBREW_RUBY_WARNINGS="-W1"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ -z "$HOMEBREW_BOTTLE_DOMAIN" ]]
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
old_trap = trap("INT") { exit! 130 }
 | 
			
		||||
 | 
			
		||||
require "global"
 | 
			
		||||
require_relative "global"
 | 
			
		||||
require "build_options"
 | 
			
		||||
require "cxxstdlib"
 | 
			
		||||
require "keg"
 | 
			
		||||
@ -240,7 +240,14 @@ rescue Exception => e # rubocop:disable Lint/RescueException
 | 
			
		||||
    error_hash["env"] = e.env
 | 
			
		||||
  when "ErrorDuringExecution"
 | 
			
		||||
    error_hash["cmd"] = e.cmd
 | 
			
		||||
    error_hash["status"] = e.status.exitstatus
 | 
			
		||||
    error_hash["status"] = if e.status.is_a?(Process::Status)
 | 
			
		||||
      {
 | 
			
		||||
        exitstatus: e.status.exitstatus,
 | 
			
		||||
        termsig:    e.status.termsig,
 | 
			
		||||
      }
 | 
			
		||||
    else
 | 
			
		||||
      e.status
 | 
			
		||||
    end
 | 
			
		||||
    error_hash["output"] = e.output
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,9 +13,9 @@ module Cask
 | 
			
		||||
    # @api private
 | 
			
		||||
    class Installer < AbstractArtifact
 | 
			
		||||
      VALID_KEYS = Set.new([
 | 
			
		||||
                             :manual,
 | 
			
		||||
                             :script,
 | 
			
		||||
                           ]).freeze
 | 
			
		||||
        :manual,
 | 
			
		||||
        :script,
 | 
			
		||||
      ]).freeze
 | 
			
		||||
 | 
			
		||||
      # Extension module for manual installers.
 | 
			
		||||
      module ManualInstaller
 | 
			
		||||
 | 
			
		||||
@ -165,7 +165,7 @@ module Cask
 | 
			
		||||
      odebug "Auditing stanzas which require an uninstall"
 | 
			
		||||
 | 
			
		||||
      return if cask.artifacts.none? { |k| k.is_a?(Artifact::Pkg) || k.is_a?(Artifact::Installer) }
 | 
			
		||||
      return if cask.artifacts.any? { |k| k.is_a?(Artifact::Uninstall) }
 | 
			
		||||
      return if cask.artifacts.any?(Artifact::Uninstall)
 | 
			
		||||
 | 
			
		||||
      add_error "installer and pkg stanzas require an uninstall stanza"
 | 
			
		||||
    end
 | 
			
		||||
@ -696,7 +696,7 @@ module Cask
 | 
			
		||||
    def check_denylist
 | 
			
		||||
      return unless cask.tap
 | 
			
		||||
      return unless cask.tap.official?
 | 
			
		||||
      return unless reason = Denylist.reason(cask.token)
 | 
			
		||||
      return unless (reason = Denylist.reason(cask.token))
 | 
			
		||||
 | 
			
		||||
      add_error "#{cask.token} is not allowed: #{reason}"
 | 
			
		||||
    end
 | 
			
		||||
@ -717,7 +717,12 @@ module Cask
 | 
			
		||||
 | 
			
		||||
      check_url_for_https_availability(cask.appcast, check_content: true) if cask.appcast && appcast?
 | 
			
		||||
 | 
			
		||||
      check_url_for_https_availability(cask.homepage, check_content: true, user_agents: [:browser]) if cask.homepage
 | 
			
		||||
      return unless cask.homepage
 | 
			
		||||
 | 
			
		||||
      check_url_for_https_availability(cask.homepage,
 | 
			
		||||
                                       user_agents:   [:browser, :default],
 | 
			
		||||
                                       check_content: true,
 | 
			
		||||
                                       strict:        strict?)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def check_url_for_https_availability(url_to_check, **options)
 | 
			
		||||
 | 
			
		||||
@ -162,7 +162,7 @@ module Cask
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def eql?(other)
 | 
			
		||||
      token == other.token
 | 
			
		||||
      instance_of?(other.class) && token == other.token
 | 
			
		||||
    end
 | 
			
		||||
    alias == eql?
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -157,7 +157,7 @@ module Cask
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def load(config:)
 | 
			
		||||
        tap.install unless tap.installed?
 | 
			
		||||
        raise TapCaskUnavailableError.new(tap, token) unless tap.installed?
 | 
			
		||||
 | 
			
		||||
        super
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -39,9 +39,9 @@ module Cask
 | 
			
		||||
      Pathname.glob(path.join("*")).sort.select(&:directory?).map do |path|
 | 
			
		||||
        token = path.basename.to_s
 | 
			
		||||
 | 
			
		||||
        if tap_path = CaskLoader.tap_paths(token).first
 | 
			
		||||
        if (tap_path = CaskLoader.tap_paths(token).first)
 | 
			
		||||
          CaskLoader::FromTapPathLoader.new(tap_path).load(config: config)
 | 
			
		||||
        elsif caskroom_path = Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first
 | 
			
		||||
        elsif (caskroom_path = Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first)
 | 
			
		||||
          CaskLoader::FromPathLoader.new(caskroom_path).load(config: config)
 | 
			
		||||
        else
 | 
			
		||||
          CaskLoader.load(token, config: config)
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,10 @@ module Cask
 | 
			
		||||
      @quarantine = quarantine
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def fetch(verify_download_integrity: true)
 | 
			
		||||
    def fetch(quiet: nil, verify_download_integrity: true, timeout: nil)
 | 
			
		||||
      downloaded_path = begin
 | 
			
		||||
        downloader.fetch
 | 
			
		||||
        downloader.shutup! if quiet
 | 
			
		||||
        downloader.fetch(timeout: timeout)
 | 
			
		||||
        downloader.cached_location
 | 
			
		||||
      rescue => e
 | 
			
		||||
        error = CaskError.new("Download failed on Cask '#{cask}' with message: #{e}")
 | 
			
		||||
@ -40,8 +41,8 @@ module Cask
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def time_file_size
 | 
			
		||||
      downloader.resolved_time_file_size
 | 
			
		||||
    def time_file_size(timeout: nil)
 | 
			
		||||
      downloader.resolved_time_file_size(timeout: timeout)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def clear_cache
 | 
			
		||||
 | 
			
		||||
@ -64,30 +64,30 @@ module Cask
 | 
			
		||||
    ].freeze
 | 
			
		||||
 | 
			
		||||
    DSL_METHODS = Set.new([
 | 
			
		||||
                            :appcast,
 | 
			
		||||
                            :artifacts,
 | 
			
		||||
                            :auto_updates,
 | 
			
		||||
                            :caveats,
 | 
			
		||||
                            :conflicts_with,
 | 
			
		||||
                            :container,
 | 
			
		||||
                            :desc,
 | 
			
		||||
                            :depends_on,
 | 
			
		||||
                            :homepage,
 | 
			
		||||
                            :language,
 | 
			
		||||
                            :languages,
 | 
			
		||||
                            :name,
 | 
			
		||||
                            :sha256,
 | 
			
		||||
                            :staged_path,
 | 
			
		||||
                            :url,
 | 
			
		||||
                            :version,
 | 
			
		||||
                            :appdir,
 | 
			
		||||
                            :discontinued?,
 | 
			
		||||
                            :livecheck,
 | 
			
		||||
                            :livecheckable?,
 | 
			
		||||
                            *ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
 | 
			
		||||
                            *ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key),
 | 
			
		||||
                            *ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
 | 
			
		||||
                          ]).freeze
 | 
			
		||||
      :appcast,
 | 
			
		||||
      :artifacts,
 | 
			
		||||
      :auto_updates,
 | 
			
		||||
      :caveats,
 | 
			
		||||
      :conflicts_with,
 | 
			
		||||
      :container,
 | 
			
		||||
      :desc,
 | 
			
		||||
      :depends_on,
 | 
			
		||||
      :homepage,
 | 
			
		||||
      :language,
 | 
			
		||||
      :languages,
 | 
			
		||||
      :name,
 | 
			
		||||
      :sha256,
 | 
			
		||||
      :staged_path,
 | 
			
		||||
      :url,
 | 
			
		||||
      :version,
 | 
			
		||||
      :appdir,
 | 
			
		||||
      :discontinued?,
 | 
			
		||||
      :livecheck,
 | 
			
		||||
      :livecheckable?,
 | 
			
		||||
      *ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
 | 
			
		||||
      *ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key),
 | 
			
		||||
      *ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
 | 
			
		||||
    ]).freeze
 | 
			
		||||
 | 
			
		||||
    attr_reader :cask, :token
 | 
			
		||||
 | 
			
		||||
@ -181,7 +181,11 @@ module Cask
 | 
			
		||||
 | 
			
		||||
      set_unique_stanza(:url, args.empty? && options.empty? && !block_given?) do
 | 
			
		||||
        if block_given?
 | 
			
		||||
          LazyObject.new { URL.new(*yield, from_block: true, caller_location: caller_location) }
 | 
			
		||||
          LazyObject.new do
 | 
			
		||||
            *args = yield
 | 
			
		||||
            options = args.last.is_a?(Hash) ? args.pop : {}
 | 
			
		||||
            URL.new(*args, **options, from_block: true, caller_location: caller_location)
 | 
			
		||||
          end
 | 
			
		||||
        else
 | 
			
		||||
          URL.new(*args, **options, caller_location: caller_location)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -10,9 +10,9 @@ module Cask
 | 
			
		||||
    # @api private
 | 
			
		||||
    class Container
 | 
			
		||||
      VALID_KEYS = Set.new([
 | 
			
		||||
                             :type,
 | 
			
		||||
                             :nested,
 | 
			
		||||
                           ]).freeze
 | 
			
		||||
        :type,
 | 
			
		||||
        :nested,
 | 
			
		||||
      ]).freeze
 | 
			
		||||
 | 
			
		||||
      attr_accessor(*VALID_KEYS, :pairs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,13 +12,13 @@ module Cask
 | 
			
		||||
    # @api private
 | 
			
		||||
    class DependsOn < SimpleDelegator
 | 
			
		||||
      VALID_KEYS = Set.new([
 | 
			
		||||
                             :formula,
 | 
			
		||||
                             :cask,
 | 
			
		||||
                             :macos,
 | 
			
		||||
                             :arch,
 | 
			
		||||
                             :x11,
 | 
			
		||||
                             :java,
 | 
			
		||||
                           ]).freeze
 | 
			
		||||
        :formula,
 | 
			
		||||
        :cask,
 | 
			
		||||
        :macos,
 | 
			
		||||
        :arch,
 | 
			
		||||
        :x11,
 | 
			
		||||
        :java,
 | 
			
		||||
      ]).freeze
 | 
			
		||||
 | 
			
		||||
      VALID_ARCHES = {
 | 
			
		||||
        intel:  { type: :intel, bits: 64 },
 | 
			
		||||
 | 
			
		||||
@ -103,6 +103,27 @@ module Cask
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Error when a cask in a specific tap is not available.
 | 
			
		||||
  #
 | 
			
		||||
  # @api private
 | 
			
		||||
  class TapCaskUnavailableError < CaskUnavailableError
 | 
			
		||||
    extend T::Sig
 | 
			
		||||
 | 
			
		||||
    attr_reader :tap
 | 
			
		||||
 | 
			
		||||
    def initialize(tap, token)
 | 
			
		||||
      super("#{tap}/#{token}")
 | 
			
		||||
      @tap = tap
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(String) }
 | 
			
		||||
    def to_s
 | 
			
		||||
      s = super
 | 
			
		||||
      s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed?
 | 
			
		||||
      s
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Error when a cask already exists.
 | 
			
		||||
  #
 | 
			
		||||
  # @api private
 | 
			
		||||
 | 
			
		||||
@ -62,13 +62,15 @@ module Cask
 | 
			
		||||
      EOS
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def fetch
 | 
			
		||||
    sig { params(quiet: T.nilable(T::Boolean), timeout: T.nilable(T.any(Integer, Float))).void }
 | 
			
		||||
    def fetch(quiet: nil, timeout: nil)
 | 
			
		||||
      odebug "Cask::Installer#fetch"
 | 
			
		||||
 | 
			
		||||
      verify_has_sha if require_sha? && !force?
 | 
			
		||||
      satisfy_dependencies
 | 
			
		||||
 | 
			
		||||
      download
 | 
			
		||||
      download(quiet: quiet, timeout: timeout)
 | 
			
		||||
 | 
			
		||||
      satisfy_dependencies
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def stage
 | 
			
		||||
@ -146,7 +148,7 @@ module Cask
 | 
			
		||||
      installed_cask = installed_caskfile.exist? ? CaskLoader.load(installed_caskfile) : @cask
 | 
			
		||||
 | 
			
		||||
      # Always force uninstallation, ignore method parameter
 | 
			
		||||
      Installer.new(installed_cask, binaries: binaries?, verbose: verbose?, force: true, upgrade: upgrade?).uninstall
 | 
			
		||||
      Installer.new(installed_cask, verbose: verbose?, force: true, upgrade: upgrade?).uninstall
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(String) }
 | 
			
		||||
@ -162,9 +164,10 @@ module Cask
 | 
			
		||||
      @downloader ||= Download.new(@cask, quarantine: quarantine?)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(Pathname) }
 | 
			
		||||
    def download
 | 
			
		||||
      @download ||= downloader.fetch(verify_download_integrity: @verify_download_integrity)
 | 
			
		||||
    sig { params(quiet: T.nilable(T::Boolean), timeout: T.nilable(T.any(Integer, Float))).returns(Pathname) }
 | 
			
		||||
    def download(quiet: nil, timeout: nil)
 | 
			
		||||
      @download ||= downloader.fetch(quiet: quiet, verify_download_integrity: @verify_download_integrity,
 | 
			
		||||
timeout: timeout)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def verify_has_sha
 | 
			
		||||
@ -179,7 +182,7 @@ module Cask
 | 
			
		||||
 | 
			
		||||
    def primary_container
 | 
			
		||||
      @primary_container ||= begin
 | 
			
		||||
        downloaded_path = download
 | 
			
		||||
        downloaded_path = download(quiet: true)
 | 
			
		||||
        UnpackStrategy.detect(downloaded_path, type: @cask.container&.type, merge_xattrs: true)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -191,7 +194,7 @@ module Cask
 | 
			
		||||
 | 
			
		||||
      basename = downloader.basename
 | 
			
		||||
 | 
			
		||||
      if nested_container = @cask.container&.nested
 | 
			
		||||
      if (nested_container = @cask.container&.nested)
 | 
			
		||||
        Dir.mktmpdir do |tmpdir|
 | 
			
		||||
          tmpdir = Pathname(tmpdir)
 | 
			
		||||
          primary_container.extract(to: tmpdir, basename: basename, verbose: verbose?)
 | 
			
		||||
 | 
			
		||||
@ -32,9 +32,7 @@ module Cask
 | 
			
		||||
        odebug "Deleting pkg files"
 | 
			
		||||
        @command.run!(
 | 
			
		||||
          "/usr/bin/xargs",
 | 
			
		||||
          args:  [
 | 
			
		||||
            "-0", "--", "/bin/rm", "--"
 | 
			
		||||
          ],
 | 
			
		||||
          args:  ["-0", "--", "/bin/rm", "--"],
 | 
			
		||||
          input: pkgutil_bom_files.join("\0"),
 | 
			
		||||
          sudo:  true,
 | 
			
		||||
        )
 | 
			
		||||
@ -44,9 +42,7 @@ module Cask
 | 
			
		||||
        odebug "Deleting pkg symlinks and special files"
 | 
			
		||||
        @command.run!(
 | 
			
		||||
          "/usr/bin/xargs",
 | 
			
		||||
          args:  [
 | 
			
		||||
            "-0", "--", "/bin/rm", "--"
 | 
			
		||||
          ],
 | 
			
		||||
          args:  ["-0", "--", "/bin/rm", "--"],
 | 
			
		||||
          input: pkgutil_bom_specials.join("\0"),
 | 
			
		||||
          sudo:  true,
 | 
			
		||||
        )
 | 
			
		||||
@ -54,19 +50,10 @@ module Cask
 | 
			
		||||
 | 
			
		||||
      unless pkgutil_bom_dirs.empty?
 | 
			
		||||
        odebug "Deleting pkg directories"
 | 
			
		||||
        deepest_path_first(pkgutil_bom_dirs).each do |dir|
 | 
			
		||||
          with_full_permissions(dir) do
 | 
			
		||||
            clean_broken_symlinks(dir)
 | 
			
		||||
            clean_ds_store(dir)
 | 
			
		||||
            rmdir(dir)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
        rmdir(deepest_path_first(pkgutil_bom_dirs))
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if root.directory? && !MacOS.undeletable?(root)
 | 
			
		||||
        clean_ds_store(root)
 | 
			
		||||
        rmdir(root)
 | 
			
		||||
      end
 | 
			
		||||
      rmdir(root) unless MacOS.undeletable?(root)
 | 
			
		||||
 | 
			
		||||
      forget
 | 
			
		||||
    end
 | 
			
		||||
@ -118,55 +105,51 @@ module Cask
 | 
			
		||||
      path.symlink? || path.chardev? || path.blockdev?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(path: Pathname).void }
 | 
			
		||||
    # Helper script to delete empty directories after deleting `.DS_Store` files and broken symlinks.
 | 
			
		||||
    # Needed in order to execute all file operations with `sudo`.
 | 
			
		||||
    RMDIR_SH = <<~'BASH'
 | 
			
		||||
      set -euo pipefail
 | 
			
		||||
 | 
			
		||||
      for path in "${@}"; do
 | 
			
		||||
        if [[ ! -e "${path}" ]]; then
 | 
			
		||||
          continue
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        if [[ -e "${path}/.DS_Store" ]]; then
 | 
			
		||||
          /bin/rm -f "${path}/.DS_Store"
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        # Some packages leave broken symlinks around; we clean them out before
 | 
			
		||||
        # attempting to `rmdir` to prevent extra cruft from accumulating.
 | 
			
		||||
        /usr/bin/find "${path}" -mindepth 1 -maxdepth 1 -type l ! -exec /bin/test -e {} \; -delete
 | 
			
		||||
 | 
			
		||||
        if [[ -L "${path}" ]]; then
 | 
			
		||||
          # Delete directory symlink.
 | 
			
		||||
          /bin/rm "${path}"
 | 
			
		||||
        elif [[ -d "${path}" ]]; then
 | 
			
		||||
          # Delete directory if empty.
 | 
			
		||||
          /usr/bin/find "${path}" -maxdepth 0 -type d -empty -exec /bin/rmdir {} \;
 | 
			
		||||
        else
 | 
			
		||||
          # Try `rmdir` anyways to show a proper error.
 | 
			
		||||
          /bin/rmdir "${path}"
 | 
			
		||||
        fi
 | 
			
		||||
      done
 | 
			
		||||
    BASH
 | 
			
		||||
    private_constant :RMDIR_SH
 | 
			
		||||
 | 
			
		||||
    sig { params(path: T.any(Pathname, T::Array[Pathname])).void }
 | 
			
		||||
    def rmdir(path)
 | 
			
		||||
      return unless path.children.empty?
 | 
			
		||||
 | 
			
		||||
      if path.symlink?
 | 
			
		||||
        @command.run!("/bin/rm", args: ["-f", "--", path], sudo: true)
 | 
			
		||||
      else
 | 
			
		||||
        @command.run!("/bin/rmdir", args: ["--", path], sudo: true)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(path: Pathname, _block: T.proc.void).void }
 | 
			
		||||
    def with_full_permissions(path, &_block)
 | 
			
		||||
      original_mode = (path.stat.mode % 01000).to_s(8)
 | 
			
		||||
      original_flags = @command.run!("/usr/bin/stat", args: ["-f", "%Of", "--", path]).stdout.chomp
 | 
			
		||||
 | 
			
		||||
      @command.run!("/bin/chmod", args: ["--", "777", path], sudo: true)
 | 
			
		||||
      yield
 | 
			
		||||
    ensure
 | 
			
		||||
      if path.exist? # block may have removed dir
 | 
			
		||||
        @command.run!("/bin/chmod", args: ["--", original_mode, path], sudo: true)
 | 
			
		||||
        @command.run!("/usr/bin/chflags", args: ["--", original_flags, path], sudo: true)
 | 
			
		||||
      end
 | 
			
		||||
      @command.run!(
 | 
			
		||||
        "/usr/bin/xargs",
 | 
			
		||||
        args:  ["-0", "--", "/bin/bash", "-c", RMDIR_SH, "--"],
 | 
			
		||||
        input: Array(path).join("\0"),
 | 
			
		||||
        sudo:  true,
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(paths: T::Array[Pathname]).returns(T::Array[Pathname]) }
 | 
			
		||||
    def deepest_path_first(paths)
 | 
			
		||||
      paths.sort_by { |path| -path.to_s.split(File::SEPARATOR).count }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(dir: Pathname).void }
 | 
			
		||||
    def clean_ds_store(dir)
 | 
			
		||||
      return unless (ds_store = dir.join(".DS_Store")).exist?
 | 
			
		||||
 | 
			
		||||
      @command.run!("/bin/rm", args: ["--", ds_store], sudo: true)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Some packages leave broken symlinks around; we clean them out before
 | 
			
		||||
    # attempting to `rmdir` to prevent extra cruft from accumulating.
 | 
			
		||||
    sig { params(dir: Pathname).void }
 | 
			
		||||
    def clean_broken_symlinks(dir)
 | 
			
		||||
      dir.children.select(&method(:broken_symlink?)).each do |path|
 | 
			
		||||
        @command.run!("/bin/rm", args: ["--", path], sudo: true)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { params(path: Pathname).returns(T::Boolean) }
 | 
			
		||||
    def broken_symlink?(path)
 | 
			
		||||
      path.symlink? && !path.exist?
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
 | 
			
		||||
require "utils/bottles"
 | 
			
		||||
 | 
			
		||||
require "utils/gems"
 | 
			
		||||
require "formula"
 | 
			
		||||
require "cask/cask_loader"
 | 
			
		||||
require "set"
 | 
			
		||||
@ -80,7 +79,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
          version = Version.new(version)
 | 
			
		||||
 | 
			
		||||
          return false unless formula_name = basename.to_s[/\A(.*?)(?:--.*?)*--?(?:#{Regexp.escape(version)})/, 1]
 | 
			
		||||
          return false unless (formula_name = basename.to_s[/\A(.*?)(?:--.*?)*--?(?:#{Regexp.escape(version)})/, 1])
 | 
			
		||||
 | 
			
		||||
          formula = begin
 | 
			
		||||
            Formulary.from_rack(HOMEBREW_CELLAR/formula_name)
 | 
			
		||||
@ -95,7 +94,7 @@ module Homebrew
 | 
			
		||||
          if resource_name == "patch"
 | 
			
		||||
            patch_hashes = formula.stable&.patches&.select(&:external?)&.map(&:resource)&.map(&:version)
 | 
			
		||||
            return true unless patch_hashes&.include?(Checksum.new(version.to_s))
 | 
			
		||||
          elsif resource_name && resource_version = formula.stable&.resources&.dig(resource_name)&.version
 | 
			
		||||
          elsif resource_name && (resource_version = formula.stable&.resources&.dig(resource_name)&.version)
 | 
			
		||||
            return true if resource_version != version
 | 
			
		||||
          elsif version.is_a?(PkgVersion)
 | 
			
		||||
            return true if formula.pkg_version > version
 | 
			
		||||
@ -111,7 +110,7 @@ module Homebrew
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def stale_cask?(scrub)
 | 
			
		||||
          return false unless name = basename.to_s[/\A(.*?)--/, 1]
 | 
			
		||||
          return false unless (name = basename.to_s[/\A(.*?)--/, 1])
 | 
			
		||||
 | 
			
		||||
          cask = begin
 | 
			
		||||
            Cask::CaskLoader.load(name)
 | 
			
		||||
 | 
			
		||||
@ -132,6 +132,12 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
        raise unreadable_error if unreadable_error.present?
 | 
			
		||||
 | 
			
		||||
        user, repo, short_name = name.downcase.split("/", 3)
 | 
			
		||||
        if repo.present? && short_name.present?
 | 
			
		||||
          tap = Tap.fetch(user, repo)
 | 
			
		||||
          raise TapFormulaOrCaskUnavailableError.new(tap, short_name)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        raise FormulaOrCaskUnavailableError, name
 | 
			
		||||
      end
 | 
			
		||||
      private :load_formula_or_cask
 | 
			
		||||
@ -265,28 +271,24 @@ module Homebrew
 | 
			
		||||
        opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
 | 
			
		||||
 | 
			
		||||
        begin
 | 
			
		||||
          if opt_prefix.symlink? && opt_prefix.directory?
 | 
			
		||||
            Keg.new(opt_prefix.resolved_path)
 | 
			
		||||
          elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
 | 
			
		||||
            Keg.new(linked_keg_ref.resolved_path)
 | 
			
		||||
          elsif dirs.length == 1
 | 
			
		||||
            Keg.new(dirs.first)
 | 
			
		||||
          return Keg.new(opt_prefix.resolved_path) if opt_prefix.symlink? && opt_prefix.directory?
 | 
			
		||||
          return Keg.new(linked_keg_ref.resolved_path) if linked_keg_ref.symlink? && linked_keg_ref.directory?
 | 
			
		||||
          return Keg.new(dirs.first) if dirs.length == 1
 | 
			
		||||
 | 
			
		||||
          f = if name.include?("/") || File.exist?(name)
 | 
			
		||||
            Formulary.factory(name)
 | 
			
		||||
          else
 | 
			
		||||
            f = if name.include?("/") || File.exist?(name)
 | 
			
		||||
              Formulary.factory(name)
 | 
			
		||||
            else
 | 
			
		||||
              Formulary.from_rack(rack)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            unless (prefix = f.latest_installed_prefix).directory?
 | 
			
		||||
              raise MultipleVersionsInstalledError, <<~EOS
 | 
			
		||||
                #{rack.basename} has multiple installed versions
 | 
			
		||||
                Run `brew uninstall --force #{rack.basename}` to remove all versions.
 | 
			
		||||
              EOS
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            Keg.new(prefix)
 | 
			
		||||
            Formulary.from_rack(rack)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          unless (prefix = f.latest_installed_prefix).directory?
 | 
			
		||||
            raise MultipleVersionsInstalledError, <<~EOS
 | 
			
		||||
              #{rack.basename} has multiple installed versions
 | 
			
		||||
              Run `brew uninstall --force #{rack.basename}` to remove all versions.
 | 
			
		||||
            EOS
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          Keg.new(prefix)
 | 
			
		||||
        rescue FormulaUnavailableError
 | 
			
		||||
          raise MultipleVersionsInstalledError, <<~EOS
 | 
			
		||||
            Multiple kegs installed to #{rack}
 | 
			
		||||
 | 
			
		||||
@ -474,7 +474,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
              "<#{type}>"
 | 
			
		||||
            end.compact
 | 
			
		||||
            types << "<subcommand>" if @named_args_type.any? { |type| type.is_a? String }
 | 
			
		||||
            types << "<subcommand>" if @named_args_type.any?(String)
 | 
			
		||||
            types.join("|")
 | 
			
		||||
          elsif SYMBOL_TO_USAGE_MAPPING.key? @named_args_type
 | 
			
		||||
            SYMBOL_TO_USAGE_MAPPING[@named_args_type]
 | 
			
		||||
@ -623,7 +623,7 @@ module Homebrew
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def split_non_options(argv)
 | 
			
		||||
        if sep = argv.index("--")
 | 
			
		||||
        if (sep = argv.index("--"))
 | 
			
		||||
          [argv.take(sep), argv.drop(sep + 1)]
 | 
			
		||||
        else
 | 
			
		||||
          [argv, []]
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,7 @@ module Homebrew
 | 
			
		||||
          - macOS ARM: `#{HOMEBREW_MACOS_ARM_DEFAULT_PREFIX}`
 | 
			
		||||
          - Linux: `#{HOMEBREW_LINUX_DEFAULT_PREFIX}`
 | 
			
		||||
 | 
			
		||||
        If <formula> is provided, display the location in the Cellar where <formula>
 | 
			
		||||
        is or would be installed.
 | 
			
		||||
        If <formula> is provided, display the location where <formula> is or would be installed.
 | 
			
		||||
      EOS
 | 
			
		||||
      switch "--unbrewed",
 | 
			
		||||
             description: "List files in Homebrew's prefix not installed by Homebrew."
 | 
			
		||||
@ -45,13 +44,10 @@ module Homebrew
 | 
			
		||||
    else
 | 
			
		||||
      formulae = args.named.to_resolved_formulae
 | 
			
		||||
      prefixes = formulae.map do |f|
 | 
			
		||||
        if f.opt_prefix.exist?
 | 
			
		||||
          f.opt_prefix
 | 
			
		||||
        elsif args.installed?
 | 
			
		||||
          nil
 | 
			
		||||
        else
 | 
			
		||||
          f.latest_installed_prefix
 | 
			
		||||
        end
 | 
			
		||||
        next nil if args.installed? && !f.opt_prefix.exist?
 | 
			
		||||
 | 
			
		||||
        # this case wil be short-circuited by brew.sh logic for a single formula
 | 
			
		||||
        f.opt_prefix
 | 
			
		||||
      end.compact
 | 
			
		||||
      puts prefixes
 | 
			
		||||
      if args.installed?
 | 
			
		||||
 | 
			
		||||
@ -1,30 +0,0 @@
 | 
			
		||||
# typed: true
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "cli/parser"
 | 
			
		||||
 | 
			
		||||
module Homebrew
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
 | 
			
		||||
  module_function
 | 
			
		||||
 | 
			
		||||
  sig { returns(CLI::Parser) }
 | 
			
		||||
  def __version_args
 | 
			
		||||
    Homebrew::CLI::Parser.new do
 | 
			
		||||
      description <<~EOS
 | 
			
		||||
        Print the version numbers of Homebrew, Homebrew/homebrew-core and Homebrew/homebrew-cask
 | 
			
		||||
        (if tapped) to standard output.
 | 
			
		||||
      EOS
 | 
			
		||||
 | 
			
		||||
      named_args :none
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def __version
 | 
			
		||||
    __version_args.parse
 | 
			
		||||
 | 
			
		||||
    puts "Homebrew #{HOMEBREW_VERSION}"
 | 
			
		||||
    puts "#{CoreTap.instance.full_name} #{CoreTap.instance.version_string}"
 | 
			
		||||
    puts "#{Tap.default_cask_tap.full_name} #{Tap.default_cask_tap.version_string}" if Tap.default_cask_tap.installed?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										31
									
								
								Library/Homebrew/cmd/--version.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Library/Homebrew/cmd/--version.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
#:  * `--version`, `-v`
 | 
			
		||||
#:
 | 
			
		||||
#:  Print the version numbers of Homebrew, Homebrew/homebrew-core and Homebrew/homebrew-cask (if tapped) to standard output.
 | 
			
		||||
 | 
			
		||||
version_string() {
 | 
			
		||||
  local repo="$1"
 | 
			
		||||
  if ! [ -d "$repo" ]; then
 | 
			
		||||
    echo "N/A"
 | 
			
		||||
    return
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  local pretty_revision
 | 
			
		||||
  pretty_revision="$(git -C "$repo" rev-parse --short --verify --quiet HEAD)"
 | 
			
		||||
  if [ -z "$pretty_revision" ]; then
 | 
			
		||||
    echo "(no Git repository)"
 | 
			
		||||
    return
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  local git_last_commit_date
 | 
			
		||||
  git_last_commit_date=$(git -C "$repo" show -s --format='%cd' --date=short HEAD)
 | 
			
		||||
  echo "(git revision ${pretty_revision}; last commit ${git_last_commit_date})"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
homebrew-version() {
 | 
			
		||||
  echo "Homebrew $HOMEBREW_VERSION"
 | 
			
		||||
  echo "Homebrew/homebrew-core $(version_string "$HOMEBREW_CORE_REPOSITORY")"
 | 
			
		||||
 | 
			
		||||
  if [ -d "$HOMEBREW_CASK_REPOSITORY" ]; then
 | 
			
		||||
    echo "Homebrew/homebrew-cask $(version_string "$HOMEBREW_CASK_REPOSITORY")"
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
@ -55,7 +55,7 @@ module Homebrew
 | 
			
		||||
      files["00.tap.out"] = { content: tap }
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    odie "`brew gist-logs` requires HOMEBREW_GITHUB_API_TOKEN to be set!" if GitHub.api_credentials_type == :none
 | 
			
		||||
    odie "`brew gist-logs` requires HOMEBREW_GITHUB_API_TOKEN to be set!" if GitHub::API.credentials_type == :none
 | 
			
		||||
 | 
			
		||||
    # Description formatted to work well as page title when viewing gist
 | 
			
		||||
    descr = if f.core_formula?
 | 
			
		||||
@ -63,9 +63,9 @@ module Homebrew
 | 
			
		||||
    else
 | 
			
		||||
      "#{f.name} (#{f.full_name}) on #{OS_VERSION} - Homebrew build logs"
 | 
			
		||||
    end
 | 
			
		||||
    url = create_gist(files, descr, private: args.private?)
 | 
			
		||||
    url = GitHub.create_gist(files, descr, private: args.private?)
 | 
			
		||||
 | 
			
		||||
    url = create_issue(f.tap, "#{f.name} failed to build on #{MacOS.full_version}", url) if args.new_issue?
 | 
			
		||||
    url = GitHub.create_issue(f.tap, "#{f.name} failed to build on #{MacOS.full_version}", url) if args.new_issue?
 | 
			
		||||
 | 
			
		||||
    puts url if url
 | 
			
		||||
  end
 | 
			
		||||
@ -85,9 +85,9 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
  # Causes some terminals to display secure password entry indicators.
 | 
			
		||||
  def noecho_gets
 | 
			
		||||
    system "stty -echo"
 | 
			
		||||
    system "stty", "-echo"
 | 
			
		||||
    result = $stdin.gets
 | 
			
		||||
    system "stty echo"
 | 
			
		||||
    system "stty", "echo"
 | 
			
		||||
    puts
 | 
			
		||||
    result
 | 
			
		||||
  end
 | 
			
		||||
@ -108,20 +108,6 @@ module Homebrew
 | 
			
		||||
    logs
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create_gist(files, description, private:)
 | 
			
		||||
    url = "https://api.github.com/gists"
 | 
			
		||||
    data = { "public" => !private, "files" => files, "description" => description }
 | 
			
		||||
    scopes = GitHub::CREATE_GIST_SCOPES
 | 
			
		||||
    GitHub.open_api(url, data: data, scopes: scopes)["html_url"]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create_issue(repo, title, body)
 | 
			
		||||
    url = "https://api.github.com/repos/#{repo}/issues"
 | 
			
		||||
    data = { "title" => title, "body" => body }
 | 
			
		||||
    scopes = GitHub::CREATE_ISSUE_FORK_OR_PR_SCOPES
 | 
			
		||||
    GitHub.open_api(url, data: data, scopes: scopes)["html_url"]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def gist_logs
 | 
			
		||||
    args = gist_logs_args.parse
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -227,7 +227,7 @@ module Homebrew
 | 
			
		||||
  def info_formula(f, args:)
 | 
			
		||||
    specs = []
 | 
			
		||||
 | 
			
		||||
    if stable = f.stable
 | 
			
		||||
    if (stable = f.stable)
 | 
			
		||||
      s = "stable #{stable.version}"
 | 
			
		||||
      s += " (bottled)" if stable.bottled? && f.pour_bottle?
 | 
			
		||||
      specs << s
 | 
			
		||||
@ -338,6 +338,7 @@ module Homebrew
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def info_cask(cask, args:)
 | 
			
		||||
    require "cask/cmd"
 | 
			
		||||
    require "cask/cmd/info"
 | 
			
		||||
 | 
			
		||||
    Cask::Cmd::Info.info(cask)
 | 
			
		||||
 | 
			
		||||
@ -264,7 +264,7 @@ module Homebrew
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
        opoo msg if msg
 | 
			
		||||
      elsif !f.any_version_installed? && old_formula = f.old_installed_formulae.first
 | 
			
		||||
      elsif !f.any_version_installed? && (old_formula = f.old_installed_formulae.first)
 | 
			
		||||
        msg = "#{old_formula.full_name} #{old_formula.any_installed_version} already installed"
 | 
			
		||||
        msg = if !old_formula.linked? && !old_formula.keg_only?
 | 
			
		||||
          <<~EOS
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
  def git_log(cd_dir, path = nil, tap = nil, args:)
 | 
			
		||||
    cd cd_dir
 | 
			
		||||
    repo = Utils.popen_read("git rev-parse --show-toplevel").chomp
 | 
			
		||||
    repo = Utils.popen_read("git", "rev-parse", "--show-toplevel").chomp
 | 
			
		||||
    if tap
 | 
			
		||||
      name = tap.to_s
 | 
			
		||||
      git_cd = "$(brew --repo #{tap})"
 | 
			
		||||
 | 
			
		||||
@ -73,7 +73,7 @@ module Homebrew
 | 
			
		||||
  def search
 | 
			
		||||
    args = search_args.parse
 | 
			
		||||
 | 
			
		||||
    if package_manager = PACKAGE_MANAGERS.find { |name,| args[:"#{name}?"] }
 | 
			
		||||
    if (package_manager = PACKAGE_MANAGERS.find { |name,| args[:"#{name}?"] })
 | 
			
		||||
      _, url = package_manager
 | 
			
		||||
      exec_browser url.call(URI.encode_www_form_component(args.named.join(" ")))
 | 
			
		||||
      return
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    Uninstall.uninstall_kegs(
 | 
			
		||||
      kegs_by_rack,
 | 
			
		||||
      casks:               casks,
 | 
			
		||||
      force:               args.force?,
 | 
			
		||||
      ignore_dependencies: args.ignore_dependencies?,
 | 
			
		||||
      named_args:          args.named,
 | 
			
		||||
@ -68,9 +69,8 @@ module Homebrew
 | 
			
		||||
    else
 | 
			
		||||
      T.unsafe(Cask::Cmd::Uninstall).uninstall_casks(
 | 
			
		||||
        *casks,
 | 
			
		||||
        binaries: EnvConfig.cask_opts_binaries?,
 | 
			
		||||
        verbose:  args.verbose?,
 | 
			
		||||
        force:    args.force?,
 | 
			
		||||
        verbose: args.verbose?,
 | 
			
		||||
        force:   args.force?,
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -129,15 +129,40 @@ module Homebrew
 | 
			
		||||
      if hub.empty?
 | 
			
		||||
        puts_stdout_or_stderr "No changes to formulae." unless args.quiet?
 | 
			
		||||
      else
 | 
			
		||||
        hub.dump(updated_formula_report: !args.preinstall?)
 | 
			
		||||
        hub.dump(updated_formula_report: !args.preinstall?) unless args.quiet?
 | 
			
		||||
        hub.reporters.each(&:migrate_tap_migration)
 | 
			
		||||
        hub.reporters.each { |r| r.migrate_formula_rename(force: args.force?, verbose: args.verbose?) }
 | 
			
		||||
        CacheStoreDatabase.use(:descriptions) do |db|
 | 
			
		||||
          DescriptionCacheStore.new(db)
 | 
			
		||||
                               .update_from_report!(hub)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        if !args.preinstall? && !args.quiet?
 | 
			
		||||
          outdated_formulae = Formula.installed.count(&:outdated?)
 | 
			
		||||
          outdated_casks = Cask::Caskroom.casks.count(&:outdated?)
 | 
			
		||||
          update_pronoun = if (outdated_formulae + outdated_casks) == 1
 | 
			
		||||
            "it"
 | 
			
		||||
          else
 | 
			
		||||
            "them"
 | 
			
		||||
          end
 | 
			
		||||
          msg = ""
 | 
			
		||||
          if outdated_formulae.positive?
 | 
			
		||||
            msg += "#{Tty.bold}#{outdated_formulae}#{Tty.reset} outdated #{"formula".pluralize(outdated_formulae)}"
 | 
			
		||||
          end
 | 
			
		||||
          if outdated_casks.positive?
 | 
			
		||||
            msg += " and " if msg.present?
 | 
			
		||||
            msg += "#{Tty.bold}#{outdated_casks}#{Tty.reset} outdated #{"cask".pluralize(outdated_casks)}"
 | 
			
		||||
          end
 | 
			
		||||
          if msg.present?
 | 
			
		||||
            puts_stdout_or_stderr
 | 
			
		||||
            puts_stdout_or_stderr <<~EOS
 | 
			
		||||
              You have #{msg} installed.
 | 
			
		||||
              You can update #{update_pronoun} with #{Tty.bold}brew upgrade#{Tty.reset}.
 | 
			
		||||
            EOS
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
      puts if args.preinstall?
 | 
			
		||||
      puts_stdout_or_stderr if args.preinstall?
 | 
			
		||||
    elsif !args.preinstall? && !ENV["HOMEBREW_UPDATE_FAILED"]
 | 
			
		||||
      puts_stdout_or_stderr "Already up-to-date." unless args.quiet?
 | 
			
		||||
    end
 | 
			
		||||
@ -161,6 +186,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    return if new_repository_version.blank?
 | 
			
		||||
 | 
			
		||||
    puts_stdout_or_stderr
 | 
			
		||||
    ohai_stdout_or_stderr "Homebrew was updated to version #{new_repository_version}"
 | 
			
		||||
    if new_repository_version.split(".").last == "0"
 | 
			
		||||
      puts_stdout_or_stderr <<~EOS
 | 
			
		||||
 | 
			
		||||
@ -308,6 +308,7 @@ homebrew-update() {
 | 
			
		||||
      -\?|-h|--help|--usage)          brew help update; exit $? ;;
 | 
			
		||||
      --verbose)                      HOMEBREW_VERBOSE=1 ;;
 | 
			
		||||
      --debug)                        HOMEBREW_DEBUG=1 ;;
 | 
			
		||||
      --quiet)                        HOMEBREW_QUIET=1 ;;
 | 
			
		||||
      --merge)                        HOMEBREW_MERGE=1 ;;
 | 
			
		||||
      --force)                        HOMEBREW_UPDATE_FORCE=1 ;;
 | 
			
		||||
      --simulate-from-current-branch) HOMEBREW_SIMULATE_FROM_CURRENT_BRANCH=1 ;;
 | 
			
		||||
@ -315,6 +316,7 @@ homebrew-update() {
 | 
			
		||||
      --*)                            ;;
 | 
			
		||||
      -*)
 | 
			
		||||
        [[ "$option" = *v* ]] && HOMEBREW_VERBOSE=1
 | 
			
		||||
        [[ "$option" = *q* ]] && HOMEBREW_QUIET=1
 | 
			
		||||
        [[ "$option" = *d* ]] && HOMEBREW_DEBUG=1
 | 
			
		||||
        [[ "$option" = *f* ]] && HOMEBREW_UPDATE_FORCE=1
 | 
			
		||||
        ;;
 | 
			
		||||
@ -661,7 +663,8 @@ EOS
 | 
			
		||||
  then
 | 
			
		||||
    brew update-report "$@"
 | 
			
		||||
    return $?
 | 
			
		||||
  elif [[ -z "$HOMEBREW_UPDATE_PREINSTALL" ]]
 | 
			
		||||
  elif [[ -z "$HOMEBREW_UPDATE_PREINSTALL" &&
 | 
			
		||||
          -z "$HOMEBREW_QUIET" ]]
 | 
			
		||||
  then
 | 
			
		||||
    echo "Already up-to-date."
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
@ -175,7 +175,7 @@ module Commands
 | 
			
		||||
    path = self.path(command)
 | 
			
		||||
    return if path.blank?
 | 
			
		||||
 | 
			
		||||
    if cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path)
 | 
			
		||||
    if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path))
 | 
			
		||||
      cmd_parser.processed_options.map do |short, long, _, desc|
 | 
			
		||||
        [long || short, desc]
 | 
			
		||||
      end
 | 
			
		||||
@ -198,7 +198,7 @@ module Commands
 | 
			
		||||
    path = self.path(command)
 | 
			
		||||
    return if path.blank?
 | 
			
		||||
 | 
			
		||||
    if cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path)
 | 
			
		||||
    if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path))
 | 
			
		||||
      if short
 | 
			
		||||
        cmd_parser.description.split(".").first
 | 
			
		||||
      else
 | 
			
		||||
 | 
			
		||||
@ -129,8 +129,6 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    sig { params(command: String).returns(T::Boolean) }
 | 
			
		||||
    def command_gets_completions?(command)
 | 
			
		||||
      return false if command.start_with? "cask " # TODO: (2.8) remove when `brew cask` commands are removed
 | 
			
		||||
 | 
			
		||||
      command_options(command).any?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -167,7 +165,7 @@ module Homebrew
 | 
			
		||||
      return unless command_gets_completions? command
 | 
			
		||||
 | 
			
		||||
      named_completion_string = ""
 | 
			
		||||
      if types = Commands.named_args_type(command)
 | 
			
		||||
      if (types = Commands.named_args_type(command))
 | 
			
		||||
        named_args_strings, named_args_types = types.partition { |type| type.is_a? String }
 | 
			
		||||
 | 
			
		||||
        named_args_types.each do |type|
 | 
			
		||||
@ -215,29 +213,51 @@ module Homebrew
 | 
			
		||||
    def generate_zsh_subcommand_completion(command)
 | 
			
		||||
      return unless command_gets_completions? command
 | 
			
		||||
 | 
			
		||||
      options = command_options(command).sort.map do |opt, desc|
 | 
			
		||||
        next opt if desc.blank?
 | 
			
		||||
      options = command_options(command)
 | 
			
		||||
 | 
			
		||||
        conflicts = generate_zsh_option_exclusions(command, opt)
 | 
			
		||||
        "#{conflicts}#{opt}[#{format_description desc}]"
 | 
			
		||||
      end
 | 
			
		||||
      if types = Commands.named_args_type(command)
 | 
			
		||||
      args_options = []
 | 
			
		||||
      if (types = Commands.named_args_type(command))
 | 
			
		||||
        named_args_strings, named_args_types = types.partition { |type| type.is_a? String }
 | 
			
		||||
 | 
			
		||||
        named_args_types.each do |type|
 | 
			
		||||
          next unless ZSH_NAMED_ARGS_COMPLETION_FUNCTION_MAPPING.key? type
 | 
			
		||||
 | 
			
		||||
          options << "::#{type}:#{ZSH_NAMED_ARGS_COMPLETION_FUNCTION_MAPPING[type]}"
 | 
			
		||||
          args_options << "- #{type}"
 | 
			
		||||
          opt = "--#{type.to_s.gsub(/(installed|outdated)_/, "")}"
 | 
			
		||||
          if options.key?(opt)
 | 
			
		||||
            desc = options[opt]
 | 
			
		||||
 | 
			
		||||
            if desc.blank?
 | 
			
		||||
              args_options << opt
 | 
			
		||||
            else
 | 
			
		||||
              conflicts = generate_zsh_option_exclusions(command, opt)
 | 
			
		||||
              args_options << "#{conflicts}#{opt}[#{format_description desc}]"
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            options.delete(opt)
 | 
			
		||||
          end
 | 
			
		||||
          args_options << "*::#{type}:#{ZSH_NAMED_ARGS_COMPLETION_FUNCTION_MAPPING[type]}"
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        options << "::subcommand:(#{named_args_strings.join(" ")})" if named_args_strings.any?
 | 
			
		||||
        if named_args_strings.any?
 | 
			
		||||
          args_options << "- subcommand"
 | 
			
		||||
          args_options << "*::subcommand:(#{named_args_strings.join(" ")})"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      options = options.sort.map do |opt, desc|
 | 
			
		||||
        next opt if desc.blank?
 | 
			
		||||
 | 
			
		||||
        conflicts = generate_zsh_option_exclusions(command, opt)
 | 
			
		||||
        "#{conflicts}#{opt}[#{format_description desc}]"
 | 
			
		||||
      end
 | 
			
		||||
      options += args_options
 | 
			
		||||
 | 
			
		||||
      <<~COMPLETION
 | 
			
		||||
        # brew #{command}
 | 
			
		||||
        _brew_#{Commands.method_name command}() {
 | 
			
		||||
          _arguments \\
 | 
			
		||||
            #{options.map! { |opt| "'#{opt}'" }.join(" \\\n    ")}
 | 
			
		||||
            #{options.map! { |opt| opt.start_with?("- ") ? opt : "'#{opt}'" }.join(" \\\n    ")}
 | 
			
		||||
        }
 | 
			
		||||
      COMPLETION
 | 
			
		||||
    end
 | 
			
		||||
@ -291,7 +311,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      subcommands = []
 | 
			
		||||
      named_args = []
 | 
			
		||||
      if types = Commands.named_args_type(command)
 | 
			
		||||
      if (types = Commands.named_args_type(command))
 | 
			
		||||
        named_args_strings, named_args_types = types.partition { |type| type.is_a? String }
 | 
			
		||||
 | 
			
		||||
        named_args_types.each do |type|
 | 
			
		||||
 | 
			
		||||
@ -67,3 +67,9 @@ HOMEBREW_TEMP = Pathname(EnvVar["HOMEBREW_TEMP"]).yield_self do |tmp|
 | 
			
		||||
  tmp.mkpath unless tmp.exist?
 | 
			
		||||
  tmp.realpath
 | 
			
		||||
end.freeze
 | 
			
		||||
 | 
			
		||||
# The Ruby path and args to use for forked Ruby calls
 | 
			
		||||
HOMEBREW_RUBY_EXEC_ARGS = [
 | 
			
		||||
  RUBY_PATH,
 | 
			
		||||
  ENV["HOMEBREW_RUBY_WARNINGS"],
 | 
			
		||||
].freeze
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ module Context
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.current
 | 
			
		||||
    if current_context = Thread.current[:context]
 | 
			
		||||
    if (current_context = Thread.current[:context])
 | 
			
		||||
      return current_context
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@ class Descriptions
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def short_names
 | 
			
		||||
    @short_names ||= Hash[@descriptions.keys.map { |k| [k, k.split("/").last] }]
 | 
			
		||||
    @short_names ||= @descriptions.keys.map { |k| [k, k.split("/").last] }.to_h
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def short_name_counts
 | 
			
		||||
 | 
			
		||||
@ -254,7 +254,7 @@ module Homebrew
 | 
			
		||||
  def bottle_formula(f, args:)
 | 
			
		||||
    return ofail "Formula not installed or up-to-date: #{f.full_name}" unless f.latest_version_installed?
 | 
			
		||||
 | 
			
		||||
    unless tap = f.tap
 | 
			
		||||
    unless (tap = f.tap)
 | 
			
		||||
      return ofail "Formula not from core or any installed taps: #{f.full_name}" unless args.force_core_tap?
 | 
			
		||||
 | 
			
		||||
      tap = CoreTap.instance
 | 
			
		||||
 | 
			
		||||
@ -125,29 +125,21 @@ module Homebrew
 | 
			
		||||
        tmp_config = cask.config
 | 
			
		||||
        tmp_url = tmp_cask.url.to_s
 | 
			
		||||
 | 
			
		||||
        if new_hash.nil? && old_hash != :no_check
 | 
			
		||||
          resource_path = fetch_resource(cask, new_version, tmp_url)
 | 
			
		||||
          Utils::Tar.validate_file(resource_path)
 | 
			
		||||
          new_hash = resource_path.sha256
 | 
			
		||||
        if old_hash != :no_check
 | 
			
		||||
          new_hash = fetch_resource(cask, new_version, tmp_url) if new_hash.nil?
 | 
			
		||||
 | 
			
		||||
          if tmp_contents.include?("Hardware::CPU.intel?")
 | 
			
		||||
            other_intel = !Hardware::CPU.intel?
 | 
			
		||||
            other_contents = tmp_contents.gsub("Hardware::CPU.intel?", other_intel.to_s)
 | 
			
		||||
            replacement_pairs << fetch_cask(other_contents, new_version)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        cask.languages.each do |language|
 | 
			
		||||
          next if language == cask.language
 | 
			
		||||
 | 
			
		||||
          lang_config = tmp_config.merge(Cask::Config.new(explicit: { languages: [language] }))
 | 
			
		||||
          lang_cask = Cask::CaskLoader.load(tmp_contents)
 | 
			
		||||
          lang_cask.config = lang_config
 | 
			
		||||
          lang_url = lang_cask.url.to_s
 | 
			
		||||
          lang_old_hash = lang_cask.sha256.to_s
 | 
			
		||||
 | 
			
		||||
          resource_path = fetch_resource(cask, new_version, lang_url)
 | 
			
		||||
          Utils::Tar.validate_file(resource_path)
 | 
			
		||||
          lang_new_hash = resource_path.sha256
 | 
			
		||||
 | 
			
		||||
          replacement_pairs << [
 | 
			
		||||
            lang_old_hash,
 | 
			
		||||
            lang_new_hash,
 | 
			
		||||
          ]
 | 
			
		||||
          replacement_pairs << fetch_cask(tmp_contents, new_version, config: lang_config)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -186,12 +178,24 @@ module Homebrew
 | 
			
		||||
    GitHub.create_bump_pr(pr_info, args: args)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fetch_resource(cask, new_version, url, **specs)
 | 
			
		||||
  def fetch_resource(cask, version, url, **specs)
 | 
			
		||||
    resource = Resource.new
 | 
			
		||||
    resource.url(url, specs)
 | 
			
		||||
    resource.owner = Resource.new(cask.token)
 | 
			
		||||
    resource.version = new_version
 | 
			
		||||
    resource.fetch
 | 
			
		||||
    resource.version = version
 | 
			
		||||
 | 
			
		||||
    resource_path = resource.fetch
 | 
			
		||||
    Utils::Tar.validate_file(resource_path)
 | 
			
		||||
    resource_path.sha256
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fetch_cask(contents, version, config: nil)
 | 
			
		||||
    cask = Cask::CaskLoader.load(contents)
 | 
			
		||||
    cask.config = config if config.present?
 | 
			
		||||
    url = cask.url.to_s
 | 
			
		||||
    old_hash = cask.sha256.to_s
 | 
			
		||||
    new_hash = fetch_resource(cask, version, url)
 | 
			
		||||
    [old_hash, new_hash]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_open_pull_requests(cask, args:)
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@ module Homebrew
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    formula.tap.path.cd do
 | 
			
		||||
      unless Utils.popen_read("git remote -v").match?(%r{^homebrew.*Homebrew/homebrew-core.*$})
 | 
			
		||||
      unless Utils.popen_read("git", "remote", "-v").match?(%r{^homebrew.*Homebrew/homebrew-core.*$})
 | 
			
		||||
        ohai "Adding #{homebrew_core_remote} remote"
 | 
			
		||||
        safe_system "git", "remote", "add", homebrew_core_remote, homebrew_core_url
 | 
			
		||||
      end
 | 
			
		||||
@ -193,7 +193,7 @@ module Homebrew
 | 
			
		||||
        end
 | 
			
		||||
        check_new_version(formula, tap_full_name, url: old_url, tag: new_tag, args: args) if new_version.blank?
 | 
			
		||||
        resource_path, forced_version = fetch_resource(formula, new_version, old_url, tag: new_tag)
 | 
			
		||||
        new_revision = Utils.popen_read("git -C \"#{resource_path}\" rev-parse -q --verify HEAD")
 | 
			
		||||
        new_revision = Utils.popen_read("git", "-C", resource_path.to_s, "rev-parse", "-q", "--verify", "HEAD")
 | 
			
		||||
        new_revision = new_revision.strip
 | 
			
		||||
      elsif new_revision.blank?
 | 
			
		||||
        odie "#{formula}: the current URL requires specifying a `--revision=` argument."
 | 
			
		||||
 | 
			
		||||
@ -16,66 +16,148 @@ module Homebrew
 | 
			
		||||
        Display out-of-date brew formulae and the latest version available.
 | 
			
		||||
        Also displays whether a pull request has been opened with the URL.
 | 
			
		||||
      EOS
 | 
			
		||||
      switch "--full-name",
 | 
			
		||||
             description: "Print formulae/casks with fully-qualified names."
 | 
			
		||||
      switch "--no-pull-requests",
 | 
			
		||||
             description: "Do not retrieve pull requests from GitHub."
 | 
			
		||||
      switch "--formula", "--formulae",
 | 
			
		||||
             description: "Check only formulae."
 | 
			
		||||
      switch "--cask", "--casks",
 | 
			
		||||
             description: "Check only casks."
 | 
			
		||||
      flag   "--limit=",
 | 
			
		||||
             description: "Limit number of package results returned."
 | 
			
		||||
 | 
			
		||||
      named_args :formula
 | 
			
		||||
      conflicts "--cask", "--formula"
 | 
			
		||||
 | 
			
		||||
      named_args [:formula, :cask]
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def bump
 | 
			
		||||
    args = bump_args.parse
 | 
			
		||||
 | 
			
		||||
    requested_formulae = args.named.to_formulae.presence
 | 
			
		||||
    requested_limit = args.limit.to_i if args.limit.present?
 | 
			
		||||
    if args.limit.present? && !args.formula? && !args.cask?
 | 
			
		||||
      raise UsageError, "`--limit` must be used with either `--formula` or `--cask`."
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if requested_formulae
 | 
			
		||||
      Livecheck.load_other_tap_strategies(requested_formulae)
 | 
			
		||||
    formulae_and_casks = if args.formula?
 | 
			
		||||
      args.named.to_formulae
 | 
			
		||||
    elsif args.cask?
 | 
			
		||||
      args.named.to_casks
 | 
			
		||||
    else
 | 
			
		||||
      args.named.to_formulae_and_casks
 | 
			
		||||
    end
 | 
			
		||||
    formulae_and_casks = formulae_and_casks&.sort_by do |formula_or_cask|
 | 
			
		||||
      formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
      requested_formulae.each_with_index do |formula, i|
 | 
			
		||||
    limit = args.limit.to_i if args.limit.present?
 | 
			
		||||
 | 
			
		||||
    if formulae_and_casks
 | 
			
		||||
      Livecheck.load_other_tap_strategies(formulae_and_casks)
 | 
			
		||||
 | 
			
		||||
      ambiguous_casks = []
 | 
			
		||||
      if !args.formula? && !args.cask?
 | 
			
		||||
        ambiguous_casks = formulae_and_casks.group_by { |item| Livecheck.formula_or_cask_name(item, full_name: true) }
 | 
			
		||||
                                            .values
 | 
			
		||||
                                            .select { |items| items.length > 1 }
 | 
			
		||||
                                            .flatten
 | 
			
		||||
                                            .select { |item| item.is_a?(Cask::Cask) }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      ambiguous_names = []
 | 
			
		||||
      unless args.full_name?
 | 
			
		||||
        ambiguous_names =
 | 
			
		||||
          (formulae_and_casks - ambiguous_casks).group_by { |item| Livecheck.formula_or_cask_name(item) }
 | 
			
		||||
                                                .values
 | 
			
		||||
                                                .select { |items| items.length > 1 }
 | 
			
		||||
                                                .flatten
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      formulae_and_casks.each_with_index do |formula_or_cask, i|
 | 
			
		||||
        puts if i.positive?
 | 
			
		||||
 | 
			
		||||
        if formula.head_only?
 | 
			
		||||
          ohai formula.name
 | 
			
		||||
          puts "Formula is HEAD-only."
 | 
			
		||||
          next
 | 
			
		||||
        use_full_name = args.full_name? || ambiguous_names.include?(formula_or_cask)
 | 
			
		||||
        name = Livecheck.formula_or_cask_name(formula_or_cask, full_name: use_full_name)
 | 
			
		||||
        repository = if formula_or_cask.is_a?(Formula)
 | 
			
		||||
          if formula_or_cask.head_only?
 | 
			
		||||
            ohai name
 | 
			
		||||
            puts "Formula is HEAD-only."
 | 
			
		||||
            next
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          Repology::HOMEBREW_CORE
 | 
			
		||||
        else
 | 
			
		||||
          Repology::HOMEBREW_CASK
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        package_data = Repology.single_package_query(formula.name)
 | 
			
		||||
        retrieve_and_display_info(formula, package_data&.values&.first)
 | 
			
		||||
        package_data = Repology.single_package_query(name, repository: repository)
 | 
			
		||||
        retrieve_and_display_info(
 | 
			
		||||
          formula_or_cask,
 | 
			
		||||
          name,
 | 
			
		||||
          package_data&.values&.first,
 | 
			
		||||
          args:           args,
 | 
			
		||||
          ambiguous_cask: ambiguous_casks.include?(formula_or_cask),
 | 
			
		||||
        )
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      outdated_packages = Repology.parse_api_response(requested_limit)
 | 
			
		||||
      outdated_packages.each_with_index do |(_name, repositories), i|
 | 
			
		||||
        puts if i.positive?
 | 
			
		||||
      api_response = {}
 | 
			
		||||
      unless args.cask?
 | 
			
		||||
        api_response[:formulae] =
 | 
			
		||||
          Repology.parse_api_response(limit, repository: Repology::HOMEBREW_CORE)
 | 
			
		||||
      end
 | 
			
		||||
      unless args.formula?
 | 
			
		||||
        api_response[:casks] =
 | 
			
		||||
          Repology.parse_api_response(limit, repository: Repology::HOMEBREW_CASK)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
        homebrew_repo = repositories.find do |repo|
 | 
			
		||||
          repo["repo"] == "homebrew"
 | 
			
		||||
      api_response.each do |package_type, outdated_packages|
 | 
			
		||||
        repository = if package_type == :formulae
 | 
			
		||||
          Repology::HOMEBREW_CORE
 | 
			
		||||
        else
 | 
			
		||||
          Repology::HOMEBREW_CASK
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        next if homebrew_repo.blank?
 | 
			
		||||
        outdated_packages.each_with_index do |(_name, repositories), i|
 | 
			
		||||
          homebrew_repo = repositories.find do |repo|
 | 
			
		||||
            repo["repo"] == repository
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
        formula = begin
 | 
			
		||||
          Formula[homebrew_repo["srcname"]]
 | 
			
		||||
        rescue
 | 
			
		||||
          next
 | 
			
		||||
          next if homebrew_repo.blank?
 | 
			
		||||
 | 
			
		||||
          formula_or_cask = begin
 | 
			
		||||
            if repository == Repology::HOMEBREW_CORE
 | 
			
		||||
              Formula[homebrew_repo["srcname"]]
 | 
			
		||||
            else
 | 
			
		||||
              Cask::CaskLoader.load(homebrew_repo["srcname"])
 | 
			
		||||
            end
 | 
			
		||||
          rescue
 | 
			
		||||
            next
 | 
			
		||||
          end
 | 
			
		||||
          name = Livecheck.formula_or_cask_name(formula_or_cask)
 | 
			
		||||
          ambiguous_cask = begin
 | 
			
		||||
            formula_or_cask.is_a?(Cask::Cask) && !args.cask? && Formula[name]
 | 
			
		||||
          rescue FormulaUnavailableError
 | 
			
		||||
            false
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          puts if i.positive?
 | 
			
		||||
          retrieve_and_display_info(formula_or_cask, name, repositories, args: args, ambiguous_cask: ambiguous_cask)
 | 
			
		||||
 | 
			
		||||
          break if limit && i >= limit
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        retrieve_and_display_info(formula, repositories)
 | 
			
		||||
 | 
			
		||||
        break if requested_limit && i >= requested_limit
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def livecheck_result(formula)
 | 
			
		||||
    skip_result = Livecheck::SkipConditions.skip_information(formula)
 | 
			
		||||
  def livecheck_result(formula_or_cask)
 | 
			
		||||
    skip_result = Livecheck::SkipConditions.skip_information(formula_or_cask)
 | 
			
		||||
    if skip_result.present?
 | 
			
		||||
      return "#{skip_result[:status]}#{" - #{skip_result[:messages].join(", ")}" if skip_result[:messages].present?}"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    version_info = Livecheck.latest_version(
 | 
			
		||||
      formula,
 | 
			
		||||
      formula_or_cask,
 | 
			
		||||
      json: true, full_name: false, verbose: false, debug: false,
 | 
			
		||||
    )
 | 
			
		||||
    latest = version_info[:latest] if version_info.present?
 | 
			
		||||
@ -83,10 +165,12 @@ module Homebrew
 | 
			
		||||
    return "unable to get versions" if latest.blank?
 | 
			
		||||
 | 
			
		||||
    latest.to_s
 | 
			
		||||
  rescue => e
 | 
			
		||||
    "error: #{e}"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def retrieve_pull_requests(formula)
 | 
			
		||||
    pull_requests = GitHub.fetch_pull_requests(formula.name, formula.tap&.full_name, state: "open")
 | 
			
		||||
  def retrieve_pull_requests(formula_or_cask, name)
 | 
			
		||||
    pull_requests = GitHub.fetch_pull_requests(name, formula_or_cask.tap&.full_name, state: "open")
 | 
			
		||||
    if pull_requests.try(:any?)
 | 
			
		||||
      pull_requests = pull_requests.map { |pr| "#{pr["title"]} (#{Formatter.url(pr["html_url"])})" }.join(", ")
 | 
			
		||||
    end
 | 
			
		||||
@ -96,8 +180,12 @@ module Homebrew
 | 
			
		||||
    pull_requests
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def retrieve_and_display_info(formula, repositories)
 | 
			
		||||
    current_version = formula.stable.version.to_s
 | 
			
		||||
  def retrieve_and_display_info(formula_or_cask, name, repositories, args:, ambiguous_cask: false)
 | 
			
		||||
    current_version = if formula_or_cask.is_a?(Formula)
 | 
			
		||||
      formula_or_cask.stable.version
 | 
			
		||||
    else
 | 
			
		||||
      Version.new(formula_or_cask.version)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    repology_latest = if repositories.present?
 | 
			
		||||
      Repology.latest_version(repositories)
 | 
			
		||||
@ -105,14 +193,15 @@ module Homebrew
 | 
			
		||||
      "not found"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    livecheck_latest = livecheck_result(formula)
 | 
			
		||||
    pull_requests = retrieve_pull_requests(formula)
 | 
			
		||||
    livecheck_latest = livecheck_result(formula_or_cask)
 | 
			
		||||
    pull_requests = retrieve_pull_requests(formula_or_cask, name) unless args.no_pull_requests?
 | 
			
		||||
 | 
			
		||||
    name += " (cask)" if ambiguous_cask
 | 
			
		||||
    title = if current_version == repology_latest &&
 | 
			
		||||
               current_version == livecheck_latest
 | 
			
		||||
      "#{formula} is up to date!"
 | 
			
		||||
      "#{name} is up to date!"
 | 
			
		||||
    else
 | 
			
		||||
      formula.name
 | 
			
		||||
      name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    ohai title
 | 
			
		||||
@ -120,7 +209,7 @@ module Homebrew
 | 
			
		||||
      Current formula version:  #{current_version}
 | 
			
		||||
      Latest Repology version:  #{repology_latest}
 | 
			
		||||
      Latest livecheck version: #{livecheck_latest}
 | 
			
		||||
      Open pull requests:       #{pull_requests}
 | 
			
		||||
    EOS
 | 
			
		||||
    puts "Open pull requests:       #{pull_requests}" unless args.no_pull_requests?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -179,7 +179,7 @@ module Homebrew
 | 
			
		||||
    # Check for disallowed formula, or names that shadow aliases,
 | 
			
		||||
    # unless --force is specified.
 | 
			
		||||
    unless args.force?
 | 
			
		||||
      if reason = MissingFormula.disallowed_reason(fc.name)
 | 
			
		||||
      if (reason = MissingFormula.disallowed_reason(fc.name))
 | 
			
		||||
        odie <<~EOS
 | 
			
		||||
          The formula '#{fc.name}' is not allowed to be created.
 | 
			
		||||
          #{reason}
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,10 @@ module Homebrew
 | 
			
		||||
             description: "Dispatch specified workflow (default: `dispatch-build-bottle.yml`)."
 | 
			
		||||
      switch "--upload",
 | 
			
		||||
             description: "Upload built bottles to Bintray."
 | 
			
		||||
      switch "--linux",
 | 
			
		||||
             description: "Dispatch bottle for Linux (using GitHub runners)."
 | 
			
		||||
 | 
			
		||||
      conflicts "--macos", "--linux"
 | 
			
		||||
      named_args :formula, min: 1
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -33,45 +36,55 @@ module Homebrew
 | 
			
		||||
  def dispatch_build_bottle
 | 
			
		||||
    args = dispatch_build_bottle_args.parse
 | 
			
		||||
 | 
			
		||||
    # Fixup version for ARM/Apple Silicon
 | 
			
		||||
    # TODO: fix label name to be 11-arm64 instead and remove this.
 | 
			
		||||
    args.macos&.gsub!(/^11-arm$/, "11-arm64")
 | 
			
		||||
 | 
			
		||||
    macos = args.macos&.yield_self do |s|
 | 
			
		||||
      MacOS::Version.from_symbol(s.to_sym)
 | 
			
		||||
    rescue MacOSVersionError
 | 
			
		||||
      MacOS::Version.new(s)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    raise UsageError, "Must specify --macos option" if macos.blank?
 | 
			
		||||
 | 
			
		||||
    # Fixup label for ARM/Apple Silicon
 | 
			
		||||
    macos_label = if macos.arch == :arm64
 | 
			
		||||
      # TODO: fix label name to be 11-arm64 instead.
 | 
			
		||||
      "#{macos}-arm"
 | 
			
		||||
    else
 | 
			
		||||
      macos.to_s
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    tap = Tap.fetch(args.tap || CoreTap.instance.name)
 | 
			
		||||
    user, repo = tap.full_name.split("/")
 | 
			
		||||
 | 
			
		||||
    workflow = args.workflow || "dispatch-build-bottle.yml"
 | 
			
		||||
    ref = "master"
 | 
			
		||||
    workflow = args.workflow || "dispatch-build-bottle.yml"
 | 
			
		||||
 | 
			
		||||
    # Ensure we dispatch the bottle in homebrew/homebrew-core
 | 
			
		||||
    # TODO: remove when core taps are merged
 | 
			
		||||
    repo.gsub!("linux", "home") unless args.tap
 | 
			
		||||
 | 
			
		||||
    if (macos = args.macos)
 | 
			
		||||
      # Fixup version for ARM/Apple Silicon
 | 
			
		||||
      # TODO: fix label name to be 11-arm64 instead and remove this.
 | 
			
		||||
      macos.gsub!(/^11-arm$/, "11-arm64")
 | 
			
		||||
 | 
			
		||||
      macos = macos.yield_self do |s|
 | 
			
		||||
        MacOS::Version.from_symbol(s.to_sym)
 | 
			
		||||
      rescue MacOSVersionError
 | 
			
		||||
        MacOS::Version.new(s)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Fixup label for ARM/Apple Silicon
 | 
			
		||||
      macos_label = if macos.arch == :arm64
 | 
			
		||||
        # TODO: fix label name to be 11-arm64 instead.
 | 
			
		||||
        "#{macos}-arm"
 | 
			
		||||
      else
 | 
			
		||||
        macos.to_s
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      dispatching_for = "macOS #{macos}"
 | 
			
		||||
    elsif T.unsafe(args).linux?
 | 
			
		||||
      workflow = args.workflow || "linux-#{workflow}"
 | 
			
		||||
      dispatching_for = "Linux"
 | 
			
		||||
    else
 | 
			
		||||
      raise UsageError, "Must specify --macos or --linux option"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    args.named.to_resolved_formulae.each do |formula|
 | 
			
		||||
      # Required inputs
 | 
			
		||||
      inputs = {
 | 
			
		||||
        formula: formula.name,
 | 
			
		||||
        macos:   macos_label,
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      # Optional inputs
 | 
			
		||||
      # These cannot be passed as nil to GitHub API
 | 
			
		||||
      inputs[:macos] = macos_label if args.macos
 | 
			
		||||
      inputs[:issue] = args.issue if args.issue
 | 
			
		||||
      inputs[:upload] = args.upload?.to_s if args.upload?
 | 
			
		||||
 | 
			
		||||
      ohai "Dispatching #{tap} bottling request of formula \"#{formula.name}\" for macOS #{macos}"
 | 
			
		||||
      ohai "Dispatching #{tap} bottling request of formula \"#{formula.name}\" for #{dispatching_for}"
 | 
			
		||||
      GitHub.workflow_dispatch_event(user, repo, workflow, ref, inputs)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -61,54 +61,55 @@ module Homebrew
 | 
			
		||||
      puts ENV["HOMEBREW_LIVECHECK_WATCHLIST"] if ENV["HOMEBREW_LIVECHECK_WATCHLIST"].present?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    formulae_and_casks_to_check =
 | 
			
		||||
      if args.tap
 | 
			
		||||
        tap = Tap.fetch(args.tap)
 | 
			
		||||
        formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) }
 | 
			
		||||
        casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) }
 | 
			
		||||
        formulae + casks
 | 
			
		||||
      elsif args.installed?
 | 
			
		||||
        formulae = args.cask? ? [] : Formula.installed
 | 
			
		||||
        casks = args.formula? ? [] : Cask::Caskroom.casks
 | 
			
		||||
        formulae + casks
 | 
			
		||||
      elsif args.all?
 | 
			
		||||
        formulae = args.cask? ? [] : Formula.to_a
 | 
			
		||||
        casks = args.formula? ? [] : Cask::Cask.to_a
 | 
			
		||||
        formulae + casks
 | 
			
		||||
      elsif args.named.present?
 | 
			
		||||
        if args.formula?
 | 
			
		||||
          args.named.to_formulae
 | 
			
		||||
        elsif args.cask?
 | 
			
		||||
          args.named.to_casks
 | 
			
		||||
        else
 | 
			
		||||
          args.named.to_formulae_and_casks
 | 
			
		||||
        end
 | 
			
		||||
      elsif File.exist?(WATCHLIST_PATH)
 | 
			
		||||
        begin
 | 
			
		||||
          names = Pathname.new(WATCHLIST_PATH).read.lines
 | 
			
		||||
                          .reject { |line| line.start_with?("#") || line.blank? }
 | 
			
		||||
                          .map(&:strip)
 | 
			
		||||
 | 
			
		||||
          named_args = T.unsafe(CLI::NamedArgs).new(*names, parent: args)
 | 
			
		||||
          named_args.to_formulae_and_casks(ignore_unavailable: true)
 | 
			
		||||
        rescue Errno::ENOENT => e
 | 
			
		||||
          onoe e
 | 
			
		||||
        end
 | 
			
		||||
    formulae_and_casks_to_check = if args.tap
 | 
			
		||||
      tap = Tap.fetch(args.tap)
 | 
			
		||||
      formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) }
 | 
			
		||||
      casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) }
 | 
			
		||||
      formulae + casks
 | 
			
		||||
    elsif args.installed?
 | 
			
		||||
      formulae = args.cask? ? [] : Formula.installed
 | 
			
		||||
      casks = args.formula? ? [] : Cask::Caskroom.casks
 | 
			
		||||
      formulae + casks
 | 
			
		||||
    elsif args.all?
 | 
			
		||||
      formulae = args.cask? ? [] : Formula.to_a
 | 
			
		||||
      casks = args.formula? ? [] : Cask::Cask.to_a
 | 
			
		||||
      formulae + casks
 | 
			
		||||
    elsif args.named.present?
 | 
			
		||||
      if args.formula?
 | 
			
		||||
        args.named.to_formulae
 | 
			
		||||
      elsif args.cask?
 | 
			
		||||
        args.named.to_casks
 | 
			
		||||
      else
 | 
			
		||||
        raise UsageError, "A watchlist file is required when no arguments are given."
 | 
			
		||||
      end&.sort_by do |formula_or_cask|
 | 
			
		||||
        formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
 | 
			
		||||
        args.named.to_formulae_and_casks
 | 
			
		||||
      end
 | 
			
		||||
    elsif File.exist?(WATCHLIST_PATH)
 | 
			
		||||
      begin
 | 
			
		||||
        names = Pathname.new(WATCHLIST_PATH).read.lines
 | 
			
		||||
                        .reject { |line| line.start_with?("#") || line.blank? }
 | 
			
		||||
                        .map(&:strip)
 | 
			
		||||
 | 
			
		||||
        named_args = T.unsafe(CLI::NamedArgs).new(*names, parent: args)
 | 
			
		||||
        named_args.to_formulae_and_casks(ignore_unavailable: true)
 | 
			
		||||
      rescue Errno::ENOENT => e
 | 
			
		||||
        onoe e
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      raise UsageError, "A watchlist file is required when no arguments are given."
 | 
			
		||||
    end
 | 
			
		||||
    formulae_and_casks_to_check = formulae_and_casks_to_check.sort_by do |formula_or_cask|
 | 
			
		||||
      formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    raise UsageError, "No formulae or casks to check." if formulae_and_casks_to_check.blank?
 | 
			
		||||
 | 
			
		||||
    options = {
 | 
			
		||||
      json:       args.json?,
 | 
			
		||||
      full_name:  args.full_name?,
 | 
			
		||||
      newer_only: args.newer_only?,
 | 
			
		||||
      quiet:      args.quiet?,
 | 
			
		||||
      debug:      args.debug?,
 | 
			
		||||
      verbose:    args.verbose?,
 | 
			
		||||
      json:                 args.json?,
 | 
			
		||||
      full_name:            args.full_name?,
 | 
			
		||||
      handle_name_conflict: !args.formula? && !args.cask?,
 | 
			
		||||
      newer_only:           args.newer_only?,
 | 
			
		||||
      quiet:                args.quiet?,
 | 
			
		||||
      debug:                args.debug?,
 | 
			
		||||
      verbose:              args.verbose?,
 | 
			
		||||
    }.compact
 | 
			
		||||
 | 
			
		||||
    Livecheck.run_checks(formulae_and_casks_to_check, **options)
 | 
			
		||||
 | 
			
		||||
@ -24,9 +24,9 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
        *Note:* Not (yet) working on Apple Silicon.
 | 
			
		||||
      EOS
 | 
			
		||||
      switch "--fail-if-changed",
 | 
			
		||||
             description: "Return a failing status code if changes are detected in the manpage outputs. This "\
 | 
			
		||||
                          "can be used to notify CI when the manpages are out of date. Additionally, "\
 | 
			
		||||
      switch "--fail-if-not-changed",
 | 
			
		||||
             description: "Return a failing status code if no changes are detected in the manpage outputs. "\
 | 
			
		||||
                          "This can be used to notify CI when the manpages are out of date. Additionally, "\
 | 
			
		||||
                          "the date used in new manpages will match those in the existing manpages (to allow "\
 | 
			
		||||
                          "comparison without factoring in the date)."
 | 
			
		||||
      named_args :none
 | 
			
		||||
@ -42,19 +42,17 @@ module Homebrew
 | 
			
		||||
    args = man_args.parse
 | 
			
		||||
 | 
			
		||||
    Commands.rebuild_internal_commands_completion_list
 | 
			
		||||
    regenerate_man_pages(preserve_date: args.fail_if_changed?, quiet: args.quiet?)
 | 
			
		||||
    regenerate_man_pages(preserve_date: args.fail_if_not_changed?, quiet: args.quiet?)
 | 
			
		||||
    Completions.update_shell_completions!
 | 
			
		||||
 | 
			
		||||
    diff = system_command "git", args: [
 | 
			
		||||
      "-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "docs/Manpage.md", "manpages", "completions"
 | 
			
		||||
    ]
 | 
			
		||||
    if diff.status.success?
 | 
			
		||||
      puts "No changes to manpage or completions output detected."
 | 
			
		||||
    elsif args.fail_if_changed?
 | 
			
		||||
      puts "Changes to manpage or completions detected:"
 | 
			
		||||
      puts diff.stdout
 | 
			
		||||
      Homebrew.failed = true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    return unless diff.status.success?
 | 
			
		||||
 | 
			
		||||
    puts "No changes to manpage or completions output detected."
 | 
			
		||||
    Homebrew.failed = true if args.fail_if_not_changed?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def regenerate_man_pages(preserve_date:, quiet:)
 | 
			
		||||
@ -62,6 +60,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    markup = build_man_page(quiet: quiet)
 | 
			
		||||
    convert_man_page(markup, TARGET_DOC_PATH/"Manpage.md", preserve_date: preserve_date)
 | 
			
		||||
    markup = I18n.transliterate(markup, locale: :en)
 | 
			
		||||
    convert_man_page(markup, TARGET_MAN_PATH/"brew.1", preserve_date: preserve_date)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -164,7 +163,7 @@ module Homebrew
 | 
			
		||||
    # preserve existing manpage order
 | 
			
		||||
    cmd_paths.sort_by(&method(:sort_key_for_path))
 | 
			
		||||
             .each do |cmd_path|
 | 
			
		||||
      cmd_man_page_lines = if cmd_parser = CLI::Parser.from_cmd_path(cmd_path)
 | 
			
		||||
      cmd_man_page_lines = if (cmd_parser = CLI::Parser.from_cmd_path(cmd_path))
 | 
			
		||||
        next if cmd_parser.hide_from_man_page
 | 
			
		||||
 | 
			
		||||
        cmd_parser_manpage_lines(cmd_parser).join
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,8 @@ module Homebrew
 | 
			
		||||
             description: "Message to include when autosquashing revision bumps, deletions, and rebuilds."
 | 
			
		||||
      flag   "--artifact=",
 | 
			
		||||
             description: "Download artifacts with the specified name (default: `bottles`)."
 | 
			
		||||
      flag   "--archive-item=",
 | 
			
		||||
             description: "Upload to the specified Internet Archive item (default: `homebrew`)."
 | 
			
		||||
      flag   "--bintray-org=",
 | 
			
		||||
             description: "Upload to the specified Bintray organisation (default: `homebrew`)."
 | 
			
		||||
      flag   "--tap=",
 | 
			
		||||
@ -65,6 +67,7 @@ module Homebrew
 | 
			
		||||
                  description: "Comma-separated list of workflows which can be ignored if they have not been run."
 | 
			
		||||
 | 
			
		||||
      conflicts "--clean", "--autosquash"
 | 
			
		||||
      conflicts "--archive-item", "--bintray-org"
 | 
			
		||||
 | 
			
		||||
      named_args :pull_request, min: 1
 | 
			
		||||
    end
 | 
			
		||||
@ -337,9 +340,9 @@ module Homebrew
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def download_artifact(url, dir, pr)
 | 
			
		||||
    odie "Credentials must be set to access the Artifacts API" if GitHub.api_credentials_type == :none
 | 
			
		||||
    odie "Credentials must be set to access the Artifacts API" if GitHub::API.credentials_type == :none
 | 
			
		||||
 | 
			
		||||
    token = GitHub.api_credentials
 | 
			
		||||
    token = GitHub::API.credentials
 | 
			
		||||
    curl_args = ["--header", "Authorization: token #{token}"]
 | 
			
		||||
 | 
			
		||||
    # Download the artifact as a zip file and unpack it into `dir`. This is
 | 
			
		||||
@ -357,6 +360,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    workflows = args.workflows.presence || ["tests.yml"]
 | 
			
		||||
    artifact = args.artifact || "bottles"
 | 
			
		||||
    archive_item = args.archive_item
 | 
			
		||||
    bintray_org = args.bintray_org || "homebrew"
 | 
			
		||||
    mirror_repo = args.bintray_mirror || "mirror"
 | 
			
		||||
    tap = Tap.fetch(args.tap || CoreTap.instance.name)
 | 
			
		||||
@ -424,7 +428,11 @@ module Homebrew
 | 
			
		||||
          upload_args << "--keep-old" if args.keep_old?
 | 
			
		||||
          upload_args << "--warn-on-upload-failure" if args.warn_on_upload_failure?
 | 
			
		||||
          upload_args << "--root-url=#{args.root_url}" if args.root_url
 | 
			
		||||
          upload_args << "--bintray-org=#{bintray_org}"
 | 
			
		||||
          upload_args << if archive_item.present?
 | 
			
		||||
            "--archive-item=#{archive_item}"
 | 
			
		||||
          else
 | 
			
		||||
            "--bintray-org=#{bintray_org}"
 | 
			
		||||
          end
 | 
			
		||||
          safe_system HOMEBREW_BREW_FILE, *upload_args
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,10 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "cli/parser"
 | 
			
		||||
require "archive"
 | 
			
		||||
require "bintray"
 | 
			
		||||
require "github_packages"
 | 
			
		||||
require "github_releases"
 | 
			
		||||
 | 
			
		||||
module Homebrew
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
@ -13,7 +16,7 @@ module Homebrew
 | 
			
		||||
  def pr_upload_args
 | 
			
		||||
    Homebrew::CLI::Parser.new do
 | 
			
		||||
      description <<~EOS
 | 
			
		||||
        Apply the bottle commit and publish bottles to Bintray or GitHub Releases.
 | 
			
		||||
        Apply the bottle commit and publish bottles to a host.
 | 
			
		||||
      EOS
 | 
			
		||||
      switch "--no-publish",
 | 
			
		||||
             description: "Apply the bottle commit and upload the bottles, but don't publish them."
 | 
			
		||||
@ -27,8 +30,12 @@ module Homebrew
 | 
			
		||||
      switch "--warn-on-upload-failure",
 | 
			
		||||
             description: "Warn instead of raising an error if the bottle upload fails. "\
 | 
			
		||||
                          "Useful for repairing bottle uploads that previously failed."
 | 
			
		||||
      flag   "--archive-item=",
 | 
			
		||||
             description: "Upload to the specified Internet Archive item (default: `homebrew`)."
 | 
			
		||||
      flag   "--bintray-org=",
 | 
			
		||||
             description: "Upload to the specified Bintray organisation (default: `homebrew`)."
 | 
			
		||||
      flag   "--github-org=",
 | 
			
		||||
             description: "Upload to the specified GitHub organisation's GitHub Packages (default: `homebrew`)."
 | 
			
		||||
      flag   "--root-url=",
 | 
			
		||||
             description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
 | 
			
		||||
 | 
			
		||||
@ -47,16 +54,34 @@ module Homebrew
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def internet_archive?(bottles_hash)
 | 
			
		||||
    @internet_archive ||= bottles_hash.values.all? do |bottle_hash|
 | 
			
		||||
      bottle_hash["bottle"]["root_url"].start_with? "#{Archive::URL_PREFIX}/"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def bintray?(bottles_hash)
 | 
			
		||||
    @bintray ||= bottles_hash.values.all? do |bottle_hash|
 | 
			
		||||
      bottle_hash["bottle"]["root_url"].match? Bintray::URL_REGEX
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def github_releases?(bottles_hash)
 | 
			
		||||
    @github_releases ||= bottles_hash.values.all? do |bottle_hash|
 | 
			
		||||
      root_url = bottle_hash["bottle"]["root_url"]
 | 
			
		||||
      url_match = root_url.match HOMEBREW_RELEASES_URL_REGEX
 | 
			
		||||
      url_match = root_url.match GitHubReleases::URL_REGEX
 | 
			
		||||
      _, _, _, tag = *url_match
 | 
			
		||||
 | 
			
		||||
      tag
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def github_packages?(bottles_hash)
 | 
			
		||||
    @github_packages ||= bottles_hash.values.all? do |bottle_hash|
 | 
			
		||||
      bottle_hash["bottle"]["root_url"].match? GitHubPackages::URL_REGEX
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def pr_upload
 | 
			
		||||
    args = pr_upload_args.parse
 | 
			
		||||
 | 
			
		||||
@ -76,11 +101,18 @@ module Homebrew
 | 
			
		||||
    bottle_args += json_files
 | 
			
		||||
 | 
			
		||||
    if args.dry_run?
 | 
			
		||||
      service = if github_releases?(bottles_hash)
 | 
			
		||||
        "GitHub Releases"
 | 
			
		||||
      else
 | 
			
		||||
        "Bintray"
 | 
			
		||||
      end
 | 
			
		||||
      service =
 | 
			
		||||
        if internet_archive?(bottles_hash)
 | 
			
		||||
          "Internet Archive"
 | 
			
		||||
        elsif bintray?(bottles_hash)
 | 
			
		||||
          "Bintray"
 | 
			
		||||
        elsif github_releases?(bottles_hash)
 | 
			
		||||
          "GitHub Releases"
 | 
			
		||||
        elsif github_packages?(bottles_hash)
 | 
			
		||||
          "GitHub Packages"
 | 
			
		||||
        else
 | 
			
		||||
          odie "Service specified by root_url is not recognized"
 | 
			
		||||
        end
 | 
			
		||||
      puts <<~EOS
 | 
			
		||||
        brew #{bottle_args.join " "}
 | 
			
		||||
        Upload bottles described by these JSON files to #{service}:
 | 
			
		||||
@ -102,38 +134,26 @@ module Homebrew
 | 
			
		||||
      safe_system HOMEBREW_BREW_FILE, *audit_args
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if github_releases?(bottles_hash)
 | 
			
		||||
      # Handle uploading to GitHub Releases.
 | 
			
		||||
      bottles_hash.each_value do |bottle_hash|
 | 
			
		||||
        root_url = bottle_hash["bottle"]["root_url"]
 | 
			
		||||
        url_match = root_url.match HOMEBREW_RELEASES_URL_REGEX
 | 
			
		||||
        _, user, repo, tag = *url_match
 | 
			
		||||
 | 
			
		||||
        # Ensure a release is created.
 | 
			
		||||
        release = begin
 | 
			
		||||
          rel = GitHub.get_release user, repo, tag
 | 
			
		||||
          odebug "Existing GitHub release \"#{tag}\" found"
 | 
			
		||||
          rel
 | 
			
		||||
        rescue GitHub::HTTPNotFoundError
 | 
			
		||||
          odebug "Creating new GitHub release \"#{tag}\""
 | 
			
		||||
          GitHub.create_or_update_release user, repo, tag
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # Upload bottles as release assets.
 | 
			
		||||
        bottle_hash["bottle"]["tags"].each_value do |tag_hash|
 | 
			
		||||
          remote_file = tag_hash["filename"]
 | 
			
		||||
          local_file = tag_hash["local_filename"]
 | 
			
		||||
          odebug "Uploading #{remote_file}"
 | 
			
		||||
          GitHub.upload_release_asset user, repo, release["id"], local_file: local_file, remote_file: remote_file
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      # Handle uploading to Bintray.
 | 
			
		||||
    if internet_archive?(bottles_hash)
 | 
			
		||||
      archive_item = args.archive_item || "homebrew"
 | 
			
		||||
      archive = Archive.new(item: archive_item)
 | 
			
		||||
      archive.upload_bottles(bottles_hash,
 | 
			
		||||
                             warn_on_error: args.warn_on_upload_failure?)
 | 
			
		||||
    elsif bintray?(bottles_hash)
 | 
			
		||||
      bintray_org = args.bintray_org || "homebrew"
 | 
			
		||||
      bintray = Bintray.new(org: bintray_org)
 | 
			
		||||
      bintray.upload_bottles(bottles_hash,
 | 
			
		||||
                             publish_package: !args.no_publish?,
 | 
			
		||||
                             warn_on_error:   args.warn_on_upload_failure?)
 | 
			
		||||
    elsif github_releases?(bottles_hash)
 | 
			
		||||
      github_releases = GitHubReleases.new
 | 
			
		||||
      github_releases.upload_bottles(bottles_hash)
 | 
			
		||||
    elsif github_packages?(bottles_hash)
 | 
			
		||||
      github_org = args.github_org || "homebrew"
 | 
			
		||||
      github_packages = GitHubPackages.new(org: github_org)
 | 
			
		||||
      github_packages.upload_bottles(bottles_hash)
 | 
			
		||||
    else
 | 
			
		||||
      odie "Service specified by root_url is not recognized"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -13,15 +13,19 @@ module Homebrew
 | 
			
		||||
    Homebrew::CLI::Parser.new do
 | 
			
		||||
      description <<~EOS
 | 
			
		||||
        Run Homebrew with a Ruby profiler. For example, `brew prof readall`.
 | 
			
		||||
 | 
			
		||||
        *Note:* Not (yet) working on Apple Silicon.
 | 
			
		||||
      EOS
 | 
			
		||||
      switch "--stackprof",
 | 
			
		||||
             description: "Use `stackprof` instead of `ruby-prof` (the default)."
 | 
			
		||||
 | 
			
		||||
      named_args :command
 | 
			
		||||
      named_args :command, min: 1
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def prof
 | 
			
		||||
    raise UsageError, "not (yet) working on Apple Silicon!" if Hardware::CPU.arm?
 | 
			
		||||
 | 
			
		||||
    args = prof_args.parse
 | 
			
		||||
 | 
			
		||||
    brew_rb = (HOMEBREW_LIBRARY_PATH/"brew.rb").resolved_path
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    begin
 | 
			
		||||
      latest_release = GitHub.get_latest_release "Homebrew", "brew"
 | 
			
		||||
    rescue GitHub::HTTPNotFoundError
 | 
			
		||||
    rescue GitHub::API::HTTPNotFoundError
 | 
			
		||||
      odie "No existing releases found!"
 | 
			
		||||
    end
 | 
			
		||||
    latest_version = Version.new latest_release["tag_name"]
 | 
			
		||||
@ -48,7 +48,7 @@ module Homebrew
 | 
			
		||||
      one_month_ago = Date.today << 1
 | 
			
		||||
      latest_major_minor_release = begin
 | 
			
		||||
        GitHub.get_release "Homebrew", "brew", "#{latest_version.major_minor}.0"
 | 
			
		||||
      rescue GitHub::HTTPNotFoundError
 | 
			
		||||
      rescue GitHub::API::HTTPNotFoundError
 | 
			
		||||
        nil
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -89,7 +89,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    begin
 | 
			
		||||
      release = GitHub.create_or_update_release "Homebrew", "brew", new_version, body: release_notes, draft: true
 | 
			
		||||
    rescue *GitHub::API_ERRORS => e
 | 
			
		||||
    rescue *GitHub::API::ERRORS => e
 | 
			
		||||
      odie "Unable to create release: #{e.message}!"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,6 @@ homebrew-rubocop() {
 | 
			
		||||
  export GEM_HOME
 | 
			
		||||
  export PATH="$GEM_HOME/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
  # Unconditional -W0 to avoid printing e.g.:
 | 
			
		||||
  # warning: parser/current is loading parser/ruby26, which recognizes
 | 
			
		||||
  # warning: 2.6.6-compliant syntax, but you are running 2.6.3.
 | 
			
		||||
  exec "$HOMEBREW_RUBY_PATH" "$RUBY_DISABLE_OPTIONS" -W0 -S rubocop "$@"
 | 
			
		||||
  RUBOCOP="$HOMEBREW_LIBRARY/Homebrew/utils/rubocop.rb"
 | 
			
		||||
  exec "$HOMEBREW_RUBY_PATH" "$RUBOCOP" "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,55 +9,68 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
  module_function
 | 
			
		||||
 | 
			
		||||
  NAMED_TIER_AMOUNT = 100
 | 
			
		||||
  URL_TIER_AMOUNT = 1000
 | 
			
		||||
 | 
			
		||||
  sig { returns(CLI::Parser) }
 | 
			
		||||
  def sponsors_args
 | 
			
		||||
    Homebrew::CLI::Parser.new do
 | 
			
		||||
      description <<~EOS
 | 
			
		||||
        Print a Markdown summary of Homebrew's GitHub Sponsors, suitable for pasting into a README.
 | 
			
		||||
        Update the list of GitHub Sponsors in the `Homebrew/brew` README.
 | 
			
		||||
      EOS
 | 
			
		||||
 | 
			
		||||
      named_args :none
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def sponsor_name(s)
 | 
			
		||||
    s["name"] || s["login"]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def sponsor_logo(s)
 | 
			
		||||
    "https://github.com/#{s["login"]}.png?size=64"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def sponsor_url(s)
 | 
			
		||||
    "https://github.com/#{s["login"]}"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def sponsors
 | 
			
		||||
    sponsors_args.parse
 | 
			
		||||
 | 
			
		||||
    sponsors = {
 | 
			
		||||
      "named" => [],
 | 
			
		||||
      "users" => 0,
 | 
			
		||||
      "orgs"  => 0,
 | 
			
		||||
    }
 | 
			
		||||
    named_sponsors = []
 | 
			
		||||
    logo_sponsors = []
 | 
			
		||||
 | 
			
		||||
    GitHub.sponsors_by_tier("Homebrew").each do |tier|
 | 
			
		||||
      sponsors["named"] += tier["sponsors"] if tier["tier"] >= 100
 | 
			
		||||
      sponsors["users"] += tier["count"]
 | 
			
		||||
      sponsors["orgs"] += tier["sponsors"].count { |s| s["type"] == "organization" }
 | 
			
		||||
      if tier["tier"] >= NAMED_TIER_AMOUNT
 | 
			
		||||
        named_sponsors += tier["sponsors"].map do |s|
 | 
			
		||||
          "[#{sponsor_name(s)}](#{sponsor_url(s)})"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      next if tier["tier"] < URL_TIER_AMOUNT
 | 
			
		||||
 | 
			
		||||
      logo_sponsors += tier["sponsors"].map do |s|
 | 
			
		||||
        "[})](#{sponsor_url(s)})"
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    items = []
 | 
			
		||||
    items += sponsors["named"].map { |s| "[#{s["name"]}](https://github.com/#{s["login"]})" }
 | 
			
		||||
    named_sponsors << "many other users and organisations via [GitHub Sponsors](https://github.com/sponsors/Homebrew)"
 | 
			
		||||
 | 
			
		||||
    anon_users = sponsors["users"] - sponsors["named"].length - sponsors["orgs"]
 | 
			
		||||
    readme = HOMEBREW_REPOSITORY/"README.md"
 | 
			
		||||
    content = readme.read
 | 
			
		||||
    content.gsub!(/(Homebrew is generously supported by) .*\Z/m, "\\1 #{named_sponsors.to_sentence}.\n")
 | 
			
		||||
    content << "\n#{logo_sponsors.join}\n" if logo_sponsors.presence
 | 
			
		||||
 | 
			
		||||
    items << if items.length > 1
 | 
			
		||||
      "#{anon_users} other users"
 | 
			
		||||
    File.open(readme, "w+") { |f| f.write(content) }
 | 
			
		||||
 | 
			
		||||
    diff = system_command "git", args: [
 | 
			
		||||
      "-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "README.md"
 | 
			
		||||
    ]
 | 
			
		||||
    if diff.status.success?
 | 
			
		||||
      puts "No changes to list of sponsors."
 | 
			
		||||
    else
 | 
			
		||||
      "#{anon_users} users"
 | 
			
		||||
      puts "List of sponsors updated in the README."
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if sponsors["orgs"] == 1
 | 
			
		||||
      items << "#{sponsors["orgs"]} organization"
 | 
			
		||||
    elsif sponsors["orgs"] > 1
 | 
			
		||||
      items << "#{sponsors["orgs"]} organizations"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sponsor_text = if items.length > 2
 | 
			
		||||
      items[0..-2].join(", ") + " and #{items.last}"
 | 
			
		||||
    else
 | 
			
		||||
      items.join(" and ")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    puts "Homebrew is generously supported by #{sponsor_text} via [GitHub Sponsors](https://github.com/sponsors/Homebrew)."
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,8 @@ module Homebrew
 | 
			
		||||
      flag   "--pull-label=",
 | 
			
		||||
             description: "Label name for pull requests ready to be pulled (default: `pr-pull`)."
 | 
			
		||||
      flag   "--branch=",
 | 
			
		||||
             description: "Initialize Git repository with the specified branch name (default: `main`)."
 | 
			
		||||
 | 
			
		||||
      conflicts "--no-git", "--branch"
 | 
			
		||||
             description: "Initialize Git repository and setup GitHub Actions workflows with the " \
 | 
			
		||||
                          "specified branch name (default: `main`)."
 | 
			
		||||
 | 
			
		||||
      named_args :tap, number: 1
 | 
			
		||||
    end
 | 
			
		||||
@ -50,11 +49,13 @@ module Homebrew
 | 
			
		||||
      # #{titleized_user} #{titleized_repo}
 | 
			
		||||
 | 
			
		||||
      ## How do I install these formulae?
 | 
			
		||||
 | 
			
		||||
      `brew install #{tap}/<formula>`
 | 
			
		||||
 | 
			
		||||
      Or `brew tap #{tap}` and then `brew install <formula>`.
 | 
			
		||||
 | 
			
		||||
      ## Documentation
 | 
			
		||||
 | 
			
		||||
      `brew help`, `man brew` or check [Homebrew's documentation](https://docs.brew.sh).
 | 
			
		||||
    MARKDOWN
 | 
			
		||||
    write_path(tap, "README.md", readme)
 | 
			
		||||
@ -63,13 +64,14 @@ module Homebrew
 | 
			
		||||
      name: brew test-bot
 | 
			
		||||
      on:
 | 
			
		||||
        push:
 | 
			
		||||
          branches: #{branch}
 | 
			
		||||
          branches:
 | 
			
		||||
            - #{branch}
 | 
			
		||||
        pull_request:
 | 
			
		||||
      jobs:
 | 
			
		||||
        test-bot:
 | 
			
		||||
          strategy:
 | 
			
		||||
            matrix:
 | 
			
		||||
              os: [ubuntu-latest, macOS-latest]
 | 
			
		||||
              os: [ubuntu-latest, macos-latest]
 | 
			
		||||
          runs-on: ${{ matrix.os }}
 | 
			
		||||
          steps:
 | 
			
		||||
            - name: Set up Homebrew
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,8 @@ module Homebrew
 | 
			
		||||
  def test
 | 
			
		||||
    args = test_args.parse
 | 
			
		||||
 | 
			
		||||
    Homebrew.install_bundler_gems!(setup_path: false)
 | 
			
		||||
 | 
			
		||||
    require "formula_assertions"
 | 
			
		||||
    require "formula_free_port"
 | 
			
		||||
 | 
			
		||||
@ -75,10 +77,7 @@ module Homebrew
 | 
			
		||||
      env = ENV.to_hash
 | 
			
		||||
 | 
			
		||||
      begin
 | 
			
		||||
        exec_args = %W[
 | 
			
		||||
          #{RUBY_PATH}
 | 
			
		||||
          #{ENV["HOMEBREW_RUBY_WARNINGS"]}
 | 
			
		||||
          -I #{$LOAD_PATH.join(File::PATH_SEPARATOR)}
 | 
			
		||||
        exec_args = HOMEBREW_RUBY_EXEC_ARGS + %W[
 | 
			
		||||
          --
 | 
			
		||||
          #{HOMEBREW_LIBRARY_PATH}/test.rb
 | 
			
		||||
          #{f.path}
 | 
			
		||||
@ -106,7 +105,7 @@ module Homebrew
 | 
			
		||||
      rescue Exception => e # rubocop:disable Lint/RescueException
 | 
			
		||||
        retry if retry_test?(f, args: args)
 | 
			
		||||
        ofail "#{f.full_name}: failed"
 | 
			
		||||
        puts e, e.backtrace
 | 
			
		||||
        $stderr.puts e, e.backtrace
 | 
			
		||||
      ensure
 | 
			
		||||
        ENV.replace(env)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -78,7 +78,7 @@ module Homebrew
 | 
			
		||||
    elsif args.dependents?
 | 
			
		||||
      formulae = all_formulae = Formula.to_a
 | 
			
		||||
 | 
			
		||||
      @sort = " (sorted by installs in the last 90 days)"
 | 
			
		||||
      @sort = " (sorted by number of dependents)"
 | 
			
		||||
    else
 | 
			
		||||
      formula_installs = {}
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@ module Homebrew
 | 
			
		||||
          nil
 | 
			
		||||
        end
 | 
			
		||||
      end.compact
 | 
			
		||||
      @sort = " (sorted by installs in the last 90 days)"
 | 
			
		||||
      @sort = " (sorted by installs in the last 90 days; top 10,000 only)"
 | 
			
		||||
 | 
			
		||||
      all_formulae = Formula
 | 
			
		||||
    end
 | 
			
		||||
@ -154,20 +154,51 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    formulae.each do |f|
 | 
			
		||||
      name = f.name.downcase
 | 
			
		||||
      if f.bottle_specification.tag?(@bottle_tag)
 | 
			
		||||
      if f.bottle_specification.tag?(@bottle_tag, exact: true)
 | 
			
		||||
        puts "#{Tty.bold}#{Tty.green}#{name}#{Tty.reset}: already bottled" if any_named_args
 | 
			
		||||
        next
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      requirement_classes = f.recursive_requirements.map(&:class)
 | 
			
		||||
      if f.disabled?
 | 
			
		||||
        puts "#{Tty.bold}#{Tty.green}#{name}#{Tty.reset}: formula disabled" if any_named_args
 | 
			
		||||
        next
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      requirements = f.recursive_requirements
 | 
			
		||||
      if @bottle_tag.to_s.end_with?("_linux")
 | 
			
		||||
        if requirement_classes.include?(MacOSRequirement)
 | 
			
		||||
        if requirements.any?(MacOSRequirement)
 | 
			
		||||
          puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: requires macOS" if any_named_args
 | 
			
		||||
          next
 | 
			
		||||
        end
 | 
			
		||||
      elsif requirement_classes.include?(LinuxRequirement)
 | 
			
		||||
      elsif requirements.any?(LinuxRequirement)
 | 
			
		||||
        puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: requires Linux" if any_named_args
 | 
			
		||||
        next
 | 
			
		||||
      else
 | 
			
		||||
        macos_version = MacOS::Version.from_symbol(@bottle_tag)
 | 
			
		||||
        macos_satisfied = requirements.all? do |r|
 | 
			
		||||
          case r
 | 
			
		||||
          when MacOSRequirement
 | 
			
		||||
            next true unless r.version_specified?
 | 
			
		||||
 | 
			
		||||
            macos_version.public_send(r.comparator, r.version)
 | 
			
		||||
          when XcodeRequirement
 | 
			
		||||
            next true unless r.version
 | 
			
		||||
 | 
			
		||||
            Version.new(MacOS::Xcode.latest_version(macos: macos_version)) >= r.version
 | 
			
		||||
          when ArchRequirement
 | 
			
		||||
            arch = r.arch
 | 
			
		||||
            arch = :intel if arch == :x86_64
 | 
			
		||||
            arch = :arm64 if arch == :arm
 | 
			
		||||
 | 
			
		||||
            arch == macos_version.arch
 | 
			
		||||
          else
 | 
			
		||||
            true
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
        unless macos_satisfied
 | 
			
		||||
          puts "#{Tty.bold}#{Tty.red}#{name}#{Tty.reset}: doesn't support this macOS" if any_named_args
 | 
			
		||||
          next
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if f.bottle_unneeded? || f.bottle_disabled?
 | 
			
		||||
@ -181,7 +212,7 @@ module Homebrew
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      deps = Array(deps_hash[f.name]).reject do |dep|
 | 
			
		||||
        dep.bottle_specification.tag?(@bottle_tag) || dep.bottle_unneeded?
 | 
			
		||||
        dep.bottle_specification.tag?(@bottle_tag, exact: true) || dep.bottle_unneeded?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if deps.blank?
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    formulae = args.named.to_formulae
 | 
			
		||||
 | 
			
		||||
    if dir = args.destdir
 | 
			
		||||
    if (dir = args.destdir)
 | 
			
		||||
      unpack_dir = Pathname.new(dir).expand_path
 | 
			
		||||
      unpack_dir.mkpath
 | 
			
		||||
    else
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
 | 
			
		||||
require "cli/parser"
 | 
			
		||||
require "utils/github"
 | 
			
		||||
require "dev-cmd/man"
 | 
			
		||||
 | 
			
		||||
module Homebrew
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
@ -61,7 +62,8 @@ module Homebrew
 | 
			
		||||
    if diff.status.success?
 | 
			
		||||
      puts "No changes to list of maintainers."
 | 
			
		||||
    else
 | 
			
		||||
      puts "List of maintainers updated in README."
 | 
			
		||||
      Homebrew.regenerate_man_pages(preserve_date: true, quiet: true)
 | 
			
		||||
      puts "List of maintainers updated in the README and the generated man pages."
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -54,9 +54,9 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
    start_commit, end_commit = nil
 | 
			
		||||
    cd HOMEBREW_REPOSITORY do
 | 
			
		||||
      start_commit = if commit = args.commit
 | 
			
		||||
      start_commit = if (commit = args.commit)
 | 
			
		||||
        commit
 | 
			
		||||
      elsif date = args.before
 | 
			
		||||
      elsif (date = args.before)
 | 
			
		||||
        Utils.popen_read("git", "rev-list", "-n1", "--before=#{date}", "origin/master").chomp
 | 
			
		||||
      elsif args.to_tag?
 | 
			
		||||
        tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname")
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ class DevelopmentTools
 | 
			
		||||
    def clang_version
 | 
			
		||||
      @clang_version ||= begin
 | 
			
		||||
        if (path = locate("clang")) &&
 | 
			
		||||
           build_version = `#{path} --version`[/(?:clang|LLVM) version (\d+\.\d)/, 1]
 | 
			
		||||
           (build_version = `#{path} --version`[/(?:clang|LLVM) version (\d+\.\d)/, 1])
 | 
			
		||||
          Version.new build_version
 | 
			
		||||
        else
 | 
			
		||||
          Version::NULL
 | 
			
		||||
@ -54,7 +54,7 @@ class DevelopmentTools
 | 
			
		||||
    def clang_build_version
 | 
			
		||||
      @clang_build_version ||= begin
 | 
			
		||||
        if (path = locate("clang")) &&
 | 
			
		||||
           build_version = `#{path} --version`[%r{clang(-| version [^ ]+ \(tags/RELEASE_)(\d{2,})}, 2]
 | 
			
		||||
           (build_version = `#{path} --version`[%r{clang(-| version [^ ]+ \(tags/RELEASE_)(\d{2,})}, 2])
 | 
			
		||||
          Version.new build_version
 | 
			
		||||
        else
 | 
			
		||||
          Version::NULL
 | 
			
		||||
@ -66,7 +66,7 @@ class DevelopmentTools
 | 
			
		||||
      @llvm_clang_build_version ||= begin
 | 
			
		||||
        path = Formulary.factory("llvm").opt_prefix/"bin/clang"
 | 
			
		||||
        if path.executable? &&
 | 
			
		||||
           build_version = `#{path} --version`[/clang version (\d\.\d\.\d)/, 1]
 | 
			
		||||
           (build_version = `#{path} --version`[/clang version (\d+\.\d\.\d)/, 1])
 | 
			
		||||
          Version.new build_version
 | 
			
		||||
        else
 | 
			
		||||
          Version::NULL
 | 
			
		||||
@ -76,10 +76,10 @@ class DevelopmentTools
 | 
			
		||||
 | 
			
		||||
    def non_apple_gcc_version(cc)
 | 
			
		||||
      (@non_apple_gcc_version ||= {}).fetch(cc) do
 | 
			
		||||
        path = HOMEBREW_PREFIX/"opt/gcc/bin"/cc
 | 
			
		||||
        path = HOMEBREW_PREFIX/"opt/#{CompilerSelector.preferred_gcc}/bin"/cc
 | 
			
		||||
        path = locate(cc) unless path.exist?
 | 
			
		||||
        version = if path &&
 | 
			
		||||
                     build_version = `#{path} --version`[/gcc(?:(?:-\d+(?:\.\d)?)? \(.+\))? (\d+\.\d\.\d)/, 1]
 | 
			
		||||
                     (build_version = `#{path} --version`[/gcc(?:(?:-\d+(?:\.\d)?)? \(.+\))? (\d+\.\d\.\d)/, 1])
 | 
			
		||||
          Version.new build_version
 | 
			
		||||
        else
 | 
			
		||||
          Version::NULL
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,11 @@ require "mechanize/http/content_disposition_parser"
 | 
			
		||||
 | 
			
		||||
require "utils/curl"
 | 
			
		||||
 | 
			
		||||
require "github_packages"
 | 
			
		||||
 | 
			
		||||
require "extend/time"
 | 
			
		||||
using TimeRemaining
 | 
			
		||||
 | 
			
		||||
# @abstract Abstract superclass for all download strategies.
 | 
			
		||||
#
 | 
			
		||||
# @api private
 | 
			
		||||
@ -50,7 +55,7 @@ class AbstractDownloadStrategy
 | 
			
		||||
  # Download and cache the resource at {#cached_location}.
 | 
			
		||||
  #
 | 
			
		||||
  # @api public
 | 
			
		||||
  def fetch; end
 | 
			
		||||
  def fetch(timeout: nil); end
 | 
			
		||||
 | 
			
		||||
  # Disable any output during downloading.
 | 
			
		||||
  #
 | 
			
		||||
@ -66,30 +71,38 @@ class AbstractDownloadStrategy
 | 
			
		||||
    Context.current.quiet? || @quiet
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Unpack {#cached_location} into the current working directory, and possibly
 | 
			
		||||
  # chdir into the newly-unpacked directory.
 | 
			
		||||
  # Unlike {Resource#stage}, this does not take a block.
 | 
			
		||||
  # Unpack {#cached_location} into the current working directory.
 | 
			
		||||
  #
 | 
			
		||||
  # Additionally, if a block is given, the working directory was previously empty
 | 
			
		||||
  # and a single directory is extracted from the archive, the block will be called
 | 
			
		||||
  # with the working directory changed to that directory. Otherwise this method
 | 
			
		||||
  # will return, or the block will be called, without changing the current working
 | 
			
		||||
  # directory.
 | 
			
		||||
  #
 | 
			
		||||
  # @api public
 | 
			
		||||
  def stage
 | 
			
		||||
  def stage(&block)
 | 
			
		||||
    UnpackStrategy.detect(cached_location,
 | 
			
		||||
                          prioritise_extension: true,
 | 
			
		||||
                          ref_type: @ref_type, ref: @ref)
 | 
			
		||||
                  .extract_nestedly(basename:             basename,
 | 
			
		||||
                                    prioritise_extension: true,
 | 
			
		||||
                                    verbose:              verbose? && !quiet?)
 | 
			
		||||
    chdir
 | 
			
		||||
    chdir(&block) if block
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def chdir
 | 
			
		||||
  def chdir(&block)
 | 
			
		||||
    entries = Dir["*"]
 | 
			
		||||
    raise "Empty archive" if entries.length.zero?
 | 
			
		||||
    return if entries.length != 1
 | 
			
		||||
 | 
			
		||||
    begin
 | 
			
		||||
      Dir.chdir entries.first
 | 
			
		||||
    rescue
 | 
			
		||||
      nil
 | 
			
		||||
    if entries.length != 1
 | 
			
		||||
      yield
 | 
			
		||||
      return
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if File.directory? entries.first
 | 
			
		||||
      Dir.chdir(entries.first, &block)
 | 
			
		||||
    else
 | 
			
		||||
      yield
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  private :chdir
 | 
			
		||||
@ -166,18 +179,20 @@ class VCSDownloadStrategy < AbstractDownloadStrategy
 | 
			
		||||
  # Download and cache the repository at {#cached_location}.
 | 
			
		||||
  #
 | 
			
		||||
  # @api public
 | 
			
		||||
  def fetch
 | 
			
		||||
  def fetch(timeout: nil)
 | 
			
		||||
    end_time = Time.now + timeout if timeout
 | 
			
		||||
 | 
			
		||||
    ohai "Cloning #{url}"
 | 
			
		||||
 | 
			
		||||
    if cached_location.exist? && repo_valid?
 | 
			
		||||
      puts "Updating #{cached_location}"
 | 
			
		||||
      update
 | 
			
		||||
      update(timeout: timeout)
 | 
			
		||||
    elsif cached_location.exist?
 | 
			
		||||
      puts "Removing invalid repository from cache"
 | 
			
		||||
      clear_cache
 | 
			
		||||
      clone_repo
 | 
			
		||||
      clone_repo(timeout: end_time)
 | 
			
		||||
    else
 | 
			
		||||
      clone_repo
 | 
			
		||||
      clone_repo(timeout: end_time)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    version.update_commit(last_commit) if head?
 | 
			
		||||
@ -222,9 +237,11 @@ class VCSDownloadStrategy < AbstractDownloadStrategy
 | 
			
		||||
    raise NotImplementedError
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo; end
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil); end
 | 
			
		||||
 | 
			
		||||
  def update; end
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update(timeout: nil); end
 | 
			
		||||
 | 
			
		||||
  def current_revision; end
 | 
			
		||||
 | 
			
		||||
@ -303,7 +320,7 @@ class AbstractFileDownloadStrategy < AbstractDownloadStrategy
 | 
			
		||||
        query_params = CGI.parse(uri.query)
 | 
			
		||||
        query_params["response-content-disposition"].each do |param|
 | 
			
		||||
          query_basename = param[/attachment;\s*filename=(["']?)(.+)\1/i, 2]
 | 
			
		||||
          return query_basename if query_basename
 | 
			
		||||
          return File.basename(query_basename) if query_basename
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -343,7 +360,9 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
  # Download and cache the file at {#cached_location}.
 | 
			
		||||
  #
 | 
			
		||||
  # @api public
 | 
			
		||||
  def fetch
 | 
			
		||||
  def fetch(timeout: nil)
 | 
			
		||||
    end_time = Time.now + timeout if timeout
 | 
			
		||||
 | 
			
		||||
    download_lock = LockFile.new(temporary_path.basename)
 | 
			
		||||
    download_lock.lock
 | 
			
		||||
 | 
			
		||||
@ -354,7 +373,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
 | 
			
		||||
      ohai "Downloading #{url}"
 | 
			
		||||
 | 
			
		||||
      resolved_url, _, url_time, = resolve_url_basename_time_file_size(url)
 | 
			
		||||
      resolved_url, _, url_time, = resolve_url_basename_time_file_size(url, timeout: end_time&.remaining!)
 | 
			
		||||
 | 
			
		||||
      fresh = if cached_location.exist? && url_time
 | 
			
		||||
        url_time <= cached_location.mtime
 | 
			
		||||
@ -368,7 +387,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
        puts "Already downloaded: #{cached_location}"
 | 
			
		||||
      else
 | 
			
		||||
        begin
 | 
			
		||||
          _fetch(url: url, resolved_url: resolved_url)
 | 
			
		||||
          _fetch(url: url, resolved_url: resolved_url, timeout: end_time&.remaining!)
 | 
			
		||||
        rescue ErrorDuringExecution
 | 
			
		||||
          raise CurlDownloadStrategyError, url
 | 
			
		||||
        end
 | 
			
		||||
@ -385,6 +404,8 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
 | 
			
		||||
      puts "Trying a mirror..."
 | 
			
		||||
      retry
 | 
			
		||||
    rescue Timeout::Error => e
 | 
			
		||||
      raise Timeout::Error, "Timed out downloading #{self.url}: #{e}"
 | 
			
		||||
    end
 | 
			
		||||
  ensure
 | 
			
		||||
    download_lock&.unlock
 | 
			
		||||
@ -396,19 +417,19 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
    rm_rf(temporary_path)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def resolved_time_file_size
 | 
			
		||||
    _, _, time, file_size = resolve_url_basename_time_file_size(url)
 | 
			
		||||
  def resolved_time_file_size(timeout: nil)
 | 
			
		||||
    _, _, time, file_size = resolve_url_basename_time_file_size(url, timeout: timeout)
 | 
			
		||||
    [time, file_size]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def resolved_url_and_basename
 | 
			
		||||
    resolved_url, basename, = resolve_url_basename_time_file_size(url)
 | 
			
		||||
  def resolved_url_and_basename(timeout: nil)
 | 
			
		||||
    resolved_url, basename, = resolve_url_basename_time_file_size(url, timeout: nil)
 | 
			
		||||
    [resolved_url, basename]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def resolve_url_basename_time_file_size(url)
 | 
			
		||||
  def resolve_url_basename_time_file_size(url, timeout: nil)
 | 
			
		||||
    @resolved_info_cache ||= {}
 | 
			
		||||
    return @resolved_info_cache[url] if @resolved_info_cache.include?(url)
 | 
			
		||||
 | 
			
		||||
@ -416,7 +437,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
      url = url.sub(%r{^((ht|f)tps?://)?}, "#{domain.chomp("/")}/")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    out, _, status= curl_output("--location", "--silent", "--head", "--request", "GET", url.to_s)
 | 
			
		||||
    out, _, status= curl_output("--location", "--silent", "--head", "--request", "GET", url.to_s, timeout: timeout)
 | 
			
		||||
 | 
			
		||||
    lines = status.success? ? out.lines.map(&:chomp) : []
 | 
			
		||||
 | 
			
		||||
@ -441,16 +462,19 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
    content_disposition_parser = Mechanize::HTTP::ContentDispositionParser.new
 | 
			
		||||
 | 
			
		||||
    parse_content_disposition = lambda do |line|
 | 
			
		||||
      next unless content_disposition = content_disposition_parser.parse(line.sub(/; *$/, ""), true)
 | 
			
		||||
      next unless (content_disposition = content_disposition_parser.parse(line.sub(/; *$/, ""), true))
 | 
			
		||||
 | 
			
		||||
      filename = nil
 | 
			
		||||
 | 
			
		||||
      if filename_with_encoding = content_disposition.parameters["filename*"]
 | 
			
		||||
      if (filename_with_encoding = content_disposition.parameters["filename*"])
 | 
			
		||||
        encoding, encoded_filename = filename_with_encoding.split("''", 2)
 | 
			
		||||
        filename = URI.decode_www_form_component(encoded_filename).encode(encoding) if encoding && encoded_filename
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      filename || content_disposition.filename
 | 
			
		||||
      # Servers may include '/' in their Content-Disposition filename header. Take only the basename of this, because:
 | 
			
		||||
      # - Unpacking code assumes this is a single file - not something living in a subdirectory.
 | 
			
		||||
      # - Directory traversal attacks are possible without limiting this to just the basename.
 | 
			
		||||
      File.basename(filename || content_disposition.filename)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    filenames = lines.map(&parse_content_disposition).compact
 | 
			
		||||
@ -472,7 +496,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
    @resolved_info_cache[url] = [redirect_url, basename, time, file_size]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def _fetch(url:, resolved_url:)
 | 
			
		||||
  def _fetch(url:, resolved_url:, timeout:)
 | 
			
		||||
    ohai "Downloading from #{resolved_url}" if url != resolved_url
 | 
			
		||||
 | 
			
		||||
    if Homebrew::EnvConfig.no_insecure_redirect? &&
 | 
			
		||||
@ -481,7 +505,7 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
      raise CurlDownloadStrategyError, url
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    curl_download resolved_url, to: temporary_path
 | 
			
		||||
    curl_download resolved_url, to: temporary_path, timeout: timeout
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Curl options to be always passed to curl,
 | 
			
		||||
@ -516,6 +540,25 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Strategy for downloading a file from an GitHub Packages URL.
 | 
			
		||||
#
 | 
			
		||||
# @api public
 | 
			
		||||
class CurlGitHubPackagesDownloadStrategy < CurlDownloadStrategy
 | 
			
		||||
  attr_accessor :checksum, :name
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def _fetch(url:, resolved_url:)
 | 
			
		||||
    raise "Empty checksum" if checksum.blank?
 | 
			
		||||
    raise "Empty name" if name.blank?
 | 
			
		||||
 | 
			
		||||
    _, org, repo, = *url.match(GitHubPackages::URL_REGEX)
 | 
			
		||||
 | 
			
		||||
    blob_url = "https://ghcr.io/v2/#{org}/#{repo}/#{name}/blobs/sha256:#{checksum}"
 | 
			
		||||
    curl_download(blob_url, "--header", "Authorization: Bearer", to: temporary_path)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Strategy for downloading a file from an Apache Mirror URL.
 | 
			
		||||
#
 | 
			
		||||
# @api public
 | 
			
		||||
@ -535,9 +578,9 @@ class CurlApacheMirrorDownloadStrategy < CurlDownloadStrategy
 | 
			
		||||
    @combined_mirrors = [*@mirrors, *backup_mirrors]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def resolve_url_basename_time_file_size(url)
 | 
			
		||||
  def resolve_url_basename_time_file_size(url, timeout: nil)
 | 
			
		||||
    if url == self.url
 | 
			
		||||
      super("#{apache_mirrors["preferred"]}#{apache_mirrors["path_info"]}")
 | 
			
		||||
      super("#{apache_mirrors["preferred"]}#{apache_mirrors["path_info"]}", timeout: timeout)
 | 
			
		||||
    else
 | 
			
		||||
      super
 | 
			
		||||
    end
 | 
			
		||||
@ -560,7 +603,7 @@ end
 | 
			
		||||
class CurlPostDownloadStrategy < CurlDownloadStrategy
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def _fetch(url:, resolved_url:)
 | 
			
		||||
  def _fetch(url:, resolved_url:, timeout:)
 | 
			
		||||
    args = if meta.key?(:data)
 | 
			
		||||
      escape_data = ->(d) { ["-d", URI.encode_www_form([d])] }
 | 
			
		||||
      [url, *meta[:data].flat_map(&escape_data)]
 | 
			
		||||
@ -569,7 +612,7 @@ class CurlPostDownloadStrategy < CurlDownloadStrategy
 | 
			
		||||
      query.nil? ? [url, "-X", "POST"] : [url, "-d", query]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    curl_download(*args, to: temporary_path)
 | 
			
		||||
    curl_download(*args, to: temporary_path, timeout: timeout)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -582,6 +625,7 @@ class NoUnzipCurlDownloadStrategy < CurlDownloadStrategy
 | 
			
		||||
    UnpackStrategy::Uncompressed.new(cached_location)
 | 
			
		||||
                                .extract(basename: basename,
 | 
			
		||||
                                         verbose:  verbose? && !quiet?)
 | 
			
		||||
    yield if block_given?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -608,7 +652,7 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
  # Download and cache the repository at {#cached_location}.
 | 
			
		||||
  #
 | 
			
		||||
  # @api public
 | 
			
		||||
  def fetch
 | 
			
		||||
  def fetch(timeout: nil)
 | 
			
		||||
    if @url.chomp("/") != repo_url || !silent_command("svn", args: ["switch", @url, cached_location]).success?
 | 
			
		||||
      clear_cache
 | 
			
		||||
    end
 | 
			
		||||
@ -649,7 +693,11 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fetch_repo(target, url, revision = nil, ignore_externals: false)
 | 
			
		||||
  sig {
 | 
			
		||||
    params(target: Pathname, url: String, revision: T.nilable(String), ignore_externals: T::Boolean,
 | 
			
		||||
   timeout: T.nilable(Time)).void
 | 
			
		||||
  }
 | 
			
		||||
  def fetch_repo(target, url, revision = nil, ignore_externals: false, timeout: nil)
 | 
			
		||||
    # Use "svn update" when the repository already exists locally.
 | 
			
		||||
    # This saves on bandwidth and will have a similar effect to verifying the
 | 
			
		||||
    # cache as it will make any changes to get the right revision.
 | 
			
		||||
@ -669,9 +717,9 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if target.directory?
 | 
			
		||||
      command!("svn", args: ["update", *args], chdir: target.to_s)
 | 
			
		||||
      command! "svn", args: ["update", *args], chdir: target.to_s, timeout: timeout&.remaining
 | 
			
		||||
    else
 | 
			
		||||
      command!("svn", args: ["checkout", url, target, *args])
 | 
			
		||||
      command! "svn", args: ["checkout", url, target, *args], timeout: timeout&.remaining
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -684,20 +732,22 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    (cached_location/".svn").directory?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil)
 | 
			
		||||
    case @ref_type
 | 
			
		||||
    when :revision
 | 
			
		||||
      fetch_repo cached_location, @url, @ref
 | 
			
		||||
      fetch_repo cached_location, @url, @ref, timeout: timeout
 | 
			
		||||
    when :revisions
 | 
			
		||||
      # nil is OK for main_revision, as fetch_repo will then get latest
 | 
			
		||||
      main_revision = @ref[:trunk]
 | 
			
		||||
      fetch_repo cached_location, @url, main_revision, ignore_externals: true
 | 
			
		||||
      fetch_repo cached_location, @url, main_revision, ignore_externals: true, timeout: timeout
 | 
			
		||||
 | 
			
		||||
      externals do |external_name, external_url|
 | 
			
		||||
        fetch_repo cached_location/external_name, external_url, @ref[external_name], ignore_externals: true
 | 
			
		||||
        fetch_repo cached_location/external_name, external_url, @ref[external_name], ignore_externals: true,
 | 
			
		||||
                                                                                     timeout:          timeout
 | 
			
		||||
      end
 | 
			
		||||
    else
 | 
			
		||||
      fetch_repo cached_location, @url
 | 
			
		||||
      fetch_repo cached_location, @url, timeout: timeout
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  alias update clone_repo
 | 
			
		||||
@ -746,12 +796,13 @@ class GitDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    0
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update(timeout: nil)
 | 
			
		||||
    config_repo
 | 
			
		||||
    update_repo
 | 
			
		||||
    checkout
 | 
			
		||||
    update_repo(timeout: timeout)
 | 
			
		||||
    checkout(timeout: timeout)
 | 
			
		||||
    reset
 | 
			
		||||
    update_submodules if submodules?
 | 
			
		||||
    update_submodules(timeout: timeout) if submodules?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def shallow_clone?
 | 
			
		||||
@ -812,6 +863,7 @@ class GitDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { void }
 | 
			
		||||
  def config_repo
 | 
			
		||||
    command! "git",
 | 
			
		||||
             args:  ["config", "remote.origin.url", @url],
 | 
			
		||||
@ -824,35 +876,42 @@ class GitDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update_repo
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update_repo(timeout: nil)
 | 
			
		||||
    return if @ref_type != :branch && ref?
 | 
			
		||||
 | 
			
		||||
    if !shallow_clone? && shallow_dir?
 | 
			
		||||
      command! "git",
 | 
			
		||||
               args:  ["fetch", "origin", "--unshallow"],
 | 
			
		||||
               chdir: cached_location
 | 
			
		||||
               args:    ["fetch", "origin", "--unshallow"],
 | 
			
		||||
               chdir:   cached_location,
 | 
			
		||||
               timeout: timeout&.remaining
 | 
			
		||||
    else
 | 
			
		||||
      command! "git",
 | 
			
		||||
               args:  ["fetch", "origin"],
 | 
			
		||||
               chdir: cached_location
 | 
			
		||||
               args:    ["fetch", "origin"],
 | 
			
		||||
               chdir:   cached_location,
 | 
			
		||||
               timeout: timeout&.remaining
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo
 | 
			
		||||
    command! "git", args: clone_args
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil)
 | 
			
		||||
    command! "git", args: clone_args, timeout: timeout&.remaining
 | 
			
		||||
 | 
			
		||||
    command! "git",
 | 
			
		||||
             args:  ["config", "homebrew.cacheversion", cache_version],
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
    checkout
 | 
			
		||||
    update_submodules if submodules?
 | 
			
		||||
             args:    ["config", "homebrew.cacheversion", cache_version],
 | 
			
		||||
             chdir:   cached_location,
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
    checkout(timeout: timeout)
 | 
			
		||||
    update_submodules(timeout: timeout) if submodules?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def checkout
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def checkout(timeout: nil)
 | 
			
		||||
    ohai "Checking out #{@ref_type} #{@ref}" if @ref_type && @ref
 | 
			
		||||
    command! "git", args: ["checkout", "-f", @ref, "--"], chdir: cached_location
 | 
			
		||||
    command! "git", args: ["checkout", "-f", @ref, "--"], chdir: cached_location, timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { void }
 | 
			
		||||
  def reset
 | 
			
		||||
    ref = case @ref_type
 | 
			
		||||
    when :branch
 | 
			
		||||
@ -866,13 +925,16 @@ class GitDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update_submodules
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update_submodules(timeout: nil)
 | 
			
		||||
    command! "git",
 | 
			
		||||
             args:  ["submodule", "foreach", "--recursive", "git submodule sync"],
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
             args:    ["submodule", "foreach", "--recursive", "git submodule sync"],
 | 
			
		||||
             chdir:   cached_location,
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
    command! "git",
 | 
			
		||||
             args:  ["submodule", "update", "--init", "--recursive"],
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
             args:    ["submodule", "update", "--init", "--recursive"],
 | 
			
		||||
             chdir:   cached_location,
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
    fix_absolute_submodule_gitdir_references!
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -1024,23 +1086,27 @@ class CVSDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    "-Q" unless verbose?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil)
 | 
			
		||||
    # Login is only needed (and allowed) with pserver; skip for anoncvs.
 | 
			
		||||
    command! "cvs", args: [*quiet_flag, "-d", @url, "login"] if @url.include? "pserver"
 | 
			
		||||
    command! "cvs", args: [*quiet_flag, "-d", @url, "login"], timeout: timeout&.remaining if @url.include? "pserver"
 | 
			
		||||
 | 
			
		||||
    command! "cvs",
 | 
			
		||||
             args:  [*quiet_flag, "-d", @url, "checkout", "-d", cached_location.basename, @module],
 | 
			
		||||
             chdir: cached_location.dirname
 | 
			
		||||
             args:    [*quiet_flag, "-d", @url, "checkout", "-d", cached_location.basename, @module],
 | 
			
		||||
             chdir:   cached_location.dirname,
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update(timeout: nil)
 | 
			
		||||
    command! "cvs",
 | 
			
		||||
             args:  [*quiet_flag, "update"],
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
             args:    [*quiet_flag, "update"],
 | 
			
		||||
             chdir:   cached_location,
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def split_url(in_url)
 | 
			
		||||
    parts = in_url.split(/:/)
 | 
			
		||||
    parts = in_url.split(":")
 | 
			
		||||
    mod = parts.pop
 | 
			
		||||
    url = parts.join(":")
 | 
			
		||||
    [mod, url]
 | 
			
		||||
@ -1088,12 +1154,14 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    (cached_location/".hg").directory?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo
 | 
			
		||||
    command! "hg", args: ["clone", @url, cached_location]
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil)
 | 
			
		||||
    command! "hg", args: ["clone", @url, cached_location], timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
    command! "hg", args: ["--cwd", cached_location, "pull", "--update"]
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update(timeout: nil)
 | 
			
		||||
    command! "hg", args: ["--cwd", cached_location, "pull", "--update"], timeout: timeout&.remaining
 | 
			
		||||
 | 
			
		||||
    update_args = if @ref_type && @ref
 | 
			
		||||
      ohai "Checking out #{@ref_type} #{@ref}"
 | 
			
		||||
@ -1102,7 +1170,7 @@ class MercurialDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
      ["--clean"]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    command! "hg", args: ["--cwd", cached_location, "update", *update_args]
 | 
			
		||||
    command! "hg", args: ["--cwd", cached_location, "update", *update_args], timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1151,16 +1219,20 @@ class BazaarDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    (cached_location/".bzr").directory?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil)
 | 
			
		||||
    # "lightweight" means history-less
 | 
			
		||||
    command! "bzr",
 | 
			
		||||
             args: ["checkout", "--lightweight", @url, cached_location]
 | 
			
		||||
             args:    ["checkout", "--lightweight", @url, cached_location],
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update(timeout: nil)
 | 
			
		||||
    command! "bzr",
 | 
			
		||||
             args:  ["update"],
 | 
			
		||||
             chdir: cached_location
 | 
			
		||||
             args:    ["update"],
 | 
			
		||||
             chdir:   cached_location,
 | 
			
		||||
             timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1203,12 +1275,14 @@ class FossilDownloadStrategy < VCSDownloadStrategy
 | 
			
		||||
    "fossil"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clone_repo
 | 
			
		||||
    silent_command!("fossil", args: ["clone", @url, cached_location])
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def clone_repo(timeout: nil)
 | 
			
		||||
    silent_command! "fossil", args: ["clone", @url, cached_location], timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update
 | 
			
		||||
    silent_command!("fossil", args: ["pull", "-R", cached_location])
 | 
			
		||||
  sig { params(timeout: T.nilable(Time)).void }
 | 
			
		||||
  def update(timeout: nil)
 | 
			
		||||
    silent_command! "fossil", args: ["pull", "-R", cached_location], timeout: timeout&.remaining
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1231,6 +1305,8 @@ class DownloadStrategyDetector
 | 
			
		||||
 | 
			
		||||
  def self.detect_from_url(url)
 | 
			
		||||
    case url
 | 
			
		||||
    when GitHubPackages::URL_REGEX
 | 
			
		||||
      CurlGitHubPackagesDownloadStrategy
 | 
			
		||||
    when %r{^https?://github\.com/[^/]+/[^/]+\.git$}
 | 
			
		||||
      GitHubGitDownloadStrategy
 | 
			
		||||
    when %r{^https?://.+\.git$},
 | 
			
		||||
 | 
			
		||||
@ -46,8 +46,12 @@ module Homebrew
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_BOTTLE_DOMAIN:                 {
 | 
			
		||||
        description:  "Use this URL as the download mirror for bottles. " \
 | 
			
		||||
                      "If bottles at that URL are temporarily unavailable, " \
 | 
			
		||||
                      "the default bottle domain will be used as a fallback mirror. " \
 | 
			
		||||
                      "For example, `HOMEBREW_BOTTLE_DOMAIN=http://localhost:8080` will cause all bottles to " \
 | 
			
		||||
                      "download from the prefix `http://localhost:8080/`.",
 | 
			
		||||
                      "download from the prefix `http://localhost:8080/`. " \
 | 
			
		||||
                      "If bottles are not available at `HOMEBREW_BOTTLE_DOMAIN` " \
 | 
			
		||||
                      "they will be downloaded from the default bottle domain.",
 | 
			
		||||
        default_text: "macOS: `https://homebrew.bintray.com/`, " \
 | 
			
		||||
                      "Linux: `https://linuxbrew.bintray.com/`.",
 | 
			
		||||
        default:      HOMEBREW_BOTTLE_DEFAULT_DOMAIN,
 | 
			
		||||
@ -168,6 +172,13 @@ module Homebrew
 | 
			
		||||
                     "\n\n    *Note:* Homebrew doesn't require permissions for any of the scopes, but some " \
 | 
			
		||||
                     "developer commands may require additional permissions.",
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_GITHUB_PACKAGES_TOKEN:         {
 | 
			
		||||
        description: "Use this GitHub personal access token when accessing the GitHub Packages Registry "\
 | 
			
		||||
                     "(where bottles may be stored).",
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_GITHUB_PACKAGES_USER:          {
 | 
			
		||||
        description: "Use this username when accessing the GitHub Packages Registry (where bottles may be stored).",
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_GIT_EMAIL:                     {
 | 
			
		||||
        description: "Set the Git author and committer email to this value.",
 | 
			
		||||
      },
 | 
			
		||||
@ -179,6 +190,10 @@ module Homebrew
 | 
			
		||||
        default_text: 'The "Beer Mug" emoji.',
 | 
			
		||||
        default:      "🍺",
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_INTERNET_ARCHIVE_KEY:          {
 | 
			
		||||
        description: "Use this API key when accessing the Internet Archive S3 API, where bottles are stored. " \
 | 
			
		||||
                     "The format is access:secret. See https://archive.org/account/s3.php",
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_LIVECHECK_WATCHLIST:           {
 | 
			
		||||
        description: "Consult this file for the list of formulae to check by default when no formula argument " \
 | 
			
		||||
                     "is passed to `brew livecheck`.",
 | 
			
		||||
@ -204,19 +219,14 @@ module Homebrew
 | 
			
		||||
        boolean:     true,
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_NO_AUTO_UPDATE:                {
 | 
			
		||||
        description: "If set, do not automatically update before running " \
 | 
			
		||||
                     "`brew install`, `brew upgrade` or `brew tap`.",
 | 
			
		||||
        description: "If set, do not automatically update before running some commands e.g. " \
 | 
			
		||||
                     "`brew install`, `brew upgrade` and `brew tap`.",
 | 
			
		||||
        boolean:     true,
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_NO_BOOTSNAP:                   {
 | 
			
		||||
        description: "If set, do not use Bootsnap to speed up repeated `brew` calls.",
 | 
			
		||||
        boolean:     true,
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK:     {
 | 
			
		||||
        description: "If set, fail on the failure of installation from a bottle rather than " \
 | 
			
		||||
                     "falling back to building from source.",
 | 
			
		||||
        boolean:     true,
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: {
 | 
			
		||||
        description: "If set, do not check for broken dependents after installing, upgrading or reinstalling " \
 | 
			
		||||
                     "formulae.",
 | 
			
		||||
@ -257,6 +267,11 @@ module Homebrew
 | 
			
		||||
        description: "If set, use Pry for the `brew irb` command.",
 | 
			
		||||
        boolean:     true,
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_SIMULATE_MACOS_ON_LINUX:       {
 | 
			
		||||
        description: "If set, running Homebrew on Linux will simulate certain macOS code paths. This is useful " \
 | 
			
		||||
                     "when auditing macOS formulae while on Linux. Implies `HOMEBREW_FORCE_HOMEBREW_ON_LINUX`.",
 | 
			
		||||
        boolean:     true,
 | 
			
		||||
      },
 | 
			
		||||
      HOMEBREW_SKIP_OR_LATER_BOTTLES:         {
 | 
			
		||||
        description: "If set along with `HOMEBREW_DEVELOPER`, do not use bottles from older versions " \
 | 
			
		||||
                     "of macOS. This is useful in development on new macOS versions.",
 | 
			
		||||
 | 
			
		||||
@ -97,6 +97,25 @@ class FormulaOrCaskUnavailableError < RuntimeError
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Raised when a formula or cask in a specific tap is not available.
 | 
			
		||||
class TapFormulaOrCaskUnavailableError < FormulaOrCaskUnavailableError
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
 | 
			
		||||
  attr_reader :tap
 | 
			
		||||
 | 
			
		||||
  def initialize(tap, name)
 | 
			
		||||
    super "#{tap}/#{name}"
 | 
			
		||||
    @tap = tap
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { returns(String) }
 | 
			
		||||
  def to_s
 | 
			
		||||
    s = super
 | 
			
		||||
    s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed?
 | 
			
		||||
    s
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Raised when a formula is not available.
 | 
			
		||||
class FormulaUnavailableError < FormulaOrCaskUnavailableError
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
@ -423,7 +442,7 @@ class BuildError < RuntimeError
 | 
			
		||||
 | 
			
		||||
  def fetch_issues
 | 
			
		||||
    GitHub.issues_for_formula(formula.name, tap: formula.tap, state: "open")
 | 
			
		||||
  rescue GitHub::RateLimitExceededError => e
 | 
			
		||||
  rescue GitHub::API::RateLimitExceededError => e
 | 
			
		||||
    opoo e.message
 | 
			
		||||
    []
 | 
			
		||||
  end
 | 
			
		||||
@ -453,7 +472,7 @@ class BuildError < RuntimeError
 | 
			
		||||
    if formula.tap && defined?(OS::ISSUES_URL)
 | 
			
		||||
      if formula.tap.official?
 | 
			
		||||
        puts Formatter.error(Formatter.url(OS::ISSUES_URL), label: "READ THIS")
 | 
			
		||||
      elsif issues_url = formula.tap.issues_url
 | 
			
		||||
      elsif (issues_url = formula.tap.issues_url)
 | 
			
		||||
        puts <<~EOS
 | 
			
		||||
          If reporting this issue please do so at (not Homebrew/brew or Homebrew/core):
 | 
			
		||||
            #{Formatter.url(issues_url)}
 | 
			
		||||
@ -579,19 +598,32 @@ class ErrorDuringExecution < RuntimeError
 | 
			
		||||
    @status = status
 | 
			
		||||
    @output = output
 | 
			
		||||
 | 
			
		||||
    raise ArgumentError, "Status cannot be nil." if status.nil?
 | 
			
		||||
 | 
			
		||||
    exitstatus = case status
 | 
			
		||||
    when Integer
 | 
			
		||||
      status
 | 
			
		||||
    when Hash
 | 
			
		||||
      status["exitstatus"]
 | 
			
		||||
    else
 | 
			
		||||
      status&.exitstatus
 | 
			
		||||
      status.exitstatus
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    termsig = case status
 | 
			
		||||
    when Integer
 | 
			
		||||
      nil
 | 
			
		||||
    when Hash
 | 
			
		||||
      status["termsig"]
 | 
			
		||||
    else
 | 
			
		||||
      status.termsig
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    redacted_cmd = redact_secrets(cmd.shelljoin.gsub('\=', "="), secrets)
 | 
			
		||||
 | 
			
		||||
    reason = if exitstatus
 | 
			
		||||
      "exited with #{exitstatus}"
 | 
			
		||||
    elsif (uncaught_signal = status&.termsig)
 | 
			
		||||
      "was terminated by uncaught signal #{Signal.signame(uncaught_signal)}"
 | 
			
		||||
    elsif termsig
 | 
			
		||||
      "was terminated by uncaught signal #{Signal.signame(termsig)}"
 | 
			
		||||
    else
 | 
			
		||||
      raise ArgumentError, "Status neither has `exitstatus` nor `termsig`."
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ module EnvActivation
 | 
			
		||||
    params(
 | 
			
		||||
      env:          T.nilable(String),
 | 
			
		||||
      cc:           T.nilable(String),
 | 
			
		||||
      build_bottle: T.nilable(T::Boolean),
 | 
			
		||||
      build_bottle: T::Boolean,
 | 
			
		||||
      bottle_arch:  T.nilable(String),
 | 
			
		||||
      _block:       T.proc.returns(T.untyped),
 | 
			
		||||
    ).returns(T.untyped)
 | 
			
		||||
 | 
			
		||||
@ -35,13 +35,14 @@ module SharedEnvExtension
 | 
			
		||||
 | 
			
		||||
  sig {
 | 
			
		||||
    params(
 | 
			
		||||
      formula:      T.nilable(Formula),
 | 
			
		||||
      cc:           T.nilable(String),
 | 
			
		||||
      build_bottle: T.nilable(T::Boolean),
 | 
			
		||||
      bottle_arch:  T.nilable(T::Boolean),
 | 
			
		||||
      formula:         T.nilable(Formula),
 | 
			
		||||
      cc:              T.nilable(String),
 | 
			
		||||
      build_bottle:    T.nilable(T::Boolean),
 | 
			
		||||
      bottle_arch:     T.nilable(String),
 | 
			
		||||
      testing_formula: T::Boolean,
 | 
			
		||||
    ).void
 | 
			
		||||
  }
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil)
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false)
 | 
			
		||||
    @formula = formula
 | 
			
		||||
    @cc = cc
 | 
			
		||||
    @build_bottle = build_bottle
 | 
			
		||||
 | 
			
		||||
@ -16,13 +16,14 @@ module Stdenv
 | 
			
		||||
  # @private
 | 
			
		||||
  sig {
 | 
			
		||||
    params(
 | 
			
		||||
      formula:      T.nilable(Formula),
 | 
			
		||||
      cc:           T.nilable(String),
 | 
			
		||||
      build_bottle: T.nilable(T::Boolean),
 | 
			
		||||
      bottle_arch:  T.nilable(T::Boolean),
 | 
			
		||||
      formula:         T.nilable(Formula),
 | 
			
		||||
      cc:              T.nilable(String),
 | 
			
		||||
      build_bottle:    T.nilable(T::Boolean),
 | 
			
		||||
      bottle_arch:     T.nilable(String),
 | 
			
		||||
      testing_formula: T::Boolean,
 | 
			
		||||
    ).void
 | 
			
		||||
  }
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil)
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false)
 | 
			
		||||
    super
 | 
			
		||||
 | 
			
		||||
    self["HOMEBREW_ENV"] = "std"
 | 
			
		||||
 | 
			
		||||
@ -44,13 +44,14 @@ module Superenv
 | 
			
		||||
  # @private
 | 
			
		||||
  sig {
 | 
			
		||||
    params(
 | 
			
		||||
      formula:      T.nilable(Formula),
 | 
			
		||||
      cc:           T.nilable(String),
 | 
			
		||||
      build_bottle: T.nilable(T::Boolean),
 | 
			
		||||
      bottle_arch:  T.nilable(T::Boolean),
 | 
			
		||||
      formula:         T.nilable(Formula),
 | 
			
		||||
      cc:              T.nilable(String),
 | 
			
		||||
      build_bottle:    T.nilable(T::Boolean),
 | 
			
		||||
      bottle_arch:     T.nilable(String),
 | 
			
		||||
      testing_formula: T::Boolean,
 | 
			
		||||
    ).void
 | 
			
		||||
  }
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil)
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false)
 | 
			
		||||
    super
 | 
			
		||||
    send(compiler)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,14 +32,14 @@ module GitRepositoryExtension
 | 
			
		||||
  # Gets the full commit hash of the HEAD commit.
 | 
			
		||||
  sig { params(safe: T::Boolean).returns(T.nilable(String)) }
 | 
			
		||||
  def git_head(safe: false)
 | 
			
		||||
    popen_git("rev-parse", "--verify", "-q", "HEAD", safe: safe)
 | 
			
		||||
    popen_git("rev-parse", "--verify", "--quiet", "HEAD", safe: safe)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Gets a short commit hash of the HEAD commit.
 | 
			
		||||
  sig { params(length: T.nilable(Integer), safe: T::Boolean).returns(T.nilable(String)) }
 | 
			
		||||
  def git_short_head(length: nil, safe: false)
 | 
			
		||||
    short_arg = length.present? ? "--short=#{length}" : "--short"
 | 
			
		||||
    popen_git("rev-parse", short_arg, "--verify", "-q", "HEAD", safe: safe)
 | 
			
		||||
    popen_git("rev-parse", short_arg, "--verify", "--quiet", "HEAD", safe: safe)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Gets the relative date of the last commit, e.g. "1 hour ago"
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,10 @@
 | 
			
		||||
 | 
			
		||||
module Stdenv
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false)
 | 
			
		||||
    generic_setup_build_environment(formula: formula, cc: cc, build_bottle: build_bottle, bottle_arch: bottle_arch)
 | 
			
		||||
    generic_setup_build_environment(
 | 
			
		||||
      formula: formula, cc: cc, build_bottle: build_bottle,
 | 
			
		||||
      bottle_arch: bottle_arch, testing_formula: testing_formula
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    prepend_path "CPATH", HOMEBREW_PREFIX/"include"
 | 
			
		||||
    prepend_path "LIBRARY_PATH", HOMEBREW_PREFIX/"lib"
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,10 @@ module Superenv
 | 
			
		||||
 | 
			
		||||
  # @private
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false)
 | 
			
		||||
    generic_setup_build_environment(formula: formula, cc: cc, build_bottle: build_bottle, bottle_arch: bottle_arch)
 | 
			
		||||
    generic_setup_build_environment(
 | 
			
		||||
      formula: formula, cc: cc, build_bottle: build_bottle,
 | 
			
		||||
      bottle_arch: bottle_arch, testing_formula: testing_formula
 | 
			
		||||
    )
 | 
			
		||||
    self["HOMEBREW_OPTIMIZATION_LEVEL"] = "O2"
 | 
			
		||||
    self["HOMEBREW_DYNAMIC_LINKER"] = determine_dynamic_linker_path
 | 
			
		||||
    self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula)
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,11 @@ class Keg
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.relocation_formulae
 | 
			
		||||
    ["patchelf"]
 | 
			
		||||
    @relocation_formulae ||= if HOMEBREW_PATCHELF_RB_WRITE
 | 
			
		||||
      []
 | 
			
		||||
    else
 | 
			
		||||
      ["patchelf"]
 | 
			
		||||
    end.freeze
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.bottle_dependencies
 | 
			
		||||
 | 
			
		||||
@ -51,10 +51,7 @@ class DevelopmentTools
 | 
			
		||||
 | 
			
		||||
    sig { returns(String) }
 | 
			
		||||
    def installation_instructions
 | 
			
		||||
      <<~EOS
 | 
			
		||||
        Install the Command Line Tools:
 | 
			
		||||
          xcode-select --install
 | 
			
		||||
      EOS
 | 
			
		||||
      MacOS::CLT.installation_instructions
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    sig { returns(String) }
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,7 @@ module Homebrew
 | 
			
		||||
          check_clt_minimum_version
 | 
			
		||||
          check_if_xcode_needs_clt_installed
 | 
			
		||||
          check_if_supported_sdk_available
 | 
			
		||||
          check_broken_sdks
 | 
			
		||||
        ].freeze
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
@ -425,7 +426,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
        source = if locator.source == :clt
 | 
			
		||||
          update_instructions = MacOS::CLT.update_instructions
 | 
			
		||||
          "CLT"
 | 
			
		||||
          "Command Line Tools (CLT)"
 | 
			
		||||
        else
 | 
			
		||||
          update_instructions = MacOS::Xcode.update_instructions
 | 
			
		||||
          "Xcode"
 | 
			
		||||
@ -438,6 +439,40 @@ module Homebrew
 | 
			
		||||
          #{update_instructions}
 | 
			
		||||
        EOS
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # The CLT 10.x -> 11.x upgrade process on 10.14 contained a bug which broke the SDKs.
 | 
			
		||||
      # Notably, MacOSX10.14.sdk would indirectly symlink to MacOSX10.15.sdk.
 | 
			
		||||
      # This diagnostic was introduced to check for this and recommend a full reinstall.
 | 
			
		||||
      def check_broken_sdks
 | 
			
		||||
        locator = MacOS.sdk_locator
 | 
			
		||||
 | 
			
		||||
        return if locator.all_sdks.all? do |sdk|
 | 
			
		||||
          path_version = sdk.path.basename.to_s[MacOS::SDK::VERSIONED_SDK_REGEX, 1]
 | 
			
		||||
          next true if path_version.blank?
 | 
			
		||||
 | 
			
		||||
          sdk.version == MacOS::Version.new(path_version).strip_patch
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        if locator.source == :clt
 | 
			
		||||
          source = "Command Line Tools (CLT)"
 | 
			
		||||
          path_to_remove = MacOS::CLT::PKG_PATH
 | 
			
		||||
          installation_instructions = MacOS::CLT.installation_instructions
 | 
			
		||||
        else
 | 
			
		||||
          source = "Xcode"
 | 
			
		||||
          path_to_remove = MacOS::Xcode.bundle_path
 | 
			
		||||
          installation_instructions = MacOS::Xcode.installation_instructions
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        <<~EOS
 | 
			
		||||
          The contents of the SDKs in your #{source} installation do not match the SDK folder names.
 | 
			
		||||
          A clean reinstall of #{source} should fix this.
 | 
			
		||||
 | 
			
		||||
          Remove the broken installation before reinstalling:
 | 
			
		||||
            sudo rm -rf #{path_to_remove}
 | 
			
		||||
 | 
			
		||||
          #{installation_instructions}
 | 
			
		||||
        EOS
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,10 @@ module Stdenv
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false)
 | 
			
		||||
    generic_setup_build_environment(formula: formula, cc: cc, build_bottle: build_bottle, bottle_arch: bottle_arch)
 | 
			
		||||
    generic_setup_build_environment(
 | 
			
		||||
      formula: formula, cc: cc, build_bottle: build_bottle,
 | 
			
		||||
      bottle_arch: bottle_arch, testing_formula: testing_formula
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # sed is strict, and errors out when it encounters files with
 | 
			
		||||
    # mixed character sets
 | 
			
		||||
 | 
			
		||||
@ -98,8 +98,6 @@ module Superenv
 | 
			
		||||
 | 
			
		||||
  def determine_cccfg
 | 
			
		||||
    s = +""
 | 
			
		||||
    # Fix issue with sed barfing on unicode characters on Mountain Lion
 | 
			
		||||
    s << "s"
 | 
			
		||||
    # Fix issue with >= Mountain Lion apr-1-config having broken paths
 | 
			
		||||
    s << "a"
 | 
			
		||||
    s.freeze
 | 
			
		||||
@ -121,7 +119,10 @@ module Superenv
 | 
			
		||||
      self["HOMEBREW_SDKROOT"] = nil
 | 
			
		||||
      self["HOMEBREW_DEVELOPER_DIR"] = nil
 | 
			
		||||
    end
 | 
			
		||||
    generic_setup_build_environment(formula: formula, cc: cc, build_bottle: build_bottle, bottle_arch: bottle_arch)
 | 
			
		||||
    generic_setup_build_environment(
 | 
			
		||||
      formula: formula, cc: cc, build_bottle: build_bottle,
 | 
			
		||||
      bottle_arch: bottle_arch, testing_formula: testing_formula
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Filter out symbols known not to be defined since GNU Autotools can't
 | 
			
		||||
    # reliably figure this out with Xcode 8 and above.
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
# The Library/Homebrew/extend/os/software_spec.rb conditional logic will need to be more nuanced
 | 
			
		||||
# if this file ever includes more than `uses_from_macos`.
 | 
			
		||||
class SoftwareSpec
 | 
			
		||||
  undef uses_from_macos
 | 
			
		||||
 | 
			
		||||
@ -9,11 +11,12 @@ class SoftwareSpec
 | 
			
		||||
 | 
			
		||||
    if deps.is_a?(Hash)
 | 
			
		||||
      bounds = deps.dup
 | 
			
		||||
      deps = Hash[*bounds.shift]
 | 
			
		||||
      deps = bounds.shift
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    bounds = bounds.transform_values { |v| MacOS::Version.from_symbol(v) }
 | 
			
		||||
    if MacOS.version >= bounds[:since]
 | 
			
		||||
    if MacOS.version >= bounds[:since] ||
 | 
			
		||||
       (Homebrew::EnvConfig.simulate_macos_on_linux? && !bounds.key?(:since))
 | 
			
		||||
      @uses_from_macos_elements << deps
 | 
			
		||||
    else
 | 
			
		||||
      depends_on deps
 | 
			
		||||
 | 
			
		||||
@ -20,10 +20,10 @@ module Utils
 | 
			
		||||
 | 
			
		||||
      alias generic_find_matching_tag find_matching_tag
 | 
			
		||||
 | 
			
		||||
      def find_matching_tag(tag)
 | 
			
		||||
      def find_matching_tag(tag, exact: false)
 | 
			
		||||
        # Used primarily by developers testing beta macOS releases.
 | 
			
		||||
        if OS::Mac.prerelease? && Homebrew::EnvConfig.developer? &&
 | 
			
		||||
           Homebrew::EnvConfig.skip_or_later_bottles?
 | 
			
		||||
        if exact || (OS::Mac.prerelease? && Homebrew::EnvConfig.developer? &&
 | 
			
		||||
                     Homebrew::EnvConfig.skip_or_later_bottles?)
 | 
			
		||||
          generic_find_matching_tag(tag)
 | 
			
		||||
        else
 | 
			
		||||
          generic_find_matching_tag(tag) ||
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
# typed: strict
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
if OS.mac?
 | 
			
		||||
if OS.mac? || Homebrew::EnvConfig.simulate_macos_on_linux?
 | 
			
		||||
  require "extend/os/mac/on_os"
 | 
			
		||||
elsif OS.linux?
 | 
			
		||||
  require "extend/os/linux/on_os"
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
# typed: strict
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
if OS.linux?
 | 
			
		||||
  require "extend/os/linux/software_spec"
 | 
			
		||||
elsif OS.mac?
 | 
			
		||||
# This logic will need to be more nuanced if this file includes more than `uses_from_macos`.
 | 
			
		||||
if OS.mac? || Homebrew::EnvConfig.simulate_macos_on_linux?
 | 
			
		||||
  require "extend/os/mac/software_spec"
 | 
			
		||||
elsif OS.linux?
 | 
			
		||||
  require "extend/os/linux/software_spec"
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								Library/Homebrew/extend/time.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Library/Homebrew/extend/time.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module TimeRemaining
 | 
			
		||||
  refine Time do
 | 
			
		||||
    def remaining
 | 
			
		||||
      [0, self - Time.now].max
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def remaining!
 | 
			
		||||
      r = remaining
 | 
			
		||||
 | 
			
		||||
      raise Timeout::Error if r <= 0
 | 
			
		||||
 | 
			
		||||
      r
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -2061,7 +2061,7 @@ class Formula
 | 
			
		||||
 | 
			
		||||
          if verbose_using_dots
 | 
			
		||||
            last_dot = Time.at(0)
 | 
			
		||||
            while buf = rd.gets
 | 
			
		||||
            while (buf = rd.gets)
 | 
			
		||||
              log.puts buf
 | 
			
		||||
              # make sure dots printed with interval of at least 1 min.
 | 
			
		||||
              next unless (Time.now - last_dot) > 60
 | 
			
		||||
@ -2072,7 +2072,7 @@ class Formula
 | 
			
		||||
            end
 | 
			
		||||
            puts
 | 
			
		||||
          else
 | 
			
		||||
            while buf = rd.gets
 | 
			
		||||
            while (buf = rd.gets)
 | 
			
		||||
              log.puts buf
 | 
			
		||||
              puts buf
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,54 @@ module Homebrew
 | 
			
		||||
  module Assertions
 | 
			
		||||
    include Context
 | 
			
		||||
 | 
			
		||||
    require "test/unit/assertions"
 | 
			
		||||
    include ::Test::Unit::Assertions
 | 
			
		||||
    require "minitest"
 | 
			
		||||
    require "minitest/assertions"
 | 
			
		||||
    include ::Minitest::Assertions
 | 
			
		||||
 | 
			
		||||
    attr_writer :assertions
 | 
			
		||||
 | 
			
		||||
    def assertions
 | 
			
		||||
      @assertions ||= 0
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Test::Unit backwards compatibility methods
 | 
			
		||||
    {
 | 
			
		||||
      assert_include:         :assert_includes,
 | 
			
		||||
      assert_path_exist:      :assert_path_exists,
 | 
			
		||||
      assert_raise:           :assert_raises,
 | 
			
		||||
      assert_throw:           :assert_throws,
 | 
			
		||||
      assert_not_empty:       :refute_empty,
 | 
			
		||||
      assert_not_equal:       :refute_equal,
 | 
			
		||||
      assert_not_in_delta:    :refute_in_delta,
 | 
			
		||||
      assert_not_in_epsilon:  :refute_in_epsilon,
 | 
			
		||||
      assert_not_include:     :refute_includes,
 | 
			
		||||
      assert_not_includes:    :refute_includes,
 | 
			
		||||
      assert_not_instance_of: :refute_instance_of,
 | 
			
		||||
      assert_not_kind_of:     :refute_kind_of,
 | 
			
		||||
      assert_not_match:       :refute_match,
 | 
			
		||||
      assert_no_match:        :refute_match,
 | 
			
		||||
      assert_not_nil:         :refute_nil,
 | 
			
		||||
      assert_not_operator:    :refute_operator,
 | 
			
		||||
      assert_path_not_exist:  :refute_path_exists,
 | 
			
		||||
      assert_not_predicate:   :refute_predicate,
 | 
			
		||||
      assert_not_respond_to:  :refute_respond_to,
 | 
			
		||||
      assert_not_same:        :refute_same,
 | 
			
		||||
    }.each do |old_method, new_method|
 | 
			
		||||
      define_method(old_method) do |*args|
 | 
			
		||||
        # odeprecated old_method, new_method
 | 
			
		||||
        send(new_method, *args)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def assert_true(act, msg = nil)
 | 
			
		||||
      # odeprecated "assert_true", "assert(...) or assert_equal(true, ...)"
 | 
			
		||||
      assert_equal(true, act, msg)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def assert_false(act, msg = nil)
 | 
			
		||||
      # odeprecated "assert_false", "assert(!...) or assert_equal(false, ...)"
 | 
			
		||||
      assert_equal(false, act, msg)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Returns the output of running cmd, and asserts the exit status.
 | 
			
		||||
    # @api public
 | 
			
		||||
@ -18,7 +64,7 @@ module Homebrew
 | 
			
		||||
      output = `#{cmd}`
 | 
			
		||||
      assert_equal result, $CHILD_STATUS.exitstatus
 | 
			
		||||
      output
 | 
			
		||||
    rescue Test::Unit::AssertionFailedError
 | 
			
		||||
    rescue Minitest::Assertion
 | 
			
		||||
      puts output if verbose?
 | 
			
		||||
      raise
 | 
			
		||||
    end
 | 
			
		||||
@ -35,7 +81,7 @@ module Homebrew
 | 
			
		||||
      end
 | 
			
		||||
      assert_equal result, $CHILD_STATUS.exitstatus unless result.nil?
 | 
			
		||||
      output
 | 
			
		||||
    rescue Test::Unit::AssertionFailedError
 | 
			
		||||
    rescue Minitest::Assertion
 | 
			
		||||
      puts output if verbose?
 | 
			
		||||
      raise
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -133,7 +133,7 @@ module Homebrew
 | 
			
		||||
        return
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if oldname = CoreTap.instance.formula_renames[name]
 | 
			
		||||
      if (oldname = CoreTap.instance.formula_renames[name])
 | 
			
		||||
        problem "'#{name}' is reserved as the old name of #{oldname} in homebrew/core."
 | 
			
		||||
        return
 | 
			
		||||
      end
 | 
			
		||||
@ -282,7 +282,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      # The number of conflicts on Linux is absurd.
 | 
			
		||||
      # TODO: remove this and check these there too.
 | 
			
		||||
      return if OS.linux?
 | 
			
		||||
      return if OS.linux? && !Homebrew::EnvConfig.simulate_macos_on_linux?
 | 
			
		||||
 | 
			
		||||
      recursive_runtime_formulae = formula.runtime_formula_dependencies(undeclared: false)
 | 
			
		||||
      version_hash = {}
 | 
			
		||||
@ -339,6 +339,18 @@ module Homebrew
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def audit_glibc
 | 
			
		||||
      return if formula.name != "glibc"
 | 
			
		||||
      return unless @core_tap
 | 
			
		||||
 | 
			
		||||
      version = formula.version.to_s
 | 
			
		||||
      return if version == "2.23"
 | 
			
		||||
 | 
			
		||||
      problem "The glibc version must be #{version}, as this is the version used by our CI on Linux. " \
 | 
			
		||||
              "Glibc is for users who have a system Glibc with a lower version, " \
 | 
			
		||||
              "which allows them to use our Linux bottles, which were compiled against system Glibc on CI."
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def audit_versioned_keg_only
 | 
			
		||||
      return unless @versioned_formula
 | 
			
		||||
      return unless @core_tap
 | 
			
		||||
@ -367,10 +379,10 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      return unless DevelopmentTools.curl_handles_most_https_certificates?
 | 
			
		||||
 | 
			
		||||
      if http_content_problem = curl_check_http_content(homepage,
 | 
			
		||||
                                                        user_agents:   [:browser, :default],
 | 
			
		||||
                                                        check_content: true,
 | 
			
		||||
                                                        strict:        @strict)
 | 
			
		||||
      if (http_content_problem = curl_check_http_content(homepage,
 | 
			
		||||
                                                         user_agents:   [:browser, :default],
 | 
			
		||||
                                                         check_content: true,
 | 
			
		||||
                                                         strict:        @strict))
 | 
			
		||||
        problem http_content_problem
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -472,7 +484,7 @@ module Homebrew
 | 
			
		||||
 | 
			
		||||
      %w[Stable HEAD].each do |name|
 | 
			
		||||
        spec_name = name.downcase.to_sym
 | 
			
		||||
        next unless spec = formula.send(spec_name)
 | 
			
		||||
        next unless (spec = formula.send(spec_name))
 | 
			
		||||
 | 
			
		||||
        ra = ResourceAuditor.new(spec, spec_name, online: @online, strict: @strict).audit
 | 
			
		||||
        ra.problems.each do |message|
 | 
			
		||||
@ -497,7 +509,7 @@ module Homebrew
 | 
			
		||||
        )
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if stable = formula.stable
 | 
			
		||||
      if (stable = formula.stable)
 | 
			
		||||
        version = stable.version
 | 
			
		||||
        problem "Stable: version (#{version}) is set to a string without a digit" if version.to_s !~ /\d/
 | 
			
		||||
        if version.to_s.start_with?("HEAD")
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ module Homebrew
 | 
			
		||||
            @desc = metadata["description"]
 | 
			
		||||
            @homepage = metadata["homepage"]
 | 
			
		||||
            @license = metadata["license"]["spdx_id"] if metadata["license"]
 | 
			
		||||
          rescue GitHub::HTTPNotFoundError
 | 
			
		||||
          rescue GitHub::API::HTTPNotFoundError
 | 
			
		||||
            # If there was no repository found assume the network connection is at
 | 
			
		||||
            # fault rather than the input URL.
 | 
			
		||||
            nil
 | 
			
		||||
@ -153,13 +153,14 @@ module Homebrew
 | 
			
		||||
          def install
 | 
			
		||||
            # ENV.deparallelize  # if your formula fails when building in parallel
 | 
			
		||||
        <% if  mode == :cmake %>
 | 
			
		||||
            system "cmake", ".", *std_cmake_args
 | 
			
		||||
            system "cmake", "-S", ".", "-B", "build", *std_cmake_args
 | 
			
		||||
            system "cmake", "--build", "build"
 | 
			
		||||
            system "cmake", "--install", "build"
 | 
			
		||||
        <% elsif  mode == :autotools %>
 | 
			
		||||
            # Remove unrecognized options if warned by configure
 | 
			
		||||
            system "./configure", "--disable-debug",
 | 
			
		||||
                                  "--disable-dependency-tracking",
 | 
			
		||||
                                  "--disable-silent-rules",
 | 
			
		||||
                                  "--prefix=\#{prefix}"
 | 
			
		||||
            # https://rubydoc.brew.sh/Formula.html#std_configure_args-instance_method
 | 
			
		||||
            system "./configure", *std_configure_args, "--disable-silent-rules"
 | 
			
		||||
            system "make", "install" # if this fails, try separate make/make install steps
 | 
			
		||||
        <% elsif  mode == :crystal %>
 | 
			
		||||
            system "shards", "build", "--release"
 | 
			
		||||
            bin.install "bin/#{name}"
 | 
			
		||||
@ -206,14 +207,9 @@ module Homebrew
 | 
			
		||||
            system "cargo", "install", *std_cargo_args
 | 
			
		||||
        <% else  %>
 | 
			
		||||
            # Remove unrecognized options if warned by configure
 | 
			
		||||
            system "./configure", "--disable-debug",
 | 
			
		||||
                                  "--disable-dependency-tracking",
 | 
			
		||||
                                  "--disable-silent-rules",
 | 
			
		||||
                                  "--prefix=\#{prefix}"
 | 
			
		||||
            # system "cmake", ".", *std_cmake_args
 | 
			
		||||
        <% end  %>
 | 
			
		||||
        <% if  mode == :autotools || mode == :cmake %>
 | 
			
		||||
            system "make", "install" # if this fails, try separate make/make install steps
 | 
			
		||||
            # https://rubydoc.brew.sh/Formula.html#std_configure_args-instance_method
 | 
			
		||||
            system "./configure", *std_configure_args, "--disable-silent-rules"
 | 
			
		||||
            # system "cmake", "-S", ".", "-B", "build", *std_cmake_args
 | 
			
		||||
        <% end  %>
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,13 +16,11 @@ class FormulaInfo
 | 
			
		||||
  # Returns nil if formula is absent or if there was an error reading it.
 | 
			
		||||
  def self.lookup(name)
 | 
			
		||||
    json = Utils.popen_read(
 | 
			
		||||
      RUBY_PATH,
 | 
			
		||||
      ENV["HOMEBREW_RUBY_WARNINGS"],
 | 
			
		||||
      "-I", $LOAD_PATH.join(File::PATH_SEPARATOR),
 | 
			
		||||
      *HOMEBREW_RUBY_EXEC_ARGS,
 | 
			
		||||
      HOMEBREW_LIBRARY_PATH/"brew.rb",
 | 
			
		||||
      "info",
 | 
			
		||||
      "--json=v1",
 | 
			
		||||
      name
 | 
			
		||||
      name,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return unless $CHILD_STATUS.success?
 | 
			
		||||
 | 
			
		||||
@ -94,7 +94,6 @@ class FormulaInstaller
 | 
			
		||||
    @options = options
 | 
			
		||||
    @requirement_messages = []
 | 
			
		||||
    @poured_bottle = false
 | 
			
		||||
    @pour_failed = false
 | 
			
		||||
    @start_time = nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -154,8 +153,6 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
  sig { params(output_warning: T::Boolean).returns(T::Boolean) }
 | 
			
		||||
  def pour_bottle?(output_warning: false)
 | 
			
		||||
    return false if @pour_failed
 | 
			
		||||
 | 
			
		||||
    return false if !formula.bottle_tag? && !formula.local_bottle_path
 | 
			
		||||
    return true  if force_bottle?
 | 
			
		||||
    return false if build_from_source? || build_bottle? || interactive?
 | 
			
		||||
@ -230,13 +227,12 @@ class FormulaInstaller
 | 
			
		||||
      raise CannotInstallFormulaError, "--force-bottle passed but #{formula.full_name} has no bottle!"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if Homebrew.default_prefix? && !Homebrew::EnvConfig.developer? &&
 | 
			
		||||
    if Homebrew.default_prefix? &&
 | 
			
		||||
       # TODO: re-enable this on Linux when we merge linuxbrew-core into
 | 
			
		||||
       # homebrew-core and have full bottle coverage.
 | 
			
		||||
       (OS.mac? || ENV["CI"]) &&
 | 
			
		||||
       !build_from_source? && !build_bottle? &&
 | 
			
		||||
       !installed_as_dependency? &&
 | 
			
		||||
       formula.tap&.core_tap? && !formula.bottle_unneeded? && !formula.any_version_installed? &&
 | 
			
		||||
       !build_from_source? && !build_bottle? && !formula.head? &&
 | 
			
		||||
       formula.tap&.core_tap? && !formula.bottle_unneeded? &&
 | 
			
		||||
       # Integration tests override homebrew-core locations
 | 
			
		||||
       ENV["HOMEBREW_TEST_TMPDIR"].nil? &&
 | 
			
		||||
       !pour_bottle?
 | 
			
		||||
@ -244,21 +240,31 @@ class FormulaInstaller
 | 
			
		||||
        formula_message = formula.pour_bottle_check_unsatisfied_reason
 | 
			
		||||
        formula_message[0] = formula_message[0].downcase
 | 
			
		||||
 | 
			
		||||
        "#{formula}: #{formula_message}"
 | 
			
		||||
      else
 | 
			
		||||
        <<~EOS
 | 
			
		||||
          #{formula}: #{formula_message}
 | 
			
		||||
        EOS
 | 
			
		||||
      # don't want to complain about no bottle available if doing an
 | 
			
		||||
      # upgrade/reinstall/dependency install (but do in the case the bottle
 | 
			
		||||
      # check fails)
 | 
			
		||||
      elsif !Homebrew::EnvConfig.developer? &&
 | 
			
		||||
            (!installed_as_dependency? || !formula.any_version_installed?) &&
 | 
			
		||||
            (!OS.mac? || !OS::Mac.outdated_release?)
 | 
			
		||||
        <<~EOS
 | 
			
		||||
          #{formula}: no bottle available!
 | 
			
		||||
        EOS
 | 
			
		||||
      end
 | 
			
		||||
      message += <<~EOS
 | 
			
		||||
        You can try to install from source with:
 | 
			
		||||
          brew install --build-from-source #{formula}
 | 
			
		||||
        Please note building from source is unsupported. You will encounter build
 | 
			
		||||
        failures with some formulae. If you experience any issues please create pull
 | 
			
		||||
        requests instead of asking for help on Homebrew's GitHub, Twitter or any other
 | 
			
		||||
        official channels.
 | 
			
		||||
      EOS
 | 
			
		||||
      raise CannotInstallFormulaError, message
 | 
			
		||||
 | 
			
		||||
      if message
 | 
			
		||||
        message += <<~EOS
 | 
			
		||||
          You can try to install from source with:
 | 
			
		||||
            brew install --build-from-source #{formula}
 | 
			
		||||
          Please note building from source is unsupported. You will encounter build
 | 
			
		||||
          failures with some formulae. If you experience any issues please create pull
 | 
			
		||||
          requests instead of asking for help on Homebrew's GitHub, Twitter or any other
 | 
			
		||||
          official channels.
 | 
			
		||||
        EOS
 | 
			
		||||
        raise CannotInstallFormulaError, message
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    type, reason = DeprecateDisable.deprecate_disable_info formula
 | 
			
		||||
@ -282,49 +288,51 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
    return if ignore_deps?
 | 
			
		||||
 | 
			
		||||
    recursive_deps = formula.recursive_dependencies
 | 
			
		||||
    recursive_formulae = recursive_deps.map(&:to_formula)
 | 
			
		||||
    if Homebrew::EnvConfig.developer?
 | 
			
		||||
      # `recursive_dependencies` trims cyclic dependencies, so we do one level and take the recursive deps of that.
 | 
			
		||||
      # Mapping direct dependencies to deeper dependencies in a hash is also useful for the cyclic output below.
 | 
			
		||||
      recursive_dep_map = formula.deps.to_h { |dep| [dep, dep.to_formula.recursive_dependencies] }
 | 
			
		||||
 | 
			
		||||
    recursive_dependencies = []
 | 
			
		||||
    invalid_arch_dependencies = []
 | 
			
		||||
    recursive_formulae.each do |dep|
 | 
			
		||||
      dep_recursive_dependencies = dep.recursive_dependencies.map(&:to_s)
 | 
			
		||||
      if dep_recursive_dependencies.include?(formula.name)
 | 
			
		||||
        recursive_dependencies << "#{formula.full_name} depends on #{dep.full_name}"
 | 
			
		||||
        recursive_dependencies << "#{dep.full_name} depends on #{formula.full_name}"
 | 
			
		||||
      cyclic_dependencies = []
 | 
			
		||||
      recursive_dep_map.each do |dep, recursive_deps|
 | 
			
		||||
        if [formula.name, formula.full_name].include?(dep.name)
 | 
			
		||||
          cyclic_dependencies << "#{formula.full_name} depends on itself directly"
 | 
			
		||||
        elsif recursive_deps.any? { |rdep| [formula.name, formula.full_name].include?(rdep.name) }
 | 
			
		||||
          cyclic_dependencies << "#{formula.full_name} depends on itself via #{dep.name}"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      if (tab = Tab.for_formula(dep)) && tab.arch.present? && tab.arch.to_s != Hardware::CPU.arch.to_s
 | 
			
		||||
      if cyclic_dependencies.present?
 | 
			
		||||
        raise CannotInstallFormulaError, <<~EOS
 | 
			
		||||
          #{formula.full_name} contains a recursive dependency on itself:
 | 
			
		||||
            #{cyclic_dependencies.join("\n  ")}
 | 
			
		||||
        EOS
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Merge into one list
 | 
			
		||||
      recursive_deps = recursive_dep_map.flat_map { |dep, rdeps| [dep] + rdeps }
 | 
			
		||||
      Dependency.merge_repeats(recursive_deps)
 | 
			
		||||
    else
 | 
			
		||||
      recursive_deps = formula.recursive_dependencies
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    invalid_arch_dependencies = []
 | 
			
		||||
    pinned_unsatisfied_deps = []
 | 
			
		||||
    recursive_deps.each do |dep|
 | 
			
		||||
      if (tab = Tab.for_formula(dep.to_formula)) && tab.arch.present? && tab.arch.to_s != Hardware::CPU.arch.to_s
 | 
			
		||||
        invalid_arch_dependencies << "#{dep} was built for #{tab.arch}"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      pinned_unsatisfied_deps << dep if dep.to_formula.pinned? && !dep.satisfied?(inherited_options_for(dep))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    unless recursive_dependencies.empty?
 | 
			
		||||
      raise CannotInstallFormulaError, <<~EOS
 | 
			
		||||
        #{formula.full_name} contains a recursive dependency on itself:
 | 
			
		||||
          #{recursive_dependencies.join("\n  ")}
 | 
			
		||||
      EOS
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if recursive_formulae.flat_map(&:recursive_dependencies)
 | 
			
		||||
                         .map(&:to_s)
 | 
			
		||||
                         .include?(formula.name)
 | 
			
		||||
      raise CannotInstallFormulaError, <<~EOS
 | 
			
		||||
        #{formula.full_name} contains a recursive dependency on itself!
 | 
			
		||||
      EOS
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    unless invalid_arch_dependencies.empty?
 | 
			
		||||
    if invalid_arch_dependencies.present?
 | 
			
		||||
      raise CannotInstallFormulaError, <<~EOS
 | 
			
		||||
        #{formula.full_name} dependencies not built for the #{Hardware::CPU.arch} CPU architecture:
 | 
			
		||||
          #{invalid_arch_dependencies.join("\n  ")}
 | 
			
		||||
      EOS
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    pinned_unsatisfied_deps = recursive_deps.select do |dep|
 | 
			
		||||
      dep.to_formula.pinned? && !dep.satisfied?(inherited_options_for(dep))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    return if pinned_unsatisfied_deps.empty?
 | 
			
		||||
 | 
			
		||||
    raise CannotInstallFormulaError,
 | 
			
		||||
@ -429,7 +437,7 @@ class FormulaInstaller
 | 
			
		||||
    if pour_bottle?
 | 
			
		||||
      begin
 | 
			
		||||
        pour
 | 
			
		||||
      rescue Exception => e # rubocop:disable Lint/RescueException
 | 
			
		||||
      rescue Exception # rubocop:disable Lint/RescueException
 | 
			
		||||
        # any exceptions must leave us with nothing installed
 | 
			
		||||
        ignore_interrupts do
 | 
			
		||||
          begin
 | 
			
		||||
@ -442,17 +450,7 @@ class FormulaInstaller
 | 
			
		||||
          end
 | 
			
		||||
          formula.rack.rmdir_if_possible
 | 
			
		||||
        end
 | 
			
		||||
        raise if Homebrew::EnvConfig.developer? ||
 | 
			
		||||
                 Homebrew::EnvConfig.no_bottle_source_fallback? ||
 | 
			
		||||
                 force_bottle? ||
 | 
			
		||||
                 e.is_a?(Interrupt)
 | 
			
		||||
 | 
			
		||||
        @pour_failed = true
 | 
			
		||||
        onoe e.message
 | 
			
		||||
        opoo "Bottle installation failed: building from source."
 | 
			
		||||
        raise UnbottledError, [formula] unless DevelopmentTools.installed?
 | 
			
		||||
 | 
			
		||||
        compute_and_install_dependencies unless ignore_deps?
 | 
			
		||||
        raise
 | 
			
		||||
      else
 | 
			
		||||
        @poured_bottle = true
 | 
			
		||||
      end
 | 
			
		||||
@ -513,7 +511,7 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
      $stderr.puts "Please report this issue to the #{formula.tap} tap (not Homebrew/brew or Homebrew/core)!"
 | 
			
		||||
      false
 | 
			
		||||
    else # rubocop:disable Layout/ElseAlignment
 | 
			
		||||
    else
 | 
			
		||||
      f.linked_keg.exist? && f.opt_prefix.exist?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@ -523,9 +521,11 @@ class FormulaInstaller
 | 
			
		||||
  # Compute and collect the dependencies needed by the formula currently
 | 
			
		||||
  # being installed.
 | 
			
		||||
  def compute_dependencies
 | 
			
		||||
    req_map, req_deps = expand_requirements
 | 
			
		||||
    check_requirements(req_map)
 | 
			
		||||
    expand_dependencies(req_deps + formula.deps)
 | 
			
		||||
    @compute_dependencies ||= begin
 | 
			
		||||
      req_map, req_deps = expand_requirements
 | 
			
		||||
      check_requirements(req_map)
 | 
			
		||||
      expand_dependencies(req_deps + formula.deps)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unbottled_dependencies(deps)
 | 
			
		||||
@ -575,7 +575,7 @@ class FormulaInstaller
 | 
			
		||||
    formula_deps_map = Dependency.expand(formula)
 | 
			
		||||
                                 .index_by(&:name)
 | 
			
		||||
 | 
			
		||||
    while f = formulae.pop
 | 
			
		||||
    while (f = formulae.pop)
 | 
			
		||||
      runtime_requirements = runtime_requirements(f)
 | 
			
		||||
      f.recursive_requirements do |dependent, req|
 | 
			
		||||
        build = effective_build_options_for(dependent)
 | 
			
		||||
@ -707,16 +707,17 @@ class FormulaInstaller
 | 
			
		||||
      quiet:                      quiet?,
 | 
			
		||||
      verbose:                    verbose?,
 | 
			
		||||
    )
 | 
			
		||||
    fi.prelude
 | 
			
		||||
    fi.fetch
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(dep: Dependency, inherited_options: Options).void }
 | 
			
		||||
  def install_dependency(dep, inherited_options)
 | 
			
		||||
    df = dep.to_formula
 | 
			
		||||
    tab = Tab.for_formula(df)
 | 
			
		||||
 | 
			
		||||
    if df.linked_keg.directory?
 | 
			
		||||
      linked_keg = Keg.new(df.linked_keg.resolved_path)
 | 
			
		||||
      tab = Tab.for_keg(linked_keg)
 | 
			
		||||
      keg_had_linked_keg = true
 | 
			
		||||
      keg_was_linked = linked_keg.linked?
 | 
			
		||||
      linked_keg.unlink
 | 
			
		||||
@ -724,12 +725,13 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
    if df.latest_version_installed?
 | 
			
		||||
      installed_keg = Keg.new(df.prefix)
 | 
			
		||||
      tab ||= Tab.for_keg(installed_keg)
 | 
			
		||||
      tmp_keg = Pathname.new("#{installed_keg}.tmp")
 | 
			
		||||
      installed_keg.rename(tmp_keg)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    tab_tap = tab.source["tap"]
 | 
			
		||||
    if tab_tap.present? && df.tap.present? && df.tap.to_s != tab_tap.to_s
 | 
			
		||||
    if df.tap.present? && tab.present? && (tab_tap = tab.source["tap"].presence) &&
 | 
			
		||||
       df.tap.to_s != tab_tap.to_s
 | 
			
		||||
      odie <<~EOS
 | 
			
		||||
        #{df} is already installed from #{tab_tap}!
 | 
			
		||||
        Please `brew uninstall #{df}` first."
 | 
			
		||||
@ -737,7 +739,7 @@ class FormulaInstaller
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    options = Options.new
 | 
			
		||||
    options |= tab.used_options
 | 
			
		||||
    options |= tab.used_options if tab.present?
 | 
			
		||||
    options |= Tab.remap_deprecated_options(df.deprecated_options, dep.options)
 | 
			
		||||
    options |= inherited_options
 | 
			
		||||
    options &= df.options
 | 
			
		||||
@ -748,7 +750,7 @@ class FormulaInstaller
 | 
			
		||||
        options:                    options,
 | 
			
		||||
        link_keg:                   keg_had_linked_keg ? keg_was_linked : nil,
 | 
			
		||||
        installed_as_dependency:    true,
 | 
			
		||||
        installed_on_request:       df.any_version_installed? && tab.installed_on_request,
 | 
			
		||||
        installed_on_request:       df.any_version_installed? && tab.present? && tab.installed_on_request,
 | 
			
		||||
        force_bottle:               false,
 | 
			
		||||
        include_test_formulae:      @include_test_formulae,
 | 
			
		||||
        build_from_source_formulae: @build_from_source_formulae,
 | 
			
		||||
@ -759,7 +761,6 @@ class FormulaInstaller
 | 
			
		||||
        verbose:                    verbose?,
 | 
			
		||||
      },
 | 
			
		||||
    )
 | 
			
		||||
    fi.prelude
 | 
			
		||||
    oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}"
 | 
			
		||||
    fi.install
 | 
			
		||||
    fi.finish
 | 
			
		||||
@ -901,13 +902,12 @@ class FormulaInstaller
 | 
			
		||||
    # 1. formulae can modify ENV, so we must ensure that each
 | 
			
		||||
    #    installation has a pristine ENV when it starts, forking now is
 | 
			
		||||
    #    the easiest way to do this
 | 
			
		||||
    args = %W[
 | 
			
		||||
      nice #{RUBY_PATH}
 | 
			
		||||
      #{ENV["HOMEBREW_RUBY_WARNINGS"]}
 | 
			
		||||
      -I #{$LOAD_PATH.join(File::PATH_SEPARATOR)}
 | 
			
		||||
      --
 | 
			
		||||
      #{HOMEBREW_LIBRARY_PATH}/build.rb
 | 
			
		||||
      #{formula.specified_path}
 | 
			
		||||
    args = [
 | 
			
		||||
      "nice",
 | 
			
		||||
      *HOMEBREW_RUBY_EXEC_ARGS,
 | 
			
		||||
      "--",
 | 
			
		||||
      HOMEBREW_LIBRARY_PATH/"build.rb",
 | 
			
		||||
      formula.specified_path,
 | 
			
		||||
    ].concat(build_argv)
 | 
			
		||||
 | 
			
		||||
    Utils.safe_fork do
 | 
			
		||||
@ -1123,25 +1123,10 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
    return if only_deps?
 | 
			
		||||
 | 
			
		||||
    if pour_bottle?(output_warning: true)
 | 
			
		||||
      begin
 | 
			
		||||
        downloader.fetch
 | 
			
		||||
      rescue Exception => e # rubocop:disable Lint/RescueException
 | 
			
		||||
        raise if Homebrew::EnvConfig.developer? ||
 | 
			
		||||
                 Homebrew::EnvConfig.no_bottle_source_fallback? ||
 | 
			
		||||
                 force_bottle? ||
 | 
			
		||||
                 e.is_a?(Interrupt)
 | 
			
		||||
 | 
			
		||||
        @pour_failed = true
 | 
			
		||||
        onoe e.message
 | 
			
		||||
        opoo "Bottle installation failed: building from source."
 | 
			
		||||
        fetch_dependencies
 | 
			
		||||
      end
 | 
			
		||||
    unless pour_bottle?(output_warning: true)
 | 
			
		||||
      formula.fetch_patches
 | 
			
		||||
      formula.resources.each(&:fetch)
 | 
			
		||||
    end
 | 
			
		||||
    return if pour_bottle?
 | 
			
		||||
 | 
			
		||||
    formula.fetch_patches
 | 
			
		||||
    formula.resources.each(&:fetch)
 | 
			
		||||
    downloader.fetch
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -1170,10 +1155,12 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
    tab = Tab.for_keg(keg)
 | 
			
		||||
 | 
			
		||||
    CxxStdlib.check_compatibility(
 | 
			
		||||
      formula, formula.recursive_dependencies,
 | 
			
		||||
      Keg.new(formula.prefix), tab.compiler
 | 
			
		||||
    )
 | 
			
		||||
    unless ignore_deps?
 | 
			
		||||
      CxxStdlib.check_compatibility(
 | 
			
		||||
        formula, formula.recursive_dependencies,
 | 
			
		||||
        Keg.new(formula.prefix), tab.compiler
 | 
			
		||||
      )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    tab.tap = formula.tap
 | 
			
		||||
    tab.poured_from_bottle = true
 | 
			
		||||
@ -1253,10 +1240,9 @@ class FormulaInstaller
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    return if forbidden_licenses.blank?
 | 
			
		||||
    return if ignore_deps?
 | 
			
		||||
 | 
			
		||||
    compute_dependencies.each do |dep, _|
 | 
			
		||||
      next if @ignore_deps
 | 
			
		||||
 | 
			
		||||
      dep_f = dep.to_formula
 | 
			
		||||
      next unless SPDX.licenses_forbid_installation? dep_f.license, forbidden_licenses
 | 
			
		||||
 | 
			
		||||
@ -1265,7 +1251,8 @@ class FormulaInstaller
 | 
			
		||||
          #{SPDX.license_expression_to_string dep_f.license}.
 | 
			
		||||
      EOS
 | 
			
		||||
    end
 | 
			
		||||
    return if @only_deps
 | 
			
		||||
 | 
			
		||||
    return if only_deps?
 | 
			
		||||
 | 
			
		||||
    return unless SPDX.licenses_forbid_installation? formula.license, forbidden_licenses
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,19 @@ module Formulary
 | 
			
		||||
    cache.fetch(path)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.clear_cache
 | 
			
		||||
    cache.each do |key, klass|
 | 
			
		||||
      next if key == :formulary_factory
 | 
			
		||||
 | 
			
		||||
      namespace = klass.name.deconstantize
 | 
			
		||||
      next if namespace.deconstantize != name
 | 
			
		||||
 | 
			
		||||
      remove_const(namespace.demodulize)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    super
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.load_formula(name, path, contents, namespace, flags:)
 | 
			
		||||
    raise "Formula loading disabled by HOMEBREW_DISABLE_LOAD_FORMULA!" if Homebrew::EnvConfig.disable_load_formula?
 | 
			
		||||
 | 
			
		||||
@ -47,6 +60,7 @@ module Formulary
 | 
			
		||||
      mod.const_set(:BUILD_FLAGS, flags)
 | 
			
		||||
      mod.module_eval(contents, path)
 | 
			
		||||
    rescue NameError, ArgumentError, ScriptError, MethodDeprecatedError => e
 | 
			
		||||
      remove_const(namespace)
 | 
			
		||||
      raise FormulaUnreadableError.new(name, e)
 | 
			
		||||
    end
 | 
			
		||||
    class_name = class_s(name)
 | 
			
		||||
@ -58,6 +72,7 @@ module Formulary
 | 
			
		||||
                      .map { |const_name| mod.const_get(const_name) }
 | 
			
		||||
                      .select { |const| const.is_a?(Class) }
 | 
			
		||||
      new_exception = FormulaClassUnavailableError.new(name, path, class_name, class_list)
 | 
			
		||||
      remove_const(namespace)
 | 
			
		||||
      raise new_exception, "", e.backtrace
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@ -467,14 +482,14 @@ module Formulary
 | 
			
		||||
      return FormulaLoader.new(name, path)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if newref = CoreTap.instance.formula_renames[ref]
 | 
			
		||||
    if (newref = CoreTap.instance.formula_renames[ref])
 | 
			
		||||
      formula_with_that_oldname = core_path(newref)
 | 
			
		||||
      return FormulaLoader.new(newref, formula_with_that_oldname) if formula_with_that_oldname.file?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    possible_tap_newname_formulae = []
 | 
			
		||||
    Tap.each do |tap|
 | 
			
		||||
      if newref = tap.formula_renames[ref]
 | 
			
		||||
      if (newref = tap.formula_renames[ref])
 | 
			
		||||
        possible_tap_newname_formulae << "#{tap.name}/#{newref}"
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
@ -502,11 +517,11 @@ module Formulary
 | 
			
		||||
    name = name.to_s.downcase
 | 
			
		||||
    taps.map do |tap|
 | 
			
		||||
      Pathname.glob([
 | 
			
		||||
                      "#{tap}Formula/#{name}.rb",
 | 
			
		||||
                      "#{tap}HomebrewFormula/#{name}.rb",
 | 
			
		||||
                      "#{tap}#{name}.rb",
 | 
			
		||||
                      "#{tap}Aliases/#{name}",
 | 
			
		||||
                    ]).find(&:file?)
 | 
			
		||||
        "#{tap}Formula/#{name}.rb",
 | 
			
		||||
        "#{tap}HomebrewFormula/#{name}.rb",
 | 
			
		||||
        "#{tap}#{name}.rb",
 | 
			
		||||
        "#{tap}Aliases/#{name}",
 | 
			
		||||
      ]).find(&:file?)
 | 
			
		||||
    end.compact
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										185
									
								
								Library/Homebrew/github_packages.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								Library/Homebrew/github_packages.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,185 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "utils/curl"
 | 
			
		||||
require "json"
 | 
			
		||||
 | 
			
		||||
# GitHub Packages client.
 | 
			
		||||
#
 | 
			
		||||
# @api private
 | 
			
		||||
class GitHubPackages
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
 | 
			
		||||
  include Context
 | 
			
		||||
  include Utils::Curl
 | 
			
		||||
 | 
			
		||||
  URL_DOMAIN = "ghcr.io"
 | 
			
		||||
  URL_PREFIX = "https://#{URL_DOMAIN}/v2/"
 | 
			
		||||
  URL_REGEX = %r{#{Regexp.escape(URL_PREFIX)}([\w-]+)/([\w-]+)}.freeze
 | 
			
		||||
 | 
			
		||||
  sig { returns(String) }
 | 
			
		||||
  def inspect
 | 
			
		||||
    "#<GitHubPackages: org=#{@github_org}>"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(org: T.nilable(String)).void }
 | 
			
		||||
  def initialize(org: "homebrew")
 | 
			
		||||
    @github_org = org
 | 
			
		||||
 | 
			
		||||
    raise UsageError, "Must set a GitHub organisation!" unless @github_org
 | 
			
		||||
 | 
			
		||||
    ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"] = "1" if @github_org == "homebrew" && !OS.mac?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  sig { params(bottles_hash: T::Hash[String, T.untyped]).void }
 | 
			
		||||
  def upload_bottles(bottles_hash)
 | 
			
		||||
    user = Homebrew::EnvConfig.github_packages_user
 | 
			
		||||
    token = Homebrew::EnvConfig.github_packages_token
 | 
			
		||||
 | 
			
		||||
    raise UsageError, "HOMEBREW_GITHUB_PACKAGES_USER is unset." if user.blank?
 | 
			
		||||
    raise UsageError, "HOMEBREW_GITHUB_PACKAGES_TOKEN is unset." if token.blank?
 | 
			
		||||
 | 
			
		||||
    docker = HOMEBREW_PREFIX/"bin/docker"
 | 
			
		||||
    unless docker.exist?
 | 
			
		||||
      ohai "Installing `docker` for upload..."
 | 
			
		||||
      safe_system HOMEBREW_BREW_FILE, "install", "--formula", "docker"
 | 
			
		||||
      docker = Formula["docker"].opt_bin/"docker"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    puts
 | 
			
		||||
    system_command!(docker, verbose: true, print_stdout: true, input: token, args: [
 | 
			
		||||
      "login", "--username", user, "--password-stdin", URL_DOMAIN
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
    oras = HOMEBREW_PREFIX/"bin/oras"
 | 
			
		||||
    unless oras.exist?
 | 
			
		||||
      ohai "Installing `oras` for upload..."
 | 
			
		||||
      safe_system HOMEBREW_BREW_FILE, "install", "oras"
 | 
			
		||||
      oras = Formula["oras"].opt_bin/"oras"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    bottles_hash.each do |formula_name, bottle_hash|
 | 
			
		||||
      _, org, repo, = *bottle_hash["bottle"]["root_url"].match(URL_REGEX)
 | 
			
		||||
 | 
			
		||||
      # docker CLI insists on lowercase org ("repository name")
 | 
			
		||||
      org = org.downcase
 | 
			
		||||
      image = "#{URL_DOMAIN}/#{org}/#{repo}/#{formula_name}"
 | 
			
		||||
 | 
			
		||||
      version = bottle_hash["formula"]["pkg_version"]
 | 
			
		||||
      rebuild = if (rebuild = bottle_hash["bottle"]["rebuild"]).positive?
 | 
			
		||||
        ".#{rebuild}"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      formula_path = HOMEBREW_REPOSITORY/bottle_hash["formula"]["path"]
 | 
			
		||||
      formula = Formulary.factory(formula_path)
 | 
			
		||||
 | 
			
		||||
      image_tags = bottle_hash["bottle"]["tags"].map do |bottle_tag, tag_hash|
 | 
			
		||||
        local_file = tag_hash["local_filename"]
 | 
			
		||||
        odebug "Uploading #{local_file}"
 | 
			
		||||
 | 
			
		||||
        tag = "#{version}.#{bottle_tag}#{rebuild}"
 | 
			
		||||
 | 
			
		||||
        tab = Tab.from_file_content(
 | 
			
		||||
          Utils.safe_popen_read("tar", "xfO", local_file, "#{formula_name}/#{version}/INSTALL_RECEIPT.json"),
 | 
			
		||||
          "#{local_file}/#{formula_name}/#{version}",
 | 
			
		||||
        )
 | 
			
		||||
        created_time = tab.source_modified_time
 | 
			
		||||
        created_time ||= Time.now
 | 
			
		||||
 | 
			
		||||
        # TODO: ideally most/all of these attributes would be stored in the
 | 
			
		||||
        # bottle JSON rather than reading them from the formula.
 | 
			
		||||
        git_revision = formula.tap.git_head
 | 
			
		||||
        git_path = formula_path.to_s.delete_prefix("#{formula.tap.path}/")
 | 
			
		||||
        manifest_hash = {
 | 
			
		||||
          "org.opencontainers.image.title"    => formula.full_name,
 | 
			
		||||
          "org.opencontainers.image.url"      => formula.homepage,
 | 
			
		||||
          "org.opencontainers.image.version"  => version,
 | 
			
		||||
          "org.opencontainers.image.revision" => git_revision,
 | 
			
		||||
          "org.opencontainers.image.source"   => "https://github.com/#{org}/#{repo}/blob/#{git_revision}/#{git_path}",
 | 
			
		||||
          "org.opencontainers.image.created"  => created_time.strftime("%F"),
 | 
			
		||||
        }
 | 
			
		||||
        manifest_hash["org.opencontainers.image.description"] = formula.desc if formula.desc.present?
 | 
			
		||||
        manifest_hash["org.opencontainers.image.license"] = formula.license if formula.license.present?
 | 
			
		||||
 | 
			
		||||
        manifest_annotations = Pathname("#{formula_name}.#{tag}.annotations.json")
 | 
			
		||||
        manifest_annotations.unlink if manifest_annotations.exist?
 | 
			
		||||
        manifest_annotations.write({ "$manifest" => manifest_hash }.to_json)
 | 
			
		||||
 | 
			
		||||
        os_version = if tab.built_on.present?
 | 
			
		||||
          /(\d+\.)*\d+/ =~ tab.built_on["os_version"]
 | 
			
		||||
          Regexp.last_match(0)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # TODO: ideally most/all of these attributes would be stored in the
 | 
			
		||||
        # bottle JSON rather than reading them from the formula.
 | 
			
		||||
        os, arch = if @bottle_tag.to_s.end_with?("_linux")
 | 
			
		||||
          ["linux", "amd64"]
 | 
			
		||||
        else
 | 
			
		||||
          os = "darwin"
 | 
			
		||||
          macos_version = MacOS::Version.from_symbol(bottle_tag.to_sym)
 | 
			
		||||
          os_version ||= macos_version.to_f.to_s
 | 
			
		||||
          arch = if macos_version.arch == :arm64
 | 
			
		||||
            "arm64"
 | 
			
		||||
          else
 | 
			
		||||
            "amd64"
 | 
			
		||||
          end
 | 
			
		||||
          [os, arch]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        tar_sha256 = Digest::SHA256.hexdigest(
 | 
			
		||||
          Utils.safe_popen_read("gunzip", "--stdout", "--decompress", local_file),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        config_hash = {
 | 
			
		||||
          "architecture" => arch,
 | 
			
		||||
          "os"           => os,
 | 
			
		||||
          "os.version"   => os_version,
 | 
			
		||||
          "rootfs"       => {
 | 
			
		||||
            "type"     => "layers",
 | 
			
		||||
            "diff_ids" => ["sha256:#{tar_sha256}"],
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        manifest_config = Pathname("#{formula_name}.#{tag}.config.json")
 | 
			
		||||
        manifest_config.unlink if manifest_config.exist?
 | 
			
		||||
        manifest_config.write(config_hash.to_json)
 | 
			
		||||
 | 
			
		||||
        # TODO: If we push the architecture-specific images to the tag :latest,
 | 
			
		||||
        # then we don't need to delete the architecture-specific tags.
 | 
			
		||||
        image_tag = "#{image}:#{tag}"
 | 
			
		||||
        puts
 | 
			
		||||
        system_command!(oras, verbose: true, print_stdout: true, args: [
 | 
			
		||||
          "push", image_tag,
 | 
			
		||||
          "--verbose",
 | 
			
		||||
          "--manifest-annotations=#{manifest_annotations}",
 | 
			
		||||
          "--manifest-config=#{manifest_config}:application/vnd.oci.image.config.v1+json",
 | 
			
		||||
          "--username", user,
 | 
			
		||||
          "--password", token,
 | 
			
		||||
          "#{local_file}:application/vnd.oci.image.layer.v1.tar+gzip"
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        image_tag
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      image_tag = "#{image}:#{version}#{rebuild}"
 | 
			
		||||
      puts
 | 
			
		||||
      system_command!(docker, verbose: true, print_stdout: true, args: [
 | 
			
		||||
        "buildx", "imagetools", "create", "--tag", image_tag, *image_tags
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      # TODO: once the main image metadata is working correctly delete the package using:
 | 
			
		||||
      # `curl -X DELETE -u $HOMEBREW_GITHUB_PACKAGES_USER:$HOMEBREW_GITHUB_PACKAGES_TOKEN
 | 
			
		||||
      #  https://api.github.com/orgs/Homebrew/packages/container/homebrew-core%2F$PACKAGE/versions/$VERSION`
 | 
			
		||||
      # Alternatively, if we push the architecture-specific images to the tag :latest,
 | 
			
		||||
      # then we don't need to delete the architecture-specific tags.
 | 
			
		||||
      # Alternatively, remove all usage of `docker` here instead.
 | 
			
		||||
    end
 | 
			
		||||
  ensure
 | 
			
		||||
    if docker
 | 
			
		||||
      puts
 | 
			
		||||
      system_command!(docker, verbose: true, print_stdout: true, args: [
 | 
			
		||||
        "logout", URL_DOMAIN
 | 
			
		||||
      ])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										44
									
								
								Library/Homebrew/github_releases.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								Library/Homebrew/github_releases.rb
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
# typed: false
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require "utils/github"
 | 
			
		||||
require "json"
 | 
			
		||||
 | 
			
		||||
# GitHub Releases client.
 | 
			
		||||
#
 | 
			
		||||
# @api private
 | 
			
		||||
class GitHubReleases
 | 
			
		||||
  extend T::Sig
 | 
			
		||||
 | 
			
		||||
  include Context
 | 
			
		||||
  include Utils::Curl
 | 
			
		||||
 | 
			
		||||
  URL_REGEX = %r{https://github\.com/([\w-]+)/([\w-]+)?/releases/download/(.+)}.freeze
 | 
			
		||||
 | 
			
		||||
  sig { params(bottles_hash: T::Hash[String, T.untyped]).void }
 | 
			
		||||
  def upload_bottles(bottles_hash)
 | 
			
		||||
    bottles_hash.each_value do |bottle_hash|
 | 
			
		||||
      root_url = bottle_hash["bottle"]["root_url"]
 | 
			
		||||
      url_match = root_url.match URL_REGEX
 | 
			
		||||
      _, user, repo, tag = *url_match
 | 
			
		||||
 | 
			
		||||
      # Ensure a release is created.
 | 
			
		||||
      release = begin
 | 
			
		||||
        rel = GitHub.get_release user, repo, tag
 | 
			
		||||
        odebug "Existing GitHub release \"#{tag}\" found"
 | 
			
		||||
        rel
 | 
			
		||||
      rescue GitHub::API::HTTPNotFoundError
 | 
			
		||||
        odebug "Creating new GitHub release \"#{tag}\""
 | 
			
		||||
        GitHub.create_or_update_release user, repo, tag
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # Upload bottles as release assets.
 | 
			
		||||
      bottle_hash["bottle"]["tags"].each_value do |tag_hash|
 | 
			
		||||
        remote_file = tag_hash["filename"]
 | 
			
		||||
        local_file = tag_hash["local_filename"]
 | 
			
		||||
        odebug "Uploading #{remote_file}"
 | 
			
		||||
        GitHub.upload_release_asset user, repo, release["id"], local_file: local_file, remote_file: remote_file
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
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