Merge branch 'master' into add-dry-run-option-to-brew-install
This commit is contained in:
commit
9c0708f2b5
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@ -14,7 +14,7 @@ permissions:
|
||||
jobs:
|
||||
ubuntu:
|
||||
if: startsWith(github.repository, 'Homebrew/')
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
7
.github/workflows/sorbet.yml
vendored
7
.github/workflows/sorbet.yml
vendored
@ -16,7 +16,7 @@ permissions:
|
||||
jobs:
|
||||
tapioca:
|
||||
if: github.repository == 'Homebrew/brew'
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -46,12 +46,13 @@ jobs:
|
||||
if git ls-remote --exit-code --heads origin "${BRANCH}"
|
||||
then
|
||||
git checkout "${BRANCH}"
|
||||
git reset --hard origin/master
|
||||
git checkout "${GITHUB_WORKSPACE}/Library/Homebrew/sorbet"
|
||||
else
|
||||
git checkout --no-track -B "${BRANCH}" origin/master
|
||||
fi
|
||||
|
||||
if brew typecheck --update --fail-if-not-changed
|
||||
brew typecheck --update
|
||||
if ! git diff --stat --exit-code "${GITHUB_WORKSPACE}/Library/Homebrew/sorbet"
|
||||
then
|
||||
git add "${GITHUB_WORKSPACE}/Library/Homebrew/sorbet"
|
||||
git commit -m "sorbet: Update RBI files." \
|
||||
|
||||
9
.github/workflows/spdx.yml
vendored
9
.github/workflows/spdx.yml
vendored
@ -7,12 +7,15 @@ on:
|
||||
- master
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
spdx:
|
||||
if: github.repository == 'Homebrew/brew'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -42,12 +45,12 @@ jobs:
|
||||
if git ls-remote --exit-code --heads origin "${BRANCH}"
|
||||
then
|
||||
git checkout "${BRANCH}"
|
||||
git reset --hard origin/master
|
||||
git checkout "${GITHUB_WORKSPACE}/Library/Homebrew/data/spdx"
|
||||
else
|
||||
git checkout --no-track -B "${BRANCH}" origin/master
|
||||
fi
|
||||
|
||||
if brew update-license-data --fail-if-not-changed
|
||||
if brew update-license-data
|
||||
then
|
||||
git add "${GITHUB_WORKSPACE}/Library/Homebrew/data/spdx"
|
||||
git commit -m "spdx: update license data." -m "Autogenerated by [a scheduled GitHub Action](https://github.com/Homebrew/brew/blob/master/.github/workflows/spdx.yml)."
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
name: Update maintainers, manpage and completions
|
||||
name: Update sponsors, maintainers, manpage and completions
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/update-man-completions.yml
|
||||
- .github/workflows/sponsors-maintainers-man-completions.yml
|
||||
- README.md
|
||||
- Library/Homebrew/cmd/**
|
||||
- Library/Homebrew/dev-cmd/**
|
||||
@ -12,8 +12,6 @@ on:
|
||||
- Library/Homebrew/cli/parser.rb
|
||||
- Library/Homebrew/completions.rb
|
||||
- Library/Homebrew/env_config.rb
|
||||
branches:
|
||||
- master
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
@ -22,11 +20,12 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-manpage:
|
||||
runs-on: ubuntu-latest
|
||||
updates:
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.repository == 'Homebrew/brew'
|
||||
steps:
|
||||
- name: Setup Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Configure Git user
|
||||
@ -39,35 +38,68 @@ jobs:
|
||||
with:
|
||||
signing_key: ${{ secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY }}
|
||||
|
||||
- name: Update maintainers, manpage and completions
|
||||
- name: Cache Bundler RubyGems
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
|
||||
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
|
||||
restore-keys: ${{ runner.os }}-rubygems-
|
||||
|
||||
- name: Update sponsors, maintainers, manpage and completions
|
||||
id: update
|
||||
run: |
|
||||
git fetch origin
|
||||
|
||||
BRANCH=update-man-completions
|
||||
if [[ -n "$GITHUB_REF_NAME" && "$GITHUB_REF_NAME" != "master" ]]
|
||||
then
|
||||
BRANCH="$GITHUB_REF_NAME"
|
||||
else
|
||||
BRANCH=sponsors-maintainers-man-completions
|
||||
fi
|
||||
echo "::set-output name=branch::${BRANCH}"
|
||||
|
||||
if git ls-remote --exit-code --heads origin "${BRANCH}"
|
||||
then
|
||||
git checkout "${BRANCH}"
|
||||
git reset --hard origin/master
|
||||
git checkout "${GITHUB_WORKSPACE}/README.md" \
|
||||
"${GITHUB_WORKSPACE}/docs/Manpage.md" \
|
||||
"${GITHUB_WORKSPACE}/manpages/brew.1" \
|
||||
"${GITHUB_WORKSPACE}/completions"
|
||||
else
|
||||
git checkout --no-track -B "${BRANCH}" origin/master
|
||||
fi
|
||||
|
||||
if [[ "${{github.event_name}}" != "push" ]]
|
||||
if brew update-sponsors
|
||||
then
|
||||
brew update-maintainers
|
||||
git add "${GITHUB_WORKSPACE}/README.md"
|
||||
git commit -m "Update sponsors." \
|
||||
-m "Autogenerated by the [sponsors-maintainers-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sponsors-maintainers-man-completions.yml) workflow."
|
||||
COMMITTED=true
|
||||
fi
|
||||
|
||||
if brew generate-man-completions --fail-if-not-changed
|
||||
if brew update-maintainers
|
||||
then
|
||||
git add "${GITHUB_WORKSPACE}/README.md" \
|
||||
"${GITHUB_WORKSPACE}/docs/Manpage.md" \
|
||||
"${GITHUB_WORKSPACE}/manpages/brew.1"
|
||||
git commit -m "Update maintainers." \
|
||||
-m "Autogenerated by the [sponsors-maintainers-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sponsors-maintainers-man-completions.yml) workflow."
|
||||
COMMITTED=true
|
||||
fi
|
||||
|
||||
if brew generate-man-completions
|
||||
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-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/update-man-completions.yml) workflow."
|
||||
git commit -m "Update manpage and completions." \
|
||||
-m "Autogenerated by the [sponsors-maintainers-man-completions](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sponsors-maintainers-man-completions.yml) workflow."
|
||||
COMMITTED=true
|
||||
fi
|
||||
|
||||
if [[ -n "$COMMITTED" ]]
|
||||
then
|
||||
echo "::set-output name=committed::true"
|
||||
PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state")"
|
||||
if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]]
|
||||
@ -77,7 +109,7 @@ jobs:
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
|
||||
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_BREW_UPDATE_MAINTAINERS_TOKEN }}
|
||||
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.HOMEBREW_BREW_UPDATE_SPONSORS_MAINTAINERS_TOKEN }}
|
||||
HOMEBREW_GPG_PASSPHRASE: ${{ secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY_PASSPHRASE }}
|
||||
|
||||
- name: Push commits
|
||||
87
.github/workflows/tests.yml
vendored
87
.github/workflows/tests.yml
vendored
@ -20,7 +20,7 @@ concurrency:
|
||||
jobs:
|
||||
syntax:
|
||||
if: github.repository == 'Homebrew/brew'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -49,10 +49,10 @@ jobs:
|
||||
vale docs/
|
||||
|
||||
tap-syntax:
|
||||
name: tap syntax (Linux)
|
||||
name: tap syntax
|
||||
needs: syntax
|
||||
if: startsWith(github.repository, 'Homebrew/')
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -82,13 +82,13 @@ jobs:
|
||||
brew update-test --commit=HEAD
|
||||
|
||||
- name: Run brew readall on all taps
|
||||
run: brew readall --aliases
|
||||
run: brew readall --eval-all --aliases
|
||||
|
||||
- name: Run brew style on homebrew-core for Linux
|
||||
run: brew style --display-cop-names homebrew/core
|
||||
|
||||
- name: Run brew audit --skip-style on all taps
|
||||
run: brew audit --skip-style --except=version --display-failures-only
|
||||
run: brew audit --eval-all --skip-style --except=version --display-failures-only
|
||||
|
||||
- name: Set up all Homebrew taps
|
||||
run: |
|
||||
@ -145,8 +145,8 @@ jobs:
|
||||
homebrew/cask-versions
|
||||
|
||||
vendored-gems:
|
||||
name: vendored gems (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
name: vendored gems
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -167,40 +167,78 @@ jobs:
|
||||
|
||||
docker:
|
||||
needs: syntax
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Build Docker image
|
||||
run: docker build -t brew --build-arg=version=16.04 .
|
||||
run: |
|
||||
docker build -t brew --build-arg=version=22.04 \
|
||||
--label org.opencontainers.image.created="$(date --rfc-3339=seconds --utc)" \
|
||||
--label org.opencontainers.image.url="https://brew.sh" \
|
||||
--label org.opencontainers.image.documentation="https://docs.brew.sh" \
|
||||
--label org.opencontainers.image.source="https://github.com/${GITHUB_REPOSITORY}" \
|
||||
--label org.opencontainers.image.revision="${GITHUB_SHA}" \
|
||||
--label org.opencontainers.image.vendor="${GITHUB_REPOSITORY_OWNER}" \
|
||||
--label org.opencontainers.image.licenses="BSD-2-Clause" \
|
||||
.
|
||||
|
||||
- name: Deploy the Docker image to GitHub Packages and Docker Hub
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
echo ${{secrets.HOMEBREW_BREW_GITHUB_PACKAGES_TOKEN}} |
|
||||
docker login ghcr.io -u BrewTestBot --password-stdin
|
||||
docker tag brew "ghcr.io/homebrew/ubuntu16.04:master"
|
||||
docker push "ghcr.io/homebrew/ubuntu16.04:master"
|
||||
docker tag brew "ghcr.io/homebrew/ubuntu22.04:master"
|
||||
docker push "ghcr.io/homebrew/ubuntu22.04:master"
|
||||
echo ${{secrets.HOMEBREW_BREW_DOCKER_TOKEN}} |
|
||||
docker login -u brewtestbot --password-stdin
|
||||
docker tag brew "homebrew/ubuntu16.04:master"
|
||||
docker tag brew "homebrew/ubuntu22.04:master"
|
||||
docker push "homebrew/ubuntu22.04:master"
|
||||
|
||||
- name: Build deprecated 16.04 Docker image
|
||||
run: |
|
||||
echo "homebrew/ubuntu16.04:master is deprecated and will soon be retired. Use homebrew/ubuntu22.04:master or homebrew/ubuntu16.04 or homebrew/brew. For CI, homebrew/ubuntu22.04:master is recommended." > .docker-deprecate
|
||||
docker build -t brew-deprecated --build-arg=version=16.04 \
|
||||
--label org.opencontainers.image.created="$(date --rfc-3339=seconds --utc)" \
|
||||
--label org.opencontainers.image.url="https://brew.sh" \
|
||||
--label org.opencontainers.image.documentation="https://docs.brew.sh" \
|
||||
--label org.opencontainers.image.source="https://github.com/${GITHUB_REPOSITORY}" \
|
||||
--label org.opencontainers.image.revision="${GITHUB_SHA}" \
|
||||
--label org.opencontainers.image.vendor="${GITHUB_REPOSITORY_OWNER}" \
|
||||
--label org.opencontainers.image.licenses="BSD-2-Clause" \
|
||||
--label org.opencontainers.image.support.end-of-support="2022-09-07T00:00:00Z" \
|
||||
.
|
||||
|
||||
- name: Deploy the deprecated 16.04 Docker image to GitHub Packages and Docker Hub
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
docker tag brew-deprecated "ghcr.io/homebrew/ubuntu16.04:master"
|
||||
docker push "ghcr.io/homebrew/ubuntu16.04:master"
|
||||
docker tag brew-deprecated "homebrew/ubuntu16.04:master"
|
||||
docker push "homebrew/ubuntu16.04:master"
|
||||
|
||||
tests:
|
||||
name: ${{ matrix.name }}
|
||||
needs: syntax
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: tests (no-compatibility mode)
|
||||
test-flags: --no-compat --online --coverage
|
||||
runs-on: ubuntu-22.04
|
||||
- name: tests (generic OS)
|
||||
test-flags: --generic --online --coverage
|
||||
- name: tests (Linux)
|
||||
runs-on: ubuntu-22.04
|
||||
- name: tests (Ubuntu 22.04)
|
||||
test-flags: --online --coverage
|
||||
runs-on: ubuntu-22.04
|
||||
# Enable later once this can be fixed.
|
||||
# - name: tests (Ubuntu 18.04)
|
||||
# test-flags: --online --coverage
|
||||
# runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -238,15 +276,24 @@ jobs:
|
||||
env:
|
||||
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378
|
||||
- uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70
|
||||
with:
|
||||
files: Library/Homebrew/test/coverage/coverage.xml
|
||||
|
||||
test-default-formula-linux:
|
||||
name: test default formula (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
env:
|
||||
HOMEBREW_BOOTSNAP: 1
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: test default formula (Ubuntu 22.04)
|
||||
test-flags: --online --coverage
|
||||
runs-on: ubuntu-22.04
|
||||
- name: test default formula (Ubuntu 18.04)
|
||||
test-flags: --online --coverage
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -260,7 +307,7 @@ jobs:
|
||||
name: test everything (macOS)
|
||||
needs: syntax
|
||||
if: startsWith(github.repository, 'Homebrew/')
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-12
|
||||
env:
|
||||
HOMEBREW_BOOTSNAP: 1
|
||||
steps:
|
||||
@ -301,7 +348,7 @@ jobs:
|
||||
Library/Taps/homebrew/homebrew-services
|
||||
|
||||
- name: Run brew readall on all taps
|
||||
run: brew readall --aliases
|
||||
run: brew readall --eval-all --aliases
|
||||
|
||||
- name: Install brew tests dependencies
|
||||
run: brew install subversion curl
|
||||
@ -339,6 +386,6 @@ jobs:
|
||||
|
||||
- run: brew test-bot --only-formulae --test-default-formula
|
||||
|
||||
- uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378
|
||||
- uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70
|
||||
with:
|
||||
files: Library/Homebrew/test/coverage/coverage.xml
|
||||
|
||||
5
.github/workflows/triage-issues.yml
vendored
5
.github/workflows/triage-issues.yml
vendored
@ -32,12 +32,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests
|
||||
uses: actions/stale@v5
|
||||
uses: actions/stale@v6
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 21
|
||||
days-before-close: 7
|
||||
close-issue-reason: "not_planned"
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs.
|
||||
@ -58,7 +57,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mark/Close Stale `bump-formula-pr` and `bump-cask-pr` Pull Requests
|
||||
uses: actions/stale@v5
|
||||
uses: actions/stale@v6
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 2
|
||||
|
||||
2
.github/workflows/triage.yml
vendored
2
.github/workflows/triage.yml
vendored
@ -18,7 +18,7 @@ concurrency: triage-${{ github.head_ref }}
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
if: startsWith(github.repository, 'Homebrew/')
|
||||
steps:
|
||||
- name: Re-run this workflow
|
||||
|
||||
16
.github/workflows/vendor-gems.yml
vendored
16
.github/workflows/vendor-gems.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
contains(github.event.pull_request.title, '/Library/Homebrew')
|
||||
)
|
||||
)
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -60,16 +60,12 @@ jobs:
|
||||
GEM_NAME: ${{ steps.checkout.outputs.gem_name }}
|
||||
HOMEBREW_GPG_PASSPHRASE: ${{ secrets.BREWTESTBOT_GPG_SIGNING_SUBKEY_PASSPHRASE }}
|
||||
run: |
|
||||
set -u
|
||||
|
||||
if brew typecheck --update --fail-if-not-changed
|
||||
brew typecheck --update
|
||||
if ! git diff --stat --exit-code "${GITHUB_WORKSPACE}/Library/Homebrew/sorbet"
|
||||
then
|
||||
if git add Library/Homebrew/sorbet
|
||||
then
|
||||
git commit -m "Update RBI files for ${GEM_NAME}."
|
||||
fi
|
||||
|
||||
git reset --hard
|
||||
git add "${GITHUB_WORKSPACE}/Library/Homebrew/sorbet"
|
||||
git commit -m "Update RBI files for ${GEM_NAME}." \
|
||||
-m "Autogenerated by the [vendor-gems](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/vendor-gemss.yml) workflow."
|
||||
fi
|
||||
|
||||
- name: Push to pull request
|
||||
|
||||
@ -21,6 +21,8 @@ RSpec/RepeatedDescription:
|
||||
Enabled: false
|
||||
RSpec/StubbedMock:
|
||||
Enabled: false
|
||||
RSpec/NoExpectationExample:
|
||||
Enabled: false
|
||||
|
||||
# TODO: try to reduce these
|
||||
RSpec/ExampleLength:
|
||||
|
||||
@ -2,7 +2,11 @@
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
ruby ">= 2.6.0"
|
||||
if ENV.fetch("HOMEBREW_DEVELOPER", "").empty? || ENV.fetch("HOMEBREW_USE_RUBY_FROM_PATH", "").empty?
|
||||
ruby "~> 2.6.0"
|
||||
else
|
||||
ruby ">= 2.6.0"
|
||||
end
|
||||
|
||||
# disallowed gems (should not be used)
|
||||
# * nokogiri - use rexml instead for XML parsing
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.1.6.1)
|
||||
activesupport (6.1.7)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
ast (2.4.2)
|
||||
bindata (2.4.10)
|
||||
bindata (2.4.11)
|
||||
bootsnap (1.13.0)
|
||||
msgpack (~> 1.2)
|
||||
byebug (11.1.3)
|
||||
@ -18,7 +18,7 @@ GEM
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.2.5)
|
||||
connection_pool (2.3.0)
|
||||
did_you_mean (1.6.1)
|
||||
diff-lcs (1.5.0)
|
||||
docile (1.4.0)
|
||||
@ -57,8 +57,8 @@ GEM
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2022.0105)
|
||||
mini_portile2 (2.8.0)
|
||||
minitest (5.16.2)
|
||||
msgpack (1.5.4)
|
||||
minitest (5.16.3)
|
||||
msgpack (1.6.0)
|
||||
mustache (1.1.1)
|
||||
net-http-digest_auth (1.4.1)
|
||||
net-http-persistent (4.0.1)
|
||||
@ -67,7 +67,7 @@ GEM
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
parallel (1.22.1)
|
||||
parallel_tests (3.11.1)
|
||||
parallel_tests (3.13.0)
|
||||
parallel
|
||||
parlour (8.0.0)
|
||||
commander (~> 4.5)
|
||||
@ -82,9 +82,9 @@ GEM
|
||||
pry (0.14.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
public_suffix (4.0.7)
|
||||
public_suffix (5.0.0)
|
||||
racc (1.6.0)
|
||||
rack (2.2.4)
|
||||
rack (3.0.0)
|
||||
rainbow (3.1.1)
|
||||
rbi (0.0.14)
|
||||
ast
|
||||
@ -92,7 +92,7 @@ GEM
|
||||
sorbet-runtime (>= 0.5.9204)
|
||||
unparser
|
||||
rdiscount (2.2.0.2)
|
||||
regexp_parser (2.5.0)
|
||||
regexp_parser (2.6.0)
|
||||
rexml (3.2.5)
|
||||
ronn (0.7.3)
|
||||
hpricot (>= 0.8.2)
|
||||
@ -104,7 +104,7 @@ GEM
|
||||
rspec-mocks (~> 3.11.0)
|
||||
rspec-core (3.11.0)
|
||||
rspec-support (~> 3.11.0)
|
||||
rspec-expectations (3.11.0)
|
||||
rspec-expectations (3.11.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.11.0)
|
||||
rspec-github (2.3.1)
|
||||
@ -117,14 +117,14 @@ GEM
|
||||
rspec-support (~> 3.11.0)
|
||||
rspec-retry (0.6.2)
|
||||
rspec-core (> 3.3)
|
||||
rspec-sorbet (1.8.3)
|
||||
rspec-sorbet (1.9.0)
|
||||
sorbet-runtime
|
||||
rspec-support (3.11.0)
|
||||
rspec-support (3.11.1)
|
||||
rspec-wait (0.0.9)
|
||||
rspec (>= 3, < 4)
|
||||
rspec_junit_formatter (0.5.1)
|
||||
rspec_junit_formatter (0.6.0)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (1.35.0)
|
||||
rubocop (1.35.1)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.1.2.1)
|
||||
@ -136,15 +136,15 @@ GEM
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.21.0)
|
||||
parser (>= 3.1.1.0)
|
||||
rubocop-performance (1.14.3)
|
||||
rubocop-performance (1.15.0)
|
||||
rubocop (>= 1.7.0, < 2.0)
|
||||
rubocop-ast (>= 0.4.0)
|
||||
rubocop-rails (2.15.2)
|
||||
rubocop-rails (2.16.1)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 1.7.0, < 2.0)
|
||||
rubocop-rspec (2.12.1)
|
||||
rubocop (~> 1.31)
|
||||
rubocop (>= 1.33.0, < 2.0)
|
||||
rubocop-rspec (2.13.2)
|
||||
rubocop (~> 1.33)
|
||||
rubocop-sorbet (0.6.11)
|
||||
rubocop (>= 0.90.0)
|
||||
ruby-macho (3.0.0)
|
||||
@ -170,7 +170,7 @@ GEM
|
||||
sorbet (>= 0.5.9204)
|
||||
sorbet-runtime (>= 0.5.9204)
|
||||
thor (>= 0.19.2)
|
||||
tapioca (0.7.2)
|
||||
tapioca (0.7.3)
|
||||
bundler (>= 1.17.3)
|
||||
pry (>= 0.12.2)
|
||||
rbi (~> 0.0.0, >= 0.0.14)
|
||||
@ -185,7 +185,7 @@ GEM
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (2.2.0)
|
||||
unicode-display_width (2.3.0)
|
||||
unparser (0.6.4)
|
||||
diff-lcs (~> 1.3)
|
||||
parser (>= 3.1.0)
|
||||
|
||||
@ -26,22 +26,34 @@ module Homebrew
|
||||
Homebrew::API.fetch "#{formula_api_path}/#{name}.json"
|
||||
end
|
||||
|
||||
sig { returns(Array) }
|
||||
sig { returns(Hash) }
|
||||
def all_formulae
|
||||
@all_formulae ||= begin
|
||||
curl_args = %w[--compressed --silent https://formulae.brew.sh/api/formula.json]
|
||||
if cached_formula_json_file.exist? && !cached_formula_json_file.empty?
|
||||
curl_args.prepend("--time-cond", cached_formula_json_file)
|
||||
end
|
||||
curl_download(*curl_args, to: HOMEBREW_CACHE_API/"#{formula_api_path}.json", max_time: 5)
|
||||
curl_download(*curl_args, to: cached_formula_json_file, max_time: 5)
|
||||
|
||||
json_formulae = JSON.parse(cached_formula_json_file.read)
|
||||
|
||||
@all_aliases = {}
|
||||
json_formulae.to_h do |json_formula|
|
||||
json_formula["aliases"].each do |alias_name|
|
||||
@all_aliases[alias_name] = json_formula["name"]
|
||||
end
|
||||
|
||||
[json_formula["name"], json_formula.except("name")]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Hash) }
|
||||
def all_aliases
|
||||
all_formulae if @all_aliases.blank?
|
||||
|
||||
@all_aliases
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,6 +7,7 @@ if ENV["HOMEBREW_STACKPROF"]
|
||||
end
|
||||
|
||||
raise "HOMEBREW_BREW_FILE was not exported! Please call bin/brew directly!" unless ENV["HOMEBREW_BREW_FILE"]
|
||||
raise "#{__FILE__} must not be loaded via `require`." if $PROGRAM_NAME != __FILE__
|
||||
|
||||
std_trap = trap("INT") { exit! 130 } # no backtrace thanks
|
||||
|
||||
@ -86,7 +87,8 @@ begin
|
||||
end
|
||||
|
||||
if internal_cmd || Commands.external_ruby_v2_cmd_path(cmd)
|
||||
if Commands::INSTALL_FROM_API_FORBIDDEN_COMMANDS.include?(cmd) && Homebrew::EnvConfig.install_from_api?
|
||||
if Commands::INSTALL_FROM_API_FORBIDDEN_COMMANDS.include?(cmd) &&
|
||||
Homebrew::EnvConfig.install_from_api? && !Homebrew::EnvConfig.developer?
|
||||
odie "This command cannot be run while HOMEBREW_INSTALL_FROM_API is set!"
|
||||
end
|
||||
|
||||
@ -120,8 +122,9 @@ begin
|
||||
# Unset HOMEBREW_HELP to avoid confusing the tap
|
||||
with_env HOMEBREW_HELP: nil do
|
||||
tap_commands = []
|
||||
cgroup = Utils.popen_read("cat", "/proc/1/cgroup")
|
||||
if %w[azpl_job actions_job docker garden kubepods].none? { |container| cgroup.include?(container) }
|
||||
if File.exist?("/.dockerenv") ||
|
||||
((cgroup = Utils.popen_read("cat", "/proc/1/cgroup").presence) &&
|
||||
%w[azpl_job actions_job docker garden kubepods].none? { |type| cgroup.include?(type) })
|
||||
brew_uid = HOMEBREW_BREW_FILE.stat.uid
|
||||
tap_commands += %W[/usr/bin/sudo -u ##{brew_uid}] if Process.uid.zero? && !brew_uid.zero?
|
||||
end
|
||||
|
||||
@ -197,6 +197,7 @@ check-run-command-as-root() {
|
||||
[[ "$(id -u)" == 0 ]] || return
|
||||
|
||||
# Allow Azure Pipelines/GitHub Actions/Docker/Concourse/Kubernetes to do everything as root (as it's normal there)
|
||||
[[ -f /.dockerenv ]] && return
|
||||
[[ -f /proc/1/cgroup ]] && grep -E "azpl_job|actions_job|docker|garden|kubepods" -q /proc/1/cgroup && return
|
||||
|
||||
# Homebrew Services may need `sudo` for system-wide daemons.
|
||||
@ -358,6 +359,18 @@ fi
|
||||
##### Now, do everything else (that may be a bit slower).
|
||||
#####
|
||||
|
||||
# Docker image deprecation
|
||||
if [[ -f "${HOMEBREW_REPOSITORY}/.docker-deprecate" ]]
|
||||
then
|
||||
DOCKER_DEPRECATION_MESSAGE="$(cat "${HOMEBREW_REPOSITORY}/.docker-deprecate")"
|
||||
if [[ -n "${GITHUB_ACTIONS}" ]]
|
||||
then
|
||||
echo "::warning::${DOCKER_DEPRECATION_MESSAGE}" >&2
|
||||
else
|
||||
opoo "${DOCKER_DEPRECATION_MESSAGE}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# USER isn't always set so provide a fall back for `brew` and subprocesses.
|
||||
export USER="${USER:-$(id -un)}"
|
||||
|
||||
@ -495,18 +508,9 @@ else
|
||||
: "${HOMEBREW_OS_VERSION:=$(uname -r)}"
|
||||
HOMEBREW_OS_USER_AGENT_VERSION="${HOMEBREW_OS_VERSION}"
|
||||
|
||||
# This is set by the user environment.
|
||||
# shellcheck disable=SC2154
|
||||
if [[ -n "${HOMEBREW_ON_DEBIAN7}" ]]
|
||||
then
|
||||
# Special version for our debian 7 docker container used to build binutils
|
||||
HOMEBREW_MINIMUM_CURL_VERSION="7.25.0"
|
||||
HOMEBREW_SYSTEM_CA_CERTIFICATES_TOO_OLD="1"
|
||||
HOMEBREW_FORCE_BREWED_CA_CERTIFICATES="1"
|
||||
else
|
||||
# Ensure the system Curl is a version that supports modern HTTPS certificates.
|
||||
HOMEBREW_MINIMUM_CURL_VERSION="7.41.0"
|
||||
fi
|
||||
# Ensure the system Curl is a version that supports modern HTTPS certificates.
|
||||
HOMEBREW_MINIMUM_CURL_VERSION="7.41.0"
|
||||
|
||||
curl_version_output="$(${HOMEBREW_CURL} --version 2>/dev/null)"
|
||||
curl_name_and_version="${curl_version_output%% (*}"
|
||||
# shellcheck disable=SC2248
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
# This script is loaded by formula_installer as a separate instance.
|
||||
# Thrown exceptions are propagated back to the parent process over a pipe
|
||||
|
||||
raise "#{__FILE__} must not be loaded via `require`." if $PROGRAM_NAME != __FILE__
|
||||
|
||||
old_trap = trap("INT") { exit! 130 }
|
||||
|
||||
require_relative "global"
|
||||
|
||||
@ -33,9 +33,17 @@ class BuildEnvironment
|
||||
module DSL
|
||||
extend T::Sig
|
||||
|
||||
# Initialise @env for each class which may use this DSL (e.g. each formula subclass).
|
||||
# `env` may never be called, and it needs to be initialised before the class is frozen.
|
||||
def inherited(child)
|
||||
super
|
||||
child.instance_eval do
|
||||
@env = BuildEnvironment.new
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(settings: Symbol).returns(BuildEnvironment) }
|
||||
def env(*settings)
|
||||
@env ||= BuildEnvironment.new
|
||||
@env.merge(settings)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "active_support/core_ext/object/deep_dup"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
# Abstract superclass for all artifacts.
|
||||
@ -127,8 +129,9 @@ module Cask
|
||||
|
||||
attr_reader :cask
|
||||
|
||||
def initialize(cask)
|
||||
def initialize(cask, *dsl_args)
|
||||
@cask = cask
|
||||
@dsl_args = dsl_args.deep_dup
|
||||
end
|
||||
|
||||
def config
|
||||
@ -139,6 +142,10 @@ module Cask
|
||||
def to_s
|
||||
"#{summarize} (#{self.class.english_name})"
|
||||
end
|
||||
|
||||
def to_args
|
||||
@dsl_args.reject(&:blank?)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -32,6 +32,10 @@ module Cask
|
||||
abstract_phase(self.class.uninstall_dsl_key)
|
||||
end
|
||||
|
||||
def summarize
|
||||
directives.keys.map(&:to_s).join(", ")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def class_for_dsl_key(dsl_key)
|
||||
@ -44,10 +48,6 @@ module Cask
|
||||
|
||||
class_for_dsl_key(dsl_key).new(cask).instance_eval(&block)
|
||||
end
|
||||
|
||||
def summarize
|
||||
directives.keys.map(&:to_s).join(", ")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -40,7 +40,7 @@ module Cask
|
||||
def initialize(cask, directives)
|
||||
directives.assert_valid_keys!(*ORDERED_DIRECTIVES)
|
||||
|
||||
super(cask)
|
||||
super(cask, **directives)
|
||||
directives[:signal] = Array(directives[:signal]).flatten.each_slice(2).to_a
|
||||
@directives = directives
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ module Cask
|
||||
attr_reader :path, :args
|
||||
|
||||
def initialize(cask, **args)
|
||||
super(cask)
|
||||
super(cask, **args)
|
||||
|
||||
if args.key?(:manual)
|
||||
@path = Pathname(args[:manual])
|
||||
|
||||
@ -23,7 +23,7 @@ module Cask
|
||||
end
|
||||
|
||||
def initialize(cask, path, **stanza_options)
|
||||
super(cask)
|
||||
super(cask, path, **stanza_options)
|
||||
@path = cask.staged_path.join(path)
|
||||
@stanza_options = stanza_options
|
||||
end
|
||||
|
||||
@ -42,12 +42,13 @@ module Cask
|
||||
attr_reader :source, :target
|
||||
|
||||
sig {
|
||||
params(cask: Cask, source: T.nilable(T.any(String, Pathname)), target: T.nilable(T.any(String, Pathname)))
|
||||
params(cask: Cask, source: T.nilable(T.any(String, Pathname)), target_hash: T.any(String, Pathname))
|
||||
.void
|
||||
}
|
||||
def initialize(cask, source, target: nil)
|
||||
super(cask)
|
||||
def initialize(cask, source, **target_hash)
|
||||
super(cask, source, **target_hash)
|
||||
|
||||
target = target_hash[:target]
|
||||
@source_string = source.to_s
|
||||
@target_string = target.to_s
|
||||
source = cask.staged_path.join(source)
|
||||
|
||||
@ -14,7 +14,7 @@ module Cask
|
||||
def self.from_args(cask, *args)
|
||||
raise CaskInvalidError.new(cask.token, "'stage_only' takes only a single argument: true") if args != [true]
|
||||
|
||||
new(cask)
|
||||
new(cask, true)
|
||||
end
|
||||
|
||||
sig { returns(T::Array[T::Boolean]) }
|
||||
|
||||
@ -24,7 +24,7 @@ module Cask
|
||||
|
||||
def initialize(cask, appcast: nil, download: nil, quarantine: nil,
|
||||
token_conflicts: nil, online: nil, strict: nil, signing: nil,
|
||||
new_cask: nil)
|
||||
new_cask: nil, only: [], except: [])
|
||||
|
||||
# `new_cask` implies `online`, `token_conflicts`, `strict` and `signing`
|
||||
online = new_cask if online.nil?
|
||||
@ -47,44 +47,22 @@ module Cask
|
||||
@signing = signing
|
||||
@new_cask = new_cask
|
||||
@token_conflicts = token_conflicts
|
||||
@only = only
|
||||
@except = except
|
||||
end
|
||||
|
||||
def run!
|
||||
check_denylist
|
||||
check_reverse_migration
|
||||
check_required_stanzas
|
||||
check_version
|
||||
check_sha256
|
||||
check_desc
|
||||
check_url
|
||||
check_unnecessary_verified
|
||||
check_missing_verified
|
||||
check_no_match
|
||||
check_generic_artifacts
|
||||
check_token_valid
|
||||
check_token_bad_words
|
||||
check_token_conflicts
|
||||
check_languages
|
||||
check_download
|
||||
check_https_availability
|
||||
check_single_pre_postflight
|
||||
check_single_uninstall_zap
|
||||
check_untrusted_pkg
|
||||
livecheck_result = check_livecheck_version
|
||||
check_hosting_with_livecheck(livecheck_result: livecheck_result)
|
||||
check_appcast_and_livecheck
|
||||
check_latest_with_appcast_or_livecheck
|
||||
check_latest_with_auto_updates
|
||||
check_stanza_requires_uninstall
|
||||
check_appcast_contains_version
|
||||
check_gitlab_repository
|
||||
check_gitlab_repository_archived
|
||||
check_gitlab_prerelease_version
|
||||
check_github_repository
|
||||
check_github_repository_archived
|
||||
check_github_prerelease_version
|
||||
check_bitbucket_repository
|
||||
check_signing
|
||||
only_audits = @only
|
||||
except_audits = @except
|
||||
|
||||
private_methods.map(&:to_s).grep(/^check_/).each do |audit_method_name|
|
||||
name = audit_method_name.delete_prefix("check_")
|
||||
next if !only_audits.empty? && only_audits&.exclude?(name)
|
||||
next if except_audits&.include?(name)
|
||||
|
||||
send(audit_method_name)
|
||||
end
|
||||
|
||||
self
|
||||
rescue => e
|
||||
odebug e, e.backtrace
|
||||
@ -100,10 +78,27 @@ module Cask
|
||||
@warnings ||= []
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def errors?
|
||||
errors.any?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def warnings?
|
||||
warnings.any?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def success?
|
||||
!(errors? || warnings?)
|
||||
end
|
||||
|
||||
sig { params(message: T.nilable(String), location: T.nilable(String)).void }
|
||||
def add_error(message, location: nil)
|
||||
errors << ({ message: message, location: location })
|
||||
end
|
||||
|
||||
sig { params(message: T.nilable(String), location: T.nilable(String)).void }
|
||||
def add_warning(message, location: nil)
|
||||
if strict?
|
||||
add_error message, location: location
|
||||
@ -112,14 +107,6 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
def errors?
|
||||
errors.any?
|
||||
end
|
||||
|
||||
def warnings?
|
||||
warnings.any?
|
||||
end
|
||||
|
||||
def result
|
||||
if errors?
|
||||
Formatter.error("failed")
|
||||
@ -150,12 +137,9 @@ module Cask
|
||||
summary.join("\n")
|
||||
end
|
||||
|
||||
def success?
|
||||
!(errors? || warnings?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def check_untrusted_pkg
|
||||
odebug "Auditing pkg stanza: allow_untrusted"
|
||||
|
||||
@ -170,6 +154,7 @@ module Cask
|
||||
add_error "allow_untrusted is not permitted in official Homebrew Cask taps"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_stanza_requires_uninstall
|
||||
odebug "Auditing stanzas which require an uninstall"
|
||||
|
||||
@ -179,6 +164,7 @@ module Cask
|
||||
add_error "installer and pkg stanzas require an uninstall stanza"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_single_pre_postflight
|
||||
odebug "Auditing preflight and postflight stanzas"
|
||||
|
||||
@ -195,6 +181,7 @@ module Cask
|
||||
add_error "only a single postflight stanza is allowed"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_single_uninstall_zap
|
||||
odebug "Auditing single uninstall_* and zap stanzas"
|
||||
|
||||
@ -221,6 +208,7 @@ module Cask
|
||||
add_error "only a single zap stanza is allowed"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_required_stanzas
|
||||
odebug "Auditing required stanzas"
|
||||
[:version, :sha256, :url, :homepage].each do |sym|
|
||||
@ -233,58 +221,66 @@ module Cask
|
||||
add_error "at least one activatable artifact stanza is required" if installable_artifacts.empty?
|
||||
end
|
||||
|
||||
def check_version
|
||||
return unless cask.version
|
||||
sig { void }
|
||||
def check_description_present
|
||||
# Fonts seldom benefit from descriptions and requiring them disproportionately increases the maintenance burden
|
||||
return if cask.tap == "homebrew/cask-fonts"
|
||||
|
||||
check_no_string_version_latest
|
||||
add_warning "Cask should have a description. Please add a `desc` stanza." if cask.desc.blank?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_no_string_version_latest
|
||||
odebug "Verifying version :latest does not appear as a string ('latest')"
|
||||
return unless cask.version
|
||||
|
||||
odebug "Auditing version :latest does not appear as a string ('latest')"
|
||||
return unless cask.version.raw_version == "latest"
|
||||
|
||||
add_error "you should use version :latest instead of version 'latest'"
|
||||
end
|
||||
|
||||
def check_sha256
|
||||
sig { void }
|
||||
def check_sha256_no_check_if_latest
|
||||
return unless cask.sha256
|
||||
|
||||
check_sha256_no_check_if_latest
|
||||
check_sha256_no_check_if_unversioned
|
||||
check_sha256_actually_256
|
||||
check_sha256_invalid
|
||||
end
|
||||
|
||||
def check_sha256_no_check_if_latest
|
||||
odebug "Verifying sha256 :no_check with version :latest"
|
||||
odebug "Auditing sha256 :no_check with version :latest"
|
||||
return unless cask.version.latest?
|
||||
return if cask.sha256 == :no_check
|
||||
|
||||
add_error "you should use sha256 :no_check when version is :latest"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_sha256_no_check_if_unversioned
|
||||
return unless cask.sha256
|
||||
return if cask.sha256 == :no_check
|
||||
|
||||
add_error "Use `sha256 :no_check` when URL is unversioned." if cask.url&.unversioned?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_sha256_actually_256
|
||||
odebug "Verifying sha256 string is a legal SHA-256 digest"
|
||||
return unless cask.sha256
|
||||
|
||||
odebug "Auditing sha256 string is a legal SHA-256 digest"
|
||||
return unless cask.sha256.is_a?(Checksum)
|
||||
return if cask.sha256.length == 64 && cask.sha256[/^[0-9a-f]+$/i]
|
||||
|
||||
add_error "sha256 string must be of 64 hexadecimal characters"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_sha256_invalid
|
||||
odebug "Verifying sha256 is not a known invalid value"
|
||||
return unless cask.sha256
|
||||
|
||||
odebug "Auditing sha256 is not a known invalid value"
|
||||
empty_sha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
return unless cask.sha256 == empty_sha256
|
||||
|
||||
add_error "cannot use the sha256 for an empty string: #{empty_sha256}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_appcast_and_livecheck
|
||||
return unless cask.appcast
|
||||
|
||||
@ -295,6 +291,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_latest_with_appcast_or_livecheck
|
||||
return unless cask.version.latest?
|
||||
|
||||
@ -302,6 +299,7 @@ module Cask
|
||||
add_error "Casks with a `livecheck` should not use `version :latest`." if cask.livecheckable?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_latest_with_auto_updates
|
||||
return unless cask.version.latest?
|
||||
return unless cask.auto_updates
|
||||
@ -311,7 +309,8 @@ module Cask
|
||||
|
||||
LIVECHECK_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#stanza-livecheck"
|
||||
|
||||
def check_hosting_with_livecheck(livecheck_result:)
|
||||
sig { params(livecheck_result: T::Boolean).void }
|
||||
def check_hosting_with_livecheck(livecheck_result: check_livecheck_version)
|
||||
return if cask.discontinued? || cask.version.latest?
|
||||
return if block_url_offline? || cask.appcast || cask.livecheckable?
|
||||
return if livecheck_result == :auto_detected
|
||||
@ -330,24 +329,12 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
def check_desc
|
||||
return if cask.desc.present?
|
||||
|
||||
# Fonts seldom benefit from descriptions and requiring them disproportionately increases the maintenance burden
|
||||
return if cask.tap == "homebrew/cask-fonts"
|
||||
|
||||
add_warning "Cask should have a description. Please add a `desc` stanza."
|
||||
end
|
||||
|
||||
def check_url
|
||||
return unless cask.url
|
||||
|
||||
check_download_url_format
|
||||
end
|
||||
|
||||
SOURCEFORGE_OSDN_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#sourceforgeosdn-urls"
|
||||
|
||||
sig { void }
|
||||
def check_download_url_format
|
||||
return unless cask.url
|
||||
|
||||
odebug "Auditing URL format"
|
||||
if bad_sourceforge_url?
|
||||
add_error "SourceForge URL format incorrect. See #{Formatter.url(SOURCEFORGE_OSDN_REFERENCE_URL)}"
|
||||
@ -356,82 +343,9 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
def bad_url_format?(regex, valid_formats_array)
|
||||
return false unless cask.url.to_s.match?(regex)
|
||||
|
||||
valid_formats_array.none? { |format| cask.url.to_s =~ format }
|
||||
end
|
||||
|
||||
def bad_sourceforge_url?
|
||||
bad_url_format?(/sourceforge/,
|
||||
[
|
||||
%r{\Ahttps://sourceforge\.net/projects/[^/]+/files/latest/download\Z},
|
||||
%r{\Ahttps://downloads\.sourceforge\.net/(?!(project|sourceforge)/)},
|
||||
])
|
||||
end
|
||||
|
||||
def bad_osdn_url?
|
||||
bad_url_format?(/osd/, [%r{\Ahttps?://([^/]+.)?dl\.osdn\.jp/}])
|
||||
end
|
||||
|
||||
def homepage
|
||||
URI(cask.homepage.to_s).host
|
||||
end
|
||||
|
||||
def domain
|
||||
URI(cask.url.to_s).host
|
||||
end
|
||||
|
||||
def url_match_homepage?
|
||||
host = cask.url.to_s
|
||||
host_uri = URI(host)
|
||||
host = if host.match?(/:\d/) && host_uri.port != 80
|
||||
"#{host_uri.host}:#{host_uri.port}"
|
||||
else
|
||||
host_uri.host
|
||||
end
|
||||
home = homepage.downcase
|
||||
if (split_host = host.split(".")).length >= 3
|
||||
host = split_host[-2..].join(".")
|
||||
end
|
||||
if (split_home = homepage.split(".")).length >= 3
|
||||
home = split_home[-2..].join(".")
|
||||
end
|
||||
host == home
|
||||
end
|
||||
|
||||
def strip_url_scheme(url)
|
||||
url.sub(%r{^[^:/]+://(www\.)?}, "")
|
||||
end
|
||||
|
||||
def url_from_verified
|
||||
strip_url_scheme(cask.url.verified)
|
||||
end
|
||||
|
||||
def verified_matches_url?
|
||||
url_domain, url_path = strip_url_scheme(cask.url.to_s).split("/", 2)
|
||||
verified_domain, verified_path = url_from_verified.split("/", 2)
|
||||
|
||||
(url_domain == verified_domain || (verified_domain && url_domain&.end_with?(".#{verified_domain}"))) &&
|
||||
(!verified_path || url_path&.start_with?(verified_path))
|
||||
end
|
||||
|
||||
def verified_present?
|
||||
cask.url.verified.present?
|
||||
end
|
||||
|
||||
def file_url?
|
||||
URI(cask.url.to_s).scheme == "file"
|
||||
end
|
||||
|
||||
def block_url_offline?
|
||||
return false if online?
|
||||
|
||||
cask.url.from_block?
|
||||
end
|
||||
|
||||
VERIFIED_URL_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#when-url-and-homepage-domains-differ-add-verified"
|
||||
|
||||
sig { void }
|
||||
def check_unnecessary_verified
|
||||
return if block_url_offline?
|
||||
return unless verified_present?
|
||||
@ -443,6 +357,7 @@ module Cask
|
||||
"See #{Formatter.url(VERIFIED_URL_REFERENCE_URL)}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_missing_verified
|
||||
return if block_url_offline?
|
||||
return if file_url?
|
||||
@ -454,6 +369,7 @@ module Cask
|
||||
"See #{Formatter.url(VERIFIED_URL_REFERENCE_URL)}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_no_match
|
||||
return if block_url_offline?
|
||||
return unless verified_present?
|
||||
@ -464,6 +380,7 @@ module Cask
|
||||
"See #{Formatter.url(VERIFIED_URL_REFERENCE_URL)}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_generic_artifacts
|
||||
cask.artifacts.select { |a| a.is_a?(Artifact::Artifact) }.each do |artifact|
|
||||
unless artifact.target.absolute?
|
||||
@ -472,6 +389,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_languages
|
||||
@cask.languages.each do |language|
|
||||
Locale.parse(language)
|
||||
@ -480,6 +398,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_token_conflicts
|
||||
return unless token_conflicts?
|
||||
return unless core_formula_names.include?(cask.token)
|
||||
@ -488,6 +407,7 @@ module Cask
|
||||
"#{Formatter.url(core_formula_url)}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_token_valid
|
||||
add_error "cask token contains non-ascii characters" unless cask.token.ascii_only?
|
||||
add_error "cask token + should be replaced by -plus-" if cask.token.include? "+"
|
||||
@ -505,6 +425,7 @@ module Cask
|
||||
add_error "cask token should not have leading or trailing hyphens"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_token_bad_words
|
||||
return unless new_cask?
|
||||
|
||||
@ -532,19 +453,7 @@ module Cask
|
||||
add_warning "cask token mentions framework"
|
||||
end
|
||||
|
||||
def core_tap
|
||||
@core_tap ||= CoreTap.instance
|
||||
end
|
||||
|
||||
def core_formula_names
|
||||
core_tap.formula_names
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def core_formula_url
|
||||
"#{core_tap.default_remote}/blob/HEAD/Formula/#{cask.token}.rb"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_download
|
||||
return if download.blank? || cask.url.blank?
|
||||
|
||||
@ -554,11 +463,11 @@ module Cask
|
||||
add_error "download not possible: #{e}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_signing
|
||||
return if !signing? || download.blank? || cask.url.blank?
|
||||
|
||||
odebug "Auditing signing"
|
||||
odebug cask.artifacts
|
||||
artifacts = cask.artifacts.select { |k| k.is_a?(Artifact::Pkg) || k.is_a?(Artifact::App) }
|
||||
|
||||
return if artifacts.empty?
|
||||
@ -571,16 +480,34 @@ module Cask
|
||||
Dir.mktmpdir do |tmpdir|
|
||||
tmpdir = Pathname(tmpdir)
|
||||
primary_container.extract_nestedly(to: tmpdir, basename: downloaded_path.basename, verbose: false)
|
||||
cask.artifacts.each do |artifact|
|
||||
result = system_command("codesign", args: [
|
||||
"--verify",
|
||||
tmpdir/artifact.source.basename,
|
||||
], print_stderr: false)
|
||||
add_warning result.merged_output unless result.success?
|
||||
artifacts.each do |artifact|
|
||||
path = case artifact
|
||||
when Artifact::Moved
|
||||
tmpdir/artifact.source.basename
|
||||
when Artifact::Pkg
|
||||
artifact.path
|
||||
end
|
||||
next unless path.exist?
|
||||
|
||||
result = system_command("codesign", args: ["--verify", path], print_stderr: false)
|
||||
|
||||
next if result.success?
|
||||
|
||||
message = "Signature verification failed:\n#{result.merged_output}\nmacOS on ARM requires applications " \
|
||||
"to be signed. Please contact the upstream developer to let them know they should "
|
||||
|
||||
message += if result.stderr.include?("not signed at all")
|
||||
"sign their app."
|
||||
else
|
||||
"fix the signature of their app."
|
||||
end
|
||||
|
||||
add_warning message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(T.any(T::Boolean, Symbol))) }
|
||||
def check_livecheck_version
|
||||
return unless appcast?
|
||||
|
||||
@ -618,6 +545,7 @@ module Cask
|
||||
false
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_appcast_contains_version
|
||||
return unless appcast?
|
||||
return if cask.appcast.to_s.empty?
|
||||
@ -644,6 +572,7 @@ module Cask
|
||||
EOS
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_github_prerelease_version
|
||||
return if cask.tap == "homebrew/cask-versions"
|
||||
|
||||
@ -657,6 +586,7 @@ module Cask
|
||||
add_error error if error
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_gitlab_prerelease_version
|
||||
return if cask.tap == "homebrew/cask-versions"
|
||||
|
||||
@ -671,6 +601,7 @@ module Cask
|
||||
add_error error if error
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_github_repository_archived
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if online?
|
||||
return if user.nil?
|
||||
@ -691,6 +622,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_gitlab_repository_archived
|
||||
user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if online?
|
||||
return if user.nil?
|
||||
@ -711,6 +643,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_github_repository
|
||||
return unless new_cask?
|
||||
|
||||
@ -723,6 +656,7 @@ module Cask
|
||||
add_error error if error
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_gitlab_repository
|
||||
return unless new_cask?
|
||||
|
||||
@ -735,6 +669,7 @@ module Cask
|
||||
add_error error if error
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_bitbucket_repository
|
||||
return unless new_cask?
|
||||
|
||||
@ -747,6 +682,62 @@ module Cask
|
||||
add_error error if error
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_denylist
|
||||
return unless cask.tap
|
||||
return unless cask.tap.official?
|
||||
return unless (reason = Denylist.reason(cask.token))
|
||||
|
||||
add_error "#{cask.token} is not allowed: #{reason}"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_reverse_migration
|
||||
return unless new_cask?
|
||||
return unless cask.tap
|
||||
return unless cask.tap.official?
|
||||
return unless cask.tap.tap_migrations.key?(cask.token)
|
||||
|
||||
add_error "#{cask.token} is listed in tap_migrations.json"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_https_availability
|
||||
return unless download
|
||||
|
||||
if cask.url && !cask.url.using
|
||||
validate_url_for_https_availability(cask.url, "binary URL", cask.token, cask.tap,
|
||||
user_agents: [cask.url.user_agent])
|
||||
end
|
||||
|
||||
if cask.appcast && appcast?
|
||||
validate_url_for_https_availability(cask.appcast, "appcast URL", cask.token, cask.tap, check_content: true)
|
||||
end
|
||||
|
||||
return unless cask.homepage
|
||||
|
||||
validate_url_for_https_availability(cask.homepage, SharedAudits::URL_TYPE_HOMEPAGE, cask.token, cask.tap,
|
||||
user_agents: [:browser, :default],
|
||||
check_content: true,
|
||||
strict: strict?)
|
||||
end
|
||||
|
||||
# sig {
|
||||
# params(url_to_check: T.any(String, URL), url_type: String, cask_token: String, tap: Tap,
|
||||
# options: T.untyped).void
|
||||
# }
|
||||
def validate_url_for_https_availability(url_to_check, url_type, cask_token, tap, **options)
|
||||
problem = curl_check_http_content(url_to_check.to_s, url_type, **options)
|
||||
exception = tap&.audit_exception(:secure_connection_audit_skiplist, cask_token, url_to_check.to_s)
|
||||
|
||||
if problem
|
||||
add_error problem unless exception
|
||||
elsif exception
|
||||
add_error "#{url_to_check} is in the secure connection audit skiplist but does not need to be skipped"
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(regex: T.any(String, Regexp)).returns(T.nilable(T::Array[String])) }
|
||||
def get_repo_data(regex)
|
||||
return unless online?
|
||||
|
||||
@ -760,52 +751,110 @@ module Cask
|
||||
[user, repo]
|
||||
end
|
||||
|
||||
def check_denylist
|
||||
return unless cask.tap
|
||||
return unless cask.tap.official?
|
||||
return unless (reason = Denylist.reason(cask.token))
|
||||
sig {
|
||||
params(regex: T.any(String, Regexp), valid_formats_array: T::Array[T.any(String, Regexp)]).returns(T::Boolean)
|
||||
}
|
||||
def bad_url_format?(regex, valid_formats_array)
|
||||
return false unless cask.url.to_s.match?(regex)
|
||||
|
||||
add_error "#{cask.token} is not allowed: #{reason}"
|
||||
valid_formats_array.none? { |format| cask.url.to_s =~ format }
|
||||
end
|
||||
|
||||
def check_reverse_migration
|
||||
return unless new_cask?
|
||||
return unless cask.tap
|
||||
return unless cask.tap.official?
|
||||
return unless cask.tap.tap_migrations.key?(cask.token)
|
||||
|
||||
add_error "#{cask.token} is listed in tap_migrations.json"
|
||||
sig { returns(T::Boolean) }
|
||||
def bad_sourceforge_url?
|
||||
bad_url_format?(/sourceforge/,
|
||||
[
|
||||
%r{\Ahttps://sourceforge\.net/projects/[^/]+/files/latest/download\Z},
|
||||
%r{\Ahttps://downloads\.sourceforge\.net/(?!(project|sourceforge)/)},
|
||||
])
|
||||
end
|
||||
|
||||
def check_https_availability
|
||||
return unless download
|
||||
|
||||
if cask.url && !cask.url.using
|
||||
check_url_for_https_availability(cask.url, "binary URL", cask.token, cask.tap,
|
||||
user_agents: [cask.url.user_agent])
|
||||
end
|
||||
|
||||
if cask.appcast && appcast?
|
||||
check_url_for_https_availability(cask.appcast, "appcast URL", cask.token, cask.tap, check_content: true)
|
||||
end
|
||||
|
||||
return unless cask.homepage
|
||||
|
||||
check_url_for_https_availability(cask.homepage, "homepage URL", cask.token, cask.tap,
|
||||
user_agents: [:browser, :default],
|
||||
check_content: true,
|
||||
strict: strict?)
|
||||
sig { returns(T::Boolean) }
|
||||
def bad_osdn_url?
|
||||
bad_url_format?(/osd/, [%r{\Ahttps?://([^/]+.)?dl\.osdn\.jp/}])
|
||||
end
|
||||
|
||||
def check_url_for_https_availability(url_to_check, url_type, cask_token, tap, **options)
|
||||
problem = curl_check_http_content(url_to_check.to_s, url_type, **options)
|
||||
exception = tap&.audit_exception(:secure_connection_audit_skiplist, cask_token, url_to_check.to_s)
|
||||
# sig { returns(String) }
|
||||
def homepage
|
||||
URI(cask.homepage.to_s).host
|
||||
end
|
||||
|
||||
if problem
|
||||
add_error problem unless exception
|
||||
elsif exception
|
||||
add_error "#{url_to_check} is in the secure connection audit skiplist but does not need to be skipped"
|
||||
# sig { returns(String) }
|
||||
def domain
|
||||
URI(cask.url.to_s).host
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def url_match_homepage?
|
||||
host = cask.url.to_s
|
||||
host_uri = URI(host)
|
||||
host = if host.match?(/:\d/) && host_uri.port != 80
|
||||
"#{host_uri.host}:#{host_uri.port}"
|
||||
else
|
||||
host_uri.host
|
||||
end
|
||||
|
||||
return false if homepage.blank?
|
||||
|
||||
home = homepage.downcase
|
||||
if (split_host = host.split(".")).length >= 3
|
||||
host = split_host[-2..].join(".")
|
||||
end
|
||||
if (split_home = homepage.split(".")).length >= 3
|
||||
home = split_home[-2..].join(".")
|
||||
end
|
||||
host == home
|
||||
end
|
||||
|
||||
# sig { params(url: String).returns(String) }
|
||||
def strip_url_scheme(url)
|
||||
url.sub(%r{^[^:/]+://(www\.)?}, "")
|
||||
end
|
||||
|
||||
# sig { returns(String) }
|
||||
def url_from_verified
|
||||
strip_url_scheme(cask.url.verified)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def verified_matches_url?
|
||||
url_domain, url_path = strip_url_scheme(cask.url.to_s).split("/", 2)
|
||||
verified_domain, verified_path = url_from_verified.split("/", 2)
|
||||
|
||||
(url_domain == verified_domain || (verified_domain && url_domain&.end_with?(".#{verified_domain}"))) &&
|
||||
(!verified_path || url_path&.start_with?(verified_path))
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def verified_present?
|
||||
cask.url.verified.present?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def file_url?
|
||||
URI(cask.url.to_s).scheme == "file"
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def block_url_offline?
|
||||
return false if online?
|
||||
|
||||
cask.url.from_block?
|
||||
end
|
||||
|
||||
sig { returns(Tap) }
|
||||
def core_tap
|
||||
@core_tap ||= CoreTap.instance
|
||||
end
|
||||
|
||||
# sig { returns(T::Array[String]) }
|
||||
def core_formula_names
|
||||
core_tap.formula_names
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def core_formula_url
|
||||
"#{core_tap.default_remote}/blob/HEAD/Formula/#{cask.token}.rb"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -144,7 +144,6 @@ module Cask
|
||||
quarantine: @quarantine,
|
||||
)
|
||||
audit.run!
|
||||
audit
|
||||
end
|
||||
|
||||
def language_blocks
|
||||
|
||||
@ -24,6 +24,11 @@ module Cask
|
||||
attr_accessor :download, :allow_reassignment
|
||||
|
||||
def self.all
|
||||
# TODO: uncomment for 3.7.0 and ideally avoid using ARGV by moving to e.g. CLI::Parser
|
||||
# if !ARGV.include?("--eval-all") && !Homebrew::EnvConfig.eval_all?
|
||||
# odeprecated "Cask::Cask#all without --all or HOMEBREW_EVAL_ALL"
|
||||
# end
|
||||
|
||||
Tap.flat_map(&:cask_files).map do |f|
|
||||
CaskLoader::FromTapPathLoader.new(f).load(config: nil)
|
||||
rescue CaskUnreadableError => e
|
||||
@ -235,7 +240,7 @@ module Cask
|
||||
"installed" => versions.last,
|
||||
"outdated" => outdated?,
|
||||
"sha256" => sha256,
|
||||
"artifacts" => artifacts.map(&method(:to_h_gsubs)),
|
||||
"artifacts" => artifacts_list,
|
||||
"caveats" => (to_h_string_gsubs(caveats) unless caveats.empty?),
|
||||
"depends_on" => depends_on,
|
||||
"conflicts_with" => conflicts_with,
|
||||
@ -281,6 +286,18 @@ module Cask
|
||||
|
||||
private
|
||||
|
||||
def artifacts_list
|
||||
artifacts.map do |artifact|
|
||||
key, value = if artifact.is_a? Artifact::AbstractFlightBlock
|
||||
artifact.summarize
|
||||
else
|
||||
[artifact.class.dsl_key, to_h_gsubs(artifact.to_args)]
|
||||
end
|
||||
|
||||
{ key => value }
|
||||
end
|
||||
end
|
||||
|
||||
def to_h_string_gsubs(string)
|
||||
string.to_s
|
||||
.gsub(Dir.home, "$HOME")
|
||||
|
||||
@ -230,8 +230,13 @@ module Cask
|
||||
end
|
||||
|
||||
# @api public
|
||||
def sha256(arg = nil)
|
||||
set_unique_stanza(:sha256, arg.nil?) do
|
||||
def sha256(arg = nil, arm: nil, intel: nil)
|
||||
should_return = arg.nil? && arm.nil? && intel.nil?
|
||||
|
||||
set_unique_stanza(:sha256, should_return) do
|
||||
@on_system_blocks_exist = true if arm.present? || intel.present?
|
||||
|
||||
arg ||= on_arch_conditional(arm: arm, intel: intel)
|
||||
case arg
|
||||
when :no_check
|
||||
arg
|
||||
@ -245,7 +250,7 @@ module Cask
|
||||
|
||||
# @api public
|
||||
def arch(arm: nil, intel: nil)
|
||||
should_return = arm.blank? && intel.blank?
|
||||
should_return = arm.nil? && intel.nil?
|
||||
|
||||
set_unique_stanza(:arch, should_return) do
|
||||
@on_system_blocks_exist = true
|
||||
|
||||
@ -150,20 +150,6 @@ module Cask
|
||||
version { split(",", 2).second }
|
||||
end
|
||||
|
||||
# @api public
|
||||
sig { returns(T.self_type) }
|
||||
def before_colon
|
||||
odisabled "Cask::DSL::Version#before_colon", "Cask::DSL::Version#csv"
|
||||
version { split(":", 2).first }
|
||||
end
|
||||
|
||||
# @api public
|
||||
sig { returns(T.self_type) }
|
||||
def after_colon
|
||||
odisabled "Cask::DSL::Version#after_colon", "Cask::DSL::Version#csv"
|
||||
version { split(":", 2).second }
|
||||
end
|
||||
|
||||
# @api public
|
||||
sig { returns(T.self_type) }
|
||||
def no_dividers
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "requirement"
|
||||
|
||||
# An adapter for casks to provide dependency information in a formula-like interface.
|
||||
class CaskDependent
|
||||
# Defines a dependency on another cask
|
||||
class Requirement < ::Requirement
|
||||
satisfy(build_env: false) do
|
||||
Cask::CaskLoader.load(cask).installed?
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :cask
|
||||
|
||||
def initialize(cask)
|
||||
@ -33,11 +42,21 @@ class CaskDependent
|
||||
dsl_reqs = @cask.depends_on
|
||||
|
||||
dsl_reqs.arch&.each do |arch|
|
||||
requirements << ArchRequirement.new([:x86_64]) if arch[:bits] == 64
|
||||
requirements << ArchRequirement.new([arch[:type]])
|
||||
arch = if arch[:bits] == 64
|
||||
if arch[:type] == :intel
|
||||
:x86_64
|
||||
else
|
||||
:"#{arch[:type]}64"
|
||||
end
|
||||
elsif arch[:type] == :intel && arch[:bits] == 32
|
||||
:i386
|
||||
else
|
||||
arch[:type]
|
||||
end
|
||||
requirements << ArchRequirement.new([arch])
|
||||
end
|
||||
dsl_reqs.cask.each do |cask_ref|
|
||||
requirements << Requirement.new([{ cask: cask_ref }])
|
||||
requirements << CaskDependent::Requirement.new([{ cask: cask_ref }])
|
||||
end
|
||||
requirements << dsl_reqs.macos if dsl_reqs.macos
|
||||
|
||||
|
||||
@ -189,8 +189,8 @@ module Homebrew
|
||||
def self.skip_clean_formula?(f)
|
||||
return false if Homebrew::EnvConfig.no_cleanup_formulae.blank?
|
||||
|
||||
skip_clean_formulae = Homebrew::EnvConfig.no_cleanup_formulae.split(",")
|
||||
skip_clean_formulae.include?(f.name) || (skip_clean_formulae & f.aliases).present?
|
||||
@skip_clean_formulae ||= Homebrew::EnvConfig.no_cleanup_formulae.split(",")
|
||||
@skip_clean_formulae.include?(f.name) || (@skip_clean_formulae & f.aliases).present?
|
||||
end
|
||||
|
||||
def self.periodic_clean_due?
|
||||
@ -235,6 +235,7 @@ module Homebrew
|
||||
cleanup_cache
|
||||
cleanup_logs
|
||||
cleanup_lockfiles
|
||||
cleanup_python_site_packages
|
||||
prune_prefix_symlinks_and_directories
|
||||
|
||||
unless dry_run?
|
||||
@ -428,7 +429,6 @@ module Homebrew
|
||||
next if !use_system_ruby && portable_ruby_latest_version == path.basename.to_s
|
||||
|
||||
portable_rubies_to_remove << path
|
||||
puts "Would remove: #{path} (#{path.abv})" if dry_run?
|
||||
end
|
||||
|
||||
return if portable_rubies_to_remove.empty?
|
||||
@ -440,20 +440,16 @@ module Homebrew
|
||||
puts Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "clean", "-ffqx", bundler_path).chomp
|
||||
end
|
||||
|
||||
return if dry_run?
|
||||
|
||||
FileUtils.rm_rf portable_rubies_to_remove
|
||||
portable_rubies_to_remove.each do |portable_ruby|
|
||||
cleanup_path(portable_ruby) { portable_ruby.rmtree }
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_bootsnap
|
||||
bootsnap = cache/"bootsnap"
|
||||
return unless bootsnap.exist?
|
||||
|
||||
if dry_run?
|
||||
puts "Would remove: #{bootsnap} (#{bootsnap.abv})"
|
||||
else
|
||||
FileUtils.rm_rf bootsnap
|
||||
end
|
||||
cleanup_path(bootsnap) { bootsnap.rmtree }
|
||||
end
|
||||
|
||||
def cleanup_cache_db(rack = nil)
|
||||
@ -489,6 +485,55 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_python_site_packages
|
||||
pyc_files = Hash.new { |h, k| h[k] = [] }
|
||||
seen_non_pyc_file = Hash.new { |h, k| h[k] = false }
|
||||
unused_pyc_files = []
|
||||
|
||||
HOMEBREW_PREFIX.glob("lib/python*/site-packages").each do |site_packages|
|
||||
site_packages.each_child do |child|
|
||||
next unless child.directory?
|
||||
# TODO: Work out a sensible way to clean up pip's, setuptools', and wheel's
|
||||
# {dist,site}-info directories. Alternatively, consider always removing
|
||||
# all `-info` directories, because we may not be making use of them.
|
||||
next if child.basename.to_s.end_with?("-info")
|
||||
|
||||
# Clean up old *.pyc files in the top-level __pycache__.
|
||||
if child.basename.to_s == "__pycache__"
|
||||
child.find do |path|
|
||||
next unless path.extname == ".pyc"
|
||||
next unless path.prune?(days)
|
||||
|
||||
unused_pyc_files << path
|
||||
end
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
# Look for directories that contain only *.pyc files.
|
||||
child.find do |path|
|
||||
next if path.directory?
|
||||
|
||||
if path.extname == ".pyc"
|
||||
pyc_files[child] << path
|
||||
else
|
||||
seen_non_pyc_file[child] = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unused_pyc_files += pyc_files.reject { |k,| seen_non_pyc_file[k] }
|
||||
.values
|
||||
.flatten
|
||||
return if unused_pyc_files.blank?
|
||||
|
||||
unused_pyc_files.each do |pyc|
|
||||
cleanup_path(pyc) { pyc.unlink }
|
||||
end
|
||||
end
|
||||
|
||||
def prune_prefix_symlinks_and_directories
|
||||
ObserverPathnameExtension.reset_counts!
|
||||
|
||||
@ -534,17 +579,22 @@ module Homebrew
|
||||
end
|
||||
|
||||
def self.autoremove(dry_run: false)
|
||||
require "utils/autoremove"
|
||||
require "cask/caskroom"
|
||||
|
||||
# If this runs after install, uninstall, reinstall or upgrade,
|
||||
# the cache of installed formulae may no longer be valid.
|
||||
Formula.clear_cache unless dry_run
|
||||
|
||||
# Remove formulae listed in HOMEBREW_NO_CLEANUP_FORMULAE.
|
||||
formulae = Formula.installed.reject(&method(:skip_clean_formula?))
|
||||
formulae = Formula.installed
|
||||
# Remove formulae listed in HOMEBREW_NO_CLEANUP_FORMULAE and their dependencies.
|
||||
if Homebrew::EnvConfig.no_cleanup_formulae.present?
|
||||
formulae -= formulae.select(&method(:skip_clean_formula?))
|
||||
.flat_map { |f| [f, *f.runtime_formula_dependencies] }
|
||||
end
|
||||
casks = Cask::Caskroom.casks
|
||||
|
||||
removable_formulae = Formula.unused_formulae_with_no_dependents(formulae, casks)
|
||||
removable_formulae = Utils::Autoremove.removable_formulae(formulae, casks)
|
||||
|
||||
return if removable_formulae.blank?
|
||||
|
||||
|
||||
@ -30,6 +30,9 @@ module Homebrew
|
||||
sig { returns(T::Boolean) }
|
||||
def newer_only?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def resources?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def full_name?; end
|
||||
|
||||
@ -132,6 +135,9 @@ module Homebrew
|
||||
sig { returns(T::Boolean) }
|
||||
def all?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def eval_all?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def full?; end
|
||||
|
||||
|
||||
@ -22,10 +22,10 @@ module Homebrew
|
||||
may be appended to the command. When given multiple formula arguments,
|
||||
show the intersection of dependencies for each formula.
|
||||
EOS
|
||||
switch "-n",
|
||||
switch "-n", "--topological",
|
||||
description: "Sort dependencies in topological order."
|
||||
switch "--1",
|
||||
description: "Only show dependencies one level down, instead of recursing."
|
||||
switch "-1", "--direct", "--declared", "--1",
|
||||
description: "Show only the direct dependencies declared in the formula."
|
||||
switch "--union",
|
||||
description: "Show the union of dependencies for multiple <formula>, instead of the intersection."
|
||||
switch "--full-name",
|
||||
@ -54,20 +54,22 @@ module Homebrew
|
||||
switch "--installed",
|
||||
description: "List dependencies for formulae that are currently installed. If <formula> is " \
|
||||
"specified, list only its dependencies that are currently installed."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to list " \
|
||||
"their dependencies."
|
||||
switch "--all",
|
||||
description: "List dependencies for all available formulae."
|
||||
hidden: true
|
||||
switch "--for-each",
|
||||
description: "Switch into the mode used by the `--all` option, but only list dependencies " \
|
||||
"for each provided <formula>, one formula per line. This is used for " \
|
||||
"debugging the `--installed`/`--all` display mode."
|
||||
switch "--formula", "--formulae",
|
||||
depends_on: "--installed",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
depends_on: "--installed",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
conflicts "--tree", "--graph"
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--formula", "--cask"
|
||||
formula_options
|
||||
@ -79,9 +81,18 @@ module Homebrew
|
||||
def deps
|
||||
args = deps_args.parse
|
||||
|
||||
all = args.eval_all?
|
||||
if args.all?
|
||||
unless all
|
||||
odeprecated "brew deps --all",
|
||||
"brew deps --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
all = true
|
||||
end
|
||||
|
||||
Formulary.enable_factory_cache!
|
||||
|
||||
recursive = !args.send(:"1?")
|
||||
recursive = !args.direct?
|
||||
installed = args.installed? || dependents(args.named.to_formulae_and_casks).all?(&:any_version_installed?)
|
||||
|
||||
@use_runtime_dependencies = installed && recursive &&
|
||||
@ -120,7 +131,7 @@ module Homebrew
|
||||
|
||||
puts_deps_tree dependents, recursive: recursive, args: args
|
||||
return
|
||||
elsif args.all?
|
||||
elsif all
|
||||
puts_deps sorted_dependents(Formula.all + Cask::Cask.all), recursive: recursive, args: args
|
||||
return
|
||||
elsif !args.no_named? && args.for_each?
|
||||
@ -149,7 +160,7 @@ module Homebrew
|
||||
condense_requirements(all_deps, args: args)
|
||||
all_deps.map! { |d| dep_display_name(d, args: args) }
|
||||
all_deps.uniq!
|
||||
all_deps.sort! unless args.n?
|
||||
all_deps.sort! unless args.topological?
|
||||
puts all_deps
|
||||
end
|
||||
|
||||
@ -286,8 +297,14 @@ module Homebrew
|
||||
end
|
||||
|
||||
display_s = "#{tree_lines} #{dep_display_name(dep, args: args)}"
|
||||
|
||||
# Detect circular dependencies and consider them a failure if present.
|
||||
is_circular = dep_stack.include?(dep.name)
|
||||
display_s = "#{display_s} (CIRCULAR DEPENDENCY)" if is_circular
|
||||
if is_circular
|
||||
display_s = "#{display_s} (CIRCULAR DEPENDENCY)"
|
||||
Homebrew.failed = true
|
||||
end
|
||||
|
||||
puts "#{prefix}#{display_s}"
|
||||
|
||||
next if !recursive || is_circular
|
||||
|
||||
@ -18,8 +18,7 @@ module Homebrew
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Display <formula>'s name and one-line description.
|
||||
Formula descriptions are cached; the cache is created on the
|
||||
first search, making that search slower than subsequent ones.
|
||||
The cache is created on the first search, making that search slower than subsequent ones.
|
||||
EOS
|
||||
switch "-s", "--search",
|
||||
description: "Search both names and descriptions for <text>. If <text> is flanked by " \
|
||||
@ -30,6 +29,9 @@ module Homebrew
|
||||
switch "-d", "--description",
|
||||
description: "Search just descriptions for <text>. If <text> is flanked by slashes, " \
|
||||
"it is interpreted as a regular expression."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to search their " \
|
||||
"descriptions. Implied if HOMEBREW_EVAL_ALL is set."
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
@ -44,6 +46,10 @@ module Homebrew
|
||||
def desc
|
||||
args = desc_args.parse
|
||||
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew desc", "brew desc --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
|
||||
search_type = if args.search?
|
||||
:either
|
||||
elsif args.name?
|
||||
|
||||
24
Library/Homebrew/cmd/docs.rb
Normal file
24
Library/Homebrew/cmd/docs.rb
Normal file
@ -0,0 +1,24 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
|
||||
module Homebrew
|
||||
extend T::Sig
|
||||
|
||||
module_function
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def docs_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Open Homebrew's online documentation (#{HOMEBREW_DOCS_WWW}) in a browser.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def docs
|
||||
exec_browser HOMEBREW_DOCS_WWW
|
||||
end
|
||||
end
|
||||
@ -13,6 +13,8 @@ module Homebrew
|
||||
|
||||
module_function
|
||||
|
||||
FETCH_MAX_TRIES = 5
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def fetch_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
@ -31,7 +33,8 @@ module Homebrew
|
||||
"seeing if an existing VCS cache has been updated."
|
||||
switch "--retry",
|
||||
description: "Retry if downloading fails or re-download if the checksum of a previously cached " \
|
||||
"version no longer matches."
|
||||
"version no longer matches. Tries at most #{FETCH_MAX_TRIES} times with " \
|
||||
"exponential backoff."
|
||||
switch "--deps",
|
||||
description: "Also download dependencies for any listed <formula>."
|
||||
switch "-s", "--build-from-source",
|
||||
@ -159,10 +162,17 @@ module Homebrew
|
||||
end
|
||||
|
||||
def retry_fetch?(f, args:)
|
||||
@fetch_failed ||= Set.new
|
||||
if args.retry? && @fetch_failed.add?(f)
|
||||
ohai "Retrying download"
|
||||
@fetch_tries ||= Hash.new { |h, k| h[k] = 1 }
|
||||
if args.retry? && (@fetch_tries[f] < FETCH_MAX_TRIES)
|
||||
wait = 2 ** @fetch_tries[f]
|
||||
remaining = FETCH_MAX_TRIES - @fetch_tries[f]
|
||||
what = "try".pluralize(remaining)
|
||||
|
||||
ohai "Retrying download in #{wait}s... (#{remaining} #{what} left)"
|
||||
sleep wait
|
||||
|
||||
f.clear_cache
|
||||
@fetch_tries[f] += 1
|
||||
true
|
||||
else
|
||||
Homebrew.failed = true
|
||||
|
||||
@ -58,9 +58,13 @@ module Homebrew
|
||||
switch "--installed",
|
||||
depends_on: "--json",
|
||||
description: "Print JSON of formulae that are currently installed."
|
||||
switch "--all",
|
||||
switch "--eval-all",
|
||||
depends_on: "--json",
|
||||
description: "Print JSON of all available formulae."
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to print their " \
|
||||
"JSON. Implied if HOMEBREW_EVAL_ALL is set."
|
||||
switch "--all",
|
||||
hidden: true,
|
||||
depends_on: "--json"
|
||||
switch "--variations",
|
||||
depends_on: "--json",
|
||||
description: "Include the variations hash in each formula's JSON output."
|
||||
@ -71,6 +75,7 @@ module Homebrew
|
||||
switch "--cask", "--casks",
|
||||
description: "Treat all named arguments as casks."
|
||||
|
||||
conflicts "--installed", "--eval-all"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--formula", "--cask"
|
||||
|
||||
@ -103,7 +108,13 @@ module Homebrew
|
||||
|
||||
print_analytics(args: args)
|
||||
elsif args.json
|
||||
print_json(args: args)
|
||||
all = args.eval_all?
|
||||
if !all && args.all? && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew info --all", "brew info --eval-all or HOMEBREW_EVAL_ALL"
|
||||
all = true
|
||||
end
|
||||
|
||||
print_json(all, args: args)
|
||||
elsif args.github?
|
||||
raise FormulaOrCaskUnspecifiedError if args.no_named?
|
||||
|
||||
@ -187,15 +198,15 @@ module Homebrew
|
||||
version_hash[version]
|
||||
end
|
||||
|
||||
sig { params(args: CLI::Args).void }
|
||||
def print_json(args:)
|
||||
raise FormulaOrCaskUnspecifiedError if !(args.all? || args.installed?) && args.no_named?
|
||||
sig { params(all: T::Boolean, args: CLI::Args).void }
|
||||
def print_json(all, args:)
|
||||
raise FormulaOrCaskUnspecifiedError if !(all || args.installed?) && args.no_named?
|
||||
|
||||
json = case json_version(args.json)
|
||||
when :v1, :default
|
||||
raise UsageError, "cannot specify --cask with --json=v1!" if args.cask?
|
||||
|
||||
formulae = if args.all?
|
||||
formulae = if all
|
||||
Formula.all.sort
|
||||
elsif args.installed?
|
||||
Formula.installed.sort
|
||||
@ -211,7 +222,7 @@ module Homebrew
|
||||
formulae.map(&:to_hash)
|
||||
end
|
||||
when :v2
|
||||
formulae, casks = if args.all?
|
||||
formulae, casks = if all
|
||||
[Formula.all.sort, Cask::Cask.all.sort_by(&:full_name)]
|
||||
elsif args.installed?
|
||||
[Formula.installed.sort, Cask::Caskroom.casks.sort_by(&:full_name)]
|
||||
|
||||
@ -37,7 +37,7 @@ module Homebrew
|
||||
def leaves
|
||||
args = leaves_args.parse
|
||||
|
||||
leaves_list = Formula.formulae_with_no_formula_dependents(Formula.installed)
|
||||
leaves_list = Formula.installed - Formula.installed.flat_map(&:runtime_formula_dependencies)
|
||||
|
||||
leaves_list.select!(&method(:installed_on_request?)) if args.installed_on_request?
|
||||
leaves_list.select!(&method(:installed_as_dependency?)) if args.installed_as_dependency?
|
||||
|
||||
@ -70,14 +70,6 @@ module Homebrew
|
||||
def list
|
||||
args = list_args.parse
|
||||
|
||||
# Unbrewed uses the PREFIX, which will exist
|
||||
# Things below use the CELLAR, which doesn't until the first formula is installed.
|
||||
unless HOMEBREW_CELLAR.exist?
|
||||
raise NoSuchKegError, args.named.first if args.named.present? && !args.cask?
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if args.full_name?
|
||||
unless args.cask?
|
||||
formula_names = args.no_named? ? Formula.installed : args.named.to_resolved_formulae
|
||||
@ -112,12 +104,10 @@ module Homebrew
|
||||
if !args.cask? && HOMEBREW_CELLAR.exist? && HOMEBREW_CELLAR.children.any?
|
||||
ohai "Formulae" if $stdout.tty? && !args.formula?
|
||||
safe_system "ls", *ls_args, HOMEBREW_CELLAR
|
||||
puts if $stdout.tty? && !args.formula?
|
||||
end
|
||||
if !args.formula? && Cask::Caskroom.any_casks_installed?
|
||||
if $stdout.tty? && !args.cask?
|
||||
puts
|
||||
ohai "Casks"
|
||||
end
|
||||
ohai "Casks" if $stdout.tty? && !args.cask?
|
||||
safe_system "ls", *ls_args, Cask::Caskroom.path
|
||||
end
|
||||
else
|
||||
|
||||
@ -19,8 +19,11 @@ module Homebrew
|
||||
description: "Show all options on a single line separated by spaces."
|
||||
switch "--installed",
|
||||
description: "Show options for formulae that are currently installed."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to show their " \
|
||||
"options."
|
||||
switch "--all",
|
||||
description: "Show options for all available formulae."
|
||||
hidden: true
|
||||
flag "--command=",
|
||||
description: "Show options for the specified <command>."
|
||||
|
||||
@ -33,7 +36,13 @@ module Homebrew
|
||||
def options
|
||||
args = options_args.parse
|
||||
|
||||
all = args.eval_all?
|
||||
if args.all?
|
||||
odeprecated "brew info --all", "brew info --eval-all" if !all && !Homebrew::EnvConfig.eval_all?
|
||||
all = true
|
||||
end
|
||||
|
||||
if all
|
||||
puts_options Formula.all.sort, args: args
|
||||
elsif args.installed?
|
||||
puts_options Formula.installed.sort, args: args
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
require "readall"
|
||||
require "cli/parser"
|
||||
require "env_config"
|
||||
|
||||
module Homebrew
|
||||
extend T::Sig
|
||||
@ -22,6 +23,9 @@ module Homebrew
|
||||
description: "Verify any alias symlinks in each tap."
|
||||
switch "--syntax",
|
||||
description: "Syntax-check all of Homebrew's Ruby files (if no `<tap>` is passed)."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not. " \
|
||||
"Implied if HOMEBREW_EVAL_ALL is set."
|
||||
|
||||
named_args :tap
|
||||
end
|
||||
@ -39,6 +43,9 @@ module Homebrew
|
||||
|
||||
options = { aliases: args.aliases? }
|
||||
taps = if args.no_named?
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew readall", "brew readall --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
Tap
|
||||
else
|
||||
args.named.to_installed_taps
|
||||
|
||||
@ -44,6 +44,10 @@ module Homebrew
|
||||
switch "--desc",
|
||||
description: "Search for formulae with a description matching <text> and casks with " \
|
||||
"a name or description matching <text>."
|
||||
switch "--eval-all",
|
||||
depends_on: "--desc",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to search their " \
|
||||
"descriptions. Implied if HOMEBREW_EVAL_ALL is set."
|
||||
switch "--pull-request",
|
||||
description: "Search for GitHub pull requests containing <text>."
|
||||
switch "--open",
|
||||
@ -75,6 +79,9 @@ module Homebrew
|
||||
string_or_regex = query_regexp(query)
|
||||
|
||||
if args.desc?
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew search --desc", "brew search --desc --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
search_descriptions(string_or_regex, args)
|
||||
elsif args.pull_request?
|
||||
search_pull_requests(query, args)
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
# HOMEBREW_CELLAR and HOMEBREW_PREFIX are set by extend/ENV/super.rb
|
||||
# HOMEBREW_REPOSITORY is set by bin/brew
|
||||
# Trailing colon in MANPATH adds default man dirs to search path in Linux, does no harm in macOS.
|
||||
# Please do not submit PRs to remove it!
|
||||
# shellcheck disable=SC2154
|
||||
homebrew-shellenv() {
|
||||
if [[ "${HOMEBREW_PATH%%:"${HOMEBREW_PREFIX}"/sbin*}" == "${HOMEBREW_PREFIX}/bin" ]]
|
||||
|
||||
@ -43,6 +43,9 @@ module Homebrew
|
||||
description: "Migrate tapped formulae from symlink-based to directory-based structure."
|
||||
switch "--list-pinned",
|
||||
description: "List all pinned taps."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all the formulae, casks and aliases in the new tap to check validity. " \
|
||||
"Implied if HOMEBREW_EVAL_ALL is set."
|
||||
|
||||
named_args :tap, max: 2
|
||||
end
|
||||
@ -65,7 +68,8 @@ module Homebrew
|
||||
tap.install clone_target: args.named.second,
|
||||
force_auto_update: args.force_auto_update?,
|
||||
custom_remote: args.custom_remote?,
|
||||
quiet: args.quiet?
|
||||
quiet: args.quiet?,
|
||||
verify: args.eval_all? || Homebrew::EnvConfig.eval_all?
|
||||
rescue TapRemoteMismatchError, TapNoCustomRemoteError => e
|
||||
odie e
|
||||
rescue TapAlreadyTappedError
|
||||
|
||||
@ -547,7 +547,7 @@ EOS
|
||||
for DIR in "${HOMEBREW_REPOSITORY}" "${HOMEBREW_LIBRARY}"/Taps/*/*
|
||||
do
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" ]] &&
|
||||
[[ -n "${HOMEBREW_UPDATE_AUTO}" ]] &&
|
||||
[[ -z "${HOMEBREW_DEVELOPER}" || -n "${HOMEBREW_UPDATE_AUTO}" ]] &&
|
||||
[[ "${DIR}" == "${HOMEBREW_CORE_REPOSITORY}" ]]
|
||||
then
|
||||
continue
|
||||
@ -705,6 +705,7 @@ EOS
|
||||
for DIR in "${HOMEBREW_REPOSITORY}" "${HOMEBREW_LIBRARY}"/Taps/*/*
|
||||
do
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" ]] &&
|
||||
[[ -z "${HOMEBREW_DEVELOPER}" || -n "${HOMEBREW_UPDATE_AUTO}" ]] &&
|
||||
[[ "${DIR}" == "${HOMEBREW_CORE_REPOSITORY}" ||
|
||||
"${DIR}" == "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-cask" ]]
|
||||
then
|
||||
@ -748,11 +749,11 @@ EOS
|
||||
if [[ -n "${HOMEBREW_INSTALL_FROM_API}" ]]
|
||||
then
|
||||
mkdir -p "${HOMEBREW_CACHE}/api"
|
||||
# TODO: use --header If-Modified-Since
|
||||
curl \
|
||||
"${CURL_DISABLE_CURLRC_ARGS[@]}" \
|
||||
--fail --compressed --silent --max-time 5 \
|
||||
--location --remote-time --output "${HOMEBREW_CACHE}/api/formula.json" \
|
||||
--time-cond "${HOMEBREW_CACHE}/api/formula.json" \
|
||||
--user-agent "${HOMEBREW_USER_AGENT_CURL}" \
|
||||
"https://formulae.brew.sh/api/formula.json"
|
||||
# TODO: we probably want to print an error if this fails.
|
||||
|
||||
@ -30,9 +30,11 @@ module Homebrew
|
||||
description: "Resolve more than one level of dependencies."
|
||||
switch "--installed",
|
||||
description: "Only list formulae and casks that are currently installed."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to show " \
|
||||
"their dependents."
|
||||
switch "--all",
|
||||
description: "List all formulae and casks whether installed or not.",
|
||||
hidden: true
|
||||
hidden: true
|
||||
switch "--include-build",
|
||||
description: "Include all formulae that specify <formula> as `:build` type dependency."
|
||||
switch "--include-test",
|
||||
@ -88,8 +90,6 @@ module Homebrew
|
||||
show_formulae_and_casks = !args.formula? && !args.cask?
|
||||
includes, ignores = args_includes_ignores(args)
|
||||
|
||||
# TODO: 3.6.0: odeprecate not specifying args.all?, require args.installed?
|
||||
|
||||
deps = []
|
||||
if use_runtime_dependents
|
||||
if show_formulae_and_casks || args.formula?
|
||||
@ -106,6 +106,18 @@ module Homebrew
|
||||
|
||||
deps
|
||||
else
|
||||
all = args.eval_all?
|
||||
if args.all?
|
||||
unless all
|
||||
odeprecated "brew uses --all",
|
||||
"brew uses --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
all = true
|
||||
end
|
||||
|
||||
if !args.installed? && !(all || Homebrew::EnvConfig.eval_all?)
|
||||
odeprecated "brew uses", "brew uses --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
if show_formulae_and_casks || args.formula?
|
||||
deps += args.installed? ? Formula.installed : Formula.all
|
||||
end
|
||||
|
||||
@ -1,21 +1,2 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Cask
|
||||
class Cask
|
||||
extend Enumerable
|
||||
|
||||
def self.each(&block)
|
||||
odisabled "`Enumerable` methods on `Cask::Cask`",
|
||||
"`Cask::Cask.all` (but avoid looping over all casks, it's slow and insecure)"
|
||||
|
||||
return to_enum unless block
|
||||
|
||||
Tap.flat_map(&:cask_files).each do |f|
|
||||
yield CaskLoader::FromTapPathLoader.new(f).load(config: nil)
|
||||
rescue CaskUnreadableError => e
|
||||
opoo e.message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,20 +1,2 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Formula
|
||||
extend Enumerable
|
||||
|
||||
def self.each(&_block)
|
||||
odisabled "`Enumerable` methods on `Formula`",
|
||||
"`Formula.all` (but avoid looping over all formulae, it's slow and insecure)"
|
||||
|
||||
files.each do |file|
|
||||
yield Formulary.factory(file)
|
||||
rescue FormulaUnavailableError, FormulaUnreadableError => e
|
||||
# Don't let one broken formula break commands. But do complain.
|
||||
onoe "Failed to import: #{file}"
|
||||
$stderr.puts e
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -172,7 +172,7 @@ class CompilerSelector
|
||||
def compiler_version(name)
|
||||
case name.to_s
|
||||
when "gcc", GNU_GCC_REGEXP
|
||||
versions.non_apple_gcc_version(name.to_s)
|
||||
versions.gcc_version(name.to_s)
|
||||
else
|
||||
versions.send("#{name}_build_version")
|
||||
end
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "delegate"
|
||||
require "cask_dependent"
|
||||
|
||||
# A collection of dependencies.
|
||||
#
|
||||
|
||||
@ -26,8 +26,23 @@ class DependencyCollector
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
# Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
|
||||
@deps = Dependencies.new
|
||||
@requirements = Requirements.new
|
||||
|
||||
init_global_dep_tree_if_needed!
|
||||
end
|
||||
|
||||
def initialize_dup(other)
|
||||
super
|
||||
@deps = @deps.dup
|
||||
@requirements = @requirements.dup
|
||||
end
|
||||
|
||||
def freeze
|
||||
@deps.freeze
|
||||
@requirements.freeze
|
||||
super
|
||||
end
|
||||
|
||||
def add(spec)
|
||||
@ -36,6 +51,11 @@ class DependencyCollector
|
||||
@deps << dep
|
||||
when Requirement
|
||||
@requirements << dep
|
||||
when nil
|
||||
# no-op when we have a nil value
|
||||
nil
|
||||
else
|
||||
raise ArgumentError, "DependencyCollector#add passed something that isn't a Dependency or Requirement!"
|
||||
end
|
||||
dep
|
||||
end
|
||||
@ -57,13 +77,19 @@ class DependencyCollector
|
||||
parse_spec(spec, Array(tags))
|
||||
end
|
||||
|
||||
sig { params(related_formula_names: T::Array[String]).returns(T.nilable(Dependency)) }
|
||||
def gcc_dep_if_needed(related_formula_names); end
|
||||
|
||||
sig { params(related_formula_names: T::Array[String]).returns(T.nilable(Dependency)) }
|
||||
def glibc_dep_if_needed(related_formula_names); end
|
||||
|
||||
def git_dep_if_needed(tags)
|
||||
return if Utils::Git.available?
|
||||
|
||||
Dependency.new("git", tags)
|
||||
end
|
||||
|
||||
def brewed_curl_dep_if_needed(tags)
|
||||
def curl_dep_if_needed(tags)
|
||||
Dependency.new("curl", tags)
|
||||
end
|
||||
|
||||
@ -99,6 +125,9 @@ class DependencyCollector
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def init_global_dep_tree_if_needed!; end
|
||||
|
||||
def parse_spec(spec, tags)
|
||||
case spec
|
||||
when String
|
||||
@ -148,7 +177,7 @@ class DependencyCollector
|
||||
strategy = spec.download_strategy
|
||||
|
||||
if strategy <= HomebrewCurlDownloadStrategy
|
||||
@deps << brewed_curl_dep_if_needed(tags)
|
||||
@deps << curl_dep_if_needed(tags)
|
||||
parse_url_spec(spec.url, tags)
|
||||
elsif strategy <= CurlDownloadStrategy
|
||||
parse_url_spec(spec.url, tags)
|
||||
|
||||
@ -34,9 +34,9 @@ class DescriptionCacheStore < CacheStore
|
||||
#
|
||||
# @return [nil]
|
||||
def populate_if_empty!
|
||||
return unless Homebrew::EnvConfig.eval_all?
|
||||
return unless database.empty?
|
||||
|
||||
# TODO: 3.6.0: consider if we want to actually read all contents of all formulae or odeprecate.
|
||||
Formula.all.each { |f| update!(f.full_name, f.desc) }
|
||||
end
|
||||
|
||||
@ -45,6 +45,7 @@ class DescriptionCacheStore < CacheStore
|
||||
# @param report [Report] an update report generated by cmd/update.rb
|
||||
# @return [nil]
|
||||
def update_from_report!(report)
|
||||
return unless Homebrew::EnvConfig.eval_all?
|
||||
return populate_if_empty! if database.empty?
|
||||
return if report.empty?
|
||||
|
||||
@ -63,6 +64,7 @@ class DescriptionCacheStore < CacheStore
|
||||
# @param formula_names [Array] the formulae to update
|
||||
# @return [nil]
|
||||
def update_from_formula_names!(formula_names)
|
||||
return unless Homebrew::EnvConfig.eval_all?
|
||||
return populate_if_empty! if database.empty?
|
||||
|
||||
formula_names.each do |name|
|
||||
@ -100,9 +102,9 @@ class CaskDescriptionCacheStore < DescriptionCacheStore
|
||||
#
|
||||
# @return [nil]
|
||||
def populate_if_empty!
|
||||
return unless Homebrew::EnvConfig.eval_all?
|
||||
return unless database.empty?
|
||||
|
||||
# TODO: 3.6.0: consider if we want to actually read all contents of all casks or odeprecate.
|
||||
Cask::Cask.all.each { |c| update!(c.full_name, [c.name.join(", "), c.desc.presence]) }
|
||||
end
|
||||
|
||||
@ -111,6 +113,7 @@ class CaskDescriptionCacheStore < DescriptionCacheStore
|
||||
# @param report [Report] an update report generated by cmd/update.rb
|
||||
# @return [nil]
|
||||
def update_from_report!(report)
|
||||
return unless Homebrew::EnvConfig.eval_all?
|
||||
return populate_if_empty! if database.empty?
|
||||
return if report.empty?
|
||||
|
||||
@ -126,6 +129,7 @@ class CaskDescriptionCacheStore < DescriptionCacheStore
|
||||
# @param cask_tokens [Array] the casks to update
|
||||
# @return [nil]
|
||||
def update_from_cask_tokens!(cask_tokens)
|
||||
return unless Homebrew::EnvConfig.eval_all?
|
||||
return populate_if_empty! if database.empty?
|
||||
|
||||
cask_tokens.each do |token|
|
||||
|
||||
@ -41,9 +41,11 @@ module Homebrew
|
||||
description: "Run additional, slower style checks that require a network connection."
|
||||
switch "--installed",
|
||||
description: "Only check formulae and casks that are currently installed."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to audit them. " \
|
||||
"Implied if HOMEBREW_EVAL_ALL is set."
|
||||
switch "--all",
|
||||
description: "Check all formulae and casks whether installed or not.",
|
||||
hidden: true
|
||||
hidden: true
|
||||
switch "--new", "--new-formula", "--new-cask",
|
||||
description: "Run various additional style checks to determine if a new formula or cask is eligible " \
|
||||
"for Homebrew. This should be used when creating new formula and implies " \
|
||||
@ -118,8 +120,6 @@ module Homebrew
|
||||
ENV.activate_extensions!
|
||||
ENV.setup_build_environment
|
||||
|
||||
# TODO: 3.6.0: odeprecate not specifying args.all?, require args.installed?
|
||||
|
||||
audit_formulae, audit_casks = if args.tap
|
||||
Tap.fetch(args.tap).then do |tap|
|
||||
[
|
||||
@ -131,12 +131,22 @@ module Homebrew
|
||||
no_named_args = true
|
||||
[Formula.installed, Cask::Caskroom.casks]
|
||||
elsif args.no_named?
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew audit",
|
||||
"brew audit --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
no_named_args = true
|
||||
[Formula.all, Cask::Cask.all]
|
||||
else
|
||||
args.named.to_formulae_and_casks
|
||||
.partition { |formula_or_cask| formula_or_cask.is_a?(Formula) }
|
||||
end
|
||||
|
||||
if audit_formulae.empty? && audit_casks.empty?
|
||||
ofail "No matching formulae or casks to audit!"
|
||||
return
|
||||
end
|
||||
|
||||
style_files = args.named.to_paths unless skip_style
|
||||
|
||||
only_cops = args.only_cops
|
||||
|
||||
@ -121,8 +121,8 @@ module Homebrew
|
||||
if new_version.present?
|
||||
if new_version.latest?
|
||||
opoo "Ignoring specified `--sha256=` argument." if new_hash.present?
|
||||
new_hash = :no_check
|
||||
elsif new_hash.nil? || cask.languages.present?
|
||||
replacement_pairs << [old_hash, ":no_check"]
|
||||
elsif old_hash != :no_check && (new_hash.nil? || cask.languages.present?)
|
||||
tmp_contents = Utils::Inreplace.inreplace_pairs(cask.sourcefile_path,
|
||||
replacement_pairs.uniq.compact,
|
||||
read_only_run: true,
|
||||
@ -131,27 +131,27 @@ module Homebrew
|
||||
tmp_cask = Cask::CaskLoader.load(tmp_contents)
|
||||
tmp_config = tmp_cask.config
|
||||
|
||||
new_hash = fetch_cask(tmp_contents)[1] if old_hash != :no_check && new_hash.nil?
|
||||
[:arm, :intel].each do |arch|
|
||||
Homebrew::SimulateSystem.arch = arch
|
||||
|
||||
cask.languages.each do |language|
|
||||
lang_config = tmp_config.merge(Cask::Config.new(explicit: { languages: [language] }))
|
||||
replacement_pairs << fetch_cask(tmp_contents, config: lang_config)
|
||||
end
|
||||
languages = cask.languages
|
||||
languages = [nil] if languages.empty?
|
||||
languages.each do |language|
|
||||
new_hash_config = if language.blank?
|
||||
tmp_config
|
||||
else
|
||||
tmp_config.merge(Cask::Config.new(explicit: { languages: [language] }))
|
||||
end
|
||||
|
||||
# TODO: Use SimulateSystem once all casks use on_system blocks
|
||||
if tmp_contents.include?("Hardware::CPU.intel?")
|
||||
other_intel = !Hardware::CPU.intel?
|
||||
Homebrew::SimulateSystem.arch = other_intel ? :intel : :arm
|
||||
other_contents = tmp_contents.gsub("Hardware::CPU.intel?", other_intel.to_s)
|
||||
other_cask = Cask::CaskLoader.load(other_contents)
|
||||
new_hash_cask = Cask::CaskLoader.load(tmp_contents)
|
||||
new_hash_cask.config = new_hash_config
|
||||
old_hash = new_hash_cask.sha256.to_s
|
||||
|
||||
if other_cask.sha256 != :no_check && other_cask.language.blank?
|
||||
replacement_pairs << fetch_cask(other_contents)
|
||||
end
|
||||
cask_download = Cask::Download.new(new_hash_cask, quarantine: true)
|
||||
download = cask_download.fetch(verify_download_integrity: false)
|
||||
Utils::Tar.validate_file(download)
|
||||
|
||||
other_cask.languages.each do |language|
|
||||
lang_config = other_cask.config.merge(Cask::Config.new(explicit: { languages: [language] }))
|
||||
replacement_pairs << fetch_cask(other_contents, config: lang_config)
|
||||
replacement_pairs << [new_hash_cask.sha256.to_s, download.sha256]
|
||||
end
|
||||
|
||||
Homebrew::SimulateSystem.clear
|
||||
@ -159,15 +159,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
if new_hash.present? && cask.language.blank? # avoid repeated replacement for multilanguage cask
|
||||
hash_regex = (old_hash == :no_check) ? ":no_check" : "[\"']#{Regexp.escape(old_hash.to_s)}[\"']"
|
||||
|
||||
replacement_pairs << [
|
||||
/sha256\s+#{hash_regex}/m,
|
||||
"sha256 #{(new_hash == :no_check) ? ":no_check" : "\"#{new_hash}\""}",
|
||||
]
|
||||
end
|
||||
|
||||
Utils::Inreplace.inreplace_pairs(cask.sourcefile_path,
|
||||
replacement_pairs.uniq.compact,
|
||||
read_only_run: args.dry_run?,
|
||||
@ -197,19 +188,6 @@ module Homebrew
|
||||
GitHub.create_bump_pr(pr_info, args: args)
|
||||
end
|
||||
|
||||
def fetch_cask(contents, config: nil)
|
||||
cask = Cask::CaskLoader.load(contents)
|
||||
cask.config = config if config.present?
|
||||
old_hash = cask.sha256.to_s
|
||||
|
||||
cask_download = Cask::Download.new(cask, quarantine: true)
|
||||
download = cask_download.fetch(verify_download_integrity: false)
|
||||
Utils::Tar.validate_file(download)
|
||||
new_hash = download.sha256
|
||||
|
||||
[old_hash, new_hash]
|
||||
end
|
||||
|
||||
def check_open_pull_requests(cask, args:)
|
||||
tap_remote_repo = cask.tap.remote_repo || cask.tap.full_name
|
||||
GitHub.check_for_duplicate_pull_requests(cask.token, tap_remote_repo,
|
||||
|
||||
@ -18,8 +18,8 @@ module Homebrew
|
||||
Create a pull request to update <formula> with a new URL or a new tag.
|
||||
|
||||
If a <URL> is specified, the <SHA-256> checksum of the new download should also
|
||||
be specified. A best effort to determine the <SHA-256> and <formula> name will
|
||||
be made if either or both values are not supplied by the user.
|
||||
be specified. A best effort to determine the <SHA-256> will be made if not supplied
|
||||
by the user.
|
||||
|
||||
If a <tag> is specified, the Git commit <revision> corresponding to that tag
|
||||
should also be specified. A best effort to determine the <revision> will be made
|
||||
@ -34,12 +34,8 @@ module Homebrew
|
||||
EOS
|
||||
switch "-n", "--dry-run",
|
||||
description: "Print what would be done rather than doing it."
|
||||
switch "--all",
|
||||
description: "Read all formulae if necessary to determine URL.",
|
||||
hidden: true
|
||||
switch "--write-only",
|
||||
description: "Make the expected file modifications without taking any Git actions."
|
||||
switch "--write", hidden: true
|
||||
switch "--commit",
|
||||
depends_on: "--write-only",
|
||||
description: "When passed with `--write-only`, generate a new commit after writing changes " \
|
||||
@ -87,11 +83,9 @@ module Homebrew
|
||||
description: "Exclude these Python packages when finding resources."
|
||||
|
||||
conflicts "--dry-run", "--write-only"
|
||||
conflicts "--dry-run", "--write"
|
||||
conflicts "--no-audit", "--strict"
|
||||
conflicts "--no-audit", "--online"
|
||||
conflicts "--url", "--tag"
|
||||
conflicts "--installed", "--all"
|
||||
|
||||
named_args :formula, max: 1
|
||||
end
|
||||
@ -100,8 +94,6 @@ module Homebrew
|
||||
def bump_formula_pr
|
||||
args = bump_formula_pr_args.parse
|
||||
|
||||
odisabled "`brew bump-formula-pr --write`", "`brew bump-formula-pr --write-only`" if args.write?
|
||||
|
||||
if args.revision.present? && args.tag.nil? && args.version.nil?
|
||||
raise UsageError, "`--revision` must be passed with either `--tag` or `--version`!"
|
||||
end
|
||||
@ -114,9 +106,7 @@ module Homebrew
|
||||
ENV["BROWSER"] = Homebrew::EnvConfig.browser
|
||||
|
||||
formula = args.named.to_formulae.first
|
||||
|
||||
new_url = args.url
|
||||
formula ||= determine_formula_from_url(new_url) if new_url.present?
|
||||
raise FormulaUnspecifiedError if formula.blank?
|
||||
|
||||
odie "This formula is disabled!" if formula.disabled?
|
||||
@ -372,27 +362,6 @@ module Homebrew
|
||||
GitHub.create_bump_pr(pr_info, args: args)
|
||||
end
|
||||
|
||||
def determine_formula_from_url(url)
|
||||
# Split the new URL on / and find any formulae that have the same URL
|
||||
# except for the last component, but don't try to match any more than the
|
||||
# first five components since sometimes the last component isn't the only
|
||||
# one to change.
|
||||
url_split = url.split("/")
|
||||
maximum_url_components_to_match = 5
|
||||
components_to_match = [url_split.count - 1, maximum_url_components_to_match].min
|
||||
base_url = url_split.first(components_to_match).join("/")
|
||||
base_url = /#{Regexp.escape(base_url)}/
|
||||
guesses = []
|
||||
# TODO: 3.6.0: odeprecate not specifying args.all?
|
||||
Formula.all.each do |f|
|
||||
guesses << f if f.stable&.url&.match(base_url)
|
||||
end
|
||||
return guesses.shift if guesses.count == 1
|
||||
return if guesses.count <= 1
|
||||
|
||||
odie "Couldn't guess formula for sure; could be one of these:\n#{guesses.map(&:name).join(", ")}"
|
||||
end
|
||||
|
||||
def determine_mirror(url)
|
||||
case url
|
||||
when %r{.*ftp\.gnu\.org/gnu.*}
|
||||
|
||||
@ -72,17 +72,18 @@ module Homebrew
|
||||
|
||||
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) }
|
||||
ambiguous_casks = formulae_and_casks \
|
||||
.group_by { |item| Livecheck.package_or_resource_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) }
|
||||
(formulae_and_casks - ambiguous_casks).group_by { |item| Livecheck.package_or_resource_name(item) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
@ -92,7 +93,7 @@ module Homebrew
|
||||
puts if i.positive?
|
||||
|
||||
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)
|
||||
name = Livecheck.package_or_resource_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
|
||||
@ -157,7 +158,7 @@ module Homebrew
|
||||
rescue
|
||||
next
|
||||
end
|
||||
name = Livecheck.formula_or_cask_name(formula_or_cask)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask)
|
||||
ambiguous_cask = begin
|
||||
formula_or_cask.is_a?(Cask::Cask) && !args.cask? && Formula[name]
|
||||
rescue FormulaUnavailableError
|
||||
@ -178,7 +179,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
def livecheck_result(formula_or_cask)
|
||||
name = Livecheck.formula_or_cask_name(formula_or_cask)
|
||||
name = Livecheck.package_or_resource_name(formula_or_cask)
|
||||
|
||||
referenced_formula_or_cask, =
|
||||
Livecheck.resolve_livecheck_reference(formula_or_cask, full_name: false, debug: false)
|
||||
|
||||
@ -69,7 +69,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
if args.linux?
|
||||
runners << "ubuntu-latest"
|
||||
runners << "ubuntu-22.04"
|
||||
elsif args.linux_self_hosted?
|
||||
runners << "linux-self-hosted-1"
|
||||
end
|
||||
|
||||
@ -23,6 +23,7 @@ module Homebrew
|
||||
Generate Homebrew's manpages and shell completions.
|
||||
EOS
|
||||
switch "--fail-if-not-changed",
|
||||
hidden: true,
|
||||
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 " \
|
||||
@ -34,27 +35,30 @@ module Homebrew
|
||||
def generate_man_completions
|
||||
args = generate_man_completions_args.parse
|
||||
|
||||
odeprecated "brew generate-man-completions --fail-if-not-changed" if args.fail_if_not_changed?
|
||||
|
||||
Commands.rebuild_internal_commands_completion_list
|
||||
regenerate_man_pages(preserve_date: args.fail_if_not_changed?, quiet: args.quiet?)
|
||||
regenerate_man_pages(quiet: args.quiet?)
|
||||
Completions.update_shell_completions!
|
||||
|
||||
diff = system_command "git", args: [
|
||||
"-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "docs/Manpage.md", "manpages", "completions"
|
||||
]
|
||||
|
||||
return unless diff.status.success?
|
||||
|
||||
puts "No changes to manpage or completions output detected."
|
||||
Homebrew.failed = true if args.fail_if_not_changed?
|
||||
if diff.status.success?
|
||||
ofail "No changes to manpage or completions."
|
||||
else
|
||||
puts "Manpage and completions updated."
|
||||
end
|
||||
end
|
||||
|
||||
def regenerate_man_pages(preserve_date:, quiet:)
|
||||
# TODO: move this method and all called functions to manpages.rb
|
||||
def regenerate_man_pages(quiet:)
|
||||
Homebrew.install_bundler_gems!
|
||||
|
||||
markup = build_man_page(quiet: quiet)
|
||||
convert_man_page(markup, TARGET_DOC_PATH/"Manpage.md", preserve_date: preserve_date)
|
||||
convert_man_page(markup, TARGET_DOC_PATH/"Manpage.md")
|
||||
markup = I18n.transliterate(markup, locale: :en)
|
||||
convert_man_page(markup, TARGET_MAN_PATH/"brew.1", preserve_date: preserve_date)
|
||||
convert_man_page(markup, TARGET_MAN_PATH/"brew.1")
|
||||
end
|
||||
|
||||
def build_man_page(quiet:)
|
||||
@ -94,13 +98,13 @@ module Homebrew
|
||||
path.basename.to_s.sub(/\.(rb|sh)$/, "").sub(/^--/, "~~")
|
||||
end
|
||||
|
||||
def convert_man_page(markup, target, preserve_date:)
|
||||
def convert_man_page(markup, target)
|
||||
manual = target.basename(".1")
|
||||
organisation = "Homebrew"
|
||||
|
||||
# Set the manpage date to the existing one if we're checking for changes.
|
||||
# Set the manpage date to the existing one if we're updating.
|
||||
# This avoids the only change being e.g. a new date.
|
||||
date = if preserve_date && target.extname == ".1" && target.exist?
|
||||
date = if target.extname == ".1" && target.exist?
|
||||
/"(\d{1,2})" "([A-Z][a-z]+) (\d{4})" "#{organisation}" "#{manual}"/ =~ target.read
|
||||
Date.parse("#{Regexp.last_match(1)} #{Regexp.last_match(2)} #{Regexp.last_match(3)}")
|
||||
else
|
||||
|
||||
@ -50,7 +50,7 @@ module Homebrew
|
||||
|
||||
if args.test?
|
||||
result.display_test_output(strict: args.strict?)
|
||||
Homebrew.failed = true if result.broken_library_linkage?(strict: args.strict?)
|
||||
Homebrew.failed = true if result.broken_library_linkage?(test: true, strict: args.strict?)
|
||||
elsif args.reverse?
|
||||
result.display_reverse_output
|
||||
else
|
||||
|
||||
@ -24,17 +24,21 @@ module Homebrew
|
||||
`~/.brew_livecheck_watchlist`.
|
||||
EOS
|
||||
switch "--full-name",
|
||||
description: "Print formulae/casks with fully-qualified names."
|
||||
description: "Print formulae and casks with fully-qualified names."
|
||||
flag "--tap=",
|
||||
description: "Check formulae/casks within the given tap, specified as <user>`/`<repo>."
|
||||
description: "Check formulae and casks within the given tap, specified as <user>`/`<repo>."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to check them."
|
||||
switch "--all",
|
||||
description: "Check all available formulae/casks."
|
||||
hidden: true
|
||||
switch "--installed",
|
||||
description: "Check formulae/casks that are currently installed."
|
||||
description: "Check formulae and casks that are currently installed."
|
||||
switch "--newer-only",
|
||||
description: "Show the latest version only if it's newer than the formula/cask."
|
||||
switch "--json",
|
||||
description: "Output information in JSON format."
|
||||
switch "-r", "--resources",
|
||||
description: "Also check resources for formulae."
|
||||
switch "-q", "--quiet",
|
||||
description: "Suppress warnings, don't print a progress bar for JSON output."
|
||||
switch "--formula", "--formulae",
|
||||
@ -43,7 +47,7 @@ module Homebrew
|
||||
description: "Only check casks."
|
||||
|
||||
conflicts "--debug", "--json"
|
||||
conflicts "--tap=", "--all", "--installed"
|
||||
conflicts "--tap=", "--eval-all", "--installed"
|
||||
conflicts "--cask", "--formula"
|
||||
|
||||
named_args [:formula, :cask]
|
||||
@ -53,6 +57,12 @@ module Homebrew
|
||||
def livecheck
|
||||
args = livecheck_args.parse
|
||||
|
||||
all = args.eval_all?
|
||||
if args.all?
|
||||
odeprecated "brew livecheck --all", "brew livecheck --eval-all" if !all && !Homebrew::EnvConfig.eval_all?
|
||||
all = true
|
||||
end
|
||||
|
||||
if args.debug? && args.verbose?
|
||||
puts args
|
||||
puts Homebrew::EnvConfig.livecheck_watchlist if Homebrew::EnvConfig.livecheck_watchlist.present?
|
||||
@ -67,7 +77,7 @@ module Homebrew
|
||||
formulae = args.cask? ? [] : Formula.installed
|
||||
casks = args.formula? ? [] : Cask::Caskroom.casks
|
||||
formulae + casks
|
||||
elsif args.all?
|
||||
elsif all
|
||||
formulae = args.cask? ? [] : Formula.all
|
||||
casks = args.formula? ? [] : Cask::Cask.all
|
||||
formulae + casks
|
||||
@ -93,6 +103,7 @@ module Homebrew
|
||||
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
|
||||
@ -103,6 +114,7 @@ module Homebrew
|
||||
json: args.json?,
|
||||
full_name: args.full_name?,
|
||||
handle_name_conflict: !args.formula? && !args.cask?,
|
||||
check_resources: args.resources?,
|
||||
newer_only: args.newer_only?,
|
||||
quiet: args.quiet?,
|
||||
debug: args.debug?,
|
||||
|
||||
@ -368,28 +368,32 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def pr_check_conflicts(user, repo, pr)
|
||||
long_build_pr_files = GitHub.search_issues(
|
||||
"org:#{user}", repo: repo, state: "open", label: "\"no long build conflict\""
|
||||
def pr_check_conflicts(repo, pr)
|
||||
long_build_pr_files = GitHub.issues(
|
||||
repo: repo, state: "open", labels: "no long build conflict",
|
||||
).each_with_object({}) do |long_build_pr, hash|
|
||||
next unless long_build_pr.key?("pull_request")
|
||||
|
||||
number = long_build_pr["number"]
|
||||
next if number == pr.to_i
|
||||
|
||||
GitHub.get_pull_request_changed_files("#{user}/#{repo}", number).each do |file|
|
||||
GitHub.get_pull_request_changed_files(repo, number).each do |file|
|
||||
key = file["filename"]
|
||||
hash[key] ||= []
|
||||
hash[key] << number
|
||||
end
|
||||
end
|
||||
|
||||
this_pr_files = GitHub.get_pull_request_changed_files("#{user}/#{repo}", pr)
|
||||
return if long_build_pr_files.blank?
|
||||
|
||||
this_pr_files = GitHub.get_pull_request_changed_files(repo, pr)
|
||||
|
||||
conflicts = this_pr_files.each_with_object({}) do |file, hash|
|
||||
filename = file["filename"]
|
||||
next unless long_build_pr_files.key?(filename)
|
||||
|
||||
long_build_pr_files[filename].each do |pr_number|
|
||||
key = "#{user}/#{repo}/pull/#{pr_number}"
|
||||
key = "#{repo}/pull/#{pr_number}"
|
||||
hash[key] ||= []
|
||||
hash[key] << filename
|
||||
end
|
||||
@ -440,7 +444,7 @@ module Homebrew
|
||||
opoo "Current branch is #{tap.path.git_branch}: do you need to pull inside #{tap.path.git_origin_branch}?"
|
||||
end
|
||||
|
||||
pr_check_conflicts(user, repo, pr)
|
||||
pr_check_conflicts("#{user}/#{repo}", pr)
|
||||
|
||||
ohai "Fetching #{tap} pull request ##{pr}"
|
||||
Dir.mktmpdir pr do |dir|
|
||||
|
||||
@ -30,7 +30,6 @@ module Homebrew
|
||||
description: "Skip running `brew bottle` before uploading."
|
||||
flag "--committer=",
|
||||
description: "Specify a committer name and email in `git`'s standard author format."
|
||||
flag "--github-org=", hidden: true
|
||||
flag "--root-url=",
|
||||
description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
|
||||
flag "--root-url-using=",
|
||||
@ -90,8 +89,6 @@ module Homebrew
|
||||
def pr_upload
|
||||
args = pr_upload_args.parse
|
||||
|
||||
odisabled "`brew pr-upload --github-org`", "`brew pr-upload` without `--github-org`" if args.github_org
|
||||
|
||||
json_files = Dir["*.bottle.json"]
|
||||
odie "No bottle JSON files found in the current working directory" if json_files.blank?
|
||||
bottles_hash = bottles_hash_from_json_files(json_files, args)
|
||||
|
||||
@ -74,7 +74,7 @@ module Homebrew
|
||||
test-bot:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
os: [ubuntu-22.04, macos-12]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
@ -119,7 +119,7 @@ module Homebrew
|
||||
jobs:
|
||||
pr-pull:
|
||||
if: contains(github.event.pull_request.labels.*.name, '#{label}')
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
@ -119,6 +119,7 @@ module Homebrew
|
||||
if args.retry? && @test_failed.add?(f)
|
||||
oh1 "Testing #{f.full_name} (again)"
|
||||
f.clear_cache
|
||||
ENV["RUST_BACKTRACE"] = "full"
|
||||
true
|
||||
else
|
||||
Homebrew.failed = true
|
||||
|
||||
@ -20,13 +20,13 @@ module Homebrew
|
||||
description: "Silence all non-critical errors."
|
||||
switch "--update",
|
||||
description: "Update RBI files."
|
||||
switch "--all",
|
||||
depends_on: "--update",
|
||||
description: "Regenerate all RBI files rather than just updated gems."
|
||||
switch "--update-all",
|
||||
description: "Update all RBI files rather than just updated gems."
|
||||
switch "--suggest-typed",
|
||||
depends_on: "--update",
|
||||
description: "Try upgrading `typed` sigils."
|
||||
switch "--fail-if-not-changed",
|
||||
hidden: true,
|
||||
description: "Return a failing status code if all gems are up to date " \
|
||||
"and gem definitions do not need a tapioca update."
|
||||
flag "--dir=",
|
||||
@ -50,7 +50,9 @@ module Homebrew
|
||||
Homebrew.install_bundler_gems!(groups: ["sorbet"])
|
||||
|
||||
HOMEBREW_LIBRARY_PATH.cd do
|
||||
if args.update?
|
||||
if args.update? || args.update_all?
|
||||
odeprecated "brew typecheck --update --fail-if-not-changed" if args.fail_if_not_changed?
|
||||
|
||||
excluded_gems = [
|
||||
"did_you_mean", # RBI file is already provided by Sorbet
|
||||
"webrobots", # RBI file is bugged
|
||||
@ -60,7 +62,7 @@ module Homebrew
|
||||
"msgpack:false", # Investigate removing this with Tapioca 0.8
|
||||
]
|
||||
tapioca_args = ["--exclude", *excluded_gems, "--typed-overrides", *typed_overrides]
|
||||
tapioca_args << "--all" if args.all?
|
||||
tapioca_args << "--all" if args.update_all?
|
||||
|
||||
ohai "Updating Tapioca RBI files..."
|
||||
safe_system "bundle", "exec", "tapioca", "gem", *tapioca_args
|
||||
@ -100,8 +102,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
Homebrew.failed = system("git", "diff", "--stat", "--exit-code") if args.fail_if_not_changed?
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@ -20,11 +20,13 @@ module Homebrew
|
||||
description: "Use the specified bottle tag (e.g. `big_sur`) instead of the current OS."
|
||||
switch "--dependents",
|
||||
description: "Skip getting analytics data and sort by number of dependents instead."
|
||||
switch "--all", "--total",
|
||||
switch "--total",
|
||||
description: "Print the number of unbottled and total formulae."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to check them. " \
|
||||
"Implied if HOMEBREW_EVAL_ALL is set."
|
||||
|
||||
conflicts "--dependents", "--all"
|
||||
conflicts "--installed", "--all"
|
||||
conflicts "--dependents", "--total"
|
||||
|
||||
named_args :formula
|
||||
end
|
||||
@ -42,16 +44,22 @@ module Homebrew
|
||||
Utils::Bottles.tag
|
||||
end
|
||||
|
||||
# TODO: 3.6.0: odeprecate args.total?
|
||||
all = args.eval_all?
|
||||
if args.total?
|
||||
if !all && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew unbottled --total", "brew unbottled --total --eval-all or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
all = true
|
||||
end
|
||||
|
||||
if args.named.blank?
|
||||
ohai "Getting formulae..."
|
||||
elsif args.all?
|
||||
raise UsageError, "cannot specify `<formula>` and `--all`/`--total`."
|
||||
elsif all
|
||||
raise UsageError, "cannot specify `<formula>` and `--eval-all`/`--total`."
|
||||
end
|
||||
|
||||
formulae, all_formulae, formula_installs =
|
||||
formulae_all_installs_from_args(args)
|
||||
formulae_all_installs_from_args(args, all)
|
||||
deps_hash, uses_hash = deps_uses_from_formulae(all_formulae)
|
||||
|
||||
if args.dependents?
|
||||
@ -60,9 +68,7 @@ module Homebrew
|
||||
dependents = uses_hash[f.name]&.length || 0
|
||||
formula_dependents[f.name] ||= dependents
|
||||
end.reverse
|
||||
end
|
||||
|
||||
if args.all?
|
||||
elsif all
|
||||
output_total(formulae)
|
||||
return
|
||||
end
|
||||
@ -78,16 +84,18 @@ module Homebrew
|
||||
output_unbottled(formulae, deps_hash, noun, hash, args.named.present?)
|
||||
end
|
||||
|
||||
def formulae_all_installs_from_args(args)
|
||||
def formulae_all_installs_from_args(args, all)
|
||||
if args.named.present?
|
||||
formulae = all_formulae = args.named.to_formulae
|
||||
elsif args.all?
|
||||
formulae = all_formulae = Formula.all
|
||||
elsif args.dependents?
|
||||
# TODO: 3.6.0: odeprecate not specifying args.all? for args.dependents?
|
||||
if !args.all? && !Homebrew::EnvConfig.eval_all?
|
||||
odeprecated "brew unbottled --dependents", "brew unbottled --all --dependents or HOMEBREW_EVAL_ALL"
|
||||
end
|
||||
formulae = all_formulae = Formula.all
|
||||
|
||||
@sort = " (sorted by number of dependents)"
|
||||
elsif all
|
||||
formulae = all_formulae = Formula.all
|
||||
else
|
||||
formula_installs = {}
|
||||
|
||||
|
||||
@ -3,9 +3,11 @@
|
||||
|
||||
require "cli/parser"
|
||||
require "utils/spdx"
|
||||
require "system_command"
|
||||
|
||||
module Homebrew
|
||||
extend T::Sig
|
||||
include SystemCommand::Mixin
|
||||
|
||||
module_function
|
||||
|
||||
@ -16,6 +18,7 @@ module Homebrew
|
||||
Update SPDX license data in the Homebrew repository.
|
||||
EOS
|
||||
switch "--fail-if-not-changed",
|
||||
hidden: true,
|
||||
description: "Return a failing status code if current license data's version is the same as " \
|
||||
"the upstream. This can be used to notify CI when the SPDX license data is out of date."
|
||||
|
||||
@ -25,11 +28,16 @@ module Homebrew
|
||||
|
||||
def update_license_data
|
||||
args = update_license_data_args.parse
|
||||
ohai "Updating SPDX license data..."
|
||||
odeprecated "brew update-license-data --fail-if-not-changed" if args.fail_if_not_changed?
|
||||
|
||||
SPDX.download_latest_license_data!
|
||||
return unless args.fail_if_not_changed?
|
||||
|
||||
Homebrew.failed = system("git", "diff", "--stat", "--exit-code", SPDX::DATA_PATH)
|
||||
diff = system_command "git", args: [
|
||||
"-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", SPDX::DATA_PATH
|
||||
]
|
||||
if diff.status.success?
|
||||
ofail "No changes to SPDX license data."
|
||||
else
|
||||
puts "SPDX license data updated."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
|
||||
# TODO: move function to manpages.rb and require that instead
|
||||
require "dev-cmd/generate-man-completions"
|
||||
|
||||
module Homebrew
|
||||
@ -57,9 +59,10 @@ module Homebrew
|
||||
"-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "README.md"
|
||||
]
|
||||
if diff.status.success?
|
||||
puts "No changes to list of maintainers."
|
||||
ofail "No changes to list of maintainers."
|
||||
else
|
||||
Homebrew.regenerate_man_pages(preserve_date: true, quiet: true)
|
||||
# TODO: move function to manpages.rb and call that instead
|
||||
Homebrew.regenerate_man_pages(quiet: true)
|
||||
puts "List of maintainers updated in the README and the generated man pages."
|
||||
end
|
||||
end
|
||||
|
||||
@ -9,11 +9,11 @@ module Homebrew
|
||||
|
||||
module_function
|
||||
|
||||
NAMED_TIER_AMOUNT = 100
|
||||
URL_TIER_AMOUNT = 1000
|
||||
NAMED_MONTHLY_AMOUNT = 100
|
||||
URL_MONTHLY_AMOUNT = 1000
|
||||
|
||||
sig { returns(CLI::Parser) }
|
||||
def sponsors_args
|
||||
def update_sponsors_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
description <<~EOS
|
||||
Update the list of GitHub Sponsors in the `Homebrew/brew` README.
|
||||
@ -23,38 +23,36 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def sponsor_name(s)
|
||||
s["name"] || s["login"]
|
||||
def sponsor_name(sponsor)
|
||||
sponsor[:name] || sponsor[:login]
|
||||
end
|
||||
|
||||
def sponsor_logo(s)
|
||||
"https://github.com/#{s["login"]}.png?size=64"
|
||||
def sponsor_logo(sponsor)
|
||||
"https://github.com/#{sponsor[:login]}.png?size=64"
|
||||
end
|
||||
|
||||
def sponsor_url(s)
|
||||
"https://github.com/#{s["login"]}"
|
||||
def sponsor_url(sponsor)
|
||||
"https://github.com/#{sponsor[:login]}"
|
||||
end
|
||||
|
||||
def sponsors
|
||||
sponsors_args.parse
|
||||
def update_sponsors
|
||||
update_sponsors_args.parse
|
||||
|
||||
named_sponsors = []
|
||||
logo_sponsors = []
|
||||
largest_monthly_amount = 0
|
||||
|
||||
GitHub.sponsors_by_tier("Homebrew").each do |tier|
|
||||
if tier["tier"] >= NAMED_TIER_AMOUNT
|
||||
named_sponsors += tier["sponsors"].map do |s|
|
||||
"[#{sponsor_name(s)}](#{sponsor_url(s)})"
|
||||
end
|
||||
end
|
||||
GitHub.sponsorships("Homebrew").each do |s|
|
||||
largest_monthly_amount = [s[:monthly_amount], s[:closest_tier_monthly_amount]].max
|
||||
named_sponsors << "[#{sponsor_name(s)}](#{sponsor_url(s)})" if largest_monthly_amount >= NAMED_MONTHLY_AMOUNT
|
||||
|
||||
next if tier["tier"] < URL_TIER_AMOUNT
|
||||
next if largest_monthly_amount < URL_MONTHLY_AMOUNT
|
||||
|
||||
logo_sponsors += tier["sponsors"].map do |s|
|
||||
"[})](#{sponsor_url(s)})"
|
||||
end
|
||||
logo_sponsors << "[})](#{sponsor_url(s)})"
|
||||
end
|
||||
|
||||
odie "No sponsorships amounts found! Ensure you have sufficient permissions!" if largest_monthly_amount.zero?
|
||||
|
||||
named_sponsors << "many other users and organisations via [GitHub Sponsors](https://github.com/sponsors/Homebrew)"
|
||||
|
||||
readme = HOMEBREW_REPOSITORY/"README.md"
|
||||
@ -68,7 +66,7 @@ module Homebrew
|
||||
"-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "README.md"
|
||||
]
|
||||
if diff.status.success?
|
||||
puts "No changes to list of sponsors."
|
||||
ofail "No changes to list of sponsors."
|
||||
else
|
||||
puts "List of sponsors updated in the README."
|
||||
end
|
||||
@ -78,8 +78,8 @@ class DevelopmentTools
|
||||
end
|
||||
|
||||
sig { params(cc: String).returns(Version) }
|
||||
def non_apple_gcc_version(cc)
|
||||
(@non_apple_gcc_version ||= {}).fetch(cc) do
|
||||
def gcc_version(cc)
|
||||
(@gcc_version ||= {}).fetch(cc) do
|
||||
path = HOMEBREW_PREFIX/"opt/#{CompilerSelector.preferred_gcc}/bin"/cc
|
||||
path = locate(cc) unless path.exist?
|
||||
version = if path &&
|
||||
@ -88,14 +88,24 @@ class DevelopmentTools
|
||||
else
|
||||
Version::NULL
|
||||
end
|
||||
@non_apple_gcc_version[cc] = version
|
||||
@gcc_version[cc] = version
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def clear_version_cache
|
||||
@clang_version = @clang_build_version = nil
|
||||
@non_apple_gcc_version = {}
|
||||
@gcc_version = {}
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def build_system_too_old?
|
||||
false
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def system_gcc_too_old?
|
||||
false
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
|
||||
@ -591,7 +591,11 @@ module Homebrew
|
||||
|
||||
def check_coretap_integrity
|
||||
coretap = CoreTap.instance
|
||||
return if !coretap.installed? && EnvConfig.install_from_api?
|
||||
unless coretap.installed?
|
||||
return if EnvConfig.install_from_api?
|
||||
|
||||
CoreTap.ensure_installed!
|
||||
end
|
||||
|
||||
broken_tap(coretap) || examine_git_origin(coretap.path, Homebrew::EnvConfig.core_git_remote)
|
||||
end
|
||||
@ -1045,11 +1049,11 @@ module Homebrew
|
||||
when :quarantine_available
|
||||
nil
|
||||
when :xattr_broken
|
||||
"There's no working version of `xattr` on this system."
|
||||
"No Cask quarantine support available: there's no working version of `xattr` on this system."
|
||||
when :no_swift
|
||||
"Swift is not available on this system."
|
||||
"No Cask quarantine support available: there's no available version of `swift` on this system."
|
||||
else
|
||||
"Unknown support status"
|
||||
"No Cask quarantine support available: unknown reason."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -153,6 +153,11 @@ module Homebrew
|
||||
"editors will do strange things in this case.",
|
||||
default_text: "`$EDITOR` or `$VISUAL`.",
|
||||
},
|
||||
HOMEBREW_EVAL_ALL: {
|
||||
description: "If set, `brew` commands evaluate all formulae and casks, executing their arbitrary code, by " \
|
||||
"default without requiring --eval-all. Required to cache formula and cask descriptions.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_FAIL_LOG_LINES: {
|
||||
description: "Output this many lines of output on formula `system` failures.",
|
||||
default: 15,
|
||||
@ -313,6 +318,10 @@ module Homebrew
|
||||
"outdated.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_PIP_INDEX_URL: {
|
||||
description: "If set, `brew install <formula>` will use this URL to download PyPI package resources.",
|
||||
default_text: "`https://pypi.org/simple`.",
|
||||
},
|
||||
HOMEBREW_PRY: {
|
||||
description: "If set, use Pry for the `brew irb` command.",
|
||||
boolean: true,
|
||||
|
||||
@ -57,7 +57,14 @@ module Stdenv
|
||||
# Os is the default Apple uses for all its stuff so let's trust them
|
||||
define_cflags "-Os #{SAFE_CFLAGS_FLAGS}"
|
||||
|
||||
send(compiler)
|
||||
begin
|
||||
send(compiler)
|
||||
rescue CompilerSelectionError
|
||||
# We don't care if our compiler fails to build the formula during `brew test`.
|
||||
raise unless testing_formula
|
||||
|
||||
send(DevelopmentTools.default_compiler)
|
||||
end
|
||||
|
||||
return unless cc&.match?(GNU_GCC_REGEXP)
|
||||
|
||||
|
||||
@ -5,11 +5,17 @@ class Module
|
||||
def attr_rw(*attrs)
|
||||
attrs.each do |attr|
|
||||
module_eval <<-EOS, __FILE__, __LINE__+1
|
||||
def #{attr}(val=nil) # def prefix(val=nil)
|
||||
@#{attr} ||= nil # @prefix ||= nil
|
||||
return @#{attr} if val.nil? # return @prefix if val.nil?
|
||||
@#{attr} = val # @prefix = val
|
||||
end # end
|
||||
def #{attr}(val=nil) # def prefix(val=nil)
|
||||
if val.nil? # if val.nil?
|
||||
if instance_variable_defined?(:@#{attr}) # if instance_variable_defined?(:@prefix)
|
||||
return @#{attr} # return @prefix
|
||||
else # else
|
||||
return nil # return nil
|
||||
end # end
|
||||
end # end
|
||||
#
|
||||
@#{attr} = val # @prefix = val
|
||||
end # end
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/os/mac/dependency_collector" if OS.mac?
|
||||
if OS.mac?
|
||||
require "extend/os/mac/dependency_collector"
|
||||
elsif OS.linux?
|
||||
require "extend/os/linux/dependency_collector"
|
||||
end
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
class CompilerSelector
|
||||
sig { returns(String) }
|
||||
def self.preferred_gcc
|
||||
# gcc-5 is the lowest gcc version we support on Linux.
|
||||
# gcc-5 is the default gcc in Ubuntu 16.04 (used for our CI)
|
||||
"gcc@5"
|
||||
OS::LINUX_PREFERRED_GCC_COMPILER_FORMULA
|
||||
end
|
||||
end
|
||||
|
||||
89
Library/Homebrew/extend/os/linux/dependency_collector.rb
Normal file
89
Library/Homebrew/extend/os/linux/dependency_collector.rb
Normal file
@ -0,0 +1,89 @@
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/glibc"
|
||||
|
||||
class DependencyCollector
|
||||
extend T::Sig
|
||||
|
||||
undef gcc_dep_if_needed
|
||||
undef glibc_dep_if_needed
|
||||
undef init_global_dep_tree_if_needed!
|
||||
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def gcc_dep_if_needed(related_formula_names)
|
||||
# gcc is required for libgcc_s.so.1 if glibc or gcc are too old
|
||||
return unless DevelopmentTools.build_system_too_old?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(GCC)
|
||||
return if global_dep_tree[GCC]&.intersect?(related_formula_names)
|
||||
|
||||
Dependency.new(GCC)
|
||||
end
|
||||
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def glibc_dep_if_needed(related_formula_names)
|
||||
return unless OS::Linux::Glibc.below_ci_version?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(GLIBC)
|
||||
return if global_dep_tree[GLIBC]&.intersect?(related_formula_names)
|
||||
|
||||
Dependency.new(GLIBC)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
GLIBC = "glibc"
|
||||
GCC = OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA
|
||||
|
||||
sig { void }
|
||||
def init_global_dep_tree_if_needed!
|
||||
return unless DevelopmentTools.build_system_too_old?
|
||||
return if building_global_dep_tree?
|
||||
return unless global_dep_tree.empty?
|
||||
|
||||
building_global_dep_tree!
|
||||
global_dep_tree[GLIBC] = Set.new(global_deps_for(GLIBC))
|
||||
# gcc depends on glibc
|
||||
global_dep_tree[GCC] = Set.new([*global_deps_for(GCC), GLIBC, *@@global_dep_tree[GLIBC]])
|
||||
built_global_dep_tree!
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(T::Array[String]) }
|
||||
def global_deps_for(name)
|
||||
@global_deps_for ||= {}
|
||||
# Always strip out glibc and gcc from all parts of dependency tree when
|
||||
# we're calculating their dependency trees. Other parts of Homebrew will
|
||||
# catch any circular dependencies.
|
||||
@global_deps_for[name] ||= Formula[name].deps.map(&:name).flat_map do |dep|
|
||||
[dep, *global_deps_for(dep)].compact
|
||||
end.uniq
|
||||
end
|
||||
|
||||
# Use class variables to avoid this expensive logic needing to be done more
|
||||
# than once.
|
||||
# rubocop:disable Style/ClassVars
|
||||
@@global_dep_tree = {}
|
||||
@@building_global_dep_tree = false
|
||||
|
||||
sig { returns(T::Hash[String, T::Set[String]]) }
|
||||
def global_dep_tree
|
||||
@@global_dep_tree
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def building_global_dep_tree!
|
||||
@@building_global_dep_tree = true
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def built_global_dep_tree!
|
||||
@@building_global_dep_tree = false
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def building_global_dep_tree?
|
||||
@@building_global_dep_tree.present?
|
||||
end
|
||||
# rubocop:enable Style/ClassVars
|
||||
end
|
||||
@ -8,10 +8,15 @@ class DevelopmentTools
|
||||
sig { params(tool: String).returns(T.nilable(Pathname)) }
|
||||
def locate(tool)
|
||||
(@locate ||= {}).fetch(tool) do |key|
|
||||
@locate[key] = if (path = HOMEBREW_PREFIX/"bin/#{tool}").executable?
|
||||
path
|
||||
elsif File.executable?(path = "/usr/bin/#{tool}")
|
||||
Pathname.new path
|
||||
@locate[key] = if build_system_too_old? &&
|
||||
(binutils_path = HOMEBREW_PREFIX/"opt/binutils/bin/#{tool}").executable?
|
||||
binutils_path
|
||||
elsif build_system_too_old? && (glibc_path = HOMEBREW_PREFIX/"opt/glibc/bin/#{tool}").executable?
|
||||
glibc_path
|
||||
elsif (homebrew_path = HOMEBREW_PREFIX/"bin/#{tool}").executable?
|
||||
homebrew_path
|
||||
elsif File.executable?(system_path = "/usr/bin/#{tool}")
|
||||
Pathname.new system_path
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -21,6 +26,21 @@ class DevelopmentTools
|
||||
:gcc
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def build_system_too_old?
|
||||
return @build_system_too_old if defined? @build_system_too_old
|
||||
|
||||
@build_system_too_old = (system_gcc_too_old? || OS::Linux::Glibc.below_ci_version?)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def system_gcc_too_old?
|
||||
gcc = "/usr/bin/gcc"
|
||||
return true unless File.exist?(gcc)
|
||||
|
||||
gcc_version(gcc) < OS::LINUX_GCC_CI_VERSION
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.nilable(String)]) }
|
||||
def build_system_info
|
||||
generic_build_system_info.merge({
|
||||
|
||||
@ -139,6 +139,35 @@ module Homebrew
|
||||
e.g. by using homebrew instead).
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_gcc_dependent_linkage
|
||||
gcc_dependents = Formula.installed.select do |formula|
|
||||
next false unless formula.tap&.core_tap?
|
||||
|
||||
# FIXME: This includes formulae that have no runtime dependency on GCC.
|
||||
formula.recursive_dependencies.map(&:name).include? "gcc"
|
||||
rescue TapFormulaUnavailableError
|
||||
false
|
||||
end
|
||||
return if gcc_dependents.empty?
|
||||
|
||||
badly_linked = gcc_dependents.select do |dependent|
|
||||
keg = Keg.new(dependent.prefix)
|
||||
keg.binary_executable_or_library_files.any? do |binary|
|
||||
paths = binary.rpaths
|
||||
versioned_linkage = paths.any? { |path| path.match?(%r{lib/gcc/\d+$}) }
|
||||
unversioned_linkage = paths.any? { |path| path.match?(%r{lib/gcc/current$}) }
|
||||
|
||||
versioned_linkage && !unversioned_linkage
|
||||
end
|
||||
end
|
||||
return if badly_linked.empty?
|
||||
|
||||
inject_file_list badly_linked, <<~EOS
|
||||
Formulae which link to GCC through a versioned path were found. These formulae
|
||||
are prone to breaking when GCC is updated. You should `brew reinstall` these formulae:
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
5
Library/Homebrew/extend/os/linux/extend/pathname.rbi
Normal file
5
Library/Homebrew/extend/os/linux/extend/pathname.rbi
Normal file
@ -0,0 +1,5 @@
|
||||
# typed: strict
|
||||
|
||||
class Pathname
|
||||
include ELFShim
|
||||
end
|
||||
@ -3,8 +3,9 @@
|
||||
|
||||
class Formula
|
||||
undef shared_library
|
||||
undef rpath
|
||||
undef loader_path
|
||||
undef deuniversalize_machos
|
||||
undef add_global_deps_to_spec
|
||||
|
||||
sig { params(name: String, version: T.nilable(T.any(String, Integer))).returns(String) }
|
||||
def shared_library(name, version = nil)
|
||||
@ -17,10 +18,29 @@ class Formula
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def rpath
|
||||
"'$ORIGIN/../lib'"
|
||||
def loader_path
|
||||
"$ORIGIN"
|
||||
end
|
||||
|
||||
sig { params(targets: T.nilable(T.any(Pathname, String))).void }
|
||||
def deuniversalize_machos(*targets); end
|
||||
|
||||
sig { params(spec: SoftwareSpec).void }
|
||||
def add_global_deps_to_spec(spec)
|
||||
return unless DevelopmentTools.build_system_too_old?
|
||||
|
||||
@global_deps ||= begin
|
||||
dependency_collector = spec.dependency_collector
|
||||
related_formula_names = Set.new([
|
||||
name,
|
||||
*aliases,
|
||||
*versioned_formulae_names,
|
||||
])
|
||||
[
|
||||
dependency_collector.gcc_dep_if_needed(related_formula_names),
|
||||
dependency_collector.glibc_dep_if_needed(related_formula_names),
|
||||
].compact.freeze
|
||||
end
|
||||
@global_deps.each { |dep| spec.dependency_collector.add(dep) }
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,21 +5,43 @@ module Homebrew
|
||||
module Install
|
||||
module_function
|
||||
|
||||
DYNAMIC_LINKERS = [
|
||||
"/lib64/ld-linux-x86-64.so.2",
|
||||
"/lib64/ld64.so.2",
|
||||
"/lib/ld-linux.so.3",
|
||||
"/lib/ld-linux.so.2",
|
||||
"/lib/ld-linux-aarch64.so.1",
|
||||
"/lib/ld-linux-armhf.so.3",
|
||||
"/system/bin/linker64",
|
||||
"/system/bin/linker",
|
||||
# This is a list of known paths to the host dynamic linker on Linux if
|
||||
# the host glibc is new enough. The symlink_ld_so method will fail if
|
||||
# the host linker cannot be found in this list.
|
||||
DYNAMIC_LINKERS = %w[
|
||||
/lib64/ld-linux-x86-64.so.2
|
||||
/lib64/ld64.so.2
|
||||
/lib/ld-linux.so.3
|
||||
/lib/ld-linux.so.2
|
||||
/lib/ld-linux-aarch64.so.1
|
||||
/lib/ld-linux-armhf.so.3
|
||||
/system/bin/linker64
|
||||
/system/bin/linker
|
||||
].freeze
|
||||
private_constant :DYNAMIC_LINKERS
|
||||
|
||||
# We link GCC runtime libraries that are not specificaly used for Fortran,
|
||||
# which are linked by the GCC formula. We only use the versioned shared libraries
|
||||
# as the other shared and static libraries are only used at build time where
|
||||
# GCC can find its own libraries.
|
||||
GCC_RUNTIME_LIBS = %w[
|
||||
libatomic.so.1
|
||||
libgcc_s.so.1
|
||||
libgomp.so.1
|
||||
libstdc++.so.6
|
||||
].freeze
|
||||
private_constant :GCC_RUNTIME_LIBS
|
||||
|
||||
def perform_preinstall_checks(all_fatal: false, cc: nil)
|
||||
generic_perform_preinstall_checks(all_fatal: all_fatal, cc: cc)
|
||||
symlink_ld_so
|
||||
setup_preferred_gcc_libs
|
||||
end
|
||||
|
||||
def global_post_install
|
||||
generic_global_post_install
|
||||
symlink_ld_so
|
||||
setup_preferred_gcc_libs
|
||||
end
|
||||
|
||||
def check_cpu
|
||||
@ -39,17 +61,73 @@ module Homebrew
|
||||
|
||||
def symlink_ld_so
|
||||
brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
|
||||
return if brew_ld_so.readable?
|
||||
|
||||
ld_so = HOMEBREW_PREFIX/"opt/glibc/lib/ld-linux-x86-64.so.2"
|
||||
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
|
||||
unless ld_so.readable?
|
||||
ld_so = DYNAMIC_LINKERS.find { |s| File.executable? s }
|
||||
raise "Unable to locate the system's dynamic linker" unless ld_so
|
||||
if ld_so.blank?
|
||||
raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return if brew_ld_so.readable? && (brew_ld_so.readlink == ld_so)
|
||||
|
||||
FileUtils.mkdir_p HOMEBREW_PREFIX/"lib"
|
||||
FileUtils.ln_sf ld_so, brew_ld_so
|
||||
end
|
||||
private_class_method :symlink_ld_so
|
||||
|
||||
def setup_preferred_gcc_libs
|
||||
gcc_opt_prefix = HOMEBREW_PREFIX/"opt/#{OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA}"
|
||||
glibc_installed = (HOMEBREW_PREFIX/"opt/glibc/bin/ld.so").readable?
|
||||
|
||||
return unless gcc_opt_prefix.readable?
|
||||
|
||||
if glibc_installed
|
||||
ld_so_conf_d = HOMEBREW_PREFIX/"etc/ld.so.conf.d"
|
||||
unless ld_so_conf_d.exist?
|
||||
ld_so_conf_d.mkpath
|
||||
FileUtils.chmod "go-w", ld_so_conf_d
|
||||
end
|
||||
|
||||
# Add gcc to ld search paths
|
||||
ld_gcc_conf = ld_so_conf_d/"50-homebrew-preferred-gcc.conf"
|
||||
ld_gcc_conf_content = <<~EOS
|
||||
# This file is generated by Homebrew. Do not modify.
|
||||
#{gcc_opt_prefix}/lib/gcc/current
|
||||
EOS
|
||||
|
||||
if !ld_gcc_conf.exist? || (ld_gcc_conf.read != ld_gcc_conf_content)
|
||||
ld_gcc_conf.atomic_write ld_gcc_conf_content
|
||||
FileUtils.chmod "u=rw,go-wx", ld_gcc_conf
|
||||
|
||||
FileUtils.rm_f HOMEBREW_PREFIX/"etc/ld.so.cache"
|
||||
system HOMEBREW_PREFIX/"opt/glibc/sbin/ldconfig"
|
||||
end
|
||||
else
|
||||
odie "#{HOMEBREW_PREFIX}/lib does not exist!" unless (HOMEBREW_PREFIX/"lib").readable?
|
||||
end
|
||||
|
||||
GCC_RUNTIME_LIBS.each do |library|
|
||||
gcc_library_symlink = HOMEBREW_PREFIX/"lib/#{library}"
|
||||
|
||||
if glibc_installed
|
||||
# Remove legacy symlinks
|
||||
FileUtils.rm gcc_library_symlink if gcc_library_symlink.symlink?
|
||||
else
|
||||
gcc_library = gcc_opt_prefix/"lib/gcc/current/#{library}"
|
||||
# Skip if the link target doesn't exist.
|
||||
next unless gcc_library.readable?
|
||||
|
||||
# Also skip if the symlink already exists.
|
||||
next if gcc_library_symlink.readable? && (gcc_library_symlink.readlink == gcc_library)
|
||||
|
||||
FileUtils.ln_sf gcc_library, gcc_library_symlink
|
||||
end
|
||||
end
|
||||
end
|
||||
private_class_method :setup_preferred_gcc_libs
|
||||
end
|
||||
end
|
||||
|
||||
@ -30,16 +30,10 @@ class Keg
|
||||
lib_path = "#{new_prefix}/lib"
|
||||
rpath << lib_path unless rpath.include? lib_path
|
||||
|
||||
# Add GCC's lib directory (as of GCC 12+) to RPATH when there is existing linkage.
|
||||
# This fixes linkage for newly-poured bottles.
|
||||
if !name.match?(Version.formula_optionally_versioned_regex(:gcc)) &&
|
||||
rpath.any? { |rp| rp.match?(%r{lib/gcc/\d+$}) }
|
||||
# TODO: Replace with
|
||||
# rpath.map! { |path| path = path.sub(%r{lib/gcc/\d+$}, "lib/gcc/current") }
|
||||
# when
|
||||
# 1. Homebrew/homebrew-core#106755 is merged
|
||||
# 2. No formula has a runtime dependency on a versioned GCC (see `envoy.rb`)
|
||||
rpath.prepend HOMEBREW_PREFIX/"opt/gcc/lib/gcc/current"
|
||||
# Add GCC's lib directory (as of GCC 12+) to RPATH when there is existing versioned linkage.
|
||||
# This prevents broken linkage when pouring bottles built with an old GCC formula.
|
||||
unless name.match?(Version.formula_optionally_versioned_regex(:gcc))
|
||||
rpath.map! { |rp| rp.sub(%r{lib/gcc/\d+$}, "lib/gcc/current") }
|
||||
end
|
||||
|
||||
rpath.join(":")
|
||||
|
||||
@ -7,10 +7,10 @@ class LinkageChecker
|
||||
# Libraries provided by glibc and gcc.
|
||||
SYSTEM_LIBRARY_ALLOWLIST = %w[
|
||||
ld-linux-x86-64.so.2
|
||||
ld-linux-aarch64.so.1
|
||||
libanl.so.1
|
||||
libatomic.so.1
|
||||
libc.so.6
|
||||
libcrypt.so.1
|
||||
libdl.so.2
|
||||
libm.so.6
|
||||
libmvec.so.1
|
||||
@ -27,18 +27,26 @@ class LinkageChecker
|
||||
].freeze
|
||||
|
||||
def display_deprecated_warning(strict: false)
|
||||
return unless @libcrypt_found
|
||||
|
||||
# Steps when moving this to `odisabled`:
|
||||
# - Remove `libcrypt.so.1` from SYSTEM_LIBRARY_ALLOWLIST above.
|
||||
# Steps when moving these to `odisabled`:
|
||||
# - Remove the old library from SYSTEM_LIBRARY_ALLOWLIST above.
|
||||
# - Remove the `disable` and `disable_for_developer` kwargs here.
|
||||
# - Remove `broken_library_linkage?` override below and the generic alias in HOMEBREW_LIBRARY/linkage_checker.rb.
|
||||
# - Remove `fail_on_libcrypt1?`.
|
||||
# Steps when removing this entirely (assuming the above has already been done):
|
||||
# - Adjust the `broken_library_linkage?` override below to not check for the library.
|
||||
# - Remove the relevant `fail_on_lib*?`.
|
||||
# If there's no more deprecations left:
|
||||
# - Remove the `broken_library_linkage?` override and the generic alias in HOMEBREW_LIBRARY/linkage_checker.rb.
|
||||
#
|
||||
# Steps when removing handling for a library entirely (assuming the steps to `odisabled` has already been done):
|
||||
# - Remove the relevant setting of `@lib*_found` in `check_dylibs` below.
|
||||
# - Remove the `odisabled` line
|
||||
# If there's no library deprecated/disabled handling left:
|
||||
# - Remove the `display_` overrides here and the associated generic aliases in HOMEBREW_LIBRARY/linkage_checker.rb
|
||||
# - Remove the setting of `@libcrypt_found` in `check_dylibs` below.
|
||||
odeprecated "linkage to libcrypt.so.1", "libcrypt.so.2 in the libxcrypt formula",
|
||||
disable: fail_on_libcrypt1?(strict: strict),
|
||||
|
||||
odisabled "linkage to libcrypt.so.1", "libcrypt.so.2 in the libxcrypt formula" if @libcrypt_found
|
||||
|
||||
return unless @libnsl_found
|
||||
|
||||
odeprecated "linkage to libnsl.so.1", "libnsl.so.3 in the libnsl formula",
|
||||
disable: fail_on_libnsl1?(strict: strict),
|
||||
disable_for_developers: false
|
||||
end
|
||||
|
||||
@ -52,29 +60,35 @@ class LinkageChecker
|
||||
display_deprecated_warning(strict: strict)
|
||||
end
|
||||
|
||||
def broken_library_linkage?(strict: false)
|
||||
generic_broken_library_linkage?(strict: strict) || (fail_on_libcrypt1?(strict: strict) && @libcrypt_found)
|
||||
def broken_library_linkage?(test: false, strict: false)
|
||||
generic_broken_library_linkage?(test: test, strict: strict) || (fail_on_libnsl1?(strict: strict) && @libnsl_found)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fail_on_libcrypt1?(strict:)
|
||||
strict || ENV["HOMEBREW_DISALLOW_LIBCRYPT1"].present?
|
||||
def fail_on_libnsl1?(strict:)
|
||||
strict || ENV["HOMEBREW_DISALLOW_LIBNSL1"].present?
|
||||
end
|
||||
|
||||
def check_dylibs(rebuild_cache:)
|
||||
generic_check_dylibs(rebuild_cache: rebuild_cache)
|
||||
|
||||
@libcrypt_found = true if @system_dylibs.any? { |s| File.basename(s) == "libcrypt.so.1" }
|
||||
@libnsl_found = true if @system_dylibs.any? { |s| File.basename(s) == "libnsl.so.1" }
|
||||
|
||||
# glibc and gcc are implicit dependencies.
|
||||
# No other linkage to system libraries is expected or desired.
|
||||
@unwanted_system_dylibs = @system_dylibs.reject do |s|
|
||||
SYSTEM_LIBRARY_ALLOWLIST.include? File.basename(s)
|
||||
end
|
||||
# FIXME: Remove this when these dependencies are injected correctly (e.g. through `DependencyCollector`)
|
||||
# See discussion at
|
||||
# https://github.com/Homebrew/brew/pull/13577
|
||||
@undeclared_deps -= [CompilerSelector.preferred_gcc, "glibc", "gcc"]
|
||||
|
||||
# We build all formulae with an RPATH that includes the gcc formula's runtime lib directory.
|
||||
# See: https://github.com/Homebrew/brew/blob/e689cc07/Library/Homebrew/extend/os/linux/extend/ENV/super.rb#L53
|
||||
# This results in formulae showing linkage with gcc whenever it is installed, even if no dependency is declared.
|
||||
# See discussions at:
|
||||
# https://github.com/Homebrew/brew/pull/13659
|
||||
# https://github.com/Homebrew/brew/pull/13796
|
||||
# TODO: Find a nicer way to handle this. (e.g. examining the ELF file to determine the required libstdc++.)
|
||||
@undeclared_deps.delete("gcc")
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# typed: strict
|
||||
# typed: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Readall
|
||||
class << self
|
||||
def valid_casks?(*)
|
||||
def valid_casks?(_casks)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
@ -28,7 +28,7 @@ module SystemConfig
|
||||
def formula_linked_version(formula)
|
||||
return "N/A" unless CoreTap.instance.installed?
|
||||
|
||||
Formulary.factory(formula).linked_version || "N/A"
|
||||
Formulary.factory(formula).any_installed_version || "N/A"
|
||||
rescue FormulaUnavailableError
|
||||
"N/A"
|
||||
end
|
||||
@ -47,7 +47,7 @@ module SystemConfig
|
||||
out.puts "Host glibc: #{host_glibc_version}"
|
||||
out.puts "/usr/bin/gcc: #{host_gcc_version}"
|
||||
out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH
|
||||
["glibc", CompilerSelector.preferred_gcc, "xorg"].each do |f|
|
||||
["glibc", CompilerSelector.preferred_gcc, OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA, "xorg"].each do |f|
|
||||
out.puts "#{f}: #{formula_linked_version(f)}"
|
||||
end
|
||||
end
|
||||
|
||||
5
Library/Homebrew/extend/os/mac/extend/pathname.rbi
Normal file
5
Library/Homebrew/extend/os/mac/extend/pathname.rbi
Normal file
@ -0,0 +1,5 @@
|
||||
# typed: strict
|
||||
|
||||
class Pathname
|
||||
include MachOShim
|
||||
end
|
||||
@ -181,6 +181,14 @@ class Formula
|
||||
|
||||
# @private
|
||||
def initialize(name, path, spec, alias_path: nil, force_bottle: false)
|
||||
# Only allow instances of subclasses. The base class does not hold any spec information (URLs etc).
|
||||
raise "Do not call `Formula.new' directly without a subclass." unless self.class < Formula
|
||||
|
||||
# Stop any subsequent modification of a formula's definition.
|
||||
# Changes do not propagate to existing instances of formulae.
|
||||
# Now that we have an instance, it's too late to make any changes to the class-level definition.
|
||||
self.class.freeze
|
||||
|
||||
@name = name
|
||||
@path = path
|
||||
@alias_path = alias_path
|
||||
@ -262,9 +270,13 @@ class Formula
|
||||
return unless spec.url
|
||||
|
||||
spec.owner = self
|
||||
add_global_deps_to_spec(spec)
|
||||
instance_variable_set("@#{name}", spec)
|
||||
end
|
||||
|
||||
sig { params(spec: SoftwareSpec).void }
|
||||
def add_global_deps_to_spec(spec); end
|
||||
|
||||
def determine_active_spec(requested)
|
||||
spec = send(requested) || stable || head
|
||||
spec || raise(FormulaSpecificationError, "formulae require at least a URL")
|
||||
@ -435,16 +447,36 @@ class Formula
|
||||
end
|
||||
|
||||
# If this is a `@`-versioned formula.
|
||||
sig { returns(T::Boolean) }
|
||||
def versioned_formula?
|
||||
name.include?("@")
|
||||
end
|
||||
|
||||
# Returns any `@`-versioned formulae for any formula (including versioned formulae).
|
||||
def versioned_formulae
|
||||
Pathname.glob(path.to_s.gsub(/(@[\d.]+)?\.rb$/, "@*.rb")).map do |versioned_path|
|
||||
# Returns any `@`-versioned formulae names for any formula (including versioned formulae).
|
||||
sig { returns(T::Array[String]) }
|
||||
def versioned_formulae_names
|
||||
versioned_paths = if tap
|
||||
# Faster path, due to `tap.versioned_formula_files` caching.
|
||||
name_prefix = "#{name.gsub(/(@[\d.]+)?$/, "")}@"
|
||||
tap.versioned_formula_files.select do |file|
|
||||
file.basename.to_s.start_with?(name_prefix)
|
||||
end
|
||||
else
|
||||
Pathname.glob(path.to_s.gsub(/(@[\d.]+)?\.rb$/, "@*.rb"))
|
||||
end
|
||||
|
||||
versioned_paths.map do |versioned_path|
|
||||
next if versioned_path == path
|
||||
|
||||
Formula[versioned_path.basename(".rb").to_s]
|
||||
versioned_path.basename(".rb").to_s
|
||||
end.compact.sort
|
||||
end
|
||||
|
||||
# Returns any `@`-versioned Formula objects for any Formula (including versioned formulae).
|
||||
sig { returns(T::Array[Formula]) }
|
||||
def versioned_formulae
|
||||
versioned_formulae_names.map do |name|
|
||||
Formula[name]
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
end.compact.sort_by(&:version).reverse
|
||||
@ -1572,9 +1604,23 @@ class Formula
|
||||
end
|
||||
|
||||
# Executable/Library RPATH according to platform conventions.
|
||||
#
|
||||
# Optionally specify a `source` or `target` depending on the location
|
||||
# of the file containing the RPATH command and where its target is located.
|
||||
#
|
||||
# <pre>
|
||||
# rpath #=> "@loader_path/../lib"
|
||||
# rpath(target: frameworks) #=> "@loader_path/../Frameworks"
|
||||
# rpath(source: libexec/"bin") #=> "@loader_path/../../lib"
|
||||
# </pre>
|
||||
sig { params(source: Pathname, target: Pathname).returns(String) }
|
||||
def rpath(source: bin, target: lib)
|
||||
"#{loader_path}/#{target.relative_path_from(source)}"
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def rpath
|
||||
"@loader_path/../lib"
|
||||
def loader_path
|
||||
"@loader_path"
|
||||
end
|
||||
|
||||
# Creates a new `Time` object for use in the formula as the build time.
|
||||
@ -1699,14 +1745,14 @@ class Formula
|
||||
elsif shell_parameter_format == :arg
|
||||
"--shell=#{shell}"
|
||||
elsif shell_parameter_format == :none
|
||||
""
|
||||
nil
|
||||
else
|
||||
"#{shell_parameter_format}#{shell}"
|
||||
end
|
||||
|
||||
popen_read_args = %w[]
|
||||
popen_read_args << commands
|
||||
popen_read_args << shell_parameter
|
||||
popen_read_args << shell_parameter if shell_parameter.present?
|
||||
popen_read_args.flatten!
|
||||
|
||||
script_path.dirname.mkpath
|
||||
@ -1760,7 +1806,10 @@ class Formula
|
||||
# this should only be used when users specify `--all` to a command
|
||||
# @private
|
||||
def self.all
|
||||
# TODO: 3.6.0: consider checking ARGV for --all
|
||||
# TODO: uncomment for 3.7.0 and ideally avoid using ARGV by moving to e.g. CLI::Parser
|
||||
# if !ARGV.include?("--eval-all") && !Homebrew::EnvConfig.eval_all?
|
||||
# odeprecated "Formula#all without --all or HOMEBREW_EVAL_ALL"
|
||||
# end
|
||||
|
||||
files.map do |file|
|
||||
Formulary.factory(file)
|
||||
@ -1795,46 +1844,6 @@ class Formula
|
||||
end.uniq(&:name)
|
||||
end
|
||||
|
||||
# An array of all installed {Formula} with {Cask} dependents.
|
||||
# @private
|
||||
def self.formulae_with_cask_dependents(casks)
|
||||
casks.flat_map { |cask| cask.depends_on[:formula] }
|
||||
.compact
|
||||
.map { |f| Formula[f] }
|
||||
.flat_map { |f| [f, *f.runtime_formula_dependencies].compact }
|
||||
end
|
||||
|
||||
# An array of all installed {Formula} without {Formula} dependents
|
||||
# @private
|
||||
def self.formulae_with_no_formula_dependents(formulae)
|
||||
return [] if formulae.blank?
|
||||
|
||||
formulae - formulae.flat_map(&:runtime_formula_dependencies)
|
||||
end
|
||||
|
||||
# Recursive function that returns an array of {Formula} without
|
||||
# {Formula} dependents that weren't installed on request.
|
||||
# @private
|
||||
def self.unused_formulae_with_no_formula_dependents(formulae)
|
||||
unused_formulae = formulae_with_no_formula_dependents(formulae).reject do |f|
|
||||
Tab.for_keg(f.any_installed_keg).installed_on_request
|
||||
end
|
||||
|
||||
if unused_formulae.present?
|
||||
unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae)
|
||||
end
|
||||
|
||||
unused_formulae
|
||||
end
|
||||
|
||||
# An array of {Formula} without {Formula} or {Cask}
|
||||
# dependents that weren't installed on request.
|
||||
# @private
|
||||
def self.unused_formulae_with_no_dependents(formulae, casks)
|
||||
unused_formulae = unused_formulae_with_no_formula_dependents(formulae)
|
||||
unused_formulae - formulae_with_cask_dependents(casks)
|
||||
end
|
||||
|
||||
def self.installed_with_alias_path(alias_path)
|
||||
return [] if alias_path.nil?
|
||||
|
||||
@ -2003,7 +2012,7 @@ class Formula
|
||||
# `any_installed_keg` and `runtime_dependencies` `select`s ensure
|
||||
# that we don't end up with something `Formula#runtime_dependencies` can't
|
||||
# read from a `Tab`.
|
||||
Formula.cache[:runtime_installed_formula_dependents] = {}
|
||||
Formula.cache[:runtime_installed_formula_dependents] ||= {}
|
||||
Formula.cache[:runtime_installed_formula_dependents][full_name] ||= Formula.installed
|
||||
.select(&:any_installed_keg)
|
||||
.select(&:runtime_dependencies)
|
||||
@ -2061,6 +2070,10 @@ class Formula
|
||||
"dependencies" => dependencies.reject(&:optional?)
|
||||
.reject(&:recommended?)
|
||||
.reject(&:build?)
|
||||
.reject(&:test?)
|
||||
.map(&:name)
|
||||
.uniq,
|
||||
"test_dependencies" => dependencies.select(&:test?)
|
||||
.map(&:name)
|
||||
.uniq,
|
||||
"recommended_dependencies" => dependencies.select(&:recommended?)
|
||||
@ -2654,9 +2667,26 @@ class Formula
|
||||
# The methods below define the formula DSL.
|
||||
class << self
|
||||
extend Predicable
|
||||
extend T::Sig
|
||||
include BuildEnvironment::DSL
|
||||
include OnSystem::MacOSAndLinux
|
||||
|
||||
# Initialise instance variables for each subclass. These need to be initialised before the class is frozen,
|
||||
# and some DSL may never be called so it can't be done lazily.
|
||||
def inherited(child)
|
||||
super
|
||||
child.instance_eval do
|
||||
# Ensure this is synced with `freeze`
|
||||
@stable = SoftwareSpec.new(flags: build_flags)
|
||||
@head = HeadSoftwareSpec.new(flags: build_flags)
|
||||
@livecheck = Livecheck.new(self)
|
||||
@conflicts = []
|
||||
@skip_clean_paths = Set.new
|
||||
@link_overwrite_paths = Set.new
|
||||
@allowed_missing_libraries = Set.new
|
||||
end
|
||||
end
|
||||
|
||||
def method_added(method)
|
||||
super
|
||||
|
||||
@ -2668,6 +2698,20 @@ class Formula
|
||||
end
|
||||
end
|
||||
|
||||
def freeze
|
||||
specs.each(&:freeze)
|
||||
@livecheck.freeze
|
||||
@conflicts.freeze
|
||||
@skip_clean_paths.freeze
|
||||
@link_overwrite_paths.freeze
|
||||
@allowed_missing_libraries.freeze
|
||||
super
|
||||
end
|
||||
|
||||
# Whether this formula was loaded using the formulae.brew.sh API
|
||||
# @private
|
||||
attr_accessor :loaded_from_api
|
||||
|
||||
# Whether this formula contains OS/arch-specific blocks
|
||||
# (e.g. `on_macos`, `on_arm`, `on_monterey :or_older`, `on_system :linux, macos: :big_sur_or_newer`).
|
||||
# @private
|
||||
@ -2748,6 +2792,18 @@ class Formula
|
||||
# @private
|
||||
attr_reader :plist_manual
|
||||
|
||||
# @private
|
||||
attr_reader :conflicts
|
||||
|
||||
# @private
|
||||
attr_reader :skip_clean_paths
|
||||
|
||||
# @private
|
||||
attr_reader :link_overwrite_paths
|
||||
|
||||
# @private
|
||||
attr_reader :allowed_missing_libraries
|
||||
|
||||
# If `pour_bottle?` returns `false` the user-visible reason to display for
|
||||
# why they cannot use the bottle.
|
||||
# @private
|
||||
@ -2778,7 +2834,7 @@ class Formula
|
||||
# A list of the {.stable} and {.head} {SoftwareSpec}s.
|
||||
# @private
|
||||
def specs
|
||||
@specs ||= [stable, head].freeze
|
||||
[stable, head].freeze
|
||||
end
|
||||
|
||||
# @!attribute [w] url
|
||||
@ -2862,8 +2918,9 @@ class Formula
|
||||
#
|
||||
# Formulae which should not be bottled should be tagged with:
|
||||
# <pre>bottle :disable, "reasons"</pre>
|
||||
def bottle(*args, &block)
|
||||
stable.bottle(*args, &block)
|
||||
sig { params(block: T.proc.bind(BottleSpecification).void).void }
|
||||
def bottle(&block)
|
||||
stable.bottle(&block)
|
||||
end
|
||||
|
||||
# @private
|
||||
@ -2894,7 +2951,6 @@ class Formula
|
||||
# depends_on "libffi"
|
||||
# end</pre>
|
||||
def stable(&block)
|
||||
@stable ||= SoftwareSpec.new(flags: build_flags)
|
||||
return @stable unless block
|
||||
|
||||
@stable.instance_eval(&block)
|
||||
@ -2913,7 +2969,6 @@ class Formula
|
||||
# or (if autodetect fails):
|
||||
# <pre>head "https://hg.is.awesome.but.git.has.won.example.com/", using: :hg</pre>
|
||||
def head(val = nil, specs = {}, &block)
|
||||
@head ||= HeadSoftwareSpec.new(flags: build_flags)
|
||||
if block
|
||||
@head.instance_eval(&block)
|
||||
elsif val
|
||||
@ -3064,11 +3119,6 @@ class Formula
|
||||
@plist_manual = options[:manual]
|
||||
end
|
||||
|
||||
# @private
|
||||
def conflicts
|
||||
@conflicts ||= []
|
||||
end
|
||||
|
||||
# One or more formulae that conflict with this one and why.
|
||||
# <pre>conflicts_with "imagemagick", because: "both install `convert` binaries"</pre>
|
||||
def conflicts_with(*names)
|
||||
@ -3089,11 +3139,6 @@ class Formula
|
||||
skip_clean_paths.merge(paths)
|
||||
end
|
||||
|
||||
# @private
|
||||
def skip_clean_paths
|
||||
@skip_clean_paths ||= Set.new
|
||||
end
|
||||
|
||||
# Software that will not be symlinked into the `brew --prefix` and will
|
||||
# only live in its Cellar. Other formulae can depend on it and Homebrew
|
||||
# will add the necessary includes, libraries, and other paths while
|
||||
@ -3197,7 +3242,6 @@ class Formula
|
||||
# regex /foo-(\d+(?:\.\d+)+)\.tar/
|
||||
# end</pre>
|
||||
def livecheck(&block)
|
||||
@livecheck ||= Livecheck.new(self)
|
||||
return @livecheck unless block
|
||||
|
||||
@livecheckable = true
|
||||
@ -3353,11 +3397,6 @@ class Formula
|
||||
link_overwrite_paths.merge(paths)
|
||||
end
|
||||
|
||||
# @private
|
||||
def link_overwrite_paths
|
||||
@link_overwrite_paths ||= Set.new
|
||||
end
|
||||
|
||||
# Permit links to certain libraries that don't exist. Available on Linux only.
|
||||
def ignore_missing_libraries(*libs)
|
||||
unless Homebrew::SimulateSystem.simulating_or_running_on_linux?
|
||||
@ -3371,11 +3410,6 @@ class Formula
|
||||
|
||||
allowed_missing_libraries.merge(libraries)
|
||||
end
|
||||
|
||||
# @private
|
||||
def allowed_missing_libraries
|
||||
@allowed_missing_libraries ||= Set.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -52,14 +52,16 @@ module Homebrew
|
||||
|
||||
def audit_file
|
||||
if formula.core_formula? && @versioned_formula
|
||||
unversioned_name = formula.name.gsub(/@.*$/, "")
|
||||
|
||||
# ignore when an unversioned formula doesn't exist after an explicit rename
|
||||
return if formula.tap.formula_renames.key?(unversioned_name)
|
||||
|
||||
# build this ourselves as we want e.g. homebrew/core to be present
|
||||
full_name = "#{formula.tap}/#{unversioned_name}"
|
||||
|
||||
unversioned_formula = begin
|
||||
# build this ourselves as we want e.g. homebrew/core to be present
|
||||
full_name = if formula.tap
|
||||
"#{formula.tap}/#{formula.name}"
|
||||
else
|
||||
formula.name
|
||||
end
|
||||
Formulary.factory(full_name.gsub(/@.*$/, "")).path
|
||||
Formulary.factory(full_name).path
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError,
|
||||
TapFormulaWithOldnameAmbiguityError
|
||||
Pathname.new formula.path.to_s.gsub(/@.*\.rb$/, ".rb")
|
||||
@ -194,6 +196,13 @@ module Homebrew
|
||||
if formula.license.present?
|
||||
licenses, exceptions = SPDX.parse_license_expression formula.license
|
||||
|
||||
sspl_licensed = licenses.any? { |license| license.to_s.start_with?("SSPL") }
|
||||
if sspl_licensed && @core_tap
|
||||
problem <<~EOS
|
||||
Formula #{formula.name} is SSPL-licensed. Software under the SSPL must not be packaged in homebrew/core.
|
||||
EOS
|
||||
end
|
||||
|
||||
non_standard_licenses = licenses.reject { |license| SPDX.valid_license? license }
|
||||
if non_standard_licenses.present?
|
||||
problem <<~EOS
|
||||
@ -344,6 +353,10 @@ module Homebrew
|
||||
recursive_runtime_formulae.each do |f|
|
||||
name = f.name
|
||||
unversioned_name, = name.split("@")
|
||||
# Allow use of the full versioned name (e.g. `python@3.99`) or an unversioned alias (`python`).
|
||||
next if formula.tap&.audit_exception :versioned_formula_dependent_conflicts_allowlist, name
|
||||
next if formula.tap&.audit_exception :versioned_formula_dependent_conflicts_allowlist, unversioned_name
|
||||
|
||||
version_hash[unversioned_name] ||= Set.new
|
||||
version_hash[unversioned_name] << name
|
||||
next if version_hash[unversioned_name].length < 2
|
||||
@ -401,6 +414,21 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def audit_gcc_dependency
|
||||
return unless @core_tap
|
||||
return if !@strict && !(@git && formula.tap.git?) # git log is required for non-strict audit
|
||||
return unless Homebrew::SimulateSystem.simulating_or_running_on_linux?
|
||||
return unless linux_only_gcc_dep?(formula)
|
||||
|
||||
bad_gcc_dep = @strict || begin
|
||||
fv = FormulaVersions.new(formula)
|
||||
fv.formula_at_revision("origin/HEAD") { |prev_f| !linux_only_gcc_dep?(prev_f) }
|
||||
end
|
||||
return unless bad_gcc_dep
|
||||
|
||||
problem "Formulae in homebrew/core should not have a Linux-only dependency on GCC."
|
||||
end
|
||||
|
||||
def audit_postgresql
|
||||
return unless formula.name == "postgresql"
|
||||
return unless @core_tap
|
||||
@ -419,11 +447,12 @@ module Homebrew
|
||||
def audit_glibc
|
||||
return unless @core_tap
|
||||
return if formula.name != "glibc"
|
||||
return if [OS::CI_GLIBC_VERSION, "2.27", "2.31", "2.35"].include?(formula.version.to_s)
|
||||
# Also allow LINUX_GLIBC_NEXT_CI_VERSION for when we're upgrading.
|
||||
return if [OS::LINUX_GLIBC_CI_VERSION, OS::LINUX_GLIBC_NEXT_CI_VERSION].include?(formula.version.to_s)
|
||||
|
||||
problem "The glibc version must be #{OS::CI_GLIBC_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."
|
||||
problem "The glibc version must be #{OS::LINUX_GLIBC_CI_VERSION}, as needed by our CI on Linux. " \
|
||||
"The glibc formula 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
|
||||
|
||||
ELASTICSEARCH_KIBANA_RELICENSED_VERSION = "7.11"
|
||||
@ -462,17 +491,14 @@ module Homebrew
|
||||
|
||||
return unless DevelopmentTools.curl_handles_most_https_certificates?
|
||||
|
||||
use_homebrew_curl = false
|
||||
%w[Stable HEAD].each do |name|
|
||||
spec_name = name.downcase.to_sym
|
||||
next unless (spec = formula.send(spec_name))
|
||||
use_homebrew_curl = [:stable, :head].any? do |spec_name|
|
||||
next false unless (spec = formula.send(spec_name))
|
||||
|
||||
use_homebrew_curl = spec.using == :homebrew_curl
|
||||
break if use_homebrew_curl
|
||||
spec.using == :homebrew_curl
|
||||
end
|
||||
|
||||
if (http_content_problem = curl_check_http_content(homepage,
|
||||
"homepage URL",
|
||||
SharedAudits::URL_TYPE_HOMEPAGE,
|
||||
user_agents: [:browser, :default],
|
||||
check_content: true,
|
||||
strict: @strict,
|
||||
@ -848,5 +874,33 @@ module Homebrew
|
||||
def head_only?(formula)
|
||||
formula.head && formula.stable.nil?
|
||||
end
|
||||
|
||||
def linux_only_gcc_dep?(formula)
|
||||
odie "`#linux_only_gcc_dep?` works only on Linux!" if Homebrew::SimulateSystem.simulating_or_running_on_macos?
|
||||
return false if formula.deps.map(&:name).exclude?("gcc")
|
||||
|
||||
variations = formula.to_hash_with_variations["variations"]
|
||||
# The formula has no variations, so all OS-version-arch triples depend on GCC.
|
||||
return false if variations.blank?
|
||||
|
||||
MacOSVersions::SYMBOLS.each_key do |macos_version|
|
||||
[:arm, :intel].each do |arch|
|
||||
bottle_tag = Utils::Bottles::Tag.new(system: macos_version, arch: arch)
|
||||
next unless bottle_tag.valid_combination?
|
||||
|
||||
variation_dependencies = variations.dig(bottle_tag.to_sym, "dependencies")
|
||||
# This variation either:
|
||||
# 1. does not exist
|
||||
# 2. has no variation-specific dependencies
|
||||
# In either case, it matches Linux. We must check for `nil` because an empty
|
||||
# array indicates that this variation does not depend on GCC.
|
||||
return false if variation_dependencies.nil?
|
||||
# We found a non-Linux variation that depends on GCC.
|
||||
return false if variation_dependencies.include?("gcc")
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -581,11 +581,9 @@ class FormulaInstaller
|
||||
end
|
||||
|
||||
def expand_dependencies_for_formula(formula, inherited_options)
|
||||
any_bottle_used = false
|
||||
|
||||
# Cache for this expansion only. FormulaInstaller has a lot of inputs which can alter expansion.
|
||||
cache_key = "FormulaInstaller-#{formula.full_name}-#{Time.now.to_f}"
|
||||
expanded_deps = Dependency.expand(formula, cache_key: cache_key) do |dependent, dep|
|
||||
Dependency.expand(formula, cache_key: cache_key) do |dependent, dep|
|
||||
inherited_options[dep.name] |= inherited_options_for(dep)
|
||||
build = effective_build_options_for(
|
||||
dependent,
|
||||
@ -601,36 +599,14 @@ class FormulaInstaller
|
||||
Dependency.prune
|
||||
elsif dep.satisfied?(inherited_options[dep.name])
|
||||
Dependency.skip
|
||||
else
|
||||
any_bottle_used ||= install_bottle_for?(dep.to_formula, build)
|
||||
end
|
||||
end
|
||||
|
||||
[expanded_deps, any_bottle_used]
|
||||
end
|
||||
|
||||
def expand_dependencies
|
||||
inherited_options = Hash.new { |hash, key| hash[key] = Options.new }
|
||||
any_bottle_used = pour_bottle?
|
||||
|
||||
expanded_deps, any_dep_bottle_used = expand_dependencies_for_formula(formula, inherited_options)
|
||||
any_bottle_used ||= any_dep_bottle_used
|
||||
|
||||
# We require some dependencies (glibc, GCC 5, etc.) if binaries were built.
|
||||
# Native binaries shouldn't exist in cross-platform `all` bottles.
|
||||
if any_bottle_used && !formula.bottled?(:all) && !Keg.bottle_dependencies.empty?
|
||||
all_bottle_deps = Keg.bottle_dependencies.flat_map do |bottle_dep|
|
||||
bottle_dep.recursive_dependencies.map(&:name) + [bottle_dep.name]
|
||||
end
|
||||
|
||||
if all_bottle_deps.exclude?(formula.name)
|
||||
bottle_deps = Keg.bottle_dependencies.flat_map do |bottle_dep|
|
||||
expanded_bottle_deps, = expand_dependencies_for_formula(bottle_dep, inherited_options)
|
||||
expanded_bottle_deps
|
||||
end
|
||||
expanded_deps = Dependency.merge_repeats(bottle_deps + expanded_deps)
|
||||
end
|
||||
end
|
||||
expanded_deps = expand_dependencies_for_formula(formula, inherited_options)
|
||||
|
||||
expanded_deps.map { |dep| [dep, inherited_options[dep.name]] }
|
||||
end
|
||||
@ -799,6 +775,8 @@ class FormulaInstaller
|
||||
|
||||
fix_dynamic_linkage(keg) if !@poured_bottle || !formula.bottle_specification.skip_relocation?
|
||||
|
||||
Homebrew::Install.global_post_install
|
||||
|
||||
if build_bottle?
|
||||
ohai "Not running 'post_install' as we're building a bottle"
|
||||
puts "You can run it manually using:"
|
||||
@ -1187,6 +1165,8 @@ class FormulaInstaller
|
||||
|
||||
if pour_bottle?(output_warning: true)
|
||||
formula.fetch_bottle_tab
|
||||
elsif formula.core_formula? && Homebrew::EnvConfig.install_from_api?
|
||||
odie "Unable to build #{formula.name} from source with HOMEBREW_INSTALL_FROM_API."
|
||||
else
|
||||
formula.fetch_patches
|
||||
formula.resources.each(&:fetch)
|
||||
@ -1222,6 +1202,7 @@ class FormulaInstaller
|
||||
tab.unused_options = []
|
||||
tab.built_as_bottle = true
|
||||
tab.poured_from_bottle = true
|
||||
tab.loaded_from_api = formula.class.loaded_from_api
|
||||
tab.installed_as_dependency = installed_as_dependency?
|
||||
tab.installed_on_request = installed_on_request?
|
||||
tab.time = Time.now.to_i
|
||||
|
||||
@ -194,7 +194,7 @@ module Formulary
|
||||
depends_on dep
|
||||
end
|
||||
|
||||
[:build, :recommended, :optional].each do |type|
|
||||
[:build, :test, :recommended, :optional].each do |type|
|
||||
json_formula["#{type}_dependencies"].each do |dep|
|
||||
next if uses_from_macos_names.include? dep
|
||||
|
||||
@ -217,6 +217,7 @@ module Formulary
|
||||
end
|
||||
end
|
||||
|
||||
klass.loaded_from_api = true
|
||||
mod.const_set(class_s, klass)
|
||||
|
||||
cache[:api] ||= {}
|
||||
@ -529,6 +530,14 @@ module Formulary
|
||||
end
|
||||
end
|
||||
|
||||
# Load aliases from the API.
|
||||
class AliasAPILoader < FormulaAPILoader
|
||||
def initialize(alias_name)
|
||||
super Homebrew::API::Formula.all_aliases[alias_name]
|
||||
@alias_path = Formulary.core_alias_path(alias_name).to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Return a {Formula} instance for the given reference.
|
||||
# `ref` is a string containing:
|
||||
#
|
||||
@ -655,22 +664,29 @@ module Formulary
|
||||
if ref.start_with?("homebrew/core/") && Homebrew::EnvConfig.install_from_api?
|
||||
name = ref.split("/", 3).last
|
||||
return FormulaAPILoader.new(name) if Homebrew::API::Formula.all_formulae.key?(name)
|
||||
return AliasAPILoader.new(name) if Homebrew::API::Formula.all_aliases.key?(name)
|
||||
end
|
||||
|
||||
return TapLoader.new(ref, from: from)
|
||||
end
|
||||
|
||||
return FromPathLoader.new(ref) if File.extname(ref) == ".rb" && Pathname.new(ref).expand_path.exist?
|
||||
pathname_ref = Pathname.new(ref)
|
||||
return FromPathLoader.new(ref) if File.extname(ref) == ".rb" && pathname_ref.expand_path.exist?
|
||||
|
||||
if Homebrew::EnvConfig.install_from_api? && Homebrew::API::Formula.all_formulae.key?(ref)
|
||||
return FormulaAPILoader.new(ref)
|
||||
if Homebrew::EnvConfig.install_from_api?
|
||||
return FormulaAPILoader.new(ref) if Homebrew::API::Formula.all_formulae.key?(ref)
|
||||
return AliasAPILoader.new(ref) if Homebrew::API::Formula.all_aliases.key?(ref)
|
||||
end
|
||||
|
||||
formula_with_that_name = core_path(ref)
|
||||
return FormulaLoader.new(ref, formula_with_that_name) if formula_with_that_name.file?
|
||||
|
||||
possible_alias = CoreTap.instance.alias_dir/ref
|
||||
return AliasLoader.new(possible_alias) if possible_alias.file?
|
||||
possible_alias = if pathname_ref.absolute?
|
||||
pathname_ref
|
||||
else
|
||||
core_alias_path(ref)
|
||||
end
|
||||
return AliasLoader.new(possible_alias) if possible_alias.symlink?
|
||||
|
||||
possible_tap_formulae = tap_paths(ref)
|
||||
raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae) if possible_tap_formulae.size > 1
|
||||
@ -705,6 +721,10 @@ module Formulary
|
||||
CoreTap.instance.formula_dir/"#{name.to_s.downcase}.rb"
|
||||
end
|
||||
|
||||
def self.core_alias_path(name)
|
||||
CoreTap.instance.alias_dir/name.to_s.downcase
|
||||
end
|
||||
|
||||
def self.tap_paths(name, taps = Dir[HOMEBREW_LIBRARY/"Taps/*/*/"])
|
||||
name = name.to_s.downcase
|
||||
taps.map do |tap|
|
||||
|
||||
@ -333,9 +333,9 @@ class GitHubPackages
|
||||
os_version ||= "macOS #{bottle_tag.to_macos_version}"
|
||||
when "linux"
|
||||
os_version&.delete_suffix!(" LTS")
|
||||
os_version ||= OS::CI_OS_VERSION
|
||||
os_version ||= OS::LINUX_CI_OS_VERSION
|
||||
glibc_version = tab["built_on"]["glibc_version"].presence if tab["built_on"].present?
|
||||
glibc_version ||= OS::CI_GLIBC_VERSION
|
||||
glibc_version ||= OS::LINUX_GLIBC_CI_VERSION
|
||||
cpu_variant = tab["oldest_cpu_family"] || Hardware::CPU::INTEL_64BIT_OLDEST_CPU.to_s
|
||||
end
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@ HOMEBREW_REQUIRED_RUBY_VERSION = ENV.fetch("HOMEBREW_REQUIRED_RUBY_VERSION").fre
|
||||
HOMEBREW_PRODUCT = ENV.fetch("HOMEBREW_PRODUCT").freeze
|
||||
HOMEBREW_VERSION = ENV.fetch("HOMEBREW_VERSION").freeze
|
||||
HOMEBREW_WWW = "https://brew.sh"
|
||||
HOMEBREW_DOCS_WWW = "https://docs.brew.sh"
|
||||
HOMEBREW_SYSTEM = ENV.fetch("HOMEBREW_SYSTEM").freeze
|
||||
HOMEBREW_PROCESSOR = ENV.fetch("HOMEBREW_PROCESSOR").freeze
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ module Hardware
|
||||
INTEL_64BIT_ARCHS = [:x86_64].freeze
|
||||
PPC_32BIT_ARCHS = [:ppc, :ppc32, :ppc7400, :ppc7450, :ppc970].freeze
|
||||
PPC_64BIT_ARCHS = [:ppc64, :ppc64le, :ppc970].freeze
|
||||
ARM_64BIT_ARCHS = [:arm64].freeze
|
||||
ARM_64BIT_ARCHS = [:arm64, :aarch64].freeze
|
||||
ALL_ARCHS = [
|
||||
*INTEL_32BIT_ARCHS,
|
||||
*INTEL_64BIT_ARCHS,
|
||||
|
||||
@ -30,6 +30,10 @@ module Homebrew
|
||||
Diagnostic.checks(:build_from_source_checks, fatal: all_fatal)
|
||||
end
|
||||
|
||||
def global_post_install; end
|
||||
alias generic_global_post_install global_post_install
|
||||
module_function :generic_global_post_install
|
||||
|
||||
def check_prefix
|
||||
if (Hardware::CPU.intel? || Hardware::CPU.in_rosetta2?) &&
|
||||
HOMEBREW_PREFIX.to_s == HOMEBREW_MACOS_ARM_DEFAULT_PREFIX
|
||||
|
||||
@ -366,17 +366,6 @@ class Keg
|
||||
def self.file_linked_libraries(_file, _string)
|
||||
[]
|
||||
end
|
||||
|
||||
def self.bottle_dependencies
|
||||
return [] unless Homebrew::SimulateSystem.simulating_or_running_on_linux?
|
||||
|
||||
@bottle_dependencies ||= begin
|
||||
formulae = []
|
||||
gcc = Formulary.factory(CompilerSelector.preferred_gcc)
|
||||
formulae << gcc if DevelopmentTools.non_apple_gcc_version("gcc") < gcc.version.to_i
|
||||
formulae
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "extend/os/keg_relocate"
|
||||
|
||||
@ -80,11 +80,16 @@ class LinkageChecker
|
||||
alias generic_display_test_output display_test_output
|
||||
private :generic_display_test_output
|
||||
|
||||
sig { params(strict: T::Boolean).returns(T::Boolean) }
|
||||
def broken_library_linkage?(strict: false)
|
||||
issues = [@broken_deps, @unwanted_system_dylibs, @version_conflict_deps]
|
||||
issues += [@undeclared_deps, @files_missing_rpaths] if strict
|
||||
[issues, unexpected_broken_dylibs, unexpected_present_dylibs].flatten.any?(&:present?)
|
||||
sig { params(test: T::Boolean, strict: T::Boolean).returns(T::Boolean) }
|
||||
def broken_library_linkage?(test: false, strict: false)
|
||||
raise ArgumentError, "Strict linkage checking requires test mode to be enabled." if strict && !test
|
||||
|
||||
issues = [@broken_deps, unexpected_broken_dylibs]
|
||||
if test
|
||||
issues += [@unwanted_system_dylibs, @version_conflict_deps, unexpected_present_dylibs]
|
||||
issues += [@undeclared_deps, @files_missing_rpaths] if strict
|
||||
end
|
||||
issues.any?(&:present?)
|
||||
end
|
||||
alias generic_broken_library_linkage? broken_library_linkage?
|
||||
private :generic_broken_library_linkage?
|
||||
@ -306,9 +311,12 @@ class LinkageChecker
|
||||
def harmless_broken_link?(dylib)
|
||||
# libgcc_s_* is referenced by programs that use the Java Service Wrapper,
|
||||
# and is harmless on x86(_64) machines
|
||||
# dyld will fall back to Apple libc++ if LLVM's is not available.
|
||||
[
|
||||
"/usr/lib/libgcc_s_ppc64.1.dylib",
|
||||
"/opt/local/lib/libgcc/libgcc_s.1.dylib",
|
||||
# TODO: Report linkage with `/usr/lib/libc++.1.dylib` when this link is broken.
|
||||
"#{HOMEBREW_PREFIX}/opt/llvm/lib/libc++.1.dylib",
|
||||
].include?(dylib)
|
||||
end
|
||||
|
||||
|
||||
@ -126,11 +126,11 @@ module Homebrew
|
||||
if debug
|
||||
# Print the chain of references for debugging
|
||||
puts "Reference Chain:"
|
||||
puts formula_or_cask_name(first_formula_or_cask, full_name: full_name)
|
||||
puts package_or_resource_name(first_formula_or_cask, full_name: full_name)
|
||||
|
||||
references << referenced_formula_or_cask
|
||||
references.each do |ref_formula_or_cask|
|
||||
puts formula_or_cask_name(ref_formula_or_cask, full_name: full_name)
|
||||
puts package_or_resource_name(ref_formula_or_cask, full_name: full_name)
|
||||
end
|
||||
end
|
||||
|
||||
@ -157,11 +157,14 @@ module Homebrew
|
||||
|
||||
# Executes the livecheck logic for each formula/cask in the
|
||||
# `formulae_and_casks_to_check` array and prints the results.
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
sig {
|
||||
params(
|
||||
formulae_and_casks_to_check: T::Array[T.any(Formula, Cask::Cask)],
|
||||
full_name: T::Boolean,
|
||||
handle_name_conflict: T::Boolean,
|
||||
check_resources: T::Boolean,
|
||||
json: T::Boolean,
|
||||
newer_only: T::Boolean,
|
||||
debug: T::Boolean,
|
||||
@ -171,24 +174,25 @@ module Homebrew
|
||||
}
|
||||
def run_checks(
|
||||
formulae_and_casks_to_check,
|
||||
full_name: false, handle_name_conflict: false, json: false, newer_only: false,
|
||||
full_name: false, handle_name_conflict: false, check_resources: false, json: false, newer_only: false,
|
||||
debug: false, quiet: false, verbose: false
|
||||
)
|
||||
load_other_tap_strategies(formulae_and_casks_to_check)
|
||||
|
||||
ambiguous_casks = []
|
||||
if handle_name_conflict
|
||||
ambiguous_casks = formulae_and_casks_to_check.group_by { |item| formula_or_cask_name(item, full_name: true) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
.select { |item| item.is_a?(Cask::Cask) }
|
||||
ambiguous_casks = formulae_and_casks_to_check \
|
||||
.group_by { |item| package_or_resource_name(item, full_name: true) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
.select { |item| item.is_a?(Cask::Cask) }
|
||||
end
|
||||
|
||||
ambiguous_names = []
|
||||
unless full_name
|
||||
ambiguous_names =
|
||||
(formulae_and_casks_to_check - ambiguous_casks).group_by { |item| formula_or_cask_name(item) }
|
||||
(formulae_and_casks_to_check - ambiguous_casks).group_by { |item| package_or_resource_name(item) }
|
||||
.values
|
||||
.select { |items| items.length > 1 }
|
||||
.flatten
|
||||
@ -218,7 +222,7 @@ module Homebrew
|
||||
cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
|
||||
|
||||
use_full_name = full_name || ambiguous_names.include?(formula_or_cask)
|
||||
name = formula_or_cask_name(formula_or_cask, full_name: use_full_name)
|
||||
name = package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
|
||||
referenced_formula_or_cask, livecheck_references =
|
||||
resolve_livecheck_reference(formula_or_cask, full_name: use_full_name, debug: debug)
|
||||
@ -282,6 +286,30 @@ module Homebrew
|
||||
version_info[:latest] if version_info.present?
|
||||
end
|
||||
|
||||
check_for_resources = check_resources && formula_or_cask.is_a?(Formula) && formula_or_cask.resources.present?
|
||||
if check_for_resources
|
||||
resource_version_info = formula_or_cask.resources.map do |resource|
|
||||
res_skip_info ||= SkipConditions.skip_information(resource, verbose: verbose)
|
||||
if res_skip_info.present?
|
||||
res_skip_info
|
||||
else
|
||||
res_version_info = resource_version(
|
||||
resource,
|
||||
json: json,
|
||||
debug: debug,
|
||||
quiet: quiet,
|
||||
verbose: verbose,
|
||||
)
|
||||
if res_version_info.empty?
|
||||
status_hash(resource, "error", ["Unable to get versions"], verbose: verbose)
|
||||
else
|
||||
res_version_info
|
||||
end
|
||||
end
|
||||
end.compact_blank
|
||||
Homebrew.failed = true if resource_version_info.any? { |info| info[:status] == "error" }
|
||||
end
|
||||
|
||||
if latest.blank?
|
||||
no_versions_msg = "Unable to get versions"
|
||||
raise Livecheck::Error, no_versions_msg unless json
|
||||
@ -289,7 +317,14 @@ module Homebrew
|
||||
|
||||
next version_info if version_info.is_a?(Hash) && version_info[:status] && version_info[:messages]
|
||||
|
||||
next status_hash(formula_or_cask, "error", [no_versions_msg], full_name: use_full_name, verbose: verbose)
|
||||
latest_info = status_hash(formula_or_cask, "error", [no_versions_msg], full_name: use_full_name,
|
||||
verbose: verbose)
|
||||
if check_for_resources
|
||||
resource_version_info.map! { |r| r.except!(:meta) } unless verbose
|
||||
latest_info[:resources] = resource_version_info
|
||||
end
|
||||
|
||||
next latest_info
|
||||
end
|
||||
|
||||
if (m = latest.to_s.match(/(.*)-release$/)) && !current.to_s.match(/.*-release$/)
|
||||
@ -324,6 +359,8 @@ module Homebrew
|
||||
info[:meta][:head_only] = true if formula&.head_only?
|
||||
info[:meta].merge!(version_info[:meta]) if version_info.present? && version_info.key?(:meta)
|
||||
|
||||
info[:resources] = resource_version_info if check_for_resources
|
||||
|
||||
next if newer_only && !info[:version][:outdated]
|
||||
|
||||
has_a_newer_upstream_version ||= true
|
||||
@ -331,10 +368,12 @@ module Homebrew
|
||||
if json
|
||||
progress&.increment
|
||||
info.except!(:meta) unless verbose
|
||||
resource_version_info.map! { |r| r.except!(:meta) } if check_for_resources && !verbose
|
||||
next info
|
||||
end
|
||||
|
||||
puts if debug
|
||||
print_latest_version(info, verbose: verbose, ambiguous_cask: ambiguous_casks.include?(formula_or_cask))
|
||||
print_resources_info(resource_version_info, verbose: verbose) if check_for_resources
|
||||
nil
|
||||
rescue => e
|
||||
Homebrew.failed = true
|
||||
@ -344,11 +383,12 @@ module Homebrew
|
||||
progress&.increment
|
||||
status_hash(formula_or_cask, "error", [e.to_s], full_name: use_full_name, verbose: verbose) unless quiet
|
||||
elsif !quiet
|
||||
name = formula_or_cask_name(formula_or_cask, full_name: use_full_name)
|
||||
name = package_or_resource_name(formula_or_cask, full_name: use_full_name)
|
||||
name += " (cask)" if ambiguous_casks.include?(formula_or_cask)
|
||||
|
||||
onoe "#{Tty.blue}#{name}#{Tty.reset}: #{e}"
|
||||
$stderr.puts e.backtrace if debug && !e.is_a?(Livecheck::Error)
|
||||
print_resources_info(resource_version_info, verbose: verbose) if check_for_resources
|
||||
nil
|
||||
end
|
||||
end
|
||||
@ -367,16 +407,20 @@ module Homebrew
|
||||
|
||||
puts JSON.pretty_generate(formulae_checked.compact)
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), full_name: T::Boolean).returns(String) }
|
||||
def formula_or_cask_name(formula_or_cask, full_name: false)
|
||||
case formula_or_cask
|
||||
sig { params(package_or_resource: T.any(Formula, Cask::Cask, Resource), full_name: T::Boolean).returns(String) }
|
||||
def package_or_resource_name(package_or_resource, full_name: false)
|
||||
case package_or_resource
|
||||
when Formula
|
||||
formula_name(formula_or_cask, full_name: full_name)
|
||||
formula_name(package_or_resource, full_name: full_name)
|
||||
when Cask::Cask
|
||||
cask_name(formula_or_cask, full_name: full_name)
|
||||
cask_name(package_or_resource, full_name: full_name)
|
||||
when Resource
|
||||
package_or_resource.name
|
||||
else
|
||||
T.absurd(formula_or_cask)
|
||||
T.absurd(package_or_resource)
|
||||
end
|
||||
end
|
||||
|
||||
@ -396,40 +440,44 @@ module Homebrew
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
status_str: String,
|
||||
messages: T.nilable(T::Array[String]),
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
status_str: String,
|
||||
messages: T.nilable(T::Array[String]),
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def status_hash(formula_or_cask, status_str, messages = nil, full_name: false, verbose: false)
|
||||
formula = formula_or_cask if formula_or_cask.is_a?(Formula)
|
||||
cask = formula_or_cask if formula_or_cask.is_a?(Cask::Cask)
|
||||
def status_hash(package_or_resource, status_str, messages = nil, full_name: false, verbose: false)
|
||||
formula = package_or_resource if package_or_resource.is_a?(Formula)
|
||||
cask = package_or_resource if package_or_resource.is_a?(Cask::Cask)
|
||||
resource = package_or_resource if package_or_resource.is_a?(Resource)
|
||||
|
||||
status_hash = {}
|
||||
if formula
|
||||
status_hash[:formula] = formula_name(formula, full_name: full_name)
|
||||
elsif cask
|
||||
status_hash[:cask] = cask_name(formula_or_cask, full_name: full_name)
|
||||
status_hash[:cask] = cask_name(cask, full_name: full_name)
|
||||
elsif resource
|
||||
status_hash[:resource] = resource.name
|
||||
end
|
||||
status_hash[:status] = status_str
|
||||
status_hash[:messages] = messages if messages.is_a?(Array)
|
||||
|
||||
status_hash[:meta] = {
|
||||
livecheckable: formula_or_cask.livecheckable?,
|
||||
livecheckable: package_or_resource.livecheckable?,
|
||||
}
|
||||
status_hash[:meta][:head_only] = true if formula&.head_only?
|
||||
|
||||
status_hash
|
||||
end
|
||||
|
||||
# Formats and prints the livecheck result for a formula.
|
||||
# Formats and prints the livecheck result for a formula/cask/resource.
|
||||
sig { params(info: Hash, verbose: T::Boolean, ambiguous_cask: T::Boolean).void }
|
||||
def print_latest_version(info, verbose:, ambiguous_cask: false)
|
||||
formula_or_cask_s = "#{Tty.blue}#{info[:formula] || info[:cask]}#{Tty.reset}"
|
||||
formula_or_cask_s += " (cask)" if ambiguous_cask
|
||||
formula_or_cask_s += " (guessed)" if !info[:meta][:livecheckable] && verbose
|
||||
def print_latest_version(info, verbose: false, ambiguous_cask: false)
|
||||
package_or_resource_s = info[:resource].present? ? " " : ""
|
||||
package_or_resource_s += "#{Tty.blue}#{info[:formula] || info[:cask] || info[:resource]}#{Tty.reset}"
|
||||
package_or_resource_s += " (cask)" if ambiguous_cask
|
||||
package_or_resource_s += " (guessed)" if verbose && !info[:meta][:livecheckable]
|
||||
|
||||
current_s = if info[:version][:newer_than_upstream]
|
||||
"#{Tty.red}#{info[:version][:current]}#{Tty.reset}"
|
||||
@ -443,47 +491,61 @@ module Homebrew
|
||||
info[:version][:latest]
|
||||
end
|
||||
|
||||
puts "#{formula_or_cask_s}: #{current_s} ==> #{latest_s}"
|
||||
puts "#{package_or_resource_s}: #{current_s} ==> #{latest_s}"
|
||||
end
|
||||
|
||||
# Prints the livecheck result for the resources of a given Formula.
|
||||
sig { params(info: T::Array[Hash], verbose: T::Boolean).void }
|
||||
def print_resources_info(info, verbose: false)
|
||||
info.each do |r_info|
|
||||
if r_info[:status] && r_info[:messages]
|
||||
SkipConditions.print_skip_information(r_info)
|
||||
else
|
||||
print_latest_version(r_info, verbose: verbose)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
livecheck_url: T.any(String, Symbol),
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
livecheck_url: T.any(String, Symbol),
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
).returns(T.nilable(String))
|
||||
}
|
||||
def livecheck_url_to_string(livecheck_url, formula_or_cask)
|
||||
def livecheck_url_to_string(livecheck_url, package_or_resource)
|
||||
case livecheck_url
|
||||
when String
|
||||
livecheck_url
|
||||
when :url
|
||||
formula_or_cask.url&.to_s if formula_or_cask.is_a?(Cask::Cask)
|
||||
package_or_resource.url&.to_s if package_or_resource.is_a?(Cask::Cask) || package_or_resource.is_a?(Resource)
|
||||
when :head, :stable
|
||||
formula_or_cask.send(livecheck_url)&.url if formula_or_cask.is_a?(Formula)
|
||||
package_or_resource.send(livecheck_url)&.url if package_or_resource.is_a?(Formula)
|
||||
when :homepage
|
||||
formula_or_cask.homepage
|
||||
package_or_resource.homepage unless package_or_resource.is_a?(Resource)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an Array containing the formula/cask URLs that can be used by livecheck.
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T::Array[String]) }
|
||||
def checkable_urls(formula_or_cask)
|
||||
# Returns an Array containing the formula/cask/resource URLs that can be used by livecheck.
|
||||
sig { params(package_or_resource: T.any(Formula, Cask::Cask, Resource)).returns(T::Array[String]) }
|
||||
def checkable_urls(package_or_resource)
|
||||
urls = []
|
||||
|
||||
case formula_or_cask
|
||||
case package_or_resource
|
||||
when Formula
|
||||
if formula_or_cask.stable
|
||||
urls << formula_or_cask.stable.url
|
||||
urls.concat(formula_or_cask.stable.mirrors)
|
||||
if package_or_resource.stable
|
||||
urls << package_or_resource.stable.url
|
||||
urls.concat(package_or_resource.stable.mirrors)
|
||||
end
|
||||
urls << formula_or_cask.head.url if formula_or_cask.head
|
||||
urls << formula_or_cask.homepage if formula_or_cask.homepage
|
||||
urls << package_or_resource.head.url if package_or_resource.head
|
||||
urls << package_or_resource.homepage if package_or_resource.homepage
|
||||
when Cask::Cask
|
||||
urls << formula_or_cask.appcast.to_s if formula_or_cask.appcast
|
||||
urls << formula_or_cask.url.to_s if formula_or_cask.url
|
||||
urls << formula_or_cask.homepage if formula_or_cask.homepage
|
||||
urls << package_or_resource.appcast.to_s if package_or_resource.appcast
|
||||
urls << package_or_resource.url.to_s if package_or_resource.url
|
||||
urls << package_or_resource.homepage if package_or_resource.homepage
|
||||
when Resource
|
||||
urls << package_or_resource.url
|
||||
else
|
||||
T.absurd(formula_or_cask)
|
||||
T.absurd(package_or_resource)
|
||||
end
|
||||
|
||||
urls.compact.uniq
|
||||
@ -561,7 +623,7 @@ module Homebrew
|
||||
homebrew_curl_root_domains.include?(url_root_domain)
|
||||
end
|
||||
|
||||
# Identifies the latest version of the formula and returns a Hash containing
|
||||
# Identifies the latest version of the formula/cask and returns a Hash containing
|
||||
# the version information. Returns nil if a latest version couldn't be found.
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
sig {
|
||||
@ -713,7 +775,6 @@ module Homebrew
|
||||
version.to_s.include?(rejection)
|
||||
end
|
||||
end
|
||||
|
||||
next if match_version_map.blank?
|
||||
|
||||
if debug
|
||||
@ -770,6 +831,195 @@ module Homebrew
|
||||
nil
|
||||
end
|
||||
# rubocop:enable Metrics/CyclomaticComplexity
|
||||
|
||||
# Identifies the latest version of a resource and returns a Hash containing the
|
||||
# version information. Returns nil if a latest version couldn't be found.
|
||||
sig {
|
||||
params(
|
||||
resource: Resource,
|
||||
json: T::Boolean,
|
||||
debug: T::Boolean,
|
||||
quiet: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def resource_version(
|
||||
resource,
|
||||
json: false,
|
||||
debug: false,
|
||||
quiet: false,
|
||||
verbose: false
|
||||
)
|
||||
has_livecheckable = resource.livecheckable?
|
||||
|
||||
if debug
|
||||
puts "\n\n"
|
||||
puts "Resource: #{resource.name}"
|
||||
puts "Livecheckable?: #{has_livecheckable ? "Yes" : "No"}"
|
||||
end
|
||||
|
||||
resource_version_info = {}
|
||||
|
||||
livecheck = resource.livecheck
|
||||
livecheck_url = livecheck.url
|
||||
livecheck_regex = livecheck.regex
|
||||
livecheck_strategy = livecheck.strategy
|
||||
livecheck_strategy_block = livecheck.strategy_block
|
||||
|
||||
livecheck_url_string = livecheck_url_to_string(livecheck_url, resource)
|
||||
|
||||
urls = [livecheck_url_string] if livecheck_url_string
|
||||
urls ||= checkable_urls(resource)
|
||||
|
||||
checked_urls = []
|
||||
# rubocop:disable Metrics/BlockLength
|
||||
urls.each_with_index do |original_url, i|
|
||||
# Only preprocess the URL when it's appropriate
|
||||
url = if STRATEGY_SYMBOLS_TO_SKIP_PREPROCESS_URL.include?(livecheck_strategy)
|
||||
original_url
|
||||
else
|
||||
preprocess_url(original_url)
|
||||
end
|
||||
next if checked_urls.include?(url)
|
||||
|
||||
strategies = Strategy.from_url(
|
||||
url,
|
||||
livecheck_strategy: livecheck_strategy,
|
||||
url_provided: livecheck_url.present?,
|
||||
regex_provided: livecheck_regex.present?,
|
||||
block_provided: livecheck_strategy_block.present?,
|
||||
)
|
||||
strategy = Strategy.from_symbol(livecheck_strategy) || strategies.first
|
||||
strategy_name = livecheck_strategy_names[strategy]
|
||||
|
||||
if debug
|
||||
puts
|
||||
if livecheck_url.is_a?(Symbol)
|
||||
# This assumes the URL symbol will fit within the available space
|
||||
puts "URL (#{livecheck_url}):".ljust(18, " ") + original_url
|
||||
else
|
||||
puts "URL: #{original_url}"
|
||||
end
|
||||
puts "URL (processed): #{url}" if url != original_url
|
||||
if strategies.present? && verbose
|
||||
puts "Strategies: #{strategies.map { |s| livecheck_strategy_names[s] }.join(", ")}"
|
||||
end
|
||||
puts "Strategy: #{strategy.blank? ? "None" : strategy_name}"
|
||||
puts "Regex: #{livecheck_regex.inspect}" if livecheck_regex.present?
|
||||
end
|
||||
|
||||
if livecheck_strategy.present?
|
||||
if livecheck_url.blank? && strategy.method(:find_versions).parameters.include?([:keyreq, :url])
|
||||
odebug "#{strategy_name} strategy requires a URL"
|
||||
next
|
||||
elsif livecheck_strategy != :page_match && strategies.exclude?(strategy)
|
||||
odebug "#{strategy_name} strategy does not apply to this URL"
|
||||
next
|
||||
end
|
||||
end
|
||||
puts if debug && strategy.blank?
|
||||
next if strategy.blank?
|
||||
|
||||
strategy_data = strategy.find_versions(
|
||||
url: url,
|
||||
regex: livecheck_regex,
|
||||
homebrew_curl: false,
|
||||
&livecheck_strategy_block
|
||||
)
|
||||
match_version_map = strategy_data[:matches]
|
||||
regex = strategy_data[:regex]
|
||||
messages = strategy_data[:messages]
|
||||
checked_urls << url
|
||||
|
||||
if messages.is_a?(Array) && match_version_map.blank?
|
||||
puts messages unless json
|
||||
next if i + 1 < urls.length
|
||||
|
||||
return status_hash(resource, "error", messages, verbose: verbose)
|
||||
end
|
||||
|
||||
if debug
|
||||
if strategy_data[:url].present? && strategy_data[:url] != url
|
||||
puts "URL (strategy): #{strategy_data[:url]}"
|
||||
end
|
||||
puts "URL (final): #{strategy_data[:final_url]}" if strategy_data[:final_url].present?
|
||||
if strategy_data[:regex].present? && strategy_data[:regex] != livecheck_regex
|
||||
puts "Regex (strategy): #{strategy_data[:regex].inspect}"
|
||||
end
|
||||
puts "Cached?: Yes" if strategy_data[:cached] == true
|
||||
end
|
||||
|
||||
match_version_map.delete_if do |_match, version|
|
||||
next true if version.blank?
|
||||
next false if has_livecheckable
|
||||
|
||||
UNSTABLE_VERSION_KEYWORDS.any? do |rejection|
|
||||
version.to_s.include?(rejection)
|
||||
end
|
||||
end
|
||||
next if match_version_map.blank?
|
||||
|
||||
if debug
|
||||
puts
|
||||
puts "Matched Versions:"
|
||||
|
||||
if verbose
|
||||
match_version_map.each do |match, version|
|
||||
puts "#{match} => #{version.inspect}"
|
||||
end
|
||||
else
|
||||
puts match_version_map.values.join(", ")
|
||||
end
|
||||
end
|
||||
|
||||
res_current = resource.version
|
||||
res_latest = Version.new(match_version_map.values.max_by { |v| LivecheckVersion.create(resource, v) })
|
||||
|
||||
return status_hash(resource, "error", ["Unable to get versions"], verbose: verbose) if res_latest.blank?
|
||||
|
||||
is_outdated = res_current < res_latest
|
||||
is_newer_than_upstream = res_current > res_latest
|
||||
|
||||
resource_version_info = {
|
||||
resource: resource.name,
|
||||
version: {
|
||||
current: res_current.to_s,
|
||||
latest: res_latest.to_s,
|
||||
outdated: is_outdated,
|
||||
newer_than_upstream: is_newer_than_upstream,
|
||||
},
|
||||
}
|
||||
|
||||
resource_version_info[:meta] = { livecheckable: has_livecheckable, url: {} }
|
||||
if livecheck_url.is_a?(Symbol) && livecheck_url_string
|
||||
resource_version_info[:meta][:url][:symbol] = livecheck_url
|
||||
end
|
||||
resource_version_info[:meta][:url][:original] = original_url
|
||||
resource_version_info[:meta][:url][:processed] = url if url != original_url
|
||||
if strategy_data[:url].present? && strategy_data[:url] != url
|
||||
resource_version_info[:meta][:url][:strategy] = strategy_data[:url]
|
||||
end
|
||||
resource_version_info[:meta][:url][:final] = strategy_data[:final_url] if strategy_data[:final_url]
|
||||
resource_version_info[:meta][:strategy] = strategy.present? ? strategy_name : nil
|
||||
if strategies.present?
|
||||
resource_version_info[:meta][:strategies] = strategies.map { |s| livecheck_strategy_names[s] }
|
||||
end
|
||||
resource_version_info[:meta][:regex] = regex.inspect if regex.present?
|
||||
resource_version_info[:meta][:cached] = true if strategy_data[:cached] == true
|
||||
|
||||
rescue => e
|
||||
Homebrew.failed = true
|
||||
if json
|
||||
status_hash(resource, "error", [e.to_s], verbose: verbose)
|
||||
elsif !quiet
|
||||
onoe "#{Tty.blue}#{resource.name}#{Tty.reset}: #{e}"
|
||||
$stderr.puts e.backtrace if debug && !e.is_a?(Livecheck::Error)
|
||||
nil
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength
|
||||
resource_version_info
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ModuleLength
|
||||
end
|
||||
|
||||
@ -11,15 +11,17 @@ module Homebrew
|
||||
|
||||
include Comparable
|
||||
|
||||
sig { params(formula_or_cask: T.any(Formula, Cask::Cask), version: Version).returns(LivecheckVersion) }
|
||||
def self.create(formula_or_cask, version)
|
||||
versions = case formula_or_cask
|
||||
when Formula
|
||||
sig {
|
||||
params(package_or_resource: T.any(Formula, Cask::Cask, Resource), version: Version).returns(LivecheckVersion)
|
||||
}
|
||||
def self.create(package_or_resource, version)
|
||||
versions = case package_or_resource
|
||||
when Formula, Resource
|
||||
[version]
|
||||
when Cask::Cask
|
||||
version.to_s.split(/[,:]/).map { |s| Version.new(s) }
|
||||
else
|
||||
T.absurd(formula_or_cask)
|
||||
T.absurd(package_or_resource)
|
||||
end
|
||||
new(versions)
|
||||
end
|
||||
|
||||
@ -6,7 +6,7 @@ require "livecheck/livecheck"
|
||||
module Homebrew
|
||||
module Livecheck
|
||||
# The `Livecheck::SkipConditions` module primarily contains methods that
|
||||
# check for various formula/cask conditions where a check should be skipped.
|
||||
# check for various formula/cask/resource conditions where a check should be skipped.
|
||||
#
|
||||
# @api private
|
||||
module SkipConditions
|
||||
@ -16,14 +16,14 @@ module Homebrew
|
||||
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
livecheckable: T::Boolean,
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
livecheckable: T::Boolean,
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def formula_or_cask_skip(formula_or_cask, livecheckable, full_name: false, verbose: false)
|
||||
formula = formula_or_cask if formula_or_cask.is_a?(Formula)
|
||||
def package_or_resource_skip(package_or_resource, livecheckable, full_name: false, verbose: false)
|
||||
formula = package_or_resource if package_or_resource.is_a?(Formula)
|
||||
|
||||
if (stable_url = formula&.stable&.url)
|
||||
stable_is_gist = stable_url.match?(%r{https?://gist\.github(?:usercontent)?\.com/}i)
|
||||
@ -33,8 +33,8 @@ module Homebrew
|
||||
stable_from_internet_archive = stable_url.match?(%r{https?://web\.archive\.org/}i)
|
||||
end
|
||||
|
||||
skip_message = if formula_or_cask.livecheck.skip_msg.present?
|
||||
formula_or_cask.livecheck.skip_msg
|
||||
skip_message = if package_or_resource.livecheck.skip_msg.present?
|
||||
package_or_resource.livecheck.skip_msg
|
||||
elsif !livecheckable
|
||||
if stable_from_google_code_archive
|
||||
"Stable URL is from Google Code Archive"
|
||||
@ -45,10 +45,10 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
return {} if !formula_or_cask.livecheck.skip? && skip_message.blank?
|
||||
return {} if !package_or_resource.livecheck.skip? && skip_message.blank?
|
||||
|
||||
skip_messages = skip_message ? [skip_message] : nil
|
||||
Livecheck.status_hash(formula_or_cask, "skipped", skip_messages, full_name: full_name, verbose: verbose)
|
||||
Livecheck.status_hash(package_or_resource, "skipped", skip_messages, full_name: full_name, verbose: verbose)
|
||||
end
|
||||
|
||||
sig {
|
||||
@ -157,7 +157,7 @@ module Homebrew
|
||||
|
||||
# Skip conditions for formulae.
|
||||
FORMULA_CHECKS = [
|
||||
:formula_or_cask_skip,
|
||||
:package_or_resource_skip,
|
||||
:formula_head_only,
|
||||
:formula_deprecated,
|
||||
:formula_disabled,
|
||||
@ -166,76 +166,85 @@ module Homebrew
|
||||
|
||||
# Skip conditions for casks.
|
||||
CASK_CHECKS = [
|
||||
:formula_or_cask_skip,
|
||||
:package_or_resource_skip,
|
||||
:cask_discontinued,
|
||||
:cask_version_latest,
|
||||
:cask_url_unversioned,
|
||||
].freeze
|
||||
|
||||
# If a formula/cask should be skipped, we return a hash from
|
||||
# Skip conditions for resources.
|
||||
RESOURCE_CHECKS = [
|
||||
:package_or_resource_skip,
|
||||
].freeze
|
||||
|
||||
# If a formula/cask/resource should be skipped, we return a hash from
|
||||
# `Livecheck#status_hash`, which contains a `status` type and sometimes
|
||||
# error `messages`.
|
||||
sig {
|
||||
params(
|
||||
formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(Hash)
|
||||
}
|
||||
def skip_information(formula_or_cask, full_name: false, verbose: false)
|
||||
livecheckable = formula_or_cask.livecheckable?
|
||||
def skip_information(package_or_resource, full_name: false, verbose: false)
|
||||
livecheckable = package_or_resource.livecheckable?
|
||||
|
||||
checks = case formula_or_cask
|
||||
checks = case package_or_resource
|
||||
when Formula
|
||||
FORMULA_CHECKS
|
||||
when Cask::Cask
|
||||
CASK_CHECKS
|
||||
when Resource
|
||||
RESOURCE_CHECKS
|
||||
end
|
||||
return {} unless checks
|
||||
|
||||
checks.each do |method_name|
|
||||
skip_hash = send(method_name, formula_or_cask, livecheckable, full_name: full_name, verbose: verbose)
|
||||
skip_hash = send(method_name, package_or_resource, livecheckable, full_name: full_name, verbose: verbose)
|
||||
return skip_hash if skip_hash.present?
|
||||
end
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
# Skip conditions for formulae/casks referenced in a `livecheck` block
|
||||
# Skip conditions for formulae/casks/resources referenced in a `livecheck` block
|
||||
# are treated differently than normal. We only respect certain skip
|
||||
# conditions (returning the related hash) and others are treated as
|
||||
# errors.
|
||||
sig {
|
||||
params(
|
||||
livecheck_formula_or_cask: T.any(Formula, Cask::Cask),
|
||||
original_formula_or_cask_name: String,
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
livecheck_package_or_resource: T.any(Formula, Cask::Cask, Resource),
|
||||
original_package_or_resource_name: String,
|
||||
full_name: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
).returns(T.nilable(Hash))
|
||||
}
|
||||
def referenced_skip_information(
|
||||
livecheck_formula_or_cask,
|
||||
original_formula_or_cask_name,
|
||||
livecheck_package_or_resource,
|
||||
original_package_or_resource_name,
|
||||
full_name: false,
|
||||
verbose: false
|
||||
)
|
||||
skip_info = SkipConditions.skip_information(
|
||||
livecheck_formula_or_cask,
|
||||
livecheck_package_or_resource,
|
||||
full_name: full_name,
|
||||
verbose: verbose,
|
||||
)
|
||||
return if skip_info.blank?
|
||||
|
||||
referenced_name = Livecheck.formula_or_cask_name(livecheck_formula_or_cask, full_name: full_name)
|
||||
referenced_type = case livecheck_formula_or_cask
|
||||
referenced_name = Livecheck.package_or_resource_name(livecheck_package_or_resource, full_name: full_name)
|
||||
referenced_type = case livecheck_package_or_resource
|
||||
when Formula
|
||||
:formula
|
||||
when Cask::Cask
|
||||
:cask
|
||||
when Resource
|
||||
:resource
|
||||
end
|
||||
|
||||
if skip_info[:status] != "error" &&
|
||||
!(skip_info[:status] == "skipped" && livecheck_formula_or_cask.livecheck.skip?)
|
||||
!(skip_info[:status] == "skipped" && livecheck_package_or_resource.livecheck.skip?)
|
||||
error_msg_end = if skip_info[:status] == "skipped"
|
||||
"automatically skipped"
|
||||
else
|
||||
@ -245,7 +254,7 @@ module Homebrew
|
||||
raise "Referenced #{referenced_type} (#{referenced_name}) is #{error_msg_end}"
|
||||
end
|
||||
|
||||
skip_info[referenced_type] = original_formula_or_cask_name
|
||||
skip_info[referenced_type] = original_package_or_resource_name
|
||||
skip_info
|
||||
end
|
||||
|
||||
@ -258,6 +267,8 @@ module Homebrew
|
||||
skip_hash[:formula]
|
||||
elsif skip_hash[:cask].is_a?(String)
|
||||
skip_hash[:cask]
|
||||
elsif skip_hash[:resource].is_a?(String)
|
||||
" #{skip_hash[:resource]}"
|
||||
end
|
||||
return unless name
|
||||
|
||||
|
||||
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