Merge branch 'master' into master

This commit is contained in:
Thibaut Hérault 2025-06-17 20:49:18 -04:00 committed by GitHub
commit 27a040cc32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
112 changed files with 2281 additions and 1511 deletions

View File

@ -0,0 +1,7 @@
# This file is synced from the `.github` repository, do not modify it directly.
extensions:
- addsTo:
pack: codeql/actions-all
extensible: trustedActionsOwnerDataModel
data:
- ["Homebrew"]

View File

@ -1,18 +1,19 @@
name: actionlint # This file is synced from the `.github` repository, do not modify it directly.
name: Actionlint
on: on:
push: push:
branches: branches:
- main
- master - master
paths:
- '.github/workflows/*.ya?ml'
- 'Formula/a/actionlint.rb'
- 'Formula/s/shellcheck.rb'
- 'Formula/z/zizmor.rb'
pull_request: pull_request:
paths: paths:
- '.github/workflows/*.ya?ml' - '.github/workflows/*.ya?ml'
- '.github/actionlint.yaml'
env:
HOMEBREW_DEVELOPER: 1
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_ENV_HINTS: 1
defaults: defaults:
run: run:
@ -22,16 +23,23 @@ concurrency:
group: "actionlint-${{ github.ref }}" group: "actionlint-${{ github.ref }}"
cancel-in-progress: ${{ github.event_name == 'pull_request' }} cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
HOMEBREW_DEVELOPER: 1
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_ENV_HINTS: 1
permissions: {} permissions: {}
jobs: jobs:
workflow_syntax: workflow_syntax:
if: github.repository_owner == 'Homebrew' if: github.repository_owner == 'Homebrew'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: read
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: setup-homebrew id: setup-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -40,31 +48,42 @@ jobs:
- name: Install tools - name: Install tools
run: brew install actionlint shellcheck zizmor run: brew install actionlint shellcheck zizmor
- name: Set up GITHUB_WORKSPACE - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
env: with:
HOMEBREW_REPOSITORY: ${{ steps.setup-homebrew.outputs.repository-path }} persist-credentials: false
run: |
# Annotations work only relative to GITHUB_WORKSPACE
(shopt -s dotglob; rm -rf "${GITHUB_WORKSPACE:?}"/*; mv "${HOMEBREW_REPOSITORY:?}"/* "$GITHUB_WORKSPACE")
rmdir "$HOMEBREW_REPOSITORY"
ln -vs "$GITHUB_WORKSPACE" "$HOMEBREW_REPOSITORY"
echo "::add-matcher::.github/actionlint-matcher.json" - run: zizmor --format sarif . > results.sarif
- run: |
# NOTE: exit code intentionally suppressed here
zizmor --format sarif . > results.sarif || true
- name: Upload SARIF file - name: Upload SARIF file
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
# We can't use the SARIF file when triggered by `merge_group` so we don't upload it.
if: always() && github.event_name != 'merge_group'
with: with:
name: results.sarif name: results.sarif
path: results.sarif path: results.sarif
- name: Set up actionlint
run: |
# In homebrew-core, setting `shell: /bin/bash` prevents shellcheck from running on
# those steps, so let's change them to `shell: bash` temporarily for better linting.
sed -i 's|shell: /bin/bash -x|shell: bash -x|' .github/workflows/*.y*ml
# In homebrew-core, the JSON matcher needs to be accessible to the container host.
cp "$(brew --repository)/.github/actionlint-matcher.json" "$HOME"
echo "::add-matcher::$HOME/actionlint-matcher.json"
- run: actionlint - run: actionlint
upload_sarif: upload_sarif:
needs: workflow_syntax needs: workflow_syntax
# We want to always upload this even if `actionlint` failed.
# This is only available on public repositories.
if: >
always() &&
!contains(fromJSON('["cancelled", "skipped"]'), needs.workflow_syntax.result) &&
!github.event.repository.private &&
github.event_name != 'merge_group'
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
@ -77,7 +96,7 @@ jobs:
path: results.sarif path: results.sarif
- name: Upload SARIF file - name: Upload SARIF file
uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with: with:
sarif_file: results.sarif sarif_file: results.sarif
category: zizmor category: zizmor

View File

@ -27,7 +27,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false

View File

@ -3,10 +3,9 @@ name: "CodeQL"
on: on:
push: push:
branches: branches:
- main
- master - master
pull_request: pull_request:
branches:
- master
defaults: defaults:
run: run:
@ -28,7 +27,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with: with:
languages: ruby languages: ruby
config: | config: |
@ -36,4 +35,4 @@ jobs:
- Library/Homebrew/vendor - Library/Homebrew/vendor
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0

View File

@ -4,6 +4,7 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- main
- master - master
merge_group: merge_group:
release: release:
@ -38,8 +39,8 @@ jobs:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Fetch origin/master from Git - name: Fetch origin/HEAD from Git
run: git fetch origin master run: git fetch origin HEAD
- name: Determine build attributes - name: Determine build attributes
id: attributes id: attributes
@ -83,12 +84,16 @@ jobs:
) )
fi fi
elif [[ "${GITHUB_EVENT_NAME}" == "push" && elif [[ "${GITHUB_EVENT_NAME}" == "push" &&
"${GITHUB_REF}" == "refs/heads/master" && ("${GITHUB_REF}" == "refs/heads/master" || "${GITHUB_REF}" == "refs/heads/main") &&
"${version}" == "22.04" ]]; then "${version}" == "22.04" ]]; then
tags+=( tags+=(
"ghcr.io/homebrew/brew:main"
"ghcr.io/homebrew/brew:master" "ghcr.io/homebrew/brew:master"
"ghcr.io/homebrew/ubuntu${version}:main"
"ghcr.io/homebrew/ubuntu${version}:master" "ghcr.io/homebrew/ubuntu${version}:master"
"homebrew/brew:main"
"homebrew/brew:master" "homebrew/brew:master"
"homebrew/ubuntu${version}:main"
"homebrew/ubuntu${version}:master" "homebrew/ubuntu${version}:master"
) )
fi fi
@ -160,8 +165,8 @@ jobs:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: Fetch origin/master from Git - name: Fetch origin/HEAD from Git
run: git fetch origin master run: git fetch origin HEAD
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0

View File

@ -24,7 +24,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -52,7 +52,7 @@ jobs:
run: vale docs/ run: vale docs/
- name: Install Ruby - name: Install Ruby
uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with: with:
bundler-cache: true bundler-cache: true
working-directory: docs working-directory: docs
@ -67,7 +67,7 @@ jobs:
- name: Generate formulae.brew.sh API samples - name: Generate formulae.brew.sh API samples
if: github.repository == 'Homebrew/formulae.brew.sh' if: github.repository == 'Homebrew/formulae.brew.sh'
working-directory: docs working-directory: docs
run: ../script/generate-api-samples.rb run: ../script/generate-api-samples.rb --template
- name: Build the site and check for broken links - name: Build the site and check for broken links
working-directory: docs working-directory: docs

View File

@ -28,7 +28,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -55,7 +55,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false

View File

@ -43,7 +43,7 @@ jobs:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -135,7 +135,7 @@ jobs:
fi fi
- name: Generate build provenance - name: Generate build provenance
uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0 uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
with: with:
subject-path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg subject-path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
@ -251,7 +251,7 @@ jobs:
issues: write issues: write
steps: steps:
- name: Open, update, or close pkg installer issue - name: Open, update, or close pkg installer issue
uses: Homebrew/actions/create-or-update-issue@master uses: Homebrew/actions/create-or-update-issue@main
with: with:
title: Failed to publish pkg installer title: Failed to publish pkg installer
body: > body: >
@ -259,7 +259,7 @@ jobs:
${{ github.ref_name }}. No pkg installer was uploaded to the GitHub ${{ github.ref_name }}. No pkg installer was uploaded to the GitHub
release. release.
labels: bug,release blocker labels: bug,release blocker
update-existing: ${{ contains(needs.*.result, 'failure') }} update-existing: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
close-existing: ${{ needs.upload.result == 'success' }} close-existing: ${{ needs.upload.result == 'success' }}
close-from-author: github-actions[bot] close-from-author: github-actions[bot]
close-comment: > close-comment: >

View File

@ -3,6 +3,7 @@ name: Ruby Documentation CI
on: on:
push: push:
branches: branches:
- main
- master - master
pull_request: pull_request:
@ -28,7 +29,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -42,7 +43,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Install Ruby - name: Install Ruby
uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0 uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0
with: with:
bundler-cache: true bundler-cache: true
working-directory: rubydoc working-directory: rubydoc

View File

@ -1,9 +1,10 @@
name: Update schema data name: Update SBOM schema
on: on:
push: push:
paths: paths:
- .github/workflows/schemas.yml - .github/workflows/sbom.yml
branches-ignore: branches-ignore:
- main
- master - master
schedule: schedule:
- cron: "0 0 * * *" - cron: "0 0 * * *"
@ -17,25 +18,25 @@ defaults:
shell: bash -xeuo pipefail {0} shell: bash -xeuo pipefail {0}
jobs: jobs:
spdx: sbom:
if: github.repository == 'Homebrew/brew' if: github.repository == 'Homebrew/brew'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
test-bot: false test-bot: false
- name: Configure Git user - name: Configure Git user
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
with: with:
username: BrewTestBot username: BrewTestBot
- name: Set up commit signing - name: Set up commit signing
uses: Homebrew/actions/setup-commit-signing@master uses: Homebrew/actions/setup-commit-signing@main
with: with:
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }} signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
@ -55,7 +56,7 @@ jobs:
git checkout "${BRANCH}" git checkout "${BRANCH}"
git checkout "Library/Homebrew/data/schemas" git checkout "Library/Homebrew/data/schemas"
else else
git checkout --no-track -B "${BRANCH}" origin/master git checkout --no-track -B "${BRANCH}" origin/HEAD
fi fi
# Intentionally tracking 2.3.x to match what we output in sbom.rb. 3.0 also doesn't have a JSON Schema. # Intentionally tracking 2.3.x to match what we output in sbom.rb. 3.0 also doesn't have a JSON Schema.
@ -67,9 +68,10 @@ jobs:
if ! git diff --exit-code Library/Homebrew/data/schemas if ! git diff --exit-code Library/Homebrew/data/schemas
then then
git add "Library/Homebrew/data/schemas" git add "Library/Homebrew/data/schemas"
git commit -m "data/schemas: update schema data." -m "Autogenerated by [a scheduled GitHub Action](https://github.com/Homebrew/brew/blob/master/.github/workflows/schemas.yml)." git commit -m "data/schemas: update schema data." -m "Autogenerated by [a scheduled GitHub Action](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/schemas.yml)."
echo "committed=true" >> "$GITHUB_OUTPUT" echo "committed=true" >> "$GITHUB_OUTPUT"
PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state")" PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state" || true)"
if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]] if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]]
then then
echo "pull_request=true" >> "$GITHUB_OUTPUT" echo "pull_request=true" >> "$GITHUB_OUTPUT"
@ -78,13 +80,13 @@ jobs:
- name: Push commits - name: Push commits
if: steps.update.outputs.committed == 'true' if: steps.update.outputs.committed == 'true'
uses: Homebrew/actions/git-try-push@master uses: Homebrew/actions/git-try-push@main
with: with:
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
directory: ${{ steps.set-up-homebrew.outputs.repository-path }} directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
branch: ${{ steps.update.outputs.branch }} branch: ${{ steps.update.outputs.branch }}
force: true force: true
origin_branch: "master" origin_branch: "HEAD"
- name: Open a pull request - name: Open a pull request
if: steps.update.outputs.pull_request == 'true' if: steps.update.outputs.pull_request == 'true'
@ -92,3 +94,26 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
issue:
needs: sbom
if: always() && github.event_name == 'schedule'
runs-on: ubuntu-latest
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
permissions:
# To create or update issues
issues: write
steps:
- name: Open, update, or close schema issue
uses: Homebrew/actions/create-or-update-issue@main
with:
title: Failed to update SBOM schema
body: >
The SBOM schema workflow [failed](${{ env.RUN_URL }}). No SBOM schema was updated.
labels: bug
update-existing: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
close-existing: ${{ needs.sbom.result == 'success' }}
close-from-author: github-actions[bot]
close-comment: >
The SBOM schema workflow [succeeded](${{ env.RUN_URL }}). Closing this issue.

View File

@ -10,6 +10,7 @@ on:
paths: paths:
- .github/workflows/sorbet.yml - .github/workflows/sorbet.yml
branches-ignore: branches-ignore:
- main
- master - master
schedule: schedule:
- cron: "0 0 * * *" - cron: "0 0 * * *"
@ -29,7 +30,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -37,13 +38,13 @@ jobs:
- name: Configure Git user - name: Configure Git user
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
with: with:
username: BrewTestBot username: BrewTestBot
- name: Set up commit signing - name: Set up commit signing
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: Homebrew/actions/setup-commit-signing@master uses: Homebrew/actions/setup-commit-signing@main
with: with:
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }} signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
@ -63,7 +64,7 @@ jobs:
git checkout "${BRANCH}" git checkout "${BRANCH}"
git checkout "Library/Homebrew/sorbet" git checkout "Library/Homebrew/sorbet"
else else
git checkout --no-track -B "${BRANCH}" origin/master git checkout --no-track -B "${BRANCH}" origin/HEAD
fi fi
fi fi
@ -80,17 +81,17 @@ jobs:
then then
git add "Library/Homebrew/sorbet" git add "Library/Homebrew/sorbet"
git commit -m "sorbet: Update RBI files." \ git commit -m "sorbet: Update RBI files." \
-m "Autogenerated by the [sorbet](https://github.com/Homebrew/brew/blob/master/.github/workflows/sorbet.yml) workflow." -m "Autogenerated by the [sorbet](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sorbet.yml) workflow."
if ! git diff --stat --exit-code "Library/Homebrew" if ! git diff --stat --exit-code "Library/Homebrew"
then then
git add "Library/Homebrew/" git add "Library/Homebrew/"
git commit -m "sorbet: Autobump sigils via Spoom" \ git commit -m "sorbet: Autobump sigils via Spoom" \
-m "Autogenerated by the [sorbet](https://github.com/Homebrew/brew/blob/master/.github/workflows/sorbet.yml) workflow." -m "Autogenerated by the [sorbet](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/sorbet.yml) workflow."
fi fi
echo "committed=true" >> "$GITHUB_OUTPUT" echo "committed=true" >> "$GITHUB_OUTPUT"
PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state")" PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state" || true)"
if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]] if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]]
then then
echo "pull_request=true" >> "$GITHUB_OUTPUT" echo "pull_request=true" >> "$GITHUB_OUTPUT"
@ -99,13 +100,13 @@ jobs:
- name: Push commits - name: Push commits
if: steps.commit.outputs.committed == 'true' if: steps.commit.outputs.committed == 'true'
uses: Homebrew/actions/git-try-push@master uses: Homebrew/actions/git-try-push@main
with: with:
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
directory: ${{ steps.set-up-homebrew.outputs.repository-path }} directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
branch: ${{ steps.update.outputs.branch }} branch: ${{ steps.update.outputs.branch }}
force: true force: true
origin_branch: "master" origin_branch: "HEAD"
- name: Open a pull request - name: Open a pull request
if: steps.commit.outputs.pull_request == 'true' if: steps.commit.outputs.pull_request == 'true'
@ -113,3 +114,26 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
issue:
needs: tapioca
if: always() && github.event_name == 'schedule'
runs-on: ubuntu-latest
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
permissions:
# To create or update issues
issues: write
steps:
- name: Open, update, or close Sorbet issue
uses: Homebrew/actions/create-or-update-issue@main
with:
title: Failed to update RBI files
body: >
The Sorbet workflow [failed](${{ env.RUN_URL }}). No RBI files were updated.
labels: bug
update-existing: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
close-existing: ${{ needs.tapioca.result == 'success' }}
close-from-author: github-actions[bot]
close-comment: >
The Sorbet workflow [succeeded](${{ env.RUN_URL }}). Closing this issue.

View File

@ -4,6 +4,7 @@ on:
paths: paths:
- .github/workflows/spdx.yml - .github/workflows/spdx.yml
branches-ignore: branches-ignore:
- main
- master - master
schedule: schedule:
- cron: "0 0 * * *" - cron: "0 0 * * *"
@ -23,19 +24,19 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
test-bot: false test-bot: false
- name: Configure Git user - name: Configure Git user
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
with: with:
username: BrewTestBot username: BrewTestBot
- name: Set up commit signing - name: Set up commit signing
uses: Homebrew/actions/setup-commit-signing@master uses: Homebrew/actions/setup-commit-signing@main
with: with:
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }} signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
@ -55,15 +56,16 @@ jobs:
git checkout "${BRANCH}" git checkout "${BRANCH}"
git checkout "Library/Homebrew/data/spdx" git checkout "Library/Homebrew/data/spdx"
else else
git checkout --no-track -B "${BRANCH}" origin/master git checkout --no-track -B "${BRANCH}" origin/HEAD
fi fi
if brew update-license-data if brew update-license-data
then then
git add "Library/Homebrew/data/spdx" git add "Library/Homebrew/data/spdx"
git commit -m "spdx: update license data." -m "Autogenerated by [a scheduled GitHub Action](https://github.com/Homebrew/brew/blob/master/.github/workflows/spdx.yml)." git commit -m "spdx: update license data." -m "Autogenerated by [a scheduled GitHub Action](https://github.com/Homebrew/brew/blob/HEAD/.github/workflows/spdx.yml)."
echo "committed=true" >> "$GITHUB_OUTPUT" echo "committed=true" >> "$GITHUB_OUTPUT"
PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state")" PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state" || true)"
if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]] if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]]
then then
echo "pull_request=true" >> "$GITHUB_OUTPUT" echo "pull_request=true" >> "$GITHUB_OUTPUT"
@ -72,13 +74,13 @@ jobs:
- name: Push commits - name: Push commits
if: steps.update.outputs.committed == 'true' if: steps.update.outputs.committed == 'true'
uses: Homebrew/actions/git-try-push@master uses: Homebrew/actions/git-try-push@main
with: with:
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
directory: ${{ steps.set-up-homebrew.outputs.repository-path }} directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
branch: ${{ steps.update.outputs.branch }} branch: ${{ steps.update.outputs.branch }}
force: true force: true
origin_branch: "master" origin_branch: "HEAD"
- name: Open a pull request - name: Open a pull request
if: steps.update.outputs.pull_request == 'true' if: steps.update.outputs.pull_request == 'true'
@ -86,3 +88,26 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
issue:
needs: spdx
if: always() && github.event_name == 'schedule'
runs-on: ubuntu-latest
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
permissions:
# To create or update issues
issues: write
steps:
- name: Open, update, or close SPDX issue
uses: Homebrew/actions/create-or-update-issue@main
with:
title: Failed to update SPDX license data
body: >
The SPDX license data workflow [failed](${{ env.RUN_URL }}). No SPDX license data was updated.
labels: bug
update-existing: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
close-existing: ${{ needs.spdx.result == 'success' }}
close-from-author: github-actions[bot]
close-comment: >
The SPDX license data workflow [succeeded](${{ env.RUN_URL }}). Closing this issue.

View File

@ -3,6 +3,7 @@ name: Update sponsors, maintainers, manpage and completions
on: on:
push: push:
branches: branches:
- main
- master - master
paths: paths:
- .github/workflows/sponsors-maintainers-man-completions.yml - .github/workflows/sponsors-maintainers-man-completions.yml
@ -32,19 +33,19 @@ jobs:
steps: steps:
- name: Setup Homebrew - name: Setup Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
test-bot: false test-bot: false
- name: Configure Git user - name: Configure Git user
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
with: with:
username: BrewTestBot username: BrewTestBot
- name: Set up commit signing - name: Set up commit signing
uses: Homebrew/actions/setup-commit-signing@master uses: Homebrew/actions/setup-commit-signing@main
with: with:
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }} signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
@ -60,7 +61,7 @@ jobs:
run: | run: |
git fetch origin git fetch origin
if [[ -n "$GITHUB_REF_NAME" && "$GITHUB_REF_NAME" != "master" ]] if [[ -n "$GITHUB_REF_NAME" && "$GITHUB_REF_NAME" != "master" && "$GITHUB_REF_NAME" != "main" ]]
then then
BRANCH="$GITHUB_REF_NAME" BRANCH="$GITHUB_REF_NAME"
else else
@ -76,7 +77,7 @@ jobs:
"manpages/brew.1" \ "manpages/brew.1" \
"completions" "completions"
else else
git checkout --force --no-track -B "${BRANCH}" origin/master git checkout --force --no-track -B "${BRANCH}" origin/HEAD
fi fi
if brew update-sponsors if brew update-sponsors
@ -111,7 +112,7 @@ jobs:
if [[ -n "${COMMITTED-}" ]] if [[ -n "${COMMITTED-}" ]]
then then
echo "committed=true" >> "$GITHUB_OUTPUT" echo "committed=true" >> "$GITHUB_OUTPUT"
PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state")" PULL_REQUEST_STATE="$(gh pr view --json=state | jq -r ".state" || true)"
if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]] if [[ "${PULL_REQUEST_STATE}" != "OPEN" ]]
then then
echo "pull_request=true" >> "$GITHUB_OUTPUT" echo "pull_request=true" >> "$GITHUB_OUTPUT"
@ -124,7 +125,7 @@ jobs:
- name: Push commits - name: Push commits
if: steps.update.outputs.committed == 'true' if: steps.update.outputs.committed == 'true'
uses: Homebrew/actions/git-try-push@master uses: Homebrew/actions/git-try-push@main
with: with:
token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} token: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
directory: ${{ steps.set-up-homebrew.outputs.repository-path }} directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
@ -137,3 +138,26 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }} GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_PUBLIC_REPO_TOKEN }}
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}
issue:
needs: updates
if: always() && github.event_name == 'schedule'
runs-on: ubuntu-latest
env:
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
permissions:
# To create or update issues
issues: write
steps:
- name: Open, update, or close sponsors, maintainers, manpage and completions issue
uses: Homebrew/actions/create-or-update-issue@main
with:
title: Failed to update sponsors, maintainers, manpage and completions
body: >
The sponsors, maintainers, manpage and completions workflow [failed](${{ env.RUN_URL }}). No sponsors, maintainers, manpage and completions were updated.
labels: bug
update-existing: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
close-existing: ${{ needs.updates.result == 'success' }}
close-from-author: github-actions[bot]
close-comment: >
The sponsors, maintainers, manpage and completions workflow [succeeded](${{ env.RUN_URL }}). Closing this issue.

View File

@ -0,0 +1,63 @@
name: Sync default branches
on:
push:
branches:
- main
- master
pull_request:
paths:
- .github/workflows/sync-default-branches.yml
permissions: {}
defaults:
run:
shell: bash -xeuo pipefail {0}
concurrency:
group: "sync-default-branches-${{ github.ref }}"
cancel-in-progress: true
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Configure Git user
uses: Homebrew/actions/git-user-config@main
with:
username: github-actions[bot]
- name: Determine source and target branches
id: branches
run: |
if [[ "${GITHUB_REF_NAME}" == "main" ]]; then
target="master"
source="main"
else
target="main"
source="master"
fi
echo "target=${target}" >> "$GITHUB_OUTPUT"
echo "source=${source}" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
persist-credentials: true
- name: Setup target branch
run: |
git checkout "${TARGET_BRANCH}" || git checkout -b "${TARGET_BRANCH}"
git reset --hard "origin/${SOURCE_BRANCH}"
env:
SOURCE_BRANCH: ${{ steps.branches.outputs.source }}
TARGET_BRANCH: ${{ steps.branches.outputs.target }}
- name: Push target branch
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
run: git push origin "${TARGET_BRANCH}" --force-with-lease
env:
TARGET_BRANCH: ${{ steps.branches.outputs.target }}

View File

@ -3,6 +3,7 @@ name: CI
on: on:
push: push:
branches: branches:
- main
- master - master
pull_request: pull_request:
merge_group: merge_group:
@ -32,7 +33,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -84,7 +85,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: true core: true
cask: true cask: true
@ -135,11 +136,12 @@ jobs:
if: github.repository_owner == 'Homebrew' && github.event_name != 'push' if: github.repository_owner == 'Homebrew' && github.event_name != 'push'
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
# TODO: switch to main when we're pushing those images
image: ghcr.io/homebrew/brew:master image: ghcr.io/homebrew/brew:master
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -162,7 +164,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: true cask: true
@ -185,14 +187,14 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
test-bot: false test-bot: false
- name: Configure Git user - name: Configure Git user
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
with: with:
username: BrewTestBot username: BrewTestBot
@ -220,7 +222,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -255,7 +257,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
# We only test needs_homebrew_core tests on macOS because # We only test needs_homebrew_core tests on macOS because
# homebrew/core is not available by default on GitHub-hosted Ubuntu # homebrew/core is not available by default on GitHub-hosted Ubuntu
@ -355,6 +357,7 @@ jobs:
container: ghcr.io/homebrew/ubuntu24.04:latest container: ghcr.io/homebrew/ubuntu24.04:latest
- name: test-bot (Linux x86_64) - name: test-bot (Linux x86_64)
runs-on: ubuntu-latest runs-on: ubuntu-latest
# TODO: switch to main when we've migrated to it
container: ghcr.io/homebrew/ubuntu22.04:master container: ghcr.io/homebrew/ubuntu22.04:master
# Use Debian Old Stable for testing Homebrew's glibc support. # Use Debian Old Stable for testing Homebrew's glibc support.
- name: test-bot (Linux Homebrew glibc) - name: test-bot (Linux Homebrew glibc)
@ -402,7 +405,7 @@ jobs:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -442,7 +445,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -496,7 +499,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
- name: Setup Python - name: Setup Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0

View File

@ -9,6 +9,7 @@ on:
paths: paths:
- .github/workflows/vendor-gems.yml - .github/workflows/vendor-gems.yml
branches-ignore: branches-ignore:
- main
- master - master
workflow_dispatch: workflow_dispatch:
inputs: inputs:
@ -31,7 +32,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false
@ -39,13 +40,13 @@ jobs:
- name: Configure Git user - name: Configure Git user
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
with: with:
username: BrewTestBot username: BrewTestBot
- name: Set up commit signing - name: Set up commit signing
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
uses: Homebrew/actions/setup-commit-signing@master uses: Homebrew/actions/setup-commit-signing@main
with: with:
signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }} signing_key: ${{ secrets.BREWTESTBOT_SSH_SIGNING_KEY }}
@ -100,7 +101,7 @@ jobs:
- name: Push to pull request - name: Push to pull request
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
uses: Homebrew/actions/git-try-push@master uses: Homebrew/actions/git-try-push@main
with: with:
token: ${{ steps.app-token.outputs.token }} token: ${{ steps.app-token.outputs.token }}
directory: ${{ steps.set-up-homebrew.outputs.repository-path }} directory: ${{ steps.set-up-homebrew.outputs.repository-path }}

View File

@ -19,7 +19,7 @@ jobs:
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
core: false core: false
cask: false cask: false

1
.github/zizmor.yml vendored
View File

@ -1,3 +1,4 @@
# This file is synced from the `.github` repository, do not modify it directly.
rules: rules:
unpinned-uses: unpinned-uses:
config: config:

11
.vscode/mcp.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"servers": {
"Homebrew": {
"type": "stdio",
"command": "brew",
"args": [
"mcp-server"
]
}
}
}

View File

@ -1,5 +1,12 @@
#!/bin/bash #!/bin/bash
HOMEBREW_PREFIX="$(cd "$(dirname "$0")"/../ && pwd)" if [[ -n "${BASH_SOURCE[0]}" ]]; then
SCRIPT_PATH="${BASH_SOURCE[0]}"
elif [[ -n "${ZSH_VERSION}" ]]; then
SCRIPT_PATH="${(%):-%x}"
else
SCRIPT_PATH="$0"
fi
HOMEBREW_PREFIX="$(cd "$(dirname "${SCRIPT_PATH}")"/../ && pwd)"
"${HOMEBREW_PREFIX}/bin/brew" install-bundler-gems --add-groups=style,typecheck,vscode >/dev/null 2>&1 "${HOMEBREW_PREFIX}/bin/brew" install-bundler-gems --add-groups=style,typecheck,vscode >/dev/null 2>&1

View File

@ -40,7 +40,6 @@
"id": "default", "id": "default",
"name": "Brew Typecheck", "name": "Brew Typecheck",
"description": "Default configuration", "description": "Default configuration",
"cwd": "${workspaceFolder}",
"command": [ "command": [
"./bin/brew", "./bin/brew",
"typecheck", "typecheck",

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "api/analytics" require "api/analytics"
@ -11,10 +11,10 @@ module Homebrew
module API module API
extend Cachable extend Cachable
HOMEBREW_CACHE_API = (HOMEBREW_CACHE/"api").freeze HOMEBREW_CACHE_API = T.let((HOMEBREW_CACHE/"api").freeze, Pathname)
HOMEBREW_CACHE_API_SOURCE = (HOMEBREW_CACHE/"api-source").freeze HOMEBREW_CACHE_API_SOURCE = T.let((HOMEBREW_CACHE/"api-source").freeze, Pathname)
sig { params(endpoint: String).returns(Hash) } sig { params(endpoint: String).returns(T::Hash[String, T.untyped]) }
def self.fetch(endpoint) def self.fetch(endpoint)
return cache[endpoint] if cache.present? && cache.key?(endpoint) return cache[endpoint] if cache.present? && cache.key?(endpoint)
@ -33,7 +33,8 @@ module Homebrew
end end
sig { sig {
params(endpoint: String, target: Pathname, stale_seconds: Integer).returns([T.any(Array, Hash), T::Boolean]) params(endpoint: String, target: Pathname,
stale_seconds: Integer).returns([T.any(T::Array[T.untyped], T::Hash[String, T.untyped]), T::Boolean])
} }
def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint, def self.fetch_json_api_file(endpoint, target: HOMEBREW_CACHE_API/endpoint,
stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i) stale_seconds: Homebrew::EnvConfig.api_auto_update_secs.to_i)
@ -96,7 +97,8 @@ module Homebrew
mtime = insecure_download ? Time.new(1970, 1, 1) : Time.now mtime = insecure_download ? Time.new(1970, 1, 1) : Time.now
FileUtils.touch(target, mtime:) unless skip_download FileUtils.touch(target, mtime:) unless skip_download
JSON.parse(target.read(encoding: Encoding::UTF_8), freeze: true) # Can use `target.read` again when/if https://github.com/sorbet/sorbet/pull/8999 is merged/released.
JSON.parse(File.read(target, encoding: Encoding::UTF_8), freeze: true)
rescue JSON::ParserError rescue JSON::ParserError
target.unlink target.unlink
retry_count += 1 retry_count += 1
@ -122,8 +124,11 @@ module Homebrew
end end
end end
sig { params(json: Hash, bottle_tag: T.nilable(::Utils::Bottles::Tag)).returns(Hash) } sig {
def self.merge_variations(json, bottle_tag: nil) params(json: T::Hash[String, T.untyped],
bottle_tag: ::Utils::Bottles::Tag).returns(T::Hash[String, T.untyped])
}
def self.merge_variations(json, bottle_tag: T.unsafe(nil))
return json unless json.key?("variations") return json unless json.key?("variations")
bottle_tag ||= Homebrew::SimulateSystem.current_tag bottle_tag ||= Homebrew::SimulateSystem.current_tag
@ -147,7 +152,10 @@ module Homebrew
false false
end end
sig { params(json_data: Hash).returns([T::Boolean, T.any(String, Array, Hash)]) } sig {
params(json_data: T::Hash[String, T.untyped])
.returns([T::Boolean, T.any(String, T::Array[T.untyped], T::Hash[String, T.untyped])])
}
private_class_method def self.verify_and_parse_jws(json_data) private_class_method def self.verify_and_parse_jws(json_data)
signatures = json_data["signatures"] signatures = json_data["signatures"]
homebrew_signature = signatures&.find { |sig| sig.dig("header", "kid") == "homebrew-1" } homebrew_signature = signatures&.find { |sig| sig.dig("header", "kid") == "homebrew-1" }

View File

@ -10,7 +10,6 @@ module Homebrew
def analytics_api_path def analytics_api_path
"analytics" "analytics"
end end
alias generic_analytics_api_path analytics_api_path
sig { params(category: String, days: T.any(Integer, String)).returns(T::Hash[String, T.untyped]) } sig { params(category: String, days: T.any(Integer, String)).returns(T::Hash[String, T.untyped]) }
def fetch(category, days) def fetch(category, days)

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "cachable" require "cachable"
@ -21,7 +21,7 @@ module Homebrew
private_class_method :cache private_class_method :cache
sig { params(token: String).returns(Hash) } sig { params(token: String).returns(T::Hash[String, T.untyped]) }
def self.fetch(token) def self.fetch(token)
Homebrew::API.fetch "cask/#{token}.json" Homebrew::API.fetch "cask/#{token}.json"
end end
@ -47,6 +47,7 @@ module Homebrew
.load(config: cask.config) .load(config: cask.config)
end end
sig { returns(Pathname) }
def self.cached_json_file_path def self.cached_json_file_path
HOMEBREW_CACHE_API/api_filename HOMEBREW_CACHE_API/api_filename
end end
@ -70,7 +71,7 @@ module Homebrew
end end
private_class_method :download_and_cache_data! private_class_method :download_and_cache_data!
sig { returns(T::Hash[String, Hash]) } sig { returns(T::Hash[String, T::Hash[String, T.untyped]]) }
def self.all_casks def self.all_casks
unless cache.key?("casks") unless cache.key?("casks")
json_updated = download_and_cache_data! json_updated = download_and_cache_data!

View File

@ -1,22 +1,26 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
# Used to substitute common paths with generic placeholders when generating JSON for the API. # Used to substitute common paths with generic placeholders when generating JSON for the API.
module APIHashable module APIHashable
sig { void }
def generating_hash! def generating_hash!
return if generating_hash? return if generating_hash?
# Apply monkeypatches for API generation # Apply monkeypatches for API generation
@old_homebrew_prefix = HOMEBREW_PREFIX @old_homebrew_prefix = T.let(HOMEBREW_PREFIX, T.nilable(Pathname))
@old_homebrew_cellar = HOMEBREW_CELLAR @old_homebrew_cellar = T.let(HOMEBREW_CELLAR, T.nilable(Pathname))
@old_home = Dir.home @old_home = T.let(Dir.home, T.nilable(String))
@old_git_config_global = T.let(ENV.fetch("GIT_CONFIG_GLOBAL", nil), T.nilable(String))
Object.send(:remove_const, :HOMEBREW_PREFIX) Object.send(:remove_const, :HOMEBREW_PREFIX)
Object.const_set(:HOMEBREW_PREFIX, Pathname.new(HOMEBREW_PREFIX_PLACEHOLDER)) Object.const_set(:HOMEBREW_PREFIX, Pathname.new(HOMEBREW_PREFIX_PLACEHOLDER))
ENV["HOME"] = HOMEBREW_HOME_PLACEHOLDER ENV["HOME"] = HOMEBREW_HOME_PLACEHOLDER
ENV["GIT_CONFIG_GLOBAL"] = File.join(@old_home, ".gitconfig")
@generating_hash = true @generating_hash = T.let(true, T.nilable(T::Boolean))
end end
sig { void }
def generated_hash! def generated_hash!
return unless generating_hash? return unless generating_hash?
@ -24,10 +28,12 @@ module APIHashable
Object.send(:remove_const, :HOMEBREW_PREFIX) Object.send(:remove_const, :HOMEBREW_PREFIX)
Object.const_set(:HOMEBREW_PREFIX, @old_homebrew_prefix) Object.const_set(:HOMEBREW_PREFIX, @old_homebrew_prefix)
ENV["HOME"] = @old_home ENV["HOME"] = @old_home
ENV["GIT_CONFIG_GLOBAL"] = @old_git_config_global
@generating_hash = false @generating_hash = false
end end
sig { returns(T::Boolean) }
def generating_hash? def generating_hash?
@generating_hash ||= false @generating_hash ||= false
@generating_hash == true @generating_hash == true

View File

@ -52,4 +52,4 @@ FORMULA_COMPONENT_PRECEDENCE_LIST = T.let([
[{ name: :caveats, type: :method_definition }], [{ name: :caveats, type: :method_definition }],
[{ name: :plist_options, type: :method_call }, { name: :plist, type: :method_definition }], [{ name: :plist_options, type: :method_call }, { name: :plist, type: :method_definition }],
[{ name: :test, type: :block_call }], [{ name: :test, type: :block_call }],
].freeze, T::Array[[{ name: Symbol, type: Symbol }]]) ].freeze, T::Array[T::Array[{ name: Symbol, type: Symbol }]])

View File

@ -614,6 +614,8 @@ esac
# and, if needed: # and, if needed:
# - MacOSVersion::SYMBOLS # - MacOSVersion::SYMBOLS
HOMEBREW_MACOS_NEWEST_UNSUPPORTED="16" HOMEBREW_MACOS_NEWEST_UNSUPPORTED="16"
# TODO: bump version when new macOS is released
HOMEBREW_MACOS_NEWEST_SUPPORTED="15"
# TODO: bump version when new macOS is released and update references in: # TODO: bump version when new macOS is released and update references in:
# - docs/Installation.md # - docs/Installation.md
# - HOMEBREW_MACOS_OLDEST_SUPPORTED in .github/workflows/pkg-installer.yml # - HOMEBREW_MACOS_OLDEST_SUPPORTED in .github/workflows/pkg-installer.yml
@ -841,6 +843,7 @@ export HOMEBREW_OS_VERSION
export HOMEBREW_MACOS_VERSION export HOMEBREW_MACOS_VERSION
export HOMEBREW_MACOS_VERSION_NUMERIC export HOMEBREW_MACOS_VERSION_NUMERIC
export HOMEBREW_MACOS_NEWEST_UNSUPPORTED export HOMEBREW_MACOS_NEWEST_UNSUPPORTED
export HOMEBREW_MACOS_NEWEST_SUPPORTED
export HOMEBREW_MACOS_OLDEST_SUPPORTED export HOMEBREW_MACOS_OLDEST_SUPPORTED
export HOMEBREW_MACOS_OLDEST_ALLOWED export HOMEBREW_MACOS_OLDEST_ALLOWED
export HOMEBREW_USER_AGENT export HOMEBREW_USER_AGENT

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "bundle/adder" require "bundle/adder"
@ -7,6 +7,7 @@ module Homebrew
module Bundle module Bundle
module Commands module Commands
module Add module Add
sig { params(args: String, type: Symbol, global: T::Boolean, file: T.nilable(String)).void }
def self.run(*args, type:, global:, file:) def self.run(*args, type:, global:, file:)
Homebrew::Bundle::Adder.add(*args, type:, global:, file:) Homebrew::Bundle::Adder.add(*args, type:, global:, file:)
end end

View File

@ -19,7 +19,6 @@ module Homebrew
puts "Installing #{name} tap. It is not currently installed." if verbose puts "Installing #{name} tap. It is not currently installed." if verbose
args = [] args = []
args << "--force" if force args << "--force" if force
args.append("--force-auto-update") if options[:force_auto_update]
success = if options[:clone_target] success = if options[:clone_target]
Bundle.brew("tap", name, options[:clone_target], *args, verbose:) Bundle.brew("tap", name, options[:clone_target], *args, verbose:)

View File

@ -139,7 +139,11 @@ module Cask
def initialize(cask, *dsl_args) def initialize(cask, *dsl_args)
@cask = cask @cask = cask
@dirmethod = nil
@dsl_args = dsl_args.deep_dup @dsl_args = dsl_args.deep_dup
@dsl_key = nil
@english_article = nil
@english_name = nil
end end
def config def config

View File

@ -41,7 +41,9 @@ module Cask
super super
target = target_hash[:target] target = target_hash[:target]
@source = nil
@source_string = source.to_s @source_string = source.to_s
@target = nil
@target_string = target.to_s @target_string = target.to_s
end end

View File

@ -1,9 +1,14 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module Cask module Cask
# Sorted set containing all cask artifacts. # Sorted set containing all cask artifacts.
class ArtifactSet < ::Set class ArtifactSet < ::Set
extend T::Generic
Elem = type_member(:out) { { fixed: Artifact::AbstractArtifact } }
sig { params(block: T.nilable(T.proc.params(arg0: Elem).returns(T.untyped))).void }
def each(&block) def each(&block)
return enum_for(T.must(__method__)) { size } unless block return enum_for(T.must(__method__)) { size } unless block
@ -11,6 +16,7 @@ module Cask
self self
end end
sig { returns(T::Array[Artifact::AbstractArtifact]) }
def to_a def to_a
super.sort super.sort
end end

View File

@ -653,11 +653,12 @@ module Cask
supports_arm = result.merged_output.include?("arm64") supports_arm = result.merged_output.include?("arm64")
mentions_rosetta = cask.caveats.include?("requires Rosetta 2") mentions_rosetta = cask.caveats.include?("requires Rosetta 2")
requires_intel = cask.depends_on.arch&.any? { |arch| arch[:type] == :intel }
if supports_arm && mentions_rosetta if supports_arm && mentions_rosetta
add_error "Artifacts do not require Rosetta 2 but the caveats say otherwise!", add_error "Artifacts do not require Rosetta 2 but the caveats say otherwise!",
location: url.location location: url.location
elsif !supports_arm && !mentions_rosetta elsif !supports_arm && !mentions_rosetta && !requires_intel
add_error "Artifacts require Rosetta 2 but this is not indicated by the caveats!", add_error "Artifacts require Rosetta 2 but this is not indicated by the caveats!",
location: url.location location: url.location
end end
@ -701,45 +702,53 @@ module Cask
return unless online? return unless online?
return unless strict? return unless strict?
odebug "Auditing minimum OS version" odebug "Auditing minimum macOS version"
plist_min_os = cask_plist_min_os bundle_min_os = cask_bundle_min_os
sparkle_min_os = livecheck_min_os sparkle_min_os = cask_sparkle_min_os
app_min_os = [bundle_min_os, sparkle_min_os].compact.max
debug_messages = [] debug_messages = []
debug_messages << "Plist #{plist_min_os}" if plist_min_os debug_messages << "from artifact: #{bundle_min_os.to_sym}" if bundle_min_os
debug_messages << "Sparkle #{sparkle_min_os}" if sparkle_min_os debug_messages << "from upstream: #{sparkle_min_os.to_sym}" if sparkle_min_os
odebug "Detected minimum OS version: #{debug_messages.join(" | ")}" unless debug_messages.empty? odebug "Detected minimum macOS: #{app_min_os.to_sym} (#{debug_messages.join(" | ")})" if app_min_os
min_os = [plist_min_os, sparkle_min_os].compact.max return if app_min_os.nil? || app_min_os <= HOMEBREW_MACOS_OLDEST_ALLOWED
return if min_os.nil? || min_os <= HOMEBREW_MACOS_OLDEST_ALLOWED
on_system_block_min_os = cask.on_system_block_min_os on_system_block_min_os = cask.on_system_block_min_os
cask_min_os = [on_system_block_min_os, cask.depends_on.macos&.minimum_version].compact.max depends_on_min_os = cask.depends_on.macos&.minimum_version
odebug "Declared minimum OS version: #{cask_min_os&.to_sym}"
return if cask_min_os&.to_sym == min_os.to_sym cask_min_os = [on_system_block_min_os, depends_on_min_os].compact.max
return if cask.on_system_blocks_exist? && debug_messages = []
OnSystem.arch_condition_met?(:arm) && debug_messages << "from on_system block: #{on_system_block_min_os.to_sym}" if on_system_block_min_os
if depends_on_min_os > HOMEBREW_MACOS_OLDEST_ALLOWED
debug_messages << "from depends_on stanza: #{depends_on_min_os.to_sym}"
end
odebug "Declared minimum macOS: #{cask_min_os.to_sym} (#{debug_messages.join(" | ").presence || "default"})"
return if cask_min_os.to_sym == app_min_os.to_sym
# ignore declared minimum OS < 11.x when auditing as ARM a cask with arch-specific artifacts
return if OnSystem.arch_condition_met?(:arm) &&
cask.on_system_blocks_exist? &&
cask_min_os.present? && cask_min_os.present? &&
cask_min_os < MacOSVersion.new("11") cask_min_os < MacOSVersion.new("11")
min_os_definition = if cask_min_os.present? min_os_definition = if cask_min_os > HOMEBREW_MACOS_OLDEST_ALLOWED
if on_system_block_min_os.present? && definition = if T.must(on_system_block_min_os.to_s <=> depends_on_min_os.to_s).positive?
on_system_block_min_os > cask.depends_on.macos&.minimum_version "an on_system block"
"a block with a minimum OS version of #{cask_min_os.to_sym.inspect}"
else else
cask_min_os.to_sym.inspect "a depends_on stanza"
end end
"#{definition} with a minimum macOS version of #{cask_min_os.to_sym.inspect}"
else else
"no minimum OS version" "no minimum macOS version"
end end
add_error "Upstream defined #{min_os.to_sym.inspect} as the minimum OS version " \ source = T.must(bundle_min_os.to_s <=> sparkle_min_os.to_s).positive? ? "Artifact" : "Upstream"
add_error "#{source} defined #{app_min_os.to_sym.inspect} as the minimum macOS version " \
"but the cask declared #{min_os_definition}", "but the cask declared #{min_os_definition}",
strict_only: true strict_only: true
end end
sig { returns(T.nilable(MacOSVersion)) } sig { returns(T.nilable(MacOSVersion)) }
def livecheck_min_os def cask_sparkle_min_os
return unless online? return unless online?
return unless cask.livecheck_defined? return unless cask.livecheck_defined?
return if cask.livecheck.strategy != :sparkle return if cask.livecheck.strategy != :sparkle
@ -772,10 +781,10 @@ module Cask
end end
sig { returns(T.nilable(MacOSVersion)) } sig { returns(T.nilable(MacOSVersion)) }
def cask_plist_min_os def cask_bundle_min_os
return unless online? return unless online?
plist_min_os = T.let(nil, T.untyped) min_os = T.let(nil, T.untyped)
@staged_path ||= cask.staged_path @staged_path ||= cask.staged_path
extract_artifacts do |artifacts, tmpdir| extract_artifacts do |artifacts, tmpdir|
@ -786,13 +795,33 @@ module Cask
next unless File.exist?(plist_path) next unless File.exist?(plist_path)
plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", plist_path]).plist plist = system_command!("plutil", args: ["-convert", "xml1", "-o", "-", plist_path]).plist
plist_min_os = plist["LSMinimumSystemVersion"].presence min_os = plist["LSMinimumSystemVersion"].presence
break if plist_min_os break if min_os
next unless (main_binary = get_plist_main_binary(path))
next if !File.exist?(main_binary) || File.open(main_binary, "rb") { |f| f.read(2) == "#!" }
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
macho.machos.map do |slice|
[
slice[:LC_VERSION_MIN_MACOSX].first&.version_string,
slice[:LC_BUILD_VERSION].first&.minos_string,
]
end.flatten
end.compact.min
break if min_os
end end
end end
begin begin
MacOSVersion.new(plist_min_os).strip_patch MacOSVersion.new(min_os).strip_patch
rescue MacOSVersion::Error rescue MacOSVersion::Error
nil nil
end end

View File

@ -295,7 +295,7 @@ module Cask
sig { returns(Pathname) } sig { returns(Pathname) }
attr_reader :path attr_reader :path
sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.anything])) } sig { returns(T.nilable(T::Hash[String, T.untyped])) }
attr_reader :from_json attr_reader :from_json
sig { sig {
@ -320,7 +320,7 @@ module Cask
sig { sig {
params( params(
token: String, token: String,
from_json: T.nilable(T::Hash[T.any(String, Symbol), T.anything]), from_json: T.nilable(T::Hash[String, T.untyped]),
path: T.nilable(Pathname), path: T.nilable(Pathname),
).void ).void
} }

View File

@ -69,7 +69,6 @@ module Cask
].freeze ].freeze
DSL_METHODS = Set.new([ DSL_METHODS = Set.new([
:appcast,
:arch, :arch,
:artifacts, :artifacts,
:auto_updates, :auto_updates,
@ -123,15 +122,21 @@ module Cask
sig { params(cask: Cask).void } sig { params(cask: Cask).void }
def initialize(cask) def initialize(cask)
# NOTE: Variables set by `set_unique_stanza` must be initialized to `nil`. # NOTE: `:"@#{stanza}"` variables set by `set_unique_stanza` must be
@auto_updates = T.let(nil, T.nilable(T::Boolean)) # initialized to `nil`.
@arch = T.let(nil, T.nilable(String)) @arch = T.let(nil, T.nilable(String))
@arch_set_in_block = T.let(false, T::Boolean)
@artifacts = T.let(ArtifactSet.new, ArtifactSet) @artifacts = T.let(ArtifactSet.new, ArtifactSet)
@auto_updates = T.let(nil, T.nilable(T::Boolean))
@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_system_block = T.let(false, T::Boolean)
@cask = T.let(cask, Cask) @cask = T.let(cask, Cask)
@caveats = T.let(DSL::Caveats.new(cask), DSL::Caveats) @caveats = T.let(DSL::Caveats.new(cask), DSL::Caveats)
@conflicts_with = T.let(nil, T.nilable(DSL::ConflictsWith)) @conflicts_with = T.let(nil, T.nilable(DSL::ConflictsWith))
@conflicts_with_set_in_block = T.let(false, T::Boolean)
@container = T.let(nil, T.nilable(DSL::Container)) @container = T.let(nil, T.nilable(DSL::Container))
@container_set_in_block = T.let(false, T::Boolean)
@depends_on = T.let(DSL::DependsOn.new, DSL::DependsOn) @depends_on = T.let(DSL::DependsOn.new, DSL::DependsOn)
@depends_on_set_in_block = T.let(false, T::Boolean) @depends_on_set_in_block = T.let(false, T::Boolean)
@deprecated = T.let(false, T::Boolean) @deprecated = T.let(false, T::Boolean)
@ -140,27 +145,32 @@ module Cask
@deprecation_replacement_cask = T.let(nil, T.nilable(String)) @deprecation_replacement_cask = T.let(nil, T.nilable(String))
@deprecation_replacement_formula = T.let(nil, T.nilable(String)) @deprecation_replacement_formula = T.let(nil, T.nilable(String))
@desc = T.let(nil, T.nilable(String)) @desc = T.let(nil, T.nilable(String))
@desc_set_in_block = T.let(false, T::Boolean)
@disable_date = T.let(nil, T.nilable(Date)) @disable_date = T.let(nil, T.nilable(Date))
@disable_reason = T.let(nil, T.nilable(T.any(String, Symbol))) @disable_reason = T.let(nil, T.nilable(T.any(String, Symbol)))
@disable_replacement_cask = T.let(nil, T.nilable(String)) @disable_replacement_cask = T.let(nil, T.nilable(String))
@disable_replacement_formula = T.let(nil, T.nilable(String)) @disable_replacement_formula = T.let(nil, T.nilable(String))
@disabled = T.let(false, T::Boolean) @disabled = T.let(false, T::Boolean)
@homepage = T.let(nil, T.nilable(String)) @homepage = T.let(nil, T.nilable(String))
@homepage_set_in_block = T.let(false, T::Boolean)
@language_blocks = T.let({}, T::Hash[T::Array[String], Proc]) @language_blocks = T.let({}, T::Hash[T::Array[String], Proc])
@language_eval = T.let(nil, T.nilable(String)) @language_eval = T.let(nil, T.nilable(String))
@livecheck = T.let(Livecheck.new(cask), Livecheck) @livecheck = T.let(Livecheck.new(cask), Livecheck)
@livecheck_defined = T.let(false, T::Boolean) @livecheck_defined = T.let(false, T::Boolean)
@name = T.let([], T::Array[String]) @name = T.let([], T::Array[String])
@autobump = T.let(true, T::Boolean)
@no_autobump_defined = T.let(false, T::Boolean) @no_autobump_defined = T.let(false, T::Boolean)
@on_system_blocks_exist = T.let(false, T::Boolean) @on_system_blocks_exist = T.let(false, T::Boolean)
@os = T.let(nil, T.nilable(String))
@on_system_block_min_os = T.let(nil, T.nilable(MacOSVersion)) @on_system_block_min_os = T.let(nil, T.nilable(MacOSVersion))
@os = T.let(nil, T.nilable(String))
@os_set_in_block = T.let(false, T::Boolean)
@sha256 = T.let(nil, T.nilable(T.any(Checksum, Symbol))) @sha256 = T.let(nil, T.nilable(T.any(Checksum, Symbol)))
@sha256_set_in_block = T.let(false, T::Boolean)
@staged_path = T.let(nil, T.nilable(Pathname)) @staged_path = T.let(nil, T.nilable(Pathname))
@token = T.let(cask.token, String) @token = T.let(cask.token, String)
@url = T.let(nil, T.nilable(URL)) @url = T.let(nil, T.nilable(URL))
@url_set_in_block = T.let(false, T::Boolean)
@version = T.let(nil, T.nilable(DSL::Version)) @version = T.let(nil, T.nilable(DSL::Version))
@version_set_in_block = T.let(false, T::Boolean)
end end
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
@ -216,7 +226,7 @@ module Cask
raise CaskInvalidError.new(cask, "'#{stanza}' stanza may only appear once.") raise CaskInvalidError.new(cask, "'#{stanza}' stanza may only appear once.")
end end
if instance_variable_defined?(:"@#{stanza}_set_in_block") && @called_in_on_system_block if instance_variable_get(:"@#{stanza}_set_in_block") && @called_in_on_system_block
raise CaskInvalidError.new(cask, "'#{stanza}' stanza may only be overridden once.") raise CaskInvalidError.new(cask, "'#{stanza}' stanza may only be overridden once.")
end end
end end
@ -476,7 +486,7 @@ module Cask
def add_implicit_macos_dependency def add_implicit_macos_dependency
return if (cask_depends_on = @depends_on).present? && cask_depends_on.macos.present? return if (cask_depends_on = @depends_on).present? && cask_depends_on.macos.present?
depends_on macos: ">= :#{MacOSVersion::SYMBOLS.key MacOSVersion::SYMBOLS.values.min}" depends_on macos: ">= #{MacOSVersion.new(HOMEBREW_MACOS_OLDEST_ALLOWED).to_sym.inspect}"
end end
# Declare conflicts that keep a cask from installing or working correctly. # Declare conflicts that keep a cask from installing or working correctly.

View File

@ -52,16 +52,17 @@ module Cask
raise "Only a single 'depends_on macos' is allowed." if defined?(@macos) raise "Only a single 'depends_on macos' is allowed." if defined?(@macos)
# workaround for https://github.com/sorbet/sorbet/issues/6860 # workaround for https://github.com/sorbet/sorbet/issues/6860
first_arg = args.first&.to_s first_arg = args.first
first_arg_s = first_arg&.to_s
begin begin
@macos = if args.count > 1 @macos = if args.count > 1
MacOSRequirement.new([args], comparator: "==") MacOSRequirement.new([args], comparator: "==")
elsif MacOSVersion::SYMBOLS.key?(args.first) elsif first_arg.is_a?(Symbol) && MacOSVersion::SYMBOLS.key?(first_arg)
MacOSRequirement.new([args.first], comparator: "==") MacOSRequirement.new([args.first], comparator: "==")
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/.match(first_arg)) elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/.match(first_arg_s))
MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator]) MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator])
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/.match(first_arg)) elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/.match(first_arg_s))
MacOSRequirement.new([md[:version]], comparator: md[:comparator]) MacOSRequirement.new([md[:version]], comparator: md[:comparator])
# This is not duplicate of the first case: see `args.first` and a different comparator. # This is not duplicate of the first case: see `args.first` and a different comparator.
else # rubocop:disable Lint/DuplicateBranch else # rubocop:disable Lint/DuplicateBranch

View File

@ -4,6 +4,7 @@
require "formula_installer" require "formula_installer"
require "unpack_strategy" require "unpack_strategy"
require "utils/topological_hash" require "utils/topological_hash"
require "utils/analytics"
require "cask/config" require "cask/config"
require "cask/download" require "cask/download"
@ -303,6 +304,20 @@ on_request: true)
next if artifact.is_a?(Artifact::Binary) && !binaries? next if artifact.is_a?(Artifact::Binary) && !binaries?
artifact = T.cast(
artifact,
T.any(
Artifact::AbstractFlightBlock,
Artifact::Installer,
Artifact::KeyboardLayout,
Artifact::Mdimporter,
Artifact::Moved,
Artifact::Pkg,
Artifact::Qlplugin,
Artifact::Symlinked,
),
)
artifact.install_phase( artifact.install_phase(
command: @command, verbose: verbose?, adopt: adopt?, auto_updates: @cask.auto_updates, command: @command, verbose: verbose?, adopt: adopt?, auto_updates: @cask.auto_updates,
force: force?, predecessor: force: force?, predecessor:
@ -548,6 +563,18 @@ on_request: true)
artifacts.each do |artifact| artifacts.each do |artifact|
if artifact.respond_to?(:uninstall_phase) if artifact.respond_to?(:uninstall_phase)
artifact = T.cast(
artifact,
T.any(
Artifact::AbstractFlightBlock,
Artifact::KeyboardLayout,
Artifact::Moved,
Artifact::Qlplugin,
Artifact::Symlinked,
Artifact::Uninstall,
),
)
odebug "Uninstalling artifact of class #{artifact.class}" odebug "Uninstalling artifact of class #{artifact.class}"
artifact.uninstall_phase( artifact.uninstall_phase(
command: @command, command: @command,
@ -562,6 +589,8 @@ on_request: true)
next unless artifact.respond_to?(:post_uninstall_phase) next unless artifact.respond_to?(:post_uninstall_phase)
artifact = T.cast(artifact, Artifact::Uninstall)
odebug "Post-uninstalling artifact of class #{artifact.class}" odebug "Post-uninstalling artifact of class #{artifact.class}"
artifact.post_uninstall_phase( artifact.post_uninstall_phase(
command: @command, command: @command,

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "abstract_command" require "abstract_command"
@ -6,6 +6,7 @@ require "formula"
require "fetch" require "fetch"
require "cask/download" require "cask/download"
require "retryable_download" require "retryable_download"
require "download_queue"
module Homebrew module Homebrew
module Cmd module Cmd
@ -69,15 +70,16 @@ module Homebrew
named_args [:formula, :cask], min: 1 named_args [:formula, :cask], min: 1
end end
sig { returns(Integer) }
def concurrency def concurrency
@concurrency ||= args.concurrency&.to_i || 1 @concurrency ||= T.let(args.concurrency&.to_i || 1, T.nilable(Integer))
end end
sig { returns(DownloadQueue) }
def download_queue def download_queue
@download_queue ||= begin @download_queue ||= T.let(begin
require "download_queue"
DownloadQueue.new(concurrency) DownloadQueue.new(concurrency)
end end, T.nilable(DownloadQueue))
end end
class Spinner class Spinner
@ -96,8 +98,8 @@ module Homebrew
sig { void } sig { void }
def initialize def initialize
@start = Time.now @start = T.let(Time.now, Time)
@i = 0 @i = T.let(0, Integer)
end end
sig { returns(String) } sig { returns(String) }
@ -136,7 +138,7 @@ module Homebrew
bucket.each do |formula_or_cask| bucket.each do |formula_or_cask|
case formula_or_cask case formula_or_cask
when Formula when Formula
formula = T.cast(formula_or_cask, Formula) formula = formula_or_cask
ref = formula.loaded_from_api? ? formula.full_name : formula.path ref = formula.loaded_from_api? ? formula.full_name : formula.path
os_arch_combinations.each do |os, arch| os_arch_combinations.each do |os, arch|
@ -189,7 +191,9 @@ module Homebrew
next if fetched_bottle next if fetched_bottle
fetch_downloadable(formula.resource) if (resource = formula.resource)
fetch_downloadable(resource)
end
formula.resources.each do |r| formula.resources.each do |r|
fetch_downloadable(r) fetch_downloadable(r)
@ -231,7 +235,7 @@ module Homebrew
end end
else else
spinner = Spinner.new spinner = Spinner.new
remaining_downloads = downloads.dup remaining_downloads = downloads.dup.to_a
previous_pending_line_count = 0 previous_pending_line_count = 0
begin begin
@ -332,10 +336,13 @@ module Homebrew
private private
sig { returns(T::Hash[T.any(Resource, Bottle, Cask::Download), Concurrent::Promises::Future]) }
def downloads def downloads
@downloads ||= {} @downloads ||= T.let({}, T.nilable(T::Hash[T.any(Resource, Bottle, Cask::Download),
Concurrent::Promises::Future]))
end end
sig { params(downloadable: T.any(Resource, Bottle, Cask::Download)).void }
def fetch_downloadable(downloadable) def fetch_downloadable(downloadable)
downloads[downloadable] ||= begin downloads[downloadable] ||= begin
tries = args.retry? ? {} : { tries: 1 } tries = args.retry? ? {} : { tries: 1 }

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "abstract_command" require "abstract_command"
@ -18,7 +18,7 @@ module Homebrew
class Info < AbstractCommand class Info < AbstractCommand
VALID_DAYS = %w[30 90 365].freeze VALID_DAYS = %w[30 90 365].freeze
VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze VALID_FORMULA_CATEGORIES = %w[install install-on-request build-error].freeze
VALID_CATEGORIES = (VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze VALID_CATEGORIES = T.let((VALID_FORMULA_CATEGORIES + %w[cask-install os-version]).freeze, T::Array[String])
cmd_args do cmd_args do
description <<~EOS description <<~EOS
@ -96,14 +96,17 @@ module Homebrew
end end
print_analytics print_analytics
elsif args.json elsif (json = args.json)
all = args.eval_all? all = args.eval_all?
print_json(all) print_json(json, all)
elsif args.github? elsif args.github?
raise FormulaOrCaskUnspecifiedError if args.no_named? raise FormulaOrCaskUnspecifiedError if args.no_named?
exec_browser(*args.named.to_formulae_and_casks.map { |f| github_info(f) }) exec_browser(*args.named.to_formulae_and_casks.map do |formula_keg_or_cask|
formula_or_cask = T.cast(formula_keg_or_cask, T.any(Formula, Cask::Cask))
github_info(formula_or_cask)
end)
elsif args.no_named? elsif args.no_named?
print_statistics print_statistics
else else
@ -111,6 +114,7 @@ module Homebrew
end end
end end
sig { params(remote: String, path: String).returns(String) }
def github_remote_path(remote, path) def github_remote_path(remote, path)
if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$} if remote =~ %r{^(?:https?://|git(?:@|://))github\.com[:/](.+)/(.+?)(?:\.git)?$}
"https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/HEAD/#{path}" "https://github.com/#{Regexp.last_match(1)}/#{Regexp.last_match(2)}/blob/HEAD/#{path}"
@ -175,6 +179,7 @@ module Homebrew
end end
end end
sig { params(version: T.any(T::Boolean, String)).returns(Symbol) }
def json_version(version) def json_version(version)
version_hash = { version_hash = {
true => :default, true => :default,
@ -187,11 +192,11 @@ module Homebrew
version_hash[version] version_hash[version]
end end
sig { params(all: T::Boolean).void } sig { params(json: T.any(T::Boolean, String), all: T::Boolean).void }
def print_json(all) def print_json(json, all)
raise FormulaOrCaskUnspecifiedError if !(all || args.installed?) && args.no_named? raise FormulaOrCaskUnspecifiedError if !(all || args.installed?) && args.no_named?
json = case json_version(args.json) json = case json_version(json)
when :v1, :default when :v1, :default
raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask? raise UsageError, "Cannot specify `--cask` when using `--json=v1`!" if args.cask?
@ -240,25 +245,31 @@ module Homebrew
puts JSON.pretty_generate(json) puts JSON.pretty_generate(json)
end end
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(String) }
def github_info(formula_or_cask) def github_info(formula_or_cask)
return formula_or_cask.path if formula_or_cask.tap.blank? || formula_or_cask.tap.remote.blank?
path = case formula_or_cask path = case formula_or_cask
when Formula when Formula
formula = formula_or_cask formula = formula_or_cask
formula.path.relative_path_from(T.must(formula.tap).path) tap = formula.tap
return formula.path.to_s if tap.blank? || tap.remote.blank?
formula.path.relative_path_from(tap.path)
when Cask::Cask when Cask::Cask
cask = formula_or_cask cask = formula_or_cask
tap = cask.tap
return cask.sourcefile_path.to_s if tap.blank? || tap.remote.blank?
if cask.sourcefile_path.blank? || cask.sourcefile_path.extname != ".rb" if cask.sourcefile_path.blank? || cask.sourcefile_path.extname != ".rb"
return "#{cask.tap.default_remote}/blob/HEAD/#{cask.tap.relative_cask_path(cask.token)}" return "#{tap.default_remote}/blob/HEAD/#{tap.relative_cask_path(cask.token)}"
end end
cask.sourcefile_path.relative_path_from(cask.tap.path) cask.sourcefile_path.relative_path_from(tap.path)
end end
github_remote_path(formula_or_cask.tap.remote, path) github_remote_path(tap.remote, path.to_s)
end end
sig { params(formula: Formula).void }
def info_formula(formula) def info_formula(formula)
specs = [] specs = []
@ -356,6 +367,7 @@ module Homebrew
Utils::Analytics.formula_output(formula, args:) Utils::Analytics.formula_output(formula, args:)
end end
sig { params(dependencies: T::Array[Dependency]).returns(String) }
def decorate_dependencies(dependencies) def decorate_dependencies(dependencies)
deps_status = dependencies.map do |dep| deps_status = dependencies.map do |dep|
if dep.satisfied?([]) if dep.satisfied?([])
@ -367,6 +379,7 @@ module Homebrew
deps_status.join(", ") deps_status.join(", ")
end end
sig { params(requirements: T::Array[Requirement]).returns(String) }
def decorate_requirements(requirements) def decorate_requirements(requirements)
req_status = requirements.map do |req| req_status = requirements.map do |req|
req_s = req.display_s req_s = req.display_s
@ -375,12 +388,14 @@ module Homebrew
req_status.join(", ") req_status.join(", ")
end end
sig { params(dep: Dependency).returns(String) }
def dep_display_s(dep) def dep_display_s(dep)
return dep.name if dep.option_tags.empty? return dep.name if dep.option_tags.empty?
"#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}" "#{dep.name} #{dep.option_tags.map { |o| "--#{o}" }.join(" ")}"
end end
sig { params(cask: Cask::Cask).void }
def info_cask(cask) def info_cask(cask)
require "cask/info" require "cask/info"

View File

@ -63,6 +63,8 @@ module Homebrew
puts info puts info
else else
info = "" info = ""
default_branches = %w[main master].freeze
taps.each_with_index do |tap, i| taps.each_with_index do |tap, i|
puts unless i.zero? puts unless i.zero?
info = "#{tap}: " info = "#{tap}: "
@ -79,7 +81,7 @@ module Homebrew
info += "\norigin: #{tap.remote}" if tap.remote != tap.default_remote info += "\norigin: #{tap.remote}" if tap.remote != tap.default_remote
info += "\nHEAD: #{tap.git_head || "(none)"}" info += "\nHEAD: #{tap.git_head || "(none)"}"
info += "\nlast commit: #{tap.git_last_commit || "never"}" info += "\nlast commit: #{tap.git_last_commit || "never"}"
info += "\nbranch: #{tap.git_branch || "(none)"}" if tap.git_branch != "master" info += "\nbranch: #{tap.git_branch || "(none)"}" if default_branches.exclude?(tap.git_branch)
else else
info += "Not installed" info += "Not installed"
end end

View File

@ -33,8 +33,6 @@ module Homebrew
"integration.", "integration.",
replacement: false, replacement: false,
disable: true disable: true
switch "--[no-]force-auto-update",
hidden: true
switch "--custom-remote", switch "--custom-remote",
description: "Install or change a tap with a custom remote. Useful for mirrors." description: "Install or change a tap with a custom remote. Useful for mirrors."
switch "--repair", switch "--repair",

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "abstract_command" require "abstract_command"
@ -39,13 +39,15 @@ module Homebrew
private private
sig { void }
def auto_update_header def auto_update_header
@auto_update_header ||= begin @auto_update_header ||= T.let(begin
ohai "Auto-updated Homebrew!" if args.auto_update? ohai "Auto-updated Homebrew!" if args.auto_update?
true true
end end, T.nilable(T::Boolean))
end end
sig { void }
def output_update_report def output_update_report
# Run `brew update` (again) if we've got a linuxbrew-core CoreTap # Run `brew update` (again) if we've got a linuxbrew-core CoreTap
if CoreTap.instance.installed? && CoreTap.instance.linuxbrew_core? && if CoreTap.instance.installed? && CoreTap.instance.linuxbrew_core? &&
@ -293,14 +295,17 @@ module Homebrew
EOS EOS
end end
sig { returns(String) }
def no_changes_message def no_changes_message
"No changes to formulae or casks." "No changes to formulae or casks."
end end
sig { params(revision: String).returns(String) }
def shorten_revision(revision) def shorten_revision(revision)
Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "rev-parse", "--short", revision).chomp Utils.popen_read("git", "-C", HOMEBREW_REPOSITORY, "rev-parse", "--short", revision).chomp
end end
sig { void }
def tap_or_untap_core_taps_if_necessary def tap_or_untap_core_taps_if_necessary
return if ENV["HOMEBREW_UPDATE_TEST"] return if ENV["HOMEBREW_UPDATE_TEST"]
@ -320,10 +325,11 @@ module Homebrew
return if (HOMEBREW_PREFIX/".homebrewdocker").exist? return if (HOMEBREW_PREFIX/".homebrewdocker").exist?
tap_output_header_printed = T.let(false, T::Boolean) tap_output_header_printed = T.let(false, T::Boolean)
default_branches = %w[main master].freeze
[CoreTap.instance, CoreCaskTap.instance].each do |tap| [CoreTap.instance, CoreCaskTap.instance].each do |tap|
next unless tap.installed? next unless tap.installed?
if tap.git_branch == "master" && if default_branches.include?(tap.git_branch) &&
(Date.parse(T.must(tap.git_repository.last_commit_date)) <= Date.today.prev_month) (Date.parse(T.must(tap.git_repository.last_commit_date)) <= Date.today.prev_month)
ohai "#{tap.name} is old and unneeded, untapping to save space..." ohai "#{tap.name} is old and unneeded, untapping to save space..."
tap.uninstall tap.uninstall
@ -339,6 +345,7 @@ module Homebrew
end end
end end
sig { params(repository: Pathname).void }
def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY) def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY)
command = "brew update" command = "brew update"
Utils::Link.link_completions(repository, command) Utils::Link.link_completions(repository, command)
@ -351,10 +358,12 @@ module Homebrew
EOS EOS
end end
sig { void }
def migrate_gcc_dependents_if_needed def migrate_gcc_dependents_if_needed
# do nothing # do nothing
end end
sig { void }
def analytics_message def analytics_message
return if Utils::Analytics.messages_displayed? return if Utils::Analytics.messages_displayed?
return if Utils::Analytics.no_message_output? return if Utils::Analytics.no_message_output?
@ -384,6 +393,7 @@ module Homebrew
Utils::Analytics.messages_displayed! if $stdout.tty? Utils::Analytics.messages_displayed! if $stdout.tty?
end end
sig { void }
def donation_message def donation_message
return if Settings.read("donationmessage") == "true" return if Settings.read("donationmessage") == "true"
@ -394,6 +404,7 @@ module Homebrew
Settings.write "donationmessage", true if $stdout.tty? Settings.write "donationmessage", true if $stdout.tty?
end end
sig { void }
def install_from_api_message def install_from_api_message
return if Settings.read("installfromapimessage") == "true" return if Settings.read("installfromapimessage") == "true"
@ -418,30 +429,38 @@ require "extend/os/cmd/update-report"
class Reporter class Reporter
class ReporterRevisionUnsetError < RuntimeError class ReporterRevisionUnsetError < RuntimeError
sig { params(var_name: String).void }
def initialize(var_name) def initialize(var_name)
super "#{var_name} is unset!" super "#{var_name} is unset!"
end end
end end
sig {
params(tap: Tap, api_names_txt: T.nilable(Pathname), api_names_before_txt: T.nilable(Pathname),
api_dir_prefix: T.nilable(Pathname)).void
}
def initialize(tap, api_names_txt: nil, api_names_before_txt: nil, api_dir_prefix: nil) def initialize(tap, api_names_txt: nil, api_names_before_txt: nil, api_dir_prefix: nil)
@tap = tap @tap = tap
# This is slightly involved/weird but all the #report logic is shared so it's worth it. # This is slightly involved/weird but all the #report logic is shared so it's worth it.
if installed_from_api?(api_names_txt, api_names_before_txt, api_dir_prefix) if installed_from_api?(api_names_txt, api_names_before_txt, api_dir_prefix)
@api_names_txt = api_names_txt @api_names_txt = T.let(api_names_txt, T.nilable(Pathname))
@api_names_before_txt = api_names_before_txt @api_names_before_txt = T.let(api_names_before_txt, T.nilable(Pathname))
@api_dir_prefix = api_dir_prefix @api_dir_prefix = T.let(api_dir_prefix, T.nilable(Pathname))
else else
initial_revision_var = "HOMEBREW_UPDATE_BEFORE#{tap.repository_var_suffix}" initial_revision_var = "HOMEBREW_UPDATE_BEFORE#{tap.repository_var_suffix}"
@initial_revision = ENV[initial_revision_var].to_s @initial_revision = T.let(ENV[initial_revision_var].to_s, String)
raise ReporterRevisionUnsetError, initial_revision_var if @initial_revision.empty? raise ReporterRevisionUnsetError, initial_revision_var if @initial_revision.empty?
current_revision_var = "HOMEBREW_UPDATE_AFTER#{tap.repository_var_suffix}" current_revision_var = "HOMEBREW_UPDATE_AFTER#{tap.repository_var_suffix}"
@current_revision = ENV[current_revision_var].to_s @current_revision = T.let(ENV[current_revision_var].to_s, String)
raise ReporterRevisionUnsetError, current_revision_var if @current_revision.empty? raise ReporterRevisionUnsetError, current_revision_var if @current_revision.empty?
end end
@report = T.let(nil, T.nilable(T::Hash[Symbol, T::Array[String]]))
end end
sig { params(auto_update: T::Boolean).returns(T::Hash[Symbol, T::Array[String]]) }
def report(auto_update: false) def report(auto_update: false)
return @report if @report return @report if @report
@ -482,9 +501,9 @@ class Reporter
case status case status
when "A", "D" when "A", "D"
full_name = tap.formula_file_to_name(src) full_name = tap.formula_file_to_name(src)
name = full_name.split("/").last name = T.must(full_name.split("/").last)
new_tap = tap.tap_migrations[name] new_tap = tap.tap_migrations[name]
@report[status.to_sym] << full_name unless new_tap @report[T.must(status).to_sym] << full_name unless new_tap
when "M" when "M"
name = tap.formula_file_to_name(src) name = tap.formula_file_to_name(src)
@ -584,6 +603,7 @@ class Reporter
@report @report
end end
sig { returns(T::Boolean) }
def updated? def updated?
if installed_from_api? if installed_from_api?
diff.present? diff.present?
@ -592,9 +612,10 @@ class Reporter
end end
end end
sig { void }
def migrate_tap_migration def migrate_tap_migration
(report[:D] + report[:DC]).each do |full_name| (Array(report[:D]) + Array(report[:DC])).each do |full_name|
name = full_name.split("/").last name = T.must(full_name.split("/").last)
new_tap_name = tap.tap_migrations[name] new_tap_name = tap.tap_migrations[name]
next if new_tap_name.nil? # skip if not in tap_migrations list. next if new_tap_name.nil? # skip if not in tap_migrations list.
@ -609,7 +630,7 @@ class Reporter
end end
# This means it is a cask # This means it is a cask
if report[:DC].include? full_name if Array(report[:DC]).include? full_name
next unless (HOMEBREW_PREFIX/"Caskroom"/new_name).exist? next unless (HOMEBREW_PREFIX/"Caskroom"/new_name).exist?
new_tap = Tap.fetch(new_tap_name) new_tap = Tap.fetch(new_tap_name)
@ -675,12 +696,14 @@ class Reporter
end end
end end
sig { void }
def migrate_cask_rename def migrate_cask_rename
Cask::Caskroom.casks.each do |cask| Cask::Caskroom.casks.each do |cask|
Cask::Migrator.migrate_if_needed(cask) Cask::Migrator.migrate_if_needed(cask)
end end
end end
sig { params(force: T::Boolean, verbose: T::Boolean).void }
def migrate_formula_rename(force:, verbose:) def migrate_formula_rename(force:, verbose:)
Formula.installed.each do |formula| Formula.installed.each do |formula|
next unless Migrator.needs_migration?(formula) next unless Migrator.needs_migration?(formula)
@ -704,14 +727,36 @@ class Reporter
private private
attr_reader :tap, :initial_revision, :current_revision, :api_names_txt, :api_names_before_txt, :api_dir_prefix sig { returns(Tap) }
attr_reader :tap
sig { returns(String) }
attr_reader :initial_revision
sig { returns(String) }
attr_reader :current_revision
sig { returns(T.nilable(Pathname)) }
attr_reader :api_names_txt
sig { returns(T.nilable(Pathname)) }
attr_reader :api_names_before_txt
sig { returns(T.nilable(Pathname)) }
attr_reader :api_dir_prefix
sig {
params(api_names_txt: T.nilable(Pathname), api_names_before_txt: T.nilable(Pathname),
api_dir_prefix: T.nilable(Pathname)).returns(T::Boolean)
}
def installed_from_api?(api_names_txt = @api_names_txt, api_names_before_txt = @api_names_before_txt, def installed_from_api?(api_names_txt = @api_names_txt, api_names_before_txt = @api_names_before_txt,
api_dir_prefix = @api_dir_prefix) api_dir_prefix = @api_dir_prefix)
!api_names_txt.nil? && !api_names_before_txt.nil? && !api_dir_prefix.nil? !api_names_txt.nil? && !api_names_before_txt.nil? && !api_dir_prefix.nil?
end end
sig { returns(String) }
def diff def diff
@diff ||= T.let(nil, T.nilable(String))
@diff ||= if installed_from_api? @diff ||= if installed_from_api?
# Hack `git diff` output with regexes to look like `git diff-tree` output. # Hack `git diff` output with regexes to look like `git diff-tree` output.
# Yes, I know this is a bit filthy but it saves duplicating the #report logic. # Yes, I know this is a bit filthy but it saves duplicating the #report logic.
@ -719,12 +764,14 @@ class Reporter
header_regex = /^(---|\+\+\+) / header_regex = /^(---|\+\+\+) /
add_delete_characters = ["+", "-"].freeze add_delete_characters = ["+", "-"].freeze
api_dir_prefix_basename = T.must(api_dir_prefix).basename
diff_output.lines.filter_map do |line| diff_output.lines.filter_map do |line|
next if line.match?(header_regex) next if line.match?(header_regex)
next unless add_delete_characters.include?(line[0]) next unless add_delete_characters.include?(line[0])
line.sub(/^\+/, "A #{api_dir_prefix.basename}/") line.sub(/^\+/, "A #{api_dir_prefix_basename}/")
.sub(/^-/, "D #{api_dir_prefix.basename}/") .sub(/^-/, "D #{api_dir_prefix_basename}/")
.sub(/$/, ".rb") .sub(/$/, ".rb")
.chomp .chomp
end.join("\n") end.join("\n")
@ -738,28 +785,33 @@ class Reporter
end end
class ReporterHub class ReporterHub
sig { returns(T::Array[Reporter]) }
attr_reader :reporters attr_reader :reporters
sig { void } sig { void }
def initialize def initialize
@hash = {} @hash = T.let({}, T::Hash[Symbol, T::Array[String]])
@reporters = [] @reporters = T.let([], T::Array[Reporter])
end end
sig { params(key: Symbol).returns(T::Array[String]) }
def select_formula_or_cask(key) def select_formula_or_cask(key)
@hash.fetch(key, []) @hash.fetch(key, [])
end end
sig { params(reporter: Reporter, auto_update: T::Boolean).void }
def add(reporter, auto_update: false) def add(reporter, auto_update: false)
@reporters << reporter @reporters << reporter
report = reporter.report(auto_update:).delete_if { |_k, v| v.empty? } report = reporter.report(auto_update:).delete_if { |_k, v| v.empty? }
@hash.update(report) { |_key, oldval, newval| oldval.concat(newval) } @hash.update(report) { |_key, oldval, newval| oldval.concat(newval) }
end end
sig { returns(T::Boolean) }
def empty? def empty?
@hash.empty? @hash.empty?
end end
sig { params(auto_update: T::Boolean).void }
def dump(auto_update: false) def dump(auto_update: false)
unless Homebrew::EnvConfig.no_update_report_new? unless Homebrew::EnvConfig.no_update_report_new?
dump_new_formula_report dump_new_formula_report
@ -814,12 +866,14 @@ class ReporterHub
private private
sig { void }
def dump_new_formula_report def dump_new_formula_report
formulae = select_formula_or_cask(:A).sort.reject { |name| installed?(name) } formulae = select_formula_or_cask(:A).sort.reject { |name| installed?(name) }
output_dump_formula_or_cask_report "New Formulae", formulae output_dump_formula_or_cask_report "New Formulae", formulae
end end
sig { void }
def dump_new_cask_report def dump_new_cask_report
return if Homebrew::SimulateSystem.simulating_or_running_on_linux? return if Homebrew::SimulateSystem.simulating_or_running_on_linux?
@ -830,6 +884,7 @@ class ReporterHub
output_dump_formula_or_cask_report "New Casks", casks output_dump_formula_or_cask_report "New Casks", casks
end end
sig { void }
def dump_deleted_formula_report def dump_deleted_formula_report
formulae = select_formula_or_cask(:D).sort.filter_map do |name| formulae = select_formula_or_cask(:D).sort.filter_map do |name|
pretty_uninstalled(name) if installed?(name) pretty_uninstalled(name) if installed?(name)
@ -838,37 +893,43 @@ class ReporterHub
output_dump_formula_or_cask_report "Deleted Installed Formulae", formulae output_dump_formula_or_cask_report "Deleted Installed Formulae", formulae
end end
sig { void }
def dump_deleted_cask_report def dump_deleted_cask_report
return if Homebrew::SimulateSystem.simulating_or_running_on_linux? return if Homebrew::SimulateSystem.simulating_or_running_on_linux?
casks = select_formula_or_cask(:DC).sort.filter_map do |name| casks = select_formula_or_cask(:DC).sort.filter_map do |name|
name = name.split("/").last name = T.must(name.split("/").last)
pretty_uninstalled(name) if cask_installed?(name) pretty_uninstalled(name) if cask_installed?(name)
end end
output_dump_formula_or_cask_report "Deleted Installed Casks", casks output_dump_formula_or_cask_report "Deleted Installed Casks", casks
end end
sig { params(title: String, formulae_or_casks: T::Array[String]).void }
def output_dump_formula_or_cask_report(title, formulae_or_casks) def output_dump_formula_or_cask_report(title, formulae_or_casks)
return if formulae_or_casks.blank? return if formulae_or_casks.blank?
ohai title, Formatter.columns(formulae_or_casks.sort) ohai title, Formatter.columns(formulae_or_casks.sort)
end end
sig { params(formula: String).returns(T::Boolean) }
def installed?(formula) def installed?(formula)
(HOMEBREW_CELLAR/formula.split("/").last).directory? (HOMEBREW_CELLAR/formula.split("/").last).directory?
end end
sig { params(formula: String).returns(T::Boolean) }
def outdated?(formula) def outdated?(formula)
Formula[formula].outdated? Formula[formula].outdated?
rescue FormulaUnavailableError rescue FormulaUnavailableError
false false
end end
sig { params(cask: String).returns(T::Boolean) }
def cask_installed?(cask) def cask_installed?(cask)
(Cask::Caskroom.path/cask).directory? (Cask::Caskroom.path/cask).directory?
end end
sig { params(cask: String).returns(T::Boolean) }
def cask_outdated?(cask) def cask_outdated?(cask)
Cask::CaskLoader.load(cask).outdated? Cask::CaskLoader.load(cask).outdated?
rescue Cask::CaskError rescue Cask::CaskError

View File

@ -54,9 +54,10 @@ git_init_if_necessary() {
fi fi
git config remote.origin.url "${HOMEBREW_BREW_GIT_REMOTE}" git config remote.origin.url "${HOMEBREW_BREW_GIT_REMOTE}"
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git config fetch.prune true
git fetch --force --tags origin git fetch --force --tags origin
git remote set-head origin --auto >/dev/null git remote set-head origin --auto >/dev/null
git reset --hard origin/master git reset --hard origin/HEAD
SKIP_FETCH_BREW_REPOSITORY=1 SKIP_FETCH_BREW_REPOSITORY=1
set +e set +e
trap - EXIT trap - EXIT
@ -77,9 +78,10 @@ git_init_if_necessary() {
fi fi
git config remote.origin.url "${HOMEBREW_CORE_GIT_REMOTE}" git config remote.origin.url "${HOMEBREW_CORE_GIT_REMOTE}"
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch --force origin refs/heads/master:refs/remotes/origin/master git config fetch.prune true
git fetch --force origin
git remote set-head origin --auto >/dev/null git remote set-head origin --auto >/dev/null
git reset --hard origin/master git reset --hard origin/HEAD
SKIP_FETCH_CORE_REPOSITORY=1 SKIP_FETCH_CORE_REPOSITORY=1
set +e set +e
trap - EXIT trap - EXIT
@ -110,7 +112,7 @@ upstream_branch() {
upstream_branch="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null)" upstream_branch="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null)"
fi fi
upstream_branch="${upstream_branch#refs/remotes/origin/}" upstream_branch="${upstream_branch#refs/remotes/origin/}"
[[ -z "${upstream_branch}" ]] && upstream_branch="master" [[ -z "${upstream_branch}" ]] && upstream_branch="main"
echo "${upstream_branch}" echo "${upstream_branch}"
} }
@ -242,7 +244,7 @@ merge_or_rebase() {
Could not 'git stash' in ${DIR}! Could not 'git stash' in ${DIR}!
Please stash/commit manually if you need to keep your changes or, if not, run: Please stash/commit manually if you need to keep your changes or, if not, run:
cd ${DIR} cd ${DIR}
git reset --hard origin/master git reset --hard origin/HEAD
EOS EOS
fi fi
git reset --hard "${QUIET_ARGS[@]}" git reset --hard "${QUIET_ARGS[@]}"
@ -260,10 +262,12 @@ EOS
then then
git checkout --force "${UPSTREAM_BRANCH}" "${QUIET_ARGS[@]}" git checkout --force "${UPSTREAM_BRANCH}" "${QUIET_ARGS[@]}"
else else
if [[ -n "${UPSTREAM_TAG}" && "${UPSTREAM_BRANCH}" != "master" ]] && if [[ -n "${UPSTREAM_TAG}" && "${UPSTREAM_BRANCH}" != "master" && "${UPSTREAM_BRANCH}" != "main" ]] &&
[[ "${INITIAL_BRANCH}" != "master" ]] [[ "${INITIAL_BRANCH}" != "master" && "${INITIAL_BRANCH}" != "main" ]]
then then
git branch --force "master" "origin/master" "${QUIET_ARGS[@]}" local detected_upstream_branch
detected_upstream_branch="$(upstream_branch)"
git branch --force "${detected_upstream_branch}" "origin/${detected_upstream_branch}" "${QUIET_ARGS[@]}"
fi fi
git checkout --force -B "${UPSTREAM_BRANCH}" "${REMOTE_REF}" "${QUIET_ARGS[@]}" git checkout --force -B "${UPSTREAM_BRANCH}" "${REMOTE_REF}" "${QUIET_ARGS[@]}"
@ -541,7 +545,8 @@ EOS
echo "HOMEBREW_CORE_GIT_REMOTE set: using ${HOMEBREW_CORE_GIT_REMOTE} as the Homebrew/homebrew-core Git remote." echo "HOMEBREW_CORE_GIT_REMOTE set: using ${HOMEBREW_CORE_GIT_REMOTE} as the Homebrew/homebrew-core Git remote."
git remote set-url origin "${HOMEBREW_CORE_GIT_REMOTE}" git remote set-url origin "${HOMEBREW_CORE_GIT_REMOTE}"
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
git fetch --force origin refs/heads/master:refs/remotes/origin/master git config fetch.prune true
git fetch --force origin
SKIP_FETCH_CORE_REPOSITORY=1 SKIP_FETCH_CORE_REPOSITORY=1
fi fi
@ -642,9 +647,9 @@ EOS
UPDATING_MESSAGE_SHOWN=1 UPDATING_MESSAGE_SHOWN=1
fi fi
# The upstream repository's default branch may not be master; # The upstream repository's default branch may not be main or master;
# check refs/remotes/origin/HEAD to see what the default # check refs/remotes/origin/HEAD to see what the default
# origin branch name is, and use that. If not set, fall back to "master". # origin branch name is, and use that. If not set, fall back to "main".
# the refspec ensures that the default upstream branch gets updated # the refspec ensures that the default upstream branch gets updated
( (
UPSTREAM_REPOSITORY_URL="$(git config remote.origin.url)" UPSTREAM_REPOSITORY_URL="$(git config remote.origin.url)"
@ -732,9 +737,9 @@ EOS
then then
local git_errors local git_errors
git_errors="$(cat "${tmp_failure_file}")" git_errors="$(cat "${tmp_failure_file}")"
# Attempt migration from master to main branch.
if [[ "${git_errors}" == "fatal: couldn't find remote ref refs/heads/master" ]] if [[ "${git_errors}" == "fatal: couldn't find remote ref refs/heads/master" ]]
then then
# Attempt migration from master to main branch.
if git fetch --tags --force "${QUIET_ARGS[@]}" origin \ if git fetch --tags --force "${QUIET_ARGS[@]}" origin \
"refs/heads/main:refs/remotes/origin/main" 2>>"${tmp_failure_file}" "refs/heads/main:refs/remotes/origin/main" 2>>"${tmp_failure_file}"
then then

View File

@ -1,12 +1,12 @@
{ {
"$schema" : "https://json-schema.org/draft/2019-09/schema", "$schema" : "https://json-schema.org/draft/2019-09/schema",
"$id" : "http://spdx.org/rdf/terms/2.3", "$id" : "http://spdx.org/rdf/terms/2.3",
"title" : "SPDX 2.3", "title" : "SPDX 2.3.1-dev",
"type" : "object", "type" : "object",
"properties" : { "properties" : {
"$schema": { "$schema": {
"type": "string", "type": "string",
"description": "Reference the SPDX 2.3 JSON schema." "description": "Reference the SPDX 2.3.1 JSON schema."
}, },
"SPDXID" : { "SPDXID" : {
"type" : "string", "type" : "string",
@ -90,7 +90,7 @@
"enum" : [ "SHA1", "BLAKE3", "SHA3-384", "SHA256", "SHA384", "BLAKE2b-512", "BLAKE2b-256", "SHA3-512", "MD2", "ADLER32", "MD4", "SHA3-256", "BLAKE2b-384", "SHA512", "MD6", "MD5", "SHA224" ] "enum" : [ "SHA1", "BLAKE3", "SHA3-384", "SHA256", "SHA384", "BLAKE2b-512", "BLAKE2b-256", "SHA3-512", "MD2", "ADLER32", "MD4", "SHA3-256", "BLAKE2b-384", "SHA512", "MD6", "MD5", "SHA224" ]
}, },
"checksumValue" : { "checksumValue" : {
"description" : "The checksumValue property provides a lower case hexidecimal encoded digest value produced using a specific algorithm.", "description" : "The checksumValue property provides a lower case hexadecimal encoded digest value produced using a specific algorithm.",
"type" : "string" "type" : "string"
} }
}, },
@ -270,10 +270,10 @@
} }
}, },
"attributionTexts" : { "attributionTexts" : {
"description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConculded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.", "description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConcluded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.",
"type" : "array", "type" : "array",
"items" : { "items" : {
"description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConculded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.", "description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConcluded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.",
"type" : "string" "type" : "string"
} }
}, },
@ -293,7 +293,7 @@
"enum" : [ "SHA1", "BLAKE3", "SHA3-384", "SHA256", "SHA384", "BLAKE2b-512", "BLAKE2b-256", "SHA3-512", "MD2", "ADLER32", "MD4", "SHA3-256", "BLAKE2b-384", "SHA512", "MD6", "MD5", "SHA224" ] "enum" : [ "SHA1", "BLAKE3", "SHA3-384", "SHA256", "SHA384", "BLAKE2b-512", "BLAKE2b-256", "SHA3-512", "MD2", "ADLER32", "MD4", "SHA3-256", "BLAKE2b-384", "SHA512", "MD6", "MD5", "SHA224" ]
}, },
"checksumValue" : { "checksumValue" : {
"description" : "The checksumValue property provides a lower case hexidecimal encoded digest value produced using a specific algorithm.", "description" : "The checksumValue property provides a lower case hexadecimal encoded digest value produced using a specific algorithm.",
"type" : "string" "type" : "string"
} }
}, },
@ -375,10 +375,10 @@
"type" : "string" "type" : "string"
}, },
"licenseInfoFromFiles" : { "licenseInfoFromFiles" : {
"description" : "The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of alllicenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same package is true or omitted, it implies an equivalent meaning to NOASSERTION.", "description" : "The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of all licenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same package is true or omitted, it implies an equivalent meaning to NOASSERTION.",
"type" : "array", "type" : "array",
"items" : { "items" : {
"description" : "License expression for licenseInfoFromFiles. See SPDX Annex D for the license expression syntax. The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of alllicenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same package is true or omitted, it implies an equivalent meaning to NOASSERTION.", "description" : "License expression for licenseInfoFromFiles. See SPDX Annex D for the license expression syntax. The licensing information that was discovered directly within the package. There will be an instance of this property for each distinct value of all licenseInfoInFile properties of all files contained in the package.\n\nIf the licenseInfoFromFiles field is not present for a package and filesAnalyzed property for that same package is true or omitted, it implies an equivalent meaning to NOASSERTION.",
"type" : "string" "type" : "string"
} }
}, },
@ -417,7 +417,7 @@
"primaryPackagePurpose" : { "primaryPackagePurpose" : {
"description" : "This field provides information about the primary purpose of the identified package. Package Purpose is intrinsic to how the package is being used rather than the content of the package.", "description" : "This field provides information about the primary purpose of the identified package. Package Purpose is intrinsic to how the package is being used rather than the content of the package.",
"type" : "string", "type" : "string",
"enum" : [ "OTHER", "INSTALL", "ARCHIVE", "FIRMWARE", "APPLICATION", "FRAMEWORK", "LIBRARY", "CONTAINER", "SOURCE", "DEVICE", "OPERATING_SYSTEM", "FILE" ] "enum" : [ "OTHER", "INSTALL", "ARCHIVE", "FIRMWARE", "APPLICATION", "FRAMEWORK", "LIBRARY", "CONTAINER", "SOURCE", "DEVICE", "OPERATING-SYSTEM", "FILE" ]
}, },
"releaseDate" : { "releaseDate" : {
"description" : "This field provides a place for recording the date the package was released.", "description" : "This field provides a place for recording the date the package was released.",
@ -494,10 +494,10 @@
} }
}, },
"attributionTexts" : { "attributionTexts" : {
"description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConculded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.", "description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConcluded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.",
"type" : "array", "type" : "array",
"items" : { "items" : {
"description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConculded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.", "description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConcluded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.",
"type" : "string" "type" : "string"
} }
}, },
@ -514,7 +514,7 @@
"enum" : [ "SHA1", "BLAKE3", "SHA3-384", "SHA256", "SHA384", "BLAKE2b-512", "BLAKE2b-256", "SHA3-512", "MD2", "ADLER32", "MD4", "SHA3-256", "BLAKE2b-384", "SHA512", "MD6", "MD5", "SHA224" ] "enum" : [ "SHA1", "BLAKE3", "SHA3-384", "SHA256", "SHA384", "BLAKE2b-512", "BLAKE2b-256", "SHA3-512", "MD2", "ADLER32", "MD4", "SHA3-256", "BLAKE2b-384", "SHA512", "MD6", "MD5", "SHA224" ]
}, },
"checksumValue" : { "checksumValue" : {
"description" : "The checksumValue property provides a lower case hexidecimal encoded digest value produced using a specific algorithm.", "description" : "The checksumValue property provides a lower case hexadecimal encoded digest value produced using a specific algorithm.",
"type" : "string" "type" : "string"
} }
}, },
@ -624,10 +624,10 @@
} }
}, },
"attributionTexts" : { "attributionTexts" : {
"description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConculded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.", "description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConcluded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.",
"type" : "array", "type" : "array",
"items" : { "items" : {
"description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConculded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.", "description" : "This field provides a place for the SPDX data creator to record acknowledgements that may be required to be communicated in some contexts. This is not meant to include the actual complete license text (see licenseConcluded and licenseDeclared), and may or may not include copyright notices (see also copyrightText). The SPDX data creator may use this field to record other acknowledgements, such as particular clauses from license texts, which may be necessary or desirable to reproduce.",
"type" : "string" "type" : "string"
} }
}, },
@ -709,7 +709,7 @@
} }
}, },
"snippetFromFile" : { "snippetFromFile" : {
"description" : "SPDX ID for File. File containing the SPDX element (e.g. the file contaning a snippet).", "description" : "SPDX ID for File. File containing the SPDX element (e.g. the file containing a snippet).",
"type" : "string" "type" : "string"
} }
}, },

View File

@ -1,13 +1,15 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "irb" require "irb"
module IRB module IRB
sig { params(binding: Binding).void }
def self.start_within(binding) def self.start_within(binding)
old_stdout_sync = $stdout.sync old_stdout_sync = $stdout.sync
$stdout.sync = true $stdout.sync = true
@setup_done ||= T.let(false, T.nilable(T::Boolean))
unless @setup_done unless @setup_done
setup(nil, argv: []) setup(nil, argv: [])
@setup_done = true @setup_done = true

View File

@ -190,7 +190,7 @@ module Homebrew
def generate_system_options(cask) def generate_system_options(cask)
current_os = Homebrew::SimulateSystem.current_os current_os = Homebrew::SimulateSystem.current_os
current_os_is_macos = MacOSVersion::SYMBOLS.include?(current_os) current_os_is_macos = MacOSVersion::SYMBOLS.include?(current_os)
newest_macos = MacOSVersion::SYMBOLS.keys.first newest_macos = MacOSVersion.new(HOMEBREW_MACOS_NEWEST_SUPPORTED).to_sym
depends_on_archs = cask.depends_on.arch&.filter_map { |arch| arch[:type] }&.uniq depends_on_archs = cask.depends_on.arch&.filter_map { |arch| arch[:type] }&.uniq

View File

@ -399,7 +399,7 @@ module Homebrew
nil nil
end end
if github_release_data.present? if github_release_data.present? && github_release_data["body"].present?
pre = "pre" if github_release_data["prerelease"].present? pre = "pre" if github_release_data["prerelease"].present?
# maximum length of PR body is 65,536 characters so let's truncate release notes to half of that. # maximum length of PR body is 65,536 characters so let's truncate release notes to half of that.
body = Formatter.truncate(github_release_data["body"], max: 32_768) body = Formatter.truncate(github_release_data["body"], max: 32_768)

View File

@ -183,41 +183,42 @@ module Homebrew
:zig :zig
end end
fc = FormulaCreator.new( formula_creator = FormulaCreator.new(
args.set_name,
args.set_version,
tap: args.tap,
url: args.named.fetch(0), url: args.named.fetch(0),
name: args.set_name,
version: args.set_version,
tap: args.tap,
mode:, mode:,
license: args.set_license, license: args.set_license,
fetch: !args.no_fetch?, fetch: !args.no_fetch?,
head: args.HEAD?, head: args.HEAD?,
) )
fc.parse_url
# ask for confirmation if name wasn't passed explicitly # ask for confirmation if name wasn't passed explicitly
if args.set_name.blank? if args.set_name.blank?
print "Formula name [#{fc.name}]: " print "Formula name [#{formula_creator.name}]: "
fc.name = __gets || fc.name confirmed_name = __gets
formula_creator.name = confirmed_name if confirmed_name.present?
end end
fc.verify formula_creator.verify_tap_available!
# Check for disallowed formula, or names that shadow aliases, # Check for disallowed formula, or names that shadow aliases,
# unless --force is specified. # unless --force is specified.
unless args.force? unless args.force?
if (reason = MissingFormula.disallowed_reason(fc.name)) if (reason = MissingFormula.disallowed_reason(formula_creator.name))
odie <<~EOS odie <<~EOS
The formula '#{fc.name}' is not allowed to be created. The formula '#{formula_creator.name}' is not allowed to be created.
#{reason} #{reason}
If you really want to create this formula use `--force`. If you really want to create this formula use `--force`.
EOS EOS
end end
Homebrew.with_no_api_env do Homebrew.with_no_api_env do
if Formula.aliases.include? fc.name if Formula.aliases.include?(formula_creator.name)
realname = Formulary.canonical_name(fc.name) realname = Formulary.canonical_name(formula_creator.name)
odie <<~EOS odie <<~EOS
The formula '#{realname}' is already aliased to '#{fc.name}'. The formula '#{realname}' is already aliased to '#{formula_creator.name}'.
Please check that you are not creating a duplicate. Please check that you are not creating a duplicate.
To force creation use `--force`. To force creation use `--force`.
EOS EOS
@ -225,19 +226,19 @@ module Homebrew
end end
end end
path = fc.write_formula! path = formula_creator.write_formula!
formula = Homebrew.with_no_api_env do formula = Homebrew.with_no_api_env do
CoreTap.instance.clear_cache CoreTap.instance.clear_cache
Formula[fc.name] Formula[formula_creator.name]
end end
PyPI.update_python_resources! formula, ignore_non_pypi_packages: true if args.python? PyPI.update_python_resources! formula, ignore_non_pypi_packages: true if args.python?
puts <<~EOS puts <<~EOS
Please audit and test formula before submitting: Please audit and test formula before submitting:
HOMEBREW_NO_INSTALL_FROM_API=1 brew audit --new #{fc.name} HOMEBREW_NO_INSTALL_FROM_API=1 brew audit --new #{formula_creator.name}
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug #{fc.name} HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug #{formula_creator.name}
HOMEBREW_NO_INSTALL_FROM_API=1 brew test #{fc.name} HOMEBREW_NO_INSTALL_FROM_API=1 brew test #{formula_creator.name}
EOS EOS
path path
end end

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:disable Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "abstract_command" require "abstract_command"
@ -172,7 +172,8 @@ module Homebrew
with_monkey_patch { Formulary.from_contents(name, file, contents, ignore_errors: true) } with_monkey_patch { Formulary.from_contents(name, file, contents, ignore_errors: true) }
end end
def with_monkey_patch sig { params(_block: T.proc.void).returns(T.untyped) }
def with_monkey_patch(&_block)
# Since `method_defined?` is not a supported type guard, the use of `alias_method` below is not typesafe: # Since `method_defined?` is not a supported type guard, the use of `alias_method` below is not typesafe:
BottleSpecification.class_eval do BottleSpecification.class_eval do
T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing)
@ -210,28 +211,28 @@ module Homebrew
BottleSpecification.class_eval do BottleSpecification.class_eval do
if method_defined?(:old_method_missing) if method_defined?(:old_method_missing)
T.unsafe(self).alias_method :method_missing, :old_method_missing T.unsafe(self).alias_method :method_missing, :old_method_missing
undef :old_method_missing T.unsafe(self).undef :old_method_missing
end end
end end
Module.class_eval do Module.class_eval do
if method_defined?(:old_method_missing) if method_defined?(:old_method_missing)
T.unsafe(self).alias_method :method_missing, :old_method_missing T.unsafe(self).alias_method :method_missing, :old_method_missing
undef :old_method_missing T.unsafe(self).undef :old_method_missing
end end
end end
Resource.class_eval do Resource.class_eval do
if method_defined?(:old_method_missing) if method_defined?(:old_method_missing)
T.unsafe(self).alias_method :method_missing, :old_method_missing T.unsafe(self).alias_method :method_missing, :old_method_missing
undef :old_method_missing T.unsafe(self).undef :old_method_missing
end end
end end
DependencyCollector.class_eval do DependencyCollector.class_eval do
if method_defined?(:old_parse_symbol_spec) if method_defined?(:old_parse_symbol_spec)
T.unsafe(self).alias_method :parse_symbol_spec, :old_parse_symbol_spec T.unsafe(self).alias_method :parse_symbol_spec, :old_parse_symbol_spec
undef :old_parse_symbol_spec T.unsafe(self).undef :old_parse_symbol_spec
end end
end end
end end

View File

@ -45,7 +45,7 @@ module Homebrew
Cask::Cask.generating_hash! Cask::Cask.generating_hash!
all_casks = {} all_casks = {}
latest_macos = MacOSVersion.new((HOMEBREW_MACOS_NEWEST_UNSUPPORTED.to_i - 1).to_s).to_sym latest_macos = MacOSVersion.new(HOMEBREW_MACOS_NEWEST_SUPPORTED).to_sym
Homebrew::SimulateSystem.with(os: latest_macos, arch: :arm) do Homebrew::SimulateSystem.with(os: latest_macos, arch: :arm) do
tap.cask_files.each do |path| tap.cask_files.each do |path|
cask = Cask::CaskLoader.load(path) cask = Cask::CaskLoader.load(path)

View File

@ -67,8 +67,8 @@ module Homebrew
end end
raise UsageError, "Only one url can be specified" if pr_url&.count&.> 1 raise UsageError, "Only one url can be specified" if pr_url&.count&.> 1
labels = if pr_url labels = if pr_url && (first_pr_url = pr_url.first)
pr = GitHub::API.open_rest(pr_url.first) pr = GitHub::API.open_rest(first_pr_url)
pr.fetch("labels").map { |l| l.fetch("name") } pr.fetch("labels").map { |l| l.fetch("name") }
else else
[] []
@ -263,11 +263,10 @@ module Homebrew
audit_exceptions << %w[homepage_https_availability] if labels.include?("ci-skip-homepage") audit_exceptions << %w[homepage_https_availability] if labels.include?("ci-skip-homepage")
if labels.include?("ci-skip-livecheck") if labels.include?("ci-skip-livecheck")
audit_exceptions << %w[hosting_with_livecheck livecheck_https_availability audit_exceptions << %w[hosting_with_livecheck livecheck_https_availability livecheck_version min_os]
livecheck_min_os livecheck_version]
end end
audit_exceptions << "livecheck_min_os" if labels.include?("ci-skip-livecheck-min-os") audit_exceptions << "min_os" if labels.include?("ci-skip-livecheck-min-os")
if labels.include?("ci-skip-repository") if labels.include?("ci-skip-repository")
audit_exceptions << %w[github_repository github_prerelease_version audit_exceptions << %w[github_repository github_prerelease_version

View File

@ -44,12 +44,11 @@ module Homebrew
titleized_repository = tap.repository.dup titleized_repository = tap.repository.dup
titleized_user[0] = titleized_user[0].upcase titleized_user[0] = titleized_user[0].upcase
titleized_repository[0] = titleized_repository[0].upcase titleized_repository[0] = titleized_repository[0].upcase
root_url = GitHubPackages.root_url(tap.user, "homebrew-#{tap.repository}") if args.github_packages? # 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?
(tap.path/"Formula").mkpath (tap.path/"Formula").mkpath
# FIXME: https://github.com/errata-ai/vale/issues/818
# <!-- vale off -->
readme = <<~MARKDOWN readme = <<~MARKDOWN
# #{titleized_user} #{titleized_repository} # #{titleized_user} #{titleized_repository}
@ -70,7 +69,6 @@ module Homebrew
`brew help`, `man brew` or check [Homebrew's documentation](https://docs.brew.sh). `brew help`, `man brew` or check [Homebrew's documentation](https://docs.brew.sh).
MARKDOWN MARKDOWN
# <!-- vale on -->
write_path(tap, "README.md", readme) write_path(tap, "README.md", readme)
tests_yml = <<~ERB tests_yml = <<~ERB
@ -99,7 +97,7 @@ module Homebrew
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
id: set-up-homebrew id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
token: ${{ github.token }} token: ${{ github.token }}
@ -164,12 +162,12 @@ module Homebrew
pull-requests: write pull-requests: write
steps: steps:
- name: Set up Homebrew - name: Set up Homebrew
uses: Homebrew/actions/setup-homebrew@master uses: Homebrew/actions/setup-homebrew@main
with: with:
token: ${{ github.token }} token: ${{ github.token }}
- name: Set up git - name: Set up git
uses: Homebrew/actions/git-user-config@master uses: Homebrew/actions/git-user-config@main
- name: Pull bottles - name: Pull bottles
env: env:
@ -182,7 +180,7 @@ module Homebrew
run: brew pr-pull --debug --tap="$GITHUB_REPOSITORY" "$PULL_REQUEST" run: brew pr-pull --debug --tap="$GITHUB_REPOSITORY" "$PULL_REQUEST"
- name: Push commits - name: Push commits
uses: Homebrew/actions/git-try-push@master uses: Homebrew/actions/git-try-push@main
with: with:
branch: <%= branch %> branch: <%= branch %>

View File

@ -51,6 +51,11 @@ module Homebrew
HOMEBREW_LIBRARY_PATH.cd do HOMEBREW_LIBRARY_PATH.cd do
setup_environment! setup_environment!
# Needs required here, after `setup_environment!`, so that
# `HOMEBREW_TEST_GENERIC_OS` is set and `OS.linux?` and `OS.mac?` both
# `return false`.
require "extend/os/dev-cmd/tests"
parallel = !args.no_parallel? parallel = !args.no_parallel?
only = args.only only = args.only
@ -267,5 +272,3 @@ module Homebrew
end end
end end
end end
require "extend/os/dev-cmd/tests"

View File

@ -142,6 +142,7 @@ module Homebrew
private private
sig { returns(String) }
def git_tags def git_tags
tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname") tags = Utils.popen_read("git", "tag", "--list", "--sort=-version:refname")
if tags.blank? if tags.blank?

View File

@ -350,7 +350,6 @@ module Homebrew
sudo chmod +t #{HOMEBREW_TEMP} sudo chmod +t #{HOMEBREW_TEMP}
EOS EOS
end end
alias generic_check_tmpdir_sticky_bit check_tmpdir_sticky_bit
def check_exist_directories def check_exist_directories
return if HOMEBREW_PREFIX.writable? return if HOMEBREW_PREFIX.writable?

View File

@ -54,8 +54,6 @@ module SharedEnvExtension
@debug_symbols = debug_symbols @debug_symbols = debug_symbols
reset reset
end end
alias generic_shared_setup_build_environment setup_build_environment
private :generic_shared_setup_build_environment
sig { void } sig { void }
def reset def reset

View File

@ -68,7 +68,6 @@ module Stdenv
gcc_formula = gcc_version_formula(cc) gcc_formula = gcc_version_formula(cc)
append_path "PATH", gcc_formula.opt_bin.to_s append_path "PATH", gcc_formula.opt_bin.to_s
end end
alias generic_setup_build_environment setup_build_environment
sig { returns(T.nilable(PATH)) } sig { returns(T.nilable(PATH)) }
def determine_pkg_config_libdir def determine_pkg_config_libdir

View File

@ -125,7 +125,6 @@ module Superenv
# These flags will also be present: # These flags will also be present:
# a - apply fix for apr-1-config path # a - apply fix for apr-1-config path
end end
alias generic_setup_build_environment setup_build_environment
private private
@ -152,7 +151,6 @@ module Superenv
.reverse .reverse
.map { |d| d.opt_libexec/"bin" } .map { |d| d.opt_libexec/"bin" }
end end
alias generic_homebrew_extra_paths homebrew_extra_paths
sig { returns(T.nilable(PATH)) } sig { returns(T.nilable(PATH)) }
def determine_path def determine_path
@ -372,8 +370,8 @@ module Superenv
append_to_cccfg "O" append_to_cccfg "O"
end end
# This is an exception where we want to use this method name format.
# rubocop: disable Naming/MethodName # rubocop: disable Naming/MethodName
# Fixes style error `Naming/MethodName: Use snake_case for method names.`
sig { params(block: T.nilable(T.proc.void)).void } sig { params(block: T.nilable(T.proc.void)).void }
def O0(&block) def O0(&block)
if block if block

View File

@ -15,6 +15,11 @@ module OS
def os_bundle_args(bundle_args) def os_bundle_args(bundle_args)
non_macos_bundle_args(bundle_args) non_macos_bundle_args(bundle_args)
end end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def os_files(files)
non_macos_files(files)
end
end end
end end
end end

View File

@ -1,19 +1,19 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module Homebrew module OS
module Linux
module DevCmd module DevCmd
class UpdateTest < AbstractCommand module UpdateTest
alias generic_git_tags git_tags
private private
sig { returns(String) } sig { returns(String) }
def git_tags def git_tags
tags = generic_git_tags super.presence || Utils.popen_read("git tag --list | sort -rV")
tags = Utils.popen_read("git tag --list | sort -rV") if tags.blank? end
tags
end end
end end
end end
end end
Homebrew::DevCmd::UpdateTest.prepend(OS::Linux::DevCmd::UpdateTest)

View File

@ -62,7 +62,7 @@ module OS
def build_system_info def build_system_info
super.merge({ super.merge({
"glibc_version" => OS::Linux::Glibc.version.to_s.presence, "glibc_version" => OS::Linux::Glibc.version.to_s.presence,
"oldest_cpu_family" => Hardware.oldest_cpu.to_s, "oldest_cpu_family" => ::Hardware.oldest_cpu.to_s,
}) })
end end
end end

View File

@ -32,7 +32,7 @@ module OS
end end
def check_tmpdir_sticky_bit def check_tmpdir_sticky_bit
message = generic_check_tmpdir_sticky_bit message = super
return if message.nil? return if message.nil?
message + <<~EOS message + <<~EOS
@ -74,11 +74,11 @@ module OS
end end
def check_supported_architecture def check_supported_architecture
return if Hardware::CPU.intel? return if ::Hardware::CPU.intel?
return if Homebrew::EnvConfig.developer? && ENV["HOMEBREW_ARM64_TESTING"].present? && Hardware::CPU.arm? return if Homebrew::EnvConfig.developer? && ENV["HOMEBREW_ARM64_TESTING"].present? && ::Hardware::CPU.arm?
<<~EOS <<~EOS
Your CPU architecture (#{Hardware::CPU.arch}) is not supported. We only support Your CPU architecture (#{::Hardware::CPU.arch}) is not supported. We only support
x86_64 CPU architectures. You will be unable to use binary packages (bottles). x86_64 CPU architectures. You will be unable to use binary packages (bottles).
#{support_tier_message(tier: 2)} #{support_tier_message(tier: 2)}

View File

@ -1,16 +1,22 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module SharedEnvExtension module OS
module Linux
module SharedEnvExtension
def effective_arch def effective_arch
if @build_bottle && @bottle_arch if @build_bottle && @bottle_arch
@bottle_arch.to_sym @bottle_arch.to_sym
elsif @build_bottle elsif @build_bottle
Hardware.oldest_cpu ::Hardware.oldest_cpu
elsif Hardware::CPU.intel? || Hardware::CPU.arm? elsif ::Hardware::CPU.intel? || ::Hardware::CPU.arm?
:native :native
else else
:dunno :dunno
end end
end end
end
end
end end
SharedEnvExtension.prepend(OS::Linux::SharedEnvExtension)

View File

@ -1,10 +1,16 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module Stdenv module OS
module Linux
module Stdenv
extend T::Helpers
requires_ancestor { ::SharedEnvExtension }
sig { sig {
params( params(
formula: T.nilable(Formula), formula: T.nilable(::Formula),
cc: T.nilable(String), cc: T.nilable(String),
build_bottle: T.nilable(T::Boolean), build_bottle: T.nilable(T::Boolean),
bottle_arch: T.nilable(String), bottle_arch: T.nilable(String),
@ -12,10 +18,9 @@ module Stdenv
debug_symbols: T.nilable(T::Boolean), debug_symbols: T.nilable(T::Boolean),
).void ).void
} }
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false, def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
debug_symbols: false) testing_formula: false, debug_symbols: false)
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:, super
testing_formula:, debug_symbols:)
prepend_path "CPATH", HOMEBREW_PREFIX/"include" prepend_path "CPATH", HOMEBREW_PREFIX/"include"
prepend_path "LIBRARY_PATH", HOMEBREW_PREFIX/"lib" prepend_path "LIBRARY_PATH", HOMEBREW_PREFIX/"lib"
@ -29,8 +34,12 @@ module Stdenv
end end
def libxml2 def libxml2
append "CPPFLAGS", "-I#{Formula["libxml2"].include/"libxml2"}" append "CPPFLAGS", "-I#{::Formula["libxml2"].include/"libxml2"}"
rescue FormulaUnavailableError rescue FormulaUnavailableError
nil nil
end end
end
end
end end
Stdenv.prepend(OS::Linux::Stdenv)

View File

@ -1,16 +1,25 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module Superenv module OS
module Linux
module Superenv
extend T::Helpers
requires_ancestor { SharedEnvExtension }
requires_ancestor { ::Superenv }
module ClassMethods
sig { returns(Pathname) } sig { returns(Pathname) }
def self.shims_path def shims_path
HOMEBREW_SHIMS_PATH/"linux/super" HOMEBREW_SHIMS_PATH/"linux/super"
end end
sig { returns(T.nilable(Pathname)) } sig { returns(T.nilable(Pathname)) }
def self.bin def bin
shims_path.realpath shims_path.realpath
end end
end
sig { sig {
params( params(
@ -22,10 +31,10 @@ module Superenv
debug_symbols: T.nilable(T::Boolean), debug_symbols: T.nilable(T::Boolean),
).void ).void
} }
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false, def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
debug_symbols: false) testing_formula: false, debug_symbols: false)
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:, super
testing_formula:, debug_symbols:)
self["HOMEBREW_OPTIMIZATION_LEVEL"] = "O2" self["HOMEBREW_OPTIMIZATION_LEVEL"] = "O2"
self["HOMEBREW_DYNAMIC_LINKER"] = determine_dynamic_linker_path self["HOMEBREW_DYNAMIC_LINKER"] = determine_dynamic_linker_path
self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula) self["HOMEBREW_RPATH_PATHS"] = determine_rpath_paths(@formula)
@ -35,11 +44,11 @@ module Superenv
# Pointer authentication and BTI are hardening techniques most distros # Pointer authentication and BTI are hardening techniques most distros
# use by default on their packages. arm64 Linux we're packaging # use by default on their packages. arm64 Linux we're packaging
# everything from scratch so the entire dependency tree can have it. # everything from scratch so the entire dependency tree can have it.
append_to_cccfg "b" if Hardware::CPU.arch == :arm64 && DevelopmentTools.gcc_version("gcc") >= 9 append_to_cccfg "b" if ::Hardware::CPU.arch == :arm64 && ::DevelopmentTools.gcc_version("gcc") >= 9
end end
def homebrew_extra_paths def homebrew_extra_paths
paths = generic_homebrew_extra_paths paths = super
paths += %w[binutils make].filter_map do |f| paths += %w[binutils make].filter_map do |f|
bin = Formulary.factory(f).opt_bin bin = Formulary.factory(f).opt_bin
bin if bin.directory? bin if bin.directory?
@ -76,4 +85,9 @@ module Superenv
path path
end end
end
end
end end
Superenv.singleton_class.prepend(OS::Linux::Superenv::ClassMethods)
Superenv.prepend(OS::Linux::Superenv)

View File

@ -1,9 +1,15 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module FormulaCellarChecks module OS
module Linux
module FormulaCellarChecks
sig { params(filename: Pathname).returns(T::Boolean) } sig { params(filename: Pathname).returns(T::Boolean) }
def valid_library_extension?(filename) def valid_library_extension?(filename)
generic_valid_library_extension?(filename) || filename.basename.to_s.include?(".so.") super || filename.basename.to_s.include?(".so.")
end
end
end end
end end
FormulaCellarChecks.prepend(OS::Linux::FormulaCellarChecks)

View File

@ -1,12 +1,18 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module Hardware module OS
class CPU module Linux
class << self module Hardware
module CPU
module ClassMethods
extend T::Helpers
requires_ancestor { T.class_of(::Hardware::CPU) }
def optimization_flags def optimization_flags
@optimization_flags ||= begin @optimization_flags ||= begin
flags = generic_optimization_flags.dup flags = super.dup
flags[:native] = arch_flag(Homebrew::EnvConfig.arch) flags[:native] = arch_flag(Homebrew::EnvConfig.arch)
flags flags
end end
@ -138,7 +144,7 @@ module Hardware
%w[aes altivec avx avx2 lm ssse3 sse4_2].each do |flag| %w[aes altivec avx avx2 lm ssse3 sse4_2].each do |flag|
define_method(:"#{flag}?") do define_method(:"#{flag}?") do
T.bind(self, T.class_of(Hardware::CPU)) T.bind(self, OS::Linux::Hardware::CPU::ClassMethods)
flags.include? flag flags.include? flag
end end
end end
@ -158,4 +164,8 @@ module Hardware
end end
end end
end end
end
end
end end
Hardware::CPU.singleton_class.prepend(OS::Linux::Hardware::CPU::ClassMethods)

View File

@ -1,8 +1,10 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module Homebrew module OS
module Linux
module Install module Install
module ClassMethods
# This is a list of known paths to the host dynamic linker on Linux if # This is a list of known paths to the host dynamic linker on Linux if
# the host glibc is new enough. The symlink_ld_so method will fail if # the host glibc is new enough. The symlink_ld_so method will fail if
# the host linker cannot be found in this list. # the host linker cannot be found in this list.
@ -16,7 +18,6 @@ module Homebrew
/system/bin/linker64 /system/bin/linker64
/system/bin/linker /system/bin/linker
].freeze ].freeze
private_constant :DYNAMIC_LINKERS
# We link GCC runtime libraries that are not specifically used for Fortran, # We link GCC runtime libraries that are not specifically used for Fortran,
# which are linked by the GCC formula. We only use the versioned shared libraries # which are linked by the GCC formula. We only use the versioned shared libraries
@ -28,44 +29,41 @@ module Homebrew
libgomp.so.1 libgomp.so.1
libstdc++.so.6 libstdc++.so.6
].freeze ].freeze
private_constant :GCC_RUNTIME_LIBS
def self.perform_preinstall_checks(all_fatal: false) def perform_preinstall_checks(all_fatal: false)
generic_perform_preinstall_checks(all_fatal:) super
symlink_ld_so
setup_preferred_gcc_libs
end
private_class_method :perform_preinstall_checks
def self.global_post_install
generic_global_post_install
symlink_ld_so symlink_ld_so
setup_preferred_gcc_libs setup_preferred_gcc_libs
end end
def self.check_cpu def global_post_install
return if Hardware::CPU.intel? && Hardware::CPU.is_64_bit? super
return if Hardware::CPU.arm? symlink_ld_so
setup_preferred_gcc_libs
end
def check_cpu
return if ::Hardware::CPU.intel? && ::Hardware::CPU.is_64_bit?
return if ::Hardware::CPU.arm?
message = "Sorry, Homebrew does not support your computer's CPU architecture!" message = "Sorry, Homebrew does not support your computer's CPU architecture!"
if Hardware::CPU.ppc64le? if ::Hardware::CPU.ppc64le?
message += <<~EOS message += <<~EOS
For OpenPOWER Linux (PPC64LE) support, see: For OpenPOWER Linux (PPC64LE) support, see:
#{Formatter.url("https://github.com/homebrew-ppc64le/brew")} #{Formatter.url("https://github.com/homebrew-ppc64le/brew")}
EOS EOS
end end
abort message ::Kernel.abort message
end end
private_class_method :check_cpu
def self.symlink_ld_so def symlink_ld_so
brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so" brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so" ld_so = HOMEBREW_PREFIX/"opt/glibc/bin/ld.so"
unless ld_so.readable? unless ld_so.readable?
ld_so = DYNAMIC_LINKERS.find { |s| File.executable? s } ld_so = DYNAMIC_LINKERS.find { |s| File.executable? s }
if ld_so.blank? if ld_so.blank?
raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable? ::Kernel.raise "Unable to locate the system's dynamic linker" unless brew_ld_so.readable?
return return
end end
@ -76,9 +74,8 @@ module Homebrew
FileUtils.mkdir_p HOMEBREW_PREFIX/"lib" FileUtils.mkdir_p HOMEBREW_PREFIX/"lib"
FileUtils.ln_sf ld_so, brew_ld_so FileUtils.ln_sf ld_so, brew_ld_so
end end
private_class_method :symlink_ld_so
def self.setup_preferred_gcc_libs def setup_preferred_gcc_libs
gcc_opt_prefix = HOMEBREW_PREFIX/"opt/#{OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA}" gcc_opt_prefix = HOMEBREW_PREFIX/"opt/#{OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA}"
glibc_installed = (HOMEBREW_PREFIX/"opt/glibc/bin/ld.so").readable? glibc_installed = (HOMEBREW_PREFIX/"opt/glibc/bin/ld.so").readable?
@ -103,10 +100,10 @@ module Homebrew
FileUtils.chmod "u=rw,go-wx", ld_gcc_conf FileUtils.chmod "u=rw,go-wx", ld_gcc_conf
FileUtils.rm_f HOMEBREW_PREFIX/"etc/ld.so.cache" FileUtils.rm_f HOMEBREW_PREFIX/"etc/ld.so.cache"
system HOMEBREW_PREFIX/"opt/glibc/sbin/ldconfig" ::Kernel.system HOMEBREW_PREFIX/"opt/glibc/sbin/ldconfig"
end end
else else
odie "#{HOMEBREW_PREFIX}/lib does not exist!" unless (HOMEBREW_PREFIX/"lib").readable? ::Kernel.odie "#{HOMEBREW_PREFIX}/lib does not exist!" unless (HOMEBREW_PREFIX/"lib").readable?
end end
GCC_RUNTIME_LIBS.each do |library| GCC_RUNTIME_LIBS.each do |library|
@ -127,6 +124,9 @@ module Homebrew
end end
end end
end end
private_class_method :setup_preferred_gcc_libs end
end
end end
end end
Homebrew::Install.singleton_class.prepend(OS::Linux::Install::ClassMethods)

View File

@ -3,7 +3,9 @@
require "compilers" require "compilers"
class LinkageChecker module OS
module Linux
module LinkageChecker
# Libraries provided by glibc and gcc. # Libraries provided by glibc and gcc.
SYSTEM_LIBRARY_ALLOWLIST = %w[ SYSTEM_LIBRARY_ALLOWLIST = %w[
ld-linux-x86-64.so.2 ld-linux-x86-64.so.2
@ -29,7 +31,7 @@ class LinkageChecker
private private
def check_dylibs(rebuild_cache:) def check_dylibs(rebuild_cache:)
generic_check_dylibs(rebuild_cache:) super
# glibc and gcc are implicit dependencies. # glibc and gcc are implicit dependencies.
# No other linkage to system libraries is expected or desired. # No other linkage to system libraries is expected or desired.
@ -39,7 +41,8 @@ class LinkageChecker
# We build all formulae with an RPATH that includes the gcc formula's runtime lib directory. # We build all formulae with an RPATH that includes the gcc formula's runtime lib directory.
# See: https://github.com/Homebrew/brew/blob/e689cc07/Library/Homebrew/extend/os/linux/extend/ENV/super.rb#L53 # See: https://github.com/Homebrew/brew/blob/e689cc07/Library/Homebrew/extend/os/linux/extend/ENV/super.rb#L53
# This results in formulae showing linkage with gcc whenever it is installed, even if no dependency is declared. # This results in formulae showing linkage with gcc whenever it is installed, even if no dependency is
# declared.
# See discussions at: # See discussions at:
# https://github.com/Homebrew/brew/pull/13659 # https://github.com/Homebrew/brew/pull/13659
# https://github.com/Homebrew/brew/pull/13796 # https://github.com/Homebrew/brew/pull/13796
@ -47,4 +50,8 @@ class LinkageChecker
@undeclared_deps.delete("gcc") @undeclared_deps.delete("gcc")
@indirect_deps.delete("gcc") @indirect_deps.delete("gcc")
end end
end
end
end end
LinkageChecker.prepend(OS::Linux::LinkageChecker)

View File

@ -5,12 +5,14 @@ require "compilers"
require "os/linux/glibc" require "os/linux/glibc"
require "system_command" require "system_command"
module SystemConfig module OS
module Linux
module SystemConfig
module ClassMethods
include SystemCommand::Mixin include SystemCommand::Mixin
HOST_RUBY_PATH = "/usr/bin/ruby" HOST_RUBY_PATH = "/usr/bin/ruby"
class << self
def host_glibc_version def host_glibc_version
version = OS::Linux::Glibc.system_version version = OS::Linux::Glibc.system_version
return "N/A" if version.null? return "N/A" if version.null?
@ -19,10 +21,10 @@ module SystemConfig
end end
def host_gcc_version def host_gcc_version
gcc = DevelopmentTools.host_gcc_path gcc = ::DevelopmentTools.host_gcc_path
return "N/A" unless gcc.executable? return "N/A" unless gcc.executable?
`#{gcc} --version 2>/dev/null`[/ (\d+\.\d+\.\d+)/, 1] Utils.popen_read(gcc, "--version")[/ (\d+\.\d+\.\d+)/, 1]
end end
def formula_linked_version(formula) def formula_linked_version(formula)
@ -42,16 +44,20 @@ module SystemConfig
def dump_verbose_config(out = $stdout) def dump_verbose_config(out = $stdout)
kernel = Utils.safe_popen_read("uname", "-mors").chomp kernel = Utils.safe_popen_read("uname", "-mors").chomp
dump_generic_verbose_config(out) super
out.puts "Kernel: #{kernel}" out.puts "Kernel: #{kernel}"
out.puts "OS: #{OS::Linux.os_version}" out.puts "OS: #{OS::Linux.os_version}"
out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl? out.puts "WSL: #{OS::Linux.wsl_version}" if OS::Linux.wsl?
out.puts "Host glibc: #{host_glibc_version}" out.puts "Host glibc: #{host_glibc_version}"
out.puts "#{DevelopmentTools.host_gcc_path}: #{host_gcc_version}" out.puts "#{::DevelopmentTools.host_gcc_path}: #{host_gcc_version}"
out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH out.puts "/usr/bin/ruby: #{host_ruby_version}" if RUBY_PATH != HOST_RUBY_PATH
["glibc", CompilerSelector.preferred_gcc, OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA, "xorg"].each do |f| ["glibc", CompilerSelector.preferred_gcc, OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA, "xorg"].each do |f|
out.puts "#{f}: #{formula_linked_version(f)}" out.puts "#{f}: #{formula_linked_version(f)}"
end end
end end
end end
end
end
end end
SystemConfig.singleton_class.prepend(OS::Linux::SystemConfig::ClassMethods)

View File

@ -15,6 +15,11 @@ module OS
def os_bundle_args(bundle_args) def os_bundle_args(bundle_args)
non_linux_bundle_args(bundle_args) non_linux_bundle_args(bundle_args)
end end
sig { params(files: T::Array[String]).returns(T::Array[String]) }
def os_files(files)
non_linux_files(files)
end
end end
end end
end end

View File

@ -1,10 +1,16 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module SharedEnvExtension module OS
module Mac
module SharedEnvExtension
extend T::Helpers
requires_ancestor { ::SharedEnvExtension }
sig { sig {
params( params(
formula: T.nilable(Formula), formula: T.nilable(::Formula),
cc: T.nilable(String), cc: T.nilable(String),
build_bottle: T.nilable(T::Boolean), build_bottle: T.nilable(T::Boolean),
bottle_arch: T.nilable(String), bottle_arch: T.nilable(String),
@ -12,11 +18,9 @@ module SharedEnvExtension
debug_symbols: T.nilable(T::Boolean), debug_symbols: T.nilable(T::Boolean),
).void ).void
} }
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false, def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
debug_symbols: false) testing_formula: false, debug_symbols: false)
generic_shared_setup_build_environment(formula:, cc:, build_bottle:, super
bottle_arch:, testing_formula:,
debug_symbols:)
# Normalise the system Perl version used, where multiple may be available # Normalise the system Perl version used, where multiple may be available
self["VERSIONER_PERL_VERSION"] = MacOS.preferred_perl_version self["VERSIONER_PERL_VERSION"] = MacOS.preferred_perl_version
@ -39,4 +43,8 @@ module SharedEnvExtension
# https://en.wikipedia.org/wiki/Xcode#Xcode_11.0_-_14.x_(since_SwiftUI_framework)_2 # https://en.wikipedia.org/wiki/Xcode#Xcode_11.0_-_14.x_(since_SwiftUI_framework)_2
OS::Mac::DevelopmentTools.ld64_version >= 711 OS::Mac::DevelopmentTools.ld64_version >= 711
end end
end
end
end end
SharedEnvExtension.prepend(OS::Mac::SharedEnvExtension)

View File

@ -1,8 +1,13 @@
# typed: true # rubocop:disable Sorbet/StrictSigil # typed: true # rubocop:disable Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module Stdenv module OS
undef homebrew_extra_pkg_config_paths module Mac
module Stdenv
extend T::Helpers
requires_ancestor { SharedEnvExtension }
requires_ancestor { ::Stdenv }
sig { returns(T::Array[Pathname]) } sig { returns(T::Array[Pathname]) }
def homebrew_extra_pkg_config_paths def homebrew_extra_pkg_config_paths
@ -20,10 +25,9 @@ module Stdenv
debug_symbols: T.nilable(T::Boolean), debug_symbols: T.nilable(T::Boolean),
).void ).void
} }
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false, def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
debug_symbols: false) testing_formula: false, debug_symbols: false)
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:, super
testing_formula:, debug_symbols:)
append "LDFLAGS", "-Wl,-headerpad_max_install_names" append "LDFLAGS", "-Wl,-headerpad_max_install_names"
@ -114,4 +118,8 @@ module Stdenv
def no_fixup_chains def no_fixup_chains
append "LDFLAGS", "-Wl,-no_fixup_chains" if no_fixup_chains_support? append "LDFLAGS", "-Wl,-no_fixup_chains" if no_fixup_chains_support?
end end
end
end
end end
Stdenv.prepend(OS::Mac::Stdenv)

View File

@ -1,34 +1,32 @@
# typed: true # rubocop:disable Sorbet/StrictSigil # typed: true # rubocop:disable Sorbet/StrictSigil
# frozen_string_literal: true # frozen_string_literal: true
module Superenv module OS
class << self module Mac
# The location of Homebrew's shims on macOS. module Superenv
extend T::Helpers
requires_ancestor { SharedEnvExtension }
requires_ancestor { ::Superenv }
module ClassMethods
sig { returns(Pathname) }
def shims_path def shims_path
HOMEBREW_SHIMS_PATH/"mac/super" HOMEBREW_SHIMS_PATH/"mac/super"
end end
undef bin sig { returns(T.nilable(Pathname)) }
def bin def bin
return unless DevelopmentTools.installed? return unless ::DevelopmentTools.installed?
shims_path.realpath shims_path.realpath
end end
end end
undef homebrew_extra_pkg_config_paths,
homebrew_extra_isystem_paths, homebrew_extra_library_paths,
homebrew_extra_cmake_include_paths,
homebrew_extra_cmake_library_paths,
homebrew_extra_cmake_frameworks_paths,
determine_cccfg
sig { returns(T::Array[Pathname]) } sig { returns(T::Array[Pathname]) }
def homebrew_extra_pkg_config_paths def homebrew_extra_pkg_config_paths
[Pathname("/usr/lib/pkgconfig"), Pathname("#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}")] [Pathname("/usr/lib/pkgconfig"), Pathname("#{HOMEBREW_LIBRARY}/Homebrew/os/mac/pkgconfig/#{MacOS.version}")]
end end
private :homebrew_extra_pkg_config_paths
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def libxml2_include_needed? def libxml2_include_needed?
@ -37,7 +35,6 @@ module Superenv
true true
end end
private :libxml2_include_needed?
def homebrew_extra_isystem_paths def homebrew_extra_isystem_paths
paths = [] paths = []
@ -51,7 +48,7 @@ module Superenv
paths = [] paths = []
if compiler == :llvm_clang if compiler == :llvm_clang
paths << "#{self["HOMEBREW_SDKROOT"]}/usr/lib" paths << "#{self["HOMEBREW_SDKROOT"]}/usr/lib"
paths << Formula["llvm"].opt_lib.to_s paths << ::Formula["llvm"].opt_lib.to_s
end end
paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries" paths << "#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries"
paths paths
@ -66,7 +63,8 @@ module Superenv
end end
def homebrew_extra_cmake_library_paths def homebrew_extra_cmake_library_paths
[Pathname("#{self["HOMEBREW_SDKROOT"]}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries")] brew_sdkroot = self["HOMEBREW_SDKROOT"]
[Pathname("#{brew_sdkroot}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries")]
end end
def homebrew_extra_cmake_frameworks_paths def homebrew_extra_cmake_frameworks_paths
@ -83,8 +81,8 @@ module Superenv
end end
# @private # @private
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil, testing_formula: false, def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil,
debug_symbols: false) testing_formula: false, debug_symbols: false)
sdk = formula ? MacOS.sdk_for_formula(formula) : MacOS.sdk sdk = formula ? MacOS.sdk_for_formula(formula) : MacOS.sdk
is_xcode_sdk = sdk&.source == :xcode is_xcode_sdk = sdk&.source == :xcode
@ -105,12 +103,11 @@ module Superenv
if deps.none? { |d| d.name == "m4" } && if deps.none? { |d| d.name == "m4" } &&
MacOS.active_developer_dir == MacOS::CLT::PKG_PATH && MacOS.active_developer_dir == MacOS::CLT::PKG_PATH &&
!File.exist?("#{MacOS::CLT::PKG_PATH}/usr/bin/m4") && !File.exist?("#{MacOS::CLT::PKG_PATH}/usr/bin/m4") &&
(gm4 = DevelopmentTools.locate("gm4").to_s).present? (gm4 = ::DevelopmentTools.locate("gm4").to_s).present?
self["M4"] = gm4 self["M4"] = gm4
end end
generic_setup_build_environment(formula:, cc:, build_bottle:, bottle_arch:, super
testing_formula:, debug_symbols:)
# Filter out symbols known not to be defined since GNU Autotools can't # Filter out symbols known not to be defined since GNU Autotools can't
# reliably figure this out with Xcode 8 and above. # reliably figure this out with Xcode 8 and above.
@ -168,4 +165,9 @@ module Superenv
def no_fixup_chains def no_fixup_chains
append_to_cccfg "f" if no_fixup_chains_support? append_to_cccfg "f" if no_fixup_chains_support?
end end
end
end
end end
Superenv.singleton_class.prepend(OS::Mac::Superenv::ClassMethods)
Superenv.prepend(OS::Mac::Superenv)

View File

@ -4,7 +4,14 @@
require "cache_store" require "cache_store"
require "linkage_checker" require "linkage_checker"
module FormulaCellarChecks module OS
module Mac
module FormulaCellarChecks
extend T::Helpers
requires_ancestor { Homebrew::FormulaAuditor }
requires_ancestor { ::FormulaCellarChecks }
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def check_shadowed_headers def check_shadowed_headers
return if ["libtool", "subversion", "berkeley-db"].any? do |formula_name| return if ["libtool", "subversion", "berkeley-db"].any? do |formula_name|
@ -31,7 +38,7 @@ module FormulaCellarChecks
def check_openssl_links def check_openssl_links
return unless formula.prefix.directory? return unless formula.prefix.directory?
keg = Keg.new(formula.prefix) keg = ::Keg.new(formula.prefix)
system_openssl = keg.mach_o_files.select do |obj| system_openssl = keg.mach_o_files.select do |obj|
dlls = obj.dynamically_linked_libraries dlls = obj.dynamically_linked_libraries
dlls.any? { |dll| %r{/usr/lib/lib(crypto|ssl|tls)\..*dylib}.match? dll } dlls.any? { |dll| %r{/usr/lib/lib(crypto|ssl|tls)\..*dylib}.match? dll }
@ -69,10 +76,10 @@ module FormulaCellarChecks
def check_linkage def check_linkage
return unless formula.prefix.directory? return unless formula.prefix.directory?
keg = Keg.new(formula.prefix) keg = ::Keg.new(formula.prefix)
CacheStoreDatabase.use(:linkage) do |db| CacheStoreDatabase.use(:linkage) do |db|
checker = LinkageChecker.new(keg, formula, cache_db: db) checker = ::LinkageChecker.new(keg, formula, cache_db: db)
next unless checker.broken_library_linkage? next unless checker.broken_library_linkage?
output = <<~EOS output = <<~EOS
@ -85,14 +92,14 @@ module FormulaCellarChecks
output += <<~EOS output += <<~EOS
Rebuild this from source with: Rebuild this from source with:
brew reinstall --build-from-source #{formula} brew reinstall --build-from-source #{formula}
If that's successful, file an issue#{formula.tap ? " here:\n #{T.must(formula.tap).issues_url}" : "."} If that's successful, file an issue#{formula.tap ? " here:\n #{formula.tap.issues_url}" : "."}
EOS EOS
end end
problem_if_output output problem_if_output output
end end
end end
sig { params(formula: Formula).returns(T.nilable(String)) } sig { params(formula: ::Formula).returns(T.nilable(String)) }
def check_flat_namespace(formula) def check_flat_namespace(formula)
return unless formula.prefix.directory? return unless formula.prefix.directory?
return if formula.tap&.audit_exception(:flat_namespace_allowlist, formula.name) return if formula.tap&.audit_exception(:flat_namespace_allowlist, formula.name)
@ -120,7 +127,7 @@ module FormulaCellarChecks
sig { void } sig { void }
def audit_installed def audit_installed
generic_audit_installed super
problem_if_output(check_shadowed_headers) problem_if_output(check_shadowed_headers)
problem_if_output(check_openssl_links) problem_if_output(check_openssl_links)
problem_if_output(check_python_framework_links(formula.lib)) problem_if_output(check_python_framework_links(formula.lib))
@ -128,9 +135,14 @@ module FormulaCellarChecks
problem_if_output(check_flat_namespace(formula)) problem_if_output(check_flat_namespace(formula))
end end
MACOS_LIB_EXTENSIONS = %w[.dylib .framework].freeze
sig { params(filename: Pathname).returns(T::Boolean) } sig { params(filename: Pathname).returns(T::Boolean) }
def valid_library_extension?(filename) def valid_library_extension?(filename)
macos_lib_extensions = %w[.dylib .framework] super || MACOS_LIB_EXTENSIONS.include?(filename.extname)
generic_valid_library_extension?(filename) || macos_lib_extensions.include?(filename.extname) end
end
end end
end end
FormulaCellarChecks.prepend(OS::Mac::FormulaCellarChecks)

View File

@ -1,15 +1,18 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module Hardware module OS
sig { params(version: T.nilable(Version)).returns(Symbol) } module Mac
def self.oldest_cpu(version = nil) module Hardware
module ClassMethods
sig { params(version: T.nilable(MacOSVersion)).returns(Symbol) }
def oldest_cpu(version = nil)
version = if version version = if version
MacOSVersion.new(version.to_s) MacOSVersion.new(version.to_s)
else else
MacOS.version MacOS.version
end end
if CPU.arch == :arm64 if ::Hardware::CPU.arch == :arm64
:arm_vortex_tempest :arm_vortex_tempest
# This cannot use a newer CPU e.g. haswell because Rosetta 2 does not # This cannot use a newer CPU e.g. haswell because Rosetta 2 does not
# support AVX instructions in bottles: # support AVX instructions in bottles:
@ -19,7 +22,12 @@ module Hardware
elsif version >= :mojave elsif version >= :mojave
:nehalem :nehalem
else else
generic_oldest_cpu super
end
end
end
end end
end end
end end
Hardware.singleton_class.prepend(OS::Mac::Hardware::ClassMethods)

View File

@ -95,7 +95,7 @@ module OS
end end
end end
generic_fix_dynamic_linkage super
end end
def loader_name_for(file, target) def loader_name_for(file, target)
@ -199,7 +199,7 @@ module OS
end end
def prepare_relocation_to_locations def prepare_relocation_to_locations
relocation = generic_prepare_relocation_to_locations relocation = super
brewed_perl = runtime_dependencies&.any? { |dep| dep["full_name"] == "perl" && dep["declared_directly"] } brewed_perl = runtime_dependencies&.any? { |dep| dep["full_name"] == "perl" && dep["declared_directly"] }
perl_path = if brewed_perl || name == "perl" perl_path = if brewed_perl || name == "perl"

View File

@ -5,9 +5,10 @@ require "cask/info"
require "cask/cask_loader" require "cask/cask_loader"
require "cask/caskroom" require "cask/caskroom"
module Homebrew module OS
module Mac
module MissingFormula module MissingFormula
class << self module ClassMethods
sig { params(name: String).returns(T.nilable(String)) } sig { params(name: String).returns(T.nilable(String)) }
def disallowed_reason(name) def disallowed_reason(name)
case name.downcase case name.downcase
@ -16,7 +17,7 @@ module Homebrew
Xcode can be installed from the App Store. Xcode can be installed from the App Store.
EOS EOS
else else
generic_disallowed_reason(name) super
end end
end end
@ -36,24 +37,27 @@ module Homebrew
EOS EOS
case command case command
when "install" when "install"
Cask::CaskLoader.load(name) ::Cask::CaskLoader.load(name)
when "uninstall" when "uninstall"
cask = Cask::Caskroom.casks.find { |installed_cask| installed_cask.to_s == name } cask = ::Cask::Caskroom.casks.find { |installed_cask| installed_cask.to_s == name }
raise Cask::CaskUnavailableError, name if cask.nil? Kernel.raise ::Cask::CaskUnavailableError, name if cask.nil?
when "info" when "info"
cask = Cask::CaskLoader.load(name) cask = ::Cask::CaskLoader.load(name)
suggestion = <<~EOS suggestion = <<~EOS
Found a cask named "#{name}" instead. Found a cask named "#{name}" instead.
#{Cask::Info.get_info(cask)} #{::Cask::Info.get_info(cask)}
EOS EOS
else else
return return
end end
suggestion suggestion
rescue Cask::CaskUnavailableError rescue ::Cask::CaskUnavailableError
nil nil
end end
end end
end end
end
end end
Homebrew::MissingFormula.singleton_class.prepend(OS::Mac::MissingFormula::ClassMethods)

View File

@ -6,6 +6,11 @@ require "system_command"
module OS module OS
module Mac module Mac
module SystemConfig module SystemConfig
module ClassMethods
extend T::Helpers
requires_ancestor { T.class_of(::SystemConfig) }
sig { returns(String) } sig { returns(String) }
def describe_clang def describe_clang
return "N/A" if ::SystemConfig.clang.null? return "N/A" if ::SystemConfig.clang.null?
@ -13,15 +18,6 @@ module OS
clang_build_info = ::SystemConfig.clang_build.null? ? "(parse error)" : ::SystemConfig.clang_build clang_build_info = ::SystemConfig.clang_build.null? ? "(parse error)" : ::SystemConfig.clang_build
"#{::SystemConfig.clang} build #{clang_build_info}" "#{::SystemConfig.clang} build #{clang_build_info}"
end end
end
end
end
SystemConfig.prepend(OS::Mac::SystemConfig)
module SystemConfig
class << self
include SystemCommand::Mixin
def xcode def xcode
@xcode ||= if MacOS::Xcode.installed? @xcode ||= if MacOS::Xcode.installed?
@ -41,11 +37,14 @@ module SystemConfig
end end
def dump_verbose_config(out = $stdout) def dump_verbose_config(out = $stdout)
dump_generic_verbose_config(out) super
out.puts "macOS: #{MacOS.full_version}-#{kernel}" out.puts "macOS: #{MacOS.full_version}-#{kernel}"
out.puts "CLT: #{clt || "N/A"}" out.puts "CLT: #{clt || "N/A"}"
out.puts "Xcode: #{xcode || "N/A"}" out.puts "Xcode: #{xcode || "N/A"}"
out.puts "Rosetta 2: #{Hardware::CPU.in_rosetta2?}" if Hardware::CPU.physical_cpu_arm64? out.puts "Rosetta 2: #{::Hardware::CPU.in_rosetta2?}" if ::Hardware::CPU.physical_cpu_arm64?
end
end
end end
end end
end end
SystemConfig.singleton_class.prepend(OS::Mac::SystemConfig::ClassMethods)

View File

@ -1,37 +1,40 @@
# typed: strict # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module Utils module OS
module Mac
module Bottles module Bottles
class << self module ClassMethods
module MacOSOverride sig { params(tag: T.nilable(T.any(Symbol, Utils::Bottles::Tag))).returns(Utils::Bottles::Tag) }
sig { params(tag: T.nilable(T.any(Symbol, Tag))).returns(Tag) }
def tag(tag = nil) def tag(tag = nil)
return Tag.new(system: MacOS.version.to_sym, arch: Hardware::CPU.arch) if tag.nil? if tag.nil?
Utils::Bottles::Tag.new(system: MacOS.version.to_sym, arch: ::Hardware::CPU.arch)
else
super super
end end
end end
prepend MacOSOverride
end end
class Collector module Collector
extend T::Helpers
requires_ancestor { Utils::Bottles::Collector }
private private
alias generic_find_matching_tag find_matching_tag sig {
params(tag: Utils::Bottles::Tag,
sig { params(tag: Utils::Bottles::Tag, no_older_versions: T::Boolean).returns(T.nilable(Utils::Bottles::Tag)) } no_older_versions: T::Boolean).returns(T.nilable(Utils::Bottles::Tag))
}
def find_matching_tag(tag, no_older_versions: false) def find_matching_tag(tag, no_older_versions: false)
# Used primarily by developers testing beta macOS releases. # Used primarily by developers testing beta macOS releases.
if no_older_versions || if no_older_versions ||
(OS::Mac.version.prerelease? && (OS::Mac.version.prerelease? &&
Homebrew::EnvConfig.developer? && Homebrew::EnvConfig.developer? &&
Homebrew::EnvConfig.skip_or_later_bottles?) Homebrew::EnvConfig.skip_or_later_bottles?)
generic_find_matching_tag(tag) super(tag)
else else
generic_find_matching_tag(tag) || super(tag) || find_older_compatible_tag(tag)
find_older_compatible_tag(tag)
end end
end end
@ -56,4 +59,8 @@ module Utils
end end
end end
end end
end
end end
Utils::Bottles.singleton_class.prepend(OS::Mac::Bottles::ClassMethods)
Utils::Bottles::Collector.prepend(OS::Mac::Bottles::Collector)

View File

@ -84,7 +84,6 @@ module FormulaCellarChecks
def valid_library_extension?(filename) def valid_library_extension?(filename)
VALID_LIBRARY_EXTENSIONS.include? filename.extname VALID_LIBRARY_EXTENSIONS.include? filename.extname
end end
alias generic_valid_library_extension? valid_library_extension?
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def check_non_libraries def check_non_libraries
@ -437,7 +436,6 @@ module FormulaCellarChecks
problem_if_output(check_cpuid_instruction(formula)) problem_if_output(check_cpuid_instruction(formula))
problem_if_output(check_binary_arches(formula)) problem_if_output(check_binary_arches(formula))
end end
alias generic_audit_installed audit_installed
private private

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "digest" require "digest"
@ -7,62 +7,81 @@ require "erb"
module Homebrew module Homebrew
# Class for generating a formula from a template. # Class for generating a formula from a template.
class FormulaCreator class FormulaCreator
sig { returns(String) }
attr_accessor :name attr_accessor :name
sig { returns(Version) }
attr_reader :version
sig { returns(T::Boolean) }
attr_reader :head
sig { sig {
params(name: T.nilable(String), version: T.nilable(String), tap: T.nilable(String), url: String, params(url: String, name: T.nilable(String), version: T.nilable(String), tap: T.nilable(String),
mode: T.nilable(Symbol), license: T.nilable(String), fetch: T::Boolean, head: T::Boolean).void mode: T.nilable(Symbol), license: T.nilable(String), fetch: T::Boolean, head: T::Boolean).void
} }
def initialize(name, version, tap:, url:, mode:, license:, fetch:, head:) def initialize(url:, name: nil, version: nil, tap: nil, mode: nil, license: nil, fetch: false, head: false)
@name = name
@version = Version.new(version) if version
@tap = Tap.fetch(tap || "homebrew/core")
@url = url @url = url
@mode = mode
@license = license
@fetch = fetch
@head = head
end
sig { void } if name.blank?
def verify
raise TapUnavailableError, @tap.name unless @tap.installed?
end
sig { params(url: String).returns(T.nilable(String)) }
def self.name_from_url(url)
stem = Pathname.new(url).stem stem = Pathname.new(url).stem
name = if stem.start_with?("index.cgi") && stem.include?("=")
# special cases first # special cases first
if stem.start_with? "index.cgi"
# gitweb URLs e.g. http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary # gitweb URLs e.g. http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary
stem.rpartition("=").last stem.rpartition("=").last
elsif url =~ %r{github\.com/\S+/(\S+)/(archive|releases)/} elsif url =~ %r{github\.com/\S+/(\S+)/(archive|releases)/}
# e.g. https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz # e.g. https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz
Regexp.last_match(1) T.must(Regexp.last_match(1))
else else
# e.g. http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz # e.g. http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz
pathver = Version.parse(stem).to_s pathver = Version.parse(stem).to_s
stem.sub(/[-_.]?#{Regexp.escape(pathver)}$/, "") stem.sub(/[-_.]?#{Regexp.escape(pathver)}$/, "")
end end
odebug "name from url: #{name}"
end
@name = T.let(name, String)
version = if version.present?
Version.new(version)
else
Version.detect(url)
end
@version = T.let(version, Version)
tap = if tap.blank?
CoreTap.instance
else
Tap.fetch(tap)
end
@tap = T.let(tap, Tap)
@mode = T.let(mode.presence, T.nilable(Symbol))
@license = T.let(license.presence, T.nilable(String))
@fetch = fetch
case url
when %r{github\.com/(\S+)/(\S+)\.git}
head = true
user = Regexp.last_match(1)
repository = Regexp.last_match(2)
github = GitHub.repository(user, repository) if fetch
when %r{github\.com/(\S+)/(\S+)/(archive|releases)/}
user = Regexp.last_match(1)
repository = Regexp.last_match(2)
github = GitHub.repository(user, repository) if fetch
end
@head = head
@github = T.let(github, T.untyped)
@sha256 = T.let(nil, T.nilable(String))
@desc = T.let(nil, T.nilable(String))
@homepage = T.let(nil, T.nilable(String))
@license = T.let(nil, T.nilable(String))
end end
sig { void } sig { void }
def parse_url def verify_tap_available!
@name = FormulaCreator.name_from_url(@url) if @name.blank? raise TapUnavailableError, @tap.name unless @tap.installed?
odebug "name_from_url: #{@name}"
@version = Version.detect(@url) if @version.nil?
case @url
when %r{github\.com/(\S+)/(\S+)\.git}
@head = true
user = Regexp.last_match(1)
repo = Regexp.last_match(2)
@github = GitHub.repository(user, repo) if @fetch
when %r{github\.com/(\S+)/(\S+)/(archive|releases)/}
user = Regexp.last_match(1)
repo = Regexp.last_match(2)
@github = GitHub.repository(user, repo) if @fetch
end
end end
sig { returns(Pathname) } sig { returns(Pathname) }
@ -91,7 +110,7 @@ module Homebrew
raise "Downloaded URL is not archive" raise "Downloaded URL is not archive"
end end
@sha256 = filepath.sha256 @sha256 = T.let(filepath.sha256, T.nilable(String))
end end
if @github if @github
@ -106,6 +125,8 @@ module Homebrew
path path
end end
private
sig { params(name: String).returns(String) } sig { params(name: String).returns(String) }
def latest_versioned_formula(name) def latest_versioned_formula(name)
name_prefix = "#{name}@" name_prefix = "#{name}@"
@ -116,8 +137,6 @@ module Homebrew
sig { returns(String) } sig { returns(String) }
def template def template
# FIXME: https://github.com/errata-ai/vale/issues/818
# <!-- vale off -->
<<~ERB <<~ERB
# Documentation: https://docs.brew.sh/Formula-Cookbook # Documentation: https://docs.brew.sh/Formula-Cookbook
# https://rubydoc.brew.sh/Formula # https://rubydoc.brew.sh/Formula
@ -261,7 +280,6 @@ module Homebrew
end end
end end
ERB ERB
# <!-- vale on -->
end end
end end
end end

View File

@ -51,6 +51,7 @@ HOMEBREW_HOME_PLACEHOLDER = "/$HOME"
HOMEBREW_CASK_APPDIR_PLACEHOLDER = "$APPDIR" HOMEBREW_CASK_APPDIR_PLACEHOLDER = "$APPDIR"
HOMEBREW_MACOS_NEWEST_UNSUPPORTED = ENV.fetch("HOMEBREW_MACOS_NEWEST_UNSUPPORTED").freeze HOMEBREW_MACOS_NEWEST_UNSUPPORTED = ENV.fetch("HOMEBREW_MACOS_NEWEST_UNSUPPORTED").freeze
HOMEBREW_MACOS_NEWEST_SUPPORTED = ENV.fetch("HOMEBREW_MACOS_NEWEST_SUPPORTED").freeze
HOMEBREW_MACOS_OLDEST_SUPPORTED = ENV.fetch("HOMEBREW_MACOS_OLDEST_SUPPORTED").freeze HOMEBREW_MACOS_OLDEST_SUPPORTED = ENV.fetch("HOMEBREW_MACOS_OLDEST_SUPPORTED").freeze
HOMEBREW_MACOS_OLDEST_ALLOWED = ENV.fetch("HOMEBREW_MACOS_OLDEST_ALLOWED").freeze HOMEBREW_MACOS_OLDEST_ALLOWED = ENV.fetch("HOMEBREW_MACOS_OLDEST_ALLOWED").freeze

View File

@ -42,7 +42,6 @@ module Hardware
ppc64le: "-mcpu=powerpc64le", ppc64le: "-mcpu=powerpc64le",
}.freeze, T.nilable(T::Hash[Symbol, String])) }.freeze, T.nilable(T::Hash[Symbol, String]))
end end
alias generic_optimization_flags optimization_flags
sig { returns(Symbol) } sig { returns(Symbol) }
def arch_32_bit def arch_32_bit
@ -219,6 +218,7 @@ module Hardware
end end
end end
sig { params(_version: T.nilable(MacOSVersion)).returns(Symbol) }
def oldest_cpu(_version = nil) def oldest_cpu(_version = nil)
if Hardware::CPU.intel? if Hardware::CPU.intel?
if Hardware::CPU.is_64_bit? if Hardware::CPU.is_64_bit?
@ -242,7 +242,6 @@ module Hardware
Hardware::CPU.family Hardware::CPU.family
end end
end end
alias generic_oldest_cpu oldest_cpu
# Returns a Rust flag to set the target CPU if necessary. # Returns a Rust flag to set the target CPU if necessary.
# Defaults to nil. # Defaults to nil.

View File

@ -37,7 +37,6 @@ module Homebrew
end end
def global_post_install; end def global_post_install; end
alias generic_global_post_install global_post_install
def check_prefix def check_prefix
if (Hardware::CPU.intel? || Hardware::CPU.in_rosetta2?) && if (Hardware::CPU.intel? || Hardware::CPU.in_rosetta2?) &&
@ -399,7 +398,6 @@ module Homebrew
Diagnostic.checks(:supported_configuration_checks, fatal: all_fatal) Diagnostic.checks(:supported_configuration_checks, fatal: all_fatal)
Diagnostic.checks(:fatal_preinstall_checks) Diagnostic.checks(:fatal_preinstall_checks)
end end
alias generic_perform_preinstall_checks perform_preinstall_checks
def attempt_directory_creation def attempt_directory_creation
Keg.must_exist_directories.each do |dir| Keg.must_exist_directories.each do |dir|

View File

@ -77,7 +77,6 @@ class Keg
FileUtils.ln_s(new_src, file) FileUtils.ln_s(new_src, file)
end end
end end
alias generic_fix_dynamic_linkage fix_dynamic_linkage
def relocate_dynamic_linkage(_relocation) def relocate_dynamic_linkage(_relocation)
[] []
@ -102,7 +101,6 @@ class Keg
relocation relocation
end end
alias generic_prepare_relocation_to_placeholders prepare_relocation_to_placeholders
def replace_locations_with_placeholders def replace_locations_with_placeholders
relocation = prepare_relocation_to_placeholders.freeze relocation = prepare_relocation_to_placeholders.freeze
@ -123,7 +121,6 @@ class Keg
relocation relocation
end end
alias generic_prepare_relocation_to_locations prepare_relocation_to_locations
def replace_placeholders_with_locations(files, skip_linkage: false) def replace_placeholders_with_locations(files, skip_linkage: false)
relocation = prepare_relocation_to_locations.freeze relocation = prepare_relocation_to_locations.freeze
@ -221,7 +218,6 @@ class Keg
[grep_bin, grep_args] [grep_bin, grep_args]
end end
alias generic_egrep_args egrep_args
def each_unique_file_matching(string) def each_unique_file_matching(string)
Utils.popen_read("fgrep", recursive_fgrep_args, string, to_s) do |io| Utils.popen_read("fgrep", recursive_fgrep_args, string, to_s) do |io|

View File

@ -188,12 +188,10 @@ class LinkageChecker
store&.update!(keg_files_dylibs:) store&.update!(keg_files_dylibs:)
end end
alias generic_check_dylibs check_dylibs
def system_libraries_exist_in_cache? def system_libraries_exist_in_cache?
false false
end end
alias generic_system_libraries_exist_in_cache? system_libraries_exist_in_cache?
def dylib_found_in_shared_cache?(dylib) def dylib_found_in_shared_cache?(dylib)
@dyld_shared_cache_contains_path ||= begin @dyld_shared_cache_contains_path ||= begin

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strong
# frozen_string_literal: true # frozen_string_literal: true
require "version" require "version"
@ -10,6 +10,7 @@ class MacOSVersion < Version
sig { returns(T.nilable(T.any(String, Symbol))) } sig { returns(T.nilable(T.any(String, Symbol))) }
attr_reader :version attr_reader :version
sig { params(version: T.nilable(T.any(String, Symbol))).void }
def initialize(version) def initialize(version)
@version = version @version = version
super "unknown or unsupported macOS version: #{version.inspect}" super "unknown or unsupported macOS version: #{version.inspect}"
@ -18,7 +19,7 @@ class MacOSVersion < Version
# NOTE: When removing symbols here, ensure that they are added # NOTE: When removing symbols here, ensure that they are added
# to `DEPRECATED_MACOS_VERSIONS` in `MacOSRequirement`. # to `DEPRECATED_MACOS_VERSIONS` in `MacOSRequirement`.
SYMBOLS = { SYMBOLS = T.let({
tahoe: "26", tahoe: "26",
sequoia: "15", sequoia: "15",
sonoma: "14", sonoma: "14",
@ -30,7 +31,7 @@ class MacOSVersion < Version
high_sierra: "10.13", high_sierra: "10.13",
sierra: "10.12", sierra: "10.12",
el_capitan: "10.11", el_capitan: "10.11",
}.freeze }.freeze, T::Hash[Symbol, String])
sig { params(macos_version: MacOSVersion).returns(Version) } sig { params(macos_version: MacOSVersion).returns(Version) }
def self.kernel_major_version(macos_version) def self.kernel_major_version(macos_version)
@ -57,7 +58,9 @@ class MacOSVersion < Version
super(T.must(version)) super(T.must(version))
@comparison_cache = {} @comparison_cache = T.let({}, T::Hash[T.untyped, T.nilable(Integer)])
@pretty_name = T.let(nil, T.nilable(String))
@sym = T.let(nil, T.nilable(Symbol))
end end
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) } sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
@ -95,7 +98,7 @@ class MacOSVersion < Version
sig { returns(Symbol) } sig { returns(Symbol) }
def to_sym def to_sym
return @sym if defined?(@sym) return @sym if @sym
sym = SYMBOLS.invert.fetch(strip_patch.to_s, :dunno) sym = SYMBOLS.invert.fetch(strip_patch.to_s, :dunno)
@ -106,7 +109,7 @@ class MacOSVersion < Version
sig { returns(String) } sig { returns(String) }
def pretty_name def pretty_name
return @pretty_name if defined?(@pretty_name) return @pretty_name if @pretty_name
pretty_name = to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze pretty_name = to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze
@ -154,5 +157,7 @@ class MacOSVersion < Version
# Represents the absence of a version. # Represents the absence of a version.
# #
# NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`. # NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`.
NULL = MacOSVersion.new("10.0").tap { |v| v.instance_variable_set(:@version, nil) }.freeze NULL = T.let(MacOSVersion.new("10.0").tap do |v|
T.let(v, MacOSVersion).instance_variable_set(:@version, nil)
end.freeze, MacOSVersion)
end end

View File

@ -93,7 +93,6 @@ module Homebrew
EOS EOS
end end
end end
alias generic_disallowed_reason disallowed_reason
sig { params(name: String).returns(T.nilable(String)) } sig { params(name: String).returns(T.nilable(String)) }
def tap_migration_reason(name) def tap_migration_reason(name)
@ -195,8 +194,10 @@ module Homebrew
end end
end end
sig { params(name: String, silent: T::Boolean, show_info: T::Boolean).returns(T.nilable(String)) }
def cask_reason(name, silent: false, show_info: false); end def cask_reason(name, silent: false, show_info: false); end
sig { params(name: String, command: String).returns(T.nilable(String)) }
def suggest_command(name, command); end def suggest_command(name, command); end
require "extend/os/missing_formula" require "extend/os/missing_formula"

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "requirement" require "requirement"
@ -7,10 +7,14 @@ require "requirement"
class ArchRequirement < Requirement class ArchRequirement < Requirement
fatal true fatal true
@arch = T.let(nil, T.nilable(Symbol))
sig { returns(T.nilable(Symbol)) }
attr_reader :arch attr_reader :arch
sig { params(tags: T::Array[Symbol]).void }
def initialize(tags) def initialize(tags)
@arch = tags.shift @arch = T.let(tags.shift, T.nilable(Symbol))
super super
end end

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "requirement" require "requirement"
@ -7,6 +7,7 @@ require "requirement"
class XcodeRequirement < Requirement class XcodeRequirement < Requirement
fatal true fatal true
sig { returns(T.nilable(String)) }
attr_reader :version attr_reader :version
satisfy(build_env: false) do satisfy(build_env: false) do
@ -14,8 +15,10 @@ class XcodeRequirement < Requirement
xcode_installed_version xcode_installed_version
end end
sig { params(tags: T::Array[String]).void }
def initialize(tags = []) def initialize(tags = [])
@version = tags.shift if tags.first.to_s.match?(/(\d\.)+\d/) version = tags.shift if tags.first.to_s.match?(/(\d\.)+\d/)
@version = T.let(version, T.nilable(String))
super super
end end
@ -53,6 +56,7 @@ class XcodeRequirement < Requirement
"#<#{self.class.name}: version>=#{@version.inspect} #{tags.inspect}>" "#<#{self.class.name}: version>=#{@version.inspect} #{tags.inspect}>"
end end
sig { returns(String) }
def display_s def display_s
return "#{name.capitalize} (on macOS)" unless @version return "#{name.capitalize} (on macOS)" unless @version

View File

@ -9,9 +9,6 @@ class Cask::Cask
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def app(*args, &block); end def app(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def appcast(*args, &block); end
sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) } sig { params(args: T.untyped, block: T.untyped).returns(T.untyped) }
def appdir(*args, &block); end def appdir(*args, &block); end

View File

@ -0,0 +1,58 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Cask::Config`.
# Please instead update this file by running `bin/tapioca dsl Cask::Config`.
module Cask
class Config
sig { returns(String) }
def appdir; end
sig { returns(String) }
def audio_unit_plugindir; end
sig { returns(String) }
def colorpickerdir; end
sig { returns(String) }
def dictionarydir; end
sig { returns(String) }
def fontdir; end
sig { returns(String) }
def input_methoddir; end
sig { returns(String) }
def internet_plugindir; end
sig { returns(String) }
def keyboard_layoutdir; end
sig { returns(T::Array[String]) }
def languages; end
sig { returns(String) }
def mdimporterdir; end
sig { returns(String) }
def prefpanedir; end
sig { returns(String) }
def qlplugindir; end
sig { returns(String) }
def screen_saverdir; end
sig { returns(String) }
def servicedir; end
sig { returns(String) }
def vst3_plugindir; end
sig { returns(String) }
def vst_plugindir; end
end
end

View File

@ -23,9 +23,6 @@ class Homebrew::Cmd::TapCmd::Args < Homebrew::CLI::Args
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def force?; end def force?; end
sig { returns(T::Boolean) }
def force_auto_update?; end
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def repair?; end def repair?; end
end end

View File

@ -0,0 +1,37 @@
# typed: strict
# frozen_string_literal: true
require_relative "../../../../global"
require "cask/config"
module Tapioca
module Compilers
class CaskConfig < Tapioca::Dsl::Compiler
ConstantType = type_member { { fixed: Module } }
sig { override.returns(T::Enumerable[Module]) }
def self.gather_constants = [Cask::Config]
sig { override.void }
def decorate
root.create_module("Cask") do |mod|
mod.create_class("Config") do |klass|
Cask::Config.defaults.each do |key, value|
return_type = if key == :languages
# :languages is a `LazyObject`, so it lazily evaluates to an
# array of strings when a method is called on it.
"T::Array[String]"
elsif key.end_with?("?")
"T::Boolean"
else
value.class.to_s
end
klass.create_method(key.to_s, return_type:, class_method: false)
end
end
end
end
end
end
end

View File

@ -128,10 +128,12 @@ module SystemConfig
out.puts "#{tap_name} origin: #{tap.remote}" if tap.remote != tap.default_remote out.puts "#{tap_name} origin: #{tap.remote}" if tap.remote != tap.default_remote
out.puts "#{tap_name} HEAD: #{tap.git_head || "(none)"}" out.puts "#{tap_name} HEAD: #{tap.git_head || "(none)"}"
out.puts "#{tap_name} last commit: #{tap.git_last_commit || "never"}" out.puts "#{tap_name} last commit: #{tap.git_last_commit || "never"}"
out.puts "#{tap_name} branch: #{tap.git_branch || "(none)"}" if tap.git_branch != "master" default_branches = %w[main master].freeze
out.puts "#{tap_name} branch: #{tap.git_branch || "(none)"}" if default_branches.exclude?(tap.git_branch)
end end
if (json_file = Homebrew::API::HOMEBREW_CACHE_API/json_file_name) && json_file.exist? json_file = Homebrew::API::HOMEBREW_CACHE_API/json_file_name
if json_file.exist?
out.puts "#{tap_name} JSON: #{json_file.mtime.utc.strftime("%d %b %H:%M UTC")}" out.puts "#{tap_name} JSON: #{json_file.mtime.utc.strftime("%d %b %H:%M UTC")}"
elsif !tap.installed? elsif !tap.installed?
out.puts "#{tap_name}: N/A" out.puts "#{tap_name}: N/A"
@ -194,7 +196,6 @@ module SystemConfig
out.puts hardware if hardware out.puts hardware if hardware
host_software_config(out) host_software_config(out)
end end
alias dump_generic_verbose_config dump_verbose_config
end end
end end

View File

@ -15,7 +15,7 @@ RSpec.describe Homebrew::Bundle::Dsl do
cask_args appdir: '/Applications' cask_args appdir: '/Applications'
tap 'homebrew/cask' tap 'homebrew/cask'
tap 'telemachus/brew', 'https://telemachus@bitbucket.org/telemachus/brew.git' tap 'telemachus/brew', 'https://telemachus@bitbucket.org/telemachus/brew.git'
tap 'auto/update', 'https://bitbucket.org/auto/update.git', force_auto_update: true tap 'auto/update', 'https://bitbucket.org/auto/update.git'
brew 'imagemagick' brew 'imagemagick'
brew 'mysql@5.6', restart_service: true, link: true, conflicts_with: ['mysql'] brew 'mysql@5.6', restart_service: true, link: true, conflicts_with: ['mysql']
brew 'emacs', args: ['with-cocoa', 'with-gnutls'], link: :overwrite brew 'emacs', args: ['with-cocoa', 'with-gnutls'], link: :overwrite
@ -40,10 +40,7 @@ RSpec.describe Homebrew::Bundle::Dsl do
expect(dsl.entries[0].name).to eql("homebrew/cask") expect(dsl.entries[0].name).to eql("homebrew/cask")
expect(dsl.entries[1].name).to eql("telemachus/brew") expect(dsl.entries[1].name).to eql("telemachus/brew")
expect(dsl.entries[1].options).to eql(clone_target: "https://telemachus@bitbucket.org/telemachus/brew.git") expect(dsl.entries[1].options).to eql(clone_target: "https://telemachus@bitbucket.org/telemachus/brew.git")
expect(dsl.entries[2].options).to eql( expect(dsl.entries[2].options).to eql(clone_target: "https://bitbucket.org/auto/update.git")
clone_target: "https://bitbucket.org/auto/update.git",
force_auto_update: true,
)
expect(dsl.entries[3].name).to eql("imagemagick") expect(dsl.entries[3].name).to eql("imagemagick")
expect(dsl.entries[4].name).to eql("mysql@5.6") expect(dsl.entries[4].name).to eql("mysql@5.6")
expect(dsl.entries[4].options).to eql(restart_service: true, link: true, conflicts_with: ["mysql"]) expect(dsl.entries[4].options).to eql(restart_service: true, link: true, conflicts_with: ["mysql"])

View File

@ -55,25 +55,5 @@ RSpec.describe Homebrew::Bundle::TapInstaller do
expect(described_class.install("homebrew/cask", clone_target: "clone_target_path")).to be(false) expect(described_class.install("homebrew/cask", clone_target: "clone_target_path")).to be(false)
end end
end end
context "with force_auto_update" do
it "taps" do
expect(Homebrew::Bundle).to receive(:system).with(HOMEBREW_BREW_FILE, "tap", "homebrew/cask",
"--force-auto-update",
verbose: false)
.and_return(true)
expect(described_class.preinstall("homebrew/cask", force_auto_update: true)).to be(true)
expect(described_class.install("homebrew/cask", force_auto_update: true)).to be(true)
end
it "fails" do
expect(Homebrew::Bundle).to receive(:system).with(HOMEBREW_BREW_FILE, "tap", "homebrew/cask",
"--force-auto-update",
verbose: false)
.and_return(false)
expect(described_class.preinstall("homebrew/cask", force_auto_update: true)).to be(true)
expect(described_class.install("homebrew/cask", force_auto_update: true)).to be(false)
end
end
end end
end end

View File

@ -19,7 +19,7 @@ RSpec.describe Cask::Artifact::Artifact, :cask do
end end
context "without target" do context "without target" do
it "fails to load" do it "fails to load", :no_api do
expect do expect do
Cask::CaskLoader.load("invalid-generic-artifact-no-target") Cask::CaskLoader.load("invalid-generic-artifact-no-target")
end.to raise_error(Cask::CaskInvalidError, /Generic Artifact.*requires.*target/) end.to raise_error(Cask::CaskInvalidError, /Generic Artifact.*requires.*target/)

View File

@ -6,7 +6,7 @@ RSpec.describe Cask::Artifact::Manpage, :cask do
context "without section" do context "without section" do
let(:cask_token) { "invalid-manpage-no-section" } let(:cask_token) { "invalid-manpage-no-section" }
it "fails to load a cask without section" do it "fails to load a cask without section", :no_api do
expect { cask }.to raise_error(Cask::CaskInvalidError, /is not a valid man page name/) expect { cask }.to raise_error(Cask::CaskInvalidError, /is not a valid man page name/)
end end
end end

Some files were not shown because too many files have changed in this diff Show More