Merge branch 'master' into mlh-outdated-packages
This commit is contained in:
commit
ad2809b615
6
.github/workflows/docker.yml
vendored
6
.github/workflows/docker.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
version: ["16.04", "18.04", "20.04"]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
uses: actions/checkout@main
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
@ -23,8 +23,8 @@ jobs:
|
||||
run: git fetch origin master
|
||||
- name: Build Docker image
|
||||
run: docker build -t brew --build-arg=version=${{matrix.version}} .
|
||||
- name: Run brew test-bot
|
||||
run: docker run --rm brew brew test-bot
|
||||
- name: Run brew test-bot --only-setup
|
||||
run: docker run --rm brew brew test-bot --only-setup
|
||||
- name: Deploy the tagged Docker image to GitHub
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
|
||||
26
.github/workflows/doctor.yml
vendored
26
.github/workflows/doctor.yml
vendored
@ -8,6 +8,9 @@ on:
|
||||
- Library/Homebrew/extend/os/diagnostic.rb
|
||||
- Library/Homebrew/extend/os/mac/diagnostic.rb
|
||||
- Library/Homebrew/os/mac/xcode.rb
|
||||
env:
|
||||
HOMEBREW_DEVELOPER: 1
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
jobs:
|
||||
tests:
|
||||
strategy:
|
||||
@ -17,21 +20,10 @@ jobs:
|
||||
runs-on: ${{ matrix.version }}
|
||||
env:
|
||||
PATH: '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
|
||||
HOMEBREW_DEVELOPER: 1
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
steps:
|
||||
- name: Update Homebrew
|
||||
run: brew update-reset
|
||||
|
||||
- name: Set up Git repository
|
||||
run: |
|
||||
cd $(brew --repo)
|
||||
git clean -ffdx
|
||||
git fetch --prune --force origin master
|
||||
git fetch --prune --force origin ${{github.sha}}
|
||||
git checkout --force ${{github.sha}}
|
||||
git log -1
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Run brew test-bot --only-cleanup-before
|
||||
run: brew test-bot --only-cleanup-before
|
||||
@ -42,9 +34,3 @@ jobs:
|
||||
- name: Run brew test-bot --only-cleanup-after
|
||||
if: always()
|
||||
run: brew test-bot --only-cleanup-after
|
||||
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
run: |
|
||||
find .
|
||||
rm -rf *
|
||||
|
||||
22
.github/workflows/spdx.yml
vendored
22
.github/workflows/spdx.yml
vendored
@ -1,30 +1,24 @@
|
||||
name: Update license data
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/spdx.yml
|
||||
schedule:
|
||||
- cron: '0 */12 * * *'
|
||||
|
||||
jobs:
|
||||
spdx:
|
||||
if: github.repository == 'Homebrew/brew'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Configure Git
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@master
|
||||
with:
|
||||
username: BrewTestBot
|
||||
|
||||
- name: Setup Homebrew
|
||||
run: |
|
||||
HOMEBREW_REPOSITORY="$(brew --repo)"
|
||||
rm -rf "$HOMEBREW_REPOSITORY"
|
||||
ln -s "$GITHUB_WORKSPACE" "$HOMEBREW_REPOSITORY"
|
||||
|
||||
- name: Update license data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
121
.github/workflows/tests.yml
vendored
121
.github/workflows/tests.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
branches: master
|
||||
pull_request: []
|
||||
env:
|
||||
HOMEBREW_GITHUB_ACTIONS: 1
|
||||
HOMEBREW_DEVELOPER: 1
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
jobs:
|
||||
tests:
|
||||
@ -16,72 +16,29 @@ jobs:
|
||||
steps:
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
run: |
|
||||
if which brew &>/dev/null; then
|
||||
HOMEBREW_PREFIX="$(brew --prefix)"
|
||||
HOMEBREW_REPOSITORY="$HOMEBREW_PREFIX/Homebrew"
|
||||
else
|
||||
HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew
|
||||
HOMEBREW_REPOSITORY="$HOMEBREW_PREFIX/Homebrew"
|
||||
sudo mkdir -p "$HOMEBREW_REPOSITORY/Library/Taps"
|
||||
git -C "$HOMEBREW_REPOSITORY" init
|
||||
git -C "$HOMEBREW_REPOSITORY" remote add origin "https://github.com/$GITHUB_REPOSITORY"
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
cd "$HOMEBREW_PREFIX"
|
||||
sudo mkdir -p bin etc include lib opt sbin share var/homebrew/linked Cellar
|
||||
sudo ln -sf ../Homebrew/bin/brew "$HOMEBREW_PREFIX/bin/"
|
||||
cd -
|
||||
fi
|
||||
|
||||
export PATH="$HOMEBREW_PREFIX/bin:$PATH"
|
||||
echo "::add-path::$HOMEBREW_PREFIX/bin"
|
||||
|
||||
cd "$HOMEBREW_REPOSITORY"
|
||||
rm -rf "$GITHUB_WORKSPACE"
|
||||
ln -s "$HOMEBREW_REPOSITORY" "$GITHUB_WORKSPACE"
|
||||
git fetch --tags origin "${{github.sha}}"
|
||||
git checkout --force -B master FETCH_HEAD
|
||||
brew update-reset Library/Taps/homebrew/homebrew-core
|
||||
cd -
|
||||
|
||||
GEMS_HASH=$(shasum -a 256 "$HOMEBREW_REPOSITORY/Library/Homebrew/Gemfile.lock" | cut -f1 -d' ')
|
||||
echo "::set-output name=gems-hash::$GEMS_HASH"
|
||||
|
||||
if [ "$RUNNER_OS" = "Linux" ]; then
|
||||
sudo chown -R "$USER" "$HOMEBREW_PREFIX"
|
||||
fi
|
||||
|
||||
- name: Set up Ruby
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/setup-ruby@main
|
||||
- name: Configure Git user
|
||||
uses: Homebrew/actions/git-user-config@master
|
||||
with:
|
||||
ruby-version: '2.6'
|
||||
username: BrewTestBot
|
||||
|
||||
- name: Run brew test-bot --only-cleanup-before
|
||||
run: brew test-bot --only-cleanup-before
|
||||
|
||||
- name: Run brew config
|
||||
run: brew config
|
||||
|
||||
- name: Run brew doctor
|
||||
run: |
|
||||
# minimally fix brew doctor failures (a full clean takes ~5m)
|
||||
if [ "$RUNNER_OS" = "Linux" ]; then
|
||||
sudo rm -rf /usr/local/include/node/
|
||||
else
|
||||
# link old formulae
|
||||
brew link --overwrite --force gettext python@3.8
|
||||
|
||||
# remove deleted formula
|
||||
brew uninstall --force python@2
|
||||
fi
|
||||
brew doctor
|
||||
run: brew doctor
|
||||
|
||||
- name: Cache Bundler RubyGems
|
||||
id: cache
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@main
|
||||
with:
|
||||
path: Library/Homebrew/vendor/bundle/ruby/
|
||||
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
|
||||
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-rubygems-
|
||||
restore-keys: ${{ runner.os }}-rubygems-
|
||||
|
||||
- name: Install Bundler RubyGems
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
@ -90,31 +47,19 @@ jobs:
|
||||
- name: Check for uncommitted RubyGems
|
||||
run: git diff --stat --exit-code Library/Homebrew/vendor/bundle/ruby
|
||||
|
||||
- name: Install taps
|
||||
- name: Set up Homebrew official command taps
|
||||
run: |
|
||||
# Install taps needed for 'brew tests' and 'brew man'
|
||||
cd "$(brew --repo)"
|
||||
# Setup taps needed for 'brew tests' and 'brew man'
|
||||
brew tap homebrew/bundle
|
||||
brew update-reset Library/Taps/homebrew/homebrew-bundle
|
||||
brew tap homebrew/services
|
||||
brew update-reset Library/Taps/homebrew/homebrew-services
|
||||
brew tap homebrew/test-bot
|
||||
brew update-reset Library/Taps/homebrew/homebrew-test-bot
|
||||
cd "$(brew --repo)"
|
||||
|
||||
if [ "$RUNNER_OS" = "macOS" ]; then
|
||||
brew tap homebrew/cask
|
||||
brew update-reset Library/Taps/homebrew/homebrew-cask
|
||||
|
||||
# don't care about `brew cask style` here.
|
||||
brew untap adoptopenjdk/openjdk
|
||||
|
||||
# don't care about `brew audit` here.
|
||||
brew untap mongodb/brew
|
||||
brew update-reset Library/Taps/homebrew/homebrew-cask Library/Taps/homebrew/homebrew-services
|
||||
else
|
||||
# Fix permissions for 'brew tests'
|
||||
sudo chmod -R g-w,o-w /home/linuxbrew /home/runner /opt
|
||||
brew update-reset Library/Taps/homebrew/homebrew-services
|
||||
fi
|
||||
|
||||
- name: Run brew style
|
||||
- name: Run brew style on Homebrew/brew
|
||||
run: brew style --display-cop-names
|
||||
|
||||
- name: Run brew man
|
||||
@ -123,12 +68,9 @@ jobs:
|
||||
- name: Run brew tests
|
||||
run: |
|
||||
# brew tests doesn't like world writable directories
|
||||
umask 022
|
||||
|
||||
# set variables for coverage reporting
|
||||
export HOMEBREW_CI_BUILD_NUMBER="$GITHUB_REF"
|
||||
export HOMEBREW_CI_BRANCH="$HEAD_GITHUB_REF"
|
||||
export HOMEBREW_GITHUB_REPOSITORY="$GITHUB_REPOSITORY"
|
||||
if [[ "$RUNNER_OS" = "Linux" ]]; then
|
||||
sudo chmod -R g-w,o-w /home/linuxbrew/.linuxbrew/Homebrew
|
||||
fi
|
||||
|
||||
# don't bother running all tests on both platforms (for speed)
|
||||
if [ "$RUNNER_OS" = "Linux" ]; then
|
||||
@ -149,8 +91,6 @@ jobs:
|
||||
|
||||
- name: Run brew update-tests
|
||||
run: |
|
||||
git config --global user.name "BrewTestBot"
|
||||
git config --global user.email "homebrew-test-bot@lists.sfconservancy.org"
|
||||
brew update-test
|
||||
brew update-test --to-tag
|
||||
brew update-test --commit=HEAD
|
||||
@ -160,16 +100,27 @@ jobs:
|
||||
run: brew readall --aliases
|
||||
|
||||
- name: Run brew style on homebrew-core
|
||||
if: matrix.os == 'macOS-latest'
|
||||
run: brew style --display-cop-names homebrew/core
|
||||
|
||||
- name: Run brew style on linuxbrew-core
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: brew style --display-cop-names homebrew/core
|
||||
|
||||
- name: Run brew style on official taps
|
||||
run: brew style --display-cop-names homebrew/bundle homebrew/services homebrew/test-bot
|
||||
|
||||
- name: Run brew cask style
|
||||
- name: Run brew cask style on all taps
|
||||
if: matrix.os == 'macOS-latest'
|
||||
run: brew cask style
|
||||
run: |
|
||||
brew tap homebrew/cask
|
||||
brew tap homebrew/cask-drivers
|
||||
brew tap homebrew/cask-fonts
|
||||
brew tap homebrew/cask-versions
|
||||
|
||||
- name: Run brew audit
|
||||
brew cask style
|
||||
|
||||
- name: Run brew audit --skip-style on all taps
|
||||
run: brew audit --skip-style
|
||||
|
||||
- name: Run vale for docs linting
|
||||
|
||||
@ -161,7 +161,6 @@ Lint/AmbiguousRegexpLiteral:
|
||||
Lint/ParenthesesAsGroupedExpression:
|
||||
Enabled: false
|
||||
|
||||
|
||||
# most metrics don't make sense to apply for formulae/taps
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
inherit_from: ./Homebrew/.rubocop.yml
|
||||
|
||||
Cask/Desc:
|
||||
Description: 'Ensure that the desc stanza conforms to various content and style checks.'
|
||||
Enabled: true
|
||||
|
||||
Cask/HomepageMatchesUrl:
|
||||
Description: 'Ensure that the homepage and url match, otherwise add a comment. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#when-url-and-homepage-hostnames-differ-add-a-comment'
|
||||
Enabled: true
|
||||
@ -13,13 +17,15 @@ Cask/NoDslVersion:
|
||||
Enabled: true
|
||||
|
||||
Cask/StanzaGrouping:
|
||||
Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order'
|
||||
Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order'
|
||||
Enabled: true
|
||||
|
||||
Cask/StanzaOrder:
|
||||
Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order'
|
||||
Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order'
|
||||
Enabled: true
|
||||
|
||||
# don't want this for casks but re-enabled for Library/Homebrew
|
||||
# don't want these for casks but re-enabled for Library/Homebrew
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
|
||||
@ -37,30 +37,33 @@ Lint/ParenthesesAsGroupedExpression:
|
||||
# TODO: try to bring down all metrics maximums
|
||||
Metrics/AbcSize:
|
||||
Enabled: true
|
||||
Max: 275
|
||||
Max: 250
|
||||
Metrics/BlockLength:
|
||||
Enabled: true
|
||||
Max: 1100
|
||||
Max: 100
|
||||
Exclude:
|
||||
- 'test/formula_spec.rb'
|
||||
- 'test/**/*'
|
||||
Metrics/BlockNesting:
|
||||
Enabled: true
|
||||
Max: 5
|
||||
Metrics/ClassLength:
|
||||
Enabled: true
|
||||
Max: 1400
|
||||
Max: 800
|
||||
Exclude:
|
||||
- 'formula.rb'
|
||||
- 'formula_installer.rb'
|
||||
Metrics/CyclomaticComplexity:
|
||||
Enabled: true
|
||||
Max: 85
|
||||
Max: 80
|
||||
Metrics/MethodLength:
|
||||
Enabled: true
|
||||
Max: 300
|
||||
Max: 260
|
||||
Metrics/ModuleLength:
|
||||
Enabled: true
|
||||
Max: 550
|
||||
Max: 600
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: true
|
||||
Max: 100
|
||||
Max: 80
|
||||
|
||||
# we won't change backward compatible predicate names
|
||||
Naming/PredicateName:
|
||||
|
||||
@ -13,11 +13,9 @@ gem "rspec-retry", require: false
|
||||
gem "rspec-wait", require: false
|
||||
gem "rubocop"
|
||||
gem "simplecov", require: false
|
||||
if ENV["HOMEBREW_SORBET"]
|
||||
gem "sorbet", "0.5.5823"
|
||||
gem "sorbet-runtime", "0.5.5823"
|
||||
gem "tapioca"
|
||||
end
|
||||
gem "sorbet"
|
||||
gem "sorbet-runtime"
|
||||
gem "tapioca"
|
||||
|
||||
# vendored gems
|
||||
gem "activesupport"
|
||||
|
||||
@ -10,12 +10,15 @@ GEM
|
||||
ast (2.4.1)
|
||||
bindata (2.4.8)
|
||||
byebug (11.1.3)
|
||||
codecov (0.2.3)
|
||||
codecov (0.2.5)
|
||||
colorize
|
||||
json
|
||||
simplecov
|
||||
coderay (1.1.3)
|
||||
colorize (0.8.1)
|
||||
concurrent-ruby (1.1.6)
|
||||
commander (4.5.2)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.1.7)
|
||||
connection_pool (2.2.3)
|
||||
diff-lcs (1.4.4)
|
||||
docile (1.3.2)
|
||||
@ -23,6 +26,7 @@ GEM
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
elftools (1.1.2)
|
||||
bindata (~> 2)
|
||||
highline (2.0.3)
|
||||
hpricot (0.8.6)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
@ -38,6 +42,7 @@ GEM
|
||||
nokogiri (~> 1.6)
|
||||
ntlm-http (~> 0.1, >= 0.1.1)
|
||||
webrobots (>= 0.0.9, < 0.2)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2020.0512)
|
||||
@ -53,13 +58,21 @@ GEM
|
||||
parallel (1.19.2)
|
||||
parallel_tests (3.1.0)
|
||||
parallel
|
||||
parlour (4.0.1)
|
||||
commander (~> 4.5)
|
||||
parser
|
||||
rainbow (~> 3.0)
|
||||
sorbet-runtime (>= 0.5)
|
||||
parser (2.7.1.4)
|
||||
ast (~> 2.4.1)
|
||||
patchelf (1.2.0)
|
||||
elftools (~> 1.1)
|
||||
plist (3.5.0)
|
||||
pry (0.13.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
rainbow (3.0.0)
|
||||
rdiscount (2.2.0.1)
|
||||
rdiscount (2.2.0.2)
|
||||
regexp_parser (1.7.1)
|
||||
rexml (3.2.4)
|
||||
ronn (0.7.3)
|
||||
@ -107,6 +120,17 @@ GEM
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.12.2)
|
||||
sorbet (0.5.5866)
|
||||
sorbet-static (= 0.5.5866)
|
||||
sorbet-runtime (0.5.5866)
|
||||
sorbet-static (0.5.5866-universal-darwin-14)
|
||||
tapioca (0.4.1)
|
||||
parlour (>= 2.1.0)
|
||||
pry (>= 0.12.2)
|
||||
sorbet-runtime
|
||||
sorbet-static (>= 0.4.4471)
|
||||
thor (>= 0.19.2)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tzinfo (1.2.7)
|
||||
thread_safe (~> 0.1)
|
||||
@ -139,6 +163,9 @@ DEPENDENCIES
|
||||
rubocop-rspec
|
||||
ruby-macho
|
||||
simplecov
|
||||
sorbet
|
||||
sorbet-runtime
|
||||
tapioca
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.3
|
||||
1.17.2
|
||||
|
||||
@ -4,6 +4,8 @@ require "utils/curl"
|
||||
require "json"
|
||||
|
||||
class Bintray
|
||||
include Context
|
||||
|
||||
API_URL = "https://api.bintray.com"
|
||||
|
||||
class Error < RuntimeError
|
||||
@ -32,8 +34,8 @@ class Bintray
|
||||
end
|
||||
|
||||
curl(*args, url,
|
||||
show_output: Homebrew.args.verbose?,
|
||||
secrets: @bintray_key)
|
||||
show_output: verbose?,
|
||||
secrets: key)
|
||||
end
|
||||
|
||||
def upload(local_file, repo:, package:, version:, remote_file:, sha256: nil)
|
||||
|
||||
@ -1,160 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "utils"
|
||||
require "formula_info"
|
||||
|
||||
class BottlePublisher
|
||||
def initialize(tap, changed_formulae_names, bintray_org, no_publish, warn_on_publish_failure)
|
||||
@tap = tap
|
||||
@changed_formulae_names = changed_formulae_names
|
||||
@no_publish = no_publish
|
||||
@bintray_org = bintray_org
|
||||
@warn_on_publish_failure = warn_on_publish_failure
|
||||
end
|
||||
|
||||
def publish_and_check_bottles
|
||||
# Formulae with affected bottles that were published
|
||||
bintray_published_formulae = []
|
||||
|
||||
# Publish bottles on Bintray
|
||||
unless @no_publish
|
||||
published = publish_changed_formula_bottles
|
||||
bintray_published_formulae.concat(published)
|
||||
end
|
||||
|
||||
# Verify bintray publishing after all patches have been applied
|
||||
bintray_published_formulae.uniq!
|
||||
verify_bintray_published(bintray_published_formulae)
|
||||
end
|
||||
|
||||
def publish_changed_formula_bottles
|
||||
raise "Need to load formulae to publish them!" if Homebrew::EnvConfig.disable_load_formula?
|
||||
|
||||
published = []
|
||||
bintray_creds = { user: Homebrew::EnvConfig.bintray_user, key: Homebrew::EnvConfig.bintray_key }
|
||||
if bintray_creds[:user] && bintray_creds[:key]
|
||||
@changed_formulae_names.each do |name|
|
||||
f = Formula[name]
|
||||
next if f.bottle_unneeded? || f.bottle_disabled?
|
||||
|
||||
bintray_org = @bintray_org || @tap.user.downcase
|
||||
next unless publish_bottle_file_on_bintray(f, bintray_org, bintray_creds)
|
||||
|
||||
published << f.full_name
|
||||
end
|
||||
else
|
||||
opoo "You must set HOMEBREW_BINTRAY_USER and HOMEBREW_BINTRAY_KEY to add or update bottles on Bintray!"
|
||||
end
|
||||
published
|
||||
end
|
||||
|
||||
# Publishes the current bottle files for a given formula to Bintray
|
||||
def publish_bottle_file_on_bintray(f, bintray_org, creds)
|
||||
repo = Utils::Bottles::Bintray.repository(f.tap)
|
||||
package = Utils::Bottles::Bintray.package(f.name)
|
||||
info = FormulaInfo.lookup(f.full_name)
|
||||
raise "Failed publishing bottle: failed reading formula info for #{f.full_name}" if info.nil?
|
||||
|
||||
unless info.bottle_info_any
|
||||
opoo "No bottle defined in formula #{package}"
|
||||
return false
|
||||
end
|
||||
version = info.pkg_version
|
||||
ohai "Publishing on Bintray: #{package} #{version}"
|
||||
curl "--write-out", '\n', "--silent", "--fail",
|
||||
"--user", "#{creds[:user]}:#{creds[:key]}", "--request", "POST",
|
||||
"--header", "Content-Type: application/json",
|
||||
"--data", '{"publish_wait_for_secs": 0}',
|
||||
"https://api.bintray.com/content/#{bintray_org}/#{repo}/#{package}/#{version}/publish"
|
||||
true
|
||||
rescue => e
|
||||
raise unless @warn_on_publish_failure
|
||||
|
||||
onoe e
|
||||
false
|
||||
end
|
||||
|
||||
# Verifies that formulae have been published on Bintray by downloading a bottle file
|
||||
# for each one. Blocks until the published files are available.
|
||||
# Raises an error if the verification fails.
|
||||
# This does not currently work for `brew pull`, because it may have cached the old
|
||||
# version of a formula.
|
||||
def verify_bintray_published(formulae_names)
|
||||
return if formulae_names.empty?
|
||||
|
||||
raise "Need to load formulae to verify their publication!" if Homebrew::EnvConfig.disable_load_formula?
|
||||
|
||||
ohai "Verifying bottles published on Bintray"
|
||||
formulae = formulae_names.map { |n| Formula[n] }
|
||||
max_retries = 300 # shared among all bottles
|
||||
poll_retry_delay_seconds = 2
|
||||
|
||||
HOMEBREW_CACHE.cd do
|
||||
formulae.each do |f|
|
||||
retry_count = 0
|
||||
wrote_dots = false
|
||||
# Choose arbitrary bottle just to get the host/port for Bintray right
|
||||
jinfo = FormulaInfo.lookup(f.full_name)
|
||||
unless jinfo
|
||||
opoo "Cannot publish bottle: Failed reading info for formula #{f.full_name}"
|
||||
next
|
||||
end
|
||||
bottle_info = jinfo.bottle_info_any
|
||||
unless bottle_info
|
||||
opoo "No bottle defined in formula #{f.full_name}"
|
||||
next
|
||||
end
|
||||
|
||||
# Poll for publication completion using a quick partial HEAD, to avoid spurious error messages
|
||||
# 401 error is normal while file is still in async publishing process
|
||||
url = URI(bottle_info["url"])
|
||||
puts "Verifying bottle: #{File.basename(url.path)}"
|
||||
http = Net::HTTP.new(url.host, url.port)
|
||||
http.use_ssl = true
|
||||
retry_count = 0
|
||||
http.start do
|
||||
loop do
|
||||
req = Net::HTTP::Head.new url
|
||||
req.initialize_http_header "User-Agent" => HOMEBREW_USER_AGENT_RUBY
|
||||
res = http.request req
|
||||
break if res.is_a?(Net::HTTPSuccess) || res.code == "302"
|
||||
|
||||
unless res.is_a?(Net::HTTPClientError)
|
||||
raise "Failed to find published #{f} bottle at #{url} (#{res.code} #{res.message})!"
|
||||
end
|
||||
|
||||
raise "Failed to find published #{f} bottle at #{url}!" if retry_count >= max_retries
|
||||
|
||||
print(wrote_dots ? "." : "Waiting on Bintray.")
|
||||
wrote_dots = true
|
||||
sleep poll_retry_delay_seconds
|
||||
retry_count += 1
|
||||
end
|
||||
end
|
||||
|
||||
# Actual download and verification
|
||||
# We do a retry on this, too, because sometimes the external curl will fail even
|
||||
# when the prior HEAD has succeeded.
|
||||
puts "\n" if wrote_dots
|
||||
filename = File.basename(url.path)
|
||||
curl_retry_delay_seconds = 4
|
||||
max_curl_retries = 1
|
||||
retry_count = 0
|
||||
# We're in the cache; make sure to force re-download
|
||||
loop do
|
||||
curl_download url, to: filename
|
||||
break
|
||||
rescue
|
||||
raise "Failed to download #{f} bottle from #{url}!" if retry_count >= max_curl_retries
|
||||
|
||||
puts "curl download failed; retrying in #{curl_retry_delay_seconds} sec"
|
||||
sleep curl_retry_delay_seconds
|
||||
curl_retry_delay_seconds *= 2
|
||||
retry_count += 1
|
||||
end
|
||||
checksum = Checksum.new(:sha256, bottle_info["sha256"])
|
||||
Pathname.new(filename).verify_checksum(checksum)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -59,7 +59,9 @@ begin
|
||||
|
||||
ARGV.delete_at(help_cmd_index) if help_cmd_index
|
||||
|
||||
Homebrew.args = Homebrew::CLI::Parser.new.parse(ARGV.dup.freeze, ignore_invalid_options: true)
|
||||
args = Homebrew::CLI::Parser.new.parse(ARGV.dup.freeze, ignore_invalid_options: true)
|
||||
Homebrew.args = args
|
||||
Context.current = args.context
|
||||
|
||||
path = PATH.new(ENV["PATH"])
|
||||
homebrew_path = PATH.new(ENV["HOMEBREW_PATH"])
|
||||
@ -102,8 +104,8 @@ begin
|
||||
# - if cmd is Cask, let Cask handle the help command instead
|
||||
if (empty_argv || help_flag) && cmd != "cask"
|
||||
require "help"
|
||||
Homebrew::Help.help cmd, empty_argv: empty_argv
|
||||
# `Homebrew.help` never returns, except for unknown commands.
|
||||
Homebrew::Help.help cmd, remaining_args: args.remaining, empty_argv: empty_argv
|
||||
# `Homebrew::Help.help` never returns, except for unknown commands.
|
||||
end
|
||||
|
||||
if internal_cmd || Commands.external_ruby_v2_cmd_path(cmd)
|
||||
@ -138,17 +140,17 @@ begin
|
||||
end
|
||||
rescue UsageError => e
|
||||
require "help"
|
||||
Homebrew::Help.help cmd, usage_error: e.message
|
||||
Homebrew::Help.help cmd, remaining_args: args.remaining, usage_error: e.message
|
||||
rescue SystemExit => e
|
||||
onoe "Kernel.exit" if Homebrew.args.debug? && !e.success?
|
||||
$stderr.puts e.backtrace if Homebrew.args.debug?
|
||||
onoe "Kernel.exit" if args.debug? && !e.success?
|
||||
$stderr.puts e.backtrace if args.debug?
|
||||
raise
|
||||
rescue Interrupt
|
||||
$stderr.puts # seemingly a newline is typical
|
||||
exit 130
|
||||
rescue BuildError => e
|
||||
Utils::Analytics.report_build_error(e)
|
||||
e.dump
|
||||
e.dump(verbose: args.verbose?)
|
||||
|
||||
if e.formula.head? || e.formula.deprecated? || e.formula.disabled?
|
||||
$stderr.puts <<~EOS
|
||||
@ -162,7 +164,7 @@ rescue RuntimeError, SystemCallError => e
|
||||
raise if e.message.empty?
|
||||
|
||||
onoe e
|
||||
$stderr.puts e.backtrace if Homebrew.args.debug?
|
||||
$stderr.puts e.backtrace if args.debug?
|
||||
|
||||
exit 1
|
||||
rescue MethodDeprecatedError => e
|
||||
@ -171,7 +173,7 @@ rescue MethodDeprecatedError => e
|
||||
$stderr.puts "If reporting this issue please do so at (not Homebrew/brew or Homebrew/core):"
|
||||
$stderr.puts " #{Formatter.url(e.issues_url)}"
|
||||
end
|
||||
$stderr.puts e.backtrace if Homebrew.args.debug?
|
||||
$stderr.puts e.backtrace if args.debug?
|
||||
exit 1
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
onoe e
|
||||
|
||||
@ -41,6 +41,7 @@ case "$*" in
|
||||
--prefix) echo "$HOMEBREW_PREFIX"; exit 0 ;;
|
||||
--cellar) echo "$HOMEBREW_CELLAR"; exit 0 ;;
|
||||
--repository|--repo) echo "$HOMEBREW_REPOSITORY"; exit 0 ;;
|
||||
--caskroom) echo "$HOMEBREW_PREFIX/Caskroom"; exit 0 ;;
|
||||
esac
|
||||
|
||||
# A depth of 1 means this command was directly invoked by a user.
|
||||
@ -283,11 +284,6 @@ export HOMEBREW_USER_AGENT
|
||||
export HOMEBREW_USER_AGENT_CURL
|
||||
export HOMEBREW_BOTTLE_DEFAULT_DOMAIN
|
||||
|
||||
if [[ -n "$HOMEBREW_DEVELOPER" ]] && [[ -z "$HOMEBREW_NO_PATCHELF_RB" ]]
|
||||
then
|
||||
export HOMEBREW_PATCHELF_RB="1"
|
||||
fi
|
||||
|
||||
if [[ -n "$HOMEBREW_MACOS" && -x "/usr/bin/xcode-select" ]]
|
||||
then
|
||||
XCODE_SELECT_PATH=$('/usr/bin/xcode-select' --print-path 2>/dev/null)
|
||||
|
||||
@ -139,7 +139,11 @@ class Build
|
||||
|
||||
formula.update_head_version
|
||||
|
||||
formula.brew(fetch: false, keep_tmp: args.keep_tmp?, interactive: args.interactive?) do |_formula, _staging|
|
||||
formula.brew(
|
||||
fetch: false,
|
||||
keep_tmp: args.keep_tmp?,
|
||||
interactive: args.interactive?,
|
||||
) do
|
||||
# For head builds, HOMEBREW_FORMULA_PREFIX should include the commit,
|
||||
# which is not known until after the formula has been staged.
|
||||
ENV["HOMEBREW_FORMULA_PREFIX"] = formula.prefix
|
||||
@ -201,16 +205,15 @@ class Build
|
||||
else
|
||||
raise
|
||||
end
|
||||
Keg.new(path).optlink
|
||||
Keg.new(path).optlink(verbose: args.verbose?)
|
||||
rescue
|
||||
raise "#{f.opt_prefix} not present or broken\nPlease reinstall #{f.full_name}. Sorry :("
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
Homebrew.args = Homebrew::CLI::Parser.new.parse(ARGV.dup.freeze, ignore_invalid_options: true)
|
||||
|
||||
args = Homebrew.install_args.parse
|
||||
Context.current = args.context
|
||||
|
||||
error_pipe = UNIXSocket.open(ENV["HOMEBREW_ERROR_PIPE"], &:recv_io)
|
||||
error_pipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
||||
|
||||
@ -1,19 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "hardware"
|
||||
|
||||
require "cask/artifact"
|
||||
require "cask/audit"
|
||||
require "cask/auditor"
|
||||
require "cask/cache"
|
||||
require "cask/cask"
|
||||
require "cask/cask_loader"
|
||||
require "cask/cask"
|
||||
require "cask/caskroom"
|
||||
require "cask/cmd"
|
||||
require "cask/config"
|
||||
require "cask/exceptions"
|
||||
require "cask/denylist"
|
||||
require "cask/download"
|
||||
require "cask/dsl"
|
||||
require "cask/exceptions"
|
||||
require "cask/installer"
|
||||
require "cask/macos"
|
||||
require "cask/metadata"
|
||||
require "cask/pkg"
|
||||
require "cask/quarantine"
|
||||
require "cask/staged"
|
||||
require "cask/topological_hash"
|
||||
require "cask/url"
|
||||
require "cask/utils"
|
||||
require "cask/verify"
|
||||
@ -13,7 +13,7 @@ module Cask
|
||||
|
||||
attr_reader :cask, :commit_range, :download
|
||||
|
||||
attr_predicate :appcast?
|
||||
attr_predicate :appcast?, :new_cask?, :strict?, :online?
|
||||
|
||||
def initialize(cask, appcast: false, download: false, quarantine: nil,
|
||||
token_conflicts: false, online: false, strict: false,
|
||||
@ -34,6 +34,7 @@ module Cask
|
||||
check_required_stanzas
|
||||
check_version
|
||||
check_sha256
|
||||
check_desc
|
||||
check_url
|
||||
check_generic_artifacts
|
||||
check_token_valid
|
||||
@ -279,6 +280,14 @@ module Cask
|
||||
end
|
||||
end
|
||||
|
||||
def check_desc
|
||||
return unless new_cask?
|
||||
|
||||
return if cask.desc.present?
|
||||
|
||||
add_warning "Cask should have a description. Please add a `desc` stanza."
|
||||
end
|
||||
|
||||
def check_url
|
||||
return unless cask.url
|
||||
|
||||
@ -339,7 +348,7 @@ module Cask
|
||||
end
|
||||
|
||||
def check_token_valid
|
||||
return unless @strict
|
||||
return unless strict?
|
||||
|
||||
add_warning "cask token is not lowercase" if cask.token.downcase!
|
||||
|
||||
@ -365,7 +374,7 @@ module Cask
|
||||
end
|
||||
|
||||
def check_token_bad_words
|
||||
return unless @strict
|
||||
return unless strict?
|
||||
|
||||
token = cask.token
|
||||
|
||||
@ -467,8 +476,8 @@ module Cask
|
||||
end
|
||||
|
||||
def get_repo_data(regex)
|
||||
return unless @online
|
||||
return unless @new_cask
|
||||
return unless online?
|
||||
return unless new_cask?
|
||||
|
||||
_, user, repo = *regex.match(cask.url.to_s)
|
||||
_, user, repo = *regex.match(cask.homepage) unless user
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/audit"
|
||||
|
||||
module Cask
|
||||
class Auditor
|
||||
extend Predicable
|
||||
|
||||
@ -155,6 +155,7 @@ module Cask
|
||||
{
|
||||
"token" => token,
|
||||
"name" => name,
|
||||
"desc" => desc,
|
||||
"homepage" => homepage,
|
||||
"url" => url,
|
||||
"appcast" => appcast,
|
||||
|
||||
@ -35,6 +35,8 @@ require "cask/cmd/internal_stanza"
|
||||
|
||||
module Cask
|
||||
class Cmd
|
||||
include Context
|
||||
|
||||
ALIASES = {
|
||||
"ls" => "list",
|
||||
"homepage" => "home",
|
||||
@ -154,7 +156,7 @@ module Cask
|
||||
end
|
||||
rescue CaskError, MethodDeprecatedError, ArgumentError, OptionParser::InvalidOption => e
|
||||
onoe e.message
|
||||
$stderr.puts e.backtrace if Homebrew.args.debug?
|
||||
$stderr.puts e.backtrace if debug?
|
||||
exit 1
|
||||
rescue StandardError, ScriptError, NoMemoryError => e
|
||||
onoe e.message
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "cask/auditor"
|
||||
|
||||
module Cask
|
||||
class Cmd
|
||||
|
||||
@ -24,15 +24,16 @@ module Cask
|
||||
|
||||
def self.template(cask_token)
|
||||
<<~RUBY
|
||||
cask '#{cask_token}' do
|
||||
version ''
|
||||
sha256 ''
|
||||
cask "#{cask_token}" do
|
||||
version ""
|
||||
sha256 ""
|
||||
|
||||
url "https://"
|
||||
name ''
|
||||
homepage ''
|
||||
name ""
|
||||
desc ""
|
||||
homepage ""
|
||||
|
||||
app ''
|
||||
app ""
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
@ -4,13 +4,39 @@ module Cask
|
||||
class Cmd
|
||||
class Reinstall < Install
|
||||
def run
|
||||
casks.each do |cask|
|
||||
Installer.new(cask, binaries: binaries?,
|
||||
self.class.reinstall_casks(
|
||||
*casks,
|
||||
binaries: binaries?,
|
||||
verbose: verbose?,
|
||||
force: force?,
|
||||
skip_cask_deps: skip_cask_deps?,
|
||||
require_sha: require_sha?,
|
||||
quarantine: quarantine?).reinstall
|
||||
quarantine: quarantine?,
|
||||
)
|
||||
end
|
||||
|
||||
def self.reinstall_casks(
|
||||
*casks,
|
||||
verbose: false,
|
||||
force: false,
|
||||
skip_cask_deps: false,
|
||||
binaries: nil,
|
||||
require_sha: nil,
|
||||
quarantine: nil
|
||||
)
|
||||
# TODO: Handle this in `CLI::Parser`.
|
||||
binaries = Homebrew::EnvConfig.cask_opts_binaries? if binaries.nil?
|
||||
quarantine = Homebrew::EnvConfig.cask_opts_quarantine? if quarantine.nil?
|
||||
require_sha = Homebrew::EnvConfig.cask_opts_require_sha? if require_sha.nil?
|
||||
|
||||
casks.each do |cask|
|
||||
Installer.new(cask,
|
||||
binaries: binaries,
|
||||
verbose: verbose,
|
||||
force: force,
|
||||
skip_cask_deps: skip_cask_deps,
|
||||
require_sha: require_sha,
|
||||
quarantine: quarantine).reinstall
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -11,17 +11,28 @@ module Cask
|
||||
end
|
||||
|
||||
def run
|
||||
self.class.uninstall_casks(
|
||||
*casks,
|
||||
binaries: binaries?,
|
||||
verbose: verbose?,
|
||||
force: force?,
|
||||
)
|
||||
end
|
||||
|
||||
def self.uninstall_casks(*casks, verbose: false, force: false, binaries: nil)
|
||||
binaries = Homebrew::EnvConfig.cask_opts_binaries? if binaries.nil?
|
||||
|
||||
casks.each do |cask|
|
||||
odebug "Uninstalling Cask #{cask}"
|
||||
|
||||
raise CaskNotInstalledError, cask unless cask.installed? || force?
|
||||
raise CaskNotInstalledError, cask unless cask.installed? || force
|
||||
|
||||
if cask.installed? && !cask.installed_caskfile.nil?
|
||||
# use the same cask file that was used for installation, if possible
|
||||
cask = CaskLoader.load(cask.installed_caskfile) if cask.installed_caskfile.exist?
|
||||
end
|
||||
|
||||
Installer.new(cask, binaries: binaries?, verbose: verbose?, force: force?).uninstall
|
||||
Installer.new(cask, binaries: binaries, verbose: verbose, force: force).uninstall
|
||||
|
||||
next if (versions = cask.versions).empty?
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "env_config"
|
||||
require "cask/config"
|
||||
|
||||
module Cask
|
||||
@ -17,35 +18,69 @@ module Cask
|
||||
end
|
||||
|
||||
def run
|
||||
outdated_casks = casks(alternative: lambda {
|
||||
Caskroom.casks.select do |cask|
|
||||
cask.outdated?(greedy?)
|
||||
self.class.upgrade_casks(
|
||||
*casks,
|
||||
force: force?,
|
||||
greedy: greedy?,
|
||||
dry_run: dry_run?,
|
||||
binaries: binaries?,
|
||||
quarantine: quarantine?,
|
||||
require_sha: require_sha?,
|
||||
skip_cask_deps: skip_cask_deps?,
|
||||
verbose: verbose?,
|
||||
)
|
||||
end
|
||||
}).select do |cask|
|
||||
raise CaskNotInstalledError, cask unless cask.installed? || force?
|
||||
|
||||
def self.upgrade_casks(
|
||||
*casks,
|
||||
force: false,
|
||||
greedy: false,
|
||||
dry_run: false,
|
||||
skip_cask_deps: false,
|
||||
verbose: false,
|
||||
binaries: nil,
|
||||
quarantine: nil,
|
||||
require_sha: nil
|
||||
)
|
||||
# TODO: Handle this in `CLI::Parser`.
|
||||
binaries = Homebrew::EnvConfig.cask_opts_binaries? if binaries.nil?
|
||||
quarantine = Homebrew::EnvConfig.cask_opts_quarantine? if quarantine.nil?
|
||||
require_sha = Homebrew::EnvConfig.cask_opts_require_sha? if require_sha.nil?
|
||||
|
||||
outdated_casks = if casks.empty?
|
||||
Caskroom.casks.select do |cask|
|
||||
cask.outdated?(greedy)
|
||||
end
|
||||
else
|
||||
casks.select do |cask|
|
||||
raise CaskNotInstalledError, cask unless cask.installed? || force
|
||||
|
||||
cask.outdated?(true)
|
||||
end
|
||||
|
||||
if outdated_casks.empty?
|
||||
oh1 "No Casks to upgrade"
|
||||
return
|
||||
end
|
||||
|
||||
ohai "Casks with `auto_updates` or `version :latest` will not be upgraded" if args.empty? && !greedy?
|
||||
verb = dry_run? ? "Would upgrade" : "Upgrading"
|
||||
return if outdated_casks.empty?
|
||||
|
||||
ohai "Casks with `auto_updates` or `version :latest` will not be upgraded" if casks.empty? && !greedy
|
||||
|
||||
verb = dry_run ? "Would upgrade" : "Upgrading"
|
||||
oh1 "#{verb} #{outdated_casks.count} #{"outdated package".pluralize(outdated_casks.count)}:"
|
||||
|
||||
caught_exceptions = []
|
||||
|
||||
upgradable_casks = outdated_casks.map { |c| [CaskLoader.load(c.installed_caskfile), c] }
|
||||
|
||||
puts upgradable_casks
|
||||
.map { |(old_cask, new_cask)| "#{new_cask.full_name} #{old_cask.version} -> #{new_cask.version}" }
|
||||
.join(", ")
|
||||
return if dry_run?
|
||||
.join("\n")
|
||||
return if dry_run
|
||||
|
||||
upgradable_casks.each do |(old_cask, new_cask)|
|
||||
upgrade_cask(old_cask, new_cask)
|
||||
upgrade_cask(
|
||||
old_cask, new_cask,
|
||||
binaries: binaries, force: force, skip_cask_deps: skip_cask_deps, verbose: verbose,
|
||||
quarantine: quarantine, require_sha: require_sha
|
||||
)
|
||||
rescue => e
|
||||
caught_exceptions << e.exception("#{new_cask.full_name}: #{e}")
|
||||
next
|
||||
@ -56,26 +91,29 @@ module Cask
|
||||
raise caught_exceptions.first if caught_exceptions.count == 1
|
||||
end
|
||||
|
||||
def upgrade_cask(old_cask, new_cask)
|
||||
def self.upgrade_cask(
|
||||
old_cask, new_cask,
|
||||
binaries:, force:, quarantine:, require_sha:, skip_cask_deps:, verbose:
|
||||
)
|
||||
odebug "Started upgrade process for Cask #{old_cask}"
|
||||
old_config = old_cask.config
|
||||
|
||||
old_cask_installer =
|
||||
Installer.new(old_cask, binaries: binaries?,
|
||||
verbose: verbose?,
|
||||
force: force?,
|
||||
Installer.new(old_cask, binaries: binaries,
|
||||
verbose: verbose,
|
||||
force: force,
|
||||
upgrade: true)
|
||||
|
||||
new_cask.config = Config.global.merge(old_config)
|
||||
|
||||
new_cask_installer =
|
||||
Installer.new(new_cask, binaries: binaries?,
|
||||
verbose: verbose?,
|
||||
force: force?,
|
||||
skip_cask_deps: skip_cask_deps?,
|
||||
require_sha: require_sha?,
|
||||
Installer.new(new_cask, binaries: binaries,
|
||||
verbose: verbose,
|
||||
force: force,
|
||||
skip_cask_deps: skip_cask_deps,
|
||||
require_sha: require_sha,
|
||||
upgrade: true,
|
||||
quarantine: quarantine?)
|
||||
quarantine: quarantine)
|
||||
|
||||
started_upgrade = false
|
||||
new_artifacts_installed = false
|
||||
|
||||
@ -64,6 +64,7 @@ module Cask
|
||||
:caveats,
|
||||
:conflicts_with,
|
||||
:container,
|
||||
:desc,
|
||||
:depends_on,
|
||||
:homepage,
|
||||
:language,
|
||||
@ -93,6 +94,10 @@ module Cask
|
||||
@name.concat(args.flatten)
|
||||
end
|
||||
|
||||
def desc(description = nil)
|
||||
set_unique_stanza(:desc, description.nil?) { description }
|
||||
end
|
||||
|
||||
def set_unique_stanza(stanza, should_return)
|
||||
return instance_variable_get("@#{stanza}") if should_return
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/utils"
|
||||
|
||||
module Cask
|
||||
class DSL
|
||||
class Base
|
||||
|
||||
@ -367,11 +367,10 @@ module Cask
|
||||
force: false,
|
||||
).install
|
||||
else
|
||||
FormulaInstaller.new(cask_or_formula).yield_self do |fi|
|
||||
FormulaInstaller.new(cask_or_formula, verbose: verbose?).yield_self do |fi|
|
||||
fi.installed_as_dependency = true
|
||||
fi.installed_on_request = false
|
||||
fi.show_header = true
|
||||
fi.verbose = verbose?
|
||||
fi.prelude
|
||||
fi.fetch
|
||||
fi.install
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "development_tools"
|
||||
require "cask/exceptions"
|
||||
|
||||
module Cask
|
||||
module Quarantine
|
||||
module_function
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
# * sets permissions on executables
|
||||
# * removes unresolved symlinks
|
||||
class Cleaner
|
||||
include Context
|
||||
|
||||
# Create a cleaner for the given formula
|
||||
def initialize(f)
|
||||
@f = f
|
||||
@ -58,7 +60,7 @@ class Cleaner
|
||||
# actual files gets removed correctly.
|
||||
dirs.reverse_each do |d|
|
||||
if d.children.empty?
|
||||
puts "rmdir: #{d} (empty)" if Homebrew.args.verbose?
|
||||
puts "rmdir: #{d} (empty)" if verbose?
|
||||
d.rmdir
|
||||
end
|
||||
end
|
||||
@ -109,7 +111,7 @@ class Cleaner
|
||||
else
|
||||
0444
|
||||
end
|
||||
if Homebrew.args.debug?
|
||||
if debug?
|
||||
old_perms = path.stat.mode & 0777
|
||||
odebug "Fixing #{path} permissions from #{old_perms.to_s(8)} to #{perms.to_s(8)}" if perms != old_perms
|
||||
end
|
||||
|
||||
@ -35,6 +35,7 @@ module Homebrew
|
||||
@resolved_formulae_casks = nil
|
||||
@formulae_paths = nil
|
||||
@casks = nil
|
||||
@loaded_casks = nil
|
||||
@kegs = nil
|
||||
@kegs_casks = nil
|
||||
|
||||
@ -125,6 +126,10 @@ module Homebrew
|
||||
.freeze
|
||||
end
|
||||
|
||||
def loaded_casks
|
||||
@loaded_casks ||= downcased_unique_named.map(&Cask::CaskLoader.method(:load)).freeze
|
||||
end
|
||||
|
||||
def kegs
|
||||
@kegs ||= downcased_unique_named.map do |name|
|
||||
resolve_keg name
|
||||
@ -183,6 +188,10 @@ module Homebrew
|
||||
flag_with_value.delete_prefix(arg_prefix)
|
||||
end
|
||||
|
||||
def context
|
||||
Context::ContextStruct.new(debug: debug?, quiet: quiet?, verbose: verbose?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def option_to_name(option)
|
||||
|
||||
@ -88,9 +88,7 @@ module Homebrew
|
||||
def env?(env)
|
||||
return false if env.blank?
|
||||
|
||||
Homebrew::EnvConfig.send("#{env}?")
|
||||
rescue NoMethodError
|
||||
false
|
||||
Homebrew::EnvConfig.try(:"#{env}?")
|
||||
end
|
||||
|
||||
def usage_banner(text)
|
||||
|
||||
@ -13,7 +13,7 @@ module Homebrew
|
||||
def __cache_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`--cache` [<options>] [<formula>]
|
||||
`--cache` [<options>] [<formula|cask>]
|
||||
|
||||
Display Homebrew's download cache. See also `HOMEBREW_CACHE`.
|
||||
|
||||
|
||||
30
Library/Homebrew/cmd/--caskroom.rb
Normal file
30
Library/Homebrew/cmd/--caskroom.rb
Normal file
@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
def __caskroom_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`--caskroom` [<cask>]
|
||||
|
||||
Display Homebrew's Caskroom path.
|
||||
|
||||
If <cask> is provided, display the location in the Caskroom where <cask>
|
||||
would be installed, without any sort of versioned directory as the last path.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def __caskroom
|
||||
args = __caskroom_args.parse
|
||||
|
||||
if args.loaded_casks.blank?
|
||||
puts Cask::Caskroom.path
|
||||
else
|
||||
args.loaded_casks.each do |cask|
|
||||
puts "#{Cask::Caskroom.path}/#{cask.token}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cask/all"
|
||||
require "cask"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
@ -96,7 +96,7 @@ module Homebrew
|
||||
if args.no_named?
|
||||
raise FormulaUnspecifiedError unless args.installed?
|
||||
|
||||
puts_deps sorted_dependents(Formula.installed + Cask::Caskroom.casks), recursive
|
||||
puts_deps sorted_dependents(Formula.installed + Cask::Caskroom.casks), recursive, args: args
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
require "help"
|
||||
|
||||
module Homebrew
|
||||
def help(cmd = nil, flags = {})
|
||||
Help.help(cmd, flags)
|
||||
def help
|
||||
Help.help
|
||||
end
|
||||
end
|
||||
|
||||
@ -211,7 +211,13 @@ module Homebrew
|
||||
|
||||
puts "From: #{Formatter.url(github_info(f))}"
|
||||
|
||||
puts "License: #{f.license.join(", ")}" if f.license
|
||||
if f.license.present?
|
||||
licenses = f.license
|
||||
.map(&:to_s)
|
||||
.join(", ")
|
||||
.sub("public_domain", "Public Domain")
|
||||
puts "License: #{licenses}"
|
||||
end
|
||||
|
||||
unless f.deps.empty?
|
||||
ohai "Dependencies"
|
||||
|
||||
@ -325,7 +325,8 @@ module Homebrew
|
||||
|
||||
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?,
|
||||
include_test_formulae: args.include_test_formulae,
|
||||
build_from_source_formulae: args.build_from_source_formulae)
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?)
|
||||
fi.options = build_options.used_options
|
||||
fi.env = args.env
|
||||
fi.force = args.force?
|
||||
|
||||
@ -31,10 +31,11 @@ module Homebrew
|
||||
def link
|
||||
args = link_args.parse
|
||||
|
||||
mode = OpenStruct.new
|
||||
|
||||
mode.overwrite = true if args.overwrite?
|
||||
mode.dry_run = true if args.dry_run?
|
||||
options = {
|
||||
overwrite: args.overwrite?,
|
||||
dry_run: args.dry_run?,
|
||||
verbose: args.verbose?,
|
||||
}
|
||||
|
||||
args.kegs.each do |keg|
|
||||
keg_only = Formulary.keg_only?(keg.rack)
|
||||
@ -53,13 +54,13 @@ module Homebrew
|
||||
next
|
||||
end
|
||||
|
||||
if mode.dry_run
|
||||
if mode.overwrite
|
||||
if args.dry_run?
|
||||
if args.overwrite?
|
||||
puts "Would remove:"
|
||||
else
|
||||
puts "Would link:"
|
||||
end
|
||||
keg.link(mode)
|
||||
keg.link(**options)
|
||||
puts_keg_only_path_message(keg) if keg_only
|
||||
next
|
||||
end
|
||||
@ -89,7 +90,7 @@ module Homebrew
|
||||
puts if args.verbose?
|
||||
|
||||
begin
|
||||
n = keg.link(mode)
|
||||
n = keg.link(**options)
|
||||
rescue Keg::LinkError
|
||||
puts
|
||||
raise
|
||||
|
||||
@ -11,9 +11,9 @@ module Homebrew
|
||||
def list_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`list`, `ls` [<options>] [<formula>]
|
||||
`list`, `ls` [<options>] [<formula|cask>]
|
||||
|
||||
List all installed formulae.
|
||||
List all installed formulae or casks
|
||||
|
||||
If <formula> is provided, summarise the paths within its current keg.
|
||||
EOS
|
||||
@ -32,8 +32,10 @@ module Homebrew
|
||||
switch "--pinned",
|
||||
description: "Show the versions of pinned formulae, or only the specified (pinned) "\
|
||||
"formulae if <formula> are provided. See also `pin`, `unpin`."
|
||||
switch "--cask",
|
||||
description: "List casks"
|
||||
switch "--formula", "--formulae",
|
||||
description: "List only formulae."
|
||||
switch "--cask", "--casks",
|
||||
description: "List only casks."
|
||||
# passed through to ls
|
||||
switch "-1",
|
||||
description: "Force output to be one entry per line. " \
|
||||
@ -46,7 +48,9 @@ module Homebrew
|
||||
switch "-t",
|
||||
description: "Sort by time modified, listing most recently modified first."
|
||||
|
||||
["--unbrewed", "--multiple", "--pinned", "-l", "-r", "-t"].each { |flag| conflicts "--cask", flag }
|
||||
["--formula", "--unbrewed", "--multiple", "--pinned", "-l", "-r", "-t"].each do |flag|
|
||||
conflicts "--cask", flag
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ module Homebrew
|
||||
ENV["PATH"] = ENV["HOMEBREW_PATH"]
|
||||
|
||||
if args.no_named?
|
||||
git_log HOMEBREW_REPOSITORY
|
||||
git_log HOMEBREW_REPOSITORY, args: args
|
||||
else
|
||||
path = Formulary.path(args.named.first)
|
||||
tap = Tap.from_path(path)
|
||||
|
||||
@ -12,15 +12,19 @@ module Homebrew
|
||||
def outdated_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`outdated` [<options>] [<formula>]
|
||||
`outdated` [<options>] [<formula>|<cask>]
|
||||
|
||||
List installed formulae that have an updated version available. By default, version
|
||||
List installed casks and formulae that have an updated version available. By default, version
|
||||
information is displayed in interactive shells, and suppressed otherwise.
|
||||
EOS
|
||||
switch "-q", "--quiet",
|
||||
description: "List only the names of outdated kegs (takes precedence over `--verbose`)."
|
||||
switch "-v", "--verbose",
|
||||
description: "Include detailed version information."
|
||||
switch "--formula",
|
||||
description: "Only output outdated formulae."
|
||||
switch "--cask",
|
||||
description: "Only output outdated casks."
|
||||
flag "--json",
|
||||
description: "Print output in JSON format. There are two versions: v1 and v2. " \
|
||||
"v1 is deprecated and is currently the default if no version is specified. " \
|
||||
@ -31,10 +35,6 @@ module Homebrew
|
||||
"updates when a new stable or development version has been released."
|
||||
switch "--greedy",
|
||||
description: "Print outdated casks with `auto_updates` or `version :latest`."
|
||||
switch "--formula",
|
||||
description: "Treat all arguments as formulae."
|
||||
switch "--cask",
|
||||
description: "Treat all arguments as casks."
|
||||
|
||||
conflicts "--quiet", "--verbose", "--json"
|
||||
conflicts "--formula", "--cask"
|
||||
@ -94,7 +94,7 @@ module Homebrew
|
||||
if formula_or_cask.is_a?(Formula)
|
||||
f = formula_or_cask
|
||||
|
||||
if verbose? args: args
|
||||
if verbose?
|
||||
outdated_kegs = f.outdated_kegs(fetch_head: args.fetch_HEAD?)
|
||||
|
||||
current_version = if f.alias_changed?
|
||||
@ -122,7 +122,7 @@ module Homebrew
|
||||
else
|
||||
c = formula_or_cask
|
||||
|
||||
puts c.outdated_info(args.greedy?, verbose?(args: args), false)
|
||||
puts c.outdated_info(args.greedy?, verbose?, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -147,13 +147,13 @@ module Homebrew
|
||||
else
|
||||
c = formula_or_cask
|
||||
|
||||
c.outdated_info(args.greedy?, verbose?(args: args), true)
|
||||
c.outdated_info(args.greedy?, verbose?, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def verbose?(args:)
|
||||
($stdout.tty? || args.verbose?) && !args.quiet?
|
||||
def verbose?
|
||||
($stdout.tty? || super) && !quiet?
|
||||
end
|
||||
|
||||
def json_version(version)
|
||||
|
||||
@ -24,7 +24,7 @@ module Homebrew
|
||||
|
||||
args.resolved_formulae.each do |f|
|
||||
ohai "Postinstalling #{f}"
|
||||
fi = FormulaInstaller.new(f)
|
||||
fi = FormulaInstaller.new(f, debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?)
|
||||
fi.post_install
|
||||
end
|
||||
end
|
||||
|
||||
@ -78,9 +78,14 @@ module Homebrew
|
||||
|
||||
return if casks.blank?
|
||||
|
||||
reinstall_cmd = Cask::Cmd::Reinstall.new(casks)
|
||||
reinstall_cmd.verbose = args.verbose?
|
||||
reinstall_cmd.force = args.force?
|
||||
reinstall_cmd.run
|
||||
Cask::Cmd::Reinstall.reinstall_casks(
|
||||
*casks,
|
||||
binaries: args.binaries?,
|
||||
verbose: args.verbose?,
|
||||
force: args.force?,
|
||||
require_sha: args.require_sha?,
|
||||
skip_cask_deps: args.skip_cask_deps?,
|
||||
quarantine: args.quarantine?,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -36,10 +36,10 @@ module Homebrew
|
||||
If no <text> is provided, list all locally available formulae (including tapped ones).
|
||||
No online search is performed.
|
||||
EOS
|
||||
switch "--formulae",
|
||||
switch "--formula", "--formulae",
|
||||
description: "Without <text>, list all locally available formulae (no online search is performed). " \
|
||||
"With <text>, search online and locally for formulae."
|
||||
switch "--casks",
|
||||
switch "--cask", "--casks",
|
||||
description: "Without <text>, list all locally available casks (including tapped ones, no online " \
|
||||
"search is performed). With <text>, search online and locally for casks."
|
||||
switch "--desc",
|
||||
@ -66,8 +66,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
if args.no_named?
|
||||
if args.casks?
|
||||
raise UsageError, "specifying both --formulae and --casks requires <text>" if args.formulae?
|
||||
if args.cask?
|
||||
raise UsageError, "specifying both --formula and --cask requires <text>" if args.formula?
|
||||
|
||||
puts Formatter.columns(Cask::Cask.to_a.map(&:full_name).sort)
|
||||
else
|
||||
@ -92,8 +92,8 @@ module Homebrew
|
||||
local_casks = search_casks(string_or_regex)
|
||||
remote_casks = remote_results[:casks]
|
||||
all_casks = local_casks + remote_casks
|
||||
print_formulae = args.formulae?
|
||||
print_casks = args.casks?
|
||||
print_formulae = args.formula?
|
||||
print_casks = args.cask?
|
||||
print_formulae = print_casks = true if !print_formulae && !print_casks
|
||||
|
||||
if print_formulae && all_formulae.any?
|
||||
@ -102,7 +102,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
if print_casks && all_casks.any?
|
||||
puts if args.formulae? && all_formulae.any?
|
||||
puts if args.formula? && all_formulae.any?
|
||||
ohai "Casks"
|
||||
puts Formatter.columns(all_casks)
|
||||
end
|
||||
|
||||
@ -49,7 +49,7 @@ module Homebrew
|
||||
|
||||
# Link new version, if not keg-only
|
||||
if Formulary.keg_only?(rack)
|
||||
keg.optlink
|
||||
keg.optlink(verbose: args.verbose?)
|
||||
puts "Opt link created for #{keg}"
|
||||
else
|
||||
puts "#{keg.link} links created for #{keg}"
|
||||
|
||||
@ -69,15 +69,14 @@ module Homebrew
|
||||
puts unless i.zero?
|
||||
info = "#{tap}: "
|
||||
if tap.installed?
|
||||
info += tap.pinned? ? "pinned" : "unpinned"
|
||||
info += ", private" if tap.private?
|
||||
info += if (contents = tap.contents).empty?
|
||||
", no commands/casks/formulae"
|
||||
info += if (contents = tap.contents).blank?
|
||||
"no commands/casks/formulae"
|
||||
else
|
||||
", #{contents.join(", ")}"
|
||||
contents.join(", ")
|
||||
end
|
||||
info += ", private" if tap.private?
|
||||
info += "\n#{tap.path} (#{tap.path.abv})"
|
||||
info += "\nFrom: #{tap.remote.nil? ? "N/A" : tap.remote}"
|
||||
info += "\nFrom: #{tap.remote.blank? ? "N/A" : tap.remote}"
|
||||
else
|
||||
info += "Not installed"
|
||||
end
|
||||
|
||||
@ -5,7 +5,6 @@ require "formula"
|
||||
require "diagnostic"
|
||||
require "migrator"
|
||||
require "cli/parser"
|
||||
require "cask/all"
|
||||
require "cask/cmd"
|
||||
require "cask/cask_loader"
|
||||
|
||||
@ -127,10 +126,12 @@ module Homebrew
|
||||
|
||||
return if casks.blank?
|
||||
|
||||
cask_uninstall = Cask::Cmd::Uninstall.new(casks)
|
||||
cask_uninstall.force = args.force?
|
||||
cask_uninstall.verbose = args.verbose?
|
||||
cask_uninstall.run
|
||||
Cask::Cmd::Uninstall.uninstall_casks(
|
||||
*casks,
|
||||
binaries: args.binaries?,
|
||||
verbose: args.verbose?,
|
||||
force: args.force?,
|
||||
)
|
||||
rescue MultipleVersionsInstalledError => e
|
||||
ofail e
|
||||
puts "Run `brew uninstall --force #{e.name}` to remove all versions."
|
||||
|
||||
@ -26,20 +26,19 @@ module Homebrew
|
||||
def unlink
|
||||
args = unlink_args.parse
|
||||
|
||||
mode = OpenStruct.new
|
||||
mode.dry_run = true if args.dry_run?
|
||||
options = { dry_run: args.dry_run?, verbose: args.verbose? }
|
||||
|
||||
args.kegs.each do |keg|
|
||||
if mode.dry_run
|
||||
if args.dry_run?
|
||||
puts "Would remove:"
|
||||
keg.unlink(mode)
|
||||
keg.unlink(**options)
|
||||
next
|
||||
end
|
||||
|
||||
keg.lock do
|
||||
print "Unlinking #{keg}... "
|
||||
puts if args.verbose?
|
||||
puts "#{keg.unlink(mode)} symlinks removed"
|
||||
puts "#{keg.unlink(**options)} symlinks removed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -120,7 +120,7 @@ module Homebrew
|
||||
else
|
||||
hub.dump(updated_formula_report: !args.preinstall?)
|
||||
hub.reporters.each(&:migrate_tap_migration)
|
||||
hub.reporters.each { |r| r.migrate_formula_rename(force: args.force?) }
|
||||
hub.reporters.each { |r| r.migrate_formula_rename(force: args.force?, verbose: args.verbose?) }
|
||||
CacheStoreDatabase.use(:descriptions) do |db|
|
||||
DescriptionCacheStore.new(db)
|
||||
.update_from_report!(hub)
|
||||
@ -371,7 +371,7 @@ class Reporter
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_formula_rename(force:)
|
||||
def migrate_formula_rename(force:, verbose:)
|
||||
Formula.installed.each do |formula|
|
||||
next unless Migrator.needs_migration?(formula)
|
||||
|
||||
|
||||
@ -14,11 +14,11 @@ module Homebrew
|
||||
def upgrade_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`upgrade` [<options>] [<formula>]
|
||||
`upgrade` [<options>] [<formula>|<cask>]
|
||||
|
||||
Upgrade outdated, unpinned formulae using the same options they were originally
|
||||
installed with, plus any appended brew formula options. If <formula> are specified,
|
||||
upgrade only the given <formula> kegs (unless they are pinned; see `pin`, `unpin`).
|
||||
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; see `pin`, `unpin`).
|
||||
|
||||
Unless `HOMEBREW_NO_INSTALL_CLEANUP` is set, `brew cleanup` will then be run for the
|
||||
upgraded formulae or, every 30 days, for all formulae.
|
||||
@ -26,6 +26,10 @@ module Homebrew
|
||||
switch "-d", "--debug",
|
||||
description: "If brewing fails, open an interactive debugging session with access to IRB "\
|
||||
"or a shell inside the temporary build directory."
|
||||
switch "--formula",
|
||||
description: "Only upgrade outdated formulae."
|
||||
switch "--cask",
|
||||
description: "Only upgrade outdated casks."
|
||||
switch "-s", "--build-from-source",
|
||||
description: "Compile <formula> from source even if a bottle is available."
|
||||
switch "-i", "--interactive",
|
||||
@ -56,6 +60,12 @@ module Homebrew
|
||||
switch "--greedy",
|
||||
description: "Upgrade casks with `auto_updates` or `version :latest`"
|
||||
conflicts "--build-from-source", "--force-bottle"
|
||||
conflicts "--formula", "--greedy"
|
||||
["--formula", "-s", "--build-from-source", "-i", "--interactive",
|
||||
"--force-bottle", "--fetch-HEAD", "--ignore-pinned", "--keep-tmp",
|
||||
"--display-times"].each do |flag|
|
||||
conflicts "--cask", flag
|
||||
end
|
||||
formula_options
|
||||
end
|
||||
end
|
||||
@ -67,14 +77,16 @@ module Homebrew
|
||||
# If one or more formulae are specified, but no casks were
|
||||
# specified, we want to make note of that so we don't
|
||||
# try to upgrade all outdated casks.
|
||||
named_formulae_specified = !formulae.empty? && casks.empty?
|
||||
named_casks_specified = !casks.empty? && formulae.empty?
|
||||
upgrade_formulae = formulae.present? && casks.blank?
|
||||
upgrade_casks = casks.present? && formulae.blank?
|
||||
|
||||
upgrade_outdated_formulae(formulae, args: args) unless named_casks_specified
|
||||
upgrade_outdated_casks(casks, args: args) unless named_formulae_specified
|
||||
upgrade_outdated_formulae(formulae, args: args) unless upgrade_casks
|
||||
upgrade_outdated_casks(casks, args: args) unless upgrade_formulae
|
||||
end
|
||||
|
||||
def upgrade_outdated_formulae(formulae, args:)
|
||||
return if args.cask?
|
||||
|
||||
FormulaInstaller.prevent_build_flags(args)
|
||||
|
||||
Install.perform_preinstall_checks
|
||||
@ -133,10 +145,18 @@ module Homebrew
|
||||
end
|
||||
|
||||
def upgrade_outdated_casks(casks, args:)
|
||||
cask_upgrade = Cask::Cmd::Upgrade.new(casks)
|
||||
cask_upgrade.force = args.force?
|
||||
cask_upgrade.dry_run = args.dry_run?
|
||||
cask_upgrade.greedy = args.greedy?
|
||||
cask_upgrade.run
|
||||
return if args.formula?
|
||||
|
||||
Cask::Cmd::Upgrade.upgrade_casks(
|
||||
*casks,
|
||||
force: args.force?,
|
||||
greedy: args.greedy?,
|
||||
dry_run: args.dry_run?,
|
||||
binaries: args.binaries?,
|
||||
quarantine: args.quarantine?,
|
||||
require_sha: args.require_sha?,
|
||||
skip_cask_deps: args.skip_cask_deps?,
|
||||
verbose: args.verbose?,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -65,15 +65,25 @@ module Homebrew
|
||||
end
|
||||
|
||||
use_runtime_dependents = args.installed? &&
|
||||
!used_formulae_missing &&
|
||||
!args.include_build? &&
|
||||
!args.include_test? &&
|
||||
!args.include_optional? &&
|
||||
!args.skip_recommended?
|
||||
|
||||
uses = intersection_of_dependents(use_runtime_dependents, used_formulae, args: args)
|
||||
|
||||
return if uses.empty?
|
||||
|
||||
puts Formatter.columns(uses.map(&:full_name).sort)
|
||||
odie "Missing formulae should not have dependents!" if used_formulae_missing
|
||||
end
|
||||
|
||||
def intersection_of_dependents(use_runtime_dependents, used_formulae, args:)
|
||||
recursive = args.recursive?
|
||||
includes, ignores = args_includes_ignores(args)
|
||||
|
||||
uses = if use_runtime_dependents && !used_formulae_missing
|
||||
if use_runtime_dependents
|
||||
used_formulae.map(&:runtime_installed_formula_dependents)
|
||||
.reduce(&:&)
|
||||
.select(&:any_version_installed?) +
|
||||
@ -87,11 +97,6 @@ module Homebrew
|
||||
|
||||
select_used_dependents(deps, used_formulae, recursive, includes, ignores)
|
||||
end
|
||||
|
||||
return if uses.empty?
|
||||
|
||||
puts Formatter.columns(uses.map(&:full_name).sort)
|
||||
odie "Missing formulae should not have dependents!" if used_formulae_missing
|
||||
end
|
||||
|
||||
def select_used_dependents(dependents, used_formulae, recursive, includes, ignores)
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "compat/dependencies_helpers"
|
||||
require "compat/cli/parser"
|
||||
require "compat/extend/nil"
|
||||
require "compat/extend/string"
|
||||
require "compat/formula"
|
||||
|
||||
32
Library/Homebrew/compat/cli/parser.rb
Normal file
32
Library/Homebrew/compat/cli/parser.rb
Normal file
@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Homebrew
|
||||
module CLI
|
||||
class Parser
|
||||
module Compat
|
||||
module DeprecatedArgs
|
||||
def respond_to_missing?(*)
|
||||
super
|
||||
end
|
||||
|
||||
def method_missing(method, *)
|
||||
if ![:debug?, :quiet?, :verbose?, :value].include?(method) && !@printed_args_warning
|
||||
odeprecated "Homebrew.args", "`args = <command>_args.parse` and pass `args` along the call chain"
|
||||
@printed_args_warning = true
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def parse(*)
|
||||
args = super
|
||||
Homebrew.args = args.dup.extend(DeprecatedArgs)
|
||||
args
|
||||
end
|
||||
end
|
||||
|
||||
prepend Compat
|
||||
end
|
||||
end
|
||||
end
|
||||
17
Library/Homebrew/compat/dependencies_helpers.rb
Normal file
17
Library/Homebrew/compat/dependencies_helpers.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/args"
|
||||
|
||||
module DependenciesHelpers
|
||||
module Compat
|
||||
def argv_includes_ignores(argv = nil)
|
||||
unless @printed_includes_ignores_warning
|
||||
odeprecated "Homebrew.argv_includes_ignores", "Homebrew.args_includes_ignores"
|
||||
@printed_includes_ignores_warning = true
|
||||
end
|
||||
args_includes_ignores(argv ? Homebrew::CLI::Args.new : Homebrew.args)
|
||||
end
|
||||
end
|
||||
|
||||
prepend Compat
|
||||
end
|
||||
71
Library/Homebrew/context.rb
Normal file
71
Library/Homebrew/context.rb
Normal file
@ -0,0 +1,71 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "monitor"
|
||||
|
||||
module Context
|
||||
extend MonitorMixin
|
||||
|
||||
def self.current=(context)
|
||||
synchronize do
|
||||
@current = context
|
||||
end
|
||||
end
|
||||
|
||||
def self.current
|
||||
if current_context = Thread.current[:context]
|
||||
return current_context
|
||||
end
|
||||
|
||||
synchronize do
|
||||
@current ||= ContextStruct.new
|
||||
end
|
||||
end
|
||||
|
||||
class ContextStruct
|
||||
def initialize(debug: nil, quiet: nil, verbose: nil)
|
||||
@debug = debug
|
||||
@quiet = quiet
|
||||
@verbose = verbose
|
||||
end
|
||||
|
||||
def debug?
|
||||
@debug
|
||||
end
|
||||
|
||||
def quiet?
|
||||
@quiet
|
||||
end
|
||||
|
||||
def verbose?
|
||||
@verbose
|
||||
end
|
||||
end
|
||||
|
||||
def debug?
|
||||
Context.current.debug?
|
||||
end
|
||||
|
||||
def quiet?
|
||||
Context.current.quiet?
|
||||
end
|
||||
|
||||
def verbose?
|
||||
Context.current.verbose?
|
||||
end
|
||||
|
||||
def with_context(**options)
|
||||
old_context = Thread.current[:context]
|
||||
|
||||
new_context = ContextStruct.new(
|
||||
debug: options.fetch(:debug, old_context&.debug?),
|
||||
quiet: options.fetch(:quiet, old_context&.quiet?),
|
||||
verbose: options.fetch(:verbose, old_context&.verbose?),
|
||||
)
|
||||
|
||||
Thread.current[:context] = new_context
|
||||
|
||||
yield
|
||||
ensure
|
||||
Thread.current[:context] = old_context
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@ require "formula"
|
||||
require "formula_versions"
|
||||
require "utils/curl"
|
||||
require "utils/notability"
|
||||
require "utils/spdx"
|
||||
require "extend/ENV"
|
||||
require "formula_cellar_checks"
|
||||
require "cmd/search"
|
||||
@ -37,6 +38,8 @@ module Homebrew
|
||||
description: "Run various additional style checks to determine if a new formula is eligible "\
|
||||
"for Homebrew. This should be used when creating new formula and implies "\
|
||||
"`--strict` and `--online`."
|
||||
flag "--tap=",
|
||||
description: "Check the formulae within the given tap, specified as <user>`/`<repo>."
|
||||
switch "--fix",
|
||||
description: "Fix style violations automatically using RuboCop's auto-correct feature."
|
||||
switch "--display-cop-names",
|
||||
@ -46,7 +49,7 @@ module Homebrew
|
||||
"make output easy to grep."
|
||||
switch "--skip-style",
|
||||
description: "Skip running non-RuboCop style checks. Useful if you plan on running "\
|
||||
"`brew style` separately."
|
||||
"`brew style` separately. Default unless a formula is specified by name"
|
||||
switch "-D", "--audit-debug",
|
||||
description: "Enable debugging and profiling of audit methods."
|
||||
comma_array "--only",
|
||||
@ -85,17 +88,23 @@ module Homebrew
|
||||
strict = new_formula || args.strict?
|
||||
online = new_formula || args.online?
|
||||
git = args.git?
|
||||
skip_style = args.skip_style? || args.no_named?
|
||||
skip_style = args.skip_style? || args.no_named? || args.tap
|
||||
|
||||
ENV.activate_extensions!
|
||||
ENV.setup_build_environment
|
||||
|
||||
audit_formulae = args.no_named? ? Formula : args.resolved_formulae
|
||||
audit_formulae = if args.tap
|
||||
Tap.fetch(args.tap).formula_names.map { |name| Formula[name] }
|
||||
elsif args.no_named?
|
||||
Formula
|
||||
else
|
||||
args.resolved_formulae
|
||||
end
|
||||
style_files = args.formulae_paths unless skip_style
|
||||
|
||||
only_cops = args.only_cops
|
||||
except_cops = args.except_cops
|
||||
options = { fix: args.fix? }
|
||||
options = { fix: args.fix?, debug: args.debug?, verbose: args.verbose? }
|
||||
|
||||
if only_cops
|
||||
options[:only_cops] = only_cops
|
||||
@ -110,8 +119,7 @@ module Homebrew
|
||||
# Check style in a single batch run up front for performance
|
||||
style_results = Style.check_style_json(style_files, options) if style_files
|
||||
# load licenses
|
||||
spdx = HOMEBREW_LIBRARY_PATH/"data/spdx.json"
|
||||
spdx_data = JSON.parse(spdx.read)
|
||||
spdx_data = SPDX.spdx_data
|
||||
new_formula_problem_lines = []
|
||||
audit_formulae.sort.each do |f|
|
||||
only = only_cops ? ["style"] : args.only
|
||||
@ -335,19 +343,42 @@ module Homebrew
|
||||
openssl@1.1
|
||||
].freeze
|
||||
|
||||
PERMITTED_LICENSE_MISMATCHES = {
|
||||
"AGPL-3.0" => ["AGPL-3.0-only", "AGPL-3.0-or-later"],
|
||||
"GPL-2.0" => ["GPL-2.0-only", "GPL-2.0-or-later"],
|
||||
"GPL-3.0" => ["GPL-3.0-only", "GPL-3.0-or-later"],
|
||||
"LGPL-2.1" => ["LGPL-2.1-only", "LGPL-2.1-or-later"],
|
||||
"LGPL-3.0" => ["LGPL-3.0-only", "LGPL-3.0-or-later"],
|
||||
}.freeze
|
||||
|
||||
def audit_license
|
||||
if formula.license.present?
|
||||
non_standard_licenses = []
|
||||
formula.license.each do |license|
|
||||
non_standard_licenses = formula.license.map do |license|
|
||||
next if license == :public_domain
|
||||
next if @spdx_data["licenses"].any? { |spdx| spdx["licenseId"] == license }
|
||||
|
||||
non_standard_licenses << license
|
||||
end
|
||||
license
|
||||
end.compact
|
||||
|
||||
if non_standard_licenses.present?
|
||||
problem "Formula #{formula.name} contains non-standard SPDX licenses: #{non_standard_licenses}."
|
||||
end
|
||||
|
||||
if @strict
|
||||
deprecated_licenses = formula.license.map do |license|
|
||||
next if license == :public_domain
|
||||
next if @spdx_data["licenses"].any? do |spdx|
|
||||
spdx["licenseId"] == license && !spdx["isDeprecatedLicenseId"]
|
||||
end
|
||||
|
||||
license
|
||||
end.compact
|
||||
|
||||
if deprecated_licenses.present?
|
||||
problem "Formula #{formula.name} contains deprecated SPDX licenses: #{deprecated_licenses}."
|
||||
end
|
||||
end
|
||||
|
||||
return unless @online
|
||||
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
|
||||
@ -355,12 +386,12 @@ module Homebrew
|
||||
|
||||
github_license = GitHub.get_repo_license(user, repo)
|
||||
return if github_license && (formula.license + ["NOASSERTION"]).include?(github_license)
|
||||
return if PERMITTED_LICENSE_MISMATCHES[github_license]&.any? { |license| formula.license.include? license }
|
||||
|
||||
problem "License mismatch - GitHub license is: #{Array(github_license)}, "\
|
||||
"but Formulae license states: #{formula.license}."
|
||||
problem "Formula license #{formula.license} does not match GitHub license #{Array(github_license)}."
|
||||
|
||||
elsif @new_formula
|
||||
problem "No license specified for package."
|
||||
elsif @new_formula && @core_tap
|
||||
problem "Formulae in homebrew/core must specify a license."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -105,7 +105,10 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
end
|
||||
[formula.tap&.full_name, "origin/master", "-"]
|
||||
origin_branch = Utils.popen_read("git", "-C", formula.tap.path.to_s, "symbolic-ref", "-q", "--short",
|
||||
"refs/remotes/origin/HEAD").chomp.presence
|
||||
origin_branch ||= "origin/master"
|
||||
[formula.tap&.full_name, origin_branch, "-"]
|
||||
end
|
||||
|
||||
def bump_formula_pr
|
||||
@ -128,7 +131,7 @@ module Homebrew
|
||||
check_open_pull_requests(formula, tap_full_name, args: args)
|
||||
|
||||
new_version = args.version
|
||||
check_all_pull_requests(formula, tap_full_name, version: new_version, args: args) if new_version
|
||||
check_closed_pull_requests(formula, tap_full_name, version: new_version, args: args) if new_version
|
||||
|
||||
requested_spec = :stable
|
||||
formula_spec = formula.stable
|
||||
@ -159,10 +162,10 @@ module Homebrew
|
||||
old_version = old_formula_version.to_s
|
||||
forced_version = new_version.present?
|
||||
new_url_hash = if new_url && new_hash
|
||||
check_all_pull_requests(formula, tap_full_name, url: new_url, args: args) unless new_version
|
||||
check_closed_pull_requests(formula, tap_full_name, url: new_url, args: args) unless new_version
|
||||
true
|
||||
elsif new_tag && new_revision
|
||||
check_all_pull_requests(formula, tap_full_name, url: old_url, tag: new_tag, args: args) unless new_version
|
||||
check_closed_pull_requests(formula, tap_full_name, url: old_url, tag: new_tag, args: args) unless new_version
|
||||
false
|
||||
elsif !hash_type
|
||||
odie "#{formula}: no --tag= or --version= argument specified!" if !new_tag && !new_version
|
||||
@ -173,7 +176,7 @@ module Homebrew
|
||||
and old tag are both #{new_tag}.
|
||||
EOS
|
||||
end
|
||||
check_all_pull_requests(formula, tap_full_name, url: old_url, tag: new_tag, args: args) unless new_version
|
||||
check_closed_pull_requests(formula, tap_full_name, url: old_url, tag: new_tag, args: args) unless new_version
|
||||
resource_path, forced_version = fetch_resource(formula, new_version, old_url, tag: new_tag)
|
||||
new_revision = Utils.popen_read("git -C \"#{resource_path}\" rev-parse -q --verify HEAD")
|
||||
new_revision = new_revision.strip
|
||||
@ -190,7 +193,7 @@ module Homebrew
|
||||
#{new_url}
|
||||
EOS
|
||||
end
|
||||
check_all_pull_requests(formula, tap_full_name, url: new_url, args: args) unless new_version
|
||||
check_closed_pull_requests(formula, tap_full_name, url: new_url, args: args) unless new_version
|
||||
resource_path, forced_version = fetch_resource(formula, new_version, new_url)
|
||||
tar_file_extensions = %w[.tar .tb2 .tbz .tbz2 .tgz .tlz .txz .tZ]
|
||||
if tar_file_extensions.any? { |extension| new_url.include? extension }
|
||||
@ -246,7 +249,8 @@ module Homebrew
|
||||
]
|
||||
end
|
||||
|
||||
old_contents = File.read(formula.path) unless args.dry_run?
|
||||
read_only_run = args.dry_run? && !args.write?
|
||||
old_contents = File.read(formula.path) unless read_only_run
|
||||
|
||||
if new_mirrors
|
||||
replacement_pairs << [
|
||||
@ -312,13 +316,13 @@ module Homebrew
|
||||
end
|
||||
|
||||
if new_formula_version < old_formula_version
|
||||
formula.path.atomic_write(old_contents) unless args.dry_run?
|
||||
formula.path.atomic_write(old_contents) unless read_only_run
|
||||
odie <<~EOS
|
||||
You need to bump this formula manually since changing the
|
||||
version from #{old_formula_version} to #{new_formula_version} would be a downgrade.
|
||||
EOS
|
||||
elsif new_formula_version == old_formula_version
|
||||
formula.path.atomic_write(old_contents) unless args.dry_run?
|
||||
formula.path.atomic_write(old_contents) unless read_only_run
|
||||
odie <<~EOS
|
||||
You need to bump this formula manually since the new version
|
||||
and old version are both #{new_formula_version}.
|
||||
@ -331,14 +335,14 @@ module Homebrew
|
||||
alias_rename.map! { |a| formula.tap.alias_dir/a }
|
||||
end
|
||||
|
||||
ohai "brew update-python-resources #{formula.name}"
|
||||
if !args.dry_run? || (args.dry_run? && args.write?)
|
||||
PyPI.update_python_resources! formula, new_formula_version, silent: true, ignore_non_pypi_packages: true
|
||||
unless read_only_run
|
||||
PyPI.update_python_resources! formula, new_formula_version, silent: args.quiet?, ignore_non_pypi_packages: true
|
||||
end
|
||||
|
||||
run_audit(formula, alias_rename, old_contents, args: args)
|
||||
|
||||
formula.path.parent.cd do
|
||||
_, base_branch = origin_branch.split("/")
|
||||
branch = "bump-#{formula.name}-#{new_formula_version}"
|
||||
git_dir = Utils.popen_read("git rev-parse --git-dir").chomp
|
||||
shallow = !git_dir.empty? && File.exist?("#{git_dir}/shallow")
|
||||
@ -354,7 +358,7 @@ module Homebrew
|
||||
"#{new_formula_version}' -- #{changed_files.join(" ")}"
|
||||
ohai "git push --set-upstream $HUB_REMOTE #{branch}:#{branch}"
|
||||
ohai "git checkout --quiet #{previous_branch}"
|
||||
ohai "create pull request with GitHub API"
|
||||
ohai "create pull request with GitHub API (base branch: #{base_branch})"
|
||||
else
|
||||
|
||||
if args.no_fork?
|
||||
@ -387,7 +391,7 @@ module Homebrew
|
||||
|
||||
begin
|
||||
url = GitHub.create_pull_request(tap_full_name, pr_title,
|
||||
"#{username}:#{branch}", "master", pr_message)["html_url"]
|
||||
"#{username}:#{branch}", base_branch, pr_message)["html_url"]
|
||||
if args.no_browse?
|
||||
puts url
|
||||
else
|
||||
@ -452,7 +456,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
def inreplace_pairs(path, replacement_pairs, args:)
|
||||
if args.dry_run?
|
||||
read_only_run = args.dry_run? && !args.write?
|
||||
if read_only_run
|
||||
str = path.open("r") { |f| Formulary.ensure_utf8_encoding(f).read }
|
||||
contents = StringInreplaceExtension.new(str)
|
||||
replacement_pairs.each do |old, new|
|
||||
@ -494,14 +499,14 @@ module Homebrew
|
||||
check_for_duplicate_pull_requests(pull_requests, args: args)
|
||||
end
|
||||
|
||||
def check_all_pull_requests(formula, tap_full_name, version: nil, url: nil, tag: nil, args:)
|
||||
def check_closed_pull_requests(formula, tap_full_name, version: nil, url: nil, tag: nil, args:)
|
||||
unless version
|
||||
specs = {}
|
||||
specs[:tag] = tag if tag
|
||||
version = Version.detect(url, specs)
|
||||
end
|
||||
# if we haven't already found open requests, try for an exact match across all requests
|
||||
pull_requests = GitHub.fetch_pull_requests("#{formula.name} #{version}", tap_full_name) if pull_requests.blank?
|
||||
# if we haven't already found open requests, try for an exact match across closed requests
|
||||
pull_requests = GitHub.fetch_pull_requests("#{formula.name} #{version}", tap_full_name, state: "closed")
|
||||
check_for_duplicate_pull_requests(pull_requests, args: args)
|
||||
end
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ module Homebrew
|
||||
|
||||
require "formula"
|
||||
require "keg"
|
||||
require "cask/all"
|
||||
require "cask"
|
||||
|
||||
ohai "Interactive Homebrew Shell"
|
||||
puts "Example commands available with: brew irb --examples"
|
||||
|
||||
@ -116,7 +116,7 @@ module Homebrew
|
||||
# git cherry-pick unfortunately has no quiet option
|
||||
ohai "Cherry-picking #{commit_count} commit#{"s" unless commit_count == 1} from ##{pr}"
|
||||
cherry_pick_args = "git", "-C", path, "cherry-pick", "--ff", "--allow-empty", "#{merge_base}..FETCH_HEAD"
|
||||
result = Homebrew.args.verbose? ? system(*cherry_pick_args) : quiet_system(*cherry_pick_args)
|
||||
result = args.verbose? ? system(*cherry_pick_args) : quiet_system(*cherry_pick_args)
|
||||
|
||||
unless result
|
||||
if args.resolve?
|
||||
|
||||
@ -8,20 +8,19 @@ module Homebrew
|
||||
def prof_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`prof` <command>
|
||||
`prof` [<command>]
|
||||
|
||||
Run Homebrew with the Ruby profiler, e.g. `brew prof readall`.
|
||||
EOS
|
||||
min_named 1
|
||||
end
|
||||
end
|
||||
|
||||
def prof
|
||||
prof_args.parse
|
||||
args = prof_args.parse
|
||||
|
||||
Homebrew.install_gem_setup_path! "ruby-prof", version: "0.18.0"
|
||||
FileUtils.mkdir_p "prof"
|
||||
brew_rb = (HOMEBREW_LIBRARY_PATH/"brew.rb").resolved_path
|
||||
safe_system "ruby-prof", "--printer=multi", "--file=prof", brew_rb, "--", *ARGV
|
||||
safe_system "ruby-prof", "--printer=multi", "--file=prof", brew_rb, "--", *args.named
|
||||
end
|
||||
end
|
||||
|
||||
@ -50,7 +50,9 @@ module Homebrew
|
||||
only_cops = args.only_cops
|
||||
except_cops = args.except_cops
|
||||
|
||||
options = { fix: args.fix?, display_cop_names: args.display_cop_names? }
|
||||
options = {
|
||||
fix: args.fix?, display_cop_names: args.display_cop_names?, debug: args.debug?, verbose: args.verbose?
|
||||
}
|
||||
if only_cops
|
||||
options[:only_cops] = only_cops
|
||||
elsif except_cops
|
||||
|
||||
@ -53,23 +53,43 @@ module Homebrew
|
||||
pull_request: []
|
||||
jobs:
|
||||
test-bot:
|
||||
runs-on: macos-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
steps:
|
||||
- name: Set up Git repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Run brew test-bot
|
||||
run: |
|
||||
set -e
|
||||
brew update
|
||||
HOMEBREW_TAP_DIR="/usr/local/Homebrew/Library/Taps/#{tap.full_name}"
|
||||
mkdir -p "$HOMEBREW_TAP_DIR"
|
||||
rm -rf "$HOMEBREW_TAP_DIR"
|
||||
ln -s "$PWD" "$HOMEBREW_TAP_DIR"
|
||||
brew test-bot
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
|
||||
- name: Cache Homebrew Bundler RubyGems
|
||||
id: cache
|
||||
uses: actions/cache@main
|
||||
with:
|
||||
path: ${{ steps.set-up-homebrew.outputs.gems-path }}
|
||||
key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
|
||||
restore-keys: ${{ runner.os }}-rubygems-
|
||||
|
||||
- name: Install Homebrew Bundler RubyGems
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: brew install-bundler-gems
|
||||
|
||||
- name: Run brew test-bot --only-cleanup-before
|
||||
run: brew test-bot --only-cleanup-before
|
||||
|
||||
- name: Run brew test-bot --only-setup
|
||||
run: brew test-bot --only-setup
|
||||
|
||||
- name: Run brew test-bot --only-tap-syntax
|
||||
run: brew test-bot --only-tap-syntax
|
||||
|
||||
- name: Run brew test-bot --only-formulae
|
||||
if: github.event_name == 'pull_request'
|
||||
run: brew test-bot --only-formulae
|
||||
YAML
|
||||
|
||||
(tap.path/".github/workflows").mkpath
|
||||
write_path(tap, ".github/workflows/main.yml", actions)
|
||||
write_path(tap, ".github/workflows/tests.yml", actions)
|
||||
ohai "Created #{tap}"
|
||||
puts tap.path.to_s
|
||||
end
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
require "utils/spdx"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
SPDX_PATH = (HOMEBREW_LIBRARY_PATH/"data/spdx.json").freeze
|
||||
SPDX_API_URL = "https://api.github.com/repos/spdx/license-list-data/releases/latest"
|
||||
|
||||
def update_license_data_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
@ -29,16 +26,14 @@ module Homebrew
|
||||
args = update_license_data_args.parse
|
||||
ohai "Updating SPDX license data..."
|
||||
|
||||
latest_tag = GitHub.open_api(SPDX_API_URL)["tag_name"]
|
||||
data_url = "https://raw.githubusercontent.com/spdx/license-list-data/#{latest_tag}/json/licenses.json"
|
||||
curl_download(data_url, to: SPDX_PATH, partial: false)
|
||||
SPDX.download_latest_license_data!
|
||||
|
||||
Homebrew.failed = system("git", "diff", "--stat", "--exit-code", SPDX_PATH) if args.fail_if_not_changed?
|
||||
Homebrew.failed = system("git", "diff", "--stat", "--exit-code", SPDX::JSON_PATH) if args.fail_if_not_changed?
|
||||
|
||||
return unless args.commit?
|
||||
|
||||
ohai "git add"
|
||||
safe_system "git", "add", SPDX_PATH
|
||||
safe_system "git", "add", SPDX::JSON_PATH
|
||||
ohai "git commit"
|
||||
system "git", "commit", "--message", "data/spdx.json: update to #{latest_tag}"
|
||||
end
|
||||
|
||||
@ -691,7 +691,7 @@ module Homebrew
|
||||
|
||||
# these will result in uncommitted gems.
|
||||
if path == HOMEBREW_REPOSITORY
|
||||
next if ENV["HOMEBREW_SORBET"] || ENV["HOMEBREW_PATCHELF_RB"]
|
||||
next if ENV["HOMEBREW_SORBET"]
|
||||
end
|
||||
|
||||
message ||= ""
|
||||
@ -907,7 +907,7 @@ module Homebrew
|
||||
|
||||
def check_cask_staging_location
|
||||
# Skip this check when running CI since the staging path is not writable for security reasons
|
||||
return if ENV["HOMEBREW_GITHUB_ACTIONS"]
|
||||
return if ENV["GITHUB_ACTIONS"]
|
||||
|
||||
path = Cask::Caskroom.path
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ require "mechanize/http/content_disposition_parser"
|
||||
class AbstractDownloadStrategy
|
||||
extend Forwardable
|
||||
include FileUtils
|
||||
include Context
|
||||
|
||||
module Pourable
|
||||
def stage
|
||||
@ -21,9 +22,9 @@ class AbstractDownloadStrategy
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :cache, :cached_location, :url, :meta, :name, :version, :shutup
|
||||
attr_reader :cache, :cached_location, :url, :meta, :name, :version
|
||||
|
||||
private :meta, :name, :version, :shutup
|
||||
private :meta, :name, :version
|
||||
|
||||
def initialize(url, name, version, **meta)
|
||||
@url = url
|
||||
@ -31,24 +32,18 @@ class AbstractDownloadStrategy
|
||||
@version = version
|
||||
@cache = meta.fetch(:cache, HOMEBREW_CACHE)
|
||||
@meta = meta
|
||||
@shutup = false
|
||||
extend Pourable if meta[:bottle]
|
||||
end
|
||||
|
||||
# Download and cache the resource as {#cached_location}.
|
||||
def fetch; end
|
||||
|
||||
# Suppress output
|
||||
def shutup!
|
||||
@shutup = true
|
||||
end
|
||||
|
||||
def puts(*args)
|
||||
super(*args) unless shutup
|
||||
super(*args) unless quiet?
|
||||
end
|
||||
|
||||
def ohai(*args)
|
||||
super(*args) unless shutup
|
||||
super(*args) unless quiet?
|
||||
end
|
||||
|
||||
# Unpack {#cached_location} into the current working directory, and possibly
|
||||
@ -60,7 +55,7 @@ class AbstractDownloadStrategy
|
||||
ref_type: @ref_type, ref: @ref)
|
||||
.extract_nestedly(basename: basename,
|
||||
prioritise_extension: true,
|
||||
verbose: Homebrew.args.verbose? && !shutup)
|
||||
verbose: verbose? && !quiet?)
|
||||
chdir
|
||||
end
|
||||
|
||||
@ -102,9 +97,9 @@ class AbstractDownloadStrategy
|
||||
def system_command!(*args, **options)
|
||||
super(
|
||||
*args,
|
||||
print_stdout: !shutup,
|
||||
print_stderr: !shutup,
|
||||
verbose: Homebrew.args.verbose? && !shutup,
|
||||
print_stdout: !quiet?,
|
||||
print_stderr: !quiet?,
|
||||
verbose: verbose? && !quiet?,
|
||||
env: env,
|
||||
**options,
|
||||
)
|
||||
@ -498,7 +493,7 @@ class NoUnzipCurlDownloadStrategy < CurlDownloadStrategy
|
||||
def stage
|
||||
UnpackStrategy::Uncompressed.new(cached_location)
|
||||
.extract(basename: basename,
|
||||
verbose: Homebrew.args.verbose? && !shutup)
|
||||
verbose: verbose? && !quiet?)
|
||||
end
|
||||
end
|
||||
|
||||
@ -553,7 +548,7 @@ class SubversionDownloadStrategy < VCSDownloadStrategy
|
||||
# This saves on bandwidth and will have a similar effect to verifying the
|
||||
# cache as it will make any changes to get the right revision.
|
||||
args = []
|
||||
args << "--quiet" unless Homebrew.args.verbose?
|
||||
args << "--quiet" unless verbose?
|
||||
|
||||
if revision
|
||||
ohai "Checking out #{@ref}"
|
||||
@ -897,7 +892,7 @@ class CVSDownloadStrategy < VCSDownloadStrategy
|
||||
end
|
||||
|
||||
def quiet_flag
|
||||
"-Q" unless Homebrew.args.verbose?
|
||||
"-Q" unless verbose?
|
||||
end
|
||||
|
||||
def clone_repo
|
||||
|
||||
@ -54,6 +54,9 @@ module Homebrew
|
||||
"Linux: `$XDG_CACHE_HOME/Homebrew` or `$HOME/.cache/Homebrew`.",
|
||||
default: HOMEBREW_DEFAULT_CACHE,
|
||||
},
|
||||
HOMEBREW_CASK_OPTS: {
|
||||
description: "Options which should be used for all `cask` commands.",
|
||||
},
|
||||
HOMEBREW_CLEANUP_MAX_AGE_DAYS: {
|
||||
description: "Cleanup all cached files older than this many days.",
|
||||
default: 120,
|
||||
@ -293,7 +296,7 @@ module Homebrew
|
||||
end
|
||||
elsif hash[:default].present?
|
||||
# Needs a custom implementation.
|
||||
next if env == "HOMEBREW_MAKE_JOBS"
|
||||
next if ["HOMEBREW_MAKE_JOBS", "HOMEBREW_CASK_OPTS"].include?(env)
|
||||
|
||||
define_method(method_name) do
|
||||
ENV[env].presence || hash.fetch(:default).to_s
|
||||
@ -315,5 +318,31 @@ module Homebrew
|
||||
.call
|
||||
.to_s
|
||||
end
|
||||
|
||||
def cask_opts
|
||||
Shellwords.shellsplit(ENV.fetch("HOMEBREW_CASK_OPTS", ""))
|
||||
end
|
||||
|
||||
def cask_opts_binaries?
|
||||
cask_opts.reverse_each do |opt|
|
||||
return true if opt == "--binaries"
|
||||
return false if opt == "--no-binaries"
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def cask_opts_quarantine?
|
||||
cask_opts.reverse_each do |opt|
|
||||
return true if opt == "--quarantine"
|
||||
return false if opt == "--no-quarantine"
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def cask_opts_require_sha?
|
||||
cask_opts.include?("--require-sha")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -247,17 +247,6 @@ class TapAlreadyTappedError < RuntimeError
|
||||
end
|
||||
end
|
||||
|
||||
class TapPinStatusError < RuntimeError
|
||||
attr_reader :name, :pinned
|
||||
|
||||
def initialize(name, pinned)
|
||||
@name = name
|
||||
@pinned = pinned
|
||||
|
||||
super pinned ? "#{name} is already pinned." : "#{name} is already unpinned."
|
||||
end
|
||||
end
|
||||
|
||||
class OperationInProgressError < RuntimeError
|
||||
def initialize(name)
|
||||
message = <<~EOS
|
||||
@ -367,10 +356,10 @@ class BuildError < RuntimeError
|
||||
[]
|
||||
end
|
||||
|
||||
def dump
|
||||
def dump(verbose: false)
|
||||
puts
|
||||
|
||||
if Homebrew.args.verbose?
|
||||
if verbose
|
||||
require "system_config"
|
||||
require "build_environment"
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ module Homebrew
|
||||
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
|
||||
# repository. This only needs to support whatever CI providers
|
||||
# Homebrew/brew is currently using.
|
||||
return if ENV["HOMEBREW_GITHUB_ACTIONS"]
|
||||
return if ENV["GITHUB_ACTIONS"]
|
||||
|
||||
message = <<~EOS
|
||||
Your Xcode (#{MacOS::Xcode.version}) is outdated.
|
||||
@ -100,7 +100,7 @@ module Homebrew
|
||||
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
|
||||
# repository. This only needs to support whatever CI providers
|
||||
# Homebrew/brew is currently using.
|
||||
return if ENV["HOMEBREW_GITHUB_ACTIONS"]
|
||||
return if ENV["GITHUB_ACTIONS"]
|
||||
|
||||
<<~EOS
|
||||
A newer Command Line Tools release is available.
|
||||
|
||||
@ -374,8 +374,11 @@ class Pathname
|
||||
|
||||
# Writes an exec script that invokes a Java jar
|
||||
def write_jar_script(target_jar, script_name, java_opts = "", java_version: nil)
|
||||
(self/script_name).write_env_script "java", "#{java_opts} -jar \"#{target_jar}\"",
|
||||
Language::Java.overridable_java_home_env(java_version)
|
||||
(self/script_name).write <<~EOS
|
||||
#!/bin/bash
|
||||
export JAVA_HOME="#{Language::Java.overridable_java_home_env(java_version)[:JAVA_HOME]}"
|
||||
exec "${JAVA_HOME}/bin/java" #{java_opts} -jar "#{target_jar}" "$@"
|
||||
EOS
|
||||
end
|
||||
|
||||
def install_metafiles(from = Pathname.pwd)
|
||||
@ -416,6 +419,8 @@ require "extend/os/pathname"
|
||||
# @private
|
||||
module ObserverPathnameExtension
|
||||
class << self
|
||||
include Context
|
||||
|
||||
attr_accessor :n, :d
|
||||
|
||||
def reset_counts!
|
||||
@ -434,8 +439,8 @@ module ObserverPathnameExtension
|
||||
MAXIMUM_VERBOSE_OUTPUT = 100
|
||||
|
||||
def verbose?
|
||||
return Homebrew.args.verbose? unless ENV["CI"]
|
||||
return false unless Homebrew.args.verbose?
|
||||
return super unless ENV["CI"]
|
||||
return false unless super
|
||||
|
||||
if total < MAXIMUM_VERBOSE_OUTPUT
|
||||
true
|
||||
|
||||
@ -54,9 +54,11 @@ class Formula
|
||||
include Utils::Inreplace
|
||||
include Utils::Shebang
|
||||
include Utils::Shell
|
||||
include Context
|
||||
extend Enumerable
|
||||
extend Forwardable
|
||||
extend Cachable
|
||||
extend Predicable
|
||||
|
||||
# @!method inreplace(paths, before = nil, after = nil)
|
||||
# Actually implemented in {Utils::Inreplace.inreplace}.
|
||||
@ -536,9 +538,11 @@ class Formula
|
||||
return false unless head&.downloader.is_a?(VCSDownloadStrategy)
|
||||
|
||||
downloader = head.downloader
|
||||
downloader.shutup! unless Homebrew.args.verbose?
|
||||
|
||||
with_context quiet: true do
|
||||
downloader.commit_outdated?(version.version.commit)
|
||||
end
|
||||
end
|
||||
|
||||
# The latest prefix for this formula. Checks for {#head}, then {#devel}
|
||||
# and then {#stable}'s {#prefix}
|
||||
@ -1182,7 +1186,7 @@ class Formula
|
||||
begin
|
||||
yield self, staging
|
||||
rescue
|
||||
staging.retain! if interactive || Homebrew.args.debug?
|
||||
staging.retain! if interactive || debug?
|
||||
raise
|
||||
ensure
|
||||
cp Dir["config.log", "CMakeCache.txt"], logs
|
||||
@ -1831,13 +1835,13 @@ class Formula
|
||||
end
|
||||
end
|
||||
rescue Exception # rubocop:disable Lint/RescueException
|
||||
staging.retain! if Homebrew.args.debug?
|
||||
staging.retain! if debug?
|
||||
raise
|
||||
end
|
||||
end
|
||||
ensure
|
||||
@testpath = nil
|
||||
@prefix_returns_versioned_prefix = false
|
||||
@testpath = nil
|
||||
end
|
||||
|
||||
# @private
|
||||
@ -1927,13 +1931,12 @@ class Formula
|
||||
# # If there is a "make", "install" available, please use it!
|
||||
# system "make", "install"</pre>
|
||||
def system(cmd, *args)
|
||||
verbose = Homebrew.args.verbose?
|
||||
verbose_using_dots = Homebrew::EnvConfig.verbose_using_dots?
|
||||
|
||||
# remove "boring" arguments so that the important ones are more likely to
|
||||
# be shown considering that we trim long ohai lines to the terminal width
|
||||
pretty_args = args.dup
|
||||
unless verbose
|
||||
unless verbose?
|
||||
case cmd
|
||||
when "./configure"
|
||||
pretty_args -= %w[--disable-dependency-tracking --disable-debug --disable-silent-rules]
|
||||
@ -1961,7 +1964,7 @@ class Formula
|
||||
log.puts Time.now, "", cmd, args, ""
|
||||
log.flush
|
||||
|
||||
if verbose
|
||||
if verbose?
|
||||
rd, wr = IO.pipe
|
||||
begin
|
||||
pid = fork do
|
||||
@ -2004,7 +2007,7 @@ class Formula
|
||||
log_lines = Homebrew::EnvConfig.fail_log_lines
|
||||
|
||||
log.flush
|
||||
if !verbose || verbose_using_dots
|
||||
if !verbose? || verbose_using_dots
|
||||
puts "Last #{log_lines} lines from #{logfn}:"
|
||||
Kernel.system "/usr/bin/tail", "-n", log_lines, logfn
|
||||
end
|
||||
@ -2221,6 +2224,8 @@ class Formula
|
||||
# Multiple licenses means that the software is licensed under multiple licenses.
|
||||
# Do not use multiple licenses if e.g. different parts are under different licenses.
|
||||
# <pre>license "BSD-2-Clause"</pre>
|
||||
# <pre>license ["MIT", "GPL-2.0"]</pre>
|
||||
# <pre>license :public_domain</pre>
|
||||
def license(args = nil)
|
||||
if args.nil?
|
||||
@licenses
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
module Homebrew
|
||||
module Assertions
|
||||
include Context
|
||||
|
||||
require "test/unit/assertions"
|
||||
include ::Test::Unit::Assertions
|
||||
|
||||
@ -12,7 +14,7 @@ module Homebrew
|
||||
assert_equal result, $CHILD_STATUS.exitstatus
|
||||
output
|
||||
rescue Test::Unit::AssertionFailedError
|
||||
puts output if Homebrew.args.verbose?
|
||||
puts output if verbose?
|
||||
raise
|
||||
end
|
||||
|
||||
@ -28,7 +30,7 @@ module Homebrew
|
||||
assert_equal result, $CHILD_STATUS.exitstatus unless result.nil?
|
||||
output
|
||||
rescue Test::Unit::AssertionFailedError
|
||||
puts output if Homebrew.args.verbose?
|
||||
puts output if verbose?
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
@ -51,7 +51,8 @@ class FormulaInstaller
|
||||
force_bottle: false,
|
||||
include_test_formulae: [],
|
||||
build_from_source_formulae: [],
|
||||
cc: nil)
|
||||
cc: nil,
|
||||
debug: false, quiet: false, verbose: false)
|
||||
@formula = formula
|
||||
@env = nil
|
||||
@force = false
|
||||
@ -68,9 +69,9 @@ class FormulaInstaller
|
||||
@interactive = false
|
||||
@git = false
|
||||
@cc = cc
|
||||
@verbose = Homebrew.args.verbose?
|
||||
@quiet = Homebrew.args.quiet?
|
||||
@debug = Homebrew.args.debug?
|
||||
@verbose = verbose
|
||||
@quiet = quiet
|
||||
@debug = debug
|
||||
@installed_as_dependency = false
|
||||
@installed_on_request = true
|
||||
@options = Options.new
|
||||
@ -603,13 +604,11 @@ class FormulaInstaller
|
||||
df = dep.to_formula
|
||||
fi = FormulaInstaller.new(df, force_bottle: false,
|
||||
include_test_formulae: include_test_formulae,
|
||||
build_from_source_formulae: build_from_source_formulae)
|
||||
build_from_source_formulae: build_from_source_formulae,
|
||||
debug: debug?, quiet: quiet?, verbose: verbose?)
|
||||
|
||||
fi.force = force?
|
||||
fi.keep_tmp = keep_tmp?
|
||||
fi.verbose = verbose?
|
||||
fi.quiet = quiet?
|
||||
fi.debug = debug?
|
||||
# When fetching we don't need to recurse the dependency tree as it's already
|
||||
# been done for us in `compute_dependencies` and there's no requirement to
|
||||
# fetch in a particular order.
|
||||
@ -644,7 +643,8 @@ class FormulaInstaller
|
||||
|
||||
fi = FormulaInstaller.new(df, force_bottle: false,
|
||||
include_test_formulae: include_test_formulae,
|
||||
build_from_source_formulae: build_from_source_formulae)
|
||||
build_from_source_formulae: build_from_source_formulae,
|
||||
debug: debug?, quiet: quiet?, verbose: verbose?)
|
||||
|
||||
fi.options |= tab.used_options
|
||||
fi.options |= Tab.remap_deprecated_options(df.deprecated_options, dep.options)
|
||||
@ -652,9 +652,6 @@ class FormulaInstaller
|
||||
fi.options &= df.options
|
||||
fi.force = force?
|
||||
fi.keep_tmp = keep_tmp?
|
||||
fi.verbose = verbose?
|
||||
fi.quiet = quiet?
|
||||
fi.debug = debug?
|
||||
fi.link_keg ||= keg_was_linked if keg_had_linked_keg
|
||||
fi.installed_as_dependency = true
|
||||
fi.installed_on_request = df.any_version_installed? && tab.installed_on_request
|
||||
@ -665,7 +662,7 @@ class FormulaInstaller
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
ignore_interrupts do
|
||||
tmp_keg.rename(installed_keg) if tmp_keg && !installed_keg.directory?
|
||||
linked_keg.link if keg_was_linked
|
||||
linked_keg.link(verbose: verbose?) if keg_was_linked
|
||||
end
|
||||
raise unless e.is_a? FormulaInstallationAlreadyAttemptedError
|
||||
|
||||
@ -845,7 +842,7 @@ class FormulaInstaller
|
||||
def link(keg)
|
||||
unless link_keg
|
||||
begin
|
||||
keg.optlink
|
||||
keg.optlink(verbose: verbose?)
|
||||
Formula.clear_cache
|
||||
rescue Keg::LinkError => e
|
||||
onoe "Failed to create #{formula.opt_prefix}"
|
||||
@ -876,7 +873,7 @@ class FormulaInstaller
|
||||
backup_dir = HOMEBREW_CACHE/"Backup"
|
||||
|
||||
begin
|
||||
keg.link
|
||||
keg.link(verbose: verbose?)
|
||||
rescue Keg::ConflictError => e
|
||||
conflict_file = e.dst
|
||||
if formula.link_overwrite?(conflict_file) && !link_overwrite_backup.key?(conflict_file)
|
||||
@ -891,8 +888,7 @@ class FormulaInstaller
|
||||
puts e
|
||||
puts
|
||||
puts "Possible conflicting files are:"
|
||||
mode = OpenStruct.new(dry_run: true, overwrite: true)
|
||||
keg.link(mode)
|
||||
keg.link(dry_run: true, overwrite: true, verbose: verbose?)
|
||||
@show_summary_heading = true
|
||||
Homebrew.failed = true
|
||||
rescue Keg::LinkError => e
|
||||
@ -939,7 +935,7 @@ class FormulaInstaller
|
||||
log.mkpath if formula.plist.include? log.to_s
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
onoe "Failed to install plist file"
|
||||
ohai e, e.backtrace if debug?
|
||||
odebug e, e.backtrace
|
||||
Homebrew.failed = true
|
||||
end
|
||||
|
||||
@ -949,7 +945,7 @@ class FormulaInstaller
|
||||
onoe "Failed to fix install linkage"
|
||||
puts "The formula built, but you may encounter issues using it or linking other"
|
||||
puts "formulae against it."
|
||||
ohai e, e.backtrace if debug?
|
||||
odebug e, e.backtrace
|
||||
Homebrew.failed = true
|
||||
@show_summary_heading = true
|
||||
end
|
||||
@ -960,7 +956,7 @@ class FormulaInstaller
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
opoo "The cleaning step did not complete successfully"
|
||||
puts "Still, the installation was successful, so we will link it into your prefix"
|
||||
ohai e, e.backtrace if debug?
|
||||
odebug e, e.backtrace
|
||||
Homebrew.failed = true
|
||||
@show_summary_heading = true
|
||||
end
|
||||
@ -996,7 +992,7 @@ class FormulaInstaller
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
opoo "The post-install step did not complete successfully"
|
||||
puts "You can try again using `brew postinstall #{formula.full_name}`"
|
||||
ohai e, e.backtrace if debug? || Homebrew::EnvConfig.developer?
|
||||
odebug e, e.backtrace, always_display: Homebrew::EnvConfig.developer?
|
||||
Homebrew.failed = true
|
||||
@show_summary_heading = true
|
||||
end
|
||||
@ -1129,14 +1125,17 @@ class FormulaInstaller
|
||||
end
|
||||
|
||||
def forbidden_license_check
|
||||
forbidden_licenses = Homebrew::EnvConfig.forbidden_licenses.to_s.split(" ")
|
||||
forbidden_licenses = Homebrew::EnvConfig.forbidden_licenses
|
||||
.to_s
|
||||
.sub("Public Domain", "public_domain")
|
||||
.split(" ")
|
||||
return if forbidden_licenses.blank?
|
||||
|
||||
compute_dependencies.each do |dep, _|
|
||||
next if @ignore_deps
|
||||
|
||||
dep_f = dep.to_formula
|
||||
next unless dep_f.license.all? { |license| forbidden_licenses.include? license }
|
||||
next unless dep_f.license.all? { |license| forbidden_licenses.include?(license.to_s) }
|
||||
|
||||
raise CannotInstallFormulaError, <<~EOS
|
||||
The installation of #{formula.name} has a dependency on #{dep.name} where all its licenses are forbidden: #{dep_f.license}.
|
||||
@ -1144,7 +1143,7 @@ class FormulaInstaller
|
||||
end
|
||||
return if @only_deps
|
||||
|
||||
return unless formula.license.all? { |license| forbidden_licenses.include? license }
|
||||
return unless formula.license.all? { |license| forbidden_licenses.include?(license.to_s) }
|
||||
|
||||
raise CannotInstallFormulaError, <<~EOS
|
||||
#{formula.name}'s licenses are all forbidden: #{formula.license}.
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
require "formula"
|
||||
|
||||
class FormulaVersions
|
||||
include Context
|
||||
|
||||
IGNORED_EXCEPTIONS = [
|
||||
ArgumentError, NameError, SyntaxError, TypeError,
|
||||
FormulaSpecificationError, FormulaValidationError,
|
||||
@ -44,7 +46,7 @@ class FormulaVersions
|
||||
rescue *IGNORED_EXCEPTIONS => e
|
||||
# We rescue these so that we can skip bad versions and
|
||||
# continue walking the history
|
||||
odebug "#{e} in #{name} at revision #{rev}", e.backtrace if Homebrew.args.debug?
|
||||
odebug "#{e} in #{name} at revision #{rev}", e.backtrace if debug?
|
||||
rescue FormulaUnavailableError
|
||||
nil
|
||||
ensure
|
||||
|
||||
@ -109,6 +109,8 @@ module Formulary
|
||||
# A FormulaLoader returns instances of formulae.
|
||||
# Subclasses implement loaders for particular sources of formulae.
|
||||
class FormulaLoader
|
||||
include Context
|
||||
|
||||
# The formula's name
|
||||
attr_reader :name
|
||||
# The formula's ruby file's path or filename
|
||||
@ -138,7 +140,7 @@ module Formulary
|
||||
private
|
||||
|
||||
def load_file(flags:)
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if Homebrew.args.debug?
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if debug?
|
||||
raise FormulaUnavailableError, name unless path.file?
|
||||
|
||||
Formulary.load_formula_from_path(name, path, flags: flags)
|
||||
@ -314,7 +316,7 @@ module Formulary
|
||||
end
|
||||
|
||||
def klass(flags:)
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if Homebrew.args.debug?
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if debug?
|
||||
namespace = "FormulaNamespace#{Digest::MD5.hexdigest(contents.to_s)}"
|
||||
Formulary.load_formula(name, path, contents, namespace, flags: flags)
|
||||
end
|
||||
|
||||
@ -37,7 +37,6 @@ require "config"
|
||||
require "os"
|
||||
require "cli/args"
|
||||
require "messages"
|
||||
require "system_command"
|
||||
|
||||
HOMEBREW_PRODUCT = ENV["HOMEBREW_PRODUCT"]
|
||||
HOMEBREW_VERSION = ENV["HOMEBREW_VERSION"]
|
||||
@ -116,6 +115,7 @@ end.compact.freeze
|
||||
|
||||
require "set"
|
||||
|
||||
require "context"
|
||||
require "extend/pathname"
|
||||
|
||||
require "extend/module"
|
||||
@ -125,6 +125,7 @@ require "active_support/core_ext/object/blank"
|
||||
require "active_support/core_ext/hash/deep_merge"
|
||||
require "active_support/core_ext/file/atomic"
|
||||
|
||||
require "system_command"
|
||||
require "exceptions"
|
||||
require "utils"
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ module Homebrew
|
||||
module Help
|
||||
module_function
|
||||
|
||||
def help(cmd = nil, empty_argv: false, usage_error: nil)
|
||||
def help(cmd = nil, empty_argv: false, usage_error: nil, remaining_args: [])
|
||||
if cmd.nil?
|
||||
# Handle `brew` (no arguments).
|
||||
if empty_argv
|
||||
@ -58,7 +58,7 @@ module Homebrew
|
||||
|
||||
# Display command-specific (or generic) help in response to `UsageError`.
|
||||
if usage_error
|
||||
$stderr.puts path ? command_help(cmd, path) : HOMEBREW_HELP
|
||||
$stderr.puts path ? command_help(cmd, path, remaining_args: remaining_args) : HOMEBREW_HELP
|
||||
$stderr.puts
|
||||
onoe usage_error
|
||||
exit 1
|
||||
@ -68,16 +68,16 @@ module Homebrew
|
||||
return if path.nil?
|
||||
|
||||
# Display help for internal command (or generic help if undocumented).
|
||||
puts command_help(cmd, path)
|
||||
puts command_help(cmd, path, remaining_args: remaining_args)
|
||||
exit 0
|
||||
end
|
||||
|
||||
def command_help(cmd, path)
|
||||
def command_help(cmd, path, remaining_args:)
|
||||
# Only some types of commands can have a parser.
|
||||
output = if Commands.valid_internal_cmd?(cmd) ||
|
||||
Commands.valid_internal_dev_cmd?(cmd) ||
|
||||
Commands.external_ruby_v2_cmd_path(cmd)
|
||||
parser_help(path)
|
||||
parser_help(path, remaining_args: remaining_args)
|
||||
end
|
||||
|
||||
output ||= comment_help(path)
|
||||
@ -90,13 +90,13 @@ module Homebrew
|
||||
output
|
||||
end
|
||||
|
||||
def parser_help(path)
|
||||
def parser_help(path, remaining_args:)
|
||||
# Let OptionParser generate help text for commands which have a parser.
|
||||
cmd_parser = CLI::Parser.from_cmd_path(path)
|
||||
return unless cmd_parser
|
||||
|
||||
# Try parsing arguments here in order to show formula options in help output.
|
||||
cmd_parser.parse(Homebrew.args.remaining, ignore_invalid_options: true)
|
||||
cmd_parser.parse(remaining_args, ignore_invalid_options: true)
|
||||
cmd_parser.generate_help_text
|
||||
end
|
||||
|
||||
|
||||
@ -323,7 +323,7 @@ class Keg
|
||||
EOS
|
||||
end
|
||||
|
||||
def unlink(mode = OpenStruct.new)
|
||||
def unlink(**options)
|
||||
ObserverPathnameExtension.reset_counts!
|
||||
|
||||
dirs = []
|
||||
@ -341,7 +341,7 @@ class Keg
|
||||
next unless dst.symlink?
|
||||
next if src != dst.resolved_path
|
||||
|
||||
if mode.dry_run
|
||||
if options[:dry_run]
|
||||
puts dst
|
||||
Find.prune if src.directory?
|
||||
next
|
||||
@ -354,7 +354,7 @@ class Keg
|
||||
end
|
||||
end
|
||||
|
||||
unless mode.dry_run
|
||||
unless options[:dry_run]
|
||||
remove_linked_keg_record if linked?
|
||||
dirs.reverse_each(&:rmdir_if_possible)
|
||||
end
|
||||
@ -436,21 +436,21 @@ class Keg
|
||||
end
|
||||
end
|
||||
|
||||
def link(mode = OpenStruct.new)
|
||||
def link(**options)
|
||||
raise AlreadyLinkedError, self if linked_keg_record.directory?
|
||||
|
||||
ObserverPathnameExtension.reset_counts!
|
||||
|
||||
optlink(mode) unless mode.dry_run
|
||||
optlink(**options) unless options[:dry_run]
|
||||
|
||||
# yeah indeed, you have to force anything you need in the main tree into
|
||||
# these dirs REMEMBER that *NOT* everything needs to be in the main tree
|
||||
link_dir("etc", mode) { :mkpath }
|
||||
link_dir("bin", mode) { :skip_dir }
|
||||
link_dir("sbin", mode) { :skip_dir }
|
||||
link_dir("include", mode) { :link }
|
||||
link_dir("etc", **options) { :mkpath }
|
||||
link_dir("bin", **options) { :skip_dir }
|
||||
link_dir("sbin", **options) { :skip_dir }
|
||||
link_dir("include", **options) { :link }
|
||||
|
||||
link_dir("share", mode) do |relative_path|
|
||||
link_dir("share", **options) do |relative_path|
|
||||
case relative_path.to_s
|
||||
when "locale/locale.alias" then :skip_file
|
||||
when INFOFILE_RX then :info
|
||||
@ -468,7 +468,7 @@ class Keg
|
||||
end
|
||||
end
|
||||
|
||||
link_dir("lib", mode) do |relative_path|
|
||||
link_dir("lib", **options) do |relative_path|
|
||||
case relative_path.to_s
|
||||
when "charset.alias" then :skip_file
|
||||
# pkg-config database gets explicitly created
|
||||
@ -494,7 +494,7 @@ class Keg
|
||||
end
|
||||
end
|
||||
|
||||
link_dir("Frameworks", mode) do |relative_path|
|
||||
link_dir("Frameworks", **options) do |relative_path|
|
||||
# Frameworks contain symlinks pointing into a subdir, so we have to use
|
||||
# the :link strategy. However, for Foo.framework and
|
||||
# Foo.framework/Versions we have to use :mkpath so that multiple formulae
|
||||
@ -506,9 +506,9 @@ class Keg
|
||||
end
|
||||
end
|
||||
|
||||
make_relative_symlink(linked_keg_record, path, mode) unless mode.dry_run
|
||||
make_relative_symlink(linked_keg_record, path, **options) unless options[:dry_run]
|
||||
rescue LinkError
|
||||
unlink
|
||||
unlink(verbose: options[:verbose])
|
||||
raise
|
||||
else
|
||||
ObserverPathnameExtension.n
|
||||
@ -536,19 +536,19 @@ class Keg
|
||||
tab.aliases || []
|
||||
end
|
||||
|
||||
def optlink(mode = OpenStruct.new)
|
||||
def optlink(**options)
|
||||
opt_record.delete if opt_record.symlink? || opt_record.exist?
|
||||
make_relative_symlink(opt_record, path, mode)
|
||||
make_relative_symlink(opt_record, path, **options)
|
||||
aliases.each do |a|
|
||||
alias_opt_record = opt_record.parent/a
|
||||
alias_opt_record.delete if alias_opt_record.symlink? || alias_opt_record.exist?
|
||||
make_relative_symlink(alias_opt_record, path, mode)
|
||||
make_relative_symlink(alias_opt_record, path, **options)
|
||||
end
|
||||
|
||||
return unless oldname_opt_record
|
||||
|
||||
oldname_opt_record.delete
|
||||
make_relative_symlink(oldname_opt_record, path, mode)
|
||||
make_relative_symlink(oldname_opt_record, path, **options)
|
||||
end
|
||||
|
||||
def delete_pyc_files!
|
||||
@ -558,7 +558,7 @@ class Keg
|
||||
|
||||
private
|
||||
|
||||
def resolve_any_conflicts(dst, mode)
|
||||
def resolve_any_conflicts(dst, **options)
|
||||
return unless dst.symlink?
|
||||
|
||||
src = dst.resolved_path
|
||||
@ -571,7 +571,7 @@ class Keg
|
||||
stat = src.lstat
|
||||
rescue Errno::ENOENT
|
||||
# dst is a broken symlink, so remove it.
|
||||
dst.unlink unless mode.dry_run
|
||||
dst.unlink unless options[:dry_run]
|
||||
return
|
||||
end
|
||||
|
||||
@ -580,25 +580,23 @@ class Keg
|
||||
begin
|
||||
keg = Keg.for(src)
|
||||
rescue NotAKegError
|
||||
if Homebrew.args.verbose?
|
||||
puts "Won't resolve conflicts for symlink #{dst} as it doesn't resolve into the Cellar"
|
||||
end
|
||||
puts "Won't resolve conflicts for symlink #{dst} as it doesn't resolve into the Cellar" if options[:verbose]
|
||||
return
|
||||
end
|
||||
|
||||
dst.unlink unless mode.dry_run
|
||||
keg.link_dir(src, mode) { :mkpath }
|
||||
dst.unlink unless options[:dry_run]
|
||||
keg.link_dir(src, **options) { :mkpath }
|
||||
true
|
||||
end
|
||||
|
||||
def make_relative_symlink(dst, src, mode)
|
||||
def make_relative_symlink(dst, src, **options)
|
||||
if dst.symlink? && src == dst.resolved_path
|
||||
puts "Skipping; link already exists: #{dst}" if Homebrew.args.verbose?
|
||||
puts "Skipping; link already exists: #{dst}" if options[:verbose]
|
||||
return
|
||||
end
|
||||
|
||||
# cf. git-clean -n: list files to delete, don't really link or delete
|
||||
if mode.dry_run && mode.overwrite
|
||||
if options[:dry_run] && options[:overwrite]
|
||||
if dst.symlink?
|
||||
puts "#{dst} -> #{dst.resolved_path}"
|
||||
elsif dst.exist?
|
||||
@ -608,12 +606,12 @@ class Keg
|
||||
end
|
||||
|
||||
# list all link targets
|
||||
if mode.dry_run
|
||||
if options[:dry_run]
|
||||
puts dst
|
||||
return
|
||||
end
|
||||
|
||||
dst.delete if mode.overwrite && (dst.exist? || dst.symlink?)
|
||||
dst.delete if options[:overwrite] && (dst.exist? || dst.symlink?)
|
||||
dst.make_relative_symlink(src)
|
||||
rescue Errno::EEXIST => e
|
||||
raise ConflictError.new(self, src.relative_path_from(path), dst, e) if dst.exist?
|
||||
@ -631,7 +629,7 @@ class Keg
|
||||
protected
|
||||
|
||||
# symlinks the contents of path+relative_dir recursively into #{HOMEBREW_PREFIX}/relative_dir
|
||||
def link_dir(relative_dir, mode)
|
||||
def link_dir(relative_dir, **options)
|
||||
root = path/relative_dir
|
||||
return unless root.exist?
|
||||
|
||||
@ -655,10 +653,10 @@ class Keg
|
||||
when :info
|
||||
next if File.basename(src) == "dir" # skip historical local 'dir' files
|
||||
|
||||
make_relative_symlink dst, src, mode
|
||||
make_relative_symlink dst, src, **options
|
||||
dst.install_info
|
||||
else
|
||||
make_relative_symlink dst, src, mode
|
||||
make_relative_symlink dst, src, **options
|
||||
end
|
||||
elsif src.directory?
|
||||
# if the dst dir already exists, then great! walk the rest of the tree tho
|
||||
@ -672,10 +670,10 @@ class Keg
|
||||
when :skip_dir
|
||||
Find.prune
|
||||
when :mkpath
|
||||
dst.mkpath unless resolve_any_conflicts(dst, mode)
|
||||
dst.mkpath unless resolve_any_conflicts(dst, **options)
|
||||
else
|
||||
unless resolve_any_conflicts(dst, mode)
|
||||
make_relative_symlink dst, src, mode
|
||||
unless resolve_any_conflicts(dst, **options)
|
||||
make_relative_symlink dst, src, **options
|
||||
Find.prune
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Livecheck can be used to check for newer versions of the software.
|
||||
# The livecheck DSL specified in the formula is evaluated the methods
|
||||
# of this class, which set the instance variables accordingly. The
|
||||
# information is used by brew livecheck when checking for newer versions
|
||||
# of the software.
|
||||
# The `Livecheck` class implements the DSL methods used in a formula's
|
||||
# `livecheck` block and stores related instance variables. Most of these methods
|
||||
# also return the related instance variable when no argument is provided.
|
||||
#
|
||||
# This information is used by the `brew livecheck` command to control its
|
||||
# behavior.
|
||||
class Livecheck
|
||||
# The reason for skipping livecheck for the formula.
|
||||
# e.g. `Not maintained`
|
||||
# A very brief description of why the formula is skipped (e.g., `No longer
|
||||
# developed or maintained`).
|
||||
# @return [String, nil]
|
||||
attr_reader :skip_msg
|
||||
|
||||
def initialize(formula)
|
||||
@ -15,52 +17,92 @@ class Livecheck
|
||||
@regex = nil
|
||||
@skip = false
|
||||
@skip_msg = nil
|
||||
@strategy = nil
|
||||
@url = nil
|
||||
end
|
||||
|
||||
# Sets the regex instance variable to the argument given, returns the
|
||||
# regex instance variable when no argument is given.
|
||||
# Sets the `@regex` instance variable to the provided `Regexp` or returns the
|
||||
# `@regex` instance variable when no argument is provided.
|
||||
# @param pattern [Regexp] regex to use for matching versions in content
|
||||
# @return [Regexp, nil]
|
||||
def regex(pattern = nil)
|
||||
return @regex if pattern.nil?
|
||||
|
||||
case pattern
|
||||
when nil
|
||||
@regex
|
||||
when Regexp
|
||||
@regex = pattern
|
||||
else
|
||||
raise TypeError, "Livecheck#regex expects a Regexp"
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the skip instance variable to true, indicating that livecheck
|
||||
# must be skipped for the formula. If an argument is given and present,
|
||||
# its value is assigned to the skip_msg instance variable, else nil is
|
||||
# assigned.
|
||||
# Sets the `@skip` instance variable to `true` and sets the `@skip_msg`
|
||||
# instance variable if a `String` is provided. `@skip` is used to indicate
|
||||
# that the formula should be skipped and the `skip_msg` very briefly describes
|
||||
# why the formula is skipped (e.g., `No longer developed or maintained`).
|
||||
# @param skip_msg [String] string describing why the formula is skipped
|
||||
# @return [Boolean]
|
||||
def skip(skip_msg = nil)
|
||||
@skip = true
|
||||
@skip_msg = skip_msg.presence
|
||||
if skip_msg.is_a?(String)
|
||||
@skip_msg = skip_msg
|
||||
elsif skip_msg.present?
|
||||
raise TypeError, "Livecheck#skip expects a String"
|
||||
end
|
||||
|
||||
# Should livecheck be skipped for the formula?
|
||||
@skip = true
|
||||
end
|
||||
|
||||
# Should `livecheck` skip this formula?
|
||||
def skip?
|
||||
@skip
|
||||
end
|
||||
|
||||
# Sets the url instance variable to the argument given, returns the url
|
||||
# instance variable when no argument is given.
|
||||
def url(val = nil)
|
||||
return @url if val.nil?
|
||||
# Sets the `@strategy` instance variable to the provided `Symbol` or returns
|
||||
# the `@strategy` instance variable when no argument is provided. The strategy
|
||||
# symbols use snake case (e.g., `:page_match`) and correspond to the strategy
|
||||
# file name.
|
||||
# @param symbol [Symbol] symbol for the desired strategy
|
||||
# @return [Symbol, nil]
|
||||
def strategy(symbol = nil)
|
||||
case symbol
|
||||
when nil
|
||||
@strategy
|
||||
when Symbol
|
||||
@strategy = symbol
|
||||
else
|
||||
raise TypeError, "Livecheck#strategy expects a Symbol"
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the `@url` instance variable to the provided argument or returns the
|
||||
# `@url` instance variable when no argument is provided. The argument can be
|
||||
# a `String` (a URL) or a supported `Symbol` corresponding to a URL in the
|
||||
# formula (e.g., `:stable`, `:homepage`, or `:head`).
|
||||
# @param val [String, Symbol] URL to check for version information
|
||||
# @return [String, nil]
|
||||
def url(val = nil)
|
||||
@url = case val
|
||||
when nil
|
||||
return @url
|
||||
when :head, :stable, :devel
|
||||
@formula.send(val).url
|
||||
when :homepage
|
||||
@formula.homepage
|
||||
else
|
||||
when String
|
||||
val
|
||||
else
|
||||
raise TypeError, "Livecheck#url expects a String or valid Symbol"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a Hash of all instance variable values.
|
||||
# Returns a `Hash` of all instance variable values.
|
||||
# @return [Hash]
|
||||
def to_hash
|
||||
{
|
||||
"regex" => @regex,
|
||||
"skip" => @skip,
|
||||
"skip_msg" => @skip_msg,
|
||||
"strategy" => @strategy,
|
||||
"url" => @url,
|
||||
}
|
||||
end
|
||||
|
||||
@ -5,6 +5,8 @@ require "keg"
|
||||
require "tab"
|
||||
|
||||
class Migrator
|
||||
include Context
|
||||
|
||||
class MigrationNeededError < RuntimeError
|
||||
def initialize(formula)
|
||||
super <<~EOS
|
||||
@ -209,7 +211,7 @@ class Migrator
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
onoe "Error occurred while migrating."
|
||||
puts e
|
||||
puts e.backtrace if Homebrew.args.debug?
|
||||
puts e.backtrace if debug?
|
||||
puts "Backing up..."
|
||||
ignore_interrupts { backup_oldname }
|
||||
ensure
|
||||
@ -267,7 +269,7 @@ class Migrator
|
||||
oh1 "Unlinking #{Formatter.identifier(oldname)}"
|
||||
old_cellar.subdirs.each do |d|
|
||||
keg = Keg.new(d)
|
||||
keg.unlink
|
||||
keg.unlink(verbose: verbose?)
|
||||
end
|
||||
end
|
||||
|
||||
@ -275,7 +277,7 @@ class Migrator
|
||||
oh1 "Temporarily unlinking #{Formatter.identifier(newname)}"
|
||||
new_cellar.subdirs.each do |d|
|
||||
keg = Keg.new(d)
|
||||
keg.unlink
|
||||
keg.unlink(verbose: verbose?)
|
||||
end
|
||||
end
|
||||
|
||||
@ -288,7 +290,7 @@ class Migrator
|
||||
# If formula is keg-only we also optlink it.
|
||||
if formula.keg_only? || !old_linked_keg_record
|
||||
begin
|
||||
new_keg.optlink
|
||||
new_keg.optlink(verbose: verbose?)
|
||||
rescue Keg::LinkError => e
|
||||
onoe "Failed to create #{formula.opt_prefix}"
|
||||
raise
|
||||
@ -299,15 +301,13 @@ class Migrator
|
||||
new_keg.remove_linked_keg_record if new_keg.linked?
|
||||
|
||||
begin
|
||||
mode = OpenStruct.new(overwrite: true)
|
||||
new_keg.link(mode)
|
||||
new_keg.link(overwrite: true, verbose: verbose?)
|
||||
rescue Keg::ConflictError => e
|
||||
onoe "Error while executing `brew link` step on #{newname}"
|
||||
puts e
|
||||
puts
|
||||
puts "Possible conflicting files are:"
|
||||
mode = OpenStruct.new(dry_run: true, overwrite: true)
|
||||
new_keg.link(mode)
|
||||
new_keg.link(dry_run: true, overwrite: true, verbose: verbose?)
|
||||
raise
|
||||
rescue Keg::LinkError => e
|
||||
onoe "Error while linking"
|
||||
@ -318,8 +318,8 @@ class Migrator
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
onoe "An unexpected error occurred during linking"
|
||||
puts e
|
||||
puts e.backtrace if Homebrew.args.debug?
|
||||
ignore_interrupts { new_keg.unlink }
|
||||
puts e.backtrace if debug?
|
||||
ignore_interrupts { new_keg.unlink(verbose: verbose?) }
|
||||
raise
|
||||
end
|
||||
end
|
||||
@ -384,7 +384,7 @@ class Migrator
|
||||
if new_cellar.exist?
|
||||
new_cellar.subdirs.each do |d|
|
||||
newname_keg = Keg.new(d)
|
||||
newname_keg.unlink
|
||||
newname_keg.unlink(verbose: verbose?)
|
||||
newname_keg.uninstall if new_cellar_existed
|
||||
end
|
||||
end
|
||||
@ -396,16 +396,16 @@ class Migrator
|
||||
# create a keg using its old path
|
||||
if old_linked_keg_record
|
||||
begin
|
||||
old_linked_keg.link
|
||||
old_linked_keg.link(verbose: verbose?)
|
||||
rescue Keg::LinkError
|
||||
old_linked_keg.unlink
|
||||
old_linked_keg.unlink(verbose: verbose?)
|
||||
raise
|
||||
rescue Keg::AlreadyLinkedError
|
||||
old_linked_keg.unlink
|
||||
old_linked_keg.unlink(verbose: verbose?)
|
||||
retry
|
||||
end
|
||||
else
|
||||
old_linked_keg.optlink
|
||||
old_linked_keg.optlink(verbose: verbose?)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -71,41 +71,19 @@ module ELFShim
|
||||
def rpath
|
||||
return @rpath if defined? @rpath
|
||||
|
||||
@rpath = if HOMEBREW_PATCHELF_RB
|
||||
rpath_using_patchelf_rb
|
||||
else
|
||||
rpath_using_patchelf
|
||||
end
|
||||
@rpath = rpath_using_patchelf_rb
|
||||
end
|
||||
|
||||
def interpreter
|
||||
return @interpreter if defined? @interpreter
|
||||
|
||||
@interpreter = if HOMEBREW_PATCHELF_RB
|
||||
patchelf_patcher.interpreter
|
||||
elsif (patchelf = DevelopmentTools.locate "patchelf")
|
||||
interp = Utils.popen_read(patchelf, "--print-interpreter", to_s, err: :out).strip
|
||||
$CHILD_STATUS.success? ? interp : nil
|
||||
elsif (file = DevelopmentTools.locate("file"))
|
||||
output = Utils.popen_read(file, "-L", "-b", to_s, err: :out).strip
|
||||
output[/^ELF.*, interpreter (.+?), /, 1]
|
||||
else
|
||||
raise "Please install either patchelf or file."
|
||||
end
|
||||
@interpreter = patchelf_patcher.interpreter
|
||||
end
|
||||
|
||||
def dynamic_elf?
|
||||
return @dynamic_elf if defined? @dynamic_elf
|
||||
|
||||
@dynamic_elf = if HOMEBREW_PATCHELF_RB
|
||||
patchelf_patcher.elf.segment_by_type(:DYNAMIC).present?
|
||||
elsif which "readelf"
|
||||
Utils.popen_read("readelf", "-l", to_path).include?(" DYNAMIC ")
|
||||
elsif which "file"
|
||||
!Utils.popen_read("file", "-L", "-b", to_path)[/dynamic|shared/].nil?
|
||||
else
|
||||
raise "Please install either readelf (from binutils) or file."
|
||||
end
|
||||
@dynamic_elf = patchelf_patcher.elf.segment_by_type(:DYNAMIC).present?
|
||||
end
|
||||
|
||||
class Metadata
|
||||
@ -139,81 +117,20 @@ module ELFShim
|
||||
def needed_libraries(path)
|
||||
return [nil, []] unless path.dynamic_elf?
|
||||
|
||||
if HOMEBREW_PATCHELF_RB
|
||||
needed_libraries_using_patchelf_rb path
|
||||
elsif DevelopmentTools.locate "readelf"
|
||||
needed_libraries_using_readelf path
|
||||
elsif DevelopmentTools.locate "patchelf"
|
||||
needed_libraries_using_patchelf path
|
||||
else
|
||||
return [nil, []] if path.basename.to_s == "patchelf"
|
||||
|
||||
raise "patchelf must be installed: brew install patchelf"
|
||||
end
|
||||
end
|
||||
|
||||
def needed_libraries_using_patchelf_rb(path)
|
||||
patcher = path.patchelf_patcher
|
||||
[patcher.soname, patcher.needed]
|
||||
end
|
||||
|
||||
def needed_libraries_using_patchelf(path)
|
||||
patchelf = DevelopmentTools.locate "patchelf"
|
||||
if path.dylib?
|
||||
command = [patchelf, "--print-soname", path.expand_path.to_s]
|
||||
soname = Utils.safe_popen_read(*command).chomp
|
||||
end
|
||||
command = [patchelf, "--print-needed", path.expand_path.to_s]
|
||||
needed = Utils.safe_popen_read(*command).split("\n")
|
||||
[soname, needed]
|
||||
end
|
||||
|
||||
def needed_libraries_using_readelf(path)
|
||||
soname = nil
|
||||
needed = []
|
||||
command = ["readelf", "-d", path.expand_path.to_s]
|
||||
lines = Utils.popen_read(*command, err: :out).split("\n")
|
||||
lines.each do |s|
|
||||
next if s.start_with?("readelf: Warning: possibly corrupt ELF header")
|
||||
|
||||
filename = s[/\[(.*)\]/, 1]
|
||||
next if filename.nil?
|
||||
|
||||
if s.include? "(SONAME)"
|
||||
soname = filename
|
||||
elsif s.include? "(NEEDED)"
|
||||
needed << filename
|
||||
end
|
||||
end
|
||||
[soname, needed]
|
||||
end
|
||||
end
|
||||
|
||||
def rpath_using_patchelf_rb
|
||||
patchelf_patcher.runpath || patchelf_patcher.rpath
|
||||
end
|
||||
|
||||
def rpath_using_patchelf
|
||||
patchelf = DevelopmentTools.locate "patchelf"
|
||||
odie "Could not locate patchelf, please: brew install patchelf." if patchelf.nil?
|
||||
|
||||
cmd_rpath = [patchelf, "--print-rpath", to_s]
|
||||
rpath = Utils.popen_read(*cmd_rpath, err: :out).strip
|
||||
|
||||
# patchelf requires that the ELF file have a .dynstr section.
|
||||
# Skip ELF files that do not have a .dynstr section.
|
||||
return if ["cannot find section .dynstr", "strange: no string table"].include?(rpath)
|
||||
|
||||
unless $CHILD_STATUS.success?
|
||||
raise ErrorDuringExecution.new(cmd_rpath, status: $CHILD_STATUS, output: [[:stderr, rpath]])
|
||||
end
|
||||
|
||||
rpath unless rpath.blank?
|
||||
end
|
||||
|
||||
def patchelf_patcher
|
||||
return unless HOMEBREW_PATCHELF_RB
|
||||
|
||||
Homebrew.install_bundler_gems!
|
||||
require "patchelf"
|
||||
@patchelf_patcher ||= PatchELF::Patcher.new to_s, on_error: :silent
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# enables experimental readelf.rb, patchelf support.
|
||||
HOMEBREW_PATCHELF_RB = ENV["HOMEBREW_PATCHELF_RB"].present?.freeze
|
||||
|
||||
module Homebrew
|
||||
DEFAULT_PREFIX ||= if Homebrew::EnvConfig.force_homebrew_on_linux?
|
||||
HOMEBREW_DEFAULT_PREFIX
|
||||
|
||||
@ -5,7 +5,7 @@ class Keg
|
||||
return if file.dylib_id == id
|
||||
|
||||
@require_relocation = true
|
||||
odebug "Changing dylib ID of #{file}\n from #{file.dylib_id}\n to #{id}" if Homebrew.args.debug?
|
||||
odebug "Changing dylib ID of #{file}\n from #{file.dylib_id}\n to #{id}"
|
||||
MachO::Tools.change_dylib_id(file, id, strict: false)
|
||||
rescue MachO::MachOError
|
||||
onoe <<~EOS
|
||||
@ -20,7 +20,7 @@ class Keg
|
||||
return if old == new
|
||||
|
||||
@require_relocation = true
|
||||
odebug "Changing install name in #{file}\n from #{old}\n to #{new}" if Homebrew.args.debug?
|
||||
odebug "Changing install name in #{file}\n from #{old}\n to #{new}"
|
||||
MachO::Tools.change_install_name(file, old, new, strict: false)
|
||||
rescue MachO::MachOError
|
||||
onoe <<~EOS
|
||||
|
||||
@ -17,7 +17,7 @@ begin
|
||||
trap("INT", old_trap)
|
||||
|
||||
formula = args.resolved_formulae.first
|
||||
formula.extend(Debrew::Formula) if Homebrew.args.debug?
|
||||
formula.extend(Debrew::Formula) if args.debug?
|
||||
formula.run_post_install
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
error_pipe.puts e.to_json
|
||||
|
||||
@ -24,7 +24,8 @@ module Homebrew
|
||||
options &= f.options
|
||||
|
||||
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?,
|
||||
build_from_source_formulae: args.build_from_source_formulae)
|
||||
build_from_source_formulae: args.build_from_source_formulae,
|
||||
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?)
|
||||
fi.options = options
|
||||
fi.force = args.force?
|
||||
fi.keep_tmp = args.keep_tmp?
|
||||
@ -48,7 +49,7 @@ module Homebrew
|
||||
rescue FormulaInstallationAlreadyAttemptedError
|
||||
nil
|
||||
rescue Exception # rubocop:disable Lint/RescueException
|
||||
ignore_interrupts { restore_backup(keg, keg_was_linked) }
|
||||
ignore_interrupts { restore_backup(keg, keg_was_linked, verbose: args.verbose?) }
|
||||
raise
|
||||
else
|
||||
begin
|
||||
@ -73,7 +74,7 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def restore_backup(keg, keg_was_linked)
|
||||
def restore_backup(keg, keg_was_linked, verbose:)
|
||||
path = backup_path(keg)
|
||||
|
||||
return unless path.directory?
|
||||
@ -81,7 +82,7 @@ module Homebrew
|
||||
Pathname.new(keg).rmtree if keg.exist?
|
||||
|
||||
path.rename keg
|
||||
keg.link if keg_was_linked
|
||||
keg.link(verbose: verbose) if keg_was_linked
|
||||
end
|
||||
|
||||
def backup_path(path)
|
||||
|
||||
@ -9,6 +9,7 @@ require "mktemp"
|
||||
# primary formula download, along with other declared resources, are instances
|
||||
# of this class.
|
||||
class Resource
|
||||
include Context
|
||||
include FileUtils
|
||||
|
||||
attr_reader :mirrors, :specs, :using, :source_modified_time, :patches, :owner
|
||||
@ -140,7 +141,7 @@ class Resource
|
||||
|
||||
def verify_download_integrity(fn)
|
||||
if fn.file?
|
||||
ohai "Verifying #{fn.basename} checksum" if Homebrew.args.verbose?
|
||||
ohai "Verifying #{fn.basename} checksum" if verbose?
|
||||
fn.verify_checksum(checksum)
|
||||
end
|
||||
rescue ChecksumMissingError
|
||||
|
||||
@ -29,7 +29,13 @@ module RuboCop
|
||||
end
|
||||
|
||||
def cask_token
|
||||
@cask_token ||= pair_node.val_node.children.first
|
||||
@cask_token ||= begin
|
||||
if dsl_version?
|
||||
pair_node.val_node.children.first
|
||||
else
|
||||
method_node.first_argument.str_content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def hash_node
|
||||
|
||||
@ -6,7 +6,8 @@ module RuboCop
|
||||
module Constants
|
||||
STANZA_GROUPS = [
|
||||
[:version, :sha256],
|
||||
[:url, :appcast, :name, :homepage],
|
||||
[:language],
|
||||
[:url, :appcast, :name, :desc, :homepage],
|
||||
[
|
||||
:auto_updates,
|
||||
:conflicts_with,
|
||||
@ -32,6 +33,7 @@ module RuboCop
|
||||
:service,
|
||||
:audio_unit_plugin,
|
||||
:vst_plugin,
|
||||
:vst3_plugin,
|
||||
:artifact,
|
||||
:stage_only,
|
||||
],
|
||||
|
||||
30
Library/Homebrew/rubocops/cask/desc.rb
Normal file
30
Library/Homebrew/rubocops/cask/desc.rb
Normal file
@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "forwardable"
|
||||
require "uri"
|
||||
require "rubocops/cask/mixin/on_desc_stanza"
|
||||
require "rubocops/shared/desc_helper"
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Cask
|
||||
# This cop audits `desc` in Casks.
|
||||
# See the `DescHelper` module for details of the checks.
|
||||
class Desc < Cop
|
||||
include OnDescStanza
|
||||
include DescHelper
|
||||
|
||||
def on_desc_stanza(stanza)
|
||||
name = cask_block.header.cask_token
|
||||
desc_call = stanza.stanza_node
|
||||
audit_desc(:cask, name, desc_call)
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
name = cask_block.header.cask_token
|
||||
autocorrect_desc(node, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
Library/Homebrew/rubocops/cask/mixin/on_desc_stanza.rb
Normal file
28
Library/Homebrew/rubocops/cask/mixin/on_desc_stanza.rb
Normal file
@ -0,0 +1,28 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Cask
|
||||
# Common functionality for checking desc stanzas.
|
||||
module OnDescStanza
|
||||
extend Forwardable
|
||||
include CaskHelp
|
||||
|
||||
def on_cask(cask_block)
|
||||
@cask_block = cask_block
|
||||
|
||||
toplevel_stanzas.select(&:desc?).each do |stanza|
|
||||
on_desc_stanza(stanza)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :cask_block
|
||||
|
||||
def_delegators :cask_block,
|
||||
:toplevel_stanzas
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -6,7 +6,7 @@ module RuboCop
|
||||
module Cop
|
||||
module Cask
|
||||
# This cop checks that a cask's stanzas are grouped correctly.
|
||||
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order
|
||||
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order
|
||||
# for more info.
|
||||
class StanzaGrouping < Cop
|
||||
extend Forwardable
|
||||
|
||||
@ -6,7 +6,7 @@ module RuboCop
|
||||
module Cop
|
||||
module Cask
|
||||
# This cop checks that a cask's stanzas are ordered correctly.
|
||||
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order
|
||||
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order
|
||||
# for more info.
|
||||
class StanzaOrder < Cop
|
||||
extend Forwardable
|
||||
|
||||
@ -10,11 +10,13 @@ ensure
|
||||
end
|
||||
|
||||
require "extend/string"
|
||||
require "rubocops/shared/helper_functions"
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
class FormulaCop < Cop
|
||||
include RangeHelp
|
||||
include HelperFunctions
|
||||
|
||||
attr_accessor :file_path
|
||||
|
||||
@ -32,28 +34,6 @@ module RuboCop
|
||||
audit_formula(node, class_node, parent_class_node, @body)
|
||||
end
|
||||
|
||||
# Checks for regex match of pattern in the node and
|
||||
# sets the appropriate instance variables to report the match
|
||||
def regex_match_group(node, pattern)
|
||||
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
|
||||
match_object = string_repr.match(pattern)
|
||||
return unless match_object
|
||||
|
||||
node_begin_pos = start_column(node)
|
||||
line_begin_pos = line_start_column(node)
|
||||
@column = if node_begin_pos == line_begin_pos
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos
|
||||
else
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
|
||||
end
|
||||
@length = match_object.to_s.length
|
||||
@line_no = line_number(node)
|
||||
@source_buf = source_buffer(node)
|
||||
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
|
||||
@offensive_node = node
|
||||
match_object
|
||||
end
|
||||
|
||||
# Yields to block when there is a match.
|
||||
# @param urls [Array] url/mirror method call nodes
|
||||
# @param regex [Regexp] pattern to match urls
|
||||
@ -442,26 +422,11 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the begin position of the node's line in source code
|
||||
def line_start_column(node)
|
||||
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
|
||||
end
|
||||
|
||||
# Returns the begin position of the node in source code
|
||||
def start_column(node)
|
||||
node.source_range.begin_pos
|
||||
end
|
||||
|
||||
# Returns the ending position of the node in source code
|
||||
def end_column(node)
|
||||
node.source_range.end_pos
|
||||
end
|
||||
|
||||
# Returns the line number of the node
|
||||
def line_number(node)
|
||||
node.loc.line
|
||||
end
|
||||
|
||||
# Returns the class node's name, nil if not a class node
|
||||
def class_name(node)
|
||||
@offensive_node = node
|
||||
@ -484,35 +449,6 @@ module RuboCop
|
||||
block.loc.end.line - block.loc.begin.line
|
||||
end
|
||||
|
||||
# Source buffer is required as an argument to report style violations
|
||||
def source_buffer(node)
|
||||
node.source_range.source_buffer
|
||||
end
|
||||
|
||||
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const
|
||||
def string_content(node)
|
||||
case node.type
|
||||
when :str
|
||||
node.str_content
|
||||
when :dstr
|
||||
content = ""
|
||||
node.each_child_node(:str, :begin) do |child|
|
||||
content += if child.begin_type?
|
||||
child.source
|
||||
else
|
||||
child.str_content
|
||||
end
|
||||
end
|
||||
content
|
||||
when :const
|
||||
node.const_name
|
||||
when :sym
|
||||
node.children.first.to_s
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the formula is versioned
|
||||
def versioned_formula?
|
||||
@formula_name.include?("@")
|
||||
@ -532,10 +468,6 @@ module RuboCop
|
||||
match_obj[1]
|
||||
end
|
||||
|
||||
def problem(msg)
|
||||
add_offense(@offensive_node, location: @offense_source_range, message: msg)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def formula_class?(node)
|
||||
|
||||
@ -1,105 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rubocops/extend/formula"
|
||||
require "rubocops/shared/desc_helper"
|
||||
require "extend/string"
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module FormulaAudit
|
||||
# This cop audits `desc` in Formulae.
|
||||
#
|
||||
# - Checks for existence of `desc`
|
||||
# - Checks if size of `desc` > 80
|
||||
# - Checks for leading/trailing whitespace in `desc`
|
||||
# - Checks if `desc` begins with an article
|
||||
# - Checks for correct usage of `command-line` in `desc`
|
||||
# - Checks description starts with a capital letter
|
||||
# - Checks if `desc` contains the formula name
|
||||
# - Checks if `desc` ends with a full stop (apart from in the case of "etc.")
|
||||
# See the `DescHelper` module for details of the checks.
|
||||
class Desc < FormulaCop
|
||||
VALID_LOWERCASE_WORDS = %w[
|
||||
macOS
|
||||
].freeze
|
||||
include DescHelper
|
||||
|
||||
def audit_formula(_node, _class_node, _parent_class_node, body_node)
|
||||
desc_call = find_node_method_by_name(body_node, :desc)
|
||||
|
||||
# Check if a formula's desc is present
|
||||
if desc_call.nil?
|
||||
problem "Formula should have a desc (Description)."
|
||||
return
|
||||
end
|
||||
|
||||
desc = parameters(desc_call).first
|
||||
|
||||
# Check the formula's desc length. Should be >0 and <80 characters.
|
||||
pure_desc_length = string_content(desc).length
|
||||
if pure_desc_length.zero?
|
||||
problem "The desc (description) should not be an empty string."
|
||||
return
|
||||
end
|
||||
|
||||
# Check for leading whitespace.
|
||||
problem "Description shouldn't have a leading space" if regex_match_group(desc, /^\s+/)
|
||||
|
||||
# Check for trailing whitespace.
|
||||
problem "Description shouldn't have a trailing space" if regex_match_group(desc, /\s+$/)
|
||||
|
||||
# Check if command-line is wrongly used in formula's desc
|
||||
if match = regex_match_group(desc, /(command ?line)/i)
|
||||
c = match.to_s[0]
|
||||
problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\""
|
||||
end
|
||||
|
||||
# Check if a/an are used in a formula's desc
|
||||
if match = regex_match_group(desc, /^(an?)\s/i)
|
||||
problem "Description shouldn't start with an indefinite article, i.e. \"#{match.to_s.strip}\""
|
||||
end
|
||||
|
||||
# Check if invalid uppercase words are at the start of a
|
||||
# formula's desc
|
||||
if !VALID_LOWERCASE_WORDS.include?(string_content(desc).split.first) &&
|
||||
regex_match_group(desc, /^[a-z]/)
|
||||
problem "Description should start with a capital letter"
|
||||
end
|
||||
|
||||
# Check if formula's desc starts with formula's name
|
||||
if regex_match_group(desc, /^#{@formula_name} /i)
|
||||
problem "Description shouldn't start with the formula name"
|
||||
end
|
||||
|
||||
# Check if a full stop is used at the end of a formula's desc (apart from in the case of "etc.")
|
||||
if regex_match_group(desc, /\.$/) && !string_content(desc).end_with?("etc.")
|
||||
problem "Description shouldn't end with a full stop"
|
||||
end
|
||||
|
||||
desc_length = "#{@formula_name}: #{string_content(desc)}".length
|
||||
max_desc_length = 80
|
||||
return if desc_length <= max_desc_length
|
||||
|
||||
problem "Description is too long. \"name: desc\" should be less than #{max_desc_length} characters. " \
|
||||
"Length is calculated as #{@formula_name} + desc. (currently #{desc_length})"
|
||||
audit_desc(:formula, @formula_name, desc_call)
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
correction = node.source
|
||||
first_word = string_content(node).split.first
|
||||
unless VALID_LOWERCASE_WORDS.include?(first_word)
|
||||
first_char = first_word.to_s[0]
|
||||
correction.sub!(/^(['"]?)([a-z])/, "\\1#{first_char.upcase}") if first_char
|
||||
end
|
||||
correction.sub!(/^(['"]?)an?\s/i, "\\1")
|
||||
correction.gsub!(/(ommand ?line)/i, "ommand-line")
|
||||
correction.gsub!(/(^|[^a-z])#{@formula_name}([^a-z]|$)/i, "\\1\\2")
|
||||
correction.gsub!(/^(['"]?)\s+/, "\\1")
|
||||
correction.gsub!(/\s+(['"]?)$/, "\\1")
|
||||
correction.gsub!(/\.(['"]?)$/, "\\1")
|
||||
correction.gsub!(/^\s+/, "")
|
||||
correction.gsub!(/\s+$/, "")
|
||||
corrector.insert_before(node.source_range, correction)
|
||||
corrector.remove(node.source_range)
|
||||
end
|
||||
autocorrect_desc(node, @formula_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -58,6 +58,14 @@ module RuboCop
|
||||
when %r{^http://([^/]*)\.(sf|sourceforge)\.net(/|$)}
|
||||
problem "#{homepage} should be `https://#{Regexp.last_match(1)}.sourceforge.io/`"
|
||||
|
||||
when /readthedocs\.org/
|
||||
offending_node(parameters(homepage_node).first)
|
||||
problem "#{homepage} should be `#{homepage.sub("readthedocs.org", "readthedocs.io")}`"
|
||||
|
||||
when %r{^https://github.com.*\.git}
|
||||
offending_node(parameters(homepage_node).first)
|
||||
problem "GitHub homepages (`#{homepage}`) should not end with .git"
|
||||
|
||||
# There's an auto-redirect here, but this mistake is incredibly common too.
|
||||
# Only applies to the homepage and subdomains for now, not the FTP URLs.
|
||||
when %r{^http://((?:build|cloud|developer|download|extensions|git|
|
||||
@ -80,6 +88,17 @@ module RuboCop
|
||||
problem "Please use https:// for #{homepage}"
|
||||
end
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
return if node.nil?
|
||||
|
||||
homepage = string_content(node)
|
||||
homepage.sub!("readthedocs.org", "readthedocs.io")
|
||||
homepage.delete_suffix!(".git") if homepage.start_with?("https://github.com")
|
||||
corrector.replace(node.source_range, "\"#{homepage}\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -284,9 +284,7 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
|
||||
find_instance_call(body_node, "ARGV") do |method_node|
|
||||
next if [:debug?, :verbose?, :value].index(method_node.method_name)
|
||||
|
||||
find_instance_call(body_node, "ARGV") do |_method_node|
|
||||
problem "Use build instead of ARGV to check options"
|
||||
end
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ require "rubocops/cask/extend/string"
|
||||
require "rubocops/cask/extend/node"
|
||||
require "rubocops/cask/mixin/cask_help"
|
||||
require "rubocops/cask/mixin/on_homepage_stanza"
|
||||
require "rubocops/cask/desc"
|
||||
require "rubocops/cask/homepage_matches_url"
|
||||
require "rubocops/cask/homepage_url_trailing_slash"
|
||||
require "rubocops/cask/no_dsl_version"
|
||||
|
||||
99
Library/Homebrew/rubocops/shared/desc_helper.rb
Normal file
99
Library/Homebrew/rubocops/shared/desc_helper.rb
Normal file
@ -0,0 +1,99 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rubocops/shared/helper_functions"
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
# This module performs common checks the `desc` field in both Formulae and Casks.
|
||||
module DescHelper
|
||||
include HelperFunctions
|
||||
|
||||
VALID_LOWERCASE_WORDS = %w[
|
||||
macOS
|
||||
].freeze
|
||||
|
||||
def audit_desc(type, name, desc_call)
|
||||
# Check if a desc is present.
|
||||
if desc_call.nil?
|
||||
problem "#{type.to_s.capitalize} should have a desc (Description)."
|
||||
return
|
||||
end
|
||||
|
||||
desc = desc_call.first_argument
|
||||
|
||||
# Check if the desc is empty.
|
||||
pure_desc_length = string_content(desc).length
|
||||
if pure_desc_length.zero?
|
||||
problem "The desc (description) should not be an empty string."
|
||||
return
|
||||
end
|
||||
|
||||
# Check the desc for leading whitespace.
|
||||
problem "Description shouldn't have leading spaces." if regex_match_group(desc, /^\s+/)
|
||||
|
||||
# Check the desc for trailing whitespace.
|
||||
problem "Description shouldn't have trailing spaces." if regex_match_group(desc, /\s+$/)
|
||||
|
||||
# Check if "command-line" is spelled incorrectly in the desc.
|
||||
if match = regex_match_group(desc, /(command ?line)/i)
|
||||
c = match.to_s[0]
|
||||
problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\"."
|
||||
end
|
||||
|
||||
# Check if the desc starts with "A" or "An".
|
||||
if match = regex_match_group(desc, /^(an?)(?=\s)/i)
|
||||
problem "Description shouldn't start with an indefinite article, i.e. \"#{match}\"."
|
||||
end
|
||||
|
||||
# Check if invalid lowercase words are at the start of a desc.
|
||||
if !VALID_LOWERCASE_WORDS.include?(string_content(desc).split.first) &&
|
||||
regex_match_group(desc, /^[a-z]/)
|
||||
problem "Description should start with a capital letter."
|
||||
end
|
||||
|
||||
# Check if the desc starts with the formula's or cask's name.
|
||||
problem "Description shouldn't start with the #{type} name." if regex_match_group(desc, /^#{name} /i)
|
||||
|
||||
# Check if a full stop is used at the end of a desc (apart from in the case of "etc.").
|
||||
if regex_match_group(desc, /\.$/) && !string_content(desc).end_with?("etc.")
|
||||
problem "Description shouldn't end with a full stop."
|
||||
end
|
||||
|
||||
# Check if the desc length exceeds 80 characters.
|
||||
desc_length = "#{name}: #{string_content(desc)}".length
|
||||
max_desc_length = 80
|
||||
return if desc_length <= max_desc_length
|
||||
|
||||
problem "Description is too long. \"name: desc\" should be less than #{max_desc_length} characters. " \
|
||||
"The current combined length is #{desc_length}."
|
||||
end
|
||||
|
||||
def autocorrect_desc(node, name)
|
||||
lambda do |corrector|
|
||||
/\A(?<quote>["'])(?<correction>.*)(?:\k<quote>)\Z/ =~ node.source
|
||||
|
||||
next if correction.nil?
|
||||
|
||||
correction.gsub!(/^\s+/, "")
|
||||
correction.gsub!(/\s+$/, "")
|
||||
|
||||
correction.sub!(/^an?\s+/i, "")
|
||||
|
||||
first_word = correction.split.first
|
||||
unless VALID_LOWERCASE_WORDS.include?(first_word)
|
||||
first_char = first_word.to_s[0]
|
||||
correction[0] = first_char.upcase if first_char
|
||||
end
|
||||
|
||||
correction.gsub!(/(ommand ?line)/i, "ommand-line")
|
||||
correction.gsub!(/(^|[^a-z])#{name}([^a-z]|$)/i, "\\1\\2")
|
||||
correction.gsub!(/^\s+/, "")
|
||||
correction.gsub!(/\s+$/, "")
|
||||
correction.gsub!(/\.$/, "")
|
||||
|
||||
corrector.replace(node.source_range, "#{quote}#{correction}#{quote}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
79
Library/Homebrew/rubocops/shared/helper_functions.rb
Normal file
79
Library/Homebrew/rubocops/shared/helper_functions.rb
Normal file
@ -0,0 +1,79 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module HelperFunctions
|
||||
include RangeHelp
|
||||
|
||||
# Checks for regex match of pattern in the node and
|
||||
# sets the appropriate instance variables to report the match
|
||||
def regex_match_group(node, pattern)
|
||||
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
|
||||
match_object = string_repr.match(pattern)
|
||||
return unless match_object
|
||||
|
||||
node_begin_pos = start_column(node)
|
||||
line_begin_pos = line_start_column(node)
|
||||
@column = if node_begin_pos == line_begin_pos
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos
|
||||
else
|
||||
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
|
||||
end
|
||||
@length = match_object.to_s.length
|
||||
@line_no = line_number(node)
|
||||
@source_buf = source_buffer(node)
|
||||
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
|
||||
@offensive_node = node
|
||||
match_object
|
||||
end
|
||||
|
||||
# Returns the begin position of the node's line in source code
|
||||
def line_start_column(node)
|
||||
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
|
||||
end
|
||||
|
||||
# Returns the begin position of the node in source code
|
||||
def start_column(node)
|
||||
node.source_range.begin_pos
|
||||
end
|
||||
|
||||
# Returns the line number of the node
|
||||
def line_number(node)
|
||||
node.loc.line
|
||||
end
|
||||
|
||||
# Source buffer is required as an argument to report style violations
|
||||
def source_buffer(node)
|
||||
node.source_range.source_buffer
|
||||
end
|
||||
|
||||
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const
|
||||
def string_content(node)
|
||||
case node.type
|
||||
when :str
|
||||
node.str_content
|
||||
when :dstr
|
||||
content = ""
|
||||
node.each_child_node(:str, :begin) do |child|
|
||||
content += if child.begin_type?
|
||||
child.source
|
||||
else
|
||||
child.str_content
|
||||
end
|
||||
end
|
||||
content
|
||||
when :const
|
||||
node.const_name
|
||||
when :sym
|
||||
node.children.first.to_s
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def problem(msg)
|
||||
add_offense(@offensive_node, location: @offense_source_range, message: msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user