Merge branch 'master' into master
This commit is contained in:
commit
dffa42839c
128
.github/dependabot.yml
vendored
128
.github/dependabot.yml
vendored
@ -1,90 +1,44 @@
|
||||
# This file is synced from the `.github` repository, do not modify it directly.
|
||||
---
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "friday"
|
||||
time: "08:00"
|
||||
timezone: "Etc/UTC"
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '08:00'
|
||||
timezone: Etc/UTC
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: docker
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '08:00'
|
||||
timezone: Etc/UTC
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
docker:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: devcontainers
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '08:00'
|
||||
timezone: Etc/UTC
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
devcontainers:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: bundler
|
||||
directories:
|
||||
- /
|
||||
- /Library/Homebrew
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "friday"
|
||||
time: "08:00"
|
||||
timezone: "Etc/UTC"
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
bundler:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "friday"
|
||||
time: "08:00"
|
||||
timezone: "Etc/UTC"
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
npm:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "friday"
|
||||
time: "08:00"
|
||||
timezone: "Etc/UTC"
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
docker:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: devcontainers
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "friday"
|
||||
time: "08:00"
|
||||
timezone: "Etc/UTC"
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
devcontainers:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: pip
|
||||
directories:
|
||||
- /
|
||||
- /Library/Homebrew/formula-analytics/
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: "friday"
|
||||
time: "08:00"
|
||||
timezone: "Etc/UTC"
|
||||
allow:
|
||||
- dependency-type: all
|
||||
groups:
|
||||
pip:
|
||||
patterns:
|
||||
- "*"
|
||||
|
2
.github/workflows/actionlint.yml
vendored
2
.github/workflows/actionlint.yml
vendored
@ -77,7 +77,7 @@ jobs:
|
||||
path: results.sarif
|
||||
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
|
||||
with:
|
||||
languages: ruby
|
||||
config: |
|
||||
@ -36,4 +36,4 @@ jobs:
|
||||
- Library/Homebrew/vendor
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
|
||||
|
107
.github/workflows/tests.yml
vendored
107
.github/workflows/tests.yml
vendored
@ -80,7 +80,7 @@ jobs:
|
||||
name: tap syntax
|
||||
needs: syntax
|
||||
if: github.repository_owner == 'Homebrew'
|
||||
runs-on: macos-14
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -141,7 +141,7 @@ jobs:
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
with:
|
||||
core: true
|
||||
core: false
|
||||
cask: false
|
||||
test-bot: false
|
||||
|
||||
@ -164,7 +164,7 @@ jobs:
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
with:
|
||||
core: true
|
||||
core: false
|
||||
cask: true
|
||||
test-bot: false
|
||||
|
||||
@ -213,10 +213,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: update-test (Ubuntu)
|
||||
- name: update-test (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
- name: update-test (macOS)
|
||||
runs-on: macos-15
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
@ -237,7 +237,6 @@ jobs:
|
||||
name: ${{ matrix.name }}
|
||||
needs: syntax
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
container: ${{ matrix.container }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@ -247,17 +246,10 @@ jobs:
|
||||
- name: tests (generic OS)
|
||||
test-flags: --generic --coverage
|
||||
runs-on: ubuntu-latest
|
||||
- name: tests (Ubuntu 24.04)
|
||||
- name: tests (Linux)
|
||||
test-flags: --coverage
|
||||
runs-on: ubuntu-24.04
|
||||
- name: tests (Ubuntu 22.04)
|
||||
test-flags: --coverage
|
||||
runs-on: ubuntu-22.04
|
||||
- name: tests (Ubuntu 20.04)
|
||||
test-flags: --coverage
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/homebrew/ubuntu20.04:latest
|
||||
- name: tests (macOS 15 arm64)
|
||||
- name: tests (macOS)
|
||||
test-flags: --coverage
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
@ -349,7 +341,7 @@ jobs:
|
||||
disable_search: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
test-default-formula:
|
||||
test-bot:
|
||||
name: ${{ matrix.name }}
|
||||
needs: syntax
|
||||
if: github.repository_owner == 'Homebrew' && github.event_name != 'push'
|
||||
@ -358,36 +350,76 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: test default formula (Ubuntu 24.04)
|
||||
runs-on: ubuntu-latest
|
||||
- name: test-bot (Linux arm64)
|
||||
runs-on: ubuntu-24.04-arm
|
||||
container: ghcr.io/homebrew/ubuntu24.04:latest
|
||||
- name: test default formula (Ubuntu 22.04)
|
||||
- name: test-bot (Linux x86_64)
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/homebrew/ubuntu22.04:master
|
||||
- name: test default formula (Ubuntu 20.04)
|
||||
# Use Debian Old Stable for testing Homebrew's glibc support.
|
||||
- name: test-bot (Linux Homebrew glibc)
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/homebrew/ubuntu20.04:latest
|
||||
- name: test default formula (macOS 13 x86_64)
|
||||
container: debian:oldstable
|
||||
- name: test-bot (macOS x86_64)
|
||||
runs-on: macos-13
|
||||
- name: test default formula (macOS 15 arm64)
|
||||
- name: test-bot (macOS arm64)
|
||||
runs-on: macos-15
|
||||
env:
|
||||
HOMEBREW_TEST_BOT_ANALYTICS: 1
|
||||
HOMEBREW_ENFORCE_SBOM: 1
|
||||
steps:
|
||||
- name: Install Homebrew and Homebrew's dependencies
|
||||
# All other images are built from our Homebrew Dockerfile.
|
||||
# This is the only one that needs to be set up manually.
|
||||
if: matrix.container == 'debian:oldstable'
|
||||
run: |
|
||||
# Slimmed down version from the Homebrew Dockerfile
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
bzip2 \
|
||||
ca-certificates \
|
||||
curl \
|
||||
file \
|
||||
g++ \
|
||||
git-core \
|
||||
less \
|
||||
locales \
|
||||
make \
|
||||
netbase \
|
||||
patch \
|
||||
procps \
|
||||
sudo \
|
||||
uuid-runtime \
|
||||
tzdata
|
||||
|
||||
# Install Homebrew
|
||||
mkdir -p /home/linuxbrew/.linuxbrew/bin
|
||||
# Don't do shallow clone or it's unshallowed by "Set up Homebrew"
|
||||
git clone https://github.com/Homebrew/brew.git /home/linuxbrew/.linuxbrew/Homebrew
|
||||
cd /home/linuxbrew/.linuxbrew/bin
|
||||
ln -s ../Homebrew/bin/brew brew
|
||||
echo "/home/linuxbrew/.linuxbrew/bin" >>"$GITHUB_PATH"
|
||||
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
with:
|
||||
core: true
|
||||
core: false
|
||||
cask: false
|
||||
test-bot: true
|
||||
|
||||
- run: brew test-bot --only-cleanup-before
|
||||
|
||||
- name: Setup environment variables
|
||||
if: matrix.container == 'ghcr.io/homebrew/ubuntu20.04:latest'
|
||||
run: echo "HOMEBREW_GLIBC_TESTING=1" >> "$GITHUB_ENV"
|
||||
run: |
|
||||
# Set enviroment variables to bypass `brew doctor` failures on Tier >=2 configurations
|
||||
if [[ "${MATRIX_NAME}" == "test-bot (Linux arm64)" ]]; then
|
||||
echo "HOMEBREW_ARM64_TESTING=1" >> "$GITHUB_ENV"
|
||||
elif [[ "${MATRIX_NAME}" == "test-bot (Linux Homebrew glibc)" ]]; then
|
||||
echo "HOMEBREW_GLIBC_TESTING=1" >> "$GITHUB_ENV"
|
||||
fi
|
||||
env:
|
||||
MATRIX_NAME: ${{ matrix.name }}
|
||||
|
||||
- run: brew test-bot --only-setup
|
||||
|
||||
@ -395,7 +427,7 @@ jobs:
|
||||
|
||||
- run: brew test-bot --only-formulae --only-json-tab --test-default-formula
|
||||
|
||||
test-brew-bundle-services:
|
||||
bundle-and-services:
|
||||
name: ${{ matrix.name }}
|
||||
needs: syntax
|
||||
if: github.repository_owner == 'Homebrew' && github.event_name != 'push'
|
||||
@ -403,16 +435,16 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: test brew bundle and brew services (Ubuntu)
|
||||
- name: bundle and services (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
- name: test brew bundle and brew services (macOS)
|
||||
runs-on: macos-15
|
||||
- name: bundle and services (macOS)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
with:
|
||||
core: true
|
||||
core: false
|
||||
cask: false
|
||||
test-bot: false
|
||||
|
||||
@ -449,13 +481,16 @@ jobs:
|
||||
brew services cleanup
|
||||
brew bundle cleanup --force
|
||||
|
||||
test-analytics:
|
||||
runs-on: ${{ matrix.os }}
|
||||
analytics:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
include:
|
||||
- name: analytics (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
- name: analytics (macOS)
|
||||
runs-on: macos-latest
|
||||
needs: syntax
|
||||
if: github.repository_owner == 'Homebrew' && github.event_name != 'push'
|
||||
steps:
|
||||
|
@ -122,14 +122,14 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(json: Hash).returns(Hash) }
|
||||
def self.merge_variations(json)
|
||||
sig { params(json: Hash, bottle_tag: T.nilable(::Utils::Bottles::Tag)).returns(Hash) }
|
||||
def self.merge_variations(json, bottle_tag: nil)
|
||||
return json unless json.key?("variations")
|
||||
|
||||
bottle_tag = ::Utils::Bottles::Tag.new(system: Homebrew::SimulateSystem.current_os,
|
||||
arch: Homebrew::SimulateSystem.current_arch)
|
||||
bottle_tag ||= Homebrew::SimulateSystem.current_tag
|
||||
|
||||
if (variation = json.dig("variations", bottle_tag.to_s).presence)
|
||||
if (variation = json.dig("variations", bottle_tag.to_s).presence) ||
|
||||
(variation = json.dig("variations", bottle_tag.to_sym).presence)
|
||||
json = json.merge(variation)
|
||||
end
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/cachable"
|
||||
require "cachable"
|
||||
require "api/download"
|
||||
|
||||
module Homebrew
|
||||
@ -12,6 +12,13 @@ module Homebrew
|
||||
|
||||
DEFAULT_API_FILENAME = "cask.jws.json"
|
||||
|
||||
sig { returns(String) }
|
||||
def self.api_filename
|
||||
return DEFAULT_API_FILENAME unless ENV.fetch("HOMEBREW_USE_INTERNAL_API", false)
|
||||
|
||||
"cask.#{SimulateSystem.current_tag}.jws.json"
|
||||
end
|
||||
|
||||
private_class_method :cache
|
||||
|
||||
sig { params(token: String).returns(Hash) }
|
||||
@ -41,12 +48,12 @@ module Homebrew
|
||||
end
|
||||
|
||||
def self.cached_json_file_path
|
||||
HOMEBREW_CACHE_API/DEFAULT_API_FILENAME
|
||||
HOMEBREW_CACHE_API/api_filename
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def self.download_and_cache_data!
|
||||
json_casks, updated = Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME
|
||||
json_casks, updated = Homebrew::API.fetch_json_api_file api_filename
|
||||
|
||||
cache["renames"] = {}
|
||||
cache["casks"] = json_casks.to_h do |json_cask|
|
||||
|
@ -1,7 +1,7 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/cachable"
|
||||
require "cachable"
|
||||
require "api/download"
|
||||
|
||||
module Homebrew
|
||||
@ -12,6 +12,13 @@ module Homebrew
|
||||
|
||||
DEFAULT_API_FILENAME = "formula.jws.json"
|
||||
|
||||
sig { returns(String) }
|
||||
def self.api_filename
|
||||
return DEFAULT_API_FILENAME unless ENV.fetch("HOMEBREW_USE_INTERNAL_API", false)
|
||||
|
||||
"internal/formula.#{SimulateSystem.current_tag}.jws.json"
|
||||
end
|
||||
|
||||
private_class_method :cache
|
||||
|
||||
sig { params(name: String).returns(T::Hash[String, T.untyped]) }
|
||||
@ -42,12 +49,12 @@ module Homebrew
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def self.cached_json_file_path
|
||||
HOMEBREW_CACHE_API/DEFAULT_API_FILENAME
|
||||
HOMEBREW_CACHE_API/api_filename
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def self.download_and_cache_data!
|
||||
json_formulae, updated = Homebrew::API.fetch_json_api_file DEFAULT_API_FILENAME
|
||||
json_formulae, updated = Homebrew::API.fetch_json_api_file api_filename
|
||||
|
||||
cache["aliases"] = {}
|
||||
cache["renames"] = {}
|
||||
|
@ -600,6 +600,11 @@ case "$1" in
|
||||
homebrew-version
|
||||
exit 0
|
||||
;;
|
||||
mcp-server)
|
||||
source "${HOMEBREW_LIBRARY}/Homebrew/cmd/mcp-server.sh"
|
||||
homebrew-mcp-server "$@"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# TODO: bump version when new macOS is released or announced and update references in:
|
||||
@ -1078,6 +1083,22 @@ else
|
||||
export HOMEBREW_GITHUB_PACKAGES_AUTH="Bearer QQ=="
|
||||
fi
|
||||
|
||||
# Avoid picking up any random `sudo` in `PATH`.
|
||||
if [[ -x /usr/bin/sudo ]]
|
||||
then
|
||||
SUDO=/usr/bin/sudo
|
||||
else
|
||||
# Do this after ensuring we're using default Bash builtins.
|
||||
SUDO="$(command -v sudo 2>/dev/null)"
|
||||
fi
|
||||
|
||||
# Reset sudo timestamp to avoid running unauthorized sudo commands
|
||||
if [[ -n "${SUDO}" ]]
|
||||
then
|
||||
"${SUDO}" --reset-timestamp 2>/dev/null || true
|
||||
fi
|
||||
unset SUDO
|
||||
|
||||
if [[ -n "${HOMEBREW_BASH_COMMAND}" ]]
|
||||
then
|
||||
# source rather than executing directly to ensure the entire file is read into
|
||||
|
@ -130,6 +130,9 @@ module Homebrew
|
||||
@formula_versions_from_env[formula_env_name]
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def prepend_pkgconf_path_if_needed!; end
|
||||
|
||||
sig { void }
|
||||
def reset!
|
||||
@mas_installed = T.let(nil, T.nilable(T::Boolean))
|
||||
|
@ -64,14 +64,8 @@ module Homebrew
|
||||
ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims"
|
||||
end
|
||||
|
||||
# Setup pkg-config, if present, to help locate packages
|
||||
# Only need this on Linux as Homebrew provides a shim on macOS
|
||||
# TODO: use extend/OS here
|
||||
# rubocop:todo Homebrew/MoveToExtendOS
|
||||
if OS.linux? && (pkgconf = Formulary.factory("pkgconf")) && pkgconf.any_version_installed?
|
||||
ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
|
||||
end
|
||||
# rubocop:enable Homebrew/MoveToExtendOS
|
||||
# Setup pkgconf, if needed, to help locate packages
|
||||
Bundle.prepend_pkgconf_path_if_needed!
|
||||
|
||||
# For commands which aren't either absolute or relative
|
||||
# Add the command directory to PATH, since it may get blown away by superenv
|
||||
@ -170,6 +164,18 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
return
|
||||
elsif subcommand == "sh"
|
||||
preferred_path = Utils::Shell.preferred_path(default: "/bin/bash")
|
||||
notice = unless Homebrew::EnvConfig.no_env_hints?
|
||||
<<~EOS
|
||||
Your shell has been configured to use a build environment from your `Brewfile`.
|
||||
This should help you build stuff.
|
||||
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
|
||||
When done, type `exit`.
|
||||
EOS
|
||||
end
|
||||
ENV["HOMEBREW_FORCE_API_AUTO_UPDATE"] = nil
|
||||
args = [Utils::Shell.shell_with_prompt("brew bundle", preferred_path:, notice:)]
|
||||
end
|
||||
|
||||
if services
|
||||
|
@ -11,19 +11,6 @@ module Homebrew
|
||||
def skip?(entry, silent: false)
|
||||
require "bundle/brew_dumper"
|
||||
|
||||
# TODO: use extend/OS here
|
||||
# rubocop:todo Homebrew/MoveToExtendOS
|
||||
if (Hardware::CPU.arm? || OS.linux?) &&
|
||||
Homebrew.default_prefix? &&
|
||||
entry.type == :brew && entry.name.exclude?("/") &&
|
||||
(formula = BrewDumper.formulae_by_full_name(entry.name)) &&
|
||||
formula[:official_tap] &&
|
||||
!formula[:bottled]
|
||||
reason = Hardware::CPU.arm? ? "Apple Silicon" : "Linux"
|
||||
puts Formatter.warning "Skipping #{entry.name} (no bottle for #{reason})" unless silent
|
||||
return true
|
||||
end
|
||||
# rubocop:enable Homebrew/MoveToExtendOS
|
||||
return true if @failed_taps&.any? do |tap|
|
||||
prefix = "#{tap}/"
|
||||
entry.name.start_with?(prefix) || entry.options[:full_name]&.start_with?(prefix)
|
||||
|
@ -8,7 +8,7 @@ require "cask/dsl"
|
||||
require "cask/metadata"
|
||||
require "cask/tab"
|
||||
require "utils/bottles"
|
||||
require "extend/api_hashable"
|
||||
require "api_hashable"
|
||||
|
||||
module Cask
|
||||
# An instance of a cask.
|
||||
@ -416,16 +416,14 @@ module Cask
|
||||
|
||||
if @dsl.on_system_blocks_exist?
|
||||
begin
|
||||
OnSystem::ALL_OS_ARCH_COMBINATIONS.each do |os, arch|
|
||||
bottle_tag = ::Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless bottle_tag.valid_combination?
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
next if bottle_tag.linux? && @dsl.os.nil?
|
||||
next if bottle_tag.macos? &&
|
||||
depends_on.macos &&
|
||||
!@dsl.depends_on_set_in_block? &&
|
||||
!depends_on.macos.allows?(bottle_tag.to_macos_version)
|
||||
|
||||
Homebrew::SimulateSystem.with(os:, arch:) do
|
||||
Homebrew::SimulateSystem.with_tag(bottle_tag) do
|
||||
refresh
|
||||
|
||||
to_h.each do |key, value|
|
||||
|
@ -6,6 +6,7 @@ require "cask/cask"
|
||||
require "uri"
|
||||
require "utils/curl"
|
||||
require "extend/hash/keys"
|
||||
require "api"
|
||||
|
||||
module Cask
|
||||
# Loads a cask from various sources.
|
||||
@ -104,8 +105,8 @@ module Cask
|
||||
return
|
||||
end
|
||||
|
||||
return if %w[.rb .json].exclude?(path.extname)
|
||||
return unless path.expand_path.exist?
|
||||
return if invalid_path?(path)
|
||||
|
||||
return if Homebrew::EnvConfig.forbid_packages_from_paths? &&
|
||||
!path.realpath.to_s.start_with?("#{Caskroom.path}/", "#{HOMEBREW_LIBRARY}/Taps/")
|
||||
@ -113,6 +114,14 @@ module Cask
|
||||
new(path)
|
||||
end
|
||||
|
||||
sig { params(pathname: Pathname, valid_extnames: T::Array[String]).returns(T::Boolean) }
|
||||
def self.invalid_path?(pathname, valid_extnames: %w[.rb .json])
|
||||
return true if valid_extnames.exclude?(pathname.extname)
|
||||
|
||||
@invalid_basenames ||= %w[INSTALL_RECEIPT.json sbom.spdx.json].freeze
|
||||
@invalid_basenames.include?(pathname.basename.to_s)
|
||||
end
|
||||
|
||||
attr_reader :token, :path
|
||||
|
||||
sig { params(path: T.any(Pathname, String), token: String).void }
|
||||
@ -135,8 +144,10 @@ module Cask
|
||||
@content = path.read(encoding: "UTF-8")
|
||||
@config = config
|
||||
|
||||
if path.extname == ".json"
|
||||
return FromAPILoader.new(token, from_json: JSON.parse(@content), path:).load(config:)
|
||||
if !self.class.invalid_path?(path, valid_extnames: %w[.json]) &&
|
||||
(from_json = JSON.parse(@content).presence) &&
|
||||
from_json.is_a?(Hash)
|
||||
return FromAPILoader.new(token, from_json:, path:).load(config:)
|
||||
end
|
||||
|
||||
begin
|
||||
@ -284,7 +295,7 @@ module Cask
|
||||
sig { returns(Pathname) }
|
||||
attr_reader :path
|
||||
|
||||
sig { returns(T.nilable(Hash)) }
|
||||
sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.anything])) }
|
||||
attr_reader :from_json
|
||||
|
||||
sig {
|
||||
@ -306,7 +317,13 @@ module Cask
|
||||
new("#{tap}/#{token}")
|
||||
end
|
||||
|
||||
sig { params(token: String, from_json: Hash, path: T.nilable(Pathname)).void }
|
||||
sig {
|
||||
params(
|
||||
token: String,
|
||||
from_json: T.nilable(T::Hash[T.any(String, Symbol), T.anything]),
|
||||
path: T.nilable(Pathname),
|
||||
).void
|
||||
}
|
||||
def initialize(token, from_json: T.unsafe(nil), path: nil)
|
||||
@token = token.sub(%r{^homebrew/(?:homebrew-)?cask/}i, "")
|
||||
@sourcefile_path = path || Homebrew::API::Cask.cached_json_file_path
|
||||
@ -400,7 +417,7 @@ module Cask
|
||||
container(**container_hash)
|
||||
end
|
||||
|
||||
json_cask[:artifacts].each do |artifact|
|
||||
json_cask[:artifacts]&.each do |artifact|
|
||||
# convert generic string replacements into actual ones
|
||||
artifact = cask.loader.from_h_gsubs(artifact, appdir)
|
||||
key = artifact.keys.first
|
||||
|
@ -26,7 +26,7 @@ require "cask/dsl/version"
|
||||
require "cask/url"
|
||||
require "cask/utils"
|
||||
|
||||
require "extend/on_system"
|
||||
require "on_system"
|
||||
|
||||
module Cask
|
||||
# Class representing the domain-specific language used for casks.
|
||||
|
@ -2,7 +2,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/utils"
|
||||
require "extend/on_system"
|
||||
require "on_system"
|
||||
|
||||
module Cask
|
||||
class DSL
|
||||
|
@ -149,7 +149,7 @@ module Cask
|
||||
|
||||
oh1 "Installing Cask #{Formatter.identifier(@cask)}"
|
||||
# GitHub Actions globally disables Gatekeeper.
|
||||
opoo "macOS's Gatekeeper has been disabled for this Cask" if !quarantine? && !GitHub::Actions.env_set?
|
||||
opoo_outside_github_actions "macOS's Gatekeeper has been disabled for this Cask" unless quarantine?
|
||||
stage
|
||||
|
||||
@cask.config = @cask.default_config.merge(old_config)
|
||||
|
@ -14,40 +14,54 @@ class Caveats
|
||||
sig { params(formula: Formula).void }
|
||||
def initialize(formula)
|
||||
@formula = formula
|
||||
@caveats = T.let(nil, T.nilable(String))
|
||||
@completions_and_elisp = T.let(nil, T.nilable(T::Array[String]))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def caveats
|
||||
caveats = []
|
||||
build = formula.build
|
||||
begin
|
||||
formula.build = Tab.for_formula(formula)
|
||||
string = formula.caveats.to_s
|
||||
caveats << "#{string.chomp}\n" unless string.empty?
|
||||
ensure
|
||||
formula.build = build
|
||||
@caveats ||= begin
|
||||
caveats = []
|
||||
build = formula.build
|
||||
begin
|
||||
formula.build = Tab.for_formula(formula)
|
||||
string = formula.caveats.to_s
|
||||
caveats << "#{string.chomp}\n" unless string.empty?
|
||||
ensure
|
||||
formula.build = build
|
||||
end
|
||||
caveats << keg_only_text
|
||||
caveats << service_caveats
|
||||
caveats.compact.join("\n")
|
||||
end
|
||||
caveats << keg_only_text
|
||||
|
||||
valid_shells = [:bash, :zsh, :fish, :pwsh].freeze
|
||||
current_shell = Utils::Shell.preferred || Utils::Shell.parent
|
||||
shells = if current_shell.present? &&
|
||||
(shell_sym = current_shell.to_sym) &&
|
||||
valid_shells.include?(shell_sym)
|
||||
[shell_sym]
|
||||
else
|
||||
valid_shells
|
||||
end
|
||||
shells.each do |shell|
|
||||
caveats << function_completion_caveats(shell)
|
||||
end
|
||||
|
||||
caveats << service_caveats
|
||||
caveats << elisp_caveats
|
||||
caveats.compact.join("\n")
|
||||
end
|
||||
|
||||
delegate [:empty?, :to_s] => :caveats
|
||||
sig { returns(T::Boolean) }
|
||||
def empty?
|
||||
caveats.blank? && completions_and_elisp.blank?
|
||||
end
|
||||
|
||||
delegate [:to_s] => :caveats
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def completions_and_elisp
|
||||
@completions_and_elisp ||= begin
|
||||
valid_shells = [:bash, :zsh, :fish, :pwsh].freeze
|
||||
current_shell = Utils::Shell.preferred || Utils::Shell.parent
|
||||
shells = if current_shell.present? &&
|
||||
(shell_sym = current_shell.to_sym) &&
|
||||
valid_shells.include?(shell_sym)
|
||||
[shell_sym]
|
||||
else
|
||||
valid_shells
|
||||
end
|
||||
completions_and_elisp = shells.map do |shell|
|
||||
function_completion_caveats(shell)
|
||||
end
|
||||
completions_and_elisp << elisp_caveats
|
||||
completions_and_elisp.compact
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(skip_reason: T::Boolean).returns(T.nilable(String)) }
|
||||
def keg_only_text(skip_reason: false)
|
||||
|
@ -208,11 +208,15 @@ module Homebrew
|
||||
return if global_switch
|
||||
|
||||
description = option_description(description, *names, hidden:)
|
||||
process_option(*names, description, type: :switch, hidden:) unless disable
|
||||
|
||||
env, counterpart = env
|
||||
if env && @non_global_processed_options.any?
|
||||
affix = counterpart ? " and `#{counterpart}` is passed." : "."
|
||||
description += " Enabled by default if `$HOMEBREW_#{env.upcase}` is set#{affix}"
|
||||
end
|
||||
if replacement || disable
|
||||
description += " (#{disable ? "disabled" : "deprecated"}#{"; replaced by #{replacement}" if replacement})"
|
||||
end
|
||||
process_option(*names, description, type: :switch, hidden:) unless disable
|
||||
|
||||
@parser.public_send(method, *names, *wrap_option_desc(description)) do |value|
|
||||
# This odeprecated should stick around indefinitely.
|
||||
|
@ -73,11 +73,10 @@ module Homebrew
|
||||
description: "`install` prints output from commands as they are run. " \
|
||||
"`check` lists all missing dependencies."
|
||||
switch "--no-upgrade",
|
||||
env: :bundle_no_upgrade,
|
||||
description: "`install` does not run `brew upgrade` on outdated dependencies. " \
|
||||
"`check` does not check for outdated dependencies. " \
|
||||
"Note they may still be upgraded by `brew install` if needed. " \
|
||||
"This is enabled by default if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set."
|
||||
"Note they may still be upgraded by `brew install` if needed.",
|
||||
env: :bundle_no_upgrade
|
||||
switch "--upgrade",
|
||||
description: "`install` runs `brew upgrade` on outdated dependencies, " \
|
||||
"even if `$HOMEBREW_BUNDLE_NO_UPGRADE` is set."
|
||||
@ -87,18 +86,15 @@ module Homebrew
|
||||
switch "--install",
|
||||
description: "Run `install` before continuing to other operations e.g. `exec`."
|
||||
switch "--services",
|
||||
env: :bundle_services,
|
||||
description: "Temporarily start services while running the `exec` or `sh` command. " \
|
||||
"This is enabled by default if `$HOMEBREW_BUNDLE_SERVICES` is set."
|
||||
description: "Temporarily start services while running the `exec` or `sh` command.",
|
||||
env: :bundle_services
|
||||
switch "-f", "--force",
|
||||
description: "`install` runs with `--force`/`--overwrite`. " \
|
||||
"`dump` overwrites an existing `Brewfile`. " \
|
||||
"`cleanup` actually performs its cleanup operations."
|
||||
switch "--cleanup",
|
||||
env: :bundle_install_cleanup,
|
||||
description: "`install` performs cleanup operation, same as running `cleanup --force`. " \
|
||||
"This is enabled by default if `$HOMEBREW_BUNDLE_INSTALL_CLEANUP` is set and " \
|
||||
"`--global` is passed."
|
||||
description: "`install` performs cleanup operation, same as running `cleanup --force`.",
|
||||
env: [:bundle_install_cleanup, "--global"]
|
||||
switch "--all",
|
||||
description: "`list` all dependencies."
|
||||
switch "--formula", "--brews",
|
||||
@ -114,14 +110,12 @@ module Homebrew
|
||||
switch "--vscode",
|
||||
description: "`list`, `dump` or `cleanup` VSCode (and forks/variants) extensions."
|
||||
switch "--no-vscode",
|
||||
env: :bundle_dump_no_vscode,
|
||||
description: "`dump` without VSCode (and forks/variants) extensions. " \
|
||||
"This is enabled by default if `$HOMEBREW_BUNDLE_DUMP_NO_VSCODE` is set."
|
||||
description: "`dump` without VSCode (and forks/variants) extensions.",
|
||||
env: :bundle_dump_no_vscode
|
||||
switch "--describe",
|
||||
env: :bundle_dump_describe,
|
||||
description: "`dump` adds a description comment above each line, unless the " \
|
||||
"dependency does not have a description. " \
|
||||
"This is enabled by default if `$HOMEBREW_BUNDLE_DUMP_DESCRIBE` is set."
|
||||
"dependency does not have a description.",
|
||||
env: :bundle_dump_describe
|
||||
switch "--no-restart",
|
||||
description: "`dump` does not add `restart_service` to formula lines."
|
||||
switch "--zap",
|
||||
@ -282,17 +276,7 @@ module Homebrew
|
||||
_subcommand, *named_args = args.named
|
||||
named_args
|
||||
when "sh"
|
||||
preferred_path = Utils::Shell.preferred_path(default: "/bin/bash")
|
||||
notice = unless Homebrew::EnvConfig.no_env_hints?
|
||||
<<~EOS
|
||||
Your shell has been configured to use a build environment from your `Brewfile`.
|
||||
This should help you build stuff.
|
||||
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
|
||||
When done, type `exit`.
|
||||
EOS
|
||||
end
|
||||
ENV["HOMEBREW_FORCE_API_AUTO_UPDATE"] = nil
|
||||
[Utils::Shell.shell_with_prompt("brew bundle", preferred_path:, notice:)]
|
||||
["sh"]
|
||||
when "env"
|
||||
["env"]
|
||||
end
|
||||
|
@ -18,8 +18,7 @@ module Homebrew
|
||||
|
||||
If any version of each formula argument is installed and no other options
|
||||
are passed, this command displays their actual runtime dependencies (similar
|
||||
to `brew linkage`), which may differ from the current versions' stated
|
||||
dependencies if the installed versions are outdated.
|
||||
to `brew linkage`), which may differ from a formula's declared dependencies.
|
||||
|
||||
*Note:* `--missing` and `--skip-recommended` have precedence over `--include-*`.
|
||||
EOS
|
||||
@ -97,21 +96,45 @@ module Homebrew
|
||||
Formulary.enable_factory_cache!
|
||||
|
||||
SimulateSystem.with(os:, arch:) do
|
||||
recursive = !args.direct?
|
||||
installed = args.installed? || dependents(args.named.to_formulae_and_casks).all?(&:any_version_installed?)
|
||||
@use_runtime_dependencies = true
|
||||
|
||||
@use_runtime_dependencies = installed && recursive &&
|
||||
!args.tree? &&
|
||||
!args.graph? &&
|
||||
!args.HEAD? &&
|
||||
!args.include_implicit? &&
|
||||
!args.include_build? &&
|
||||
!args.include_test? &&
|
||||
!args.include_optional? &&
|
||||
!args.skip_recommended? &&
|
||||
!args.missing? &&
|
||||
args.os.nil? &&
|
||||
args.arch.nil?
|
||||
installed = args.installed? || dependents(args.named.to_formulae_and_casks).all?(&:any_version_installed?)
|
||||
unless installed
|
||||
not_using_runtime_dependencies_reason = if args.installed?
|
||||
"not all the named formulae were installed"
|
||||
else
|
||||
"`--installed` was not passed"
|
||||
end
|
||||
|
||||
@use_runtime_dependencies = false
|
||||
end
|
||||
|
||||
%w[direct tree graph HEAD skip_recommended missing
|
||||
include_implicit include_build include_test include_optional].each do |arg|
|
||||
next unless args.public_send("#{arg}?")
|
||||
|
||||
not_using_runtime_dependencies_reason = "--#{arg.tr("_", "-")} was passed"
|
||||
|
||||
@use_runtime_dependencies = false
|
||||
end
|
||||
|
||||
%w[os arch].each do |arg|
|
||||
next if args.public_send(arg).nil?
|
||||
|
||||
not_using_runtime_dependencies_reason = "--#{arg.tr("_", "-")} was passed"
|
||||
|
||||
@use_runtime_dependencies = false
|
||||
end
|
||||
|
||||
if !@use_runtime_dependencies && !Homebrew::EnvConfig.no_env_hints?
|
||||
opoo <<~EOS
|
||||
`brew deps` is not the actual runtime dependencies because #{not_using_runtime_dependencies_reason}!
|
||||
This means dependencies may differ from a formula's declared dependencies.
|
||||
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
|
||||
EOS
|
||||
end
|
||||
|
||||
recursive = !args.direct?
|
||||
|
||||
if args.tree? || args.graph?
|
||||
dependents = if args.named.present?
|
||||
|
@ -33,8 +33,8 @@ module Homebrew
|
||||
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
||||
"or a shell inside the temporary build directory."
|
||||
switch "--display-times",
|
||||
env: :display_install_times,
|
||||
description: "Print install times for each package at the end of the run."
|
||||
description: "Print install times for each package at the end of the run.",
|
||||
env: :display_install_times
|
||||
switch "-f", "--force",
|
||||
description: "Install formulae without checking for previously installed keg-only or " \
|
||||
"non-migrated versions. When installing casks, overwrite existing files " \
|
||||
@ -44,9 +44,9 @@ module Homebrew
|
||||
switch "-n", "--dry-run",
|
||||
description: "Show what would be installed, but do not actually install anything."
|
||||
switch "--ask",
|
||||
env: :ask,
|
||||
description: "Ask for confirmation before downloading and installing formulae. " \
|
||||
"Print bottles and dependencies download size and install size."
|
||||
"Print download and install sizes of bottles and dependencies.",
|
||||
env: :ask
|
||||
[
|
||||
[:switch, "--formula", "--formulae", {
|
||||
description: "Treat all named arguments as formulae.",
|
||||
|
23
Library/Homebrew/cmd/mcp-server.rb
Normal file
23
Library/Homebrew/cmd/mcp-server.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# typed: strong
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "shell_command"
|
||||
|
||||
module Homebrew
|
||||
module Cmd
|
||||
class McpServerCmd < AbstractCommand
|
||||
# This is a shell command as MCP servers need a faster startup time
|
||||
# than a normal Homebrew Ruby command allows.
|
||||
include ShellCommand
|
||||
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Starts the Homebrew MCP (Model Context Protocol) server.
|
||||
EOS
|
||||
switch "-d", "--debug", description: "Enable debug logging to stderr."
|
||||
switch "--ping", description: "Start the server, act as if receiving a ping and then exit.", hidden: true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
14
Library/Homebrew/cmd/mcp-server.sh
Normal file
14
Library/Homebrew/cmd/mcp-server.sh
Normal file
@ -0,0 +1,14 @@
|
||||
# Documentation defined in Library/Homebrew/cmd/mcp-server.rb
|
||||
|
||||
# This is a shell command as MCP servers need a faster startup time
|
||||
# than a normal Homebrew Ruby command allows.
|
||||
|
||||
# HOMEBREW_LIBRARY is set by brew.sh
|
||||
# HOMEBREW_BREW_FILE is set by extend/ENV/super.rb
|
||||
# shellcheck disable=SC2154
|
||||
homebrew-mcp-server() {
|
||||
source "${HOMEBREW_LIBRARY}/Homebrew/utils/ruby.sh"
|
||||
setup-ruby-path
|
||||
export HOMEBREW_VERSION
|
||||
"${HOMEBREW_RUBY_PATH}" "-r${HOMEBREW_LIBRARY}/Homebrew/mcp_server.rb" -e "Homebrew::McpServer.new.run" "$@"
|
||||
}
|
@ -32,12 +32,10 @@ module Homebrew
|
||||
"formula is outdated. Otherwise, the repository's HEAD will only be checked for " \
|
||||
"updates when a new stable or development version has been released."
|
||||
switch "-g", "--greedy",
|
||||
env: :upgrade_greedy,
|
||||
description: "Also include outdated casks with `auto_updates true` or `version :latest`."
|
||||
|
||||
description: "Also include outdated casks with `auto_updates true` or `version :latest`.",
|
||||
env: :upgrade_greedy
|
||||
switch "--greedy-latest",
|
||||
description: "Also include outdated casks including those with `version :latest`."
|
||||
|
||||
switch "--greedy-auto-updates",
|
||||
description: "Also include outdated casks including those with `auto_updates true`."
|
||||
|
||||
|
@ -32,8 +32,8 @@ module Homebrew
|
||||
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
||||
"or a shell inside the temporary build directory."
|
||||
switch "--display-times",
|
||||
env: :display_install_times,
|
||||
description: "Print install times for each package at the end of the run."
|
||||
description: "Print install times for each package at the end of the run.",
|
||||
env: :display_install_times
|
||||
switch "-f", "--force",
|
||||
description: "Install without checking for previously installed keg-only or " \
|
||||
"non-migrated versions."
|
||||
@ -41,7 +41,7 @@ module Homebrew
|
||||
description: "Print the verification and post-install steps."
|
||||
switch "--ask",
|
||||
description: "Ask for confirmation before downloading and upgrading formulae. " \
|
||||
"Print bottles and dependencies download size, install and net install size.",
|
||||
"Print download, install and net install sizes of bottles and dependencies.",
|
||||
env: :ask
|
||||
[
|
||||
[:switch, "--formula", "--formulae", { description: "Treat all named arguments as formulae." }],
|
||||
|
@ -13,18 +13,20 @@ module Homebrew
|
||||
description <<~EOS
|
||||
Valid shells: bash|csh|fish|pwsh|sh|tcsh|zsh
|
||||
|
||||
Print export statements. When run in a shell, this installation of Homebrew will be added to your `PATH`, `MANPATH`, and `INFOPATH`.
|
||||
Print export statements. When run in a shell, this installation of Homebrew will be added to your
|
||||
`$PATH`, `$MANPATH`, and `$INFOPATH`.
|
||||
|
||||
The variables `$HOMEBREW_PREFIX`, `$HOMEBREW_CELLAR` and `$HOMEBREW_REPOSITORY` are also exported to avoid
|
||||
querying them multiple times.
|
||||
To help guarantee idempotence, this command produces no output when Homebrew's `bin` and `sbin` directories
|
||||
are first and second respectively in your `PATH`. Consider adding evaluation of this command's output to
|
||||
are first and second respectively in your `$PATH`. Consider adding evaluation of this command's output to
|
||||
your dotfiles (e.g. `~/.bash_profile` or ~/.zprofile` on macOS and ~/.bashrc` or ~/.zshrc` on Linux) with:
|
||||
`eval "$(brew shellenv)"`
|
||||
|
||||
The shell can be specified explicitly with a supported shell name parameter. Unknown shells will output
|
||||
POSIX exports.
|
||||
EOS
|
||||
|
||||
named_args :shell
|
||||
end
|
||||
end
|
||||
|
@ -725,31 +725,53 @@ EOS
|
||||
local tmp_failure_file="${DIR}/.git/TMP_FETCH_FAILURES"
|
||||
rm -f "${tmp_failure_file}"
|
||||
|
||||
if [[ -n "${HOMEBREW_UPDATE_AUTO}" ]]
|
||||
if ! git fetch --tags --force "${QUIET_ARGS[@]}" origin \
|
||||
"refs/heads/${UPSTREAM_BRANCH_DIR}:refs/remotes/origin/${UPSTREAM_BRANCH_DIR}" 2>>"${tmp_failure_file}"
|
||||
then
|
||||
git fetch --tags --force "${QUIET_ARGS[@]}" origin \
|
||||
"refs/heads/${UPSTREAM_BRANCH_DIR}:refs/remotes/origin/${UPSTREAM_BRANCH_DIR}" 2>/dev/null
|
||||
else
|
||||
# Capture stderr to tmp_failure_file
|
||||
if ! git fetch --tags --force "${QUIET_ARGS[@]}" origin \
|
||||
"refs/heads/${UPSTREAM_BRANCH_DIR}:refs/remotes/origin/${UPSTREAM_BRANCH_DIR}" 2>>"${tmp_failure_file}"
|
||||
if [[ -f "${tmp_failure_file}" ]]
|
||||
then
|
||||
# Reprint fetch errors to stderr
|
||||
[[ -f "${tmp_failure_file}" ]] && cat "${tmp_failure_file}" 1>&2
|
||||
|
||||
if [[ "${UPSTREAM_SHA_HTTP_CODE}" == "404" ]]
|
||||
local git_errors
|
||||
git_errors="$(cat "${tmp_failure_file}")"
|
||||
if [[ "${git_errors}" == "fatal: couldn't find remote ref refs/heads/master" ]]
|
||||
then
|
||||
TAP="${DIR#"${HOMEBREW_LIBRARY}"/Taps/}"
|
||||
echo "${TAP} does not exist! Run \`brew untap ${TAP}\` to remove it." >>"${update_failed_file}"
|
||||
else
|
||||
echo "Fetching ${DIR} failed!" >>"${update_failed_file}"
|
||||
|
||||
if [[ -f "${tmp_failure_file}" ]] &&
|
||||
[[ "$(cat "${tmp_failure_file}")" == "fatal: couldn't find remote ref refs/heads/${UPSTREAM_BRANCH_DIR}" ]]
|
||||
# Attempt migration from master to main branch.
|
||||
if git fetch --tags --force "${QUIET_ARGS[@]}" origin \
|
||||
"refs/heads/main:refs/remotes/origin/main" 2>>"${tmp_failure_file}"
|
||||
then
|
||||
echo "${DIR}" >>"${missing_remote_ref_dirs_file}"
|
||||
rm -f "${DIR}/.git/refs/remotes/origin/HEAD" "${DIR}/.git/refs/remotes/origin/master"
|
||||
UPSTREAM_BRANCH_DIR="$(upstream_branch)"
|
||||
declare UPSTREAM_BRANCH"${TAP_VAR}"="${UPSTREAM_BRANCH_DIR}"
|
||||
git branch -m master main "${QUIET_ARGS[@]}"
|
||||
git branch -u origin/main main "${QUIET_ARGS[@]}"
|
||||
rm -f "${tmp_failure_file}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "${tmp_failure_file}"
|
||||
fi
|
||||
|
||||
# Don't output errors if HOMEBREW_UPDATE_AUTO is set.
|
||||
if [[ -n "${HOMEBREW_UPDATE_AUTO}" ]]
|
||||
then
|
||||
exit
|
||||
fi
|
||||
|
||||
# Reprint fetch errors to stderr
|
||||
[[ -n "${git_errors}" ]] && echo "${git_errors}" 1>&2
|
||||
|
||||
if [[ "${UPSTREAM_SHA_HTTP_CODE}" == "404" ]]
|
||||
then
|
||||
TAP="${DIR#"${HOMEBREW_LIBRARY}"/Taps/}"
|
||||
echo "${TAP} does not exist! Run \`brew untap ${TAP}\` to remove it." >>"${update_failed_file}"
|
||||
else
|
||||
echo "Fetching ${DIR} failed!" >>"${update_failed_file}"
|
||||
|
||||
if [[ -f "${tmp_failure_file}" ]] &&
|
||||
[[ "$(cat "${tmp_failure_file}")" == "fatal: couldn't find remote ref refs/heads/${UPSTREAM_BRANCH_DIR}" ]]
|
||||
then
|
||||
echo "${DIR}" >>"${missing_remote_ref_dirs_file}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -29,8 +29,8 @@ module Homebrew
|
||||
description: "If brewing fails, open an interactive debugging session with access to IRB " \
|
||||
"or a shell inside the temporary build directory."
|
||||
switch "--display-times",
|
||||
env: :display_install_times,
|
||||
description: "Print install times for each package at the end of the run."
|
||||
description: "Print install times for each package at the end of the run.",
|
||||
env: :display_install_times
|
||||
switch "-f", "--force",
|
||||
description: "Install formulae without checking for previously installed keg-only or " \
|
||||
"non-migrated versions. When installing casks, overwrite existing files " \
|
||||
@ -41,7 +41,7 @@ module Homebrew
|
||||
description: "Show what would be upgraded, but do not actually upgrade anything."
|
||||
switch "--ask",
|
||||
description: "Ask for confirmation before downloading and upgrading formulae. " \
|
||||
"Print bottles and dependencies download size, install and net install size.",
|
||||
"Print download, install and net install sizes of bottles and dependencies.",
|
||||
env: :ask
|
||||
[
|
||||
[:switch, "--formula", "--formulae", {
|
||||
|
@ -101,6 +101,7 @@ fetch() {
|
||||
local first_try=1
|
||||
local vendor_locations
|
||||
local temporary_path
|
||||
local curl_exit_code=0
|
||||
|
||||
curl_args=()
|
||||
|
||||
@ -149,19 +150,27 @@ fetch() {
|
||||
# HOMEBREW_CURL is set by brew.sh (and isn't misspelt here)
|
||||
# shellcheck disable=SC2153
|
||||
"${HOMEBREW_CURL}" "${curl_args[@]}" -C - "${url}" -o "${temporary_path}"
|
||||
if [[ $? -eq 33 ]]
|
||||
curl_exit_code="$?"
|
||||
if [[ "${curl_exit_code}" -eq 33 ]]
|
||||
then
|
||||
[[ -n "${HOMEBREW_QUIET}" ]] || echo "Trying a full download" >&2
|
||||
rm -f "${temporary_path}"
|
||||
"${HOMEBREW_CURL}" "${curl_args[@]}" "${url}" -o "${temporary_path}"
|
||||
curl_exit_code="$?"
|
||||
fi
|
||||
else
|
||||
"${HOMEBREW_CURL}" "${curl_args[@]}" "${url}" -o "${temporary_path}"
|
||||
curl_exit_code="$?"
|
||||
fi
|
||||
|
||||
[[ -f "${temporary_path}" ]] && break
|
||||
done
|
||||
|
||||
if [[ "${curl_exit_code}" -ne 0 ]]
|
||||
then
|
||||
rm -f "${temporary_path}"
|
||||
fi
|
||||
|
||||
if [[ ! -f "${temporary_path}" ]]
|
||||
then
|
||||
vendor_locations="$(printf " - %s\n" "${VENDOR_URLs[@]}")"
|
||||
|
@ -5,7 +5,7 @@ require "dependency"
|
||||
require "dependencies"
|
||||
require "requirement"
|
||||
require "requirements"
|
||||
require "extend/cachable"
|
||||
require "cachable"
|
||||
|
||||
# A dependency is a formula that another formula needs to install.
|
||||
# A requirement is something other than a formula that another formula
|
||||
|
@ -43,7 +43,7 @@ module Homebrew
|
||||
description: "Only check formulae and casks that are currently installed."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to audit them. " \
|
||||
"Implied if `HOMEBREW_EVAL_ALL` is set."
|
||||
"Implied if `$HOMEBREW_EVAL_ALL` is set."
|
||||
switch "--new",
|
||||
description: "Run various additional style checks to determine if a new formula or cask is eligible " \
|
||||
"for Homebrew. This should be used when creating new formulae or casks and implies " \
|
||||
|
@ -227,7 +227,7 @@ module Homebrew
|
||||
private
|
||||
|
||||
sig {
|
||||
params(string: String, keg: Keg, ignores: T::Array[String],
|
||||
params(string: String, keg: Keg, ignores: T::Array[Regexp],
|
||||
formula_and_runtime_deps_names: T.nilable(T::Array[String])).returns(T::Boolean)
|
||||
}
|
||||
def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil)
|
||||
@ -373,35 +373,17 @@ module Homebrew
|
||||
[gnu_tar(gnu_tar_formula), reproducible_gnutar_args(mtime)].freeze
|
||||
end
|
||||
|
||||
sig { params(formula: T.untyped).returns(T::Array[T.untyped]) }
|
||||
sig { params(formula: Formula).returns(T::Array[Regexp]) }
|
||||
def formula_ignores(formula)
|
||||
ignores = []
|
||||
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
|
||||
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
|
||||
|
||||
# Ignore matches to go keg, because all go binaries are statically linked.
|
||||
any_go_deps = formula.deps.any? do |dep|
|
||||
Version.formula_optionally_versioned_regex(:go).match?(dep.name)
|
||||
end
|
||||
if any_go_deps
|
||||
go_regex = Version.formula_optionally_versioned_regex(:go, full: false)
|
||||
ignores << %r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec}
|
||||
end
|
||||
return [] unless any_go_deps
|
||||
|
||||
# TODO: Refactor and move to extend/os
|
||||
# rubocop:disable Homebrew/MoveToExtendOS
|
||||
ignores << case formula.name
|
||||
# On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
|
||||
# https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
|
||||
when Version.formula_optionally_versioned_regex(:gcc)
|
||||
Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
|
||||
# binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
|
||||
when Version.formula_optionally_versioned_regex(:binutils)
|
||||
%r{#{cellar_regex}/binutils} if OS.linux?
|
||||
end
|
||||
# rubocop:enable Homebrew/MoveToExtendOS
|
||||
|
||||
ignores.compact
|
||||
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
|
||||
go_regex = Version.formula_optionally_versioned_regex(:go, full: false)
|
||||
Array(%r{#{cellar_regex}/#{go_regex}/[\d.]+/libexec})
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).void }
|
||||
|
@ -118,7 +118,7 @@ module Homebrew
|
||||
else
|
||||
raise UsageError,
|
||||
"`brew bump` without named arguments needs `--installed` or `--eval-all` passed or " \
|
||||
"`HOMEBREW_EVAL_ALL` set!"
|
||||
"`$HOMEBREW_EVAL_ALL` set!"
|
||||
end
|
||||
|
||||
if args.start_with
|
||||
|
@ -88,16 +88,21 @@ module Homebrew
|
||||
contributions <<
|
||||
"#{Utils.pluralize("time", grand_totals[username].values.sum, include_count: true)} (total)"
|
||||
|
||||
puts [
|
||||
contributions_string = [
|
||||
"#{username} contributed",
|
||||
*contributions.to_sentence,
|
||||
"#{time_period(from:, to: args.to)}.",
|
||||
].join(" ")
|
||||
if args.csv?
|
||||
$stderr.puts contributions_string
|
||||
else
|
||||
puts contributions_string
|
||||
end
|
||||
end
|
||||
|
||||
return unless args.csv?
|
||||
|
||||
puts
|
||||
$stderr.puts
|
||||
puts generate_csv(grand_totals)
|
||||
end
|
||||
|
||||
|
@ -21,7 +21,8 @@ module Homebrew
|
||||
"dependents.",
|
||||
env: :eval_all
|
||||
switch "--dependents",
|
||||
description: "Determine runners for testing dependents. Requires `--eval-all` or `HOMEBREW_EVAL_ALL`.",
|
||||
description: "Determine runners for testing dependents. " \
|
||||
"Requires `--eval-all` or `$HOMEBREW_EVAL_ALL` to be set.",
|
||||
depends_on: "--eval-all"
|
||||
|
||||
named_args max: 2
|
||||
|
@ -9,10 +9,9 @@ module Homebrew
|
||||
class Edit < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Open a <formula>, <cask> or <tap> in the editor set by `EDITOR` or `HOMEBREW_EDITOR`,
|
||||
Open a <formula>, <cask> or <tap> in the editor set by `$EDITOR` or `$HOMEBREW_EDITOR`,
|
||||
or open the Homebrew repository for editing if no argument is provided.
|
||||
EOS
|
||||
|
||||
switch "--formula", "--formulae",
|
||||
description: "Treat all named arguments as formulae."
|
||||
switch "--cask", "--casks",
|
||||
|
@ -77,7 +77,7 @@ module Homebrew
|
||||
analytics_data_dir = root_dir/"_data/analytics"
|
||||
analytics_api_dir = root_dir/"api/analytics"
|
||||
|
||||
threads = []
|
||||
analytics_output_queue = Queue.new
|
||||
|
||||
CATEGORIES.each do |category|
|
||||
formula_analytics_args = []
|
||||
@ -124,15 +124,40 @@ module Homebrew
|
||||
DAYS.each do |days|
|
||||
next if days != "30" && category_name == "build-error" && !data_source.nil?
|
||||
|
||||
threads << Thread.new do
|
||||
args = %W[--days-ago=#{days}]
|
||||
(analytics_data_path/"#{days}d.json").write run_formula_analytics(*formula_analytics_args, *args)
|
||||
(analytics_api_path/"#{days}d.json").write analytics_json_template(category_name, data_source:)
|
||||
end
|
||||
analytics_output_queue << {
|
||||
formula_analytics_args: formula_analytics_args.dup,
|
||||
days: days,
|
||||
analytics_data_path: analytics_data_path,
|
||||
analytics_api_path: analytics_api_path,
|
||||
category_name: category_name,
|
||||
data_source: data_source,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
threads.each(&:join)
|
||||
workers = []
|
||||
4.times do
|
||||
workers << Thread.new do
|
||||
until analytics_output_queue.empty?
|
||||
analytics_output_type = begin
|
||||
analytics_output_queue.pop(true)
|
||||
rescue ThreadError
|
||||
break
|
||||
end
|
||||
|
||||
days = analytics_output_type[:days]
|
||||
args = ["--days-ago=#{days}"]
|
||||
|
||||
(analytics_output_type[:analytics_data_path]/"#{days}d.json").write \
|
||||
run_formula_analytics(*analytics_output_type[:formula_analytics_args], *args)
|
||||
|
||||
data_source = analytics_output_type[:data_source]
|
||||
(analytics_output_type[:analytics_api_path]/"#{days}d.json").write \
|
||||
analytics_json_template(analytics_output_type[:category_name], data_source:)
|
||||
end
|
||||
end
|
||||
end
|
||||
workers.each(&:join)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -33,7 +33,7 @@ module Homebrew
|
||||
raise TapUnavailableError, tap.name unless tap.installed?
|
||||
|
||||
unless args.dry_run?
|
||||
directories = ["_data/cask", "api/cask", "api/cask-source", "cask", "api/internal/v3"].freeze
|
||||
directories = ["_data/cask", "api/cask", "api/cask-source", "cask", "api/internal"].freeze
|
||||
FileUtils.rm_rf directories
|
||||
FileUtils.mkdir_p directories
|
||||
end
|
||||
@ -44,12 +44,14 @@ module Homebrew
|
||||
|
||||
Cask::Cask.generating_hash!
|
||||
|
||||
all_casks = {}
|
||||
latest_macos = MacOSVersion.new((HOMEBREW_MACOS_NEWEST_UNSUPPORTED.to_i - 1).to_s).to_sym
|
||||
Homebrew::SimulateSystem.with(os: latest_macos, arch: :arm) do
|
||||
tap.cask_files.each do |path|
|
||||
cask = Cask::CaskLoader.load(path)
|
||||
name = cask.token
|
||||
json = JSON.pretty_generate(cask.to_hash_with_variations)
|
||||
all_casks[name] = cask.to_hash_with_variations
|
||||
json = JSON.pretty_generate(all_casks[name])
|
||||
cask_source = path.read
|
||||
html_template_name = html_template(name)
|
||||
|
||||
@ -67,6 +69,14 @@ module Homebrew
|
||||
|
||||
canonical_json = JSON.pretty_generate(tap.cask_renames)
|
||||
File.write("_data/cask_canonical.json", "#{canonical_json}\n") unless args.dry_run?
|
||||
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
variation_casks = all_casks.map do |_, cask|
|
||||
Homebrew::API.merge_variations(cask, bottle_tag:)
|
||||
end
|
||||
|
||||
File.write("api/internal/cask.#{bottle_tag}.json", JSON.generate(variation_casks)) unless args.dry_run?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,7 +32,7 @@ module Homebrew
|
||||
raise TapUnavailableError, tap.name unless tap.installed?
|
||||
|
||||
unless args.dry_run?
|
||||
directories = ["_data/formula", "api/formula", "formula", "api/internal/v3"]
|
||||
directories = ["_data/formula", "api/formula", "formula", "api/internal"]
|
||||
FileUtils.rm_rf directories + ["_data/formula_canonical.json"]
|
||||
FileUtils.mkdir_p directories
|
||||
end
|
||||
@ -44,12 +44,14 @@ module Homebrew
|
||||
Formulary.enable_factory_cache!
|
||||
Formula.generating_hash!
|
||||
|
||||
all_formulae = {}
|
||||
latest_macos = MacOSVersion.new((HOMEBREW_MACOS_NEWEST_UNSUPPORTED.to_i - 1).to_s).to_sym
|
||||
Homebrew::SimulateSystem.with(os: latest_macos, arch: :arm) do
|
||||
tap.formula_names.each do |name|
|
||||
formula = Formulary.factory(name)
|
||||
name = formula.name
|
||||
json = JSON.pretty_generate(formula.to_hash_with_variations)
|
||||
all_formulae[name] = formula.to_hash_with_variations
|
||||
json = JSON.pretty_generate(all_formulae[name])
|
||||
html_template_name = html_template(name)
|
||||
|
||||
unless args.dry_run?
|
||||
@ -65,6 +67,30 @@ module Homebrew
|
||||
|
||||
canonical_json = JSON.pretty_generate(tap.formula_renames.merge(tap.alias_table))
|
||||
File.write("_data/formula_canonical.json", "#{canonical_json}\n") unless args.dry_run?
|
||||
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
variation_formulae = all_formulae.to_h do |name, formula|
|
||||
formula = Homebrew::API.merge_variations(formula, bottle_tag:)
|
||||
|
||||
version = Version.new(formula.dig("versions", "stable"))
|
||||
pkg_version = PkgVersion.new(version, formula["revision"])
|
||||
rebuild = formula.dig("bottle", "stable", "rebuild") || 0
|
||||
|
||||
bottle_collector = Utils::Bottles::Collector.new
|
||||
formula.dig("bottle", "stable", "files")&.each do |tag, data|
|
||||
tag = Utils::Bottles::Tag.from_symbol(tag)
|
||||
bottle_collector.add tag, checksum: Checksum.new(data["sha256"]), cellar: :any
|
||||
end
|
||||
|
||||
sha256 = bottle_collector.specification_for(bottle_tag)&.checksum&.to_s
|
||||
|
||||
[name, [pkg_version.to_s, rebuild, sha256]]
|
||||
end
|
||||
|
||||
unless args.dry_run?
|
||||
File.write("api/internal/formula.#{bottle_tag}.json", JSON.generate(variation_formulae))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -44,8 +44,8 @@ module Homebrew
|
||||
switch "--examples",
|
||||
description: "Show several examples."
|
||||
switch "--pry",
|
||||
env: :pry,
|
||||
description: "Use Pry instead of IRB. Implied if `HOMEBREW_PRY` is set."
|
||||
description: "Use Pry instead of IRB.",
|
||||
env: :pry
|
||||
end
|
||||
|
||||
# work around IRB modifying ARGV.
|
||||
|
@ -23,7 +23,7 @@ module Homebrew
|
||||
description: "For every library that a keg references, print its dylib path followed by the " \
|
||||
"binaries that link to it."
|
||||
switch "--cached",
|
||||
description: "Print the cached linkage values stored in `HOMEBREW_CACHE`, set by a previous " \
|
||||
description: "Print the cached linkage values stored in `$HOMEBREW_CACHE`, set by a previous " \
|
||||
"`brew linkage` run."
|
||||
|
||||
named_args :installed_formula
|
||||
|
@ -13,7 +13,7 @@ module Homebrew
|
||||
description <<~EOS
|
||||
Check for newer versions of formulae and/or casks from upstream.
|
||||
If no formula or cask argument is passed, the list of formulae and
|
||||
casks to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` or
|
||||
casks to check is taken from `$HOMEBREW_LIVECHECK_WATCHLIST` or
|
||||
`~/.homebrew/livecheck_watchlist.txt`.
|
||||
EOS
|
||||
switch "--full-name",
|
||||
|
@ -13,11 +13,11 @@ module Homebrew
|
||||
Enter an interactive shell for Homebrew's build environment. Use years-battle-hardened
|
||||
build logic to help your `./configure && make && make install`
|
||||
and even your `gem install` succeed. Especially handy if you run Homebrew
|
||||
in an Xcode-only configuration since it adds tools like `make` to your `PATH`
|
||||
in an Xcode-only configuration since it adds tools like `make` to your `$PATH`
|
||||
which build systems would not find otherwise.
|
||||
EOS
|
||||
flag "--env=",
|
||||
description: "Use the standard `PATH` instead of superenv's when `std` is passed."
|
||||
description: "Use the standard `$PATH` instead of superenv's when `std` is passed."
|
||||
flag "-c=", "--cmd=",
|
||||
description: "Execute commands in a non-interactive shell."
|
||||
|
||||
|
@ -28,6 +28,8 @@ module Homebrew
|
||||
description: "Only runs tests on files that were changed from the master branch."
|
||||
switch "--fail-fast",
|
||||
description: "Exit early on the first failing test."
|
||||
switch "--no-parallel",
|
||||
description: "Run tests serially."
|
||||
flag "--only=",
|
||||
description: "Run only `<test_script>_spec.rb`. Appending `:<line_number>` will start at a " \
|
||||
"specific line."
|
||||
@ -49,7 +51,7 @@ module Homebrew
|
||||
HOMEBREW_LIBRARY_PATH.cd do
|
||||
setup_environment!
|
||||
|
||||
parallel = true
|
||||
parallel = !args.no_parallel?
|
||||
|
||||
only = args.only
|
||||
files = if only
|
||||
@ -120,29 +122,13 @@ module Homebrew
|
||||
]
|
||||
bundle_args << "--fail-fast" if args.fail_fast?
|
||||
bundle_args << "--profile" << args.profile if args.profile
|
||||
|
||||
# TODO: Refactor and move to extend/os
|
||||
# rubocop:disable Homebrew/MoveToExtendOS
|
||||
unless OS.mac?
|
||||
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
|
||||
files = files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
unless OS.linux?
|
||||
bundle_args << "--tag" << "~needs_linux"
|
||||
files = files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
|
||||
end
|
||||
# rubocop:enable Homebrew/MoveToExtendOS
|
||||
|
||||
bundle_args << "--tag" << "~needs_arm" unless Hardware::CPU.arm?
|
||||
|
||||
bundle_args << "--tag" << "~needs_intel" unless Hardware::CPU.intel?
|
||||
|
||||
bundle_args << "--tag" << "~needs_network" unless args.online?
|
||||
unless ENV["CI"]
|
||||
bundle_args << "--tag" << "~needs_ci" \
|
||||
<< "--tag" << "~needs_svn"
|
||||
end
|
||||
bundle_args << "--tag" << "~needs_ci" unless ENV["CI"]
|
||||
|
||||
bundle_args = os_bundle_args(bundle_args)
|
||||
files = os_files(files)
|
||||
|
||||
puts "Randomized with seed #{seed}"
|
||||
|
||||
@ -170,6 +156,41 @@ module Homebrew
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
# for generic tests, remove macOS or Linux specific tests
|
||||
non_linux_bundle_args(non_macos_bundle_args(bundle_args))
|
||||
end
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_macos_bundle_args(bundle_args)
|
||||
bundle_args << "--tag" << "~needs_homebrew_core" if ENV["CI"]
|
||||
bundle_args << "--tag" << "~needs_svn" unless args.online?
|
||||
|
||||
bundle_args << "--tag" << "~needs_macos" << "--tag" << "~cask"
|
||||
end
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_linux_bundle_args(bundle_args)
|
||||
bundle_args << "--tag" << "~needs_linux" << "--tag" << "~needs_systemd"
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_files(files)
|
||||
# for generic tests, remove macOS or Linux specific files
|
||||
non_linux_files(non_macos_files(files))
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_macos_files(files)
|
||||
files.grep_v(%r{^test/(os/mac|cask)(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
sig { params(files: T::Array[String]).returns(T::Array[String]) }
|
||||
def non_linux_files(files)
|
||||
files.grep_v(%r{^test/os/linux(/.*|_spec\.rb)$})
|
||||
end
|
||||
|
||||
sig { returns(T::Array[String]) }
|
||||
def changed_test_files
|
||||
changed_files = Utils.popen_read("git", "diff", "--name-only", "master")
|
||||
@ -221,9 +242,6 @@ module Homebrew
|
||||
ENV["HOMEBREW_SORBET_RUNTIME"] = "1"
|
||||
ENV["HOMEBREW_NO_FORCE_BREW_WRAPPER"] = "1"
|
||||
|
||||
# TODO: remove this and fix tests when possible.
|
||||
ENV["HOMEBREW_NO_INSTALL_FROM_API"] = "1"
|
||||
|
||||
ENV["USER"] ||= system_command!("id", args: ["-nu"]).stdout.chomp
|
||||
|
||||
# Avoid local configuration messing with tests, e.g. git being configured
|
||||
@ -249,3 +267,5 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "extend/os/dev-cmd/tests"
|
||||
|
@ -23,7 +23,7 @@ module Homebrew
|
||||
description: "Print the `homebrew/core` commits where bottles were lost in the last week."
|
||||
switch "--eval-all",
|
||||
description: "Evaluate all available formulae and casks, whether installed or not, to check them. " \
|
||||
"Implied if `HOMEBREW_EVAL_ALL` is set."
|
||||
"Implied if `$HOMEBREW_EVAL_ALL` is set."
|
||||
|
||||
conflicts "--dependents", "--total", "--lost"
|
||||
|
||||
@ -68,7 +68,7 @@ module Homebrew
|
||||
all = args.eval_all?
|
||||
if args.total?
|
||||
if !all && !Homebrew::EnvConfig.eval_all?
|
||||
raise UsageError, "`brew unbottled --total` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!"
|
||||
raise UsageError, "`brew unbottled --total` needs `--eval-all` passed or `$HOMEBREW_EVAL_ALL` set!"
|
||||
end
|
||||
|
||||
all = true
|
||||
@ -119,7 +119,7 @@ module Homebrew
|
||||
elsif args.dependents?
|
||||
if !args.eval_all? && !Homebrew::EnvConfig.eval_all?
|
||||
raise UsageError,
|
||||
"`brew unbottled --dependents` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!"
|
||||
"`brew unbottled --dependents` needs `--eval-all` passed or `$HOMEBREW_EVAL_ALL` set!"
|
||||
end
|
||||
|
||||
formulae = all_formulae = Formula.all(eval_all: args.eval_all?)
|
||||
@ -136,7 +136,7 @@ module Homebrew
|
||||
if analytics.blank?
|
||||
raise UsageError,
|
||||
"default sort by analytics data requires " \
|
||||
"`HOMEBREW_NO_GITHUB_API` and `HOMEBREW_NO_ANALYTICS` to be unset"
|
||||
"`$HOMEBREW_NO_GITHUB_API` and `$HOMEBREW_NO_ANALYTICS` to be unset."
|
||||
end
|
||||
|
||||
formulae = analytics["items"].filter_map do |i|
|
||||
|
@ -15,7 +15,7 @@ module Homebrew
|
||||
If no options are passed, use `origin/master` as the start commit.
|
||||
EOS
|
||||
switch "--to-tag",
|
||||
description: "Set `HOMEBREW_UPDATE_TO_TAG` to test updating between tags."
|
||||
description: "Set `$HOMEBREW_UPDATE_TO_TAG` to test updating between tags."
|
||||
switch "--keep-tmp",
|
||||
description: "Retain the temporary directory containing the new repository clone."
|
||||
flag "--commit=",
|
||||
|
@ -463,7 +463,7 @@ module Homebrew
|
||||
default_text: "`~/.ssh/config`",
|
||||
},
|
||||
HOMEBREW_SUDO_THROUGH_SUDO_USER: {
|
||||
description: "If set, Homebrew will use the `SUDO_USER` environment variable to define the user to " \
|
||||
description: "If set, Homebrew will use the `$SUDO_USER` environment variable to define the user to " \
|
||||
"`sudo`(8) through when running `sudo`(8).",
|
||||
boolean: true,
|
||||
},
|
||||
|
@ -7,16 +7,6 @@ require "extend/ENV/shared"
|
||||
require "extend/ENV/std"
|
||||
require "extend/ENV/super"
|
||||
|
||||
module Kernel
|
||||
sig { params(env: T.nilable(String)).returns(T::Boolean) }
|
||||
def superenv?(env)
|
||||
return false if env == "std"
|
||||
|
||||
!Superenv.bin.nil?
|
||||
end
|
||||
private :superenv?
|
||||
end
|
||||
|
||||
# <!-- vale off -->
|
||||
# @!parse
|
||||
# # `ENV` is not actually a class, but this makes YARD happy
|
||||
|
@ -5,6 +5,14 @@
|
||||
# TODO: move these out of `Kernel`.
|
||||
|
||||
module Kernel
|
||||
sig { params(env: T.nilable(String)).returns(T::Boolean) }
|
||||
def superenv?(env)
|
||||
return false if env == "std"
|
||||
|
||||
!Superenv.bin.nil?
|
||||
end
|
||||
private :superenv?
|
||||
|
||||
def require?(path)
|
||||
return false if path.nil?
|
||||
|
||||
@ -82,6 +90,17 @@ module Kernel
|
||||
end
|
||||
end
|
||||
|
||||
# Print a warning message only if not running in GitHub Actions.
|
||||
#
|
||||
# @api public
|
||||
sig { params(message: T.any(String, Exception)).void }
|
||||
def opoo_outside_github_actions(message)
|
||||
require "utils/github/actions"
|
||||
return if GitHub::Actions.env_set?
|
||||
|
||||
opoo(message)
|
||||
end
|
||||
|
||||
# Print an error message.
|
||||
#
|
||||
# @api public
|
||||
|
5
Library/Homebrew/extend/os/dev-cmd/tests.rb
Normal file
5
Library/Homebrew/extend/os/dev-cmd/tests.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/os/linux/dev-cmd/tests" if OS.linux?
|
||||
require "extend/os/mac/dev-cmd/tests" if OS.mac?
|
@ -9,6 +9,16 @@ module OS
|
||||
def mas_installed?
|
||||
false
|
||||
end
|
||||
|
||||
# Setup pkg-config, if present, to help locate packages
|
||||
# Only need this on Linux as Homebrew provides a shim on macOS
|
||||
sig { void }
|
||||
def prepend_pkgconf_path_if_needed!
|
||||
pkgconf = Formulary.factory("pkgconf")
|
||||
return unless pkgconf.any_version_installed?
|
||||
|
||||
ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
23
Library/Homebrew/extend/os/linux/dev-cmd/tests.rb
Normal file
23
Library/Homebrew/extend/os/linux/dev-cmd/tests.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OS
|
||||
module Linux
|
||||
module DevCmd
|
||||
module Tests
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { Homebrew::DevCmd::Tests }
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
non_macos_bundle_args(bundle_args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Homebrew::DevCmd::Tests.prepend(OS::Linux::DevCmd::Tests)
|
@ -18,6 +18,26 @@ module OS
|
||||
def gnu_tar(gnu_tar_formula)
|
||||
"#{gnu_tar_formula.opt_bin}/gtar"
|
||||
end
|
||||
|
||||
sig { params(formula: Formula).returns(T::Array[Regexp]) }
|
||||
def formula_ignores(formula)
|
||||
ignores = super
|
||||
|
||||
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
|
||||
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
|
||||
|
||||
ignores << case formula.name
|
||||
# On Linux, GCC installation can be moved so long as the whole directory tree is moved together:
|
||||
# https://gcc-help.gcc.gnu.narkive.com/GnwuCA7l/moving-gcc-from-the-installation-path-is-it-allowed.
|
||||
when Version.formula_optionally_versioned_regex(:gcc)
|
||||
Regexp.union(%r{#{cellar_regex}/gcc}, %r{#{prefix_regex}/opt/gcc}) if OS.linux?
|
||||
# binutils is relocatable for the same reason: https://github.com/Homebrew/brew/pull/11899#issuecomment-906804451.
|
||||
when Version.formula_optionally_versioned_regex(:binutils)
|
||||
%r{#{cellar_regex}/binutils} if OS.linux?
|
||||
end
|
||||
|
||||
ignores.compact
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
23
Library/Homebrew/extend/os/mac/dev-cmd/tests.rb
Normal file
23
Library/Homebrew/extend/os/mac/dev-cmd/tests.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OS
|
||||
module Mac
|
||||
module DevCmd
|
||||
module Tests
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { Homebrew::DevCmd::Tests }
|
||||
|
||||
private
|
||||
|
||||
sig { params(bundle_args: T::Array[String]).returns(T::Array[String]) }
|
||||
def os_bundle_args(bundle_args)
|
||||
non_linux_bundle_args(bundle_args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Homebrew::DevCmd::Tests.prepend(OS::Mac::DevCmd::Tests)
|
@ -425,8 +425,8 @@ module OS
|
||||
end
|
||||
|
||||
def check_deprecated_caskroom_taps
|
||||
tapped_caskroom_taps = Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
|
||||
.map(&:name)
|
||||
tapped_caskroom_taps = ::Tap.select { |t| t.user == "caskroom" || t.name == "phinze/cask" }
|
||||
.map(&:name)
|
||||
return if tapped_caskroom_taps.empty?
|
||||
|
||||
<<~EOS
|
||||
|
@ -8,7 +8,7 @@ module OS
|
||||
|
||||
requires_ancestor { Kernel }
|
||||
|
||||
sig { params(tap: Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
|
||||
sig { params(tap: ::Tap, os_name: T.nilable(Symbol), arch: T.nilable(Symbol)).returns(T::Boolean) }
|
||||
def valid_casks?(tap, os_name: nil, arch: ::Hardware::CPU.type)
|
||||
return true if os_name == :linux
|
||||
|
||||
|
17
Library/Homebrew/extend/os/mac/tap.rb
Normal file
17
Library/Homebrew/extend/os/mac/tap.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OS
|
||||
module Mac
|
||||
module Tap
|
||||
module ClassMethods
|
||||
sig { returns(T::Array[::Tap]) }
|
||||
def core_taps
|
||||
[CoreTap.instance, CoreCaskTap.instance].freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Tap.singleton_class.prepend(OS::Mac::Tap::ClassMethods)
|
@ -47,7 +47,7 @@ module Utils
|
||||
return if tag_version.blank?
|
||||
|
||||
tags.find do |candidate|
|
||||
next if candidate.arch != tag.arch
|
||||
next if candidate.standardized_arch != tag.standardized_arch
|
||||
|
||||
candidate.to_macos_version <= tag_version
|
||||
rescue MacOSVersion::Error
|
||||
|
4
Library/Homebrew/extend/os/tap.rb
Normal file
4
Library/Homebrew/extend/os/tap.rb
Normal file
@ -0,0 +1,4 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/os/mac/tap" if OS.mac?
|
@ -1,79 +1,9 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
module DiskUsageExtension
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { Pathname }
|
||||
|
||||
sig { returns(Integer) }
|
||||
def disk_usage
|
||||
return @disk_usage if defined?(@disk_usage)
|
||||
|
||||
compute_disk_usage
|
||||
@disk_usage
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def file_count
|
||||
return @file_count if defined?(@file_count)
|
||||
|
||||
compute_disk_usage
|
||||
@file_count
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def abv
|
||||
out = +""
|
||||
compute_disk_usage
|
||||
out << "#{number_readable(@file_count)} files, " if @file_count > 1
|
||||
out << disk_usage_readable(@disk_usage).to_s
|
||||
out.freeze
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def compute_disk_usage
|
||||
if symlink? && !exist?
|
||||
@file_count = 1
|
||||
@disk_usage = 0
|
||||
return
|
||||
end
|
||||
|
||||
path = if symlink?
|
||||
resolved_path
|
||||
else
|
||||
self
|
||||
end
|
||||
|
||||
if path.directory?
|
||||
scanned_files = Set.new
|
||||
@file_count = 0
|
||||
@disk_usage = 0
|
||||
path.find do |f|
|
||||
if f.directory?
|
||||
@disk_usage += f.lstat.size
|
||||
else
|
||||
@file_count += 1 if f.basename.to_s != ".DS_Store"
|
||||
# use Pathname#lstat instead of Pathname#stat to get info of symlink itself.
|
||||
stat = f.lstat
|
||||
file_id = [stat.dev, stat.ino]
|
||||
# count hardlinks only once.
|
||||
unless scanned_files.include?(file_id)
|
||||
@disk_usage += stat.size
|
||||
scanned_files.add(file_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@file_count = 1
|
||||
@disk_usage = path.lstat.size
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "system_command"
|
||||
require "extend/pathname/disk_usage_extension"
|
||||
require "extend/pathname/observer_pathname_extension"
|
||||
|
||||
# Homebrew extends Ruby's `Pathname` to make our code more readable.
|
||||
# @see https://ruby-doc.org/stdlib-2.6.3/libdoc/pathname/rdoc/Pathname.html Ruby's Pathname API
|
||||
@ -524,92 +454,3 @@ class Pathname
|
||||
end
|
||||
end
|
||||
require "extend/os/pathname"
|
||||
|
||||
require "context"
|
||||
|
||||
module ObserverPathnameExtension
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { Pathname }
|
||||
|
||||
class << self
|
||||
include Context
|
||||
|
||||
sig { returns(Integer) }
|
||||
attr_accessor :n, :d
|
||||
|
||||
sig { void }
|
||||
def reset_counts!
|
||||
@n = @d = 0
|
||||
@put_verbose_trimmed_warning = false
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def total
|
||||
n + d
|
||||
end
|
||||
|
||||
sig { returns([Integer, Integer]) }
|
||||
def counts
|
||||
[n, d]
|
||||
end
|
||||
|
||||
MAXIMUM_VERBOSE_OUTPUT = 100
|
||||
private_constant :MAXIMUM_VERBOSE_OUTPUT
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def verbose?
|
||||
return super unless ENV["CI"]
|
||||
return false unless super
|
||||
|
||||
if total < MAXIMUM_VERBOSE_OUTPUT
|
||||
true
|
||||
else
|
||||
unless @put_verbose_trimmed_warning
|
||||
puts "Only the first #{MAXIMUM_VERBOSE_OUTPUT} operations were output."
|
||||
@put_verbose_trimmed_warning = true
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def unlink
|
||||
super
|
||||
puts "rm #{self}" if ObserverPathnameExtension.verbose?
|
||||
ObserverPathnameExtension.n += 1
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def mkpath
|
||||
super
|
||||
puts "mkdir -p #{self}" if ObserverPathnameExtension.verbose?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def rmdir
|
||||
super
|
||||
puts "rmdir #{self}" if ObserverPathnameExtension.verbose?
|
||||
ObserverPathnameExtension.d += 1
|
||||
end
|
||||
|
||||
sig { params(src: Pathname).void }
|
||||
def make_relative_symlink(src)
|
||||
super
|
||||
puts "ln -s #{src.relative_path_from(dirname)} #{basename}" if ObserverPathnameExtension.verbose?
|
||||
ObserverPathnameExtension.n += 1
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def install_info
|
||||
super
|
||||
puts "info #{self}" if ObserverPathnameExtension.verbose?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def uninstall_info
|
||||
super
|
||||
puts "uninfo #{self}" if ObserverPathnameExtension.verbose?
|
||||
end
|
||||
end
|
||||
|
74
Library/Homebrew/extend/pathname/disk_usage_extension.rb
Normal file
74
Library/Homebrew/extend/pathname/disk_usage_extension.rb
Normal file
@ -0,0 +1,74 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
module DiskUsageExtension
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { Pathname }
|
||||
|
||||
sig { returns(Integer) }
|
||||
def disk_usage
|
||||
return @disk_usage if defined?(@disk_usage)
|
||||
|
||||
compute_disk_usage
|
||||
@disk_usage
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def file_count
|
||||
return @file_count if defined?(@file_count)
|
||||
|
||||
compute_disk_usage
|
||||
@file_count
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def abv
|
||||
out = +""
|
||||
compute_disk_usage
|
||||
out << "#{number_readable(@file_count)} files, " if @file_count > 1
|
||||
out << disk_usage_readable(@disk_usage).to_s
|
||||
out.freeze
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { void }
|
||||
def compute_disk_usage
|
||||
if symlink? && !exist?
|
||||
@file_count = 1
|
||||
@disk_usage = 0
|
||||
return
|
||||
end
|
||||
|
||||
path = if symlink?
|
||||
resolved_path
|
||||
else
|
||||
self
|
||||
end
|
||||
|
||||
if path.directory?
|
||||
scanned_files = Set.new
|
||||
@file_count = 0
|
||||
@disk_usage = 0
|
||||
path.find do |f|
|
||||
if f.directory?
|
||||
@disk_usage += f.lstat.size
|
||||
else
|
||||
@file_count += 1 if f.basename.to_s != ".DS_Store"
|
||||
# use Pathname#lstat instead of Pathname#stat to get info of symlink itself.
|
||||
stat = f.lstat
|
||||
file_id = [stat.dev, stat.ino]
|
||||
# count hardlinks only once.
|
||||
unless scanned_files.include?(file_id)
|
||||
@disk_usage += stat.size
|
||||
scanned_files.add(file_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@file_count = 1
|
||||
@disk_usage = path.lstat.size
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,91 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "context"
|
||||
|
||||
module ObserverPathnameExtension
|
||||
extend T::Helpers
|
||||
|
||||
requires_ancestor { Pathname }
|
||||
|
||||
class << self
|
||||
include Context
|
||||
|
||||
sig { returns(Integer) }
|
||||
attr_accessor :n, :d
|
||||
|
||||
sig { void }
|
||||
def reset_counts!
|
||||
@n = @d = 0
|
||||
@put_verbose_trimmed_warning = false
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def total
|
||||
n + d
|
||||
end
|
||||
|
||||
sig { returns([Integer, Integer]) }
|
||||
def counts
|
||||
[n, d]
|
||||
end
|
||||
|
||||
MAXIMUM_VERBOSE_OUTPUT = 100
|
||||
private_constant :MAXIMUM_VERBOSE_OUTPUT
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def verbose?
|
||||
return super unless ENV["CI"]
|
||||
return false unless super
|
||||
|
||||
if total < MAXIMUM_VERBOSE_OUTPUT
|
||||
true
|
||||
else
|
||||
unless @put_verbose_trimmed_warning
|
||||
puts "Only the first #{MAXIMUM_VERBOSE_OUTPUT} operations were output."
|
||||
@put_verbose_trimmed_warning = true
|
||||
end
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def unlink
|
||||
super
|
||||
puts "rm #{self}" if ObserverPathnameExtension.verbose?
|
||||
ObserverPathnameExtension.n += 1
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def mkpath
|
||||
super
|
||||
puts "mkdir -p #{self}" if ObserverPathnameExtension.verbose?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def rmdir
|
||||
super
|
||||
puts "rmdir #{self}" if ObserverPathnameExtension.verbose?
|
||||
ObserverPathnameExtension.d += 1
|
||||
end
|
||||
|
||||
sig { params(src: Pathname).void }
|
||||
def make_relative_symlink(src)
|
||||
super
|
||||
puts "ln -s #{src.relative_path_from(dirname)} #{basename}" if ObserverPathnameExtension.verbose?
|
||||
ObserverPathnameExtension.n += 1
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def install_info
|
||||
super
|
||||
puts "info #{self}" if ObserverPathnameExtension.verbose?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def uninstall_info
|
||||
super
|
||||
puts "uninfo #{self}" if ObserverPathnameExtension.verbose?
|
||||
end
|
||||
end
|
@ -5,5 +5,9 @@ require "time"
|
||||
|
||||
class Time
|
||||
# Backwards compatibility for formulae that used this ActiveSupport extension
|
||||
alias rfc3339 xmlschema
|
||||
sig { returns(String) }
|
||||
def rfc3339
|
||||
odeprecated "Time#rfc3339", "Time#xmlschema"
|
||||
xmlschema
|
||||
end
|
||||
end
|
||||
|
@ -38,9 +38,9 @@ require "tab"
|
||||
require "mktemp"
|
||||
require "find"
|
||||
require "utils/spdx"
|
||||
require "extend/on_system"
|
||||
require "on_system"
|
||||
require "api"
|
||||
require "extend/api_hashable"
|
||||
require "api_hashable"
|
||||
|
||||
# A formula provides instructions and metadata for Homebrew to install a piece
|
||||
# of software. Every Homebrew formula is a {Formula}.
|
||||
@ -2593,11 +2593,8 @@ class Formula
|
||||
|
||||
if path.exist? && on_system_blocks_exist?
|
||||
formula_contents = path.read
|
||||
OnSystem::ALL_OS_ARCH_COMBINATIONS.each do |os, arch|
|
||||
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless bottle_tag.valid_combination?
|
||||
|
||||
Homebrew::SimulateSystem.with(os:, arch:) do
|
||||
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
|
||||
Homebrew::SimulateSystem.with_tag(bottle_tag) do
|
||||
variations_namespace = Formulary.class_s("Variations#{bottle_tag.to_sym.capitalize}")
|
||||
variations_formula_class = Formulary.load_formula(name, path, formula_contents, variations_namespace,
|
||||
flags: self.class.build_flags, ignore_errors: true)
|
||||
|
@ -114,7 +114,7 @@ module Homebrew
|
||||
ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name}
|
||||
EOS
|
||||
else
|
||||
problem "Formula has other versions so create an alias named #{alias_name}."
|
||||
problem "Formula has other versions so create an alias named '#{alias_name}'."
|
||||
end
|
||||
end
|
||||
|
||||
@ -153,7 +153,7 @@ module Homebrew
|
||||
next if synced_formula == name
|
||||
|
||||
if (synced_version = Formulary.factory(synced_formula).version) != version
|
||||
problem "Version of `#{synced_formula}` (#{synced_version}) should match version of `#{name}` (#{version})"
|
||||
problem "Version of #{synced_formula} (#{synced_version}) should match version of #{name} (#{version})"
|
||||
end
|
||||
end
|
||||
|
||||
@ -188,7 +188,7 @@ module Homebrew
|
||||
return if formula.core_formula?
|
||||
return unless Formula.core_names.include?(name)
|
||||
|
||||
problem "Formula name conflicts with existing core formula."
|
||||
problem "Formula name conflicts with an existing formula in homebrew/core."
|
||||
end
|
||||
|
||||
PERMITTED_LICENSE_MISMATCHES = {
|
||||
@ -226,7 +226,7 @@ module Homebrew
|
||||
problem <<~EOS
|
||||
Formula #{formula.name} contains incompatible licenses: #{incompatible_licenses}.
|
||||
Formulae in homebrew/core must either use a Debian Free Software Guidelines license
|
||||
or be released into the public domain. See #{Formatter.url("https://docs.brew.sh/License-Guidelines")}
|
||||
or be released into the public domain: #{Formatter.url("https://docs.brew.sh/License-Guidelines")}
|
||||
EOS
|
||||
end
|
||||
|
||||
@ -327,7 +327,7 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
problem "Dependency '#{dep}' does not define option #{opt.name.inspect}"
|
||||
problem "Dependency '#{dep}' does not define option: #{opt.name.inspect}"
|
||||
end
|
||||
|
||||
problem "Don't use 'git' as a dependency (it's always available)" if @new_formula && dep.name == "git"
|
||||
@ -448,7 +448,7 @@ module Homebrew
|
||||
|
||||
if T.must(tap).formula_renames.key?(conflict.name) || T.must(tap).aliases.include?(conflict.name)
|
||||
problem "Formula conflict should be declared using " \
|
||||
"canonical name (#{conflicting_formula.name}) instead of #{conflict.name}"
|
||||
"canonical name (#{conflicting_formula.name}) instead of '#{conflict.name}'"
|
||||
end
|
||||
|
||||
reverse_conflict_found = T.let(false, T::Boolean)
|
||||
@ -457,7 +457,7 @@ module Homebrew
|
||||
if T.must(tap).formula_renames.key?(reverse_conflict.name) ||
|
||||
T.must(tap).aliases.include?(reverse_conflict.name)
|
||||
problem "Formula #{conflicting_formula.name} conflict should be declared using " \
|
||||
"canonical name (#{reverse_conflict_formula.name}) instead of #{reverse_conflict.name}"
|
||||
"canonical name (#{reverse_conflict_formula.name}) instead of '#{reverse_conflict.name}'"
|
||||
end
|
||||
|
||||
reverse_conflict_found ||= reverse_conflict_formula == formula
|
||||
@ -651,7 +651,7 @@ module Homebrew
|
||||
metadata = SharedAudits.github_repo_data(user, repo)
|
||||
return if metadata.nil?
|
||||
|
||||
problem "GitHub repo is archived" if metadata["archived"]
|
||||
problem "GitHub repository is archived" if metadata["archived"]
|
||||
end
|
||||
|
||||
def audit_gitlab_repository_archived
|
||||
@ -663,7 +663,7 @@ module Homebrew
|
||||
metadata = SharedAudits.gitlab_repo_data(user, repo)
|
||||
return if metadata.nil?
|
||||
|
||||
problem "GitLab repo is archived" if metadata["archived"]
|
||||
problem "GitLab repository is archived" if metadata["archived"]
|
||||
end
|
||||
|
||||
def audit_github_repository
|
||||
@ -712,7 +712,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
def audit_specs
|
||||
problem "Head-only (no stable download)" if head_only?(formula)
|
||||
problem "HEAD-only (no stable download)" if head_only?(formula)
|
||||
|
||||
%w[Stable HEAD].each do |name|
|
||||
spec_name = name.downcase.to_sym
|
||||
@ -759,7 +759,7 @@ module Homebrew
|
||||
|
||||
if formula.head && @versioned_formula &&
|
||||
!formula.tap&.audit_exception(:versioned_head_spec_allowlist, formula.name)
|
||||
problem "Versioned formulae should not have a `HEAD` spec"
|
||||
problem "Versioned formulae should not have a `head` spec"
|
||||
end
|
||||
|
||||
stable = formula.stable
|
||||
@ -771,7 +771,7 @@ module Homebrew
|
||||
|
||||
stable_version_string = version.to_s
|
||||
if stable_version_string.start_with?("HEAD")
|
||||
problem "Stable: non-HEAD version name (#{stable_version_string}) should not begin with HEAD"
|
||||
problem "Stable: non-HEAD version (#{stable_version_string}) should not begin with `HEAD`"
|
||||
end
|
||||
|
||||
stable_url_version = Version.parse(stable.url)
|
||||
@ -790,7 +790,7 @@ module Homebrew
|
||||
return if formula.tap&.audit_exception :unstable_allowlist, formula.name, version_prefix
|
||||
return if formula.tap&.audit_exception :unstable_devel_allowlist, formula.name, version_prefix
|
||||
|
||||
problem "Stable version URLs should not contain #{matched}"
|
||||
problem "Stable: version URLs should not contain `#{matched}`"
|
||||
when %r{download\.gnome\.org/sources}, %r{ftp\.gnome\.org/pub/GNOME/sources}i
|
||||
version_prefix = stable.version.major_minor
|
||||
return if formula.tap&.audit_exception :gnome_devel_allowlist, formula.name, version_prefix
|
||||
@ -800,11 +800,11 @@ module Homebrew
|
||||
return if stable_url_version >= Version.new("40.0")
|
||||
return if stable_url_minor_version.even?
|
||||
|
||||
problem "#{stable.version} is a development release"
|
||||
problem "Stable: version (#{stable.version}) is a development release"
|
||||
when %r{isc.org/isc/bind\d*/}i
|
||||
return if stable_url_minor_version.even?
|
||||
|
||||
problem "#{stable.version} is a development release"
|
||||
problem "Stable: version (#{stable.version}) is a development release"
|
||||
|
||||
when %r{https?://gitlab\.com/([\w-]+)/([\w-]+)}
|
||||
owner = T.must(Regexp.last_match(1))
|
||||
@ -845,7 +845,7 @@ module Homebrew
|
||||
if !newest_committed[:version].nil? &&
|
||||
current_version < newest_committed[:version] &&
|
||||
current_version_scheme == previous_committed[:version_scheme]
|
||||
problem "stable version should not decrease (from #{newest_committed[:version]} to #{current_version})"
|
||||
problem "Stable: version should not decrease (from #{newest_committed[:version]} to #{current_version})"
|
||||
end
|
||||
end
|
||||
|
||||
@ -867,14 +867,14 @@ module Homebrew
|
||||
!current_revision.zero? &&
|
||||
current_revision == newest_committed[:revision] &&
|
||||
current_revision == previous_committed[:revision]
|
||||
problem "'revision #{current_revision}' should be removed"
|
||||
problem "`revision #{current_revision}` should be removed"
|
||||
elsif current_version == previous_committed[:version] &&
|
||||
!previous_committed[:revision].nil? &&
|
||||
current_revision < previous_committed[:revision]
|
||||
problem "revision should not decrease (from #{previous_committed[:revision]} to #{current_revision})"
|
||||
problem "`revision` should not decrease (from #{previous_committed[:revision]} to #{current_revision})"
|
||||
elsif newest_committed[:revision] &&
|
||||
current_revision > (newest_committed[:revision] + 1)
|
||||
problem "revisions should only increment by 1"
|
||||
problem "`revision` should only increment by 1"
|
||||
end
|
||||
end
|
||||
|
||||
@ -891,10 +891,10 @@ module Homebrew
|
||||
return if previous_committed[:version_scheme].nil?
|
||||
|
||||
if current_version_scheme < previous_committed[:version_scheme]
|
||||
problem "version_scheme should not decrease (from #{previous_committed[:version_scheme]} " \
|
||||
problem "`version_scheme` should not decrease (from #{previous_committed[:version_scheme]} " \
|
||||
"to #{current_version_scheme})"
|
||||
elsif current_version_scheme > (previous_committed[:version_scheme] + 1)
|
||||
problem "version_schemes should only increment by 1"
|
||||
problem "`version_scheme` should only increment by 1"
|
||||
end
|
||||
end
|
||||
|
||||
@ -935,7 +935,7 @@ module Homebrew
|
||||
bin_names.each do |name|
|
||||
shell_commands.each do |cmd|
|
||||
if text.to_s.match?(/test do.*#{cmd}[(\s]+['"]#{Regexp.escape(name)}[\s'"]/m)
|
||||
problem %Q(fully scope test #{cmd} calls, e.g. #{cmd} "\#{bin}/#{name}")
|
||||
problem %Q(Fully scope test `#{cmd}` calls, e.g.: #{cmd} "\#{bin}/#{name}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -520,6 +520,7 @@ class FormulaInstaller
|
||||
oh1 "Installing #{Formatter.identifier(formula.full_name)} #{options}".strip if show_header?
|
||||
|
||||
if (tap = formula.tap) && tap.should_report_analytics?
|
||||
require "utils/analytics"
|
||||
Utils::Analytics.report_package_event(:formula_install, package_name: formula.name, tap_name: tap.name,
|
||||
on_request: installed_on_request?, options:)
|
||||
end
|
||||
@ -892,9 +893,11 @@ on_request: installed_on_request?, options:)
|
||||
return if quiet?
|
||||
|
||||
caveats = Caveats.new(formula)
|
||||
|
||||
return if caveats.empty?
|
||||
|
||||
Homebrew.messages.record_completions_and_elisp(caveats.completions_and_elisp)
|
||||
return if caveats.caveats.empty?
|
||||
|
||||
@show_summary_heading = true
|
||||
ohai "Caveats", caveats.to_s
|
||||
Homebrew.messages.record_caveats(formula.name, caveats)
|
||||
|
@ -2,7 +2,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "digest/sha2"
|
||||
require "extend/cachable"
|
||||
require "cachable"
|
||||
require "tab"
|
||||
require "utils"
|
||||
require "utils/bottles"
|
||||
|
@ -138,7 +138,7 @@ require "extend/kernel"
|
||||
require "os"
|
||||
|
||||
require "extend/array"
|
||||
require "extend/cachable"
|
||||
require "cachable"
|
||||
require "extend/enumerable"
|
||||
require "extend/string"
|
||||
require "extend/pathname"
|
||||
|
@ -4,7 +4,7 @@
|
||||
require "keg_relocate"
|
||||
require "language/python"
|
||||
require "lock_file"
|
||||
require "extend/cachable"
|
||||
require "cachable"
|
||||
|
||||
# Installation prefix of a formula.
|
||||
class Keg
|
||||
|
@ -19,6 +19,7 @@ class MacOSVersion < Version
|
||||
# NOTE: When removing symbols here, ensure that they are added
|
||||
# to `DEPRECATED_MACOS_VERSIONS` in `MacOSRequirement`.
|
||||
SYMBOLS = {
|
||||
tahoe: "26",
|
||||
sequoia: "15",
|
||||
sonoma: "14",
|
||||
ventura: "13",
|
||||
@ -34,7 +35,9 @@ class MacOSVersion < Version
|
||||
sig { params(macos_version: MacOSVersion).returns(Version) }
|
||||
def self.kernel_major_version(macos_version)
|
||||
version_major = macos_version.major.to_i
|
||||
if version_major > 10
|
||||
if version_major >= 26
|
||||
Version.new((version_major - 1).to_s)
|
||||
elsif version_major > 10
|
||||
Version.new((version_major + 9).to_s)
|
||||
else
|
||||
version_minor = macos_version.minor.to_i
|
||||
@ -50,7 +53,7 @@ class MacOSVersion < Version
|
||||
|
||||
sig { params(version: T.nilable(String)).void }
|
||||
def initialize(version)
|
||||
raise MacOSVersion::Error, version unless /\A1\d+(?:\.\d+){0,2}\Z/.match?(version)
|
||||
raise MacOSVersion::Error, version unless /\A\d{2,}(?:\.\d+){0,2}\z/.match?(version)
|
||||
|
||||
super(T.must(version))
|
||||
|
||||
|
@ -133,7 +133,7 @@ If no search term is provided, all locally available formulae are listed.
|
||||
## CUSTOM EXTERNAL COMMANDS
|
||||
|
||||
Homebrew, like `git`(1), supports external commands. These are executable
|
||||
scripts that reside somewhere in the `PATH`, named `brew-`<cmdname> or
|
||||
scripts that reside somewhere in the `$PATH`, named `brew-`<cmdname> or
|
||||
`brew-`<cmdname>`.rb`, which can be invoked like `brew` <cmdname>. This
|
||||
allows you to create your own commands without modifying Homebrew's internals.
|
||||
|
||||
@ -184,7 +184,7 @@ files:
|
||||
|
||||
User-specific environment files take precedence over prefix-specific files and
|
||||
prefix-specific files take precedence over system-wide files (unless
|
||||
`HOMEBREW_SYSTEM_ENV_TAKES_PRIORITY` is set, see below).
|
||||
`$HOMEBREW_SYSTEM_ENV_TAKES_PRIORITY` is set, see below).
|
||||
|
||||
Note that these files do not support shell variable expansion e.g. `$HOME` or
|
||||
command execution e.g. `$(cat file)`.
|
||||
|
272
Library/Homebrew/mcp_server.rb
Normal file
272
Library/Homebrew/mcp_server.rb
Normal file
@ -0,0 +1,272 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This is a standalone Ruby script as MCP servers need a faster startup time
|
||||
# than a normal Homebrew Ruby command allows.
|
||||
require_relative "standalone"
|
||||
require "json"
|
||||
require "stringio"
|
||||
|
||||
module Homebrew
|
||||
# Provides a Model Context Protocol (MCP) server for Homebrew.
|
||||
# See https://modelcontextprotocol.io/introduction for more information.
|
||||
#
|
||||
# https://modelcontextprotocol.io/docs/tools/inspector is useful for testing.
|
||||
class McpServer
|
||||
HOMEBREW_BREW_FILE = T.let(ENV.fetch("HOMEBREW_BREW_FILE").freeze, String)
|
||||
HOMEBREW_VERSION = T.let(ENV.fetch("HOMEBREW_VERSION").freeze, String)
|
||||
JSON_RPC_VERSION = T.let("2.0", String)
|
||||
MCP_PROTOCOL_VERSION = T.let("2025-03-26", String)
|
||||
ERROR_CODE = T.let(-32601, Integer)
|
||||
|
||||
SERVER_INFO = T.let({
|
||||
name: "brew-mcp-server",
|
||||
version: HOMEBREW_VERSION,
|
||||
}.freeze, T::Hash[Symbol, String])
|
||||
|
||||
FORMULA_OR_CASK_PROPERTIES = T.let({
|
||||
formula_or_cask: {
|
||||
type: "string",
|
||||
description: "Formula or cask name",
|
||||
},
|
||||
}.freeze, T::Hash[Symbol, T.anything])
|
||||
|
||||
# NOTE: Cursor (as of June 2025) will only query/use a maximum of 40 tools.
|
||||
TOOLS = T.let({
|
||||
search: {
|
||||
name: "search",
|
||||
description: "Perform a substring search of cask tokens and formula names for <text>. " \
|
||||
"If <text> is flanked by slashes, it is interpreted as a regular expression.",
|
||||
command: "brew search",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
text_or_regex: {
|
||||
type: "string",
|
||||
description: "Text or regex to search for",
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["text_or_regex"],
|
||||
},
|
||||
info: {
|
||||
name: "info",
|
||||
description: "Display brief statistics for your Homebrew installation. " \
|
||||
"If a <formula> or <cask> is provided, show summary of information about it.",
|
||||
command: "brew info",
|
||||
inputSchema: { type: "object", properties: FORMULA_OR_CASK_PROPERTIES },
|
||||
},
|
||||
install: {
|
||||
name: "install",
|
||||
description: "Install a <formula> or <cask>.",
|
||||
command: "brew install",
|
||||
inputSchema: { type: "object", properties: FORMULA_OR_CASK_PROPERTIES },
|
||||
required: ["formula_or_cask"],
|
||||
},
|
||||
update: {
|
||||
name: "update",
|
||||
description: "Fetch the newest version of Homebrew and all formulae from GitHub using `git` and " \
|
||||
"perform any necessary migrations.",
|
||||
command: "brew update",
|
||||
inputSchema: { type: "object", properties: {} },
|
||||
},
|
||||
upgrade: {
|
||||
name: "upgrade",
|
||||
description: "Upgrade outdated casks and outdated, unpinned formulae using the same options they were " \
|
||||
"originally installed with, plus any appended brew formula options. If <cask> or <formula> " \
|
||||
"are specified, upgrade only the given <cask> or <formula> kegs (unless they are pinned).",
|
||||
command: "brew upgrade",
|
||||
inputSchema: { type: "object", properties: FORMULA_OR_CASK_PROPERTIES },
|
||||
},
|
||||
uninstall: {
|
||||
name: "uninstall",
|
||||
description: "Uninstall a <formula> or <cask>.",
|
||||
command: "brew uninstall",
|
||||
inputSchema: { type: "object", properties: FORMULA_OR_CASK_PROPERTIES },
|
||||
required: ["formula_or_cask"],
|
||||
},
|
||||
list: {
|
||||
name: "list",
|
||||
description: "List all installed formulae and casks. " \
|
||||
"If <formula> is provided, summarise the paths within its current keg. " \
|
||||
"If <cask> is provided, list its artifacts.",
|
||||
command: "brew list",
|
||||
inputSchema: { type: "object", properties: FORMULA_OR_CASK_PROPERTIES },
|
||||
},
|
||||
config: {
|
||||
name: "config",
|
||||
description: "Show Homebrew and system configuration info useful for debugging. " \
|
||||
"If you file a bug report, you will be required to provide this information.",
|
||||
command: "brew config",
|
||||
inputSchema: { type: "object", properties: {} },
|
||||
},
|
||||
doctor: {
|
||||
name: "doctor",
|
||||
description: "Check your system for potential problems. Will exit with a non-zero status " \
|
||||
"if any potential problems are found. " \
|
||||
"Please note that these warnings are just used to help the Homebrew maintainers " \
|
||||
"with debugging if you file an issue. If everything you use Homebrew for " \
|
||||
"is working fine: please don't worry or file an issue; just ignore this.",
|
||||
command: "brew doctor",
|
||||
inputSchema: { type: "object", properties: {} },
|
||||
},
|
||||
commands: {
|
||||
name: "commands",
|
||||
description: "Show lists of built-in and external commands.",
|
||||
command: "brew commands",
|
||||
inputSchema: { type: "object", properties: {} },
|
||||
},
|
||||
help: {
|
||||
name: "help",
|
||||
description: "Outputs the usage instructions for `brew` <command>.",
|
||||
command: "brew help",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
command: {
|
||||
type: "string",
|
||||
description: "Command to get help for",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}.freeze, T::Hash[Symbol, T::Hash[Symbol, T.anything]])
|
||||
|
||||
sig { params(stdin: T.any(IO, StringIO), stdout: T.any(IO, StringIO), stderr: T.any(IO, StringIO)).void }
|
||||
def initialize(stdin: $stdin, stdout: $stdout, stderr: $stderr)
|
||||
@debug_logging = T.let(ARGV.include?("--debug") || ARGV.include?("-d"), T::Boolean)
|
||||
@ping_switch = T.let(ARGV.include?("--ping"), T::Boolean)
|
||||
@stdin = T.let(stdin, T.any(IO, StringIO))
|
||||
@stdout = T.let(stdout, T.any(IO, StringIO))
|
||||
@stderr = T.let(stderr, T.any(IO, StringIO))
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def debug_logging? = @debug_logging
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ping_switch? = @ping_switch
|
||||
|
||||
sig { void }
|
||||
def run
|
||||
@stderr.puts "==> Started Homebrew MCP server..."
|
||||
|
||||
loop do
|
||||
input = if ping_switch?
|
||||
{ jsonrpc: JSON_RPC_VERSION, id: 1, method: "ping" }.to_json
|
||||
else
|
||||
@stdin.gets
|
||||
end
|
||||
next if input.nil? || input.strip.empty?
|
||||
|
||||
request = JSON.parse(input)
|
||||
debug("Request: #{JSON.pretty_generate(request)}")
|
||||
|
||||
response = handle_request(request)
|
||||
if response.nil?
|
||||
debug("Response: nil")
|
||||
next
|
||||
end
|
||||
|
||||
debug("Response: #{JSON.pretty_generate(response)}")
|
||||
output = JSON.dump(response).strip
|
||||
@stdout.puts(output)
|
||||
@stdout.flush
|
||||
|
||||
break if ping_switch?
|
||||
end
|
||||
rescue Interrupt
|
||||
exit 0
|
||||
rescue => e
|
||||
log("Error: #{e.message}")
|
||||
exit 1
|
||||
end
|
||||
|
||||
sig { params(text: String).void }
|
||||
def debug(text)
|
||||
return unless debug_logging?
|
||||
|
||||
log(text)
|
||||
end
|
||||
|
||||
sig { params(text: String).void }
|
||||
def log(text)
|
||||
@stderr.puts(text)
|
||||
@stderr.flush
|
||||
end
|
||||
|
||||
sig { params(request: T::Hash[String, T.untyped]).returns(T.nilable(T::Hash[Symbol, T.anything])) }
|
||||
def handle_request(request)
|
||||
id = request["id"]
|
||||
return if id.nil?
|
||||
|
||||
case request["method"]
|
||||
when "initialize"
|
||||
respond_result(id, {
|
||||
protocolVersion: MCP_PROTOCOL_VERSION,
|
||||
capabilities: {
|
||||
tools: { listChanged: false },
|
||||
prompts: {},
|
||||
resources: {},
|
||||
logging: {},
|
||||
roots: {},
|
||||
},
|
||||
serverInfo: SERVER_INFO,
|
||||
})
|
||||
when "resources/list"
|
||||
respond_result(id, { resources: [] })
|
||||
when "resources/templates/list"
|
||||
respond_result(id, { resourceTemplates: [] })
|
||||
when "prompts/list"
|
||||
respond_result(id, { prompts: [] })
|
||||
when "ping"
|
||||
respond_result(id)
|
||||
when "get_server_info"
|
||||
respond_result(id, SERVER_INFO)
|
||||
when "logging/setLevel"
|
||||
@debug_logging = request["params"]["level"] == "debug"
|
||||
respond_result(id)
|
||||
when "notifications/initialized", "notifications/cancelled"
|
||||
respond_result
|
||||
when "tools/list"
|
||||
respond_result(id, { tools: TOOLS.values })
|
||||
when "tools/call"
|
||||
if (tool = TOOLS.fetch(request["params"]["name"].to_sym, nil))
|
||||
require "shellwords"
|
||||
|
||||
arguments = request["params"]["arguments"]
|
||||
argument = arguments.fetch("formula_or_cask", "")
|
||||
argument = arguments.fetch("text_or_regex", "") if argument.strip.empty?
|
||||
argument = arguments.fetch("command", "") if argument.strip.empty?
|
||||
argument = nil if argument.strip.empty?
|
||||
brew_command = T.cast(tool.fetch(:command), String)
|
||||
.delete_prefix("brew ")
|
||||
full_command = [HOMEBREW_BREW_FILE, brew_command, argument].compact
|
||||
.map { |arg| Shellwords.escape(arg) }
|
||||
.join(" ")
|
||||
output = `#{full_command} 2>&1`.strip
|
||||
respond_result(id, { content: [{ type: "text", text: output }] })
|
||||
else
|
||||
respond_error(id, "Unknown tool")
|
||||
end
|
||||
else
|
||||
respond_error(id, "Method not found")
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(id: T.nilable(Integer),
|
||||
result: T::Hash[Symbol, T.anything]).returns(T.nilable(T::Hash[Symbol, T.anything]))
|
||||
}
|
||||
def respond_result(id = nil, result = {})
|
||||
return if id.nil?
|
||||
|
||||
{ jsonrpc: JSON_RPC_VERSION, id:, result: }
|
||||
end
|
||||
|
||||
sig { params(id: T.nilable(Integer), message: String).returns(T::Hash[Symbol, T.anything]) }
|
||||
def respond_error(id, message)
|
||||
{ jsonrpc: JSON_RPC_VERSION, id:, error: { code: ERROR_CODE, message: } }
|
||||
end
|
||||
end
|
||||
end
|
@ -16,6 +16,7 @@ class Messages
|
||||
sig { void }
|
||||
def initialize
|
||||
@caveats = T.let([], T::Array[T::Hash[Symbol, Symbol]])
|
||||
@completions_and_elisp = T.let(Set.new, T::Set[String])
|
||||
@package_count = T.let(0, Integer)
|
||||
@install_times = T.let([], T::Array[T::Hash[String, Float]])
|
||||
end
|
||||
@ -25,6 +26,11 @@ class Messages
|
||||
@caveats.push(package:, caveats:)
|
||||
end
|
||||
|
||||
sig { params(completions_and_elisp: T::Array[String]).void }
|
||||
def record_completions_and_elisp(completions_and_elisp)
|
||||
@completions_and_elisp.merge(completions_and_elisp)
|
||||
end
|
||||
|
||||
sig { params(package: String, elapsed_time: Float).void }
|
||||
def package_installed(package, elapsed_time)
|
||||
@package_count += 1
|
||||
@ -40,13 +46,14 @@ class Messages
|
||||
sig { params(force: T::Boolean).void }
|
||||
def display_caveats(force: false)
|
||||
return if @package_count.zero?
|
||||
return if @package_count == 1 && !force
|
||||
return if @caveats.empty?
|
||||
return if @caveats.empty? && @completions_and_elisp.empty?
|
||||
|
||||
oh1 "Caveats"
|
||||
@caveats.each do |c|
|
||||
ohai c[:package], c[:caveats]
|
||||
end
|
||||
oh1 "Caveats" unless @completions_and_elisp.empty?
|
||||
@completions_and_elisp.each { |c| puts c }
|
||||
return if @package_count == 1 && !force
|
||||
|
||||
oh1 "Caveats" if @completions_and_elisp.empty?
|
||||
@caveats.each { |c| ohai c[:package], c[:caveats] }
|
||||
end
|
||||
|
||||
sig { void }
|
||||
|
@ -9,6 +9,13 @@ module OnSystem
|
||||
ALL_OS_OPTIONS = [*MacOSVersion::SYMBOLS.keys, :linux].freeze
|
||||
ALL_OS_ARCH_COMBINATIONS = ALL_OS_OPTIONS.product(ARCH_OPTIONS).freeze
|
||||
|
||||
VALID_OS_ARCH_TAGS = ALL_OS_ARCH_COMBINATIONS.filter_map do |os, arch|
|
||||
tag = Utils::Bottles::Tag.new(system: os, arch:)
|
||||
next unless tag.valid_combination?
|
||||
|
||||
tag
|
||||
end.freeze
|
||||
|
||||
sig { params(arch: Symbol).returns(T::Boolean) }
|
||||
def self.arch_condition_met?(arch)
|
||||
raise ArgumentError, "Invalid arch condition: #{arch.inspect}" if ARCH_OPTIONS.exclude?(arch)
|
@ -5,7 +5,7 @@ libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: expat
|
||||
Version: 2.6.3
|
||||
Version: 2.7.1
|
||||
Description: expat XML parser
|
||||
URL: https://libexpat.github.io/
|
||||
Libs: -L${libdir} -lexpat
|
||||
|
12
Library/Homebrew/os/mac/pkgconfig/26/bzip2.pc
Normal file
12
Library/Homebrew/os/mac/pkgconfig/26/bzip2.pc
Normal file
@ -0,0 +1,12 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
bindir=${exec_prefix}/bin
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: bzip2
|
||||
Description: Lossless, block-sorting data compression
|
||||
Version: 1.0.8
|
||||
Libs: -L${libdir} -lbz2
|
||||
Cflags:
|
14
Library/Homebrew/os/mac/pkgconfig/26/expat.pc
Normal file
14
Library/Homebrew/os/mac/pkgconfig/26/expat.pc
Normal file
@ -0,0 +1,14 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: expat
|
||||
Version: 2.7.1
|
||||
Description: expat XML parser
|
||||
URL: https://libexpat.github.io/
|
||||
Libs: -L${libdir} -lexpat
|
||||
Libs.private:
|
||||
Cflags:
|
||||
Cflags.private: -DXML_STATIC
|
42
Library/Homebrew/os/mac/pkgconfig/26/libcurl.pc
Normal file
42
Library/Homebrew/os/mac/pkgconfig/26/libcurl.pc
Normal file
@ -0,0 +1,42 @@
|
||||
#***************************************************************************
|
||||
# _ _ ____ _
|
||||
# Project ___| | | | _ \| |
|
||||
# / __| | | | |_) | |
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
#
|
||||
# This software is licensed as described in the file COPYING, which
|
||||
# you should have received as part of this distribution. The terms
|
||||
# are also available at https://curl.se/docs/copyright.html.
|
||||
#
|
||||
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
# copies of the Software, and permit persons to whom the Software is
|
||||
# furnished to do so, under the terms of the COPYING file.
|
||||
#
|
||||
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
# KIND, either express or implied.
|
||||
#
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
|
||||
# This should most probably benefit from getting a "Requires:" field added
|
||||
# dynamically by configure.
|
||||
#
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
supported_protocols="DICT FILE FTP FTPS GOPHER GOPHERS HTTP HTTPS IMAP IMAPS IPFS IPNS LDAP LDAPS MQTT POP3 POP3S RTSP SMB SMBS SMTP SMTPS TELNET TFTP"
|
||||
supported_features="alt-svc AsynchDNS GSS-API HSTS HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz MultiSSL NTLM SPNEGO SSL threadsafe UnixSockets"
|
||||
|
||||
Name: libcurl
|
||||
URL: https://curl.se/
|
||||
Description: Library to transfer files with ftp, http, etc.
|
||||
Version: 8.7.1
|
||||
Libs: -L${libdir} -lcurl
|
||||
Libs.private: -lldap -lz
|
||||
Cflags:
|
12
Library/Homebrew/os/mac/pkgconfig/26/libedit.pc
Normal file
12
Library/Homebrew/os/mac/pkgconfig/26/libedit.pc
Normal file
@ -0,0 +1,12 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: libedit
|
||||
Description: command line editor library provides generic line editing, history, and tokenization functions.
|
||||
Version: 3.0
|
||||
Requires:
|
||||
Libs: -L${libdir} -ledit
|
||||
Cflags: -I${includedir}/editline
|
14
Library/Homebrew/os/mac/pkgconfig/26/libexslt.pc
Normal file
14
Library/Homebrew/os/mac/pkgconfig/26/libexslt.pc
Normal file
@ -0,0 +1,14 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
|
||||
Name: libexslt
|
||||
Version: 0.8.20
|
||||
Description: EXSLT Extension library
|
||||
Requires: libxml-2.0, libxslt
|
||||
Cflags:
|
||||
Libs: -L${libdir} -lexslt
|
||||
Libs.private:
|
12
Library/Homebrew/os/mac/pkgconfig/26/libffi.pc
Normal file
12
Library/Homebrew/os/mac/pkgconfig/26/libffi.pc
Normal file
@ -0,0 +1,12 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
toolexeclibdir=${libdir}
|
||||
includedir=${prefix}/include/ffi
|
||||
|
||||
Name: libffi
|
||||
Description: Library supporting Foreign Function Interfaces
|
||||
Version: 3.4-rc1
|
||||
Libs: -L${toolexeclibdir} -lffi
|
||||
Cflags: -I${includedir}
|
14
Library/Homebrew/os/mac/pkgconfig/26/libxml-2.0.pc
Normal file
14
Library/Homebrew/os/mac/pkgconfig/26/libxml-2.0.pc
Normal file
@ -0,0 +1,14 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
modules=1
|
||||
|
||||
Name: libXML
|
||||
Version: 2.9.13
|
||||
Description: libXML library version2.
|
||||
Requires:
|
||||
Libs: -L${libdir} -lxml2
|
||||
Libs.private: -lz -lpthread -licucore -lm
|
||||
Cflags:
|
14
Library/Homebrew/os/mac/pkgconfig/26/libxslt.pc
Normal file
14
Library/Homebrew/os/mac/pkgconfig/26/libxslt.pc
Normal file
@ -0,0 +1,14 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
|
||||
Name: libxslt
|
||||
Version: 1.1.35
|
||||
Description: XSLT library version 2.
|
||||
Requires: libxml-2.0
|
||||
Cflags:
|
||||
Libs: -L${libdir} -lxslt
|
||||
Libs.private:
|
17
Library/Homebrew/os/mac/pkgconfig/26/ncurses.pc
Normal file
17
Library/Homebrew/os/mac/pkgconfig/26/ncurses.pc
Normal file
@ -0,0 +1,17 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
abi_version=5.4
|
||||
major_version=6
|
||||
version=6.0.20150808
|
||||
|
||||
Name: ncurses
|
||||
Description: ncurses 6.0 library
|
||||
Version: ${version}
|
||||
URL: http://invisible-island.net/ncurses
|
||||
Requires.private:
|
||||
Libs: -L${libdir} -lncurses
|
||||
Libs.private:
|
||||
Cflags: -D_DARWIN_C_SOURCE
|
17
Library/Homebrew/os/mac/pkgconfig/26/ncursesw.pc
Normal file
17
Library/Homebrew/os/mac/pkgconfig/26/ncursesw.pc
Normal file
@ -0,0 +1,17 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
abi_version=5.4
|
||||
major_version=6
|
||||
version=6.0.20150808
|
||||
|
||||
Name: ncursesw
|
||||
Description: ncurses 6.0 library
|
||||
Version: ${version}
|
||||
URL: http://invisible-island.net/ncurses
|
||||
Requires.private:
|
||||
Libs: -L${libdir} -lncurses
|
||||
Libs.private:
|
||||
Cflags: -D_DARWIN_C_SOURCE
|
12
Library/Homebrew/os/mac/pkgconfig/26/sqlite3.pc
Normal file
12
Library/Homebrew/os/mac/pkgconfig/26/sqlite3.pc
Normal file
@ -0,0 +1,12 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: SQLite
|
||||
Description: SQL database engine
|
||||
Version: 3.48.0
|
||||
Libs: -L${libdir} -lsqlite3
|
||||
Libs.private:
|
||||
Cflags:
|
14
Library/Homebrew/os/mac/pkgconfig/26/uuid.pc
Normal file
14
Library/Homebrew/os/mac/pkgconfig/26/uuid.pc
Normal file
@ -0,0 +1,14 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
sharedlibdir=${libdir}
|
||||
includedir=${prefix}/include/uuid
|
||||
|
||||
Name: uuid
|
||||
Description: Universally unique id library
|
||||
Version: 1.0
|
||||
|
||||
Requires:
|
||||
Libs:
|
||||
Cflags: -I${includedir}
|
14
Library/Homebrew/os/mac/pkgconfig/26/zlib.pc
Normal file
14
Library/Homebrew/os/mac/pkgconfig/26/zlib.pc
Normal file
@ -0,0 +1,14 @@
|
||||
homebrew_sdkroot=/Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk
|
||||
prefix=${homebrew_sdkroot}/usr
|
||||
exec_prefix=/usr
|
||||
libdir=${exec_prefix}/lib
|
||||
sharedlibdir=${libdir}
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: zlib
|
||||
Description: zlib compression library
|
||||
Version: 1.2.12
|
||||
|
||||
Requires:
|
||||
Libs: -L${libdir} -L${sharedlibdir} -lz
|
||||
Cflags:
|
@ -15,10 +15,10 @@ module OS
|
||||
# This may be a beta version for a beta macOS.
|
||||
sig { params(macos: MacOSVersion).returns(String) }
|
||||
def self.latest_version(macos: MacOS.version)
|
||||
latest_stable = "15.4"
|
||||
macos = macos.strip_patch
|
||||
case macos
|
||||
when "15" then "16.0"
|
||||
when "14" then latest_stable
|
||||
when "15" then "16.4"
|
||||
when "14" then "16.2"
|
||||
when "13" then "15.2"
|
||||
when "12" then "14.2"
|
||||
when "11" then "13.2.1"
|
||||
@ -28,10 +28,10 @@ module OS
|
||||
when "10.12" then "9.2"
|
||||
when "10.11" then "8.2.1"
|
||||
else
|
||||
raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.version.prerelease?
|
||||
raise "macOS '#{macos}' is invalid" unless macos.prerelease?
|
||||
|
||||
# Default to newest known version of Xcode for unreleased macOS versions.
|
||||
latest_stable
|
||||
# Assume matching yearly Xcode release
|
||||
"#{macos}.0"
|
||||
end
|
||||
end
|
||||
|
||||
@ -41,7 +41,8 @@ module OS
|
||||
# also in beta).
|
||||
sig { returns(String) }
|
||||
def self.minimum_version
|
||||
case MacOS.version
|
||||
macos = MacOS.version
|
||||
case macos
|
||||
when "15" then "16.0"
|
||||
when "14" then "15.0"
|
||||
when "13" then "14.1"
|
||||
@ -51,7 +52,9 @@ module OS
|
||||
when "10.14" then "10.2"
|
||||
when "10.13" then "9.0"
|
||||
when "10.12" then "8.0"
|
||||
else "7.3"
|
||||
when "10.11" then "7.3"
|
||||
else
|
||||
"#{macos}.0"
|
||||
end
|
||||
end
|
||||
|
||||
@ -225,10 +228,8 @@ module OS
|
||||
detect_version_from_clang_version
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def self.detect_version_from_clang_version
|
||||
version = ::DevelopmentTools.clang_version
|
||||
|
||||
sig { params(version: ::Version).returns(String) }
|
||||
def self.detect_version_from_clang_version(version = ::DevelopmentTools.clang_version)
|
||||
return "dunno" if version.null?
|
||||
|
||||
# This logic provides a fake Xcode version based on the
|
||||
@ -255,8 +256,9 @@ module OS
|
||||
when "13.1.6" then "13.4.1"
|
||||
when "14.0.0" then "14.2"
|
||||
when "14.0.3" then "14.3.1"
|
||||
when "16.0.0" then "16.0"
|
||||
else "15.4"
|
||||
when "15.0.0" then "15.4"
|
||||
when "16.0.0" then "16.2"
|
||||
else "26.0"
|
||||
end
|
||||
end
|
||||
|
||||
@ -355,8 +357,9 @@ module OS
|
||||
sig { returns(String) }
|
||||
def self.latest_clang_version
|
||||
case MacOS.version
|
||||
when "15" then "1600.0.20.10"
|
||||
when "14" then "1500.3.9.4"
|
||||
when "26" then "1700.3.9.908"
|
||||
when "15" then "1700.0.13.5"
|
||||
when "14" then "1600.0.26.6"
|
||||
when "13" then "1500.1.0.2.5"
|
||||
when "12" then "1400.0.29.202"
|
||||
when "11" then "1300.0.29.30"
|
||||
@ -373,7 +376,8 @@ module OS
|
||||
# that macOS version.
|
||||
sig { returns(String) }
|
||||
def self.minimum_version
|
||||
case MacOS.version
|
||||
macos = MacOS.version
|
||||
case macos
|
||||
when "15" then "16.0.0"
|
||||
when "14" then "15.0.0"
|
||||
when "13" then "14.0.0"
|
||||
@ -383,7 +387,9 @@ module OS
|
||||
when "10.14" then "10.0.0"
|
||||
when "10.13" then "9.0.0"
|
||||
when "10.12" then "8.0.0"
|
||||
else "7.3.0"
|
||||
when "10.11" then "7.3.0"
|
||||
else
|
||||
"#{macos}.0.0"
|
||||
end
|
||||
end
|
||||
|
||||
@ -410,7 +416,10 @@ module OS
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def self.detect_version_from_clang_version
|
||||
detect_clang_version&.sub(/^(\d+)0(\d)\./, "\\1.\\2.")
|
||||
clang_version = detect_clang_version
|
||||
return if clang_version.nil?
|
||||
|
||||
MacOS::Xcode.detect_version_from_clang_version(Version.new(clang_version))
|
||||
end
|
||||
|
||||
# Version string (a pretty long one) of the CLT package.
|
||||
|
@ -1,17 +1,17 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
class IO
|
||||
sig { params(sep: String).returns(String) }
|
||||
def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR)
|
||||
class ReadlineNonblock
|
||||
sig { params(io: IO).returns(String) }
|
||||
def self.read(io)
|
||||
line = +""
|
||||
buffer = +""
|
||||
|
||||
begin
|
||||
loop do
|
||||
break if buffer == sep
|
||||
break if buffer == $INPUT_RECORD_SEPARATOR
|
||||
|
||||
read_nonblock(1, buffer)
|
||||
io.read_nonblock(1, buffer)
|
||||
line.concat(buffer)
|
||||
end
|
||||
|
@ -4,7 +4,7 @@
|
||||
require "downloadable"
|
||||
require "mktemp"
|
||||
require "livecheck"
|
||||
require "extend/on_system"
|
||||
require "on_system"
|
||||
|
||||
# Resource is the fundamental representation of an external resource. The
|
||||
# primary formula download, along with other declared resources, are instances
|
||||
|
@ -63,7 +63,7 @@ module Homebrew
|
||||
url_strategy = DownloadStrategyDetector.detect(url)
|
||||
|
||||
if (using == :git || url_strategy == GitDownloadStrategy) && specs[:tag] && !specs[:revision]
|
||||
problem "Git should specify :revision when a :tag is specified."
|
||||
problem "Git should specify `revision:` when a `tag:` is specified."
|
||||
end
|
||||
|
||||
return unless using
|
||||
@ -71,7 +71,7 @@ module Homebrew
|
||||
if using == :cvs
|
||||
mod = specs[:module]
|
||||
|
||||
problem "Redundant :module value in URL" if mod == name
|
||||
problem "Redundant `module:` value in URL" if mod == name
|
||||
|
||||
if url.match?(%r{:[^/]+$})
|
||||
mod = url.split(":").last
|
||||
@ -79,14 +79,14 @@ module Homebrew
|
||||
if mod == name
|
||||
problem "Redundant CVS module appended to URL"
|
||||
else
|
||||
problem "Specify CVS module as `:module => \"#{mod}\"` instead of appending it to the URL"
|
||||
problem "Specify CVS module as `module: \"#{mod}\"` instead of appending it to the URL"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return if url_strategy != DownloadStrategyDetector.detect("", using)
|
||||
|
||||
problem "Redundant :using value in URL"
|
||||
problem "Redundant `using:` value in URL"
|
||||
end
|
||||
|
||||
def audit_checksum
|
||||
@ -125,7 +125,7 @@ module Homebrew
|
||||
|
||||
return if name.casecmp(pypi_package_name).zero?
|
||||
|
||||
problem "resource name should be `#{pypi_package_name}` to match the PyPI package name"
|
||||
problem "`resource` name should be '#{pypi_package_name}' to match the PyPI package name"
|
||||
end
|
||||
|
||||
def audit_urls
|
||||
@ -166,12 +166,12 @@ module Homebrew
|
||||
remote_exists = Utils::Git.remote_exists?(url)
|
||||
attempts += 1
|
||||
end
|
||||
problem "The URL #{url} is not a valid git URL" unless remote_exists
|
||||
problem "The URL #{url} is not a valid Git URL" unless remote_exists
|
||||
elsif strategy <= SubversionDownloadStrategy
|
||||
next unless DevelopmentTools.subversion_handles_most_https_certificates?
|
||||
next unless Utils::Svn.available?
|
||||
|
||||
problem "The URL #{url} is not a valid svn URL" unless Utils::Svn.remote_exists? url
|
||||
problem "The URL #{url} is not a valid SVN URL" unless Utils::Svn.remote_exists? url
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -188,7 +188,7 @@ module Homebrew
|
||||
.match(%r{ref: refs/heads/(.*?)\s+HEAD})&.to_a&.second
|
||||
return if branch.blank? || branch == specs[:branch]
|
||||
|
||||
problem "Use `branch: \"#{branch}\"` to specify the default branch"
|
||||
problem "Specify the default branch as `branch: \"#{branch}\"`"
|
||||
end
|
||||
|
||||
def problem(text)
|
||||
|
@ -11,14 +11,14 @@ module RuboCop
|
||||
# TODO: Update this list if new stanzas are added to `Cask::DSL` that call `set_unique_stanza`.
|
||||
OVERRIDABLE_METHODS = [
|
||||
:appcast, :arch, :auto_updates, :conflicts_with, :container,
|
||||
:desc, :homepage, :sha256, :url, :version
|
||||
:desc, :homepage, :os, :sha256, :url, :version
|
||||
].freeze
|
||||
MESSAGE = "Do not use a top-level `%<stanza>s` stanza as the default. " \
|
||||
"Add it to an `on_{system}` block instead. " \
|
||||
"Use `:or_older` or `:or_newer` to specify a range of macOS versions."
|
||||
|
||||
sig { override.params(cask_block: RuboCop::Cask::AST::CaskBlock).void }
|
||||
def on_cask(cask_block)
|
||||
message = "Do not use a top-level `%<stanza>s` stanza as the default. " \
|
||||
"Add it to an `on_{system}` block instead. " \
|
||||
"Use `:or_older` or `:or_newer` to specify a range of macOS versions."
|
||||
cask_stanzas = cask_block.toplevel_stanzas
|
||||
|
||||
return if (on_blocks = on_system_methods(cask_stanzas)).none?
|
||||
@ -31,12 +31,14 @@ module RuboCop
|
||||
# Skip if the stanza outside of a block is not also in an `on_*` block.
|
||||
next unless stanzas_in_blocks.include?(stanza.stanza_name)
|
||||
|
||||
add_offense(stanza.source_range, message: format(MESSAGE, stanza: stanza.stanza_name))
|
||||
add_offense(stanza.source_range, message: format(message, stanza: stanza.stanza_name))
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(on_system: T::Array[RuboCop::Cask::AST::Stanza]).returns(T::Set[Symbol]) }
|
||||
def on_system_stanzas(on_system)
|
||||
message = "Do not use a `depends_on macos:` stanza inside an `on_{system}` block. " \
|
||||
"Add it once to specify the oldest macOS supported by any version in the cask."
|
||||
names = T.let(Set.new, T::Set[Symbol])
|
||||
method_nodes = on_system.map(&:method_node)
|
||||
method_nodes.select(&:block_type?).each do |node|
|
||||
@ -51,6 +53,14 @@ module RuboCop
|
||||
end
|
||||
next if RuboCop::Cask::Constants::ON_SYSTEM_METHODS.include?(send_node.method_name)
|
||||
|
||||
if send_node.method_name == :depends_on &&
|
||||
send_node.arguments.first.pairs.any? { |a| a.key.value == :macos } &&
|
||||
OnSystemConditionalsHelper::ON_SYSTEM_OPTIONS.map do |m|
|
||||
:"on_#{m}"
|
||||
end.include?(T.cast(node, RuboCop::AST::BlockNode).method_name)
|
||||
add_offense(send_node.source_range, message:)
|
||||
end
|
||||
|
||||
names.add(send_node.method_name)
|
||||
end
|
||||
end
|
||||
|
@ -41,6 +41,15 @@ module RuboCop
|
||||
|
||||
return unless hash_node.hash_type?
|
||||
|
||||
unless stanza_node.source.match?(/",\n *\w+:/)
|
||||
add_offense(
|
||||
stanza_node.source_range,
|
||||
message: "Keyword URL parameter should be on a new indented line.",
|
||||
) do |corrector|
|
||||
corrector.replace(stanza_node.source_range, stanza_node.source.gsub(/",\s*/, "\",\n "))
|
||||
end
|
||||
end
|
||||
|
||||
hash_node.each_pair do |key_node, value_node|
|
||||
next if key_node.source != "verified"
|
||||
next unless value_node.str_type?
|
||||
|
@ -30,7 +30,7 @@ module RuboCop
|
||||
def audit_formula(_formula_nodes)
|
||||
caveats_strings.each do |n|
|
||||
if regex_match_group(n, /\bsetuid\b/i)
|
||||
problem "Don't recommend `setuid` in the caveats, suggest `sudo` instead."
|
||||
problem "Instead of recommending `setuid` in the caveats, suggest `sudo`."
|
||||
end
|
||||
|
||||
problem "Don't use ANSI escape codes in the caveats." if regex_match_group(n, /\e/)
|
||||
|
@ -28,17 +28,17 @@ module RuboCop
|
||||
return if checksum.nil?
|
||||
|
||||
if regex_match_group(checksum, /^$/)
|
||||
problem "sha256 is empty"
|
||||
problem "`sha256` is empty"
|
||||
return
|
||||
end
|
||||
|
||||
if string_content(checksum).size != 64 && regex_match_group(checksum, /^\w*$/)
|
||||
problem "sha256 should be 64 characters"
|
||||
problem "`sha256` should be 64 characters"
|
||||
end
|
||||
|
||||
return unless regex_match_group(checksum, /[^a-f0-9]+/i)
|
||||
|
||||
add_offense(T.must(@offensive_source_range), message: "sha256 contains invalid characters")
|
||||
add_offense(T.must(@offensive_source_range), message: "`sha256` contains invalid characters")
|
||||
end
|
||||
end
|
||||
|
||||
@ -54,7 +54,7 @@ module RuboCop
|
||||
next if checksum.nil?
|
||||
next unless regex_match_group(checksum, /[A-F]+/)
|
||||
|
||||
add_offense(@offensive_source_range, message: "sha256 should be lowercase") do |corrector|
|
||||
add_offense(@offensive_source_range, message: "`sha256` should be lowercase") do |corrector|
|
||||
correction = T.must(@offensive_node).source.downcase
|
||||
corrector.insert_before(T.must(@offensive_node).source_range, correction)
|
||||
corrector.remove(T.must(@offensive_node).source_range)
|
||||
|
@ -23,7 +23,7 @@ module RuboCop
|
||||
parent_class = class_name(parent_class_node)
|
||||
return unless DEPRECATED_CLASSES.include?(parent_class)
|
||||
|
||||
problem "#{parent_class} is deprecated, use Formula instead" do |corrector|
|
||||
problem "`#{parent_class}` is deprecated, use `Formula` instead" do |corrector|
|
||||
corrector.replace(parent_class_node.source_range, "Formula")
|
||||
end
|
||||
end
|
||||
@ -49,14 +49,14 @@ module RuboCop
|
||||
p1, p2 = params
|
||||
if (match = string_content(p1).match(%r{(/usr/local/(s?bin))}))
|
||||
offending_node(p1)
|
||||
problem "use \#{#{match[2]}} instead of #{match[1]} in #{node}" do |corrector|
|
||||
problem "Use `\#{#{match[2]}}` instead of `#{match[1]}` in `#{node}`" do |corrector|
|
||||
corrector.replace(p1.source_range, p1.source.sub(match[1], "\#{#{match[2]}}"))
|
||||
end
|
||||
end
|
||||
|
||||
if node == :shell_output && node_equals?(p2, 0)
|
||||
offending_node(p2)
|
||||
problem "Passing 0 to shell_output() is redundant" do |corrector|
|
||||
problem "Passing 0 to `shell_output` is redundant" do |corrector|
|
||||
corrector.remove(range_with_surrounding_comma(range_with_surrounding_space(range: p2.source_range,
|
||||
side: :left)))
|
||||
end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user