mirror of
https://github.com/Homebrew/brew.git
synced 2026-08-12 22:29:27 +04:00
Merge branch 'main' into remove-nilable-booleans
This commit is contained in:
@@ -65,6 +65,8 @@ updates:
|
||||
- dependency-type: all
|
||||
cooldown:
|
||||
default-days: 7
|
||||
exclude:
|
||||
- Homebrew/actions/*
|
||||
- package-ecosystem: uv
|
||||
directories:
|
||||
- "/Library/Homebrew/formula-analytics/"
|
||||
|
||||
@@ -34,12 +34,12 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: setup-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
- uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
# Don't cache the prefix for the formulae.brew.sh repository as it won't download its own JSON API files
|
||||
if: github.repository != 'Homebrew/formulae.brew.sh'
|
||||
with:
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
path: results.sarif
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
||||
@@ -8,6 +8,11 @@ on:
|
||||
- completions/**
|
||||
- docs/Manpage.md
|
||||
- manpages/brew.1
|
||||
- Library/Homebrew/cmd/**
|
||||
- Library/Homebrew/dev-cmd/**
|
||||
- Library/Homebrew/cli/parser.rb
|
||||
- Library/Homebrew/completions.rb
|
||||
- Library/Homebrew/env_config.rb
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -27,7 +32,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
@@ -37,7 +37,6 @@ jobs:
|
||||
pull-requests: write
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
PR_AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_TEMPLATE_URL: ${{ github.server_url }}/${{ github.repository }}/blob/main/.github/PULL_REQUEST_TEMPLATE.md
|
||||
steps:
|
||||
@@ -78,14 +77,9 @@ jobs:
|
||||
|
||||
- name: Check pull request template
|
||||
id: template
|
||||
env:
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
run: |
|
||||
case "${PR_AUTHOR_ASSOCIATION}" in
|
||||
OWNER | MEMBER | COLLABORATOR | CONTRIBUTOR)
|
||||
echo "complete_template=true" >>"${GITHUB_OUTPUT:?}"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# homebrew-core and homebrew-cask allow condensed PR bodies from recognised bump tools.
|
||||
if [[ "${GITHUB_REPOSITORY}" == "Homebrew/homebrew-core" ||
|
||||
"${GITHUB_REPOSITORY}" == "Homebrew/homebrew-cask" ]]
|
||||
@@ -101,7 +95,9 @@ jobs:
|
||||
complete_template="$(
|
||||
ruby "${RUNNER_TEMP:?}/check_template.rb" pull-request \
|
||||
"${RUNNER_TEMP}/check-prs/body" \
|
||||
"${RUNNER_TEMP}/check-prs/template"
|
||||
"${RUNNER_TEMP}/check-prs/template" \
|
||||
"${PR_TITLE}" \
|
||||
"${GITHUB_REPOSITORY:?}"
|
||||
)"
|
||||
case "${complete_template}" in
|
||||
true | false) ;;
|
||||
@@ -161,7 +157,7 @@ jobs:
|
||||
--raw-field body="$(
|
||||
cat <<COMMENT
|
||||
<!-- incomplete-pr-template -->
|
||||
Thanks for your pull request. This has been closed because it appears to use an incomplete or outdated pull request template.
|
||||
Thanks for your pull request. This has been closed because it appears to be missing the pull request template, perhaps because this was written by an AI not a human. We require humans to read and fill in these templates.
|
||||
|
||||
Please edit this pull request to fill in the current [pull request template](${PR_TEMPLATE_URL:?}). This workflow will reopen this pull request automatically once the template is complete. **Do not open a new pull request for this.**
|
||||
COMMENT
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
uses: github/codeql-action/init@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
|
||||
with:
|
||||
languages: ruby
|
||||
config: |
|
||||
@@ -35,4 +35,4 @@ jobs:
|
||||
- Library/Homebrew/vendor
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
uses: github/codeql-action/analyze@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: true
|
||||
cask: true
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- run: brew install-bundler-gems --groups=all
|
||||
|
||||
# install Homebrew formulae we might need
|
||||
- uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
- uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
install: shellcheck shfmt gh gnu-tar subversion curl
|
||||
workflow-key: copilot-setup-steps
|
||||
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
echo "push=$(jq --raw-output "${filter}" <<<"${PUSH}")" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Log in to GitHub Packages (github-actions[bot])
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: github-actions[bot]
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
|
||||
- name: Log in to GitHub Packages (BrewTestBot)
|
||||
if: fromJSON(steps.attributes.outputs.push)
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: BrewTestBot
|
||||
@@ -265,7 +265,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Log in to GitHub Packages (BrewTestBot)
|
||||
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: BrewTestBot
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
run: vale docs/
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: Homebrew/actions/setup-ruby@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-ruby@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
bundler-cache: true
|
||||
portable-ruby: true
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
issues: write # for Homebrew/actions/create-or-update-issue
|
||||
steps:
|
||||
- name: Open, update, or close deploy issue
|
||||
uses: Homebrew/actions/create-or-update-issue@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/create-or-update-issue@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
|
||||
- name: Install git-pkgs
|
||||
run: brew install git-pkgs
|
||||
|
||||
@@ -77,13 +77,13 @@ jobs:
|
||||
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Install Pandoc
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
install: pandoc
|
||||
workflow-key: release
|
||||
@@ -212,7 +212,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Generate build provenance
|
||||
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0
|
||||
uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4.2.1
|
||||
with:
|
||||
subject-path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
|
||||
|
||||
|
||||
@@ -24,19 +24,19 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: BrewTestBot
|
||||
|
||||
- name: Set up commit signing
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
uses: Homebrew/actions/setup-commit-signing@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-commit-signing@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
|
||||
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
|
||||
- name: Push commits
|
||||
if: steps.update.outputs.committed == 'true' && github.actor != 'dependabot[bot]'
|
||||
uses: Homebrew/actions/git-try-push@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-try-push@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
|
||||
directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
|
||||
@@ -106,7 +106,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Open, update, or close schema issue
|
||||
uses: Homebrew/actions/create-or-update-issue@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/create-or-update-issue@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
title: Failed to update SBOM schema
|
||||
body: >
|
||||
|
||||
@@ -30,20 +30,20 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Configure Git user
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: BrewTestBot
|
||||
|
||||
- name: Set up commit signing
|
||||
if: github.event_name != 'pull_request' && github.actor != 'dependabot[bot]'
|
||||
uses: Homebrew/actions/setup-commit-signing@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-commit-signing@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
|
||||
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
|
||||
- name: Push commits
|
||||
if: steps.commit.outputs.committed == 'true' && github.actor != 'dependabot[bot]'
|
||||
uses: Homebrew/actions/git-try-push@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-try-push@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
|
||||
directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Open, update, or close Sorbet issue
|
||||
uses: Homebrew/actions/create-or-update-issue@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/create-or-update-issue@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
title: Failed to update RBI files
|
||||
body: >
|
||||
|
||||
@@ -24,19 +24,19 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: BrewTestBot
|
||||
|
||||
- name: Set up commit signing
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
uses: Homebrew/actions/setup-commit-signing@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-commit-signing@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
|
||||
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
|
||||
- name: Push commits
|
||||
if: steps.update.outputs.committed == 'true' && github.actor != 'dependabot[bot]'
|
||||
uses: Homebrew/actions/git-try-push@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-try-push@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
|
||||
directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Open, update, or close SPDX issue
|
||||
uses: Homebrew/actions/create-or-update-issue@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/create-or-update-issue@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
title: Failed to update SPDX license data
|
||||
body: >
|
||||
|
||||
@@ -33,18 +33,18 @@ jobs:
|
||||
steps:
|
||||
- name: Setup Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: BrewTestBot
|
||||
|
||||
- name: Set up commit signing
|
||||
uses: Homebrew/actions/setup-commit-signing@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-commit-signing@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
|
||||
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
|
||||
- name: Push commits
|
||||
if: steps.update.outputs.committed == 'true'
|
||||
uses: Homebrew/actions/git-try-push@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-try-push@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
|
||||
directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Open, update, or close sponsors, maintainers, manpage and completions issue
|
||||
uses: Homebrew/actions/create-or-update-issue@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/create-or-update-issue@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
title: Failed to update sponsors, maintainers, manpage and completions
|
||||
body: >
|
||||
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests
|
||||
uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||
uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 21
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale `bump-formula-pr` and `bump-cask-pr` Pull Requests
|
||||
uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||
uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 2
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: github-actions[bot]
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@5037c6f1012cc5ea0b9f03fedbaf9955ed9e51e0 # 2026.08.03.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
|
||||
- name: Set up git
|
||||
id: git_setup
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
|
||||
- name: Bump formulae
|
||||
env:
|
||||
|
||||
@@ -31,13 +31,13 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up git
|
||||
id: git_setup
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
|
||||
- name: Pull bottles
|
||||
env:
|
||||
@@ -59,6 +59,6 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Push commits
|
||||
uses: Homebrew/actions/git-try-push@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-try-push@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
branch: TAP_NEW_BRANCH
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
+15
-17
@@ -34,7 +34,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
run: brew install-bundler-gems --groups=style,typecheck
|
||||
|
||||
- name: Install actionlint, shellcheck and shfmt
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
install: actionlint shellcheck shfmt
|
||||
workflow-key: tests-syntax
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: true
|
||||
cask: true
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: true
|
||||
cask: false
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: true
|
||||
cask: true
|
||||
@@ -156,13 +156,13 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: BrewTestBot
|
||||
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
@@ -239,7 +239,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
# We only test needs_homebrew_core tests on macOS because
|
||||
# homebrew/core is not available by default on GitHub-hosted Ubuntu
|
||||
@@ -271,14 +271,14 @@ jobs:
|
||||
|
||||
- name: Install brew tests --online dependencies
|
||||
if: matrix.name == 'tests (online)'
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
install: curl subversion
|
||||
workflow-key: tests-tests-online
|
||||
|
||||
- name: Install brew tests macOS dependencies
|
||||
if: runner.os != 'Linux'
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
install: subversion gnupg
|
||||
workflow-key: tests-tests-macos
|
||||
@@ -298,7 +298,6 @@ jobs:
|
||||
run: brew tests ${{ matrix.test-flags }}
|
||||
env:
|
||||
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HOMEBREW_SANDBOX_LINUX_LANDLOCK: 1
|
||||
# These cannot be queried at the macOS level on GitHub Actions.
|
||||
HOMEBREW_LANGUAGES: en-GB
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -359,7 +358,6 @@ jobs:
|
||||
- name: test-bot (macOS arm64)
|
||||
runs-on: macos-26
|
||||
env:
|
||||
HOMEBREW_SANDBOX_LINUX_LANDLOCK: 1
|
||||
HOMEBREW_TEST_BOT_ANALYTICS: 1
|
||||
steps:
|
||||
- name: Install Homebrew and Homebrew's dependencies
|
||||
@@ -399,12 +397,12 @@ jobs:
|
||||
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: true
|
||||
cask: false
|
||||
|
||||
- uses: Homebrew/actions/cache-homebrew-prefix@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
- uses: Homebrew/actions/cache-homebrew-prefix@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
install: gnu-tar
|
||||
workflow-key: test-bot
|
||||
@@ -444,7 +442,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
@@ -496,7 +494,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
|
||||
- name: Cache Homebrew Bundler RubyGems
|
||||
id: cache
|
||||
|
||||
@@ -36,14 +36,14 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
- name: Configure Git user
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: Homebrew/actions/git-user-config@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-user-config@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
username: github-actions[bot]
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Push to pull request
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: Homebrew/actions/git-try-push@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/git-try-push@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@df4b09108a1de9d6f995fe68f302b3f68bd6d2ef # 2026.07.20.1
|
||||
uses: Homebrew/actions/setup-homebrew@fd832223f9f99ebf0244dd20658680e5d4aca049 # 2026.08.03.2
|
||||
with:
|
||||
core: false
|
||||
cask: false
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
|
||||
# Ignore dependencies we don't wish to vendor
|
||||
**/vendor/bundle/ruby/*/gems/ast-*/
|
||||
**/vendor/bundle/ruby/*/gems/base64-*/
|
||||
**/vendor/bundle/ruby/*/gems/benchmark-*/
|
||||
**/vendor/bundle/ruby/*/gems/bigdecimal-*/
|
||||
**/vendor/bundle/ruby/*/gems/bootsnap-*/
|
||||
|
||||
@@ -29,7 +29,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
&& retry apt-get update --error-on=any \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
acl \
|
||||
bubblewrap \
|
||||
bzip2 \
|
||||
ca-certificates \
|
||||
curl \
|
||||
|
||||
+49
-6
@@ -24,6 +24,7 @@ AllCops:
|
||||
# otherwise taps will be prompted to use newer Ruby syntax too early.
|
||||
TargetRubyVersion: 4.0
|
||||
NewCops: enable
|
||||
UseProjectIndex: true
|
||||
Include:
|
||||
- "**/*.rbi"
|
||||
Exclude:
|
||||
@@ -71,6 +72,14 @@ Homebrew/CompactBlank:
|
||||
# `blank?` is not necessarily available here:
|
||||
- "Homebrew/extend/enumerable.rb"
|
||||
|
||||
Homebrew/NoBase64:
|
||||
Description: "Use `String#unpack1` or `Array#pack` instead of the `base64` gem."
|
||||
Include:
|
||||
- "/**/{Formula,Casks}/**/*.rb"
|
||||
- "**/{Formula,Casks}/**/*.rb"
|
||||
Exclude:
|
||||
- "Homebrew/test/**/*.rb"
|
||||
|
||||
Homebrew/NoFileutilsRmrf:
|
||||
Include:
|
||||
- "/**/{Formula,Casks}/**/*.rb"
|
||||
@@ -198,6 +207,35 @@ Lint/DuplicateBranch:
|
||||
- "/**/{Formula,Casks}/**/*.rb"
|
||||
- "**/{Formula,Casks}/**/*.rb"
|
||||
|
||||
# Formula files are loaded independently, so constants with the same names do
|
||||
# not reassign each other. RBI constants declare their Ruby definitions.
|
||||
Lint/ConstantReassignment:
|
||||
Exclude:
|
||||
- "**/*.rbi"
|
||||
- "Taps/*/*/*.rb"
|
||||
- "/**/Formula/**/*.rb"
|
||||
- "**/Formula/**/*.rb"
|
||||
|
||||
# Detect references to project APIs marked with a YARD `@deprecated` tag.
|
||||
Lint/DeprecatedReference:
|
||||
Enabled: true
|
||||
|
||||
# Formula files from separate taps can define the same class without being
|
||||
# loaded together. RBI methods declare their Ruby implementations.
|
||||
Lint/DuplicateMethods:
|
||||
Exclude:
|
||||
- "**/*.rbi"
|
||||
- "Taps/*/*/*.rb"
|
||||
- "/**/Formula/**/*.rb"
|
||||
- "**/Formula/**/*.rb"
|
||||
|
||||
# The project-only index does not include Ruby core or gem APIs.
|
||||
Lint/NameTypo:
|
||||
AllowedNames:
|
||||
- "IfNode" # Defined by rubocop-ast.
|
||||
- "instance_method" # Defined by Ruby core.
|
||||
- "send" # Defined by Ruby core.
|
||||
|
||||
# so many of these in formulae and can't be autocorrected
|
||||
Lint/ParenthesesAsGroupedExpression:
|
||||
Exclude:
|
||||
@@ -346,12 +384,6 @@ Style/AndOr:
|
||||
Style/ArgumentsForwarding:
|
||||
Enabled: false
|
||||
|
||||
# Autocorrects `include?`/`member?` block patterns to `intersect?` even when the second operand
|
||||
# isn't actually an `Array` (e.g. `String`, `Set`, `Enumerator`), which can raise at runtime.
|
||||
# Re-enable once a rubocop version with https://github.com/rubocop/rubocop/pull/15442 is released.
|
||||
Style/ArrayIntersect:
|
||||
Enabled: false
|
||||
|
||||
# Avoid leaking resources.
|
||||
Style/AutoResourceCleanup:
|
||||
Enabled: true
|
||||
@@ -438,6 +470,11 @@ Style/HashAsLastArrayItem:
|
||||
- "/**/Formula/**/*.rb"
|
||||
- "**/Formula/**/*.rb"
|
||||
|
||||
# Restore when the fix for https://github.com/rubocop/rubocop/issues/15531
|
||||
# is released.
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
Style/InverseMethods:
|
||||
InverseMethods:
|
||||
:blank?: :present?
|
||||
@@ -483,6 +520,12 @@ Style/OptionalBooleanParameter:
|
||||
Style/RescueStandardError:
|
||||
EnforcedStyle: implicit
|
||||
|
||||
# The project index compares lexical resolution but Ruby also searches class
|
||||
# ancestors, including formula subclasses. Removing `::` can therefore resolve
|
||||
# a different constant.
|
||||
Style/RedundantConstantBase:
|
||||
Enabled: false
|
||||
|
||||
# Returning `nil` is unnecessary.
|
||||
Style/ReturnNil:
|
||||
Enabled: true
|
||||
|
||||
@@ -4,6 +4,35 @@ inherit_from:
|
||||
Homebrew/MoveToExtendOS:
|
||||
Enabled: true
|
||||
|
||||
# The project index does not model explicit removal or isolated entry points.
|
||||
Lint/ConstantReassignment:
|
||||
Exclude:
|
||||
# Replaced or bypassed by isolated test environments.
|
||||
- "cask/config.rb"
|
||||
- "startup/config.rb"
|
||||
- "test/support/helper/spec/shared_context/homebrew_cask.rb"
|
||||
- "test/support/lib/startup/config.rb"
|
||||
# Defined by mutually exclusive application entry points.
|
||||
- "global.rb"
|
||||
- "utils/ruby_check_version_script.rb"
|
||||
|
||||
Lint/DuplicateMethods:
|
||||
Exclude:
|
||||
# OS-specific implementations intentionally override shared fallbacks. The
|
||||
# project index reports both definitions, so both files must be excluded.
|
||||
- "formula_installer.rb"
|
||||
- "extend/os/linux/formula_installer.rb"
|
||||
- "requirements/xcode_requirement.rb"
|
||||
- "extend/os/linux/requirements/xcode_requirement.rb"
|
||||
- "hardware.rb"
|
||||
- "extend/os/mac/hardware/cpu/hardware.rb"
|
||||
- "keg_only_reason.rb"
|
||||
- "extend/os/mac/keg_only_reason.rb"
|
||||
|
||||
# Detect unreferenced private methods across Homebrew's project index.
|
||||
Lint/UnusedPrivateMethod:
|
||||
Enabled: true
|
||||
|
||||
# Want to preserve our own API for these methods for now.
|
||||
Naming/PredicatePrefix:
|
||||
inherit_mode:
|
||||
|
||||
@@ -45,6 +45,7 @@ group :style, optional: true do
|
||||
gem "rubocop-performance", require: false
|
||||
gem "rubocop-rspec", require: false
|
||||
gem "rubocop-sorbet", require: false
|
||||
gem "rubydex", require: false
|
||||
end
|
||||
group :style, :tests, optional: true do
|
||||
gem "test-prof", require: false
|
||||
@@ -79,7 +80,6 @@ group :audit, :bump_unversioned_casks, :livecheck, optional: true do
|
||||
end
|
||||
|
||||
# vendored gems (no group)
|
||||
gem "base64"
|
||||
gem "concurrent-ruby"
|
||||
gem "patchelf"
|
||||
gem "plist"
|
||||
|
||||
@@ -7,14 +7,14 @@ GEM
|
||||
bigdecimal (4.1.2)
|
||||
bindata (2.5.1)
|
||||
concurrent-ruby (1.3.8)
|
||||
csv (3.3.5)
|
||||
csv (3.3.6)
|
||||
diff-lcs (1.6.2)
|
||||
drb (2.2.3)
|
||||
elftools (1.3.1)
|
||||
bindata (~> 2)
|
||||
erubi (1.13.1)
|
||||
hana (1.3.7)
|
||||
json (2.21.1)
|
||||
json (2.21.2)
|
||||
json_schemer (2.5.0)
|
||||
bigdecimal
|
||||
hana (~> 1.3)
|
||||
@@ -36,7 +36,7 @@ GEM
|
||||
parser (3.3.12.0)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
patchelf (1.5.2)
|
||||
patchelf (1.6.2)
|
||||
elftools (>= 1.3)
|
||||
logger (~> 1)
|
||||
plist (3.7.2)
|
||||
@@ -46,7 +46,7 @@ GEM
|
||||
rbi (0.4.1)
|
||||
prism (~> 1.0)
|
||||
rbs (>= 4.0.1)
|
||||
rbs (4.0.3)
|
||||
rbs (4.1.2)
|
||||
logger
|
||||
prism (>= 1.6.0)
|
||||
tsort
|
||||
@@ -74,7 +74,7 @@ GEM
|
||||
rspec-support (3.13.7)
|
||||
rspec_junit_formatter (0.6.0)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (1.88.2)
|
||||
rubocop (1.89.0)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.1.0)
|
||||
@@ -99,7 +99,7 @@ GEM
|
||||
lint_roller (~> 1.1)
|
||||
regexp_parser (>= 2.0)
|
||||
rubocop (~> 1.86, >= 1.86.2)
|
||||
rubocop-sorbet (0.13.2)
|
||||
rubocop-sorbet (0.14.0)
|
||||
lint_roller
|
||||
rubocop (>= 1.75.2)
|
||||
ruby-lsp (0.26.10)
|
||||
@@ -115,20 +115,20 @@ GEM
|
||||
rubydex (0.3.0-arm64-darwin)
|
||||
rubydex (0.3.0-x86_64-darwin)
|
||||
rubydex (0.3.0-x86_64-linux)
|
||||
simplecov (1.0.2)
|
||||
simplecov (1.0.3)
|
||||
simplecov-cobertura (4.0.0)
|
||||
rexml
|
||||
simplecov (~> 1.0)
|
||||
simpleidn (0.2.3)
|
||||
sorbet (0.6.13359)
|
||||
sorbet-static (= 0.6.13359)
|
||||
sorbet-runtime (0.6.13359)
|
||||
sorbet-static (0.6.13359-aarch64-linux)
|
||||
sorbet-static (0.6.13359-universal-darwin)
|
||||
sorbet-static (0.6.13359-x86_64-linux)
|
||||
sorbet-static-and-runtime (0.6.13359)
|
||||
sorbet (= 0.6.13359)
|
||||
sorbet-runtime (= 0.6.13359)
|
||||
sorbet (0.6.13386)
|
||||
sorbet-static (= 0.6.13386)
|
||||
sorbet-runtime (0.6.13386)
|
||||
sorbet-static (0.6.13386-aarch64-linux)
|
||||
sorbet-static (0.6.13386-universal-darwin)
|
||||
sorbet-static (0.6.13386-x86_64-linux)
|
||||
sorbet-static-and-runtime (0.6.13386)
|
||||
sorbet (= 0.6.13386)
|
||||
sorbet-runtime (= 0.6.13386)
|
||||
spoom (1.8.6)
|
||||
erubi (>= 1.10.0)
|
||||
prism (>= 0.28.0)
|
||||
@@ -170,7 +170,6 @@ PLATFORMS
|
||||
x86_64-linux-gnu
|
||||
|
||||
DEPENDENCIES
|
||||
base64
|
||||
concurrent-ruby
|
||||
csv
|
||||
json_schemer
|
||||
@@ -197,6 +196,7 @@ DEPENDENCIES
|
||||
ruby-macho
|
||||
ruby-prof
|
||||
ruby-progressbar
|
||||
rubydex
|
||||
simplecov
|
||||
simplecov-cobertura
|
||||
sorbet-runtime
|
||||
@@ -216,13 +216,13 @@ CHECKSUMS
|
||||
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
||||
bindata (2.5.1) sha256=53186a1ec2da943d4cb413583d680644eb810aacbf8902497aac8f191fad9e58
|
||||
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
|
||||
csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f
|
||||
csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
|
||||
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
||||
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
||||
elftools (1.3.1) sha256=9d46fca49a7d5e821b9c80127b19d59e8ce578bfaa17b99c8ff259222686aaf8
|
||||
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
||||
hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
|
||||
json (2.21.1) sha256=13a43df75d95641443f5702dff350f237164a9d811ff0f2c2800d4d980220583
|
||||
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
||||
json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
|
||||
kramdown (2.5.2) sha256=1ba542204c66b6f9111ff00dcc26075b95b220b07f2905d8261740c82f7f02fa
|
||||
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
||||
@@ -234,13 +234,13 @@ CHECKSUMS
|
||||
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
||||
parallel_tests (5.7.0) sha256=3f1762c46ca2c223b8af8ef877217f9d76974e191bfa934f2580b58bcf1d005c
|
||||
parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
|
||||
patchelf (1.5.2) sha256=7efd746a52344be18c44b2fafeb80517c2c12e18dc24ac65d328b7c0609bc386
|
||||
patchelf (1.6.2) sha256=b3867281dd5f4089bc656311e5de774515e01f97aa44a27a1536ec1ad4a77bda
|
||||
plist (3.7.2) sha256=d37a4527cc1116064393df4b40e1dbbc94c65fa9ca2eec52edf9a13616718a42
|
||||
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
||||
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
||||
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
||||
rbi (0.4.1) sha256=66611ca331b0b47d98607a7afda12ab44e0a98297d5393d4b93b846b9786d44d
|
||||
rbs (4.0.3) sha256=5a7bf70e2628549d9a1f44eae447b2cfe55968a9c60cfff52693a4bdcc020e14
|
||||
rbs (4.1.2) sha256=050eb1d8b508f1233bed929c0f2c7052302f7adf295230d9cb314e9024078f48
|
||||
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
||||
require-hooks (0.4.1) sha256=3a4cd3ed0131b9ec5c2e68f64cb0c5ba56daec6fc363d56e8eef74816c374d7d
|
||||
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
||||
@@ -253,12 +253,12 @@ CHECKSUMS
|
||||
rspec-sorbet (1.9.2) sha256=28bf3969fa136ed22edd05b00d23aab52c276f2fcc89fccbb16b8fd0c48931c8
|
||||
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
||||
rspec_junit_formatter (0.6.0) sha256=40dde674e6ae4e6cc0ff560da25497677e34fefd2338cc467a8972f602b62b15
|
||||
rubocop (1.88.2) sha256=8def251c90cd955feb4daa3edc0ab56893250c4ce90ef81e6c80c03f9a939bbf
|
||||
rubocop (1.89.0) sha256=4dee8e3ee9c45e474834efd9e8d6fd031e8331c8dacdff0de4ad65ae0a6faae7
|
||||
rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
|
||||
rubocop-md (2.0.4) sha256=0d076b6b5e99dea2ddc928c4bd702497decb9a8400da7808091e02ebcafcfb93
|
||||
rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
|
||||
rubocop-rspec (3.10.2) sha256=0b3e2ecc592cd10ecbf0095bb58d1e357905276e069643523cc19eb7495f65e2
|
||||
rubocop-sorbet (0.13.2) sha256=7901e57b6b8e9e9b970d941dbdbb89f2c9de0183bfd2551460468f8aa9548655
|
||||
rubocop-sorbet (0.14.0) sha256=30899556c9d02ab291d8bb761bae1cc025de56f72d909286b6960c1620d67bda
|
||||
ruby-lsp (0.26.10) sha256=e67284af94423531f6b9a583350596421b5a6a4dd93083f1c2ba03da7c23bbed
|
||||
ruby-macho (6.0.0) sha256=7bae15ff5564d06342a045523e47049cbf09f16e80b5ceb5bbc5fd8d8ec00c8e
|
||||
ruby-prof (2.0.5) sha256=dbc3b5112089538ac33303145db0cbd77cbdcdbc05b09e44a967ff72a36d6c0e
|
||||
@@ -267,15 +267,15 @@ CHECKSUMS
|
||||
rubydex (0.3.0-arm64-darwin) sha256=19f685610216e4e7f488722ac90dfbc33113a384dd16db309cf939606f4c32dd
|
||||
rubydex (0.3.0-x86_64-darwin) sha256=59d20c98b0bf2226f5db5bee14cf57075aa93aa0c3fa0176f496a217691ff7b6
|
||||
rubydex (0.3.0-x86_64-linux) sha256=dfe4026591f226b4a1b53f82f86dc14fad2e7c221ee8a5be99a0a46bf2d58b04
|
||||
simplecov (1.0.2) sha256=c6459434efe4b948b46477cc2df2faa73ab365f83a33c7c17f81262f4f7f1244
|
||||
simplecov (1.0.3) sha256=38ef0514f16ae7562f0d0f4df02610071115103d301b6de7dacbcc000082e39b
|
||||
simplecov-cobertura (4.0.0) sha256=e4fb3159b1ecea545b44f5452a8611305323e78ad23eae8aed35924d072e01ea
|
||||
simpleidn (0.2.3) sha256=08ce96f03fa1605286be22651ba0fc9c0b2d6272c9b27a260bc88be05b0d2c29
|
||||
sorbet (0.6.13359) sha256=772eabba1cadbfdba4cd8416d342e4ad5f195b8e57bf08467cf1fb103fc5a415
|
||||
sorbet-runtime (0.6.13359) sha256=5edcd198c678d69f07ec2758b9d0f277bce2e322efd7d5bd902a34c1056a2e5a
|
||||
sorbet-static (0.6.13359-aarch64-linux) sha256=d6ffbc62c2189d6d02ae2d7b054e78565f404a5d254b34c876dd2adade621f63
|
||||
sorbet-static (0.6.13359-universal-darwin) sha256=ebad32899f8cf929589a1932f48670fb4e522ea75a9f4cdfa1395475377282f5
|
||||
sorbet-static (0.6.13359-x86_64-linux) sha256=08bb9cc0db2d0a6f9e181af0259c54455bfa66577b1581a4f11e928ad58190bd
|
||||
sorbet-static-and-runtime (0.6.13359) sha256=cea6c6c1e930becdaadf6bbe732c119441ef206bf16c74e8bc11d11975dcb181
|
||||
sorbet (0.6.13386) sha256=8f9cb0771c0843f1f2610847bf38a976716459fbec87b040ba8897414a286aa3
|
||||
sorbet-runtime (0.6.13386) sha256=716aa0a47e75bba1167205d408d59b622db3648109854e9fd309fb901564df48
|
||||
sorbet-static (0.6.13386-aarch64-linux) sha256=ebe41ccfbe9659d318cf6c5e28cede1069d4141bdc2f6524d4ba4d7b18440519
|
||||
sorbet-static (0.6.13386-universal-darwin) sha256=1d3cfdfd1c57a6108b322fe5d2657da2b4021aa1fed413634762d9c44d4ffea4
|
||||
sorbet-static (0.6.13386-x86_64-linux) sha256=29123908d23ca2bfc135c94c2da698934c74e9fbf02d5855274dc91d71071a93
|
||||
sorbet-static-and-runtime (0.6.13386) sha256=ca4450fa2ef490fd17526eac9e0067fbf2c2b657b83637a9957135658e6b8207
|
||||
spoom (1.8.6) sha256=b59ea53b8e258b044c08ef48ef819dd6f12aa386c47a745aa9d8e7fb1ae34a29
|
||||
stackprof (0.2.28) sha256=4ec2ace02f386012b40ca20ef80c030ad711831f59511da12e83b34efb0f9a04
|
||||
tapioca (0.19.2) sha256=938731b07811aee8d23871b1aee8861d464fbaf2cfffbf79a62b0c869a5120ec
|
||||
|
||||
@@ -179,7 +179,7 @@ class Bottle
|
||||
sig { returns(T::Boolean) }
|
||||
def skip_relocation?
|
||||
attrs = tab_attributes
|
||||
tab = Tab.new(attrs) unless attrs.empty?
|
||||
tab = Tab.new(**attrs.transform_keys(&:to_sym)) unless attrs.empty?
|
||||
@spec.skip_relocation?(tag: @tag, tab:)
|
||||
end
|
||||
|
||||
|
||||
@@ -241,13 +241,6 @@ then
|
||||
export HOMEBREW_COLOR="1"
|
||||
fi
|
||||
|
||||
# This is set by Homebrew's self-hosted runner environment.
|
||||
# shellcheck disable=SC2154
|
||||
if [[ -n "${HOMEBREW_LINUX}" && -n "${GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED}" ]]
|
||||
then
|
||||
export HOMEBREW_SANDBOX_LINUX_LANDLOCK="1"
|
||||
fi
|
||||
|
||||
setup-locale
|
||||
|
||||
#####
|
||||
|
||||
@@ -6,6 +6,20 @@ require "bundle/extensions/extension"
|
||||
module Homebrew
|
||||
module Bundle
|
||||
class Cargo < Extension
|
||||
SourceOptions = T.type_alias { T::Hash[Symbol, String] }
|
||||
Crate = T.type_alias { { name: String, source: T.nilable(String) } }
|
||||
Checkable = T.type_alias { { name: String, options: SourceOptions } }
|
||||
CrateEntry = T.type_alias { T.any(Crate, Checkable) }
|
||||
|
||||
# `cargo install --list` reports the origin of anything not installed from a
|
||||
# registry, as either a git URL or a local path. Only a git URL that resolves
|
||||
# from another machine is supported here, so neither a path nor the `file://`
|
||||
# scheme is accepted. `--git` rejects an scp-style remote, so a scheme is
|
||||
# required too.
|
||||
GIT_SOURCE_REGEX = %r{\A(?:ssh|git|https?)://}
|
||||
GIT_REFERENCE_KEYS = %w[branch tag rev].freeze
|
||||
PACKAGE_LIST_REGEX = /\A(?<name>[^\s:]+)\s+v[0-9A-Za-z.+-]+(?:\s+\((?<origin>[^)]+)\))?/
|
||||
|
||||
class << self
|
||||
sig { override.returns(Symbol) }
|
||||
def type = :cargo
|
||||
@@ -16,10 +30,36 @@ module Homebrew
|
||||
sig { override.returns(String) }
|
||||
def banner_name = "Cargo packages"
|
||||
|
||||
sig { override.params(name: String, options: Homebrew::Bundle::EntryInputOptions).returns(Dsl::Entry) }
|
||||
def entry(name, options = {})
|
||||
unknown_options = options.keys - [:source]
|
||||
raise "unknown options(#{unknown_options.inspect}) for cargo" if unknown_options.present?
|
||||
|
||||
source = options.fetch(:source, nil)
|
||||
if !source.nil? && !source.is_a?(String)
|
||||
raise "options[:source](#{source.inspect}) should be a String object"
|
||||
end
|
||||
|
||||
normalized_options = {}
|
||||
if source.present?
|
||||
normalized_source = normalize_source(source)
|
||||
raise "options[:source](#{source.inspect}) should be a git URL" if normalized_source.nil?
|
||||
|
||||
selector = normalized_source.partition("?").last
|
||||
if selector.present? && GIT_REFERENCE_KEYS.exclude?(selector.partition("=").first)
|
||||
raise "options[:source](#{source.inspect}) should select a branch, tag or rev"
|
||||
end
|
||||
|
||||
normalized_options[:source] = normalized_source
|
||||
end
|
||||
|
||||
Dsl::Entry.new(:cargo, name, normalized_options)
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def reset!
|
||||
@packages = T.let(nil, T.nilable(T::Array[String]))
|
||||
@installed_packages = T.let(nil, T.nilable(T::Array[String]))
|
||||
@packages = T.let(nil, T.nilable(T::Array[Crate]))
|
||||
@installed_packages = T.let(nil, T.nilable(T::Array[Crate]))
|
||||
end
|
||||
|
||||
sig { override.returns(T.nilable(String)) }
|
||||
@@ -37,7 +77,7 @@ module Homebrew
|
||||
which("cargo", ORIGINAL_PATHS)
|
||||
end
|
||||
|
||||
sig { override.returns(T::Array[String]) }
|
||||
sig { override.returns(T::Array[Crate]) }
|
||||
def packages
|
||||
packages = @packages
|
||||
return packages if packages
|
||||
@@ -53,24 +93,47 @@ module Homebrew
|
||||
@packages
|
||||
end
|
||||
|
||||
sig { override.params(package: Object).returns(String) }
|
||||
def dump_name(package)
|
||||
T.cast(package, CrateEntry)[:name]
|
||||
end
|
||||
|
||||
sig { params(package: Object).returns(T.nilable(String)) }
|
||||
def dump_source(package)
|
||||
package = T.cast(package, CrateEntry)
|
||||
return package[:source] if package.key?(:source)
|
||||
|
||||
package[:options].fetch(:source, nil)
|
||||
end
|
||||
|
||||
sig { override.params(package: Object).returns(String) }
|
||||
def dump_entry(package)
|
||||
line = super
|
||||
source = dump_source(package)
|
||||
line = "#{line}, source: #{quote(source)}" if source.present?
|
||||
|
||||
line
|
||||
end
|
||||
|
||||
sig {
|
||||
override.params(
|
||||
name: String,
|
||||
with: T.nilable(T::Array[String]),
|
||||
source: T.nilable(String),
|
||||
verbose: T::Boolean,
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def install_package!(name, with: nil, verbose: false)
|
||||
def install_package!(name, with: nil, source: nil, verbose: false)
|
||||
_ = with
|
||||
|
||||
cargo = package_manager_executable!
|
||||
|
||||
with_env(cargo_env(cargo)) do
|
||||
Bundle.system(cargo.to_s, "install", "--locked", name, verbose:)
|
||||
Bundle.system(cargo.to_s, "install", "--locked", *source_args(source), name, verbose:)
|
||||
end
|
||||
end
|
||||
|
||||
sig { override.returns(T::Array[String]) }
|
||||
sig { override.returns(T::Array[Crate]) }
|
||||
def installed_packages
|
||||
installed_packages = @installed_packages
|
||||
return installed_packages if installed_packages
|
||||
@@ -88,17 +151,135 @@ module Homebrew
|
||||
cargo_env(executable)
|
||||
end
|
||||
|
||||
sig { params(output: String).returns(T::Array[String]) }
|
||||
sig {
|
||||
override.params(
|
||||
name: String,
|
||||
with: T.nilable(T::Array[String]),
|
||||
source: T.nilable(String),
|
||||
).returns(Object)
|
||||
}
|
||||
def package_record(name, with: nil, source: nil)
|
||||
_ = with
|
||||
|
||||
crate_record(name, source:)
|
||||
end
|
||||
|
||||
sig { params(name: String, source: T.nilable(String)).returns(Crate) }
|
||||
def crate_record(name, source: nil)
|
||||
{ name: name.strip, source: normalize_source(source) }
|
||||
end
|
||||
private :crate_record
|
||||
|
||||
sig {
|
||||
override.params(
|
||||
name: String,
|
||||
with: T.nilable(T::Array[String]),
|
||||
source: T.nilable(String),
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def package_installed?(name, with: nil, source: nil)
|
||||
installed_packages.include?(package_record(name, with:, source:))
|
||||
end
|
||||
|
||||
sig {
|
||||
override.params(
|
||||
name: String,
|
||||
with: T.nilable(T::Array[String]),
|
||||
source: T.nilable(String),
|
||||
no_upgrade: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
_options: Homebrew::Bundle::EntryOption,
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def preinstall!(name, with: nil, source: nil, no_upgrade: false, verbose: false, **_options)
|
||||
_ = no_upgrade
|
||||
|
||||
ensure_package_manager_installed!(name, verbose:)
|
||||
|
||||
if package_installed?(name, with:, source:)
|
||||
puts "Skipping install of #{name} #{package_description}. It is already installed." if verbose
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
sig {
|
||||
override.params(
|
||||
name: String,
|
||||
with: T.nilable(T::Array[String]),
|
||||
source: T.nilable(String),
|
||||
preinstall: T::Boolean,
|
||||
no_upgrade: T::Boolean,
|
||||
verbose: T::Boolean,
|
||||
force: T::Boolean,
|
||||
_options: Homebrew::Bundle::EntryOption,
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def install!(name, with: nil, source: nil, preinstall: true, no_upgrade: false, verbose: false, force: false,
|
||||
**_options)
|
||||
_ = no_upgrade
|
||||
_ = force
|
||||
|
||||
return true unless preinstall
|
||||
|
||||
puts "Installing #{name} #{package_description}. It is not currently installed." if verbose
|
||||
return false unless install_package!(name, with:, source:, verbose:)
|
||||
|
||||
package = crate_record(name, source:)
|
||||
installed_packages << package unless installed_packages.include?(package)
|
||||
packages << package unless packages.include?(package)
|
||||
true
|
||||
end
|
||||
|
||||
# A branch, tag or revision selected at install time is reported by
|
||||
# `cargo install --list` as a URL query, and never more than one of them,
|
||||
# but `--git` rejects a query: it has to be passed as the matching flag.
|
||||
sig { params(source: T.nilable(String)).returns(T::Array[String]) }
|
||||
def source_args(source)
|
||||
source = normalize_source(source)
|
||||
return [] if source.nil?
|
||||
|
||||
url, _, query = source.partition("?")
|
||||
key, _, value = query.partition("=")
|
||||
args = ["--git", url]
|
||||
args.push("--#{key}", value) if GIT_REFERENCE_KEYS.include?(key)
|
||||
|
||||
args
|
||||
end
|
||||
private :source_args
|
||||
|
||||
sig { params(output: String).returns(T::Array[Crate]) }
|
||||
def parse_package_list(output)
|
||||
output.lines.filter_map do |line|
|
||||
next if line.match?(/^\s/)
|
||||
|
||||
match = line.match(/\A(?<name>[^\s:]+)\s+v[0-9A-Za-z.+-]+/)
|
||||
match[:name] if match
|
||||
match = line.match(PACKAGE_LIST_REGEX)
|
||||
next if match.nil?
|
||||
|
||||
name = match[:name]
|
||||
next if name.nil?
|
||||
|
||||
{ name:, source: normalize_source(match[:origin]) }
|
||||
end.uniq
|
||||
end
|
||||
private :parse_package_list
|
||||
|
||||
# The resolved revision that `cargo install --list` appends to a git origin
|
||||
# is deliberately dropped: a dumped Brewfile has to compare equal to a
|
||||
# hand-written one, which a pinned revision would prevent. Anything that is
|
||||
# not a git URL has no `source:` to dump, whether it is a registry crate or
|
||||
# a local path.
|
||||
sig { params(source: T.nilable(String)).returns(T.nilable(String)) }
|
||||
def normalize_source(source)
|
||||
source = source.presence&.strip&.sub(/#[^#]*\z/, "")
|
||||
return if source.blank?
|
||||
return source if source.match?(GIT_SOURCE_REGEX)
|
||||
|
||||
nil
|
||||
end
|
||||
private :normalize_source
|
||||
|
||||
sig { params(cargo: Pathname).returns(T::Hash[String, String]) }
|
||||
def cargo_env(cargo)
|
||||
{
|
||||
@@ -110,6 +291,21 @@ module Homebrew
|
||||
end
|
||||
private :cargo_env
|
||||
end
|
||||
|
||||
sig { override.params(entries: T::Array[Dsl::Entry]).returns(T::Array[Object]) }
|
||||
def format_checkable(entries)
|
||||
checkable_entries(entries).map do |entry|
|
||||
{ name: entry.name, options: entry.options }
|
||||
end
|
||||
end
|
||||
|
||||
sig { override.params(package: Object, no_upgrade: T::Boolean).returns(T::Boolean) }
|
||||
def installed_and_up_to_date?(package, no_upgrade: false)
|
||||
self.class.package_installed?(
|
||||
self.class.dump_name(package),
|
||||
source: self.class.dump_source(package),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -203,7 +203,6 @@ module Cask
|
||||
|
||||
sig { returns(T.nilable(Sandbox)) }
|
||||
def cask_sandbox
|
||||
Sandbox.ensure_sandbox_installed!
|
||||
return unless Sandbox.available?
|
||||
|
||||
Sandbox.new.tap do |sandbox|
|
||||
|
||||
@@ -137,7 +137,7 @@ module Cask
|
||||
next
|
||||
end
|
||||
|
||||
if resolved_path.each_filename.any? { |part| [".", ".."].include?(part) }
|
||||
if resolved_path.each_filename.to_a.intersect?([".", ".."])
|
||||
opoo "Skipping #{Formatter.identifier(action)} for path with relative segments '#{path}'."
|
||||
next
|
||||
end
|
||||
|
||||
@@ -5,6 +5,7 @@ require "cask/artifact/symlinked"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
# Artifact corresponding to the `app_image` stanza.
|
||||
class AppImage < Symlinked
|
||||
sig { override.params(target: T.any(String, Pathname), base_dir: T.nilable(Pathname)).returns(Pathname) }
|
||||
def resolve_target(target, base_dir: nil)
|
||||
|
||||
@@ -6,6 +6,7 @@ require "install_steps"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
# Abstract superclass for install steps artifacts.
|
||||
class AbstractInstallSteps < AbstractArtifact
|
||||
abstract!
|
||||
|
||||
@@ -34,6 +35,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
# Artifact corresponding to the `preflight_steps` stanza.
|
||||
class PreflightSteps < AbstractInstallSteps
|
||||
sig { params(command: T.class_of(SystemCommand), _options: T.anything).void }
|
||||
def install_phase(command: SystemCommand, **_options)
|
||||
@@ -46,6 +48,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
# Artifact corresponding to the `postflight_steps` stanza.
|
||||
class PostflightSteps < AbstractInstallSteps
|
||||
sig { params(command: T.class_of(SystemCommand), _options: T.anything).void }
|
||||
def install_phase(command: SystemCommand, **_options)
|
||||
@@ -58,6 +61,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
# Artifact corresponding to the `uninstall_preflight_steps` stanza.
|
||||
class UninstallPreflightSteps < AbstractInstallSteps
|
||||
sig { params(command: T.class_of(SystemCommand), _options: T.anything).void }
|
||||
def uninstall_phase(command: SystemCommand, **_options)
|
||||
@@ -65,6 +69,7 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
# Artifact corresponding to the `uninstall_postflight_steps` stanza.
|
||||
class UninstallPostflightSteps < AbstractInstallSteps
|
||||
sig { params(command: T.class_of(SystemCommand), _options: T.anything).void }
|
||||
def uninstall_phase(command: SystemCommand, **_options)
|
||||
|
||||
@@ -5,6 +5,7 @@ require "cask/artifact/symlinked"
|
||||
|
||||
module Cask
|
||||
module Artifact
|
||||
# Superclass for all artifacts that are installed as shell completions.
|
||||
class ShellCompletion < Symlinked
|
||||
sig { override.overridable.params(_: T.any(String, Pathname), base_dir: T.nilable(Pathname)).returns(Pathname) }
|
||||
def resolve_target(_, base_dir: nil)
|
||||
|
||||
@@ -79,6 +79,9 @@ module Cask
|
||||
(target.realpath == source.realpath || target.realpath.to_s.start_with?("#{cask.caskroom_path}/"))
|
||||
opoo "#{message}; overwriting."
|
||||
Utils.gain_permissions_remove(target, command:)
|
||||
elsif target_links_to_source?
|
||||
ohai "#{self.class.english_name} '#{source.basename}' is already linked to '#{target}'"
|
||||
return
|
||||
elsif (formula = conflicting_formula)
|
||||
opoo "#{message} from formula #{formula}; skipping link."
|
||||
return
|
||||
@@ -113,6 +116,14 @@ module Cask
|
||||
sudo: !target.dirname.writable?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def target_links_to_source?
|
||||
target.symlink? && target.realpath == source.realpath
|
||||
rescue => e
|
||||
odebug "Error checking whether #{target} links to #{source}: #{e}"
|
||||
false
|
||||
end
|
||||
|
||||
# Check if the target file is a symlink that originates from a formula
|
||||
# with the same name as this cask, indicating a potential conflict
|
||||
sig { returns(T.nilable(String)) }
|
||||
|
||||
+153
-139
@@ -89,12 +89,12 @@ module Cask
|
||||
only_audits = @only
|
||||
except_audits = @except
|
||||
|
||||
private_methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name|
|
||||
public_methods.map(&:to_s).grep(/^audit_/).each do |audit_method_name|
|
||||
name = audit_method_name.delete_prefix("audit_")
|
||||
next if !only_audits.empty? && only_audits.exclude?(name)
|
||||
next if except_audits.include?(name)
|
||||
|
||||
send(audit_method_name)
|
||||
public_send(audit_method_name)
|
||||
end
|
||||
|
||||
self
|
||||
@@ -272,8 +272,6 @@ module Cask
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def audit_untrusted_pkg
|
||||
odebug "Auditing pkg stanza: allow_untrusted"
|
||||
@@ -491,6 +489,15 @@ module Cask
|
||||
add_error "OSDN download urls are disabled.", location: url.location, strict_only: true
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def audit_unnecessary_verified
|
||||
return unless new_cask?
|
||||
return unless cask.url
|
||||
return unless verified_present?
|
||||
|
||||
add_error "the `verified` parameter has been deprecated; use the `url` stanza without it"
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def audit_generic_artifacts
|
||||
cask.artifacts.grep(Artifact::Artifact).each do |artifact|
|
||||
@@ -909,141 +916,6 @@ module Cask
|
||||
"but the cask declared #{min_os_definition}"
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(MacOSVersion)) }
|
||||
def cask_sparkle_min_os
|
||||
return unless online?
|
||||
return unless cask.livecheck_defined?
|
||||
return if cask.livecheck.strategy != :sparkle
|
||||
|
||||
# `Sparkle` strategy blocks that use the `items` argument (instead of
|
||||
# `item`) contain arbitrary logic that ignores/overrides the strategy's
|
||||
# sorting, so we can't identify which item would be first/newest here.
|
||||
return if cask.livecheck.strategy_block.present? &&
|
||||
cask.livecheck.strategy_block.parameters[0] == [:opt, :items]
|
||||
|
||||
content = Homebrew::Livecheck::Strategy.page_content(cask.livecheck.url)[:content]
|
||||
return if content.blank?
|
||||
|
||||
begin
|
||||
items = Homebrew::Livecheck::Strategy::Sparkle.sort_items(
|
||||
Homebrew::Livecheck::Strategy::Sparkle.filter_items(
|
||||
Homebrew::Livecheck::Strategy::Sparkle.items_from_content(content),
|
||||
),
|
||||
)
|
||||
rescue
|
||||
return
|
||||
end
|
||||
return if items.blank?
|
||||
|
||||
normalize_min_os(items[0]&.minimum_system_version)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(MacOSVersion)) }
|
||||
def cask_bundle_min_os
|
||||
return unless online?
|
||||
|
||||
min_os = T.let(nil, T.untyped)
|
||||
@staged_path ||= T.let(cask.staged_path, T.nilable(Pathname))
|
||||
|
||||
extract_artifacts do |artifacts, tmpdir|
|
||||
artifacts.each do |artifact|
|
||||
next if artifact.is_a?(Artifact::Installer)
|
||||
|
||||
artifact_path = artifact.is_a?(Artifact::Pkg) ? artifact.path : artifact.source
|
||||
path = tmpdir/artifact_path.relative_path_from(cask.staged_path)
|
||||
|
||||
# Handle .pkg artifacts by expanding and checking Distribution file
|
||||
if artifact.is_a?(Artifact::Pkg)
|
||||
pkg_expanded_dir = tmpdir/"pkg-expanded"
|
||||
begin
|
||||
system_command!("pkgutil", args: ["--expand", path.to_s, pkg_expanded_dir.to_s])
|
||||
|
||||
distribution_file = pkg_expanded_dir/"Distribution"
|
||||
if File.exist?(distribution_file)
|
||||
distribution_content = File.read(distribution_file)
|
||||
if (match = distribution_content.match(/<os-version\s+min="(?<version>[^"]+)"/))
|
||||
min_os = match[:version]
|
||||
break if min_os
|
||||
end
|
||||
end
|
||||
rescue
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
info_plist_paths = Dir.glob("#{path}/**/Contents/Info.plist")
|
||||
|
||||
# Ensure the main `Info.plist` file is checked first, as this can
|
||||
# sometimes use the min_os version from a framework instead
|
||||
if info_plist_paths.delete("#{path}/Contents/Info.plist")
|
||||
info_plist_paths.insert(0, "#{path}/Contents/Info.plist")
|
||||
end
|
||||
|
||||
info_plist_paths.each do |plist_path|
|
||||
next unless File.exist?(plist_path)
|
||||
|
||||
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", plist_path]).plist
|
||||
min_os = plist["LSMinimumSystemVersion"].presence
|
||||
break if min_os
|
||||
|
||||
# Get the app bundle path from the plist path
|
||||
app_bundle_path = Pathname(plist_path).dirname.dirname
|
||||
next unless (main_binary = get_plist_main_binary(app_bundle_path))
|
||||
next if !File.exist?(main_binary) || File.open(main_binary, "rb") { |f| f.read(2) == "#!" }
|
||||
|
||||
require "macho"
|
||||
macho = MachO.open(main_binary)
|
||||
min_os = case macho
|
||||
when MachO::MachOFile
|
||||
[
|
||||
macho[:LC_VERSION_MIN_MACOSX].first&.version_string,
|
||||
macho[:LC_BUILD_VERSION].first&.minos_string,
|
||||
]
|
||||
when MachO::FatFile
|
||||
# Collect requirements by architecture
|
||||
arch_min_os = { arm: [], intel: [] }
|
||||
macho.machos.each do |slice|
|
||||
macos_reqs = [
|
||||
slice[:LC_VERSION_MIN_MACOSX].first&.version_string,
|
||||
slice[:LC_BUILD_VERSION].first&.minos_string,
|
||||
]
|
||||
|
||||
case slice.cputype
|
||||
when *Hardware::CPU::ARM_ARCHS
|
||||
arch_min_os[:arm].concat(macos_reqs)
|
||||
when *Hardware::CPU::INTEL_ARCHS
|
||||
arch_min_os[:intel].concat(macos_reqs)
|
||||
end
|
||||
end
|
||||
|
||||
# Only use the requirements for the current architecture
|
||||
arch_min_os.fetch(Homebrew::SimulateSystem.current_arch, [])
|
||||
end.compact.max
|
||||
break if min_os
|
||||
end
|
||||
break if min_os
|
||||
end
|
||||
end
|
||||
|
||||
normalize_min_os(min_os)
|
||||
end
|
||||
|
||||
sig { params(path: Pathname).returns(T.nilable(String)) }
|
||||
def get_plist_main_binary(path)
|
||||
return unless online?
|
||||
|
||||
plist_path = "#{path}/Contents/Info.plist"
|
||||
return unless File.exist?(plist_path)
|
||||
|
||||
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", plist_path]).plist
|
||||
binary = plist["CFBundleExecutable"].presence
|
||||
return unless binary
|
||||
|
||||
binary_path = "#{path}/Contents/MacOS/#{binary}"
|
||||
|
||||
binary_path if File.exist?(binary_path) && File.executable?(binary_path)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def audit_github_prerelease_version
|
||||
return if (url = cask.url).nil?
|
||||
@@ -1301,6 +1173,143 @@ module Cask
|
||||
add_error error if error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T.nilable(MacOSVersion)) }
|
||||
def cask_sparkle_min_os
|
||||
return unless online?
|
||||
return unless cask.livecheck_defined?
|
||||
return if cask.livecheck.strategy != :sparkle
|
||||
|
||||
# `Sparkle` strategy blocks that use the `items` argument (instead of
|
||||
# `item`) contain arbitrary logic that ignores/overrides the strategy's
|
||||
# sorting, so we can't identify which item would be first/newest here.
|
||||
return if cask.livecheck.strategy_block.present? &&
|
||||
cask.livecheck.strategy_block.parameters[0] == [:opt, :items]
|
||||
|
||||
content = Homebrew::Livecheck::Strategy.page_content(cask.livecheck.url)[:content]
|
||||
return if content.blank?
|
||||
|
||||
begin
|
||||
items = Homebrew::Livecheck::Strategy::Sparkle.sort_items(
|
||||
Homebrew::Livecheck::Strategy::Sparkle.filter_items(
|
||||
Homebrew::Livecheck::Strategy::Sparkle.items_from_content(content),
|
||||
),
|
||||
)
|
||||
rescue
|
||||
return
|
||||
end
|
||||
return if items.blank?
|
||||
|
||||
normalize_min_os(items[0]&.minimum_system_version)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(MacOSVersion)) }
|
||||
def cask_bundle_min_os
|
||||
return unless online?
|
||||
|
||||
min_os = T.let(nil, T.untyped)
|
||||
@staged_path ||= T.let(cask.staged_path, T.nilable(Pathname))
|
||||
|
||||
extract_artifacts do |artifacts, tmpdir|
|
||||
artifacts.each do |artifact|
|
||||
next if artifact.is_a?(Artifact::Installer)
|
||||
|
||||
artifact_path = artifact.is_a?(Artifact::Pkg) ? artifact.path : artifact.source
|
||||
path = tmpdir/artifact_path.relative_path_from(cask.staged_path)
|
||||
|
||||
# Handle .pkg artifacts by expanding and checking Distribution file
|
||||
if artifact.is_a?(Artifact::Pkg)
|
||||
pkg_expanded_dir = tmpdir/"pkg-expanded"
|
||||
begin
|
||||
system_command!("pkgutil", args: ["--expand", path.to_s, pkg_expanded_dir.to_s])
|
||||
|
||||
distribution_file = pkg_expanded_dir/"Distribution"
|
||||
if File.exist?(distribution_file)
|
||||
distribution_content = File.read(distribution_file)
|
||||
if (match = distribution_content.match(/<os-version\s+min="(?<version>[^"]+)"/))
|
||||
min_os = match[:version]
|
||||
break if min_os
|
||||
end
|
||||
end
|
||||
rescue
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
info_plist_paths = Dir.glob("#{path}/**/Contents/Info.plist")
|
||||
|
||||
# Ensure the main `Info.plist` file is checked first, as this can
|
||||
# sometimes use the min_os version from a framework instead
|
||||
if info_plist_paths.delete("#{path}/Contents/Info.plist")
|
||||
info_plist_paths.insert(0, "#{path}/Contents/Info.plist")
|
||||
end
|
||||
|
||||
info_plist_paths.each do |plist_path|
|
||||
next unless File.exist?(plist_path)
|
||||
|
||||
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", plist_path]).plist
|
||||
min_os = plist["LSMinimumSystemVersion"].presence
|
||||
break if min_os
|
||||
|
||||
# Get the app bundle path from the plist path
|
||||
app_bundle_path = Pathname(plist_path).dirname.dirname
|
||||
next unless (main_binary = get_plist_main_binary(app_bundle_path))
|
||||
next if !File.exist?(main_binary) || File.open(main_binary, "rb") { |f| f.read(2) == "#!" }
|
||||
|
||||
require "macho"
|
||||
macho = MachO.open(main_binary)
|
||||
min_os = case macho
|
||||
when MachO::MachOFile
|
||||
[
|
||||
macho[:LC_VERSION_MIN_MACOSX].first&.version_string,
|
||||
macho[:LC_BUILD_VERSION].first&.minos_string,
|
||||
]
|
||||
when MachO::FatFile
|
||||
# Collect requirements by architecture
|
||||
arch_min_os = { arm: [], intel: [] }
|
||||
macho.machos.each do |slice|
|
||||
macos_reqs = [
|
||||
slice[:LC_VERSION_MIN_MACOSX].first&.version_string,
|
||||
slice[:LC_BUILD_VERSION].first&.minos_string,
|
||||
]
|
||||
|
||||
case slice.cputype
|
||||
when *Hardware::CPU::ARM_ARCHS
|
||||
arch_min_os[:arm].concat(macos_reqs)
|
||||
when *Hardware::CPU::INTEL_ARCHS
|
||||
arch_min_os[:intel].concat(macos_reqs)
|
||||
end
|
||||
end
|
||||
|
||||
# Only use the requirements for the current architecture
|
||||
arch_min_os.fetch(Homebrew::SimulateSystem.current_arch, [])
|
||||
end.compact.max
|
||||
break if min_os
|
||||
end
|
||||
break if min_os
|
||||
end
|
||||
end
|
||||
|
||||
normalize_min_os(min_os)
|
||||
end
|
||||
|
||||
sig { params(path: Pathname).returns(T.nilable(String)) }
|
||||
def get_plist_main_binary(path)
|
||||
return unless online?
|
||||
|
||||
plist_path = "#{path}/Contents/Info.plist"
|
||||
return unless File.exist?(plist_path)
|
||||
|
||||
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", plist_path]).plist
|
||||
binary = plist["CFBundleExecutable"].presence
|
||||
return unless binary
|
||||
|
||||
binary_path = "#{path}/Contents/MacOS/#{binary}"
|
||||
|
||||
binary_path if File.exist?(binary_path) && File.executable?(binary_path)
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
url_to_check: T.any(String, URL),
|
||||
@@ -1369,6 +1378,11 @@ module Cask
|
||||
URI(cask.url.to_s).host
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def verified_present?
|
||||
cask.url&.verified.present?
|
||||
end
|
||||
|
||||
sig { returns(Tap) }
|
||||
def core_tap
|
||||
@core_tap ||= T.let(CoreTap.instance, T.nilable(Tap))
|
||||
|
||||
@@ -121,11 +121,14 @@ module Cask
|
||||
|
||||
@default_config = T.let(config || Config.new, Config)
|
||||
|
||||
@config = T.let(if config_path.exist?
|
||||
Config.from_json(File.read(config_path), ignore_invalid_keys: true)
|
||||
else
|
||||
@default_config
|
||||
end, Config)
|
||||
@config = T.let(
|
||||
if config_path.exist?
|
||||
Config.from_json(File.read(config_path), ignore_invalid_keys: true)
|
||||
else
|
||||
@default_config
|
||||
end,
|
||||
Config,
|
||||
)
|
||||
refresh
|
||||
end
|
||||
|
||||
@@ -613,8 +616,6 @@ module Cask
|
||||
if dsl!.on_system_blocks_exist?
|
||||
begin
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
next if bottle_tag.linux? && dsl!.os.nil? && !dsl!.sha256_set_for_linux?
|
||||
|
||||
macos_requirements = [depends_on.macos, depends_on.maximum_macos].compact
|
||||
next if bottle_tag.macos? &&
|
||||
macos_requirements.present? &&
|
||||
|
||||
@@ -197,6 +197,7 @@ module Cask
|
||||
@auto_updates_set_in_block = T.let(false, T::Boolean)
|
||||
@autobump = T.let(true, T::Boolean)
|
||||
@called_in_on_system_block = T.let(false, T::Boolean)
|
||||
@called_in_on_os_block = T.let(false, T::Boolean)
|
||||
@cask = cask
|
||||
@caveats = T.let(DSL::Caveats.new(cask), DSL::Caveats)
|
||||
@conflicts_with = T.let(nil, T.nilable(DSL::ConflictsWith))
|
||||
@@ -236,7 +237,6 @@ module Cask
|
||||
@os_set_in_block = T.let(false, T::Boolean)
|
||||
@rename = T.let([], T::Array[DSL::Rename])
|
||||
@sha256 = T.let(nil, T.nilable(T.any(Checksum, Symbol)))
|
||||
@sha256_set_for_linux = T.let(false, T::Boolean)
|
||||
@sha256_set_in_block = T.let(false, T::Boolean)
|
||||
@staged_path = T.let(nil, T.nilable(Pathname))
|
||||
@token = T.let(cask.token, String)
|
||||
@@ -264,9 +264,6 @@ module Cask
|
||||
sig { returns(T::Boolean) }
|
||||
def on_os_blocks_exist? = @on_os_blocks_exist
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def sha256_set_for_linux? = @sha256_set_for_linux
|
||||
|
||||
# Specifies the cask's name.
|
||||
#
|
||||
# NOTE: Multiple names can be specified.
|
||||
@@ -554,7 +551,6 @@ module Cask
|
||||
if arm.present? || x86_64.present? || x86_64_linux.present? || arm64_linux.present?
|
||||
@on_system_blocks_exist = true
|
||||
end
|
||||
@sha256_set_for_linux = true if x86_64_linux.present? || arm64_linux.present?
|
||||
|
||||
val = arg || on_system_conditional(
|
||||
macos: on_arch_conditional(arm:, intel: x86_64),
|
||||
@@ -570,13 +566,16 @@ module Cask
|
||||
# running OS, matching `sha256` inside an `on_macos`/`on_linux` block;
|
||||
# `depends_on` governs whether the cask is usable there. A checksum
|
||||
# declared for the running OS but missing the running architecture
|
||||
# still raises.
|
||||
# still raises on the real system but is nil under simulation so
|
||||
# API variations can be generated for the missing architecture.
|
||||
running_os_checksums = if OnSystem.os_condition_met?(:linux)
|
||||
[x86_64_linux, arm64_linux]
|
||||
else
|
||||
[arm, x86_64]
|
||||
end
|
||||
raise CaskInvalidError.new(cask, "invalid 'sha256' value: nil") if running_os_checksums.any?(&:present?)
|
||||
if running_os_checksums.any?(&:present?) && !Homebrew::SimulateSystem.simulating?
|
||||
raise CaskInvalidError.new(cask, "invalid 'sha256' value: nil")
|
||||
end
|
||||
|
||||
nil
|
||||
else
|
||||
@@ -653,7 +652,10 @@ module Cask
|
||||
return @depends_on if kwargs.empty?
|
||||
|
||||
begin
|
||||
@depends_on.load(kwargs, set_in_block: @called_in_on_system_block)
|
||||
# Only OS blocks scope a dependency to one OS: `on_arm`/`on_intel`
|
||||
# blocks are evaluated on every OS, so a macOS dependency inside one
|
||||
# applies everywhere and marks the cask macOS-only.
|
||||
@depends_on.load(kwargs, set_in_block: @called_in_on_system_block, os_scoped: @called_in_on_os_block)
|
||||
rescue RuntimeError => e
|
||||
raise CaskInvalidError.new(cask, e)
|
||||
end
|
||||
@@ -858,6 +860,7 @@ module Cask
|
||||
[klass.dsl_key, klass.uninstall_dsl_key].each do |dsl_key|
|
||||
define_method(dsl_key) do |&block|
|
||||
T.bind(self, DSL)
|
||||
# odeprecated "`#{dsl_key}`", "`#{dsl_key}_steps`"
|
||||
artifacts.add(klass.new(cask, dsl_key => block))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,6 +50,7 @@ module Cask
|
||||
@macos = T.let(nil, T.nilable(MacOSRequirement))
|
||||
@maximum_macos = T.let(nil, T.nilable(MacOSRequirement))
|
||||
@linux = T.let(nil, T.nilable(LinuxRequirement))
|
||||
@macos_required = T.let(false, T::Boolean)
|
||||
@macos_bare_set_top_level = T.let(false, T::Boolean)
|
||||
@macos_version_set_top_level = T.let(false, T::Boolean)
|
||||
@maximum_macos_set_top_level = T.let(false, T::Boolean)
|
||||
@@ -70,20 +71,22 @@ module Cask
|
||||
params(
|
||||
pairs: T::Hash[Symbol, T.any(String, Symbol, T::Array[T.any(String, Symbol)])],
|
||||
set_in_block: T::Boolean,
|
||||
os_scoped: T::Boolean,
|
||||
).void
|
||||
}
|
||||
def load(pairs, set_in_block: false)
|
||||
def load(pairs, set_in_block: false, os_scoped: false)
|
||||
pairs.each do |key, value|
|
||||
raise "invalid depends_on key: '#{key.inspect}'" unless VALID_KEYS.include?(key)
|
||||
|
||||
previous_macos = @macos if key == :macos
|
||||
__getobj__[key] = case key
|
||||
case key
|
||||
when :macos, :maximum_macos
|
||||
send(:"#{key}=", *value, set_in_block:)
|
||||
else
|
||||
send(:"#{key}=", *value)
|
||||
end
|
||||
record_os_requirement(key, set_in_block:)
|
||||
__getobj__[key] = public_send(key)
|
||||
record_os_requirement(key, set_in_block:, os_scoped:)
|
||||
next if key != :macos
|
||||
next if value != :any
|
||||
next unless previous_macos&.version_specified?
|
||||
@@ -93,24 +96,24 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(args: String).returns(T::Array[String]) }
|
||||
sig { params(args: String).void }
|
||||
def formula=(*args)
|
||||
formula.concat(args)
|
||||
end
|
||||
|
||||
sig { params(args: String).returns(T::Array[String]) }
|
||||
sig { params(args: String).void }
|
||||
def cask=(*args)
|
||||
cask.concat(args)
|
||||
end
|
||||
|
||||
sig { params(args: T.any(String, Symbol), set_in_block: T::Boolean).returns(T.nilable(MacOSRequirement)) }
|
||||
sig { params(args: T.any(String, Symbol), set_in_block: T::Boolean).void }
|
||||
def macos=(*args, set_in_block: false)
|
||||
@macos = MacOSRequirement.parse(args, comparator: ">=")
|
||||
rescue MacOSVersion::Error, TypeError => e
|
||||
raise "invalid 'depends_on macos' value: #{e}"
|
||||
end
|
||||
|
||||
sig { params(args: T.any(String, Symbol), set_in_block: T::Boolean).returns(T.nilable(MacOSRequirement)) }
|
||||
sig { params(args: T.any(String, Symbol), set_in_block: T::Boolean).void }
|
||||
def maximum_macos=(*args, set_in_block: false)
|
||||
raise "invalid 'depends_on maximum_macos' value: only a single macOS version is allowed" if args.count != 1
|
||||
|
||||
@@ -126,7 +129,7 @@ module Cask
|
||||
@maximum_macos = maximum_macos
|
||||
end
|
||||
|
||||
sig { params(args: T.any(String, Symbol)).returns(T.nilable(LinuxRequirement)) }
|
||||
sig { params(args: T.any(String, Symbol)).void }
|
||||
def linux=(*args)
|
||||
raise "Only a single 'depends_on linux' is allowed." if @linux
|
||||
raise "invalid 'depends_on linux' value: #{args.first.inspect}" if args.first != :any
|
||||
@@ -134,7 +137,7 @@ module Cask
|
||||
@linux = LinuxRequirement.new
|
||||
end
|
||||
|
||||
sig { params(args: Symbol).returns(T::Array[T::Hash[Symbol, T.any(Symbol, Integer)]]) }
|
||||
sig { params(args: Symbol).void }
|
||||
def arch=(*args)
|
||||
@arch ||= []
|
||||
arches = args.map do |elt|
|
||||
@@ -153,26 +156,24 @@ module Cask
|
||||
def present? = !empty?
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def requires_macos?
|
||||
@macos_bare_set_top_level || @macos_version_set_top_level || @maximum_macos_set_top_level
|
||||
end
|
||||
def requires_macos? = @macos_required
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def requires_linux? = @linux_set_top_level
|
||||
|
||||
sig { params(key: Symbol, set_in_block: T::Boolean).void }
|
||||
def record_os_requirement(key, set_in_block:)
|
||||
sig { params(key: Symbol, set_in_block: T::Boolean, os_scoped: T::Boolean).void }
|
||||
def record_os_requirement(key, set_in_block:, os_scoped:)
|
||||
case key
|
||||
when :macos
|
||||
macos = @macos
|
||||
raise "invalid 'depends_on macos' value" unless macos
|
||||
|
||||
record_macos_requirement(macos, set_in_block:)
|
||||
record_macos_requirement(macos, set_in_block:, os_scoped:)
|
||||
when :maximum_macos
|
||||
maximum_macos = @maximum_macos
|
||||
raise "invalid 'depends_on maximum_macos' value" unless maximum_macos
|
||||
|
||||
record_macos_requirement(maximum_macos, set_in_block:)
|
||||
record_macos_requirement(maximum_macos, set_in_block:, os_scoped:)
|
||||
when :linux
|
||||
return if set_in_block
|
||||
raise "`depends_on :linux` cannot be combined with `depends_on macos:`" if requires_macos?
|
||||
@@ -181,8 +182,13 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(requirement: MacOSRequirement, set_in_block: T::Boolean).void }
|
||||
def record_macos_requirement(requirement, set_in_block:)
|
||||
sig { params(requirement: MacOSRequirement, set_in_block: T::Boolean, os_scoped: T::Boolean).void }
|
||||
def record_macos_requirement(requirement, set_in_block:, os_scoped:)
|
||||
# `on_arm`/`on_intel` blocks are evaluated on every OS, so a macOS
|
||||
# dependency inside one applies everywhere; only an OS block scopes a
|
||||
# dependency to macOS alone.
|
||||
@macos_required = true unless os_scoped
|
||||
|
||||
return if set_in_block
|
||||
|
||||
raise "`depends_on :linux` cannot be combined with `depends_on macos:`" if requires_linux?
|
||||
|
||||
@@ -358,6 +358,7 @@ on_request: true)
|
||||
sig { void }
|
||||
def check_requirements
|
||||
check_stanza_os_requirements
|
||||
check_supported_system
|
||||
check_macos_requirements
|
||||
check_arch_requirements
|
||||
end
|
||||
@@ -369,6 +370,20 @@ on_request: true)
|
||||
raise CaskError, "#{@cask}: This cask requires Linux."
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_supported_system
|
||||
# Audited casks always have an activatable artifact for the systems they
|
||||
# support, so API data without one means this system is unsupported.
|
||||
# Source loads keep working for unaudited casks, e.g. naked containers.
|
||||
return unless @cask.loaded_from_api?
|
||||
return if @cask.artifacts.any? do |artifact|
|
||||
artifact.respond_to?(:install_phase) || artifact.is_a?(Artifact::StageOnly)
|
||||
end
|
||||
|
||||
os_name = Homebrew::SimulateSystem.simulating_or_running_on_macos? ? "macOS" : "Linux"
|
||||
raise CaskError, "#{@cask}: This cask is not available on #{os_name}."
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def check_macos_requirements
|
||||
macos_requirement = [@cask.depends_on.macos, @cask.depends_on.maximum_macos].compact.find { !it.satisfied? }
|
||||
|
||||
@@ -14,12 +14,16 @@ module Cask
|
||||
sig { returns(T.nilable(T::Array[T.untyped])) }
|
||||
attr_accessor :uninstall_artifacts
|
||||
|
||||
sig { params(attributes: T.any(T::Hash[String, T.untyped], T::Hash[Symbol, T.untyped])).void }
|
||||
def initialize(attributes = {})
|
||||
@uninstall_flight_blocks = T.let(nil, T.nilable(T::Boolean))
|
||||
@uninstall_artifacts = T.let(nil, T.nilable(T::Array[T.untyped]))
|
||||
sig {
|
||||
params(uninstall_flight_blocks: T.nilable(T::Boolean),
|
||||
uninstall_artifacts: T.nilable(T::Array[T.untyped]),
|
||||
rest: T.untyped).void
|
||||
}
|
||||
def initialize(uninstall_flight_blocks: nil, uninstall_artifacts: nil, **rest)
|
||||
@uninstall_flight_blocks = uninstall_flight_blocks
|
||||
@uninstall_artifacts = uninstall_artifacts
|
||||
|
||||
super
|
||||
super(**rest)
|
||||
end
|
||||
|
||||
# Instantiates a {Tab} for a new installation of a cask.
|
||||
|
||||
@@ -35,7 +35,7 @@ module Cask
|
||||
attr_reader :using
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_reader :tag, :branch, :revision, :only_path
|
||||
attr_reader :tag, :branch, :revision, :only_path, :verified
|
||||
|
||||
extend Forwardable
|
||||
|
||||
@@ -73,6 +73,7 @@ module Cask
|
||||
header = Array(header) unless header.nil?
|
||||
|
||||
specs = {}
|
||||
specs[:verified] = @verified = T.let(verified, T.nilable(String))
|
||||
specs[:using] = @using = T.let(using, T.nilable(T.any(T::Class[AbstractDownloadStrategy], Symbol)))
|
||||
specs[:tag] = @tag = T.let(tag, T.nilable(String))
|
||||
specs[:branch] = @branch = T.let(branch, T.nilable(String))
|
||||
|
||||
@@ -552,7 +552,7 @@ module Homebrew
|
||||
(formula.requirements.any? { |requirement| self.class.requirement_for_other_os?(requirement) } ||
|
||||
(stable.present? ? stable.bottled? && formula.pour_bottle? : formula.head.blank?))))
|
||||
|
||||
deps = formula.deps.send(type).uniq
|
||||
deps = formula.deps.public_send(type).uniq
|
||||
next if deps.empty?
|
||||
|
||||
tab_deps = (kegs.any? && type != "build") ? tab_runtime_deps : nil
|
||||
|
||||
@@ -38,7 +38,7 @@ module Homebrew
|
||||
Homebrew.with_no_api_env do
|
||||
if args.syntax? && args.no_named?
|
||||
scan_files = "#{HOMEBREW_LIBRARY_PATH}/**/*.rb"
|
||||
ruby_files = Dir.glob(scan_files).grep_v(%r{/(vendor)/})
|
||||
ruby_files = Dir.glob(scan_files).grep_v(%r{/(vendor)/}).map { Pathname(it) }
|
||||
|
||||
Homebrew.failed = true unless Readall.valid_ruby_syntax?(ruby_files)
|
||||
end
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "shell_command"
|
||||
|
||||
module Homebrew
|
||||
module Cmd
|
||||
class SetupSandbox < AbstractCommand
|
||||
include ShellCommand
|
||||
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Run any necessary commands to setup the Homebrew sandbox.
|
||||
Must be run with `sudo`. Currently a no-op on non-Linux.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,67 +0,0 @@
|
||||
# Documentation defined in Library/Homebrew/cmd/setup-sandbox.rb
|
||||
|
||||
# This Bubblewrap installation mirrors the package manager approaches in
|
||||
# https://github.com/Homebrew/install and the Homebrew formula fallback in
|
||||
# `ensure_sandbox_installed!` in Library/Homebrew/extend/os/linux/sandbox.rb.
|
||||
|
||||
# `sudo` strips `GITHUB_ACTIONS`, so also detect the runner via `/proc/1/cgroup`
|
||||
# like `check-run-command-as-root` in Library/Homebrew/brew.sh does.
|
||||
homebrew-on-github-actions() {
|
||||
[[ -n "${GITHUB_ACTIONS}" ]] && return 0
|
||||
grep -q "actions_job" /proc/1/cgroup &>/dev/null
|
||||
}
|
||||
|
||||
homebrew-setup-sandbox() {
|
||||
# The sandbox sysctls and Bubblewrap are Linux-only.
|
||||
[[ -z "${HOMEBREW_LINUX}" ]] && return 0
|
||||
|
||||
if homebrew-on-github-actions && ! command -v bwrap &>/dev/null
|
||||
then
|
||||
if command -v apt-get &>/dev/null
|
||||
then
|
||||
apt-get install --yes bubblewrap
|
||||
elif command -v dnf &>/dev/null
|
||||
then
|
||||
dnf install --assumeyes bubblewrap
|
||||
elif command -v yum &>/dev/null
|
||||
then
|
||||
yum install --assumeyes bubblewrap
|
||||
elif command -v pacman &>/dev/null
|
||||
then
|
||||
pacman --sync --noconfirm bubblewrap
|
||||
elif command -v apk &>/dev/null
|
||||
then
|
||||
apk add bubblewrap
|
||||
fi
|
||||
fi
|
||||
|
||||
# These settings mirror SANDBOX_SYSCTL_SETTINGS in
|
||||
# Library/Homebrew/extend/os/linux/sandbox.rb; keep both in sync.
|
||||
local proc_sys_root="${HOMEBREW_PROC_SYS:-/proc/sys}"
|
||||
local sysctl_value
|
||||
local unprivileged_userns_clone_sysctl="${proc_sys_root}/kernel/unprivileged_userns_clone"
|
||||
if [[ -e "${unprivileged_userns_clone_sysctl}" ]] &&
|
||||
sysctl_value="$(sysctl -n "kernel.unprivileged_userns_clone")" &&
|
||||
[[ "${sysctl_value}" != "1" ]] &&
|
||||
[[ -w "${unprivileged_userns_clone_sysctl}" ]]
|
||||
then
|
||||
sysctl -w kernel.unprivileged_userns_clone=1 || true
|
||||
fi
|
||||
local max_user_namespaces_sysctl="${proc_sys_root}/user/max_user_namespaces"
|
||||
if [[ -e "${max_user_namespaces_sysctl}" ]] &&
|
||||
sysctl_value="$(sysctl -n "user.max_user_namespaces")" &&
|
||||
[[ "${sysctl_value}" -lt 28633 ]] &&
|
||||
[[ -w "${max_user_namespaces_sysctl}" ]]
|
||||
then
|
||||
sysctl -w user.max_user_namespaces=28633 || true
|
||||
fi
|
||||
|
||||
local apparmor_restrict_unprivileged_userns_sysctl="${proc_sys_root}/kernel/apparmor_restrict_unprivileged_userns"
|
||||
if [[ -e "${apparmor_restrict_unprivileged_userns_sysctl}" ]] &&
|
||||
sysctl_value="$(sysctl -n "kernel.apparmor_restrict_unprivileged_userns")" &&
|
||||
[[ "${sysctl_value}" != "0" ]] &&
|
||||
[[ -w "${apparmor_restrict_unprivileged_userns_sysctl}" ]]
|
||||
then
|
||||
sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
|
||||
fi
|
||||
}
|
||||
@@ -39,6 +39,8 @@ class ReporterHub
|
||||
|
||||
sig { params(auto_update: T::Boolean).void }
|
||||
def dump(auto_update: false)
|
||||
return if auto_update && Homebrew::EnvConfig.auto_update_quiet?
|
||||
|
||||
unless Homebrew::EnvConfig.no_update_report_new?
|
||||
dump_new_formula_report
|
||||
dump_new_cask_report
|
||||
|
||||
@@ -22,6 +22,16 @@ module Homebrew
|
||||
flag "--brewfile",
|
||||
description: "Check formulae listed in a Brewfile. " \
|
||||
"Defaults to `./Brewfile`; use `--brewfile=`<path> to specify another."
|
||||
switch "--fix-available",
|
||||
description: "Only report vulnerabilities that have a fix available. " \
|
||||
"Note that this may exclude vulnerabilities with fixes available " \
|
||||
"if we cannot determine that the fix is included in the version " \
|
||||
"under consideration."
|
||||
switch "--no-fix-available",
|
||||
description: "Only report vulnerabilities that do not have a fix available. " \
|
||||
"Note that this may include vulnerabilities with fixes available " \
|
||||
"if we cannot determine that the fix is included in the version " \
|
||||
"under consideration."
|
||||
flag "-s", "--severity=",
|
||||
description: "Only report findings at or above: `low`, `medium`, `high`, `critical`."
|
||||
flag "-m", "--max-summary=",
|
||||
@@ -29,6 +39,8 @@ module Homebrew
|
||||
switch "-j", "--json",
|
||||
description: "Output JSON."
|
||||
|
||||
conflicts "--fix-available", "--no-fix-available"
|
||||
|
||||
named_args :formula
|
||||
end
|
||||
|
||||
@@ -43,6 +55,8 @@ module Homebrew
|
||||
formulae,
|
||||
ignore_patches: !args.no_ignore_patches?,
|
||||
min_severity: severity,
|
||||
only_fixed: args.fix_available?,
|
||||
except_fixed: args.no_fix_available?,
|
||||
).scan
|
||||
|
||||
if args.json?
|
||||
|
||||
@@ -99,15 +99,11 @@ class DependencyCollector
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def glibc_dep_if_needed(related_formula_names); end
|
||||
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def bubblewrap_dep_if_needed(related_formula_names); end
|
||||
|
||||
# Names implicitly added to any formula's deps right now, reusing the same checks
|
||||
# `Formula#add_global_deps_to_spec` uses to inject them onto a real formula.
|
||||
sig { returns(T::Set[String]) }
|
||||
def implicit_dependency_names
|
||||
[
|
||||
bubblewrap_dep_if_needed(Set.new),
|
||||
gcc_dep_if_needed(Set.new),
|
||||
glibc_dep_if_needed(Set.new),
|
||||
].compact.to_set(&:name)
|
||||
|
||||
@@ -44,8 +44,7 @@ module Homebrew
|
||||
titleized_repository = tap.repository.dup
|
||||
titleized_user[0] = T.must(titleized_user[0]).upcase
|
||||
titleized_repository[0] = T.must(titleized_repository[0]).upcase
|
||||
# Duplicate assignment to silence `assigned but unused variable` warning
|
||||
root_url = root_url = GitHubPackages.root_url(tap.user, "homebrew-#{tap.repository}") if args.github_packages?
|
||||
root_url = GitHubPackages.root_url(tap.user, "homebrew-#{tap.repository}") if args.github_packages?
|
||||
|
||||
(tap.path/"Formula").mkpath
|
||||
|
||||
@@ -163,8 +162,8 @@ module Homebrew
|
||||
YAML
|
||||
end
|
||||
# Pick a random 5 minute block in which to execute the autobump action to avoid peak GitHub loads
|
||||
hour = Random.rand(23)
|
||||
minute = Random.rand(11) * 5
|
||||
hour = Random.rand(24)
|
||||
minute = Random.rand(12) * 5
|
||||
workflow.gsub!("this will be changed later and randomised by brew tap-new") do
|
||||
"Every day at #{hour}:#{minute} UTC"
|
||||
end
|
||||
|
||||
@@ -82,7 +82,6 @@ module Homebrew
|
||||
|
||||
exec_args << "--HEAD" if f.head?
|
||||
|
||||
Sandbox.ensure_sandbox_installed!
|
||||
if Sandbox.available?
|
||||
sandbox = Sandbox.new
|
||||
f.logs.mkpath
|
||||
|
||||
@@ -559,6 +559,14 @@ module Homebrew
|
||||
@default_download_queue ||= T.let(DownloadQueue.new, T.nilable(DownloadQueue))
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def self.reset_default_download_queue
|
||||
# Skip `shutdown` for a leaked RSpec double, which cannot receive
|
||||
# messages outside the per-example rspec-mocks lifecycle.
|
||||
@default_download_queue.shutdown if @default_download_queue.is_a?(DownloadQueue)
|
||||
@default_download_queue = nil
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def self.shutdown_default_download_queue
|
||||
@default_download_queue&.shutdown
|
||||
|
||||
@@ -96,6 +96,11 @@ module Homebrew
|
||||
replacement: "the default behaviour",
|
||||
odeprecated: true,
|
||||
},
|
||||
HOMEBREW_AUTO_UPDATE_QUIET: {
|
||||
description: "If set, the auto-update run before commands like `brew install`, `brew upgrade` or " \
|
||||
"`brew tap` will not show information about new, outdated or deleted formulae and casks.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_AUTO_UPDATE_SECS: {
|
||||
description: "Run `brew update` once every `$HOMEBREW_AUTO_UPDATE_SECS` seconds before some commands, " \
|
||||
"e.g. `brew install`, `brew upgrade` or `brew tap`. Alternatively, " \
|
||||
@@ -658,7 +663,7 @@ module Homebrew
|
||||
default: true,
|
||||
},
|
||||
HOMEBREW_SANDBOX_LINUX: {
|
||||
description: "The `bwrap`(1) sandbox is the default for formula installation and testing " \
|
||||
description: "The Landlock sandbox is the default for formula installation and testing " \
|
||||
"on Linux unless `$HOMEBREW_NO_SANDBOX_LINUX` is set.",
|
||||
boolean: :set,
|
||||
disabled_by: :HOMEBREW_NO_SANDBOX_LINUX,
|
||||
@@ -968,7 +973,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def cask_opts_binaries?
|
||||
def self.cask_opts_binaries?
|
||||
cask_opts.reverse_each do |opt|
|
||||
return true if opt == "--binaries"
|
||||
return false if opt == "--no-binaries"
|
||||
|
||||
@@ -121,7 +121,7 @@ module SharedEnvExtension
|
||||
|
||||
sig { params(key: String, path: T.any(String, Pathname)).void }
|
||||
def append_path(key, path)
|
||||
self[key] = PATH.new(self[key]).append(path)
|
||||
self[key] = PATH.new(self[key]).append(path).to_s
|
||||
end
|
||||
|
||||
sig { params(rustflags: String).void }
|
||||
@@ -140,7 +140,7 @@ module SharedEnvExtension
|
||||
def prepend_path(key, path)
|
||||
return if %w[/usr/bin /bin /usr/sbin /sbin].include? path.to_s
|
||||
|
||||
self[key] = PATH.new(self[key]).prepend(path)
|
||||
self[key] = PATH.new(self[key]).prepend(path).to_s
|
||||
end
|
||||
|
||||
sig { params(key: String, path: T.any(String, Pathname)).void }
|
||||
@@ -285,7 +285,7 @@ module SharedEnvExtension
|
||||
end
|
||||
if gfortran
|
||||
puts "This may be changed by setting the `$FC` environment variable."
|
||||
self["FC"] = self["F77"] = gfortran
|
||||
self["FC"] = self["F77"] = gfortran.to_s
|
||||
flags = FC_FLAG_VARS
|
||||
end
|
||||
end
|
||||
@@ -352,12 +352,12 @@ module SharedEnvExtension
|
||||
sig { params(_flags: T::Array[String], _map: T::Hash[Symbol, String]).void }
|
||||
def set_cpu_flags(_flags, _map = {}); end
|
||||
|
||||
sig { params(val: T.any(String, Pathname)).returns(String) }
|
||||
sig { params(val: T.any(String, Pathname)).void }
|
||||
def cc=(val)
|
||||
self["CC"] = self["OBJC"] = val.to_s
|
||||
end
|
||||
|
||||
sig { params(val: T.any(String, Pathname)).returns(String) }
|
||||
sig { params(val: T.any(String, Pathname)).void }
|
||||
def cxx=(val)
|
||||
self["CXX"] = self["OBJCXX"] = val.to_s
|
||||
end
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# typed: strict
|
||||
|
||||
module SharedEnvExtension
|
||||
# Overload to allow `PATH` values.
|
||||
sig {
|
||||
type_parameters(:U).params(
|
||||
key: String,
|
||||
value: T.all(T.type_parameter(:U), T.nilable(T.any(String, Pathname, PATH))),
|
||||
).returns(T.type_parameter(:U))
|
||||
}
|
||||
def []=(key, value); end
|
||||
end
|
||||
@@ -31,7 +31,7 @@ module Stdenv
|
||||
|
||||
# Set the default pkg-config search path, overriding the built-in paths
|
||||
# Anything in PKG_CONFIG_PATH is searched before paths in this variable
|
||||
self["PKG_CONFIG_LIBDIR"] = determine_pkg_config_libdir
|
||||
self["PKG_CONFIG_LIBDIR"] = determine_pkg_config_libdir&.to_s
|
||||
|
||||
self["MAKEFLAGS"] = "-j#{make_jobs}"
|
||||
self["RUSTC_WRAPPER"] = "#{HOMEBREW_SHIMS_PATH}/shared/rustc_wrapper"
|
||||
|
||||
@@ -82,9 +82,9 @@ module Superenv
|
||||
self["MAKEFLAGS"] ||= "-j#{determine_make_jobs}"
|
||||
self["RUSTC_WRAPPER"] = "#{HOMEBREW_SHIMS_PATH}/shared/rustc_wrapper"
|
||||
self["HOMEBREW_RUSTFLAGS"] = Hardware.rustflags_target_cpu(effective_arch)
|
||||
self["PATH"] = determine_path
|
||||
self["PKG_CONFIG_PATH"] = determine_pkg_config_path
|
||||
self["PKG_CONFIG_LIBDIR"] = determine_pkg_config_libdir || ""
|
||||
self["PATH"] = determine_path&.to_s
|
||||
self["PKG_CONFIG_PATH"] = determine_pkg_config_path&.to_s
|
||||
self["PKG_CONFIG_LIBDIR"] = (determine_pkg_config_libdir || "").to_s
|
||||
self["HOMEBREW_CCCFG"] = determine_cccfg
|
||||
self["HOMEBREW_OPTIMIZATION_LEVEL"] = compiler.match?(GNU_GCC_REGEXP) ? "O2" : "Os"
|
||||
self["HOMEBREW_BREW_FILE"] = HOMEBREW_BREW_FILE.to_s
|
||||
@@ -94,17 +94,17 @@ module Superenv
|
||||
self["HOMEBREW_TEMP"] = HOMEBREW_TEMP.to_s
|
||||
self["HOMEBREW_OPTFLAGS"] = determine_optflags
|
||||
self["HOMEBREW_MAKE_JOBS"] = determine_make_jobs.to_s
|
||||
self["CMAKE_PREFIX_PATH"] = determine_cmake_prefix_path
|
||||
self["CMAKE_FRAMEWORK_PATH"] = determine_cmake_frameworks_path
|
||||
self["CMAKE_INCLUDE_PATH"] = determine_cmake_include_path
|
||||
self["CMAKE_LIBRARY_PATH"] = determine_cmake_library_path
|
||||
self["ACLOCAL_PATH"] = determine_aclocal_path
|
||||
self["CMAKE_PREFIX_PATH"] = determine_cmake_prefix_path&.to_s
|
||||
self["CMAKE_FRAMEWORK_PATH"] = determine_cmake_frameworks_path&.to_s
|
||||
self["CMAKE_INCLUDE_PATH"] = determine_cmake_include_path&.to_s
|
||||
self["CMAKE_LIBRARY_PATH"] = determine_cmake_library_path&.to_s
|
||||
self["ACLOCAL_PATH"] = determine_aclocal_path&.to_s
|
||||
self["M4"] = "#{HOMEBREW_PREFIX}/opt/m4/bin/m4" if deps.any? { |d| d.name == "libtool" }
|
||||
self["HOMEBREW_ISYSTEM_PATHS"] = determine_isystem_paths
|
||||
self["HOMEBREW_INCLUDE_PATHS"] = determine_include_paths
|
||||
self["HOMEBREW_LIBRARY_PATHS"] = determine_library_paths
|
||||
self["HOMEBREW_ISYSTEM_PATHS"] = determine_isystem_paths&.to_s
|
||||
self["HOMEBREW_INCLUDE_PATHS"] = determine_include_paths&.to_s
|
||||
self["HOMEBREW_LIBRARY_PATHS"] = determine_library_paths&.to_s
|
||||
self["HOMEBREW_DEPENDENCIES"] = determine_dependencies
|
||||
self["HOMEBREW_FORMULA_PREFIX"] = @formula.prefix unless @formula.nil?
|
||||
self["HOMEBREW_FORMULA_PREFIX"] = @formula.prefix.to_s unless @formula.nil?
|
||||
# Prevent the OpenSSL rust crate from building a vendored OpenSSL.
|
||||
# https://github.com/sfackler/rust-openssl/blob/994e5ff8c63557ab2aa85c85cc6956b0b0216ca7/openssl/src/lib.rs#L65
|
||||
self["OPENSSL_NO_VENDOR"] = "1"
|
||||
@@ -155,9 +155,10 @@ module Superenv
|
||||
|
||||
private
|
||||
|
||||
sig { params(val: T.any(String, Pathname)).returns(String) }
|
||||
sig { params(val: T.any(String, Pathname)).void }
|
||||
def cc=(val)
|
||||
self["HOMEBREW_CC"] = super
|
||||
super
|
||||
self["HOMEBREW_CC"] = val.to_s
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
|
||||
@@ -2,22 +2,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "os/linux/glibc"
|
||||
require "sandbox"
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module DependencyCollector
|
||||
sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) }
|
||||
def bubblewrap_dep_if_needed(related_formula_names)
|
||||
return unless bubblewrap_dependency_needed?
|
||||
return if building_global_dep_tree?
|
||||
return if related_formula_names.include?(BUBBLEWRAP)
|
||||
return if global_dep_tree[BUBBLEWRAP]&.intersect?(related_formula_names)
|
||||
return unless formula_for(BUBBLEWRAP)
|
||||
|
||||
Dependency.new(BUBBLEWRAP, [:implicit])
|
||||
end
|
||||
|
||||
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
|
||||
@@ -50,31 +38,18 @@ module OS
|
||||
|
||||
GLIBC = "glibc"
|
||||
GCC = OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA
|
||||
BUBBLEWRAP = "bubblewrap"
|
||||
private_constant :GLIBC, :GCC, :BUBBLEWRAP
|
||||
private_constant :GLIBC, :GCC
|
||||
|
||||
sig { void }
|
||||
def init_global_dep_tree_if_needed!
|
||||
return if building_global_dep_tree?
|
||||
|
||||
sandbox_tree_needed = bubblewrap_dependency_needed?
|
||||
build_formulae_tree_needed = ::DevelopmentTools.needs_build_formulae?
|
||||
return if !sandbox_tree_needed && !build_formulae_tree_needed
|
||||
return if (!sandbox_tree_needed || global_dep_tree.key?(BUBBLEWRAP)) &&
|
||||
(!build_formulae_tree_needed || (global_dep_tree.key?(GLIBC) && global_dep_tree.key?(GCC)))
|
||||
return unless ::DevelopmentTools.needs_build_formulae?
|
||||
return if global_dep_tree.key?(GLIBC) && global_dep_tree.key?(GCC)
|
||||
|
||||
building_global_dep_tree!
|
||||
if sandbox_tree_needed
|
||||
include_build = OS.not_tier_one_configuration? || build_formulae_tree_needed
|
||||
global_dep_tree[BUBBLEWRAP] = Set.new(global_deps_for(BUBBLEWRAP, include_build:))
|
||||
end
|
||||
if build_formulae_tree_needed
|
||||
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]])
|
||||
# bubblewrap depends on gcc
|
||||
global_dep_tree[BUBBLEWRAP]&.merge([GCC, *@@global_dep_tree[GCC]])
|
||||
end
|
||||
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
|
||||
|
||||
@@ -86,27 +61,17 @@ module OS
|
||||
nil
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def bubblewrap_dependency_needed?
|
||||
return false unless ::Homebrew::EnvConfig.sandbox_linux?
|
||||
return false if ENV["HOMEBREW_TESTS"]
|
||||
return false if OS::Linux::Sandbox.landlock?
|
||||
|
||||
::Sandbox.executable.blank?
|
||||
end
|
||||
|
||||
sig { params(name: String, include_build: T::Boolean).returns(T::Array[String]) }
|
||||
def global_deps_for(name, include_build: true)
|
||||
sig { params(name: String).returns(T::Array[String]) }
|
||||
def global_deps_for(name)
|
||||
@global_deps_for ||= T.let({}, T.nilable(T::Hash[String, T::Array[String]]))
|
||||
# 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}|#{include_build}"] ||= if (formula = formula_for(name))
|
||||
@global_deps_for[name] ||= if (formula = formula_for(name))
|
||||
formula.deps.filter_map do |dep|
|
||||
next if dep.test? && !dep.build?
|
||||
next if dep.build? && !include_build
|
||||
|
||||
[dep.name, *global_deps_for(dep.name, include_build:)].compact
|
||||
[dep.name, *global_deps_for(dep.name)].compact
|
||||
end.flatten.uniq
|
||||
else
|
||||
[]
|
||||
|
||||
@@ -17,20 +17,8 @@ module OS
|
||||
return unless Homebrew::EnvConfig.sandbox_linux?
|
||||
|
||||
require "sandbox"
|
||||
return if !::Sandbox.available? && GitHub::Actions.env_set?
|
||||
|
||||
if OS::Linux::Sandbox.landlock?
|
||||
unless ::Sandbox.available?
|
||||
return if GitHub::Actions.env_set?
|
||||
|
||||
::Sandbox.ensure_sandbox_available!
|
||||
end
|
||||
|
||||
::Sandbox.configure!
|
||||
elsif GitHub::Actions.env_set?
|
||||
::Sandbox.configure!
|
||||
else
|
||||
::Sandbox.ensure_sandbox_installed!(install_from_tests: true)
|
||||
end
|
||||
::Sandbox.ensure_sandbox_available!
|
||||
end
|
||||
|
||||
|
||||
@@ -196,64 +196,24 @@ module OS
|
||||
def check_linux_sandbox
|
||||
return unless Homebrew::EnvConfig.sandbox_linux?
|
||||
|
||||
inside_docker = OS::Linux.inside_docker?
|
||||
return if inside_docker && !GitHub::Actions.env_set?
|
||||
return if OS::Linux.inside_docker? && !GitHub::Actions.env_set?
|
||||
|
||||
state = ::Sandbox.state
|
||||
return if state == :available
|
||||
|
||||
reason = ::Sandbox.failure_reason || "The Linux sandbox is not available."
|
||||
state = :landlock if OS::Linux::Sandbox.landlock?
|
||||
reason_append = case state
|
||||
when :setuid
|
||||
"\n\nHomebrew's Linux sandbox requires a rootless `bwrap` executable."
|
||||
when :unavailable
|
||||
"\n\nHomebrew's Linux sandbox requires rootless Bubblewrap and unprivileged user namespaces."
|
||||
fix = if state == :missing_fiddle
|
||||
"Run Homebrew with its vendored Ruby, which includes Fiddle."
|
||||
else
|
||||
""
|
||||
end
|
||||
reason += reason_append
|
||||
|
||||
fix_lines = case state
|
||||
when :missing
|
||||
missing_lines = [
|
||||
reason,
|
||||
"",
|
||||
"Install Bubblewrap and ensure a rootless `bwrap` executable is available on `PATH`.",
|
||||
]
|
||||
if (install_command = ::Sandbox.sandbox_install_command)
|
||||
missing_lines.push("", "On this system, install it with:", " #{install_command}")
|
||||
end
|
||||
missing_lines
|
||||
when :setuid
|
||||
[
|
||||
"Install a non-setuid Bubblewrap or put it earlier on `PATH`.",
|
||||
]
|
||||
when :unavailable
|
||||
[
|
||||
reason,
|
||||
"",
|
||||
"Homebrew's Linux sandbox requires rootless Bubblewrap and unprivileged",
|
||||
"user namespaces. Run `sudo brew setup-sandbox` or check and update this system configuration:",
|
||||
*::Sandbox.configuration_command_messages,
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
if state == :unavailable && inside_docker && GitHub::Actions.env_set?
|
||||
fix_lines.push("",
|
||||
"If this is a GitHub Actions container, add `options: --privileged` to the job's " \
|
||||
"`container` configuration.")
|
||||
"Homebrew's Linux sandbox requires a kernel with Landlock enabled."
|
||||
end
|
||||
|
||||
::Homebrew::Diagnostic::Finding.new(
|
||||
reason,
|
||||
remediation: [
|
||||
*fix_lines,
|
||||
"",
|
||||
"As a final workaround, disable the Linux sandbox:",
|
||||
" export HOMEBREW_NO_SANDBOX_LINUX=1",
|
||||
].join("\n").to_s,
|
||||
::Sandbox.failure_reason || "The Linux sandbox is not available.",
|
||||
remediation: <<~EOS.chomp,
|
||||
#{fix}
|
||||
As a final workaround, disable the Linux sandbox:
|
||||
export HOMEBREW_NO_SANDBOX_LINUX=1
|
||||
EOS
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ module OS
|
||||
|
||||
self["HOMEBREW_OPTIMIZATION_LEVEL"] = "O2"
|
||||
self["HOMEBREW_DYNAMIC_LINKER"] = determine_dynamic_linker_path
|
||||
self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(formula)
|
||||
self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(formula).to_s
|
||||
m4_path_deps = ["libtool", "bison"]
|
||||
self["M4"] = "#{HOMEBREW_PREFIX}/opt/m4/bin/m4" if deps.any? { m4_path_deps.include?(it.name) }
|
||||
return unless ::Hardware::CPU.arm64?
|
||||
|
||||
@@ -37,7 +37,6 @@ module OS
|
||||
related_formula_names.merge(versioned_formulae_names)
|
||||
end
|
||||
[
|
||||
dependency_collector.bubblewrap_dep_if_needed(related_formula_names),
|
||||
dependency_collector.gcc_dep_if_needed(related_formula_names),
|
||||
dependency_collector.glibc_dep_if_needed(related_formula_names),
|
||||
].compact.freeze
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/os/linux/sandbox/bubblewrap"
|
||||
require "extend/os/linux/sandbox/landlock"
|
||||
|
||||
module OS
|
||||
@@ -15,31 +14,6 @@ module OS
|
||||
TIOCSCTTY = 0x540E
|
||||
private_constant :TIOCSCTTY
|
||||
|
||||
sig { returns(::PATH) }
|
||||
def self.bubblewrap_candidate_paths
|
||||
::Sandbox::Bubblewrap.executable_candidate_paths
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(::Pathname)) }
|
||||
def self.bubblewrap_executable
|
||||
::Sandbox::Bubblewrap.executable
|
||||
end
|
||||
|
||||
sig { returns(::Pathname) }
|
||||
def self.bubblewrap_executable!
|
||||
::Sandbox::Bubblewrap.executable!
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def self.landlock?
|
||||
ENV.fetch("HOMEBREW_SANDBOX_LINUX_LANDLOCK", nil) == "1"
|
||||
end
|
||||
|
||||
sig { returns(T.any(T.class_of(::Sandbox::Bubblewrap), T.class_of(::Sandbox::Landlock))) }
|
||||
def self.sandbox_implementation
|
||||
landlock? ? ::Sandbox::Landlock : ::Sandbox::Bubblewrap
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def allow_write_temp_and_cache
|
||||
allow_write_path "/tmp"
|
||||
@@ -67,101 +41,31 @@ module OS
|
||||
|
||||
requires_ancestor { T.class_of(::Sandbox) }
|
||||
|
||||
sig { returns(String) }
|
||||
def executable_name
|
||||
::Sandbox::Bubblewrap.executable_name
|
||||
end
|
||||
|
||||
sig { params(candidate: ::Pathname).returns(T::Boolean) }
|
||||
def executable_usable?(candidate)
|
||||
::Sandbox::Bubblewrap.executable_usable?(candidate)
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def system_bubblewrap_paths
|
||||
::Sandbox::Bubblewrap.system_paths
|
||||
end
|
||||
|
||||
sig { returns(::PATH) }
|
||||
def executable_candidate_paths
|
||||
::Sandbox::Bubblewrap.executable_candidate_paths
|
||||
end
|
||||
|
||||
sig { returns(::PATH) }
|
||||
def bubblewrap_candidate_paths
|
||||
executable_candidate_paths
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(::Pathname)) }
|
||||
def bubblewrap_executable
|
||||
::Sandbox::Bubblewrap.executable
|
||||
end
|
||||
|
||||
sig { returns(::Pathname) }
|
||||
def bubblewrap_executable!
|
||||
::Sandbox::Bubblewrap.executable!
|
||||
end
|
||||
|
||||
sig { params(install_from_tests: T::Boolean).void }
|
||||
def ensure_sandbox_installed!(install_from_tests: false)
|
||||
OS::Linux::Sandbox.sandbox_implementation.ensure_installed!(install_from_tests:)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def available?
|
||||
OS::Linux::Sandbox.sandbox_implementation.available?
|
||||
::Sandbox::Landlock.available?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def full_write_isolation?
|
||||
OS::Linux::Sandbox.sandbox_implementation.full_write_isolation?
|
||||
end
|
||||
|
||||
# Bubblewrap reports this specific namespace error when an outer
|
||||
# Bubblewrap sandbox prevents Homebrew from creating another rootless
|
||||
# sandbox. The shared `avoid_nested_sandboxing?` only calls this once the
|
||||
# `$HOMEBREW_AVOID_NESTED_SANDBOXING` opt-in is set.
|
||||
sig { returns(T::Boolean) }
|
||||
def nested_sandbox?
|
||||
OS::Linux::Sandbox.sandbox_implementation.nested_sandbox?
|
||||
::Sandbox::Landlock.full_write_isolation?
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def state
|
||||
OS::Linux::Sandbox.sandbox_implementation.state
|
||||
::Sandbox::Landlock.state
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def reset_state!
|
||||
::Sandbox::Bubblewrap.reset_state!
|
||||
::Sandbox::Landlock.reset_state!
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def configuration_commands
|
||||
OS::Linux::Sandbox.sandbox_implementation.configuration_commands
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def configuration_command_messages
|
||||
OS::Linux::Sandbox.sandbox_implementation.configuration_command_messages
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def configure!
|
||||
OS::Linux::Sandbox.sandbox_implementation.configure!
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def failure_reason
|
||||
return super if self != ::Sandbox
|
||||
|
||||
OS::Linux::Sandbox.sandbox_implementation.failure_reason
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def sandbox_install_command
|
||||
OS::Linux::Sandbox.sandbox_implementation.install_command
|
||||
::Sandbox::Landlock.failure_reason
|
||||
end
|
||||
|
||||
# `ioctl` request used to attach the sandboxed child to a controlling TTY.
|
||||
@@ -173,43 +77,24 @@ module OS
|
||||
|
||||
sig { params(args: T.any(String, ::Pathname)).void }
|
||||
def run(*args)
|
||||
implementation.run { super }
|
||||
end
|
||||
|
||||
sig { params(tmpdir: String).returns(T::Array[String]) }
|
||||
def bubblewrap_args(tmpdir)
|
||||
bubblewrap.arguments(tmpdir)
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, Symbol]) }
|
||||
def writable_paths
|
||||
bubblewrap.writable_paths
|
||||
landlock.run { super }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(args: T::Array[T.any(String, ::Pathname)], tmpdir: String).returns(T::Array[T.any(String, ::Pathname)]) }
|
||||
def sandbox_command(args, tmpdir)
|
||||
implementation.command(args, tmpdir)
|
||||
landlock.command(args, tmpdir)
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def apply_sandbox
|
||||
sandbox = implementation
|
||||
sandbox.apply! if sandbox.is_a?(::Sandbox::Landlock)
|
||||
landlock.apply!
|
||||
end
|
||||
|
||||
sig { returns(T.any(::Sandbox::Bubblewrap, ::Sandbox::Landlock)) }
|
||||
def implementation
|
||||
@implementation ||= T.let(
|
||||
OS::Linux::Sandbox.sandbox_implementation.new(profile),
|
||||
T.nilable(T.any(::Sandbox::Bubblewrap, ::Sandbox::Landlock)),
|
||||
)
|
||||
end
|
||||
|
||||
sig { returns(::Sandbox::Bubblewrap) }
|
||||
def bubblewrap
|
||||
@bubblewrap ||= T.let(::Sandbox::Bubblewrap.new(profile), T.nilable(::Sandbox::Bubblewrap))
|
||||
sig { returns(::Sandbox::Landlock) }
|
||||
def landlock
|
||||
@landlock ||= T.let(::Sandbox::Landlock.new(profile), T.nilable(::Sandbox::Landlock))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,372 +0,0 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "fileutils"
|
||||
require "env_config"
|
||||
require "system_command"
|
||||
require "utils/popen"
|
||||
require "utils/github/actions"
|
||||
require "extend/os/linux/sandbox/backend"
|
||||
|
||||
class Sandbox
|
||||
class Bubblewrap < LinuxBackend
|
||||
extend SystemCommand::Mixin
|
||||
extend Utils::Output::Mixin
|
||||
|
||||
EXECUTABLE = "bwrap"
|
||||
TEST_ARGS = [
|
||||
"--unshare-user",
|
||||
"--unshare-ipc",
|
||||
"--unshare-pid",
|
||||
"--unshare-uts",
|
||||
"--unshare-cgroup-try",
|
||||
"--ro-bind", "/", "/",
|
||||
"--proc", "/proc",
|
||||
"--dev", "/dev",
|
||||
"true"
|
||||
].freeze
|
||||
SYSTEM_PATHS = %w[
|
||||
/usr/bin
|
||||
/bin
|
||||
].freeze
|
||||
HOMEBREW_PATHS = [
|
||||
"#{HOMEBREW_PREFIX}/bin",
|
||||
].freeze
|
||||
NESTED_ERROR = "Creating new namespace failed: nesting depth or /proc/sys/user/max_*_namespaces exceeded"
|
||||
class SysctlSetting < T::Struct
|
||||
const :assignment, String
|
||||
const :description, T::Array[String]
|
||||
const :optional, T::Boolean, default: false
|
||||
end
|
||||
# These settings mirror the `sysctl` assignments in
|
||||
# Library/Homebrew/cmd/setup-sandbox.sh; keep both in sync.
|
||||
SYSCTL_SETTINGS = T.let([
|
||||
SysctlSetting.new(
|
||||
assignment: "kernel.unprivileged_userns_clone=1",
|
||||
description: [
|
||||
"Allows unprivileged processes to create user namespaces. Rootless",
|
||||
"Bubblewrap needs this to isolate builds without elevated privileges.",
|
||||
],
|
||||
),
|
||||
SysctlSetting.new(
|
||||
assignment: "user.max_user_namespaces=28633",
|
||||
description: [
|
||||
"Allows each user to allocate enough user namespaces. A zero or low",
|
||||
"limit can prevent Bubblewrap from creating its sandbox.",
|
||||
],
|
||||
),
|
||||
SysctlSetting.new(
|
||||
assignment: "kernel.apparmor_restrict_unprivileged_userns=0",
|
||||
description: [
|
||||
"Allows unprivileged user namespaces on AppArmor-enabled systems",
|
||||
"that restrict them by default. Older kernels may not provide this",
|
||||
"setting.",
|
||||
],
|
||||
optional: true,
|
||||
),
|
||||
].freeze, T::Array[SysctlSetting])
|
||||
# Per-distro Bubblewrap install commands, detected by package manager and
|
||||
# checked in priority order. Mirrors the build tools instructions in
|
||||
# `Homebrew/install`'s `install.sh`.
|
||||
INSTALL_COMMANDS = T.let({
|
||||
"apt-get" => "sudo apt-get install bubblewrap",
|
||||
"dnf" => "sudo dnf install bubblewrap",
|
||||
"yum" => "sudo yum install bubblewrap",
|
||||
"pacman" => "sudo pacman -S bubblewrap",
|
||||
"apk" => "sudo apk add bubblewrap",
|
||||
}.freeze, T::Hash[String, String])
|
||||
private_constant :EXECUTABLE, :TEST_ARGS, :SYSTEM_PATHS, :HOMEBREW_PATHS, :NESTED_ERROR, :SysctlSetting,
|
||||
:SYSCTL_SETTINGS, :INSTALL_COMMANDS
|
||||
|
||||
class << self
|
||||
sig { returns(String) }
|
||||
def executable_name
|
||||
EXECUTABLE
|
||||
end
|
||||
|
||||
sig { params(candidate: ::Pathname).returns(T::Boolean) }
|
||||
def executable_usable?(candidate)
|
||||
!File.stat(candidate).setuid?
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def system_paths
|
||||
SYSTEM_PATHS
|
||||
end
|
||||
|
||||
sig { returns(::PATH) }
|
||||
def executable_candidate_paths
|
||||
PATH.new(HOMEBREW_PATHS, system_paths, ORIGINAL_PATHS, ENV.fetch("PATH"), HOMEBREW_ORIGINAL_BREW_FILE.dirname)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(::Pathname)) }
|
||||
def executable
|
||||
executable_candidate_paths.each do |path|
|
||||
begin
|
||||
candidate = ::Pathname.new(File.expand_path(executable_name, path))
|
||||
rescue ArgumentError
|
||||
next
|
||||
end
|
||||
|
||||
next if !candidate.file? || !candidate.executable?
|
||||
next unless executable_usable?(candidate)
|
||||
|
||||
return candidate
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
sig { returns(::Pathname) }
|
||||
def executable!
|
||||
executable || raise("Bubblewrap is required to use the Linux sandbox.")
|
||||
end
|
||||
|
||||
sig { params(install_from_tests: T::Boolean).void }
|
||||
def ensure_installed!(install_from_tests: false)
|
||||
return unless Homebrew::EnvConfig.sandbox_linux?
|
||||
return if ENV["HOMEBREW_TESTS"] && !install_from_tests
|
||||
return if ENV["HOMEBREW_INSTALLING_BUBBLEWRAP"]
|
||||
return if executable
|
||||
|
||||
begin
|
||||
require "exceptions"
|
||||
require "formula"
|
||||
with_env(HOMEBREW_INSTALLING_BUBBLEWRAP: "1") do
|
||||
::Formula["bubblewrap"].ensure_installed!(reason: "Linux sandboxing")
|
||||
end
|
||||
reset_state!
|
||||
return if executable
|
||||
rescue ::FormulaUnavailableError
|
||||
nil
|
||||
end
|
||||
|
||||
return unless GitHub::Actions.env_set?
|
||||
return unless ENV.fetch("HOMEBREW_GITHUB_HOSTED_RUNNER", nil)
|
||||
return unless which("apt-get")
|
||||
|
||||
ohai "Installing Bubblewrap..."
|
||||
command = ["apt-get", "install", "--yes", "bubblewrap"]
|
||||
command.unshift("sudo") unless Process.euid.zero?
|
||||
system(*command)
|
||||
reset_state!
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def available?
|
||||
state == :available
|
||||
end
|
||||
|
||||
# Bubblewrap reports this specific namespace error when an outer
|
||||
# Bubblewrap sandbox prevents Homebrew from creating another rootless
|
||||
# sandbox. The shared `avoid_nested_sandboxing?` only calls this once the
|
||||
# `$HOMEBREW_AVOID_NESTED_SANDBOXING` opt-in is set.
|
||||
sig { returns(T::Boolean) }
|
||||
def nested_sandbox?
|
||||
return false unless Homebrew::EnvConfig.sandbox_linux?
|
||||
|
||||
bubblewrap = executable
|
||||
return false unless bubblewrap
|
||||
|
||||
Utils.popen_read(bubblewrap.to_s, *TEST_ARGS, err: :out).include?(NESTED_ERROR)
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def state
|
||||
return :config_disabled unless Homebrew::EnvConfig.sandbox_linux?
|
||||
|
||||
@state ||= T.let(compute_state, T.nilable(Symbol))
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def reset_state!
|
||||
@state = T.let(nil, T.nilable(Symbol))
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def configuration_commands
|
||||
SYSCTL_SETTINGS.map do |setting|
|
||||
command = "sudo sysctl -w #{setting.assignment}"
|
||||
command += " || true" if setting.optional
|
||||
command
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def configuration_command_messages
|
||||
commands = configuration_commands
|
||||
SYSCTL_SETTINGS.each_with_index.flat_map do |setting, index|
|
||||
[
|
||||
" #{commands.fetch(index)}",
|
||||
*setting.description.map { |line| " #{line}" },
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def configure!
|
||||
unless executable
|
||||
ensure_installed!(install_from_tests: true)
|
||||
unless executable
|
||||
reset_state!
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
ohai "Configuring Bubblewrap..."
|
||||
command = [HOMEBREW_BREW_FILE.to_s, "setup-sandbox"]
|
||||
command.unshift("sudo") unless Process.euid.zero?
|
||||
raise ErrorDuringExecution.new(command, status: $CHILD_STATUS || 1) unless system(*command)
|
||||
|
||||
reset_state!
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def failure_reason
|
||||
case state
|
||||
when :config_disabled, :available
|
||||
nil
|
||||
when :missing
|
||||
"Bubblewrap is required to use the Linux sandbox but was not found."
|
||||
when :setuid
|
||||
"A rootless Bubblewrap executable is required to use the Linux sandbox, " \
|
||||
"but all found `bwrap` executables are setuid."
|
||||
when :unavailable
|
||||
"Bubblewrap is installed but cannot create a rootless sandbox."
|
||||
else
|
||||
"The Linux sandbox is not available."
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def install_command
|
||||
INSTALL_COMMANDS.find { |package_manager, _| which(package_manager) }&.last
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def compute_state
|
||||
bubblewraps = executables
|
||||
return :missing if bubblewraps.empty?
|
||||
|
||||
bubblewraps = bubblewraps.select { |candidate| executable_usable?(candidate) }
|
||||
return :setuid if bubblewraps.empty?
|
||||
|
||||
return :available if bubblewraps.any? { |candidate| sandbox_available?(candidate) }
|
||||
|
||||
:unavailable
|
||||
end
|
||||
|
||||
sig { returns(T::Array[::Pathname]) }
|
||||
def executables
|
||||
executable_candidate_paths.filter_map do |path|
|
||||
begin
|
||||
candidate = ::Pathname.new(File.expand_path(executable_name, path))
|
||||
rescue ArgumentError
|
||||
next
|
||||
end
|
||||
|
||||
candidate if candidate.file? && candidate.executable?
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(bubblewrap: ::Pathname).returns(T::Boolean) }
|
||||
def sandbox_available?(bubblewrap)
|
||||
result = system_command(
|
||||
bubblewrap,
|
||||
args: TEST_ARGS,
|
||||
print_stderr: false,
|
||||
)
|
||||
return true if result.success?
|
||||
|
||||
opoo "bubblewrap test probe failed"
|
||||
$stderr.print result.merged_output
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(profile: SandboxProfile).void }
|
||||
def initialize(profile)
|
||||
super
|
||||
@masked_read_paths = T.let([], T::Array[::Pathname])
|
||||
end
|
||||
|
||||
sig { params(block: T.proc.void).void }
|
||||
def run(&block)
|
||||
old_report_on_exception = T.let(Thread.report_on_exception, T.nilable(T::Boolean))
|
||||
Thread.report_on_exception = false
|
||||
super
|
||||
ensure
|
||||
Thread.report_on_exception = old_report_on_exception unless old_report_on_exception.nil?
|
||||
@masked_read_paths.reverse_each { |path| FileUtils.rm_rf(path) }
|
||||
@masked_read_paths.clear
|
||||
end
|
||||
|
||||
sig { params(args: T::Array[T.any(String, ::Pathname)], tmpdir: String).returns(T::Array[T.any(String, ::Pathname)]) }
|
||||
def command(args, tmpdir)
|
||||
[self.class.executable!, *arguments(tmpdir), "--", *args]
|
||||
end
|
||||
|
||||
sig { params(tmpdir: String).returns(T::Array[String]) }
|
||||
def arguments(tmpdir)
|
||||
args = T.let([
|
||||
"--unshare-user",
|
||||
"--unshare-ipc",
|
||||
"--unshare-pid",
|
||||
"--unshare-uts",
|
||||
"--unshare-cgroup-try",
|
||||
"--die-with-parent",
|
||||
"--new-session",
|
||||
"--ro-bind", "/", "/",
|
||||
"--dev", "/dev",
|
||||
"--proc", "/proc"
|
||||
], T::Array[String])
|
||||
args << "--unshare-net" if deny_all_network?
|
||||
|
||||
writable_paths.each do |path, type|
|
||||
prepare_writable_path(path, type)
|
||||
args += ["--bind", path, path]
|
||||
end
|
||||
|
||||
denied_write_paths.each do |path|
|
||||
next unless File.exist?(path)
|
||||
|
||||
args += ["--ro-bind", path, path]
|
||||
end
|
||||
|
||||
denied_read_paths.each do |path|
|
||||
next unless File.exist?(path)
|
||||
|
||||
args += if File.directory?(path)
|
||||
["--bind", masked_read_path, path]
|
||||
else
|
||||
["--ro-bind", File::NULL, path]
|
||||
end
|
||||
end
|
||||
|
||||
args += ["--bind", tmpdir, tmpdir, "--chdir", tmpdir]
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def denied_write_paths
|
||||
profile_paths(allow: false, operation: "file-write")
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def denied_read_paths
|
||||
profile_paths(allow: false, operation: "file-read")
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def masked_read_path
|
||||
path = ::Pathname.new(Dir.mktmpdir("homebrew-sandbox-deny-read", HOMEBREW_TEMP))
|
||||
@masked_read_paths << path
|
||||
path.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -94,8 +94,8 @@ class Sandbox
|
||||
|
||||
class << self
|
||||
# Landlock cannot restrict chmod, chown, extended attributes or timestamp
|
||||
# changes. Callers requiring Bubblewrap-equivalent write isolation must
|
||||
# compensate for these limitations:
|
||||
# changes. Callers requiring full write isolation must compensate for
|
||||
# these limitations:
|
||||
# https://www.kernel.org/doc/html/latest/userspace-api/landlock.html#filesystem-flags
|
||||
sig { returns(T::Boolean) }
|
||||
def full_write_isolation? = false
|
||||
@@ -159,26 +159,6 @@ class Sandbox
|
||||
@abi_version = T.let(nil, T.nilable(Integer))
|
||||
end
|
||||
|
||||
sig { params(install_from_tests: T::Boolean).void }
|
||||
def ensure_installed!(install_from_tests: false); end
|
||||
|
||||
sig { void }
|
||||
def configure!
|
||||
ensure_available!
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def configuration_commands = []
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def configuration_command_messages = []
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def install_command = nil
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def nested_sandbox? = false
|
||||
|
||||
sig { params(attributes: T.nilable(String), size: Integer, flags: Integer).returns(Integer) }
|
||||
def landlock_create_ruleset(attributes, size, flags)
|
||||
@landlock_create_ruleset ||= T.let(
|
||||
@@ -264,13 +244,6 @@ class Sandbox
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def ensure_available!
|
||||
return if available?
|
||||
|
||||
raise failure_reason || "Landlock is not available."
|
||||
end
|
||||
|
||||
sig { returns(Symbol) }
|
||||
def compute_state
|
||||
return :config_disabled unless Homebrew::EnvConfig.sandbox_linux?
|
||||
@@ -357,7 +330,7 @@ class Sandbox
|
||||
# https://github.com/torvalds/linux/blob/master/drivers/char/mem.c
|
||||
# POSIX shared memory and message queues use `/dev/shm` and
|
||||
# `/dev/mqueue`. These grants retain normal kernel permissions but do
|
||||
# not provide Bubblewrap's private IPC namespace:
|
||||
# not provide a private IPC namespace:
|
||||
# https://github.com/bminor/glibc/blob/master/sysdeps/posix/shm-directory.c
|
||||
# https://www.kernel.org/doc/html/latest/filesystems/mqueue.html
|
||||
device_path_rules = T.let({
|
||||
|
||||
@@ -22,10 +22,7 @@ module OS
|
||||
sig { returns(T::Boolean) }
|
||||
def configure_sandbox!
|
||||
require "sandbox"
|
||||
::Sandbox.configure!
|
||||
::Sandbox.available?
|
||||
rescue ::ErrorDuringExecution
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -64,7 +64,10 @@ module OS
|
||||
check_access_directories
|
||||
]
|
||||
|
||||
# Developer tools are checked when building from source.
|
||||
# We need the developer tools for `codesign` on Intel:
|
||||
# https://github.com/Homebrew/brew/issues/23418
|
||||
checks << "check_for_installed_developer_tools" unless ::Hardware::CPU.arm?
|
||||
|
||||
checks.freeze
|
||||
end
|
||||
|
||||
|
||||
@@ -108,6 +108,43 @@ module OS
|
||||
def codesign_patched_binary(file)
|
||||
return if MacOS.version < :big_sur
|
||||
|
||||
unless ::Hardware::CPU.arm?
|
||||
# Intel macOS rejects ruby-macho's ad-hoc signatures on larger
|
||||
# binaries and does not require unsigned binaries to be signed,
|
||||
# so use `codesign` to re-sign only the binaries whose existing
|
||||
# signature our modifications have just broken:
|
||||
# https://github.com/Homebrew/brew/issues/23418
|
||||
result = system_command("codesign", args: ["--verify", file], print_stderr: false)
|
||||
return unless result.stderr.match?(/invalid signature/i)
|
||||
|
||||
odebug "Codesigning #{file}"
|
||||
return if quiet_system("codesign", "--sign", "-", "--force",
|
||||
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
||||
file)
|
||||
|
||||
# If the codesigning fails, it may be a bug in Apple's codesign utility.
|
||||
# A known workaround is to copy the file to another inode, then move it back
|
||||
# erasing the previous file. Then sign again.
|
||||
Dir::Tmpname.create("workaround") do |tmppath|
|
||||
FileUtils.cp file, tmppath
|
||||
FileUtils.mv tmppath, file, force: true
|
||||
end
|
||||
|
||||
odebug "Codesigning (2nd try) #{file}"
|
||||
result = system_command("codesign", args: [
|
||||
"--sign", "-", "--force",
|
||||
"--preserve-metadata=entitlements,requirements,flags,runtime",
|
||||
file
|
||||
], print_stderr: false)
|
||||
return if result.success?
|
||||
|
||||
onoe <<~EOS
|
||||
Failed applying an ad-hoc signature to #{file}:
|
||||
#{result.stderr}
|
||||
EOS
|
||||
return
|
||||
end
|
||||
|
||||
require "macho"
|
||||
|
||||
odebug "Codesigning #{file}"
|
||||
|
||||
@@ -12,8 +12,15 @@ module OS
|
||||
|
||||
requires_ancestor { Kernel }
|
||||
|
||||
sig { params(tap: ::Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
|
||||
def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type)
|
||||
sig {
|
||||
params(
|
||||
tap: ::Tap,
|
||||
os_name: T.nilable(Symbol),
|
||||
arch: T.nilable(Symbol),
|
||||
files: T.nilable(T::Array[::Pathname]),
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type, files: nil)
|
||||
return super if os_name == :linux
|
||||
|
||||
current_macos_version = if os_name.is_a?(Symbol)
|
||||
@@ -23,7 +30,7 @@ module OS
|
||||
end
|
||||
|
||||
success = T.let(true, T::Boolean)
|
||||
tap.cask_files.each do |file|
|
||||
(files || tap.cask_files).each do |file|
|
||||
cask = ::Cask::CaskLoader.load(file)
|
||||
|
||||
# Fine to have missing URLs for unsupported macOS
|
||||
|
||||
@@ -13,16 +13,17 @@ module OS
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
super
|
||||
@xcode = T.let(nil, T.nilable(String))
|
||||
@clt = T.let(nil, T.nilable(Version))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def describe_clang
|
||||
return "N/A" if ::SystemConfig.clang.null?
|
||||
return "N/A" if clang.null?
|
||||
|
||||
clang_build_info = ::SystemConfig.clang_build.null? ? "(parse error)" : ::SystemConfig.clang_build
|
||||
"#{::SystemConfig.clang} build #{clang_build_info}"
|
||||
clang_build_info = clang_build.null? ? "(parse error)" : clang_build
|
||||
"#{clang} build #{clang_build_info}"
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
@@ -39,12 +40,6 @@ module OS
|
||||
@clt ||= MacOS::CLT.version if MacOS::CLT.installed?
|
||||
end
|
||||
|
||||
sig { params(out: T.any(File, StringIO, IO)).void }
|
||||
def core_tap_config(out = $stdout)
|
||||
dump_tap_config(CoreTap.instance, out)
|
||||
dump_tap_config(CoreCaskTap.instance, out)
|
||||
end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def metal_toolchain
|
||||
return unless ::Hardware::CPU.arm64?
|
||||
@@ -81,4 +76,5 @@ module OS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SystemConfig.singleton_class.prepend(OS::Mac::SystemConfig::ClassMethods)
|
||||
|
||||
@@ -538,8 +538,7 @@ class Formula
|
||||
!!head && !stable
|
||||
end
|
||||
|
||||
# Stop RuboCop from erroneously indenting hash target
|
||||
delegate [ # rubocop:disable Layout/HashAlignment
|
||||
delegate [
|
||||
:bottle_defined?,
|
||||
:bottle_tag?,
|
||||
:bottled?,
|
||||
@@ -618,8 +617,7 @@ class Formula
|
||||
# @see .version
|
||||
delegate version: :active_spec
|
||||
|
||||
# Stop RuboCop from erroneously indenting hash target
|
||||
delegate [ # rubocop:disable Layout/HashAlignment
|
||||
delegate [
|
||||
:allow_network_access!,
|
||||
:deny_network_access!,
|
||||
:network_access_allowed?,
|
||||
@@ -1574,7 +1572,7 @@ class Formula
|
||||
|
||||
delegate pour_bottle_check_unsatisfied_reason: :"self.class"
|
||||
|
||||
# Can be overridden to run commands on both source and bottle installation.
|
||||
# odeprecated
|
||||
sig { overridable.void }
|
||||
def post_install; end
|
||||
|
||||
@@ -1646,7 +1644,10 @@ class Formula
|
||||
|
||||
with_logging("post_install") do
|
||||
run_post_install_steps if post_install_steps_defined?
|
||||
post_install if post_install_defined?
|
||||
if post_install_defined?
|
||||
# odeprecated "`post_install`", "`post_install_steps`"
|
||||
post_install
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4083,9 +4084,6 @@ class Formula
|
||||
current_steps.concat(
|
||||
if block
|
||||
Homebrew::InstallSteps::DSL.build(
|
||||
# TODO: Remove the undocumented `default_base: :var` compatibility default after official taps use
|
||||
# explicit bases.
|
||||
default_base: :var,
|
||||
default_source_base: :prefix,
|
||||
default_target_base: :prefix,
|
||||
&block
|
||||
|
||||
@@ -69,7 +69,7 @@ module Homebrew
|
||||
@problems = T.let([], T::Array[T.any(String, T::Hash[Symbol, T.untyped])])
|
||||
@new_formula_problems = T.let([], T::Array[T.any(String, T::Hash[Symbol, T.untyped])])
|
||||
@text = T.let(formula.path.open("rb", &:read), String)
|
||||
@specs = T.let(%w[stable head].filter_map { |s| formula.send(s) }, T::Array[SoftwareSpec])
|
||||
@specs = T.let(%w[stable head].filter_map { |s| formula.public_send(s) }, T::Array[SoftwareSpec])
|
||||
@spdx_license_data = spdx_license_data
|
||||
@spdx_exception_data = spdx_exception_data
|
||||
@tap_audit = tap_audit
|
||||
@@ -644,7 +644,7 @@ module Homebrew
|
||||
return if homepage.match?(%r{^https?://www\.(?:non)?gnu\.org/.+}) && github_runner
|
||||
|
||||
use_homebrew_curl = [:stable, :head].any? do |spec_name|
|
||||
next false unless (spec = formula.send(spec_name))
|
||||
next false unless (spec = formula.public_send(spec_name))
|
||||
|
||||
spec.using == :homebrew_curl
|
||||
end
|
||||
@@ -855,7 +855,7 @@ module Homebrew
|
||||
|
||||
%w[Stable HEAD].each do |name|
|
||||
spec_name = name.downcase.to_sym
|
||||
next unless (spec = formula.send(spec_name))
|
||||
next unless (spec = formula.public_send(spec_name))
|
||||
|
||||
except = @except.to_a
|
||||
if spec_name == :head &&
|
||||
@@ -1324,12 +1324,12 @@ module Homebrew
|
||||
|
||||
sig { params(message: String, location: T.nilable(Homebrew::SourceLocation), corrected: T::Boolean).void }
|
||||
def problem(message, location: nil, corrected: false)
|
||||
@problems << ({ message:, location:, corrected: })
|
||||
@problems << { message:, location:, corrected: }
|
||||
end
|
||||
|
||||
sig { params(message: String, location: T.nilable(Homebrew::SourceLocation), corrected: T::Boolean).void }
|
||||
def new_formula_problem(message, location: nil, corrected: false)
|
||||
@new_formula_problems << ({ message:, location:, corrected: })
|
||||
@new_formula_problems << { message:, location:, corrected: }
|
||||
end
|
||||
|
||||
sig { params(repo_owner: String).returns(T::Boolean) }
|
||||
|
||||
@@ -448,20 +448,7 @@ class FormulaInstaller
|
||||
if Homebrew::EnvConfig.developer?
|
||||
# `recursive_dependencies` trims cyclic dependencies, so we do one level and take the recursive deps of that.
|
||||
# Mapping direct dependencies to deeper dependencies in a hash is also useful for the cyclic output below.
|
||||
recursive_dep_map = formula.deps.to_h do |dep|
|
||||
# We cheat a bit with bubblewrap. We eagerly add it to build dependencies on tier-one systems.
|
||||
# But this cyclic dependency check is (intentionally) overly strict and forbids cyclic build dependencies,
|
||||
# to help prevent cases that would break, for example, mass bottling.
|
||||
recursive_deps = if dep.name == "bubblewrap" && dep.implicit?
|
||||
[]
|
||||
else
|
||||
dep.to_formula.recursive_dependencies do |_dependent, recursive_dep|
|
||||
Dependable::PRUNE if recursive_dep.name == "bubblewrap" && recursive_dep.implicit?
|
||||
end
|
||||
end
|
||||
|
||||
[dep, recursive_deps]
|
||||
end
|
||||
recursive_dep_map = formula.deps.to_h { |dep| [dep, dep.to_formula.recursive_dependencies] }
|
||||
|
||||
cyclic_dependencies = []
|
||||
recursive_dep_map.each do |dep, recursive_deps|
|
||||
@@ -1066,8 +1053,6 @@ on_request: installed_on_request?, options:)
|
||||
# let's reset Utils::Git.available? if we just installed git
|
||||
Utils::Git.clear_available_cache if formula.name == "git"
|
||||
|
||||
Sandbox.reset_state! if formula.name == "bubblewrap"
|
||||
|
||||
# use installed ca-certificates when it's needed and available
|
||||
if formula.name == "ca-certificates" &&
|
||||
!DevelopmentTools.ca_file_handles_most_https_certificates?
|
||||
@@ -1369,10 +1354,10 @@ on_request: installed_on_request?, options:)
|
||||
@show_summary_heading = true
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
sig { returns(T.any(String, Pathname)) }
|
||||
def post_install_formula_path
|
||||
# Use the formula from the keg when any of the following is true:
|
||||
# * We're installing from the JSON API
|
||||
# * We're installing from the JSON API and it has a Ruby post-install hook
|
||||
# * We're installing a local bottle file
|
||||
# * We're building from source
|
||||
# * The formula doesn't exist in the tap (or the tap isn't installed)
|
||||
@@ -1387,7 +1372,11 @@ on_request: installed_on_request?, options:)
|
||||
return tap_formula_path if installed_prefix.nil?
|
||||
|
||||
keg_formula_path = installed_prefix/".brew/#{formula.name}.rb"
|
||||
return keg_formula_path if formula.loaded_from_api?
|
||||
if formula.loaded_from_api?
|
||||
return formula.full_name unless formula.post_install_defined?
|
||||
|
||||
return keg_formula_path
|
||||
end
|
||||
return keg_formula_path if formula.local_bottle_path
|
||||
return keg_formula_path if build_from_source?
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ class GitHubRunnerMatrix
|
||||
unless self_hosted
|
||||
container = {
|
||||
image: "ghcr.io/homebrew/brew:main",
|
||||
options: "--init --user linuxbrew --env HOMEBREW_SANDBOX_LINUX_LANDLOCK=1",
|
||||
options: "--init --user linuxbrew",
|
||||
}
|
||||
workdir = "/github/home"
|
||||
end
|
||||
|
||||
@@ -65,9 +65,11 @@ Once an install step DSL method has shipped in a stable `Homebrew/brew`
|
||||
release, keep accepting and executing it throughout this migration. A better
|
||||
replacement may stop being documented and become the target of tap migrations,
|
||||
but the old method must remain marked with `# odeprecated` until it can be
|
||||
deprecated in a later release. Each `Homebrew/brew` PR must continue to pass
|
||||
deprecated in a later release. Each capability PR must continue to pass
|
||||
tap-wide syntax checks against the default branches of `homebrew/core` and
|
||||
`homebrew/cask`; paired tap branches cannot provide atomic compatibility.
|
||||
`homebrew/cask`; paired tap branches cannot provide atomic compatibility. The
|
||||
final official-hook enforcement PR is the deliberate exception: keep it at the
|
||||
top of the stack and red until both tap migrations have landed.
|
||||
|
||||
RuboCop autocorrection converts the simplest existing `post_install` and
|
||||
`*flight` Ruby blocks to steps blocks when every statement is a supported file
|
||||
@@ -105,37 +107,40 @@ The delivery order is now:
|
||||
in their recorded order. A tap file containing several step types is
|
||||
assigned to the latest brew capability it needs so every intermediate tap
|
||||
commit remains loadable.
|
||||
1. Add sandbox hardening, conservative autocorrection and audit cops after the
|
||||
taps can consume every new DSL method.
|
||||
1. Keep the official-tap enforcement PR at the top of the stack. Its tap syntax
|
||||
job is expected to fail until both tap stacks reach zero legacy hooks, then
|
||||
it can merge without changing runtime compatibility.
|
||||
1. Add conflicts or legacy-hook deprecations only after the merged tap heads,
|
||||
rather than only local branches, pass the zero-hook gate.
|
||||
|
||||
The `Homebrew/brew` implementation stack is:
|
||||
|
||||
| Commit | Capability |
|
||||
| Commit, PR or branch | Capability |
|
||||
| --- | --- |
|
||||
| `df477ca13b` | scoped path and platform guards |
|
||||
| `55190efb93` | shared path, token and privilege handling |
|
||||
| `51093bf43d` | copies |
|
||||
| `9f4a8c9e19` | removals |
|
||||
| `8ed34862ff` | `inreplace` steps |
|
||||
| `7bfe8cf697` | recursive install-step validation |
|
||||
| `82e7898974` | cask command wrappers |
|
||||
| `053658e0fb` | generated cask scripts |
|
||||
| `516d7e209e` | formula permission steps |
|
||||
| `f4b8adfc25` | constrained commands |
|
||||
| `996b7168a3` | process termination |
|
||||
| `b312195625` | scoped warnings |
|
||||
| `644eae48d6` | GCC runtime configuration |
|
||||
| `9b45d57aca` | gzipped executable installation |
|
||||
| `c315929071` | glibc runtime setup |
|
||||
| `39956d5347` | Clang system configuration |
|
||||
| `8714d6873d` | PHP configuration |
|
||||
| `3e52a3efda` | Python bootstrap |
|
||||
| `3d8f2c5c5c` | compatibility and canonical interface alignment |
|
||||
| `81180c1ec5` | temporary cask flight migration bridge |
|
||||
| `50829a2e04` | canonical autocorrection and validation |
|
||||
| `138ae14570` | canonical cookbook documentation |
|
||||
| `3f237af118` | scoped path and platform guards |
|
||||
| `a51ad058fa` | shared path, token and privilege handling |
|
||||
| `f98b955d42` | copies |
|
||||
| `af66235c7e` | removals |
|
||||
| `390c74bd49` | `inreplace` steps |
|
||||
| `e05c08f986` | recursive install-step validation |
|
||||
| `c33846e9da` | cask command wrappers with declared names |
|
||||
| `eeaae4a0dc` | generated cask scripts |
|
||||
| `677dad25a5` | formula permission steps |
|
||||
| `cf5bc21c6b` | constrained commands |
|
||||
| `2c2089fc74` | process termination |
|
||||
| `46b43d57d0` | scoped warnings |
|
||||
| `7dc5f2bbaf` | GCC runtime configuration and idempotence |
|
||||
| `a959228644` | gzipped executable installation |
|
||||
| `d39b823008` | glibc runtime setup |
|
||||
| `b312752fc0` | Clang system configuration |
|
||||
| `Homebrew/brew#23191` | PHP configuration |
|
||||
| `Homebrew/brew#23192` | Python bootstrap |
|
||||
| `Homebrew/brew#23193` | compatibility and canonical interface alignment |
|
||||
| `Homebrew/brew#23194` | temporary cask flight migration bridge |
|
||||
| `Homebrew/brew#23195` | canonical autocorrection and validation |
|
||||
| `Homebrew/brew#23196` | canonical cookbook documentation |
|
||||
| `install-step-25-compact-json` | compact structured step payloads |
|
||||
| `install-step-26-official-hook-enforcement` | reject new legacy hooks in official taps |
|
||||
|
||||
### Compatibility bridge
|
||||
|
||||
@@ -159,34 +164,34 @@ The `homebrew/core` branch `install-step-migrations` is split into this stack:
|
||||
|
||||
| Commit | Migration | Brew dependency |
|
||||
| --- | --- | --- |
|
||||
| `0b9bd868455` | copies | `7bfe8cf697` |
|
||||
| `051113f88ab` | removals | `7bfe8cf697` |
|
||||
| `249f4350d30` | `inreplace` steps | `7bfe8cf697` |
|
||||
| `0f7ceca3561` | formula permissions | `516d7e209e` |
|
||||
| `c5ef325490e` | commands | `f4b8adfc25` |
|
||||
| `6a27ffeb425` | process termination | `996b7168a3` |
|
||||
| `cc9fa35cb13` | config warnings | `b312195625` |
|
||||
| `b710256aded` | GCC runtimes | `644eae48d6` |
|
||||
| `ed9c245477f` | gzipped executables | `9b45d57aca` |
|
||||
| `8de30da8919` | glibc runtime setup | `c315929071` |
|
||||
| `15220c281ec` | Clang configs | `39956d5347` |
|
||||
| `ccb800cfe2e` | PHP configuration | `8714d6873d` |
|
||||
| `424452ca1d2` | Python bootstrap | `3e52a3efda` |
|
||||
| `c5181e26c9c` | existing rebuild actions | `3d8f2c5c5c` |
|
||||
| `c63aad69e25` | canonical names | `3d8f2c5c5c` |
|
||||
| `8b2a3736ad2` | copies | `e05c08f986` |
|
||||
| `a410f86f0e5` | removals | `e05c08f986` |
|
||||
| `8fff4cfe6ac` | `inreplace` steps | `e05c08f986` |
|
||||
| `36a86e4cd3` | formula permissions | `677dad25a5` |
|
||||
| `b737b6ddfc5` | commands | `cf5bc21c6b` |
|
||||
| `de01d0cb71f` | process termination | `2c2089fc74` |
|
||||
| `99f7e7ee196` | config warnings | `46b43d57d0` |
|
||||
| `954c3eeb0e1` | GCC runtimes | `7dc5f2bbaf` |
|
||||
| `60b9fd667cb` | gzipped executables | `a959228644` |
|
||||
| `12494317fea` | glibc runtime setup | `d39b823008` |
|
||||
| `1ae21b8bacc` | Clang configs | `b312752fc0` |
|
||||
| `b1363e7f414` | PHP configuration | `Homebrew/brew#23191` |
|
||||
| `54b3a12d201` | Python bootstrap | `Homebrew/brew#23192` |
|
||||
| `b6f242bc378` | existing rebuild actions | `Homebrew/brew#23193` |
|
||||
| `fba7166ace8` | canonical names | `Homebrew/brew#23193` |
|
||||
|
||||
The `homebrew/cask` branch `install-step-migrations` is split into this stack:
|
||||
|
||||
| Commit | Migration | Brew dependency |
|
||||
| --- | --- | --- |
|
||||
| `759984cd350` | removals | `7bfe8cf697` |
|
||||
| `b970d628f53` | `inreplace` steps | `7bfe8cf697` |
|
||||
| `6827cc8f6b5` | command wrappers | `82e7898974` |
|
||||
| `3421d25b25d` | generated scripts | `053658e0fb` |
|
||||
| `985a8dc24f7` | commands and mixed steps | `f4b8adfc25` |
|
||||
| `1951737bab1` | process termination | `996b7168a3` |
|
||||
| `c5d23bfe585` | existing structured flight steps | `3d8f2c5c5c` |
|
||||
| `8581c70e3a9` | canonical names | `3d8f2c5c5c` |
|
||||
| `98460ee76f7` | removals | `e05c08f986` |
|
||||
| `c4600c11d1c` | `inreplace` steps | `e05c08f986` |
|
||||
| `cc918a9697d` | command wrappers | `c33846e9da` |
|
||||
| `1010ee37a29` | generated scripts | `eeaae4a0dc` |
|
||||
| `a6dda33a841` | commands and mixed steps | `cf5bc21c6b` |
|
||||
| `e6787710ae2` | process termination | `2c2089fc74` |
|
||||
| `46ac0e3034b` | existing structured flight steps | `Homebrew/brew#23193` |
|
||||
| `79ac6f5fc5c` | canonical names | `Homebrew/brew#23193` |
|
||||
|
||||
`gcloud-cli` is the only cask copy user, but its matching flight blocks also
|
||||
need removal, linking and command steps. It therefore lands in the command
|
||||
@@ -206,8 +211,8 @@ formula files and `82` `post_install` methods, and `homebrew/cask` at
|
||||
`892cff1a33bb`, with `7,701` cask files and `146` legacy flight blocks in `124`
|
||||
casks.
|
||||
|
||||
The refreshed local tap migrations at `homebrew/core` `6e23a533d3e4` and
|
||||
`homebrew/cask` `215a345b6b40` now pass the gate:
|
||||
The refreshed local tap migrations at `homebrew/core` `fba7166ace8` and
|
||||
`homebrew/cask` `79ac6f5fc5c` now pass the gate:
|
||||
|
||||
- `homebrew/core` has `0` `post_install` methods after converting all `86`
|
||||
hook-bearing files.
|
||||
@@ -219,6 +224,13 @@ The compatibility naming pass also updates structured-step users that did not
|
||||
have a legacy hook. The complete local stacks therefore differ from their tap
|
||||
heads in `140` formula files and `137` cask files.
|
||||
|
||||
The final brew-only audit on 30 July 2026 found `21` formula hooks at
|
||||
`homebrew/core` `2927a618306` and `12` cask flight blocks at `homebrew/cask`
|
||||
`13de0293aa0`. Every remaining file is already covered by the prepared
|
||||
migration stacks, but those stacks now conflict with their moving tap heads and
|
||||
must be rebased during PR 29. Until that happens, the official-hook enforcement
|
||||
PR is expected to fail tap syntax.
|
||||
|
||||
This proves that the implemented DSL is sufficient, but it does not authorise
|
||||
bridge conflicts yet. The tap stacks must first be reviewed and merged against
|
||||
current heads after a stable `Homebrew/brew` release contains the new DSL. The
|
||||
@@ -335,11 +347,11 @@ less consistent with Formula syntax without reducing the runner surface.
|
||||
|
||||
## API Source Download Gates
|
||||
|
||||
Formula JSON API installs need to preserve `post_install` because it is the
|
||||
only install-time Ruby hook recorded for bottle installs. The hook runs from
|
||||
the formula stored in the installed keg, while source builds and local patch
|
||||
handling use `Homebrew::API::Formula.source_download_formula` for build-time
|
||||
reasons outside this post-install DSL work.
|
||||
Formula JSON API installs load `post_install_steps` through `FormulaStruct` and
|
||||
run them without downloading formula Ruby. Bottles retain the formula stored in
|
||||
the keg only for legacy `post_install` compatibility. Source builds and local
|
||||
patch handling still use `Homebrew::API::Formula.source_download_formula` for
|
||||
build-time reasons outside this post-install DSL work.
|
||||
|
||||
Cask JSON API installs use `Homebrew::API::Cask.source_download_cask` when
|
||||
`Cask#caskfile_only?` is true. Legacy `preflight`, `postflight`,
|
||||
@@ -348,7 +360,15 @@ API data only records that a block exists, not the Ruby body. Current API data
|
||||
stores each language block's locale group, default marker, return value and
|
||||
resulting stanza differences, so language-specific URLs can be resolved before
|
||||
the download is enqueued. Older API data with only the flat `languages` array
|
||||
continues to download source as a compatibility fallback.
|
||||
continues to download source as a compatibility fallback. Once the official tap
|
||||
contains only structured flight steps, those artifacts do not make
|
||||
`Cask#caskfile_only?` true and cask Ruby is not downloaded.
|
||||
|
||||
Formula structs omit empty steps and false legacy-hook markers. Cask structs
|
||||
store artifacts as compact positional arrays and omit blank fields. Individual
|
||||
steps omit values supplied by runner defaults while retaining values, such as
|
||||
the canonical `move` overwrite default, that are required to distinguish them
|
||||
from already released payloads.
|
||||
|
||||
## Installed Cask Metadata Format
|
||||
|
||||
@@ -470,7 +490,7 @@ is stripped during metadata serialisation.
|
||||
- [x] PR 4.1, formula install-step sandboxing.
|
||||
Commit: `Sandbox formula install steps`.
|
||||
Scope: run structured formula steps inside the existing post-install child
|
||||
process so macOS Seatbelt and Linux Bubblewrap apply the same filesystem and
|
||||
process so macOS Seatbelt and Linux Landlock apply the same filesystem and
|
||||
network policy as legacy `post_install` hooks. This must land before any tap
|
||||
migrations use filesystem-mutating steps.
|
||||
- PR 5, default config and template writes (historical split workflow).
|
||||
@@ -681,14 +701,20 @@ is stripped during metadata serialisation.
|
||||
- [ ] PR 29, refresh and merge both tap stacks.
|
||||
Scope: after the brew DSL ships in a stable release, rebase each stack onto
|
||||
the current tap head, repeat the zero-hook gate and merge in order.
|
||||
- [ ] PR 30, sandbox and runner hardening.
|
||||
Scope: sandbox eligible helpers and commands without adding migration DSL.
|
||||
- [ ] PR 31, enforcement and migration cops.
|
||||
Scope: add conservative autocorrection and audits after both taps consume the
|
||||
complete DSL. Do not introduce conflicts here.
|
||||
- [x] PR 30, compact structured step payloads.
|
||||
Scope: omit runner defaults from internal JSON while preserving explicit
|
||||
values needed to distinguish released compatibility behaviour.
|
||||
- [ ] PR 31, official-tap enforcement.
|
||||
Scope: reject `post_install` and legacy flight blocks in taps owned by the
|
||||
Homebrew organisation, remove their authoring documentation and mark the
|
||||
runtime call sites with commented `odeprecated` calls. Keep this PR at the
|
||||
top of the stack and expect tap syntax to fail until PR 29 reaches zero
|
||||
hooks. Do not introduce runtime conflicts or warnings here; third-party
|
||||
compatibility remains intact.
|
||||
- [ ] PR 32, close the bridges and deprecate legacy hooks.
|
||||
Hard prerequisite: the merged `homebrew/core` head has no `post_install`
|
||||
methods and the merged `homebrew/cask` head has no legacy flight blocks.
|
||||
methods and the merged `homebrew/cask` head has no legacy flight blocks. Add
|
||||
actual `odeprecated` calls in the next major or minor Homebrew release.
|
||||
- [ ] PR 33, remove the documented formula `var` default while retaining it
|
||||
temporarily as a runtime compatibility fallback.
|
||||
- [ ] PR 34, migrate every implicit `var` path in `homebrew/core` to an
|
||||
|
||||
@@ -25,7 +25,9 @@ module OnSystem
|
||||
|
||||
sig { params(os_name: Symbol, or_condition: T.nilable(Symbol)).returns(T::Boolean) }
|
||||
def self.os_condition_met?(os_name, or_condition = nil)
|
||||
return Homebrew::SimulateSystem.send(:"simulating_or_running_on_#{os_name}?") if BASE_OS_OPTIONS.include?(os_name)
|
||||
if BASE_OS_OPTIONS.include?(os_name)
|
||||
return Homebrew::SimulateSystem.public_send(:"simulating_or_running_on_#{os_name}?")
|
||||
end
|
||||
|
||||
raise ArgumentError, "Invalid OS condition: #{os_name.inspect}" unless MacOSVersion::SYMBOLS.key?(os_name)
|
||||
|
||||
@@ -92,8 +94,10 @@ module OnSystem
|
||||
return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(T.must(__method__))
|
||||
|
||||
@called_in_on_system_block = true
|
||||
@called_in_on_os_block = T.let(true, T.nilable(T::Boolean))
|
||||
result = block.call
|
||||
@called_in_on_system_block = false
|
||||
@called_in_on_os_block = false
|
||||
|
||||
result
|
||||
end
|
||||
@@ -113,8 +117,10 @@ module OnSystem
|
||||
return if !OnSystem.os_condition_met?(os_version, or_condition) && !OnSystem.os_condition_met?(:linux)
|
||||
|
||||
@called_in_on_system_block = true
|
||||
@called_in_on_os_block = T.let(true, T.nilable(T::Boolean))
|
||||
result = block.call
|
||||
@called_in_on_system_block = false
|
||||
@called_in_on_os_block = false
|
||||
|
||||
result
|
||||
end
|
||||
@@ -149,8 +155,10 @@ module OnSystem
|
||||
T.nilable(MacOSVersion),
|
||||
)
|
||||
@called_in_on_system_block = T.let(true, T.nilable(T::Boolean))
|
||||
@called_in_on_os_block = T.let(true, T.nilable(T::Boolean))
|
||||
result = block.call
|
||||
@called_in_on_system_block = false
|
||||
@called_in_on_os_block = false
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
@@ -13,6 +13,7 @@ require "utils/popen"
|
||||
module Patch
|
||||
CVE_PATTERN = /CVE-?(\d{4})-(\d{4,})/i
|
||||
GHSA_PATTERN = /\AGHSA(-[23456789cfghjmpqrvwx]{4}){3}\z/
|
||||
OSV_PATTERN = /\AOSV-\d{4}-\d+\z/
|
||||
# CycloneDX `pedigree.patches.type` values applicable to source diffs.
|
||||
# `monkey` is omitted: it describes runtime modification, which `patch do` cannot express.
|
||||
# Keep in sync with `PATCH_TYPES` in `Library/Homebrew/rubocops/patches.rb`.
|
||||
@@ -35,7 +36,7 @@ module Patch
|
||||
|
||||
sig { params(id: String).returns(String) }
|
||||
def self.resolves_type(id)
|
||||
return "security" if id.match?(/\ACVE-\d{4}-\d{4,}\z/) || id.match?(GHSA_PATTERN)
|
||||
return "security" if id.match?(/\ACVE-\d{4}-\d{4,}\z/) || id.match?(GHSA_PATTERN) || id.match?(OSV_PATTERN)
|
||||
|
||||
"defect"
|
||||
end
|
||||
|
||||
+169
-45
@@ -3,34 +3,59 @@
|
||||
|
||||
require "formula"
|
||||
require "cask/cask_loader"
|
||||
require "system_command"
|
||||
require "tempfile"
|
||||
require "utils/output"
|
||||
|
||||
# Helper module for validating syntax in taps.
|
||||
module Readall
|
||||
extend T::Generic
|
||||
extend Cachable
|
||||
extend SystemCommand::Mixin
|
||||
extend Utils::Output::Mixin
|
||||
|
||||
Cache = type_template { { fixed: T::Hash[Symbol, T.untyped] } }
|
||||
|
||||
private_class_method :cache
|
||||
|
||||
MIN_FILES_PER_WORKER = 4
|
||||
private_constant :MIN_FILES_PER_WORKER
|
||||
|
||||
# Buffers Ruby compile warnings from {.syntax_errors_or_warnings?} so they
|
||||
# can be filtered before being printed to `$stderr`.
|
||||
module WarningBuffer
|
||||
sig { params(message: String, category: T.nilable(Symbol)).void }
|
||||
def warn(message, category: nil)
|
||||
buffer = Readall.warning_buffer
|
||||
buffer ? buffer << message : super
|
||||
end
|
||||
end
|
||||
private_constant :WarningBuffer
|
||||
Warning.singleton_class.prepend(WarningBuffer)
|
||||
|
||||
@warning_buffer = T.let(nil, T.nilable(T::Array[String]))
|
||||
|
||||
class << self
|
||||
sig { returns(T.nilable(T::Array[String])) }
|
||||
attr_accessor :warning_buffer
|
||||
end
|
||||
|
||||
sig { params(ruby_files: T::Array[Pathname]).returns(T::Boolean) }
|
||||
def self.valid_ruby_syntax?(ruby_files)
|
||||
failed = T.let(false, T::Boolean)
|
||||
ruby_files.each do |ruby_file|
|
||||
# As a side effect, print syntax errors/warnings to `$stderr`.
|
||||
failed = true if syntax_errors_or_warnings?(ruby_file)
|
||||
parallel_slices_valid?(ruby_files) do |files|
|
||||
failed = T.let(false, T::Boolean)
|
||||
files.each do |ruby_file|
|
||||
# As a side effect, print syntax errors/warnings to `$stderr`.
|
||||
failed = true if syntax_errors_or_warnings?(ruby_file)
|
||||
end
|
||||
!failed
|
||||
end
|
||||
!failed
|
||||
end
|
||||
|
||||
sig { params(alias_dir: Pathname, formula_dir: Pathname).returns(T::Boolean) }
|
||||
def self.valid_aliases?(alias_dir, formula_dir)
|
||||
return true unless alias_dir.directory?
|
||||
|
||||
formula_basenames = Set.new(formula_dir.glob("**/*.rb").map { |formula_file| formula_file.basename.to_s })
|
||||
|
||||
failed = T.let(false, T::Boolean)
|
||||
alias_dir.each_child do |f|
|
||||
if !f.symlink?
|
||||
@@ -41,7 +66,7 @@ module Readall
|
||||
failed = true
|
||||
end
|
||||
|
||||
if formula_dir.glob("**/#{f.basename}.rb").any?(&:exist?)
|
||||
if formula_basenames.include?("#{f.basename}.rb")
|
||||
onoe "Formula duplicating alias: #{f}"
|
||||
failed = true
|
||||
end
|
||||
@@ -49,12 +74,16 @@ module Readall
|
||||
!failed
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, bottle_tag: T.nilable(Utils::Bottles::Tag)).returns(T::Boolean) }
|
||||
def self.valid_formulae?(tap, bottle_tag: nil)
|
||||
sig {
|
||||
params(
|
||||
tap: Tap, bottle_tag: T.nilable(Utils::Bottles::Tag), files: T.nilable(T::Array[Pathname]),
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def self.valid_formulae?(tap, bottle_tag: nil, files: nil)
|
||||
cache[:valid_formulae] ||= {}
|
||||
|
||||
success = T.let(true, T::Boolean)
|
||||
tap.formula_files.each do |file|
|
||||
(files || tap.formula_files).each do |file|
|
||||
valid = cache[:valid_formulae][file]
|
||||
next if valid == true || valid&.include?(bottle_tag)
|
||||
|
||||
@@ -82,8 +111,12 @@ module Readall
|
||||
success
|
||||
end
|
||||
|
||||
sig { params(tap: Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
|
||||
def self.valid_casks?(tap, os_name: nil, arch: nil)
|
||||
sig {
|
||||
params(
|
||||
tap: Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol), files: T.nilable(T::Array[Pathname]),
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def self.valid_casks?(tap, os_name: nil, arch: nil, files: nil)
|
||||
validating_linux = if os_name.nil?
|
||||
Homebrew::SimulateSystem.current_os == :linux
|
||||
else
|
||||
@@ -95,9 +128,7 @@ module Readall
|
||||
os_and_arch += " on #{(arch == :intel) ? "Intel x86_64" : "ARM64"}" if arch
|
||||
|
||||
success = T.let(true, T::Boolean)
|
||||
tap.cask_files.each do |file|
|
||||
next if file.read.match?(/^\s*depends_on(?:\s*\(\s*|\s+)(?::macos\b|macos:)/)
|
||||
|
||||
(files || tap.cask_files).each do |file|
|
||||
cask = if arch
|
||||
Homebrew::SimulateSystem.with(os: :macos, arch:) do
|
||||
loaded_cask = Cask::CaskLoader.load(file)
|
||||
@@ -111,24 +142,28 @@ module Readall
|
||||
end
|
||||
next unless cask
|
||||
|
||||
linux_sha256 = if arch
|
||||
Homebrew::SimulateSystem.with(os: :linux, arch:) do
|
||||
cask.refresh
|
||||
cask.sha256
|
||||
end
|
||||
check_linux_sha256 = lambda do
|
||||
cask.refresh
|
||||
arch_types = cask.depends_on.arch&.map { |cask_arch| cask_arch[:type] }
|
||||
# `depends_on arch:` excludes this architecture, so no Linux
|
||||
# checksum is expected for it.
|
||||
next true if arch_types&.exclude?(Homebrew::SimulateSystem.current_arch)
|
||||
|
||||
!cask.sha256.nil?
|
||||
end
|
||||
linux_sha256_valid = if arch
|
||||
Homebrew::SimulateSystem.with(os: :linux, arch:, &check_linux_sha256)
|
||||
else
|
||||
Homebrew::SimulateSystem.with(os: :linux) do
|
||||
cask.refresh
|
||||
cask.sha256
|
||||
end
|
||||
Homebrew::SimulateSystem.with(os: :linux, &check_linux_sha256)
|
||||
end
|
||||
# No `sha256` matched Linux, so the cask cannot be downloaded there
|
||||
# despite not being marked macOS-only.
|
||||
next unless linux_sha256.nil?
|
||||
next if linux_sha256_valid
|
||||
|
||||
onoe "Invalid cask (#{os_and_arch}): #{file}"
|
||||
$stderr.puts "Missing Linux stanzas can leave Linux `sha256` as nil. " \
|
||||
"Add `depends_on :macos` if this cask is macOS-only."
|
||||
"Add `depends_on :macos` if this cask is macOS-only or " \
|
||||
"`depends_on arch:` if it does not support this architecture."
|
||||
success = false
|
||||
rescue Interrupt
|
||||
raise
|
||||
@@ -155,40 +190,129 @@ module Readall
|
||||
success = false unless valid_aliases
|
||||
end
|
||||
|
||||
if no_simulate
|
||||
success = false unless valid_formulae?(tap)
|
||||
success = false unless valid_casks?(tap)
|
||||
else
|
||||
os_arch_combinations.each do |os, arch|
|
||||
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless bottle_tag.valid_combination?
|
||||
items = tap.formula_files.map { |file| [:formula, file] } +
|
||||
tap.cask_files.map { |file| [:cask, file] }
|
||||
|
||||
Homebrew::SimulateSystem.with(os:, arch:) do
|
||||
success = false unless valid_formulae?(tap, bottle_tag:)
|
||||
success = false unless valid_casks?(tap, os_name: os, arch:)
|
||||
all_files_valid = parallel_slices_valid?(items) do |slice|
|
||||
formula_files = slice.filter_map { |type, file| file if type == :formula }
|
||||
cask_files = slice.filter_map { |type, file| file if type == :cask }
|
||||
|
||||
slice_success = T.let(true, T::Boolean)
|
||||
if no_simulate
|
||||
slice_success = false unless valid_formulae?(tap, files: formula_files)
|
||||
slice_success = false unless valid_casks?(tap, files: cask_files)
|
||||
else
|
||||
os_arch_combinations.each do |os, arch|
|
||||
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless bottle_tag.valid_combination?
|
||||
|
||||
Homebrew::SimulateSystem.with(os:, arch:) do
|
||||
slice_success = false unless valid_formulae?(tap, bottle_tag:, files: formula_files)
|
||||
slice_success = false unless valid_casks?(tap, os_name: os, arch:, files: cask_files)
|
||||
end
|
||||
end
|
||||
end
|
||||
slice_success
|
||||
end
|
||||
success = false unless all_files_valid
|
||||
|
||||
success
|
||||
end
|
||||
|
||||
sig { params(filename: Pathname).returns(T::Boolean) }
|
||||
private_class_method def self.syntax_errors_or_warnings?(filename)
|
||||
# Retrieve messages about syntax errors/warnings printed to `$stderr`.
|
||||
_, err, status = system_command(RUBY_PATH, args: ["-c", "-w", filename], print_stderr: false).to_a
|
||||
# Compile in-process (much faster than spawning `ruby -c -w` per file),
|
||||
# buffering compile warnings so they can be filtered.
|
||||
error = T.let(nil, T.nilable(String))
|
||||
warnings = self.warning_buffer = []
|
||||
old_verbose = $VERBOSE
|
||||
$VERBOSE = true
|
||||
begin
|
||||
RubyVM::InstructionSequence.compile_file(filename.to_s)
|
||||
rescue ScriptError, ArgumentError => e
|
||||
error = "#{e.message.chomp}\n"
|
||||
ensure
|
||||
$VERBOSE = old_verbose
|
||||
self.warning_buffer = nil
|
||||
end
|
||||
|
||||
# Ignore unnecessary warning about named capture conflicts.
|
||||
# See https://bugs.ruby-lang.org/issues/12359.
|
||||
messages = err.lines
|
||||
.grep_v(/named capture conflicts a local variable/)
|
||||
.join
|
||||
messages = warnings.grep_v(/named capture conflicts a local variable/).join
|
||||
messages += error if error
|
||||
|
||||
$stderr.print messages
|
||||
|
||||
# Only syntax errors result in a non-zero status code. To detect syntax
|
||||
# warnings we also need to inspect the output to `$stderr`.
|
||||
!status.success? || !messages.chomp.empty?
|
||||
# Both syntax errors and syntax warnings count as failures.
|
||||
!messages.chomp.empty?
|
||||
end
|
||||
|
||||
sig {
|
||||
type_parameters(:U).params(
|
||||
items: T::Array[T.type_parameter(:U)],
|
||||
_block: T.proc.params(arg0: T::Array[T.type_parameter(:U)]).returns(T::Boolean),
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
private_class_method def self.parallel_slices_valid?(items, &_block)
|
||||
require "hardware"
|
||||
|
||||
worker_count = [Hardware::CPU.cores, items.length / MIN_FILES_PER_WORKER].min
|
||||
return yield(items) if worker_count <= 1
|
||||
|
||||
workers = items.each_slice((items.length.to_f / worker_count).ceil).map do |slice|
|
||||
reader, writer = IO.pipe
|
||||
stdout_file = Tempfile.new("readall-stdout")
|
||||
stderr_file = Tempfile.new("readall-stderr")
|
||||
pid = Process.fork do
|
||||
reader.close
|
||||
success = begin
|
||||
# Capture output so parallel workers cannot interleave lines.
|
||||
$stdout = stdout_file.to_io
|
||||
$stderr = stderr_file.to_io
|
||||
yield(slice)
|
||||
rescue Interrupt
|
||||
false
|
||||
# Report any worker exception as a validation failure.
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
$stderr.puts e.full_message
|
||||
false
|
||||
ensure
|
||||
$stdout.flush
|
||||
$stderr.flush
|
||||
end
|
||||
writer.write(Marshal.dump(success))
|
||||
writer.close
|
||||
exit!(true)
|
||||
end
|
||||
writer.close
|
||||
[pid, reader, stdout_file, stderr_file]
|
||||
end
|
||||
|
||||
success = T.let(true, T::Boolean)
|
||||
workers.each do |pid, reader, stdout_file, stderr_file|
|
||||
worker_success = begin
|
||||
# The data being loaded was written by our own forked child process.
|
||||
Marshal.load(reader) # rubocop:disable Security/MarshalLoad
|
||||
rescue EOFError
|
||||
nil
|
||||
end
|
||||
reader.close
|
||||
Process.wait(pid)
|
||||
|
||||
[stdout_file, stderr_file].each(&:rewind)
|
||||
$stdout.print stdout_file.read
|
||||
$stderr.print stderr_file.read
|
||||
[stdout_file, stderr_file].each(&:close!)
|
||||
|
||||
case worker_success
|
||||
when nil
|
||||
onoe "readall worker exited unexpectedly!"
|
||||
success = false
|
||||
when false
|
||||
success = false
|
||||
end
|
||||
end
|
||||
success
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ require_relative "full_name_split"
|
||||
require_relative "io_read"
|
||||
require_relative "move_to_extend_os"
|
||||
require_relative "negate_include"
|
||||
require_relative "no_base64"
|
||||
require_relative "no_fileutils_rmrf"
|
||||
require_relative "no_instance_variable_access_in_tests"
|
||||
require_relative "no_send_in_tests"
|
||||
|
||||
@@ -42,7 +42,7 @@ module RuboCop
|
||||
[
|
||||
:suite,
|
||||
:app,
|
||||
:appimage,
|
||||
:app_image,
|
||||
:pkg,
|
||||
:generated_script,
|
||||
:installer,
|
||||
|
||||
@@ -21,6 +21,7 @@ module RuboCop
|
||||
}.freeze,
|
||||
T::Hash[Symbol, Symbol],
|
||||
)
|
||||
LEGACY_FLIGHT_MSG = "Casks in official Homebrew taps must use `%<steps>s` instead of `%<flight>s`."
|
||||
KEYCHAIN_HASHES_SOURCE =
|
||||
'hashes = stdout.lines.grep(/^SHA-256 hash:/) { |l| l.split(":").second.strip }'
|
||||
KEYCHAIN_DELETE_SOURCE = T.let(
|
||||
@@ -60,14 +61,27 @@ module RuboCop
|
||||
next unless (flight_stanza = stanzas.find { |stanza| stanza.stanza_name == flight_block })
|
||||
|
||||
steps_stanza = stanzas.find { |stanza| stanza.stanza_name == steps_block }
|
||||
audit_flight_block(flight_stanza, steps_block) if steps_stanza.nil?
|
||||
converted_flight = autocorrect_flight_block?(flight_stanza, steps_block) if steps_stanza.nil?
|
||||
|
||||
# odeprecated: remove the official-tap scope in the next major or minor release.
|
||||
next unless official_homebrew_tap?(processed_source.file_path)
|
||||
next if converted_flight
|
||||
|
||||
add_offense(flight_stanza.method_node,
|
||||
message: format(LEGACY_FLIGHT_MSG, steps: steps_block, flight: flight_block))
|
||||
end
|
||||
|
||||
stanzas.each do |stanza|
|
||||
next unless INSTALL_STEP_PAIRS.value?(stanza.stanza_name)
|
||||
next unless stanza.method_node.block_type?
|
||||
|
||||
block_node = T.cast(stanza.method_node, RuboCop::AST::BlockNode)
|
||||
if (offense_node = brew_ruby_step_node(block_node))
|
||||
add_offense(offense_node, message: BREW_RUBY_STEP_MSG)
|
||||
next
|
||||
end
|
||||
next unless (offense_node = install_step_block_offense_node(
|
||||
T.cast(stanza.method_node, RuboCop::AST::BlockNode),
|
||||
block_node,
|
||||
allowed_methods: CASK_ALLOWED_STEP_METHODS,
|
||||
))
|
||||
|
||||
@@ -77,9 +91,9 @@ module RuboCop
|
||||
|
||||
private
|
||||
|
||||
sig { params(flight_stanza: RuboCop::Cask::AST::Stanza, steps_block: Symbol).void }
|
||||
def audit_flight_block(flight_stanza, steps_block)
|
||||
return unless flight_stanza.method_node.block_type?
|
||||
sig { params(flight_stanza: RuboCop::Cask::AST::Stanza, steps_block: Symbol).returns(T::Boolean) }
|
||||
def autocorrect_flight_block?(flight_stanza, steps_block)
|
||||
return false unless flight_stanza.method_node.block_type?
|
||||
|
||||
block_node = T.cast(flight_stanza.method_node, RuboCop::AST::BlockNode)
|
||||
step_lines = keychain_certificate_step_lines(block_node.body) ||
|
||||
@@ -89,7 +103,7 @@ module RuboCop
|
||||
default_target_base: :staged_path,
|
||||
rebuild_actions: false,
|
||||
permission_actions: true)
|
||||
return if step_lines.blank?
|
||||
return false if step_lines.blank?
|
||||
|
||||
add_offense(block_node.source_range,
|
||||
message: format(SIMPLE_STEP_CONVERSION_MSG, steps_block:)) do |corrector|
|
||||
@@ -98,6 +112,7 @@ module RuboCop
|
||||
install_steps_block_source(steps_block, step_lines, block_node.source_range.column),
|
||||
)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
sig { params(body_node: T.nilable(RuboCop::AST::Node)).returns(T.nilable(T::Array[String])) }
|
||||
|
||||
@@ -15,7 +15,17 @@ module RuboCop
|
||||
# TODO: Re-enable when formula `post_install` and `post_install_steps`
|
||||
# cannot coexist after the incremental conversion bridge is removed.
|
||||
# CONFLICT_MSG = "`post_install` and `post_install_steps` cannot both be used."
|
||||
LEGACY_POST_INSTALL_MSG =
|
||||
"Formulae in official Homebrew taps must use `post_install_steps` instead of `post_install`."
|
||||
REDUNDANT_SERVICE_PATH_DIRS_MSG = "`%<block>s` only creates directories created by `brew services`."
|
||||
EXPLICIT_BASE_MSG = "Formula install-step paths must specify their base explicitly."
|
||||
EXPLICIT_BASE_STEP_METHODS = [:if_path_exists, :unless_path_exists, :mkdir, :mkdir_p, :touch, :remove,
|
||||
:inreplace, :write, :write_file, :init_data_dir, :set_permissions].freeze
|
||||
RUN_PATH_KEYWORDS = [:stdin_path, :stdout_path, :chdir].freeze
|
||||
ABSOLUTE_PATH_TEMPLATE_TOKENS = %w[
|
||||
HOMEBREW_PREFIX HOMEBREW_CELLAR prefix opt_prefix bin sbin lib libexec share pkgshare var etc pkgetc rack
|
||||
staged_path appdir caskroom_path temp bash_completion zsh_completion fish_completion pwsh_completion
|
||||
].freeze
|
||||
CERTIFICATE_REMOVE_SOURCE = 'rm(pkgetc/"cert.pem") if (pkgetc/"cert.pem").exist?'
|
||||
CERTIFICATE_INSTALL_SYMLINK_SOURCE =
|
||||
'pkgetc.install_symlink Formula["ca-certificates"].pkgetc/"cert.pem"'
|
||||
@@ -89,7 +99,11 @@ module RuboCop
|
||||
# problem CONFLICT_MSG
|
||||
# end
|
||||
|
||||
audit_step_block(post_install_steps_block)
|
||||
redundant_post_install_steps = post_install_steps_block.present? &&
|
||||
redundant_service_path_dirs_block?(post_install_steps_block,
|
||||
service_path_dirs,
|
||||
:post_install_steps)
|
||||
audit_step_block(post_install_steps_block) unless redundant_post_install_steps
|
||||
add_redundant_service_path_dirs_offense(post_install_steps_block, service_path_dirs, :post_install_steps)
|
||||
redundant_post_install = post_install_method.present? &&
|
||||
redundant_service_path_dirs_block?(post_install_method, service_path_dirs,
|
||||
@@ -97,18 +111,114 @@ module RuboCop
|
||||
add_redundant_service_path_dirs_offense(post_install_method, service_path_dirs, :post_install)
|
||||
return if redundant_post_install
|
||||
|
||||
audit_post_install_method(post_install_method, post_install_steps_block,
|
||||
body_node, formula_nodes.class_node.const_name)
|
||||
converted_post_install = autocorrect_post_install_method?(post_install_method, post_install_steps_block,
|
||||
body_node, formula_nodes.class_node.const_name)
|
||||
# odeprecated: remove the official-tap scope in the next major or minor release.
|
||||
return unless official_homebrew_tap?(processed_source.file_path)
|
||||
return if post_install_method.nil? || converted_post_install
|
||||
|
||||
add_offense(post_install_method, message: LEGACY_POST_INSTALL_MSG)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(block_node: T.nilable(RuboCop::AST::BlockNode)).void }
|
||||
def audit_step_block(block_node)
|
||||
return unless (offense_node = install_step_block_offense_node(block_node))
|
||||
return if block_node.nil?
|
||||
|
||||
offending_node(offense_node)
|
||||
problem STEP_BLOCK_MSG
|
||||
if (offense_node = brew_ruby_step_node(block_node))
|
||||
offending_node(offense_node)
|
||||
problem BREW_RUBY_STEP_MSG
|
||||
return
|
||||
end
|
||||
|
||||
if (offense_node = install_step_block_offense_node(block_node))
|
||||
offending_node(offense_node)
|
||||
problem STEP_BLOCK_MSG
|
||||
return
|
||||
end
|
||||
|
||||
add_implicit_var_path_offenses(block_node)
|
||||
end
|
||||
|
||||
sig { params(block_node: RuboCop::AST::BlockNode).void }
|
||||
def add_implicit_var_path_offenses(block_node)
|
||||
block_node.each_descendant(:send) do |node|
|
||||
send_node = T.cast(node, RuboCop::AST::SendNode)
|
||||
next if send_node.receiver
|
||||
|
||||
if EXPLICIT_BASE_STEP_METHODS.include?(send_node.method_name)
|
||||
add_implicit_var_base_offense(send_node)
|
||||
elsif send_node.method_name == :run
|
||||
add_implicit_var_run_path_offenses(send_node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(send_node: RuboCop::AST::SendNode).void }
|
||||
def add_implicit_var_base_offense(send_node)
|
||||
path_node = send_node.arguments.first
|
||||
return if path_node.nil? || explicit_formula_step_path?(path_node)
|
||||
|
||||
options = send_node.arguments.last
|
||||
options = nil unless options&.hash_type?
|
||||
return if options && T.cast(options, RuboCop::AST::HashNode).pairs.any? do |pair|
|
||||
pair.key.sym_type? && pair.key.value == :base
|
||||
end
|
||||
|
||||
add_offense(send_node, message: EXPLICIT_BASE_MSG) do |corrector|
|
||||
if options
|
||||
options = T.cast(options, RuboCop::AST::HashNode)
|
||||
pair = options.pairs.last
|
||||
if pair
|
||||
corrector.insert_after(pair.source_range, ", base: :var")
|
||||
else
|
||||
corrector.replace(options, "base: :var")
|
||||
end
|
||||
else
|
||||
argument = send_node.arguments.last
|
||||
next if argument.nil?
|
||||
|
||||
range = if argument.loc.respond_to?(:heredoc_end) && argument.loc.heredoc_end
|
||||
argument.loc.expression
|
||||
else
|
||||
argument.source_range
|
||||
end
|
||||
corrector.insert_after(range, ", base: :var")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(send_node: RuboCop::AST::SendNode).void }
|
||||
def add_implicit_var_run_path_offenses(send_node)
|
||||
options = send_node.arguments.last
|
||||
return unless options&.hash_type?
|
||||
|
||||
T.cast(options, RuboCop::AST::HashNode).pairs.each do |pair|
|
||||
next if !pair.key.sym_type? || !RUN_PATH_KEYWORDS.include?(pair.key.value)
|
||||
next if explicit_formula_step_path?(pair.value)
|
||||
|
||||
add_offense(pair.value, message: EXPLICIT_BASE_MSG) do |corrector|
|
||||
next unless pair.value.str_type?
|
||||
|
||||
path = T.cast(pair.value, RuboCop::AST::StrNode).str_content
|
||||
corrector.replace(pair.value, "{{var}}/#{path}".dump)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::Node).returns(T::Boolean) }
|
||||
def explicit_formula_step_path?(node)
|
||||
if node.array_type?
|
||||
paths = node.child_nodes
|
||||
return paths.present? && paths.all? { |path| explicit_formula_step_path?(path) }
|
||||
end
|
||||
return false unless node.str_type?
|
||||
|
||||
path = T.cast(node, RuboCop::AST::StrNode).str_content
|
||||
return true if path.start_with?("/", "~")
|
||||
|
||||
ABSOLUTE_PATH_TEMPLATE_TOKENS.any? { |token| path.start_with?("{{#{token}}}") }
|
||||
end
|
||||
|
||||
sig {
|
||||
@@ -117,14 +227,15 @@ module RuboCop
|
||||
post_install_steps_block: T.nilable(RuboCop::AST::BlockNode),
|
||||
formula_body: RuboCop::AST::Node,
|
||||
formula_class: String,
|
||||
).void
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def audit_post_install_method(post_install_method, post_install_steps_block, formula_body, formula_class)
|
||||
return if post_install_method.nil?
|
||||
return unless post_install_method.def_type?
|
||||
def autocorrect_post_install_method?(post_install_method, post_install_steps_block, formula_body,
|
||||
formula_class)
|
||||
return false if post_install_method.nil?
|
||||
return false unless post_install_method.def_type?
|
||||
|
||||
post_install_def = T.cast(post_install_method, RuboCop::AST::DefNode)
|
||||
return if post_install_steps_block && post_install_steps_block.loc.line > post_install_def.loc.line
|
||||
return false if post_install_steps_block && post_install_steps_block.loc.line > post_install_def.loc.line
|
||||
|
||||
step_nodes = T.let({}, T::Hash[RuboCop::AST::Node, T::Array[String]])
|
||||
removable_methods = T.let([], T::Array[RuboCop::AST::Node])
|
||||
@@ -139,16 +250,16 @@ module RuboCop
|
||||
unless step_nodes.empty?
|
||||
add_formula_step_conversion_offense(post_install_def, post_install_steps_block, direct_nodes, step_nodes,
|
||||
removable_methods)
|
||||
return
|
||||
return true
|
||||
end
|
||||
|
||||
return if post_install_steps_block
|
||||
return false if post_install_steps_block
|
||||
|
||||
step_lines = simple_install_step_lines(post_install_def.body,
|
||||
default_base: :var,
|
||||
default_source_base: :prefix,
|
||||
default_target_base: :prefix)
|
||||
return if step_lines.blank?
|
||||
return false if step_lines.blank?
|
||||
|
||||
add_offense(post_install_method,
|
||||
message: format(SIMPLE_STEP_CONVERSION_MSG, steps_block: "post_install_steps")) do |corrector|
|
||||
@@ -157,6 +268,7 @@ module RuboCop
|
||||
install_steps_block_source(:post_install_steps, step_lines, post_install_method.source_range.column),
|
||||
)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
sig {
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Homebrew
|
||||
# Enforces the use of `String#unpack1` and `Array#pack` over the
|
||||
# `base64` gem, which Homebrew no longer includes.
|
||||
#
|
||||
# ### Example
|
||||
#
|
||||
# ```ruby
|
||||
# # bad
|
||||
# require "base64"
|
||||
# Base64.decode64(encoded)
|
||||
# Base64.strict_encode64(decoded)
|
||||
#
|
||||
# # good
|
||||
# encoded.unpack1("m")
|
||||
# [decoded].pack("m0")
|
||||
# ```
|
||||
class NoBase64 < Base
|
||||
include RangeHelp
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = "Homebrew no longer includes the `base64` gem; " \
|
||||
"use `String#unpack1` or `Array#pack` instead."
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).void }
|
||||
def on_send(node)
|
||||
if require_base64?(node)
|
||||
add_offense(node) do |corrector|
|
||||
parent = node.parent
|
||||
next if parent && !parent.begin_type?
|
||||
|
||||
corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
|
||||
end
|
||||
elsif top_level_const?(node.receiver, :Base64)
|
||||
add_offense(node) do |corrector|
|
||||
autocorrect_base64_call(corrector, node)
|
||||
end
|
||||
end
|
||||
end
|
||||
alias on_csend on_send
|
||||
|
||||
sig { params(node: RuboCop::AST::ConstNode).void }
|
||||
def on_const(node)
|
||||
return unless top_level_const?(node, :Base64)
|
||||
|
||||
parent = node.parent
|
||||
return if parent.is_a?(RuboCop::AST::SendNode) && parent.receiver == node
|
||||
# Formulae for base64 tools are legitimately named `Base64`.
|
||||
return if parent.is_a?(RuboCop::AST::ClassNode) && parent.identifier == node
|
||||
|
||||
add_offense(node)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).returns(T::Boolean) }
|
||||
def require_base64?(node)
|
||||
return false unless node.method?(:require)
|
||||
|
||||
receiver = node.receiver
|
||||
return false if receiver && !top_level_const?(receiver, :Kernel)
|
||||
|
||||
arg = node.first_argument
|
||||
node.arguments.one? && arg.is_a?(RuboCop::AST::StrNode) && arg.value == "base64"
|
||||
end
|
||||
|
||||
sig { params(node: T.nilable(RuboCop::AST::Node), name: Symbol).returns(T::Boolean) }
|
||||
def top_level_const?(node, name)
|
||||
return false unless node.is_a?(RuboCop::AST::ConstNode)
|
||||
return false if node.short_name != name
|
||||
|
||||
namespace = node.namespace
|
||||
namespace.nil? || namespace.cbase_type?
|
||||
end
|
||||
|
||||
sig { params(corrector: RuboCop::Cop::Corrector, node: RuboCop::AST::SendNode).void }
|
||||
def autocorrect_base64_call(corrector, node)
|
||||
return unless node.arguments.one?
|
||||
|
||||
arg = node.first_argument
|
||||
replacement = case node.method_name
|
||||
when :decode64, :strict_decode64
|
||||
directive = (node.method_name == :decode64) ? "m" : "m0"
|
||||
"#{arg.source}.unpack1(\"#{directive}\")" if chainable?(arg)
|
||||
when :encode64, :strict_encode64
|
||||
directive = (node.method_name == :encode64) ? "m" : "m0"
|
||||
"[#{arg.source}].pack(\"#{directive}\")" if !arg.splat_type? && !arg.block_pass_type?
|
||||
end
|
||||
return if replacement.nil?
|
||||
|
||||
corrector.replace(node, replacement)
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::Node).returns(T::Boolean) }
|
||||
def chainable?(node)
|
||||
if node.is_a?(RuboCop::AST::SendNode)
|
||||
!node.operator_method? && !node.assignment_method?
|
||||
else
|
||||
node.variable? || node.const_type? || node.begin_type? ||
|
||||
(node.literal? && !node.range_type?)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -28,9 +28,11 @@ module RuboCop
|
||||
:vst_plugin,
|
||||
:vst3_plugin,
|
||||
].freeze
|
||||
LINUX_ONLY_CASK_STANZAS = [:app_image].freeze
|
||||
|
||||
CASK_STANZA_ORDER = T.let(RuboCop::Cask::Constants::STANZA_ORDER, T::Array[Symbol])
|
||||
MACOS_DEPENDENCY_STANZAS = [:macos, :maximum_macos].freeze
|
||||
LINUX_DEPENDENCY_STANZAS = [:linux].freeze
|
||||
|
||||
RESTRICT_ON_SEND = [:depends_on].freeze
|
||||
|
||||
@@ -41,6 +43,7 @@ module RuboCop
|
||||
return if send_node.method_name != :cask
|
||||
|
||||
add_missing_macos_dependency(node)
|
||||
add_missing_linux_dependency(node)
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).void }
|
||||
@@ -114,19 +117,14 @@ module RuboCop
|
||||
return if os_depends_on?(body)
|
||||
|
||||
macos_stanza = stanzas.find do |stanza|
|
||||
case stanza.method_name
|
||||
when :installer
|
||||
if stanza.method_name == :installer
|
||||
stanza.arguments.any? do |argument|
|
||||
argument.hash_type? && argument.pairs.any? { |pair| symbol_key(pair) == :manual }
|
||||
argument.hash_type? && argument.pairs.any? do |pair|
|
||||
symbol_key(pair) == :manual
|
||||
end
|
||||
end
|
||||
when :os
|
||||
pairs = depends_on_pairs(stanza)
|
||||
pairs.any? { |pair| symbol_key(pair) == :macos } &&
|
||||
pairs.none? { |pair| symbol_key(pair) == :linux }
|
||||
when *MACOS_ONLY_CASK_STANZAS
|
||||
true
|
||||
else
|
||||
false
|
||||
MACOS_ONLY_CASK_STANZAS.include?(stanza.method_name)
|
||||
end
|
||||
end
|
||||
return unless macos_stanza
|
||||
@@ -145,14 +143,6 @@ module RuboCop
|
||||
range_by_whole_lines(following_stanza.source_range, include_final_newline: false),
|
||||
" depends_on :macos\n\n",
|
||||
)
|
||||
elsif (preceding_stanza = stanzas.rfind do |stanza|
|
||||
stanza_index = CASK_STANZA_ORDER.index(stanza.method_name)
|
||||
stanza_index && stanza_index <= depends_on_stanza_index
|
||||
end)
|
||||
corrector.insert_after(
|
||||
range_by_whole_lines(preceding_stanza.source_range, include_final_newline: true),
|
||||
"\n depends_on :macos\n",
|
||||
)
|
||||
else
|
||||
corrector.insert_before(
|
||||
range_by_whole_lines(macos_stanza.source_range, include_final_newline: false),
|
||||
@@ -162,6 +152,46 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::BlockNode).void }
|
||||
def add_missing_linux_dependency(node)
|
||||
body = node.body
|
||||
return unless body
|
||||
|
||||
stanzas = (body.begin_type? ? body.child_nodes : [body]).filter_map do |child|
|
||||
if child.send_type?
|
||||
T.cast(child, RuboCop::AST::SendNode)
|
||||
elsif child.block_type?
|
||||
T.cast(child, RuboCop::AST::BlockNode).send_node
|
||||
end
|
||||
end
|
||||
return if os_depends_on?(body)
|
||||
|
||||
linux_stanza = stanzas.find { |stanza| LINUX_ONLY_CASK_STANZAS.include?(stanza.method_name) }
|
||||
return unless linux_stanza
|
||||
|
||||
add_offense(linux_stanza.source_range,
|
||||
message: "Add `depends_on :linux` for Linux-only casks.") do |corrector|
|
||||
depends_on_stanza_index = CASK_STANZA_ORDER.index(:depends_on) ||
|
||||
raise("unexpected nil value for depends_on stanza index")
|
||||
following_stanza = stanzas.find do |stanza|
|
||||
stanza_index = CASK_STANZA_ORDER.index(stanza.method_name)
|
||||
stanza_index && stanza_index > depends_on_stanza_index
|
||||
end
|
||||
|
||||
if following_stanza
|
||||
corrector.insert_before(
|
||||
range_by_whole_lines(following_stanza.source_range, include_final_newline: false),
|
||||
" depends_on :linux\n\n",
|
||||
)
|
||||
else
|
||||
corrector.insert_before(
|
||||
range_by_whole_lines(linux_stanza.source_range, include_final_newline: false),
|
||||
" depends_on :linux\n\n",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).returns(T::Array[RuboCop::AST::PairNode]) }
|
||||
def depends_on_pairs(node)
|
||||
node.arguments.filter_map do |argument|
|
||||
@@ -198,8 +228,7 @@ module RuboCop
|
||||
next false if send_node.method_name != :depends_on
|
||||
|
||||
bare_os_depends_on?(send_node, :macos) || bare_os_depends_on?(send_node, :linux) ||
|
||||
top_level_macos_depends_on?(send_node) ||
|
||||
depends_on_pairs(send_node).any? { |pair| symbol_key(pair) == :linux }
|
||||
top_level_macos_depends_on?(send_node) || top_level_linux_depends_on?(send_node)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -212,6 +241,11 @@ module RuboCop
|
||||
def top_level_macos_depends_on?(node)
|
||||
depends_on_pairs(node).any? { |pair| MACOS_DEPENDENCY_STANZAS.include?(symbol_key(pair)) }
|
||||
end
|
||||
|
||||
sig { params(node: RuboCop::AST::SendNode).returns(T::Boolean) }
|
||||
def top_level_linux_depends_on?(node)
|
||||
depends_on_pairs(node).any? { |pair| LINUX_DEPENDENCY_STANZAS.include?(symbol_key(pair)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -143,13 +143,14 @@ module RuboCop
|
||||
def resolves_problems(node)
|
||||
unless node.str_type?
|
||||
offending_node(node)
|
||||
problem "`resolves` should be passed identifier strings (CVE/GHSA id or issue URL)"
|
||||
problem "`resolves` should be passed identifier strings (CVE/GHSA/OSV id or issue URL)"
|
||||
return
|
||||
end
|
||||
|
||||
value = string_content(node)
|
||||
return if value.match?(/\ACVE-\d{4}-\d{4,}\z/)
|
||||
return if value.match?(/\AGHSA(-[23456789cfghjmpqrvwx]{4}){3}\z/)
|
||||
return if value.match?(/\AOSV-\d{4}-\d+\z/)
|
||||
return if value.match?(%r{\Ahttps?://})
|
||||
|
||||
offending_node(node)
|
||||
@@ -159,7 +160,7 @@ module RuboCop
|
||||
corrector.replace(node.source_range, corrected.inspect)
|
||||
end
|
||||
else
|
||||
problem "`resolves` should be a CVE/GHSA identifier or issue URL, got: #{value.inspect}"
|
||||
problem "`resolves` should be a CVE/GHSA/OSV identifier or issue URL, got: #{value.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
module RuboCop
|
||||
module Cop
|
||||
module InstallStepsHelper
|
||||
OFFICIAL_HOMEBREW_TAP_PATH_REGEX = %r{(?:\A|/)Taps/homebrew/homebrew-[\w-]+/}i
|
||||
FILE_PREPARATION_STEP_METHODS =
|
||||
[:mkdir, :mkdir_p, :touch, :move, :mv, :move_children, :move_contents, :copy, :remove, :inreplace,
|
||||
:symlink,
|
||||
@@ -50,6 +51,7 @@ module RuboCop
|
||||
"#{(ALLOWED_STEP_METHODS - COMPATIBILITY_STEP_METHODS).map { |method| "`#{method}`" }.join(", ")}.".freeze,
|
||||
String,
|
||||
)
|
||||
BREW_RUBY_STEP_MSG = "Install steps must not use `brew ruby` because it enables developer mode."
|
||||
SIMPLE_STEP_CONVERSION_MSG = "Use `%<steps_block>s` for simple file preparation."
|
||||
REBUILD_ACTION_STEP_LINES = T.let(
|
||||
T.let([
|
||||
@@ -82,6 +84,11 @@ module RuboCop
|
||||
T::Hash[String, String],
|
||||
)
|
||||
|
||||
sig { params(file_path: String).returns(T::Boolean) }
|
||||
def official_homebrew_tap?(file_path)
|
||||
file_path.match?(OFFICIAL_HOMEBREW_TAP_PATH_REGEX)
|
||||
end
|
||||
|
||||
sig { params(allowed_methods: T::Array[Symbol]).returns(String) }
|
||||
def step_block_msg(allowed_methods)
|
||||
"Steps blocks may only contain install step DSL calls. Prefer canonical calls: " \
|
||||
@@ -113,6 +120,33 @@ module RuboCop
|
||||
nil
|
||||
end
|
||||
|
||||
sig { params(block_node: T.nilable(RuboCop::AST::BlockNode)).returns(T.nilable(RuboCop::AST::Node)) }
|
||||
def brew_ruby_step_node(block_node)
|
||||
return if block_node.nil?
|
||||
|
||||
block_node.each_descendant(:send).each do |node|
|
||||
send_node = T.cast(node, RuboCop::AST::SendNode)
|
||||
next if send_node.receiver.present? || send_node.method_name != :run
|
||||
|
||||
command = send_node.first_argument
|
||||
next unless command&.str_type?
|
||||
next if command.str_content != "{{HOMEBREW_BREW_FILE}}"
|
||||
|
||||
options = send_node.arguments.last
|
||||
next unless options&.hash_type?
|
||||
|
||||
args = T.cast(options, RuboCop::AST::HashNode).pairs.find do |pair|
|
||||
pair.key.sym_type? && pair.key.value == :args
|
||||
end&.value
|
||||
next unless args&.array_type?
|
||||
|
||||
first_arg = args.child_nodes.first
|
||||
return command if first_arg&.str_type? && first_arg.str_content == "ruby"
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
body_node: T.nilable(RuboCop::AST::Node),
|
||||
|
||||
@@ -120,9 +120,6 @@ class Sandbox
|
||||
true
|
||||
end
|
||||
|
||||
sig { params(install_from_tests: T::Boolean).void }
|
||||
def self.ensure_sandbox_installed!(install_from_tests: false); end
|
||||
|
||||
sig { void }
|
||||
def self.ensure_sandbox_available!
|
||||
return if available?
|
||||
@@ -145,24 +142,8 @@ class Sandbox
|
||||
sig { void }
|
||||
def self.reset_state!; end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def self.configuration_commands = []
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def self.configuration_command_messages = []
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def self.sandbox_install_command = nil
|
||||
|
||||
sig { void }
|
||||
def self.configure!
|
||||
ensure_sandbox_installed!
|
||||
reset_state!
|
||||
end
|
||||
|
||||
sig { params(command: T.any(String, Pathname), writable_path: T.any(String, Pathname), deny_network: T::Boolean).void }
|
||||
def self.run_command(*command, writable_path:, deny_network: false)
|
||||
ensure_sandbox_installed!
|
||||
ensure_sandbox_available!
|
||||
|
||||
writable_path = Pathname(writable_path).expand_path
|
||||
|
||||
@@ -76,6 +76,11 @@ module Homebrew
|
||||
@os = @arch = nil
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def simulating?
|
||||
os.present? || arch.present?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def simulating_or_running_on_macos?
|
||||
[:macos, *MacOSVersion::SYMBOLS.keys].include?(os)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# typed: true
|
||||
|
||||
# DO NOT EDIT MANUALLY
|
||||
# This is an autogenerated file for dynamic methods in `Homebrew::Cmd::SetupSandbox`.
|
||||
# Please instead update this file by running `bin/tapioca dsl Homebrew::Cmd::SetupSandbox`.
|
||||
|
||||
|
||||
class Homebrew::Cmd::SetupSandbox
|
||||
sig { returns(Homebrew::Cmd::SetupSandbox::Args) }
|
||||
def args; end
|
||||
end
|
||||
|
||||
class Homebrew::Cmd::SetupSandbox::Args < Homebrew::CLI::Args; end
|
||||
@@ -17,6 +17,9 @@ class Homebrew::Cmd::Vulns::Args < Homebrew::CLI::Args
|
||||
sig { returns(T::Boolean) }
|
||||
def deps?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def fix_available?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def j?; end
|
||||
|
||||
@@ -29,6 +32,9 @@ class Homebrew::Cmd::Vulns::Args < Homebrew::CLI::Args
|
||||
sig { returns(T.nilable(String)) }
|
||||
def max_summary; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def no_fix_available?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def no_ignore_patches?; end
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ module Homebrew::EnvConfig
|
||||
sig { returns(T::Boolean) }
|
||||
def ask?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def auto_update_quiet?; end
|
||||
|
||||
sig { returns(String) }
|
||||
def auto_update_secs; end
|
||||
|
||||
|
||||
@@ -153,6 +153,9 @@ module RSpec::Matchers
|
||||
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
|
||||
def be_installed_on_request(*args, &block); end
|
||||
|
||||
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
|
||||
def be_installed_on_request_present(*args, &block); end
|
||||
|
||||
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
|
||||
def be_latest_version_installed(*args, &block); end
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ class RuboCop::Cask::AST::Stanza
|
||||
def app?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def appcast?; end
|
||||
def app_image?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def appimage?; end
|
||||
def appcast?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def arch?; end
|
||||
|
||||
Generated
+36
-36
@@ -19,12 +19,12 @@ class CSV
|
||||
|
||||
def <<(row); end
|
||||
def add_row(row); end
|
||||
def binmode(*args, **_arg1, &block); end
|
||||
def binmode(*_arg0, **_arg1, &_arg2); end
|
||||
def binmode?; end
|
||||
def close(*args, **_arg1, &block); end
|
||||
def close_read(*args, **_arg1, &block); end
|
||||
def close_write(*args, **_arg1, &block); end
|
||||
def closed?(*args, **_arg1, &block); end
|
||||
def close(*_arg0, **_arg1, &_arg2); end
|
||||
def close_read(*_arg0, **_arg1, &_arg2); end
|
||||
def close_write(*_arg0, **_arg1, &_arg2); end
|
||||
def closed?(*_arg0, **_arg1, &_arg2); end
|
||||
def col_sep; end
|
||||
def convert(name = T.unsafe(nil), &converter); end
|
||||
def converters; end
|
||||
@@ -32,53 +32,53 @@ class CSV
|
||||
def encoding; end
|
||||
def eof; end
|
||||
def eof?; end
|
||||
def external_encoding(*args, **_arg1, &block); end
|
||||
def fcntl(*args, **_arg1, &block); end
|
||||
def external_encoding(*_arg0, **_arg1, &_arg2); end
|
||||
def fcntl(*_arg0, **_arg1, &_arg2); end
|
||||
def field_size_limit; end
|
||||
def fileno(*args, **_arg1, &block); end
|
||||
def fileno(*_arg0, **_arg1, &_arg2); end
|
||||
def flock(*args); end
|
||||
def flush(*args, **_arg1, &block); end
|
||||
def flush(*_arg0, **_arg1, &_arg2); end
|
||||
def force_quotes?; end
|
||||
def fsync(*args, **_arg1, &block); end
|
||||
def fsync(*_arg0, **_arg1, &_arg2); end
|
||||
def gets; end
|
||||
def header_convert(name = T.unsafe(nil), &converter); end
|
||||
def header_converters; end
|
||||
def header_row?; end
|
||||
def headers; end
|
||||
def inspect; end
|
||||
def internal_encoding(*args, **_arg1, &block); end
|
||||
def internal_encoding(*_arg0, **_arg1, &_arg2); end
|
||||
def ioctl(*args); end
|
||||
def isatty(*args, **_arg1, &block); end
|
||||
def isatty(*_arg0, **_arg1, &_arg2); end
|
||||
def liberal_parsing?; end
|
||||
def line; end
|
||||
def lineno; end
|
||||
def max_field_size; end
|
||||
def path; end
|
||||
def pid(*args, **_arg1, &block); end
|
||||
def pos(*args, **_arg1, &block); end
|
||||
def pos=(*args, **_arg1, &block); end
|
||||
def pid(*_arg0, **_arg1, &_arg2); end
|
||||
def pos(*_arg0, **_arg1, &_arg2); end
|
||||
def pos=(*_arg0, **_arg1, &_arg2); end
|
||||
def puts(row); end
|
||||
def quote_char; end
|
||||
def read; end
|
||||
def readline; end
|
||||
def readlines; end
|
||||
def reopen(*args, **_arg1, &block); end
|
||||
def reopen(*_arg0, **_arg1, &_arg2); end
|
||||
def return_headers?; end
|
||||
def rewind; end
|
||||
def row_sep; end
|
||||
def seek(*args, **_arg1, &block); end
|
||||
def seek(*_arg0, **_arg1, &_arg2); end
|
||||
def shift; end
|
||||
def skip_blanks?; end
|
||||
def skip_lines; end
|
||||
def stat(*args); end
|
||||
def string(*args, **_arg1, &block); end
|
||||
def sync(*args, **_arg1, &block); end
|
||||
def sync=(*args, **_arg1, &block); end
|
||||
def tell(*args, **_arg1, &block); end
|
||||
def string(*_arg0, **_arg1, &_arg2); end
|
||||
def sync(*_arg0, **_arg1, &_arg2); end
|
||||
def sync=(*_arg0, **_arg1, &_arg2); end
|
||||
def tell(*_arg0, **_arg1, &_arg2); end
|
||||
def to_i; end
|
||||
def to_io; end
|
||||
def truncate(*args, **_arg1, &block); end
|
||||
def tty?(*args, **_arg1, &block); end
|
||||
def truncate(*_arg0, **_arg1, &_arg2); end
|
||||
def tty?(*_arg0, **_arg1, &_arg2); end
|
||||
def unconverted_fields?; end
|
||||
def write_headers?; end
|
||||
|
||||
@@ -93,7 +93,6 @@ class CSV
|
||||
def header_fields_converter; end
|
||||
def normalize_converters(converters); end
|
||||
def parser; end
|
||||
def parser_enumerator; end
|
||||
def parser_fields_converter; end
|
||||
def parser_options; end
|
||||
def raw_encoding; end
|
||||
@@ -103,22 +102,22 @@ class CSV
|
||||
|
||||
class << self
|
||||
def filter(input = T.unsafe(nil), output = T.unsafe(nil), **options); end
|
||||
def foreach(path, mode = T.unsafe(nil), **options, &block); end
|
||||
def foreach(path_or_io, mode = T.unsafe(nil), **options, &block); end
|
||||
def generate(str = T.unsafe(nil), **options); end
|
||||
def generate_line(row, **options); end
|
||||
def generate_lines(rows, **options); end
|
||||
def instance(data = T.unsafe(nil), **options); end
|
||||
def open(filename_or_io, mode = T.unsafe(nil), **options); end
|
||||
def open(path_or_io, mode = T.unsafe(nil), **options); end
|
||||
def parse(str, **options, &block); end
|
||||
def parse_line(line, **options); end
|
||||
def read(path, **options); end
|
||||
def readlines(path, **options); end
|
||||
def table(path, **options); end
|
||||
def read(path_or_io, **options); end
|
||||
def readlines(path_or_io, **options); end
|
||||
def table(path_or_io, **options); end
|
||||
|
||||
private
|
||||
|
||||
def create_stringio(str, mode, opts); end
|
||||
def may_enable_bom_detection_automatically(filename_or_io, mode, options, file_opts); end
|
||||
def may_enable_bom_detection_automatically(path_or_io, mode, options, file_opts); end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -187,6 +186,7 @@ class CSV::Parser
|
||||
def initialize(input, options); end
|
||||
|
||||
def column_separator; end
|
||||
def eof?; end
|
||||
def field_size_limit; end
|
||||
def header_row?; end
|
||||
def headers; end
|
||||
@@ -310,7 +310,7 @@ class CSV::Row
|
||||
def dig(index_or_header, *indexes); end
|
||||
def each(&block); end
|
||||
def each_pair(&block); end
|
||||
def empty?(*args, **_arg1, &block); end
|
||||
def empty?(*_arg0, **_arg1, &_arg2); end
|
||||
def fetch(header, *varargs); end
|
||||
def field(header_or_index, minimum_index = T.unsafe(nil)); end
|
||||
def field?(data); end
|
||||
@@ -324,10 +324,10 @@ class CSV::Row
|
||||
def index(header, minimum_index = T.unsafe(nil)); end
|
||||
def inspect; end
|
||||
def key?(header); end
|
||||
def length(*args, **_arg1, &block); end
|
||||
def length(*_arg0, **_arg1, &_arg2); end
|
||||
def member?(header); end
|
||||
def push(*args); end
|
||||
def size(*args, **_arg1, &block); end
|
||||
def size(*_arg0, **_arg1, &_arg2); end
|
||||
def to_ary(*_arg0); end
|
||||
def to_csv(**options); end
|
||||
def to_h; end
|
||||
@@ -368,13 +368,13 @@ class CSV::Table
|
||||
def delete_if(&block); end
|
||||
def dig(index_or_header, *index_or_headers); end
|
||||
def each(&block); end
|
||||
def empty?(*args, **_arg1, &block); end
|
||||
def empty?(*_arg0, **_arg1, &_arg2); end
|
||||
def headers; end
|
||||
def inspect; end
|
||||
def length(*args, **_arg1, &block); end
|
||||
def length(*_arg0, **_arg1, &_arg2); end
|
||||
def mode; end
|
||||
def push(*rows); end
|
||||
def size(*args, **_arg1, &block); end
|
||||
def size(*_arg0, **_arg1, &_arg2); end
|
||||
def to_a; end
|
||||
def to_csv(write_headers: T.unsafe(nil), limit: T.unsafe(nil), **options); end
|
||||
def to_s(write_headers: T.unsafe(nil), limit: T.unsafe(nil), **options); end
|
||||
Generated
+73
-5
@@ -494,6 +494,21 @@ class RBS::AST::Ruby::Annotations::ModuleAliasAnnotation < ::RBS::AST::Ruby::Ann
|
||||
def type_fingerprint; end
|
||||
end
|
||||
|
||||
class RBS::AST::Ruby::Annotations::ModuleSelfAnnotation < ::RBS::AST::Ruby::Annotations::Base
|
||||
def initialize(location:, prefix_location:, keyword_location:, colon_location:, name:, args:, open_bracket_location:, close_bracket_location:, args_comma_locations:, comment_location:); end
|
||||
|
||||
def args; end
|
||||
def args_comma_locations; end
|
||||
def close_bracket_location; end
|
||||
def colon_location; end
|
||||
def comment_location; end
|
||||
def keyword_location; end
|
||||
def map_type_name; end
|
||||
def name; end
|
||||
def open_bracket_location; end
|
||||
def type_fingerprint; end
|
||||
end
|
||||
|
||||
class RBS::AST::Ruby::Annotations::NodeTypeAssertion < ::RBS::AST::Ruby::Annotations::Base
|
||||
def initialize(location:, prefix_location:, type:); end
|
||||
|
||||
@@ -723,9 +738,11 @@ class RBS::AST::Ruby::Members::Base
|
||||
end
|
||||
|
||||
class RBS::AST::Ruby::Members::DefMember < ::RBS::AST::Ruby::Members::Base
|
||||
def initialize(buffer, name, node, method_type, leading_comment); end
|
||||
def initialize(buffer, name, node, method_type, leading_comment, kind: T.unsafe(nil)); end
|
||||
|
||||
def annotations; end
|
||||
def instance?; end
|
||||
def kind; end
|
||||
def leading_comment; end
|
||||
def location; end
|
||||
def method_type; end
|
||||
@@ -734,6 +751,7 @@ class RBS::AST::Ruby::Members::DefMember < ::RBS::AST::Ruby::Members::Base
|
||||
def node; end
|
||||
def overloading?; end
|
||||
def overloads; end
|
||||
def singleton?; end
|
||||
def type_fingerprint; end
|
||||
end
|
||||
|
||||
@@ -804,6 +822,16 @@ class RBS::AST::Ruby::Members::MixinMember < ::RBS::AST::Ruby::Members::Base
|
||||
def type_fingerprint; end
|
||||
end
|
||||
|
||||
class RBS::AST::Ruby::Members::ModuleSelfMember < ::RBS::AST::Ruby::Members::Base
|
||||
def initialize(buffer, annotation); end
|
||||
|
||||
def annotation; end
|
||||
def args; end
|
||||
def location; end
|
||||
def name; end
|
||||
def type_fingerprint; end
|
||||
end
|
||||
|
||||
class RBS::AST::Ruby::Members::PrependMember < ::RBS::AST::Ruby::Members::MixinMember; end
|
||||
|
||||
class RBS::AST::TypeParam
|
||||
@@ -908,6 +936,7 @@ class RBS::Buffer
|
||||
def initialize(content:, name: T.unsafe(nil), parent: T.unsafe(nil)); end
|
||||
|
||||
def absolute_position(position); end
|
||||
def character_offset(byte_offset); end
|
||||
def content; end
|
||||
def detach; end
|
||||
def inspect; end
|
||||
@@ -922,7 +951,7 @@ class RBS::Buffer
|
||||
def pos_to_loc(pos); end
|
||||
def ranges; end
|
||||
def rbs_location(location, loc2 = T.unsafe(nil)); end
|
||||
def sub_buffer(lines:); end
|
||||
def sub_buffer(lines:, byte_lines_hint: T.unsafe(nil)); end
|
||||
def top_buffer; end
|
||||
end
|
||||
|
||||
@@ -1069,6 +1098,7 @@ class RBS::Collection::Config::LockfileGenerator::GemfileLockMismatchError < ::S
|
||||
def message; end
|
||||
end
|
||||
|
||||
RBS::Collection::Config::LockfileGenerator::NONGEM_STDLIBS = T.let(T.unsafe(nil), Set)
|
||||
RBS::Collection::Config::PATH = T.let(T.unsafe(nil), Pathname)
|
||||
|
||||
class RBS::Collection::Installer
|
||||
@@ -1863,6 +1893,7 @@ class RBS::InlineParser::Parser < ::Prism::Visitor
|
||||
def skip_node?(node); end
|
||||
def visit_call_node(node); end
|
||||
def visit_class_node(node); end
|
||||
def visit_class_or_module_body(decl, node); end
|
||||
def visit_constant_path_write_node(node); end
|
||||
def visit_constant_write_node(node); end
|
||||
def visit_def_node(node); end
|
||||
@@ -2056,12 +2087,15 @@ class RBS::Namespace
|
||||
def to_type_name; end
|
||||
|
||||
class << self
|
||||
def [](path, absolute); end
|
||||
def empty; end
|
||||
def parse(string); end
|
||||
def root; end
|
||||
end
|
||||
end
|
||||
|
||||
module RBS::Namespace::INTERN_LEAF; end
|
||||
|
||||
class RBS::NoMixinFoundError < ::RBS::DefinitionError
|
||||
include ::RBS::DetailedMessageable
|
||||
|
||||
@@ -2128,9 +2162,12 @@ class RBS::Parser
|
||||
def _parse_inline_leading_annotation(_arg0, _arg1, _arg2, _arg3); end
|
||||
def _parse_inline_trailing_annotation(_arg0, _arg1, _arg2, _arg3); end
|
||||
def _parse_method_type(_arg0, _arg1, _arg2, _arg3, _arg4); end
|
||||
def _parse_method_type_to_bytes(_arg0, _arg1, _arg2, _arg3, _arg4); end
|
||||
def _parse_signature(_arg0, _arg1, _arg2); end
|
||||
def _parse_signature_to_bytes(_arg0, _arg1, _arg2); end
|
||||
def _parse_type(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7); end
|
||||
def _parse_type_params(_arg0, _arg1, _arg2, _arg3); end
|
||||
def _parse_type_to_bytes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7); end
|
||||
def buffer(source); end
|
||||
def byte_range(char_range, content); end
|
||||
def lex(source); end
|
||||
@@ -2247,32 +2284,47 @@ class RBS::Prototype::RBI
|
||||
|
||||
def initialize; end
|
||||
|
||||
def append_decl(decl); end
|
||||
def attribute_type(kind, sigs); end
|
||||
def call_node?(node, name:, receiver: T.unsafe(nil), args: T.unsafe(nil)); end
|
||||
def const_to_name(node); end
|
||||
def current_context; end
|
||||
def current_context!; end
|
||||
def current_module; end
|
||||
def current_module!; end
|
||||
def current_namespace; end
|
||||
def decls; end
|
||||
def each_arg(array, &block); end
|
||||
def each_child(node); end
|
||||
def join_comments(nodes, comments); end
|
||||
def last_sig; end
|
||||
def member_visibility(context); end
|
||||
def method_type(args_node, type_node, variables:, overloads:); end
|
||||
def modules; end
|
||||
def nested_name(name); end
|
||||
def node_to_hash(node); end
|
||||
def parse(string); end
|
||||
def parse_params(args_node, args, method_type, variables:, overloads:); end
|
||||
def pop_sig; end
|
||||
def proc_type?(type_node); end
|
||||
def process(node, comments:, outer: T.unsafe(nil)); end
|
||||
def process_attribute(node, comments:); end
|
||||
def process_visibility(node, outer:, comments:); end
|
||||
def push_class(name, super_class, comment:); end
|
||||
def push_module(name, comment:); end
|
||||
def push_sig(node); end
|
||||
def sync_visibility(visibility); end
|
||||
def type_of(type_node, variables:); end
|
||||
def type_of0(type_node, variables:); end
|
||||
end
|
||||
|
||||
class RBS::Prototype::RBI::Context
|
||||
def initialize(singleton:, visibility:); end
|
||||
|
||||
def singleton; end
|
||||
def singleton=(_arg0); end
|
||||
def visibility; end
|
||||
def visibility=(_arg0); end
|
||||
end
|
||||
|
||||
class RBS::Prototype::Runtime
|
||||
include ::RBS::Prototype::Helpers
|
||||
include ::RBS::Prototype::Runtime::Helpers
|
||||
@@ -2509,13 +2561,28 @@ class RBS::Resolver::TypeNameResolver
|
||||
def resolve_namespace(type_name, context:); end
|
||||
def resolve_namespace0(type_name, context, visited); end
|
||||
def resolve_type_name(type_name, context); end
|
||||
def try_cache(query); end
|
||||
def try_cache(type_name, context); end
|
||||
|
||||
class << self
|
||||
def build(env); end
|
||||
end
|
||||
end
|
||||
|
||||
class RBS::Rewriter
|
||||
def initialize(buffer); end
|
||||
|
||||
def add_comment(*locations, content:); end
|
||||
def buffer; end
|
||||
def delete_comment(comment); end
|
||||
def replace_comment(comment, content:); end
|
||||
def rewrite(location, string); end
|
||||
def string; end
|
||||
|
||||
private
|
||||
|
||||
def format_comment(content, indent); end
|
||||
end
|
||||
|
||||
module RBS::Source; end
|
||||
|
||||
class RBS::Source::RBS
|
||||
@@ -2652,6 +2719,7 @@ class RBS::TypeName
|
||||
def with_prefix(namespace); end
|
||||
|
||||
class << self
|
||||
def [](namespace, name); end
|
||||
def parse(string); end
|
||||
end
|
||||
end
|
||||
+73
-16
@@ -299,6 +299,7 @@ class RuboCop::Config
|
||||
def disabled_new_cops?; end
|
||||
def each(*_arg0, **_arg1, &_arg2); end
|
||||
def each_key(*_arg0, **_arg1, &_arg2); end
|
||||
def enabled_new_cop?(qualified_cop_name); end
|
||||
def enabled_new_cops?; end
|
||||
def fetch(*_arg0, **_arg1, &_arg2); end
|
||||
def file_to_exclude?(file); end
|
||||
@@ -340,10 +341,14 @@ class RuboCop::Config
|
||||
|
||||
private
|
||||
|
||||
def comparable_version(value); end
|
||||
def department_of(qualified_cop_name); end
|
||||
def enable_cop?(qualified_cop_name, cop_options); end
|
||||
def gem_version_to_major_minor_float(gem_version); end
|
||||
def match_relative_or_absolute_path?(pattern, relative_file_path, absolute_file_path); end
|
||||
def new_cops_covered?(qualified_cop_name); end
|
||||
def new_cops_setting_for(qualified_cop_name); end
|
||||
def new_cops_version_covers?(new_cops_version, qualified_cop_name); end
|
||||
def read_gem_versions_from_target_lockfile; end
|
||||
def read_rails_version_from_bundler_lock_file; end
|
||||
def target_rails_version_from_bundler_lock_file; end
|
||||
@@ -691,11 +696,15 @@ class RuboCop::ConfigValidator
|
||||
def check_target_ruby; end
|
||||
def each_invalid_parameter(cop_name); end
|
||||
def list_unknown_cops(invalid_cop_names); end
|
||||
def new_cops_value_for_department(name, section); end
|
||||
def new_cops_version_value?(value); end
|
||||
def param_error_message(parent, key, value, supposed_values); end
|
||||
def reject_conflicting_safe_settings; end
|
||||
def reject_mutually_exclusive_defaults; end
|
||||
def suggestion(name); end
|
||||
def target_ruby; end
|
||||
def validate_all_cops_new_cops_parameter; end
|
||||
def validate_department_new_cops_parameters; end
|
||||
def validate_enforced_styles(valid_cop_names); end
|
||||
def validate_new_cops_parameter; end
|
||||
def validate_parameter_names(valid_cop_names); end
|
||||
@@ -716,6 +725,8 @@ RuboCop::ConfigValidator::INTERNAL_PARAMS = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::ConfigValidator::NEW_COPS_VALUES = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::ConfigValidator::NEW_COPS_VERSION_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Alignment::SPACE = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::AllowedIdentifiers::SIGILS = T.let(T.unsafe(nil), String)
|
||||
@@ -1058,6 +1069,12 @@ RuboCop::Cop::Generator::CONFIGURATION_ADDED_MESSAGE = T.let(T.unsafe(nil), Stri
|
||||
|
||||
RuboCop::Cop::Generator::ConfigurationInjector::TEMPLATE = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Generator::RegistrationInjector::DEPARTMENT_REQUIRE_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Generator::RegistrationInjector::ENTRY_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Generator::RegistrationInjector::EXTEND_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Generator::RequireFileInjector::REQUIRE_PATH = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Generator::SOURCE_TEMPLATE = T.let(T.unsafe(nil), String)
|
||||
@@ -1420,10 +1437,12 @@ RuboCop::Cop::Lint::AmbiguousAssignment::MISTAKES = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
RuboCop::Cop::Lint::AmbiguousAssignment::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::AmbiguousAssignment::SIMPLE_ASSIGNMENT_TYPES = T.let(T.unsafe(nil), Array)
|
||||
RuboCop::Cop::Lint::AmbiguousBlockAssociation::BLOCK_METHODS = T.let(T.unsafe(nil), Set)
|
||||
|
||||
RuboCop::Cop::Lint::AmbiguousBlockAssociation::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::AmbiguousBlockAssociation::MSG_DO_END_BLOCK = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::AmbiguousOperator::AMBIGUITIES = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
RuboCop::Cop::Lint::AmbiguousOperator::MSG_FORMAT = T.let(T.unsafe(nil), String)
|
||||
@@ -1514,6 +1533,14 @@ RuboCop::Cop::Lint::DeprecatedOpenSSLConstant::NO_ARG_ALGORITHM = T.let(T.unsafe
|
||||
|
||||
RuboCop::Cop::Lint::DeprecatedOpenSSLConstant::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Lint::DeprecatedReference::CONSTANT_MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DeprecatedReference::DEPRECATED_TAG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DeprecatedReference::DEPRECATION_DETAIL = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Lint::DeprecatedReference::METHOD_MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DisjunctiveAssignmentInConstructor::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DuplicateBranch::MSG = T.let(T.unsafe(nil), String)
|
||||
@@ -1528,9 +1555,9 @@ RuboCop::Cop::Lint::DuplicateMagicComment::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DuplicateMatchPattern::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DuplicateMethods::MSG = T.let(T.unsafe(nil), String)
|
||||
RuboCop::Cop::Lint::DuplicateMethods::INDEXABLE_METHOD_NAME = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Lint::DuplicateMethods::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
|
||||
RuboCop::Cop::Lint::DuplicateMethods::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::DuplicateRegexpCharacterClassElement::MSG_REPEATED_ELEMENT = T.let(T.unsafe(nil), String)
|
||||
|
||||
@@ -1642,6 +1669,8 @@ RuboCop::Cop::Lint::IneffectiveAccessModifier::ALTERNATIVE_PROTECTED = T.let(T.u
|
||||
|
||||
RuboCop::Cop::Lint::IneffectiveAccessModifier::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::InheritException::INDIRECT_MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::InheritException::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::InheritException::PREFERRED_BASE_CLASS = T.let(T.unsafe(nil), Hash)
|
||||
@@ -1700,6 +1729,14 @@ RuboCop::Cop::Lint::MultipleComparison::RESTRICT_ON_SEND = T.let(T.unsafe(nil),
|
||||
|
||||
RuboCop::Cop::Lint::MultipleComparison::SET_OPERATION_OPERATORS = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Lint::NameTypo::CONSTANT_MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::NameTypo::LITERAL_IDENTIFIER_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Lint::NameTypo::METHOD_MEMBER_REGEXP = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Lint::NameTypo::METHOD_MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::NestedMethodDefinition::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::NestedPercentLiteral::MSG = T.let(T.unsafe(nil), String)
|
||||
@@ -1980,6 +2017,12 @@ RuboCop::Cop::Lint::UnreachablePatternBranch::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::UnreachablePatternBranch::MSG_ELSE = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::UnusedPrivateMethod::IDENTIFIER_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Lint::UnusedPrivateMethod::IMPLICITLY_INVOKED_METHODS = T.let(T.unsafe(nil), Set)
|
||||
|
||||
RuboCop::Cop::Lint::UnusedPrivateMethod::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Lint::UriEscapeUnescape::ALTERNATE_METHODS_OF_URI_ESCAPE = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Lint::UriEscapeUnescape::ALTERNATE_METHODS_OF_URI_UNESCAPE = T.let(T.unsafe(nil), Array)
|
||||
@@ -2308,16 +2351,18 @@ class RuboCop::Cop::Registry
|
||||
def department_missing?(badge, name); end
|
||||
def departments; end
|
||||
def disabled(config); end
|
||||
def disabled_names(config); end
|
||||
def dismiss(cop); end
|
||||
def each(&block); end
|
||||
def enabled(config); end
|
||||
def enabled?(cop, config); end
|
||||
def enabled_pending_cop?(cop_cfg, config); end
|
||||
def enabled_pending_cop?(cop_cfg, config, cop = T.unsafe(nil)); end
|
||||
def enlist(cop); end
|
||||
def filter_by_badge(options = T.unsafe(nil)); end
|
||||
def find_by_cop_name(cop_name); end
|
||||
def find_cops_by_directive(directive); end
|
||||
def freeze; end
|
||||
def lazy_load(cop_name, constant_name); end
|
||||
def lazy_load(badge, constant_name); end
|
||||
def length; end
|
||||
def names; end
|
||||
def names_for_department(department); end
|
||||
@@ -2334,16 +2379,21 @@ class RuboCop::Cop::Registry
|
||||
def with_department(department); end
|
||||
def without_department(department); end
|
||||
|
||||
protected
|
||||
|
||||
def add_entry(badge, cop_or_name); end
|
||||
|
||||
private
|
||||
|
||||
def clear_enrollment_queue; end
|
||||
def emit_warning(path, message); end
|
||||
def enabled_cop_name?(cop_name, config); end
|
||||
def initialize_copy(reg); end
|
||||
def load_all_lazy_cops; end
|
||||
def load_lazy_cop(badge); end
|
||||
def registered?(badge); end
|
||||
def registered_badges; end
|
||||
def resolve_badge(given_badge, real_badge, source_path, warn: T.unsafe(nil)); end
|
||||
def with(cops); end
|
||||
|
||||
class << self
|
||||
def all; end
|
||||
@@ -2355,6 +2405,8 @@ class RuboCop::Cop::Registry
|
||||
end
|
||||
end
|
||||
|
||||
RuboCop::Cop::ReparsedEquivalence::MAX_VERIFICATION_FRAGMENT_SIZE = T.let(T.unsafe(nil), Integer)
|
||||
|
||||
RuboCop::Cop::RequireLibrary::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Security::CompoundHash::COMBINATOR_IN_HASH_MSG = T.let(T.unsafe(nil), String)
|
||||
@@ -2647,6 +2699,8 @@ RuboCop::Cop::Style::DocumentDynamicEvalDefinition::MSG = T.let(T.unsafe(nil), S
|
||||
|
||||
RuboCop::Cop::Style::DocumentDynamicEvalDefinition::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::Documentation::DIRECTIVE_COMMENT_REGEXP = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Style::Documentation::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::DocumentationMethod::MSG = T.let(T.unsafe(nil), String)
|
||||
@@ -2865,6 +2919,8 @@ RuboCop::Cop::Style::IfInsideElse::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::IfUnlessModifier::MSG_USE_MODIFIER = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::IfUnlessModifier::MSG_USE_MODIFIER_PARENS = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::IfUnlessModifier::MSG_USE_NORMAL = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::IfUnlessModifierOfIfUnless::MSG = T.let(T.unsafe(nil), String)
|
||||
@@ -3317,21 +3373,17 @@ RuboCop::Cop::Style::RedundantInterpolation::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::RedundantInterpolationUnfreeze::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::ALLOWED_STRING_TOKENS = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::ARGUMENT_TAKING_FLOW_TOKEN_TYPES = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::ARGUMENT_TYPES = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::ARITHMETIC_OPERATOR_TOKENS = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::LINE_CONTINUATION = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::LINE_CONTINUATION_PATTERN = T.let(T.unsafe(nil), Regexp)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::MSG = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::STRING_LITERAL_BEGIN_TOKENS = T.let(T.unsafe(nil), Array)
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::STRING_LITERAL_BEGINNING_TOKEN_TYPES = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::STRING_LITERAL_ENDING_TOKEN_TYPES = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantLineContinuation::STRING_TOKEN_TYPES = T.let(T.unsafe(nil), Array)
|
||||
|
||||
RuboCop::Cop::Style::RedundantMinMaxBy::MSG_BLOCK = T.let(T.unsafe(nil), String)
|
||||
|
||||
@@ -3964,6 +4016,8 @@ RuboCop::DirectiveComment::MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP = T.let(T
|
||||
|
||||
RuboCop::DirectiveComment::PUSH_POP_ARGS_PATTERN = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::DirectiveComment::STYLE_DISABLE_COPS_DIRECTIVE_COP = T.let(T.unsafe(nil), String)
|
||||
|
||||
RuboCop::DirectiveComment::TRAILING_COMMENT_MARKER = T.let(T.unsafe(nil), String)
|
||||
|
||||
class RuboCop::Error < ::StandardError; end
|
||||
@@ -4288,6 +4342,8 @@ class RuboCop::Runner
|
||||
private
|
||||
|
||||
def add_redundant_disables(file, offenses, source); end
|
||||
def build_project_index(target_files); end
|
||||
def bundled_gem_files; end
|
||||
def cached_result(file, team); end
|
||||
def cached_run?; end
|
||||
def check_for_infinite_loop(processed_source, offenses_by_iteration); end
|
||||
@@ -4301,7 +4357,6 @@ class RuboCop::Runner
|
||||
def file_iterator(files, &block); end
|
||||
def file_offenses(file); end
|
||||
def file_started(file); end
|
||||
def filter_cop_classes(cop_classes, config); end
|
||||
def find_target_files(paths); end
|
||||
def finished_report(file, index, offenses); end
|
||||
def formatter_set; end
|
||||
@@ -4313,6 +4368,7 @@ class RuboCop::Runner
|
||||
def list_files(paths); end
|
||||
def mark_as_safe_by_config?(config); end
|
||||
def minimum_severity_to_fail; end
|
||||
def mobilize_cop_badge?(badge, config); end
|
||||
def mobilize_team(processed_source); end
|
||||
def mobilized_cop_classes(config); end
|
||||
def offense_displayed?(offense); end
|
||||
@@ -4323,6 +4379,7 @@ class RuboCop::Runner
|
||||
def process_report_queue_entry(index); end
|
||||
def project_index_disables_parallel?; end
|
||||
def project_index_enabled?; end
|
||||
def project_index_files(target_files); end
|
||||
def qualify_option_cop_names; end
|
||||
def redundant_cop_disable_directive(file); end
|
||||
def run_in_parallel?(files); end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user