Merge branch 'mlh-outdated-packages' of github.com:MLH-Fellowship/brew into mlh-outdated-packages
This commit is contained in:
commit
0402f0c95e
8
.github/codecov.yml
vendored
Normal file
8
.github/codecov.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
fixes:
|
||||
- "::Library/Homebrew/"
|
||||
coverage:
|
||||
round: nearest
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 0.05%
|
||||
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
@ -114,6 +114,9 @@ jobs:
|
||||
- name: Run brew man
|
||||
run: brew man --fail-if-changed
|
||||
|
||||
- name: Check for outdated license data
|
||||
run: brew update-license-data --fail-if-changed
|
||||
|
||||
- name: Run brew tests
|
||||
run: |
|
||||
# brew tests doesn't like world writable directories
|
||||
@ -136,8 +139,7 @@ jobs:
|
||||
HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# set variables for coverage reporting
|
||||
HOMEBREW_CI_NAME: github-actions
|
||||
HOMEBREW_COVERALLS_REPO_TOKEN: 3F6U6ZqctoNJwKyREremsqMgpU3qYgxFk
|
||||
HOMEBREW_CODECOV_TOKEN: 3ea0364c-80ce-47a3-9fba-93a940d4b5d7
|
||||
|
||||
# These cannot be queried at the macOS level on GitHub Actions.
|
||||
HOMEBREW_LANGUAGES: en-GB
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -86,7 +86,7 @@
|
||||
**/vendor/bundle/ruby/*/gems/byebug-*/
|
||||
**/vendor/bundle/ruby/*/gems/coderay-*/
|
||||
**/vendor/bundle/ruby/*/gems/connection_pool-*/
|
||||
**/vendor/bundle/ruby/*/gems/coveralls-*/
|
||||
**/vendor/bundle/ruby/*/gems/codecov-*/
|
||||
**/vendor/bundle/ruby/*/gems/diff-lcs-*/
|
||||
**/vendor/bundle/ruby/*/gems/docile-*/
|
||||
**/vendor/bundle/ruby/*/gems/domain_name-*/
|
||||
@ -127,11 +127,9 @@
|
||||
**/vendor/bundle/ruby/*/gems/ruby-prof-*/
|
||||
**/vendor/bundle/ruby/*/gems/ruby-progressbar-*/
|
||||
**/vendor/bundle/ruby/*/gems/simplecov-*/
|
||||
**/vendor/bundle/ruby/*/gems/simplecov-cobertura-*/
|
||||
**/vendor/bundle/ruby/*/gems/simplecov-html-*/
|
||||
**/vendor/bundle/ruby/*/gems/term-ansicolor-*/
|
||||
**/vendor/bundle/ruby/*/gems/thor-*/
|
||||
**/vendor/bundle/ruby/*/gems/tins-*/
|
||||
**/vendor/bundle/ruby/*/gems/url-*/
|
||||
**/vendor/bundle/ruby/*/gems/unf_ext-*/
|
||||
**/vendor/bundle/ruby/*/gems/unf-*/
|
||||
**/vendor/bundle/ruby/*/gems/unicode-display_width-*/
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "English"
|
||||
|
||||
@ -20,7 +21,8 @@ SimpleCov.start do
|
||||
|
||||
# Just save result, but don't write formatted output.
|
||||
coverage_result = Coverage.result
|
||||
SimpleCov.add_not_loaded_files(coverage_result)
|
||||
# TODO: this method is private, find a better way.
|
||||
SimpleCov.send(:add_not_loaded_files, coverage_result)
|
||||
simplecov_result = SimpleCov::Result.new(coverage_result)
|
||||
SimpleCov::ResultMerger.store_result(simplecov_result)
|
||||
|
||||
@ -50,8 +52,8 @@ SimpleCov.start do
|
||||
|
||||
require "rbconfig"
|
||||
host_os = RbConfig::CONFIG["host_os"]
|
||||
add_filter %r{/os/mac} if host_os !~ /darwin/
|
||||
add_filter %r{/os/linux} if host_os !~ /linux/
|
||||
add_filter %r{/os/mac} unless /darwin/.match?(host_os)
|
||||
add_filter %r{/os/linux} unless /linux/.match?(host_os)
|
||||
|
||||
# Add groups and the proper project name to the output.
|
||||
project_name "Homebrew"
|
||||
|
||||
@ -4,7 +4,7 @@ source "https://rubygems.org"
|
||||
|
||||
# installed gems
|
||||
gem "byebug"
|
||||
gem "coveralls", "~> 0.8", require: false
|
||||
gem "codecov", require: false
|
||||
gem "parallel_tests"
|
||||
gem "ronn", require: false
|
||||
gem "rspec"
|
||||
|
||||
@ -9,15 +9,13 @@ GEM
|
||||
zeitwerk (~> 2.2, >= 2.2.2)
|
||||
ast (2.4.1)
|
||||
byebug (11.1.3)
|
||||
codecov (0.1.17)
|
||||
json
|
||||
simplecov
|
||||
url
|
||||
concurrent-ruby (1.1.6)
|
||||
connection_pool (2.2.3)
|
||||
coveralls (0.8.23)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov (~> 0.16.1)
|
||||
term-ansicolor (~> 1.3)
|
||||
thor (>= 0.19.4, < 2.0)
|
||||
tins (~> 1.6)
|
||||
diff-lcs (1.4.3)
|
||||
diff-lcs (1.4.4)
|
||||
docile (1.3.2)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
@ -26,7 +24,7 @@ GEM
|
||||
domain_name (~> 0.5)
|
||||
i18n (1.8.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.3.0)
|
||||
json (2.3.1)
|
||||
mechanize (2.7.6)
|
||||
domain_name (~> 0.5, >= 0.5.1)
|
||||
http-cookie (~> 1.0)
|
||||
@ -95,28 +93,22 @@ GEM
|
||||
parser (>= 2.7.0.1)
|
||||
rubocop-performance (1.6.1)
|
||||
rubocop (>= 0.71.0)
|
||||
rubocop-rspec (1.40.0)
|
||||
rubocop-rspec (1.41.0)
|
||||
rubocop (>= 0.68.1)
|
||||
ruby-macho (2.2.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
simplecov (0.16.1)
|
||||
simplecov (0.18.5)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
sync (0.5.0)
|
||||
term-ansicolor (1.7.1)
|
||||
tins (~> 1.0)
|
||||
thor (1.0.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.12.2)
|
||||
thread_safe (0.3.6)
|
||||
tins (1.25.0)
|
||||
sync
|
||||
tzinfo (1.2.7)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
url (0.3.2)
|
||||
webrobots (0.1.2)
|
||||
zeitwerk (2.3.1)
|
||||
|
||||
@ -126,8 +118,8 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
activesupport
|
||||
byebug
|
||||
codecov
|
||||
concurrent-ruby
|
||||
coveralls (~> 0.8)
|
||||
mechanize
|
||||
parallel_tests
|
||||
plist
|
||||
@ -143,4 +135,4 @@ DEPENDENCIES
|
||||
simplecov
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
1.17.3
|
||||
|
||||
@ -41,7 +41,7 @@ class Bintray
|
||||
|
||||
def upload(local_file, repo:, package:, version:, remote_file:, sha256: nil)
|
||||
url = "#{API_URL}/content/#{@bintray_org}/#{repo}/#{package}/#{version}/#{remote_file}"
|
||||
args = ["--upload-file", local_file]
|
||||
args = ["--fail", "--upload-file", local_file]
|
||||
args += ["--header", "X-Checksum-Sha2: #{sha256}"] unless sha256.blank?
|
||||
result = open_api url, *args
|
||||
json = JSON.parse(result.stdout)
|
||||
@ -50,12 +50,15 @@ class Bintray
|
||||
result
|
||||
end
|
||||
|
||||
def publish(repo:, package:, version:, file_count:)
|
||||
def publish(repo:, package:, version:, file_count:, warn_on_error: false)
|
||||
url = "#{API_URL}/content/#{@bintray_org}/#{repo}/#{package}/#{version}/publish"
|
||||
result = open_api url, "--request", "POST"
|
||||
result = open_api url, "--request", "POST", "--fail"
|
||||
json = JSON.parse(result.stdout)
|
||||
if file_count.present? && json["files"] != file_count
|
||||
raise "Bottle publish failed: expected #{file_count} bottles, but published #{json["files"]} instead."
|
||||
message = "Bottle publish failed: expected #{file_count} bottles, but published #{json["files"]} instead."
|
||||
raise message unless warn_on_error
|
||||
|
||||
opoo message
|
||||
end
|
||||
|
||||
odebug "Published #{json["files"]} bottles"
|
||||
@ -143,7 +146,7 @@ class Bintray
|
||||
end
|
||||
end
|
||||
|
||||
def upload_bottle_json(json_files, publish_package: false)
|
||||
def upload_bottle_json(json_files, publish_package: false, warn_on_error: false)
|
||||
bottles_hash = json_files.reduce({}) do |hash, json_file|
|
||||
hash.deep_merge(JSON.parse(IO.read(json_file)))
|
||||
end
|
||||
@ -161,14 +164,19 @@ class Bintray
|
||||
|
||||
odebug "Checking remote file #{@bintray_org}/#{bintray_repo}/#{filename}"
|
||||
if file_published? repo: bintray_repo, remote_file: filename
|
||||
raise Error, <<~EOS
|
||||
#{filename} is already published.
|
||||
already_published = "#{filename} is already published."
|
||||
failed_message = <<~EOS
|
||||
#{already_published}
|
||||
Please remove it manually from:
|
||||
https://bintray.com/#{@bintray_org}/#{bintray_repo}/#{bintray_package}/view#files
|
||||
Or run:
|
||||
curl -X DELETE -u $HOMEBREW_BINTRAY_USER:$HOMEBREW_BINTRAY_KEY \\
|
||||
https://api.bintray.com/content/#{@bintray_org}/#{bintray_repo}/#{filename}
|
||||
EOS
|
||||
raise Error, failed_message unless warn_on_error
|
||||
|
||||
opoo already_published
|
||||
next
|
||||
end
|
||||
|
||||
if !formula_packaged[formula_name] && !package_exists?(repo: bintray_repo, package: bintray_package)
|
||||
@ -189,7 +197,11 @@ class Bintray
|
||||
|
||||
bottle_count = bottle_hash["bottle"]["tags"].length
|
||||
odebug "Publishing #{@bintray_org}/#{bintray_repo}/#{bintray_package}/#{version}"
|
||||
publish repo: bintray_repo, package: bintray_package, version: version, file_count: bottle_count
|
||||
publish(repo: bintray_repo,
|
||||
package: bintray_package,
|
||||
version: version,
|
||||
file_count: bottle_count,
|
||||
warn_on_error: warn_on_error)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -36,9 +36,11 @@ module Homebrew
|
||||
# Reset cache values reliant on named_args
|
||||
@formulae = nil
|
||||
@resolved_formulae = nil
|
||||
@resolved_formulae_casks = nil
|
||||
@formulae_paths = nil
|
||||
@casks = nil
|
||||
@kegs = nil
|
||||
@kegs_casks = nil
|
||||
|
||||
self[:named_args] = named_args
|
||||
self[:named_args].freeze
|
||||
@ -96,6 +98,25 @@ module Homebrew
|
||||
end.uniq(&:name).freeze
|
||||
end
|
||||
|
||||
def resolved_formulae_casks
|
||||
@resolved_formulae_casks ||= begin
|
||||
resolved_formulae = []
|
||||
casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
resolved_formulae << Formulary.resolve(name, spec: spec(nil))
|
||||
rescue FormulaUnavailableError
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No available formula or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
[resolved_formulae.freeze, casks.freeze].freeze
|
||||
end
|
||||
end
|
||||
|
||||
def formulae_paths
|
||||
@formulae_paths ||= (downcased_unique_named - casks).map do |name|
|
||||
Formulary.path(name)
|
||||
@ -108,57 +129,35 @@ module Homebrew
|
||||
end
|
||||
|
||||
def kegs
|
||||
require "keg"
|
||||
require "formula"
|
||||
require "missing_formula"
|
||||
|
||||
@kegs ||= downcased_unique_named.map do |name|
|
||||
raise UsageError if name.empty?
|
||||
|
||||
rack = Formulary.to_rack(name.downcase)
|
||||
|
||||
dirs = rack.directory? ? rack.subdirs : []
|
||||
|
||||
if dirs.empty?
|
||||
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
|
||||
$stderr.puts reason
|
||||
end
|
||||
raise NoSuchKegError, rack.basename
|
||||
end
|
||||
|
||||
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
|
||||
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
|
||||
|
||||
begin
|
||||
if opt_prefix.symlink? && opt_prefix.directory?
|
||||
Keg.new(opt_prefix.resolved_path)
|
||||
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
|
||||
Keg.new(linked_keg_ref.resolved_path)
|
||||
elsif dirs.length == 1
|
||||
Keg.new(dirs.first)
|
||||
else
|
||||
f = if name.include?("/") || File.exist?(name)
|
||||
Formulary.factory(name)
|
||||
else
|
||||
Formulary.from_rack(rack)
|
||||
end
|
||||
|
||||
unless (prefix = f.installed_prefix).directory?
|
||||
raise MultipleVersionsInstalledError, rack.basename
|
||||
end
|
||||
|
||||
Keg.new(prefix)
|
||||
end
|
||||
rescue FormulaUnavailableError
|
||||
raise <<~EOS
|
||||
Multiple kegs installed to #{rack}
|
||||
However we don't know which one you refer to.
|
||||
Please delete (with rm -rf!) all but one and then try again.
|
||||
EOS
|
||||
resolve_keg name
|
||||
rescue NoSuchKegError => e
|
||||
if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall"))
|
||||
$stderr.puts reason
|
||||
end
|
||||
raise e
|
||||
end.freeze
|
||||
end
|
||||
|
||||
def kegs_casks
|
||||
@kegs_casks ||= begin
|
||||
kegs = []
|
||||
casks = []
|
||||
|
||||
downcased_unique_named.each do |name|
|
||||
kegs << resolve_keg(name)
|
||||
rescue NoSuchKegError
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
raise "No installed keg or cask with the name \"#{name}\""
|
||||
end
|
||||
end
|
||||
|
||||
[kegs.freeze, casks.freeze].freeze
|
||||
end
|
||||
end
|
||||
|
||||
def build_stable?
|
||||
!(HEAD? || devel?)
|
||||
end
|
||||
@ -241,6 +240,50 @@ module Homebrew
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_keg(name)
|
||||
require "keg"
|
||||
require "formula"
|
||||
require "missing_formula"
|
||||
|
||||
raise UsageError if name.blank?
|
||||
|
||||
rack = Formulary.to_rack(name.downcase)
|
||||
|
||||
dirs = rack.directory? ? rack.subdirs : []
|
||||
raise NoSuchKegError, rack.basename if dirs.empty?
|
||||
|
||||
linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename
|
||||
opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}"
|
||||
|
||||
begin
|
||||
if opt_prefix.symlink? && opt_prefix.directory?
|
||||
Keg.new(opt_prefix.resolved_path)
|
||||
elsif linked_keg_ref.symlink? && linked_keg_ref.directory?
|
||||
Keg.new(linked_keg_ref.resolved_path)
|
||||
elsif dirs.length == 1
|
||||
Keg.new(dirs.first)
|
||||
else
|
||||
f = if name.include?("/") || File.exist?(name)
|
||||
Formulary.factory(name)
|
||||
else
|
||||
Formulary.from_rack(rack)
|
||||
end
|
||||
|
||||
unless (prefix = f.installed_prefix).directory?
|
||||
raise MultipleVersionsInstalledError, "#{rack.basename} has multiple installed versions"
|
||||
end
|
||||
|
||||
Keg.new(prefix)
|
||||
end
|
||||
rescue FormulaUnavailableError
|
||||
raise MultipleVersionsInstalledError, <<~EOS
|
||||
Multiple kegs installed to #{rack}
|
||||
However we don't know which one you refer to.
|
||||
Please delete (with rm -rf!) all but one and then try again.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -211,6 +211,8 @@ module Homebrew
|
||||
|
||||
puts "From: #{Formatter.url(github_info(f))}"
|
||||
|
||||
puts "License: #{f.license}" if f.license
|
||||
|
||||
unless f.deps.empty?
|
||||
ohai "Dependencies"
|
||||
%w[build required recommended optional].map do |type|
|
||||
|
||||
@ -7,6 +7,7 @@ require "install"
|
||||
require "search"
|
||||
require "cleanup"
|
||||
require "cli/parser"
|
||||
require "upgrade"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
@ -261,6 +262,9 @@ module Homebrew
|
||||
install_formula(f)
|
||||
Cleanup.install_formula_clean!(f)
|
||||
end
|
||||
|
||||
check_installed_dependents
|
||||
|
||||
Homebrew.messages.display_messages
|
||||
rescue FormulaUnreadableError, FormulaClassUnavailableError,
|
||||
TapFormulaUnreadableError, TapFormulaClassUnavailableError => e
|
||||
|
||||
@ -11,10 +11,10 @@ module Homebrew
|
||||
usage_banner <<~EOS
|
||||
`readall` [<options>] [<tap>]
|
||||
|
||||
Import all formulae from the specified <tap>, or from all installed taps if none is provided.
|
||||
This can be useful for debugging issues across all formulae when making
|
||||
Import all items from the specified <tap>, or from all installed taps if none is provided.
|
||||
This can be useful for debugging issues across all items when making
|
||||
significant changes to `formula.rb`, testing the performance of loading
|
||||
all formulae or checking if any current formulae have Ruby issues.
|
||||
all items or checking if any current formulae/casks have Ruby issues.
|
||||
EOS
|
||||
switch "--aliases",
|
||||
description: "Verify any alias symlinks in each tap."
|
||||
@ -30,7 +30,7 @@ module Homebrew
|
||||
|
||||
if args.syntax?
|
||||
scan_files = "#{HOMEBREW_LIBRARY_PATH}/**/*.rb"
|
||||
ruby_files = Dir.glob(scan_files).reject { |file| file =~ %r{/(vendor|cask)/} }
|
||||
ruby_files = Dir.glob(scan_files).reject { |file| file =~ %r{/(vendor)/} }
|
||||
|
||||
Homebrew.failed = true unless Readall.valid_ruby_syntax?(ruby_files)
|
||||
end
|
||||
|
||||
@ -6,6 +6,10 @@ require "messages"
|
||||
require "reinstall"
|
||||
require "cli/parser"
|
||||
require "cleanup"
|
||||
require "cask/cmd"
|
||||
require "cask/utils"
|
||||
require "cask/macos"
|
||||
require "upgrade"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
@ -56,7 +60,8 @@ module Homebrew
|
||||
|
||||
Install.perform_preinstall_checks
|
||||
|
||||
args.resolved_formulae.each do |f|
|
||||
resolved_formulae, casks = args.resolved_formulae_casks
|
||||
resolved_formulae.each do |f|
|
||||
if f.pinned?
|
||||
onoe "#{f.full_name} is pinned. You must unpin it to reinstall."
|
||||
next
|
||||
@ -65,6 +70,16 @@ module Homebrew
|
||||
reinstall_formula(f)
|
||||
Cleanup.install_formula_clean!(f)
|
||||
end
|
||||
|
||||
check_installed_dependents
|
||||
|
||||
Homebrew.messages.display_messages
|
||||
|
||||
return if casks.blank?
|
||||
|
||||
reinstall_cmd = Cask::Cmd::Reinstall.new(casks)
|
||||
reinstall_cmd.verbose = args.verbose?
|
||||
reinstall_cmd.force = args.force?
|
||||
reinstall_cmd.run
|
||||
end
|
||||
end
|
||||
|
||||
@ -5,6 +5,9 @@ require "formula"
|
||||
require "diagnostic"
|
||||
require "migrator"
|
||||
require "cli/parser"
|
||||
require "cask/all"
|
||||
require "cask/cmd"
|
||||
require "cask/cask_loader"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
@ -29,15 +32,26 @@ module Homebrew
|
||||
def uninstall
|
||||
uninstall_args.parse
|
||||
|
||||
kegs_by_rack = if args.force?
|
||||
Hash[args.named.map do |name|
|
||||
rack = Formulary.to_rack(name)
|
||||
next unless rack.directory?
|
||||
if args.force?
|
||||
casks = []
|
||||
kegs_by_rack = {}
|
||||
|
||||
[rack, rack.subdirs.map { |d| Keg.new(d) }]
|
||||
end]
|
||||
args.named.each do |name|
|
||||
rack = Formulary.to_rack(name)
|
||||
|
||||
if rack.directory?
|
||||
kegs_by_rack[rack] = rack.subdirs.map { |d| Keg.new(d) }
|
||||
else
|
||||
begin
|
||||
casks << Cask::CaskLoader.load(name)
|
||||
rescue Cask::CaskUnavailableError
|
||||
# Since the uninstall was forced, ignore any unavailable casks
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
args.kegs.group_by(&:rack)
|
||||
all_kegs, casks = args.kegs_casks
|
||||
kegs_by_rack = all_kegs.group_by(&:rack)
|
||||
end
|
||||
|
||||
handle_unsatisfied_dependents(kegs_by_rack)
|
||||
@ -108,6 +122,13 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return if casks.blank?
|
||||
|
||||
cask_uninstall = Cask::Cmd::Uninstall.new(casks)
|
||||
cask_uninstall.force = args.force?
|
||||
cask_uninstall.verbose = args.verbose?
|
||||
cask_uninstall.run
|
||||
rescue MultipleVersionsInstalledError => e
|
||||
ofail e
|
||||
puts "Run `brew uninstall --force #{e.name}` to remove all versions."
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "install"
|
||||
require "reinstall"
|
||||
require "formula_installer"
|
||||
require "development_tools"
|
||||
require "messages"
|
||||
require "cleanup"
|
||||
require "cli/parser"
|
||||
require "formula_installer"
|
||||
require "install"
|
||||
require "upgrade"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
@ -114,241 +111,8 @@ module Homebrew
|
||||
|
||||
upgrade_formulae(formulae_to_install)
|
||||
|
||||
check_dependents(formulae_to_install)
|
||||
check_installed_dependents
|
||||
|
||||
Homebrew.messages.display_messages
|
||||
end
|
||||
|
||||
def upgrade_formulae(formulae_to_install)
|
||||
return if formulae_to_install.empty?
|
||||
return if args.dry_run?
|
||||
|
||||
# Sort keg-only before non-keg-only formulae to avoid any needless conflicts
|
||||
# with outdated, non-keg-only versions of formulae being upgraded.
|
||||
formulae_to_install.sort! do |a, b|
|
||||
if !a.keg_only? && b.keg_only?
|
||||
1
|
||||
elsif a.keg_only? && !b.keg_only?
|
||||
-1
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
formulae_to_install.each do |f|
|
||||
Migrator.migrate_if_needed(f)
|
||||
begin
|
||||
upgrade_formula(f)
|
||||
Cleanup.install_formula_clean!(f)
|
||||
rescue UnsatisfiedRequirements => e
|
||||
Homebrew.failed = true
|
||||
onoe "#{f}: #{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def upgrade_formula(f)
|
||||
return if args.dry_run?
|
||||
|
||||
if f.opt_prefix.directory?
|
||||
keg = Keg.new(f.opt_prefix.resolved_path)
|
||||
keg_had_linked_opt = true
|
||||
keg_was_linked = keg.linked?
|
||||
end
|
||||
|
||||
formulae_maybe_with_kegs = [f] + f.old_installed_formulae
|
||||
outdated_kegs = formulae_maybe_with_kegs
|
||||
.map(&:linked_keg)
|
||||
.select(&:directory?)
|
||||
.map { |k| Keg.new(k.resolved_path) }
|
||||
linked_kegs = outdated_kegs.select(&:linked?)
|
||||
|
||||
if f.opt_prefix.directory?
|
||||
keg = Keg.new(f.opt_prefix.resolved_path)
|
||||
tab = Tab.for_keg(keg)
|
||||
end
|
||||
|
||||
build_options = BuildOptions.new(Options.create(args.flags_only), f.options)
|
||||
options = build_options.used_options
|
||||
options |= f.build.used_options
|
||||
options &= f.options
|
||||
|
||||
fi = FormulaInstaller.new(f)
|
||||
fi.options = options
|
||||
fi.build_bottle = args.build_bottle?
|
||||
fi.installed_on_request = args.named.present?
|
||||
fi.link_keg ||= keg_was_linked if keg_had_linked_opt
|
||||
if tab
|
||||
fi.build_bottle ||= tab.built_bottle?
|
||||
fi.installed_as_dependency = tab.installed_as_dependency
|
||||
fi.installed_on_request ||= tab.installed_on_request
|
||||
end
|
||||
|
||||
upgrade_version = if f.optlinked?
|
||||
"#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
|
||||
else
|
||||
"-> #{f.pkg_version}"
|
||||
end
|
||||
oh1 "Upgrading #{Formatter.identifier(f.full_specified_name)} #{upgrade_version} #{fi.options.to_a.join(" ")}"
|
||||
|
||||
fi.prelude
|
||||
fi.fetch
|
||||
|
||||
# first we unlink the currently active keg for this formula otherwise it is
|
||||
# possible for the existing build to interfere with the build we are about to
|
||||
# do! Seriously, it happens!
|
||||
outdated_kegs.each(&:unlink)
|
||||
|
||||
fi.install
|
||||
fi.finish
|
||||
rescue FormulaInstallationAlreadyAttemptedError
|
||||
# We already attempted to upgrade f as part of the dependency tree of
|
||||
# another formula. In that case, don't generate an error, just move on.
|
||||
nil
|
||||
rescue CannotInstallFormulaError => e
|
||||
ofail e
|
||||
rescue BuildError => e
|
||||
e.dump
|
||||
puts
|
||||
Homebrew.failed = true
|
||||
rescue DownloadError => e
|
||||
ofail e
|
||||
ensure
|
||||
# restore previous installation state if build failed
|
||||
begin
|
||||
linked_kegs.each(&:link) unless f.latest_version_installed?
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# @private
|
||||
def depends_on(a, b)
|
||||
if a.opt_or_installed_prefix_keg
|
||||
&.runtime_dependencies
|
||||
&.any? { |d| d["full_name"] == b.full_name }
|
||||
1
|
||||
else
|
||||
a <=> b
|
||||
end
|
||||
end
|
||||
|
||||
def check_dependents(formulae_to_install)
|
||||
return if formulae_to_install.empty?
|
||||
|
||||
oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run?
|
||||
outdated_dependents =
|
||||
formulae_to_install.flat_map(&:runtime_installed_formula_dependents)
|
||||
.select(&:outdated?)
|
||||
if outdated_dependents.blank?
|
||||
ohai "No dependents found!" unless args.dry_run?
|
||||
return
|
||||
end
|
||||
outdated_dependents -= formulae_to_install if args.dry_run?
|
||||
|
||||
upgradeable_dependents =
|
||||
outdated_dependents.reject(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
pinned_dependents =
|
||||
outdated_dependents.select(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
|
||||
if pinned_dependents.present?
|
||||
plural = "dependent".pluralize(pinned_dependents.count)
|
||||
ohai "Not upgrading #{pinned_dependents.count} pinned #{plural}:"
|
||||
puts(pinned_dependents.map do |f|
|
||||
"#{f.full_specified_name} #{f.pkg_version}"
|
||||
end.join(", "))
|
||||
end
|
||||
|
||||
# Print the upgradable dependents.
|
||||
if upgradeable_dependents.blank?
|
||||
ohai "No outdated dependents to upgrade!" unless args.dry_run?
|
||||
else
|
||||
plural = "dependent".pluralize(upgradeable_dependents.count)
|
||||
verb = args.dry_run? ? "Would upgrade" : "Upgrading"
|
||||
ohai "#{verb} #{upgradeable_dependents.count} #{plural}:"
|
||||
formulae_upgrades = upgradeable_dependents.map do |f|
|
||||
name = f.full_specified_name
|
||||
if f.optlinked?
|
||||
"#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
|
||||
else
|
||||
"#{name} #{f.pkg_version}"
|
||||
end
|
||||
end
|
||||
puts formulae_upgrades.join(", ")
|
||||
end
|
||||
|
||||
upgrade_formulae(upgradeable_dependents)
|
||||
|
||||
# Assess the dependents tree again now we've upgraded.
|
||||
oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run?
|
||||
broken_dependents = CacheStoreDatabase.use(:linkage) do |db|
|
||||
formulae_to_install.flat_map(&:runtime_installed_formula_dependents)
|
||||
.select do |f|
|
||||
keg = f.opt_or_installed_prefix_keg
|
||||
next unless keg
|
||||
|
||||
LinkageChecker.new(keg, cache_db: db)
|
||||
.broken_library_linkage?
|
||||
end.compact
|
||||
end
|
||||
if broken_dependents.blank?
|
||||
if args.dry_run?
|
||||
ohai "No currently broken dependents found!"
|
||||
opoo "If they are broken by the upgrade they will also be upgraded or reinstalled."
|
||||
else
|
||||
ohai "No broken dependents found!"
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
reinstallable_broken_dependents =
|
||||
broken_dependents.reject(&:outdated?)
|
||||
.reject(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
outdated_pinned_broken_dependents =
|
||||
broken_dependents.select(&:outdated?)
|
||||
.select(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
|
||||
# Print the pinned dependents.
|
||||
if outdated_pinned_broken_dependents.present?
|
||||
count = outdated_pinned_broken_dependents.count
|
||||
plural = "dependent".pluralize(outdated_pinned_broken_dependents.count)
|
||||
onoe "Not reinstalling #{count} broken and outdated, but pinned #{plural}:"
|
||||
$stderr.puts(outdated_pinned_broken_dependents.map do |f|
|
||||
"#{f.full_specified_name} #{f.pkg_version}"
|
||||
end.join(", "))
|
||||
end
|
||||
|
||||
# Print the broken dependents.
|
||||
if reinstallable_broken_dependents.blank?
|
||||
ohai "No broken dependents to reinstall!"
|
||||
else
|
||||
count = reinstallable_broken_dependents.count
|
||||
plural = "dependent".pluralize(reinstallable_broken_dependents.count)
|
||||
ohai "Reinstalling #{count} broken #{plural} from source:"
|
||||
puts reinstallable_broken_dependents.map(&:full_specified_name)
|
||||
.join(", ")
|
||||
end
|
||||
|
||||
return if args.dry_run?
|
||||
|
||||
reinstallable_broken_dependents.each do |f|
|
||||
reinstall_formula(f, build_from_source: true)
|
||||
rescue FormulaInstallationAlreadyAttemptedError
|
||||
# We already attempted to reinstall f as part of the dependency tree of
|
||||
# another formula. In that case, don't generate an error, just move on.
|
||||
nil
|
||||
rescue CannotInstallFormulaError => e
|
||||
ofail e
|
||||
rescue BuildError => e
|
||||
e.dump
|
||||
puts
|
||||
Homebrew.failed = true
|
||||
rescue DownloadError => e
|
||||
ofail e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -148,8 +148,7 @@ module Commands
|
||||
cmds = internal_commands + internal_developer_commands + internal_commands_aliases
|
||||
|
||||
file = HOMEBREW_REPOSITORY/"completions/internal_commands_list.txt"
|
||||
file.delete if file.exist?
|
||||
file.write(cmds.sort.join("\n") + "\n")
|
||||
file.atomic_write(cmds.sort.join("\n") + "\n")
|
||||
end
|
||||
|
||||
def rebuild_commands_completion_list
|
||||
@ -157,7 +156,6 @@ module Commands
|
||||
HOMEBREW_CACHE.mkpath
|
||||
|
||||
file = HOMEBREW_CACHE/"all_commands_list.txt"
|
||||
file.delete if file.exist?
|
||||
file.write(commands(aliases: true).sort.join("\n") + "\n")
|
||||
file.atomic_write(commands(aliases: true).sort.join("\n") + "\n")
|
||||
end
|
||||
end
|
||||
|
||||
5454
Library/Homebrew/data/spdx.json
Normal file
5454
Library/Homebrew/data/spdx.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ require "date"
|
||||
require "missing_formula"
|
||||
require "digest"
|
||||
require "cli/parser"
|
||||
require "json"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
@ -109,7 +110,9 @@ 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)
|
||||
new_formula_problem_lines = []
|
||||
audit_formulae.sort.each do |f|
|
||||
only = only_cops ? ["style"] : args.only
|
||||
@ -120,6 +123,7 @@ module Homebrew
|
||||
git: git,
|
||||
only: only,
|
||||
except: args.except,
|
||||
spdx_data: spdx_data,
|
||||
}
|
||||
options[:style_offenses] = style_results.file_offenses(f.path) if style_results
|
||||
options[:display_cop_names] = args.display_cop_names?
|
||||
@ -215,6 +219,7 @@ module Homebrew
|
||||
@new_formula_problems = []
|
||||
@text = FormulaText.new(formula.path)
|
||||
@specs = %w[stable devel head].map { |s| formula.send(s) }.compact
|
||||
@spdx_data = options[:spdx_data]
|
||||
end
|
||||
|
||||
def audit_style
|
||||
@ -327,6 +332,27 @@ module Homebrew
|
||||
openssl@1.1
|
||||
].freeze
|
||||
|
||||
def audit_license
|
||||
if formula.license.present?
|
||||
if @spdx_data["licenses"].any? { |lic| lic["licenseId"] == formula.license }
|
||||
return unless @online
|
||||
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
|
||||
return if user.blank?
|
||||
|
||||
github_license = GitHub.get_repo_license(user, repo)
|
||||
return if github_license && [formula.license, "NOASSERTION"].include?(github_license)
|
||||
|
||||
problem "License mismatch - GitHub license is: #{github_license}, "\
|
||||
"but Formulae license states: #{formula.license}."
|
||||
else
|
||||
problem "#{formula.license} is not a standard SPDX license."
|
||||
end
|
||||
elsif @new_formula
|
||||
problem "No license specified for package."
|
||||
end
|
||||
end
|
||||
|
||||
def audit_deps
|
||||
@specs.each do |spec|
|
||||
# Check for things we don't like to depend on.
|
||||
@ -502,8 +528,9 @@ module Homebrew
|
||||
end
|
||||
|
||||
def audit_github_repository
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*})
|
||||
return if user.nil?
|
||||
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
|
||||
|
||||
return if user.blank?
|
||||
|
||||
warning = SharedAudits.github(user, repo)
|
||||
return if warning.nil?
|
||||
@ -512,8 +539,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
def audit_gitlab_repository
|
||||
user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*})
|
||||
return if user.nil?
|
||||
user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
|
||||
return if user.blank?
|
||||
|
||||
warning = SharedAudits.gitlab(user, repo)
|
||||
return if warning.nil?
|
||||
@ -522,8 +549,8 @@ module Homebrew
|
||||
end
|
||||
|
||||
def audit_bitbucket_repository
|
||||
user, repo = get_repo_data(%r{https?://bitbucket\.org/([^/]+)/([^/]+)/?.*})
|
||||
return if user.nil?
|
||||
user, repo = get_repo_data(%r{https?://bitbucket\.org/([^/]+)/([^/]+)/?.*}) if @new_formula
|
||||
return if user.blank?
|
||||
|
||||
warning = SharedAudits.bitbucket(user, repo)
|
||||
return if warning.nil?
|
||||
@ -534,7 +561,6 @@ module Homebrew
|
||||
def get_repo_data(regex)
|
||||
return unless @core_tap
|
||||
return unless @online
|
||||
return unless @new_formula
|
||||
|
||||
_, user, repo = *regex.match(formula.stable.url) if formula.stable
|
||||
_, user, repo = *regex.match(formula.homepage) unless user
|
||||
@ -554,6 +580,7 @@ module Homebrew
|
||||
"aws-sdk-cpp" => "10",
|
||||
"awscli@1" => "10",
|
||||
"balena-cli" => "10",
|
||||
"gatsby-cli" => "10",
|
||||
"quicktype" => "10",
|
||||
"vim" => "50",
|
||||
}.freeze
|
||||
@ -721,11 +748,11 @@ module Homebrew
|
||||
current_revision = formula.revision
|
||||
|
||||
previous_version = nil
|
||||
previous_checksum = nil
|
||||
previous_version_scheme = nil
|
||||
previous_revision = nil
|
||||
|
||||
newest_committed_version = nil
|
||||
newest_committed_checksum = nil
|
||||
newest_committed_revision = nil
|
||||
|
||||
fv.rev_list("origin/master") do |rev|
|
||||
@ -739,6 +766,7 @@ module Homebrew
|
||||
previous_revision = f.revision
|
||||
|
||||
newest_committed_version ||= previous_version
|
||||
newest_committed_checksum ||= previous_checksum
|
||||
newest_committed_revision ||= previous_revision
|
||||
end
|
||||
|
||||
@ -746,7 +774,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
if current_version == previous_version &&
|
||||
current_checksum != previous_checksum
|
||||
current_checksum != newest_committed_checksum
|
||||
problem(
|
||||
"stable sha256 changed without the version also changing; " \
|
||||
"please create an issue upstream to rule out malicious " \
|
||||
@ -845,15 +873,6 @@ module Homebrew
|
||||
# TODO: check could be in RuboCop
|
||||
problem "`env :userpaths` in formulae is deprecated" if line.include?("env :userpaths")
|
||||
|
||||
if line =~ /system ((["'])[^"' ]*(?:\s[^"' ]*)+\2)/
|
||||
bad_system = Regexp.last_match(1)
|
||||
unless %w[| < > & ; *].any? { |c| bad_system.include? c }
|
||||
good_system = bad_system.gsub(" ", "\", \"")
|
||||
# TODO: check could be in RuboCop
|
||||
problem "Use `system #{good_system}` instead of `system #{bad_system}` "
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: check could be in RuboCop
|
||||
problem "`#{Regexp.last_match(1)}` is now unnecessary" if line =~ /(require ["']formula["'])/
|
||||
|
||||
|
||||
@ -531,25 +531,21 @@ module Homebrew
|
||||
odie "--keep-old was passed but there was no existing bottle block!" if args.keep_old?
|
||||
puts output
|
||||
update_or_add = "add"
|
||||
if s.include? "stable do"
|
||||
indent = s.slice(/^( +)stable do/, 1).length
|
||||
string = s.sub!(/^ {#{indent}}stable do(.|\n)+?^ {#{indent}}end\n/m, '\0' + output + "\n")
|
||||
else
|
||||
pattern = /(
|
||||
(\ {2}\#[^\n]*\n)* # comments
|
||||
\ {2}( # two spaces at the beginning
|
||||
(url|head)\ ['"][\S\ ]+['"] # url or head with a string
|
||||
(
|
||||
,[\S\ ]*$ # url may have options
|
||||
(\n^\ {3}[\S\ ]+$)* # options can be in multiple lines
|
||||
)?|
|
||||
(homepage|desc|sha1|sha256|version|mirror)\ ['"][\S\ ]+['"]| # specs with a string
|
||||
(revision|version_scheme)\ \d+ # revision with a number
|
||||
)\n+ # multiple empty lines
|
||||
)+
|
||||
/mx
|
||||
string = s.sub!(pattern, '\0' + output + "\n")
|
||||
end
|
||||
pattern = /(
|
||||
(\ {2}\#[^\n]*\n)* # comments
|
||||
\ {2}( # two spaces at the beginning
|
||||
(url|head)\ ['"][\S\ ]+['"] # url or head with a string
|
||||
(
|
||||
,[\S\ ]*$ # url may have options
|
||||
(\n^\ {3}[\S\ ]+$)* # options can be in multiple lines
|
||||
)?|
|
||||
(homepage|desc|sha256|version|mirror|license)\ ['"][\S\ ]+['"]| # specs with a string
|
||||
(revision|version_scheme)\ \d+| # revision with a number
|
||||
(stable|livecheck)\ do(\n+^\ {4}[\S\ ]+$)*\n+^\ {2}end # components with blocks
|
||||
)\n+ # multiple empty lines
|
||||
)+
|
||||
/mx
|
||||
string = s.sub!(pattern, '\0' + output + "\n")
|
||||
odie "Bottle block addition failed!" unless string
|
||||
end
|
||||
end
|
||||
|
||||
@ -47,6 +47,8 @@ module Homebrew
|
||||
description: "Explicitly set the <name> of the new formula."
|
||||
flag "--set-version=",
|
||||
description: "Explicitly set the <version> of the new formula."
|
||||
flag "--set-license=",
|
||||
description: "Explicitly set the <license> of the new formula."
|
||||
flag "--tap=",
|
||||
description: "Generate the new formula within the given tap, specified as <user>`/`<repo>."
|
||||
switch :force
|
||||
@ -68,11 +70,13 @@ module Homebrew
|
||||
|
||||
version = args.set_version
|
||||
name = args.set_name
|
||||
license = args.set_license
|
||||
tap = args.tap
|
||||
|
||||
fc = FormulaCreator.new
|
||||
fc.name = name
|
||||
fc.version = version
|
||||
fc.license = license
|
||||
fc.tap = Tap.fetch(tap || "homebrew/core")
|
||||
raise TapUnavailableError, tap unless fc.tap.installed?
|
||||
|
||||
|
||||
@ -191,7 +191,10 @@ module Homebrew
|
||||
# Remove any existing version suffixes, as a new one will be added later
|
||||
name.sub!(/\b@(.*)\z\b/i, "")
|
||||
versioned_name = Formulary.class_s("#{name}@#{version}")
|
||||
result.gsub!("class #{class_name} < Formula", "class #{versioned_name} < Formula")
|
||||
result.sub!("class #{class_name} < Formula", "class #{versioned_name} < Formula")
|
||||
|
||||
# Remove bottle blocks, they won't work.
|
||||
result.sub!(/ bottle do.+?end\n\n/m, "") if destination_tap != source_tap
|
||||
|
||||
path = destination_tap.path/"Formula/#{name}@#{version}.rb"
|
||||
if path.exist?
|
||||
@ -205,7 +208,8 @@ module Homebrew
|
||||
odebug "Overwriting existing formula at #{path}"
|
||||
path.delete
|
||||
end
|
||||
ohai "Writing formula for #{name} from revision #{rev} to #{path}"
|
||||
ohai "Writing formula for #{name} from revision #{rev} to:"
|
||||
puts path
|
||||
path.write result
|
||||
end
|
||||
|
||||
|
||||
@ -33,6 +33,9 @@ module Homebrew
|
||||
switch "--resolve",
|
||||
description: "When a patch fails to apply, leave in progress and allow user to resolve, "\
|
||||
"instead of aborting."
|
||||
switch "--warn-on-upload-failure",
|
||||
description: "Warn instead of raising an error if the bottle upload fails. "\
|
||||
"Useful for repairing bottle uploads that previously failed."
|
||||
flag "--workflow=",
|
||||
description: "Retrieve artifacts from the specified workflow (default: `tests.yml`)."
|
||||
flag "--artifact=",
|
||||
@ -73,13 +76,18 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def signoff!(pr, path: ".")
|
||||
message = Utils.popen_read "git", "-C", path, "log", "-1", "--pretty=%B"
|
||||
def signoff!(pr, tap:)
|
||||
message = Utils.popen_read "git", "-C", tap.path, "log", "-1", "--pretty=%B"
|
||||
subject = message.lines.first.strip
|
||||
|
||||
# Skip the subject and separate lines that look like trailers (e.g. "Co-authored-by")
|
||||
# from lines that look like regular body text.
|
||||
trailers, body = message.lines.drop(1).partition { |s| s.match?(/^[a-z-]+-by:/i) }
|
||||
|
||||
# Approving reviewers also sign-off on merge
|
||||
trailers += GitHub.approved_reviews(tap.user, "homebrew-#{tap.repo}", pr).map do |r|
|
||||
"Signed-off-by: #{r["name"]} <#{r["email"]}>\n"
|
||||
end
|
||||
trailers = trailers.uniq.join.strip
|
||||
body = body.join.strip.gsub(/\n{3,}/, "\n\n")
|
||||
|
||||
@ -90,7 +98,7 @@ module Homebrew
|
||||
if Homebrew.args.dry_run?
|
||||
puts "git commit --amend --signoff -m $message"
|
||||
else
|
||||
safe_system "git", "-C", path, "commit", "--amend", "--signoff", "--allow-empty", "-q", "-m", new_message
|
||||
safe_system "git", "-C", tap.path, "commit", "--amend", "--signoff", "--allow-empty", "-q", "-m", new_message
|
||||
end
|
||||
end
|
||||
|
||||
@ -232,7 +240,7 @@ module Homebrew
|
||||
cd dir do
|
||||
original_commit = Utils.popen_read("git", "-C", tap.path, "rev-parse", "HEAD").chomp
|
||||
cherry_pick_pr! pr, path: tap.path
|
||||
signoff! pr, path: tap.path unless args.clean?
|
||||
signoff! pr, tap: tap unless args.clean?
|
||||
|
||||
unless args.no_upload?
|
||||
mirror_formulae(tap, original_commit, org: bintray_org, repo: mirror_repo, publish: !args.no_publish?)
|
||||
@ -253,9 +261,10 @@ module Homebrew
|
||||
upload_args << "--verbose" if Homebrew.args.verbose?
|
||||
upload_args << "--no-publish" if args.no_publish?
|
||||
upload_args << "--dry-run" if args.dry_run?
|
||||
upload_args << "--warn-on-upload-failure" if args.warn_on_upload_failure?
|
||||
upload_args << "--root_url=#{args.root_url}" if args.root_url
|
||||
upload_args << "--bintray-org=#{bintray_org}"
|
||||
system HOMEBREW_BREW_FILE, *upload_args
|
||||
safe_system HOMEBREW_BREW_FILE, *upload_args
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -17,6 +17,9 @@ module Homebrew
|
||||
description: "Apply the bottle commit and upload the bottles, but don't publish them."
|
||||
switch "-n", "--dry-run",
|
||||
description: "Print what would be done rather than doing it."
|
||||
switch "--warn-on-upload-failure",
|
||||
description: "Warn instead of raising an error if the bottle upload fails. "\
|
||||
"Useful for repairing bottle uploads that previously failed."
|
||||
flag "--bintray-org=",
|
||||
description: "Upload to the specified Bintray organisation (default: `homebrew`)."
|
||||
flag "--root-url=",
|
||||
@ -42,13 +45,15 @@ module Homebrew
|
||||
if args.dry_run?
|
||||
puts "brew #{bottle_args.join " "}"
|
||||
else
|
||||
system HOMEBREW_BREW_FILE, *bottle_args
|
||||
safe_system HOMEBREW_BREW_FILE, *bottle_args
|
||||
end
|
||||
|
||||
if args.dry_run?
|
||||
puts "Upload bottles described by these JSON files to Bintray:\n #{Dir["*.json"].join("\n ")}"
|
||||
else
|
||||
bintray.upload_bottle_json Dir["*.json"], publish_package: !args.no_publish?
|
||||
bintray.upload_bottle_json(Dir["*.json"],
|
||||
publish_package: !args.no_publish?,
|
||||
warn_on_error: args.warn_on_upload_failure?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
59
Library/Homebrew/dev-cmd/sponsors.rb
Normal file
59
Library/Homebrew/dev-cmd/sponsors.rb
Normal file
@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "utils/github"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
def sponsors_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`sponsors`
|
||||
|
||||
Print a Markdown summary of Homebrew's GitHub Sponsors, suitable for pasting into a README.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
|
||||
def sponsors
|
||||
sponsors_args.parse
|
||||
|
||||
sponsors = {
|
||||
"named" => [],
|
||||
"users" => 0,
|
||||
"orgs" => 0,
|
||||
}
|
||||
|
||||
GitHub.sponsors_by_tier("Homebrew").each do |tier|
|
||||
sponsors["named"] += tier["sponsors"] if tier["tier"] >= 100
|
||||
sponsors["users"] += tier["count"]
|
||||
sponsors["orgs"] += tier["sponsors"].count { |s| s["type"] == "organization" }
|
||||
end
|
||||
|
||||
items = []
|
||||
items += sponsors["named"].map { |s| "[#{s["name"]}](https://github.com/#{s["login"]})" }
|
||||
|
||||
anon_users = sponsors["users"] - sponsors["named"].length - sponsors["orgs"]
|
||||
|
||||
items << if items.length > 1
|
||||
"#{anon_users} other users"
|
||||
else
|
||||
"#{anon_users} users"
|
||||
end
|
||||
|
||||
if sponsors["orgs"] == 1
|
||||
items << "#{sponsors["orgs"]} organization"
|
||||
elsif sponsors["orgs"] > 1
|
||||
items << "#{sponsors["orgs"]} organizations"
|
||||
end
|
||||
|
||||
sponsor_text = if items.length > 2
|
||||
items[0..-2].join(", ") + " and #{items.last}"
|
||||
else
|
||||
items.join(" and ")
|
||||
end
|
||||
|
||||
puts "Homebrew is generously supported by #{sponsor_text} via [GitHub Sponsors](https://github.com/sponsors/Homebrew)."
|
||||
end
|
||||
end
|
||||
39
Library/Homebrew/dev-cmd/update-license-data.rb
Normal file
39
Library/Homebrew/dev-cmd/update-license-data.rb
Normal file
@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "commands"
|
||||
require "cli/parser"
|
||||
require "json"
|
||||
require "net/http"
|
||||
require "open-uri"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
SPDX_PATH = (HOMEBREW_LIBRARY_PATH/"data/spdx.json").freeze
|
||||
SPDX_DATA_URL = "https://raw.githubusercontent.com/spdx/license-list-data/HEAD/json/licenses.json"
|
||||
|
||||
def update_license_data_args
|
||||
Homebrew::CLI::Parser.new do
|
||||
usage_banner <<~EOS
|
||||
`update_license_data` <cmd>
|
||||
|
||||
Update SPDX license data in the Homebrew repository.
|
||||
EOS
|
||||
switch "--fail-if-changed",
|
||||
description: "Return a failing status code if current license data's version is different from " \
|
||||
"the upstream. This can be used to notify CI when the SPDX license data is out of date."
|
||||
|
||||
max_named 0
|
||||
end
|
||||
end
|
||||
|
||||
def update_license_data
|
||||
update_license_data_args.parse
|
||||
ohai "Updating SPDX license data..."
|
||||
curl_download(SPDX_DATA_URL, to: SPDX_PATH, partial: false)
|
||||
|
||||
return unless args.fail_if_changed?
|
||||
|
||||
safe_system "git", "diff", "--stat", "--exit-code", SPDX_PATH
|
||||
end
|
||||
end
|
||||
@ -29,14 +29,7 @@ class KegUnspecifiedError < UsageError
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleVersionsInstalledError < RuntimeError
|
||||
attr_reader :name
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
super "#{name} has multiple installed versions"
|
||||
end
|
||||
end
|
||||
class MultipleVersionsInstalledError < RuntimeError; end
|
||||
|
||||
class NotAKegError < RuntimeError; end
|
||||
|
||||
|
||||
9
Library/Homebrew/extend/os/linux/readall.rb
Normal file
9
Library/Homebrew/extend/os/linux/readall.rb
Normal file
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Readall
|
||||
class << self
|
||||
def valid_casks?(*)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -12,6 +12,7 @@ module Homebrew
|
||||
check_xcode_minimum_version
|
||||
check_clt_minimum_version
|
||||
check_if_xcode_needs_clt_installed
|
||||
check_if_supported_sdk_available
|
||||
].freeze
|
||||
end
|
||||
|
||||
@ -357,6 +358,34 @@ module Homebrew
|
||||
Untap them with `brew untap`.
|
||||
EOS
|
||||
end
|
||||
|
||||
def check_if_supported_sdk_available
|
||||
return unless MacOS.sdk_root_needed?
|
||||
return if MacOS.sdk
|
||||
|
||||
locator = MacOS.sdk_locator
|
||||
|
||||
source = if locator.source == :clt
|
||||
"CLT"
|
||||
else
|
||||
"Xcode"
|
||||
end
|
||||
|
||||
all_sdks = locator.all_sdks
|
||||
sdks_found_msg = unless all_sdks.empty?
|
||||
<<~EOS
|
||||
Homebrew found the following SDKs in the #{source} install:
|
||||
#{locator.all_sdks.map(&:version).join("\n ")}
|
||||
EOS
|
||||
end
|
||||
|
||||
<<~EOS
|
||||
Could not find an SDK that supports macOS #{MacOS.version}.
|
||||
You may have have an outdated or incompatible #{source}.
|
||||
#{sdks_found_msg}
|
||||
Please update #{source} or uninstall it if no updates are available.
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
3
Library/Homebrew/extend/os/readall.rb
Normal file
3
Library/Homebrew/extend/os/readall.rb
Normal file
@ -0,0 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "extend/os/linux/readall" if OS.linux?
|
||||
@ -351,6 +351,9 @@ class Formula
|
||||
# @see .desc=
|
||||
delegate desc: :"self.class"
|
||||
|
||||
# The SPDX ID of the software license.
|
||||
delegate license: :"self.class"
|
||||
|
||||
# The homepage for the software.
|
||||
# @method homepage
|
||||
# @see .homepage=
|
||||
@ -1109,13 +1112,14 @@ class Formula
|
||||
return false if tab_tap.nil?
|
||||
|
||||
begin
|
||||
Formulary.factory(keg.name)
|
||||
f = Formulary.factory(keg.name)
|
||||
rescue FormulaUnavailableError
|
||||
# formula for this keg is deleted, so defer to allowlist
|
||||
rescue TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
||||
return false # this keg belongs to another formula
|
||||
else
|
||||
return false # this keg belongs to another formula
|
||||
# this keg belongs to another unrelated formula
|
||||
return false unless (f.aliases + f.oldname).include?(keg.name)
|
||||
end
|
||||
end
|
||||
to_check = path.relative_path_from(HOMEBREW_PREFIX).to_s
|
||||
@ -1687,6 +1691,7 @@ class Formula
|
||||
"aliases" => aliases.sort,
|
||||
"versioned_formulae" => versioned_formulae.map(&:name),
|
||||
"desc" => desc,
|
||||
"license" => license,
|
||||
"homepage" => homepage,
|
||||
"versions" => {
|
||||
"stable" => stable&.version&.to_s,
|
||||
@ -2211,6 +2216,13 @@ class Formula
|
||||
# <pre>desc "Example formula"</pre>
|
||||
attr_rw :desc
|
||||
|
||||
# @!attribute [w]
|
||||
# The SPDX ID of the open-source license that the formula uses.
|
||||
# Shows when running `brew info`.
|
||||
#
|
||||
# <pre>license "BSD-2-Clause"</pre>
|
||||
attr_rw :license
|
||||
|
||||
# @!attribute [w] homepage
|
||||
# The homepage for the software. Used by users to get more information
|
||||
# about the software and Homebrew maintainers as a point of contact for
|
||||
@ -2648,9 +2660,13 @@ class Formula
|
||||
#
|
||||
# The block will create, run in and delete a temporary directory.
|
||||
#
|
||||
# We are fine if the executable does not error out, so we know linking
|
||||
# and building the software was OK.
|
||||
# <pre>system bin/"foobar", "--version"</pre>
|
||||
# We want tests that don't require any user input
|
||||
# and test the basic functionality of the application.
|
||||
# For example foo build-foo input.foo is a good test
|
||||
# and foo --version and foo --help are bad tests.
|
||||
# However, a bad test is better than no test at all.
|
||||
#
|
||||
# See: https://docs.brew.sh/Formula-Cookbook#add-a-test-to-the-formula
|
||||
#
|
||||
# <pre>(testpath/"test.file").write <<~EOS
|
||||
# writing some test file, if you need to
|
||||
|
||||
@ -6,7 +6,7 @@ require "erb"
|
||||
module Homebrew
|
||||
class FormulaCreator
|
||||
attr_reader :url, :sha256, :desc, :homepage
|
||||
attr_accessor :name, :version, :tap, :path, :mode
|
||||
attr_accessor :name, :version, :tap, :path, :mode, :license
|
||||
|
||||
def url=(url)
|
||||
@url = url
|
||||
@ -100,6 +100,7 @@ module Homebrew
|
||||
<% end %>
|
||||
sha256 "#{sha256}"
|
||||
<% end %>
|
||||
license "#{license}"
|
||||
|
||||
<% if mode == :cmake %>
|
||||
depends_on "cmake" => :build
|
||||
|
||||
@ -77,6 +77,14 @@ class FormulaInstaller
|
||||
@attempted = Set.new
|
||||
end
|
||||
|
||||
def self.installed
|
||||
@installed ||= Set.new
|
||||
end
|
||||
|
||||
def self.clear_installed
|
||||
@installed = Set.new
|
||||
end
|
||||
|
||||
# When no build tools are available and build flags are passed through ARGV,
|
||||
# it's necessary to interrupt the user before any sort of installation
|
||||
# can proceed. Only invoked when the user has no developer tools.
|
||||
@ -292,7 +300,7 @@ class FormulaInstaller
|
||||
|
||||
self.class.attempted << formula
|
||||
|
||||
if pour_bottle?(warn: true)
|
||||
if pour_bottle?
|
||||
begin
|
||||
pour
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
@ -700,6 +708,8 @@ class FormulaInstaller
|
||||
|
||||
ohai "Summary" if verbose? || show_summary_heading?
|
||||
puts summary
|
||||
|
||||
self.class.installed << formula
|
||||
ensure
|
||||
unlock
|
||||
end
|
||||
@ -983,11 +993,24 @@ class FormulaInstaller
|
||||
|
||||
return if only_deps?
|
||||
|
||||
unless pour_bottle?
|
||||
formula.fetch_patches
|
||||
formula.resources.each(&:fetch)
|
||||
end
|
||||
if pour_bottle?(warn: true)
|
||||
begin
|
||||
downloader.fetch
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
raise if Homebrew::EnvConfig.developer? ||
|
||||
Homebrew::EnvConfig.no_bottle_source_fallback? ||
|
||||
e.is_a?(Interrupt)
|
||||
|
||||
@pour_failed = true
|
||||
onoe e.message
|
||||
opoo "Bottle installation failed: building from source."
|
||||
fetch_dependencies
|
||||
end
|
||||
end
|
||||
return if pour_bottle?
|
||||
|
||||
formula.fetch_patches
|
||||
formula.resources.each(&:fetch)
|
||||
downloader.fetch
|
||||
end
|
||||
|
||||
|
||||
@ -167,7 +167,7 @@ module Language
|
||||
def needs_python?(python)
|
||||
return true if build.with?(python)
|
||||
|
||||
(requirements.to_a | deps).any? { |r| r.name == python && r.required? }
|
||||
(requirements.to_a | deps).any? { |r| r.name.split("/").last == python && r.required? }
|
||||
end
|
||||
|
||||
# Helper method for the common case of installing a Python application.
|
||||
|
||||
@ -85,6 +85,10 @@ module Homebrew
|
||||
cargo is part of the rust formula:
|
||||
brew install rust
|
||||
EOS
|
||||
when "cargo-completion" then <<~EOS
|
||||
cargo-completion is part of the rust formula:
|
||||
brew install rust
|
||||
EOS
|
||||
when "uconv" then <<~EOS
|
||||
uconv is part of the icu4c formula:
|
||||
brew install icu4c
|
||||
|
||||
@ -97,14 +97,16 @@ module OS
|
||||
# If no specific SDK is requested, the SDK matching the OS version is returned,
|
||||
# if available. Otherwise, the latest SDK is returned.
|
||||
|
||||
def sdk(v = nil)
|
||||
@locator ||= if CLT.installed? && CLT.provides_sdk?
|
||||
CLTSDKLocator.new
|
||||
def sdk_locator
|
||||
if CLT.installed? && CLT.provides_sdk?
|
||||
CLT.sdk_locator
|
||||
else
|
||||
XcodeSDKLocator.new
|
||||
Xcode.sdk_locator
|
||||
end
|
||||
end
|
||||
|
||||
@locator.sdk_if_applicable(v)
|
||||
def sdk(v = nil)
|
||||
sdk_locator.sdk_if_applicable(v)
|
||||
end
|
||||
|
||||
def sdk_for_formula(f, v = nil)
|
||||
|
||||
@ -31,6 +31,10 @@ module OS
|
||||
SDK.new v, path, source
|
||||
end
|
||||
|
||||
def all_sdks
|
||||
sdk_paths.map { |v, p| SDK.new v, p, source }
|
||||
end
|
||||
|
||||
def sdk_if_applicable(v = nil)
|
||||
sdk = begin
|
||||
if v.nil?
|
||||
@ -47,15 +51,11 @@ module OS
|
||||
sdk
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def source
|
||||
nil
|
||||
end
|
||||
|
||||
def source_version
|
||||
OS::Mac::Version::NULL
|
||||
end
|
||||
private
|
||||
|
||||
def sdk_prefix
|
||||
""
|
||||
@ -81,15 +81,11 @@ module OS
|
||||
end
|
||||
|
||||
class XcodeSDKLocator < BaseSDKLocator
|
||||
private
|
||||
|
||||
def source
|
||||
:xcode
|
||||
end
|
||||
|
||||
def source_version
|
||||
OS::Mac::Xcode.version
|
||||
end
|
||||
private
|
||||
|
||||
def sdk_prefix
|
||||
@sdk_prefix ||= begin
|
||||
@ -105,15 +101,11 @@ module OS
|
||||
end
|
||||
|
||||
class CLTSDKLocator < BaseSDKLocator
|
||||
private
|
||||
|
||||
def source
|
||||
:clt
|
||||
end
|
||||
|
||||
def source_version
|
||||
OS::Mac::CLT.version
|
||||
end
|
||||
private
|
||||
|
||||
# While CLT SDKs existed prior to Xcode 10, those packages also
|
||||
# installed a traditional Unix-style header layout and we prefer
|
||||
|
||||
@ -107,10 +107,12 @@ module OS
|
||||
!prefix.nil?
|
||||
end
|
||||
|
||||
def sdk(v = nil)
|
||||
@locator ||= XcodeSDKLocator.new
|
||||
def sdk_locator
|
||||
@sdk_locator ||= XcodeSDKLocator.new
|
||||
end
|
||||
|
||||
@locator.sdk_if_applicable(v)
|
||||
def sdk(v = nil)
|
||||
sdk_locator.sdk_if_applicable(v)
|
||||
end
|
||||
|
||||
def sdk_path(v = nil)
|
||||
@ -219,10 +221,12 @@ module OS
|
||||
version >= "8"
|
||||
end
|
||||
|
||||
def sdk(v = nil)
|
||||
@locator ||= CLTSDKLocator.new
|
||||
def sdk_locator
|
||||
@sdk_locator ||= CLTSDKLocator.new
|
||||
end
|
||||
|
||||
@locator.sdk_if_applicable(v)
|
||||
def sdk(v = nil)
|
||||
sdk_locator.sdk_if_applicable(v)
|
||||
end
|
||||
|
||||
def sdk_path(v = nil)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "formula"
|
||||
require "cask/cask_loader"
|
||||
|
||||
module Readall
|
||||
class << self
|
||||
@ -35,28 +36,43 @@ module Readall
|
||||
end
|
||||
|
||||
def valid_formulae?(formulae)
|
||||
failed = false
|
||||
success = true
|
||||
formulae.each do |file|
|
||||
Formulary.factory(file)
|
||||
rescue Interrupt
|
||||
raise
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
onoe "Invalid formula: #{file}"
|
||||
puts e
|
||||
failed = true
|
||||
$stderr.puts e
|
||||
success = false
|
||||
end
|
||||
!failed
|
||||
success
|
||||
end
|
||||
|
||||
def valid_casks?(casks)
|
||||
success = true
|
||||
casks.each do |file|
|
||||
Cask::CaskLoader.load(file)
|
||||
rescue Interrupt
|
||||
raise
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
onoe "Invalid cask: #{file}"
|
||||
$stderr.puts e
|
||||
success = false
|
||||
end
|
||||
success
|
||||
end
|
||||
|
||||
def valid_tap?(tap, options = {})
|
||||
failed = false
|
||||
success = true
|
||||
if options[:aliases]
|
||||
valid_aliases = valid_aliases?(tap.alias_dir, tap.formula_dir)
|
||||
failed = true unless valid_aliases
|
||||
success = false unless valid_aliases
|
||||
end
|
||||
valid_formulae = valid_formulae?(tap.formula_files)
|
||||
failed = true unless valid_formulae
|
||||
!failed
|
||||
valid_casks = valid_casks?(tap.cask_files)
|
||||
success = false if !valid_formulae || !valid_casks
|
||||
success
|
||||
end
|
||||
|
||||
private
|
||||
@ -79,3 +95,5 @@ module Readall
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "extend/os/readall"
|
||||
|
||||
@ -24,7 +24,8 @@ module RuboCop
|
||||
[{ name: :mirror, type: :method_call }],
|
||||
[{ name: :version, type: :method_call }],
|
||||
[{ name: :sha256, type: :method_call }],
|
||||
[{ name: :revision, type: :method_call }],
|
||||
[{ name: :license, type: :method_call }],
|
||||
[{ name: :revision, type: :method_call }],
|
||||
[{ name: :version_scheme, type: :method_call }],
|
||||
[{ name: :head, type: :method_call }],
|
||||
[{ name: :stable, type: :block_call }],
|
||||
|
||||
@ -22,18 +22,28 @@ module RuboCop
|
||||
Firefox
|
||||
].freeze
|
||||
|
||||
reason = string_content(parameters(keg_only_node).first)
|
||||
reason = parameters(keg_only_node).first
|
||||
offending_node(reason)
|
||||
name = Regexp.new(@formula_name, Regexp::IGNORECASE)
|
||||
reason = reason.sub(name, "")
|
||||
reason = string_content(reason).sub(name, "")
|
||||
first_word = reason.split.first
|
||||
|
||||
if reason =~ /\A[A-Z]/ && !reason.start_with?(*allowlist)
|
||||
problem "'#{first_word}' from the keg_only reason should be '#{first_word.downcase}'."
|
||||
problem "'#{first_word}' from the `keg_only` reason should be '#{first_word.downcase}'."
|
||||
end
|
||||
|
||||
return unless reason.end_with?(".")
|
||||
|
||||
problem "keg_only reason should not end with a period."
|
||||
problem "`keg_only` reason should not end with a period."
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
reason = string_content(node)
|
||||
reason[0] = reason[0].downcase
|
||||
reason = reason.delete_suffix(".")
|
||||
corrector.replace(node.source_range, "\"#{reason}\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -555,6 +555,55 @@ module RuboCop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ShellCommands < FormulaCop
|
||||
def audit_formula(_node, _class_node, _parent_class_node, body_node)
|
||||
# Match shell commands separated by spaces in the same string
|
||||
shell_cmd_with_spaces_regex = /[^"' ]*(?:\s[^"' ]*)+/
|
||||
|
||||
popen_commands = [
|
||||
:popen_read,
|
||||
:safe_popen_read,
|
||||
:popen_write,
|
||||
:safe_popen_write,
|
||||
]
|
||||
|
||||
shell_metacharacters = %w[> < < | ; : & * $ ? : ~ + @ !` ( ) [ ]]
|
||||
|
||||
find_every_method_call_by_name(body_node, :system).each do |method|
|
||||
# Only separate when no shell metacharacters are present
|
||||
next if shell_metacharacters.any? { |meta| string_content(parameters(method).first).include?(meta) }
|
||||
|
||||
next unless match = regex_match_group(parameters(method).first, shell_cmd_with_spaces_regex)
|
||||
|
||||
good_args = match[0].gsub(" ", "\", \"")
|
||||
offending_node(parameters(method).first)
|
||||
problem "Separate `system` commands into `\"#{good_args}\"`"
|
||||
end
|
||||
|
||||
popen_commands.each do |command|
|
||||
find_instance_method_call(body_node, "Utils", command) do |method|
|
||||
index = parameters(method).first.hash_type? ? 1 : 0
|
||||
|
||||
# Only separate when no shell metacharacters are present
|
||||
next if shell_metacharacters.any? { |meta| string_content(parameters(method)[index]).include?(meta) }
|
||||
|
||||
next unless match = regex_match_group(parameters(method)[index], shell_cmd_with_spaces_regex)
|
||||
|
||||
good_args = match[0].gsub(" ", "\", \"")
|
||||
offending_node(parameters(method)[index])
|
||||
problem "Separate `Utils.#{command}` commands into `\"#{good_args}\"`"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def autocorrect(node)
|
||||
lambda do |corrector|
|
||||
good_args = node.source.gsub(" ", "\", \"")
|
||||
corrector.replace(node.source_range, good_args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -47,12 +47,12 @@ class Sandbox
|
||||
end
|
||||
|
||||
def allow_cvs
|
||||
allow_write_path "/Users/#{ENV["USER"]}/.cvspass"
|
||||
allow_write_path "#{ENV["HOME"]}/.cvspass"
|
||||
end
|
||||
|
||||
def allow_fossil
|
||||
allow_write_path "/Users/#{ENV["USER"]}/.fossil"
|
||||
allow_write_path "/Users/#{ENV["USER"]}/.fossil-journal"
|
||||
allow_write_path "#{ENV["HOME"]}/.fossil"
|
||||
allow_write_path "#{ENV["HOME"]}/.fossil-journal"
|
||||
end
|
||||
|
||||
def allow_write_cellar(formula)
|
||||
@ -63,7 +63,7 @@ class Sandbox
|
||||
|
||||
# Xcode projects expect access to certain cache/archive dirs.
|
||||
def allow_write_xcode
|
||||
allow_write_path "/Users/#{ENV["USER"]}/Library/Developer"
|
||||
allow_write_path "#{ENV["HOME"]}/Library/Developer"
|
||||
end
|
||||
|
||||
def allow_write_log(formula)
|
||||
|
||||
@ -168,6 +168,7 @@ false:
|
||||
- ./dev-cmd/test.rb
|
||||
- ./dev-cmd/tests.rb
|
||||
- ./dev-cmd/unpack.rb
|
||||
- ./dev-cmd/update-license-data.rb
|
||||
- ./dev-cmd/update-test.rb
|
||||
- ./dev-cmd/vendor-gems.rb
|
||||
- ./development_tools.rb
|
||||
@ -185,6 +186,7 @@ false:
|
||||
- ./extend/os/linux/hardware/cpu.rb
|
||||
- ./extend/os/linux/install.rb
|
||||
- ./extend/os/linux/keg_relocate.rb
|
||||
- ./extend/os/linux/readall.rb
|
||||
- ./extend/os/linux/requirements/osxfuse_requirement.rb
|
||||
- ./extend/os/linux/system_config.rb
|
||||
- ./extend/os/linux/tap.rb
|
||||
@ -348,6 +350,7 @@ false:
|
||||
- ./test/dev-cmd/audit_spec.rb
|
||||
- ./test/dev-cmd/create_spec.rb
|
||||
- ./test/dev-cmd/extract_spec.rb
|
||||
- ./test/dev-cmd/update-license-data_spec.rb
|
||||
- ./test/diagnostic_checks_spec.rb
|
||||
- ./test/download_strategies_spec.rb
|
||||
- ./test/error_during_execution_spec.rb
|
||||
@ -443,9 +446,9 @@ false:
|
||||
- ./unpack_strategy/xar.rb
|
||||
- ./unpack_strategy/xz.rb
|
||||
- ./unpack_strategy/zip.rb
|
||||
- ./upgrade.rb
|
||||
- ./utils.rb
|
||||
- ./utils/analytics.rb
|
||||
- ./utils/bottles.rb
|
||||
- ./utils/curl.rb
|
||||
- ./utils/fork.rb
|
||||
- ./utils/formatter.rb
|
||||
@ -453,7 +456,6 @@ false:
|
||||
- ./utils/github.rb
|
||||
- ./utils/notability.rb
|
||||
- ./utils/popen.rb
|
||||
- ./utils/tty.rb
|
||||
- ./utils/user.rb
|
||||
|
||||
false:
|
||||
@ -848,8 +850,6 @@ false:
|
||||
- ./utils/inreplace.rb
|
||||
- ./utils/link.rb
|
||||
- ./utils/shebang.rb
|
||||
- ./utils/shell.rb
|
||||
- ./utils/svn.rb
|
||||
- ./version.rb
|
||||
|
||||
true:
|
||||
@ -889,6 +889,10 @@ true:
|
||||
- ./tap_constants.rb
|
||||
- ./test/support/helper/fixtures.rb
|
||||
- ./test/support/lib/config.rb
|
||||
- ./utils/bottles.rb
|
||||
- ./utils/shell.rb
|
||||
- ./utils/svn.rb
|
||||
- ./utils/tty.rb
|
||||
- ./version/null.rb
|
||||
|
||||
strict:
|
||||
@ -920,6 +924,7 @@ strict:
|
||||
- ./extend/os/mac/formula_support.rb
|
||||
- ./extend/os/missing_formula.rb
|
||||
- ./extend/os/pathname.rb
|
||||
- ./extend/os/readall.rb
|
||||
- ./extend/os/requirements/java_requirement.rb
|
||||
- ./extend/os/requirements/osxfuse_requirement.rb
|
||||
- ./extend/os/requirements/x11_requirement.rb
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
||||
# tapioca sync
|
||||
# tapioca sync --exclude json
|
||||
|
||||
# typed: true
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
||||
# tapioca sync
|
||||
# tapioca sync --exclude json
|
||||
|
||||
# typed: true
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
||||
# tapioca sync
|
||||
# tapioca sync --exclude json
|
||||
|
||||
# typed: true
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
||||
# tapioca sync
|
||||
|
||||
# typed: true
|
||||
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
||||
# tapioca sync
|
||||
|
||||
# typed: true
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# This file is autogenerated. Do not edit it by hand. Regenerate it with:
|
||||
# tapioca sync
|
||||
# tapioca sync --exclude json
|
||||
|
||||
# typed: true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
38
Library/Homebrew/sorbet/rbi/utils/shell.rbi
Normal file
38
Library/Homebrew/sorbet/rbi/utils/shell.rbi
Normal file
@ -0,0 +1,38 @@
|
||||
# typed: strict
|
||||
|
||||
module Utils::Shell
|
||||
include Kernel
|
||||
|
||||
sig{ params(path: String).returns(T.nilable(Symbol)) }
|
||||
def from_path(path)
|
||||
end
|
||||
|
||||
sig{ returns(T.nilable(Symbol)) }
|
||||
def preferred
|
||||
end
|
||||
|
||||
def parent
|
||||
end
|
||||
|
||||
def export_value(key, value, shell = preferred)
|
||||
end
|
||||
|
||||
sig{ returns(String) }
|
||||
def profile
|
||||
end
|
||||
|
||||
def set_variable_in_profile(variable, value)
|
||||
end
|
||||
|
||||
sig{ params(path: String).returns(T.nilable(String)) }
|
||||
def prepend_path_in_profile(path)
|
||||
end
|
||||
|
||||
sig{ params(str: String).returns(T.nilable(String)) }
|
||||
def csh_quote(str)
|
||||
end
|
||||
|
||||
sig{ params(str: String).returns(T.nilable(String)) }
|
||||
def sh_quote(str)
|
||||
end
|
||||
end
|
||||
36
Library/Homebrew/sorbet/rbi/utils/tty.rbi
Normal file
36
Library/Homebrew/sorbet/rbi/utils/tty.rbi
Normal file
@ -0,0 +1,36 @@
|
||||
# typed: strict
|
||||
|
||||
module Tty
|
||||
include Kernel
|
||||
|
||||
sig{ params(string: String).returns(String) }
|
||||
def strip_ansi(string)
|
||||
end
|
||||
|
||||
sig{ returns(Integer) }
|
||||
def width()
|
||||
end
|
||||
|
||||
sig{ params(string: String).returns(T.nilable(String)) }
|
||||
def truncate(string)
|
||||
end
|
||||
|
||||
def append_to_escape_sequence(code)
|
||||
end
|
||||
|
||||
sig{ returns(String) }
|
||||
def current_escape_sequence()
|
||||
end
|
||||
|
||||
sig{ void }
|
||||
def reset_escape_sequence!()
|
||||
end
|
||||
|
||||
sig{ returns(String) }
|
||||
def to_s
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def color?
|
||||
end
|
||||
end
|
||||
8
Library/Homebrew/sorbet/rbi/utils/utils.rbi
Normal file
8
Library/Homebrew/sorbet/rbi/utils/utils.rbi
Normal file
@ -0,0 +1,8 @@
|
||||
# typed: strict
|
||||
|
||||
module Utils
|
||||
include Kernel
|
||||
|
||||
class Bottles
|
||||
end
|
||||
end
|
||||
@ -8,7 +8,7 @@ describe "Homebrew.search_args" do
|
||||
end
|
||||
|
||||
describe "brew search", :integration_test do
|
||||
it "falls back to a GitHub tap search when no formula is found", :needs_network do
|
||||
it "falls back to a GitHub tap search when no formula is found", :needs_macos, :needs_network do
|
||||
setup_test_formula "testball"
|
||||
setup_remote_tap "homebrew/cask"
|
||||
|
||||
@ -16,4 +16,13 @@ describe "brew search", :integration_test do
|
||||
.to output(/firefox/).to_stdout
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
# doesn't actually need Linux but only want one integration test per-OS.
|
||||
it "finds formula in search", :need_linux do
|
||||
setup_test_formula "testball"
|
||||
|
||||
expect { brew "search", "testball" }
|
||||
.to output(/testball/).to_stdout
|
||||
.and be_a_success
|
||||
end
|
||||
end
|
||||
|
||||
@ -79,6 +79,92 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
describe "#audit_license" do
|
||||
let(:spdx_data) {
|
||||
JSON.parse Pathname(File.join(File.dirname(__FILE__), "../../data/spdx.json")).read
|
||||
}
|
||||
|
||||
let(:custom_spdx_id) { "zzz" }
|
||||
let(:standard_mismatch_spdx_id) { "0BSD" }
|
||||
|
||||
it "does not check if the formula is not a new formula" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: false
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license ""
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "detects no license info" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license ""
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "No license specified for package."
|
||||
end
|
||||
|
||||
it "detects if license is not a standard spdx-id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "#{custom_spdx_id}"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "#{custom_spdx_id} is not a standard SPDX license."
|
||||
end
|
||||
|
||||
it "verifies that a license info is a standard spdx id" do
|
||||
fa = formula_auditor "foo", <<~RUBY, spdx_data: spdx_data, new_formula: true
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "0BSD"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "checks online and verifies that a standard license id is the same "\
|
||||
"as what is indicated on its Github repo" do
|
||||
fa = formula_auditor "cask", <<~RUBY, spdx_data: spdx_data, online: true, core_tap: true, new_formula: true
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
license "GPL-3.0"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems).to be_empty
|
||||
end
|
||||
|
||||
it "checks online and detects that a formula-specified license is not "\
|
||||
"the same as what is indicated on its Github repository" do
|
||||
fa = formula_auditor "cask", <<~RUBY, online: true, spdx_data: spdx_data, core_tap: true, new_formula: true
|
||||
class Cask < Formula
|
||||
url "https://github.com/cask/cask/archive/v0.8.4.tar.gz"
|
||||
head "https://github.com/cask/cask.git"
|
||||
license "#{standard_mismatch_spdx_id}"
|
||||
end
|
||||
RUBY
|
||||
|
||||
fa.audit_license
|
||||
expect(fa.problems.first).to match "License mismatch - GitHub license is: GPL-3.0, "\
|
||||
"but Formulae license states: #{standard_mismatch_spdx_id}."
|
||||
end
|
||||
end
|
||||
|
||||
describe "#audit_file" do
|
||||
specify "no issue" do
|
||||
fa = formula_auditor "foo", <<~RUBY
|
||||
@ -277,6 +363,7 @@ module Homebrew
|
||||
origin_formula_path.write <<~RUBY
|
||||
class Foo#{foo_version} < Formula
|
||||
url "https://brew.sh/foo-1.0.tar.gz"
|
||||
sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"
|
||||
revision 2
|
||||
version_scheme 1
|
||||
end
|
||||
@ -302,7 +389,7 @@ module Homebrew
|
||||
formula_path.write text
|
||||
end
|
||||
|
||||
def formula_gsub_commit(before, after = "")
|
||||
def formula_gsub_origin_commit(before, after = "")
|
||||
text = origin_formula_path.read
|
||||
text.gsub!(before, after)
|
||||
origin_formula_path.unlink
|
||||
@ -318,19 +405,48 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
context "checksums" do
|
||||
context "should not change with the same version" do
|
||||
before do
|
||||
formula_gsub(
|
||||
'sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"',
|
||||
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to match("stable sha256 changed without the version also changing") }
|
||||
end
|
||||
|
||||
context "can change with the different version" do
|
||||
before do
|
||||
formula_gsub_origin_commit(
|
||||
'sha256 "31cccfc6630528db1c8e3a06f6decf2a370060b982841cfab2b8677400a5092e"',
|
||||
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
||||
)
|
||||
formula_gsub "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_origin_commit(
|
||||
'sha256 "3622d2a53236ed9ca62de0616a7e80fd477a9a3f862ba09d503da188f53ca523"',
|
||||
'sha256 "e048c5e6144f5932d8672c2fade81d9073d5b3ca1517b84df006de3d25414fc1"',
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
context "revisions" do
|
||||
context "should not be removed when first committed above 0" do
|
||||
it { is_expected.to be_nil }
|
||||
end
|
||||
|
||||
context "should not decrease with the same version" do
|
||||
before { formula_gsub_commit "revision 2", "revision 1" }
|
||||
before { formula_gsub_origin_commit "revision 2", "revision 1" }
|
||||
|
||||
it { is_expected.to match("revision should not decrease (from 2 to 1)") }
|
||||
end
|
||||
|
||||
context "should not be removed with the same version" do
|
||||
before { formula_gsub_commit "revision 2" }
|
||||
before { formula_gsub_origin_commit "revision 2" }
|
||||
|
||||
it { is_expected.to match("revision should not decrease (from 2 to 0)") }
|
||||
end
|
||||
@ -342,15 +458,15 @@ module Homebrew
|
||||
end
|
||||
|
||||
context "should be removed with a newer version" do
|
||||
before { formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" }
|
||||
before { formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz" }
|
||||
|
||||
it { is_expected.to match("'revision 2' should be removed") }
|
||||
end
|
||||
|
||||
context "should not warn on an newer version revision removal" do
|
||||
before do
|
||||
formula_gsub_commit "revision 2", ""
|
||||
formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_origin_commit "revision 2", ""
|
||||
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
@ -367,9 +483,9 @@ module Homebrew
|
||||
|
||||
context "should not warn on past increment by more than 1" do
|
||||
before do
|
||||
formula_gsub_commit "revision 2", "# no revision"
|
||||
formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_commit "# no revision", "revision 3"
|
||||
formula_gsub_origin_commit "revision 2", "# no revision"
|
||||
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_origin_commit "# no revision", "revision 3"
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
@ -378,16 +494,16 @@ module Homebrew
|
||||
|
||||
context "version_schemes" do
|
||||
context "should not decrease with the same version" do
|
||||
before { formula_gsub_commit "version_scheme 1" }
|
||||
before { formula_gsub_origin_commit "version_scheme 1" }
|
||||
|
||||
it { is_expected.to match("version_scheme should not decrease (from 1 to 0)") }
|
||||
end
|
||||
|
||||
context "should not decrease with a new version" do
|
||||
before do
|
||||
formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_commit "version_scheme 1", ""
|
||||
formula_gsub_commit "revision 2", ""
|
||||
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_origin_commit "version_scheme 1", ""
|
||||
formula_gsub_origin_commit "revision 2", ""
|
||||
end
|
||||
|
||||
it { is_expected.to match("version_scheme should not decrease (from 1 to 0)") }
|
||||
@ -395,10 +511,10 @@ module Homebrew
|
||||
|
||||
context "should only increment by 1" do
|
||||
before do
|
||||
formula_gsub_commit "version_scheme 1", "# no version_scheme"
|
||||
formula_gsub_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_commit "revision 2", ""
|
||||
formula_gsub_commit "# no version_scheme", "version_scheme 3"
|
||||
formula_gsub_origin_commit "version_scheme 1", "# no version_scheme"
|
||||
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-1.1.tar.gz"
|
||||
formula_gsub_origin_commit "revision 2", ""
|
||||
formula_gsub_origin_commit "# no version_scheme", "version_scheme 3"
|
||||
end
|
||||
|
||||
it { is_expected.to match("version_schemes should only increment by 1") }
|
||||
@ -414,8 +530,8 @@ module Homebrew
|
||||
|
||||
context "committed can decrease" do
|
||||
before do
|
||||
formula_gsub_commit "revision 2"
|
||||
formula_gsub_commit "foo-1.0.tar.gz", "foo-0.9.tar.gz"
|
||||
formula_gsub_origin_commit "revision 2"
|
||||
formula_gsub_origin_commit "foo-1.0.tar.gz", "foo-0.9.tar.gz"
|
||||
end
|
||||
|
||||
it { is_expected.to be_nil }
|
||||
|
||||
7
Library/Homebrew/test/dev-cmd/sponsors_spec.rb
Normal file
7
Library/Homebrew/test/dev-cmd/sponsors_spec.rb
Normal file
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
describe "Homebrew.sponsors_args" do
|
||||
it_behaves_like "parseable arguments"
|
||||
end
|
||||
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
describe "Homebrew.update_license_data_args" do
|
||||
it_behaves_like "parseable arguments"
|
||||
end
|
||||
@ -3,7 +3,7 @@
|
||||
require "exceptions"
|
||||
|
||||
describe MultipleVersionsInstalledError do
|
||||
subject { described_class.new("foo") }
|
||||
subject { described_class.new("foo has multiple installed versions") }
|
||||
|
||||
its(:to_s) { is_expected.to eq("foo has multiple installed versions") }
|
||||
end
|
||||
|
||||
@ -19,6 +19,18 @@ describe RuboCop::Cop::FormulaAudit::ComponentsOrder do
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "When license precedes sha256" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
homepage "https://brew.sh"
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
license "0BSD"
|
||||
sha256 "samplesha256"
|
||||
^^^^^^^^^^^^^^^^^^^^^ `sha256` (line 5) should be put before `license` (line 4)
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "When `bottle` precedes `livecheck`" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
|
||||
@ -13,7 +13,7 @@ describe RuboCop::Cop::FormulaAudit::KegOnly do
|
||||
homepage "https://brew.sh"
|
||||
|
||||
keg_only "Because why not"
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ 'Because' from the keg_only reason should be 'because'.
|
||||
^^^^^^^^^^^^^^^^^ 'Because' from the `keg_only` reason should be 'because'.
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
@ -25,11 +25,53 @@ describe RuboCop::Cop::FormulaAudit::KegOnly do
|
||||
homepage "https://brew.sh"
|
||||
|
||||
keg_only "ending with a period."
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ keg_only reason should not end with a period.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^ `keg_only` reason should not end with a period.
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
specify "keg_only_autocorrects_downcasing" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
homepage "https://brew.sh"
|
||||
keg_only "Because why not"
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
homepage "https://brew.sh"
|
||||
keg_only "because why not"
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
|
||||
specify "keg_only_autocorrects_redundant_period" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
homepage "https://brew.sh"
|
||||
keg_only "ending with a period."
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
url "https://brew.sh/foo-1.0.tgz"
|
||||
homepage "https://brew.sh"
|
||||
keg_only "ending with a period"
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
|
||||
specify "keg_only_handles_block_correctly" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
|
||||
@ -1096,3 +1096,252 @@ describe RuboCop::Cop::FormulaAuditStrict::MakeCheck do
|
||||
|
||||
include_examples "formulae exist", described_class::MAKE_CHECK_ALLOWLIST
|
||||
end
|
||||
|
||||
describe RuboCop::Cop::FormulaAuditStrict::ShellCommands do
|
||||
subject(:cop) { described_class.new }
|
||||
|
||||
context "When auditing shell commands" do
|
||||
it "system arguments should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "foo bar"
|
||||
^^^^^^^^^ Separate `system` commands into `\"foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "system arguments with string interpolation should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "\#{bin}/foo bar"
|
||||
^^^^^^^^^^^^^^^^ Separate `system` commands into `\"\#{bin}/foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "system arguments with metacharacters should not be separated" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "foo bar > baz"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "only the first system argument should be separated" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "foo", "bar baz"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.popen arguments should not be separated" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen("foo bar")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.popen_read arguments should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("foo bar")
|
||||
^^^^^^^^^ Separate `Utils.popen_read` commands into `\"foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.safe_popen_read arguments should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.safe_popen_read("foo bar")
|
||||
^^^^^^^^^ Separate `Utils.safe_popen_read` commands into `\"foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.popen_write arguments should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_write("foo bar")
|
||||
^^^^^^^^^ Separate `Utils.popen_write` commands into `\"foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.safe_popen_write arguments should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.safe_popen_write("foo bar")
|
||||
^^^^^^^^^ Separate `Utils.safe_popen_write` commands into `\"foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.popen_read arguments with string interpolation should be separated" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("\#{bin}/foo bar")
|
||||
^^^^^^^^^^^^^^^^ Separate `Utils.popen_read` commands into `\"\#{bin}/foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.popen_read arguments with metacharacters should not be separated" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("foo bar > baz")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "only the first Utils.popen_read argument should be separated" do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("foo", "bar baz")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "Utils.popen_read arguments should be separated following a shell variable" do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read({ "SHELL" => "bash"}, "foo bar")
|
||||
^^^^^^^^^ Separate `Utils.popen_read` commands into `\"foo\", \"bar\"`
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
it "separates shell commands in system" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "foo bar"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "foo", "bar"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
|
||||
it "separates shell commands with string interpolation in system" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "\#{foo}/bar baz"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
system "\#{foo}/bar", "baz"
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
|
||||
it "separates shell commands in Utils.popen_read" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("foo bar")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("foo", "bar")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
|
||||
it "separates shell commands with string interpolation in Utils.popen_read" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("\#{foo}/bar baz")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read("\#{foo}/bar", "baz")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
|
||||
it "separates shell commands following a shell variable in Utils.popen_read" do
|
||||
source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read({ "SHELL" => "bash" }, "foo bar")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
corrected_source = <<~RUBY
|
||||
class Foo < Formula
|
||||
def install
|
||||
Utils.popen_read({ "SHELL" => "bash" }, "foo", "bar")
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
new_source = autocorrect_source(source)
|
||||
expect(new_source).to eq(corrected_source)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -4,12 +4,10 @@ if ENV["HOMEBREW_TESTS_COVERAGE"]
|
||||
require "simplecov"
|
||||
|
||||
formatters = [SimpleCov::Formatter::HTMLFormatter]
|
||||
if ENV["HOMEBREW_COVERALLS_REPO_TOKEN"] && RUBY_PLATFORM[/darwin/]
|
||||
require "coveralls"
|
||||
if ENV["HOMEBREW_CODECOV_TOKEN"] && RUBY_PLATFORM[/darwin/]
|
||||
require "codecov"
|
||||
|
||||
Coveralls::Output.no_color if !ENV["HOMEBREW_COLOR"] && (ENV["HOMEBREW_NO_COLOR"] || !$stdout.tty?)
|
||||
|
||||
formatters << Coveralls::SimpleCov::Formatter
|
||||
formatters << SimpleCov::Formatter::Codecov
|
||||
|
||||
if ENV["TEST_ENV_NUMBER"]
|
||||
SimpleCov.at_exit do
|
||||
@ -18,16 +16,7 @@ if ENV["HOMEBREW_TESTS_COVERAGE"]
|
||||
end
|
||||
end
|
||||
|
||||
ENV["CI_NAME"] = ENV["HOMEBREW_CI_NAME"]
|
||||
ENV["COVERALLS_REPO_TOKEN"] = ENV["HOMEBREW_COVERALLS_REPO_TOKEN"]
|
||||
|
||||
ENV["CI_BUILD_NUMBER"] = ENV["HOMEBREW_CI_BUILD_NUMBER"]
|
||||
ENV["CI_BRANCH"] = ENV["HOMEBREW_CI_BRANCH"]
|
||||
%r{refs/pull/(?<pr>\d+)/merge} =~ ENV["HOMEBREW_CI_BUILD_NUMBER"]
|
||||
ENV["CI_PULL_REQUEST"] = pr
|
||||
ENV["CI_BUILD_URL"] = "https://github.com/#{ENV["HOMEBREW_GITHUB_REPOSITORY"]}/pull/#{pr}/checks"
|
||||
|
||||
ENV["CI_JOB_ID"] = ENV["TEST_ENV_NUMBER"] || "1"
|
||||
ENV["CODECOV_TOKEN"] = ENV["HOMEBREW_CODECOV_TOKEN"]
|
||||
end
|
||||
|
||||
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(formatters)
|
||||
@ -174,6 +163,7 @@ RSpec.configure do |config|
|
||||
Keg.clear_cache
|
||||
Tab.clear_cache
|
||||
FormulaInstaller.clear_attempted
|
||||
FormulaInstaller.clear_installed
|
||||
|
||||
TEST_DIRECTORIES.each(&:mkpath)
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ RSpec.shared_context "integration test" do
|
||||
|
||||
example.run
|
||||
ensure
|
||||
FileUtils.rm_r HOMEBREW_PREFIX/"bin"
|
||||
FileUtils.rm_rf HOMEBREW_PREFIX/"bin"
|
||||
end
|
||||
|
||||
# Generate unique ID to be able to
|
||||
|
||||
@ -42,6 +42,21 @@ describe GitHub do
|
||||
end
|
||||
end
|
||||
|
||||
describe "::approved_reviews", :needs_network do
|
||||
it "can get reviews for a pull request" do
|
||||
reviews = subject.approved_reviews("Homebrew", "homebrew-core", 1, commit: "deadbeef")
|
||||
expect(reviews).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "::sponsors_by_tier", :needs_network do
|
||||
it "errors on an unauthenticated token" do
|
||||
expect {
|
||||
subject.sponsors_by_tier("Homebrew")
|
||||
}.to raise_error(/INSUFFICIENT_SCOPES|FORBIDDEN/)
|
||||
end
|
||||
end
|
||||
|
||||
describe "::get_artifact_url", :needs_network do
|
||||
it "fails to find a nonexistant workflow" do
|
||||
expect {
|
||||
|
||||
242
Library/Homebrew/upgrade.rb
Normal file
242
Library/Homebrew/upgrade.rb
Normal file
@ -0,0 +1,242 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "reinstall"
|
||||
require "formula_installer"
|
||||
require "development_tools"
|
||||
require "messages"
|
||||
require "cleanup"
|
||||
|
||||
module Homebrew
|
||||
module_function
|
||||
|
||||
def upgrade_formulae(formulae_to_install)
|
||||
return if formulae_to_install.empty?
|
||||
return if args.dry_run?
|
||||
|
||||
# Sort keg-only before non-keg-only formulae to avoid any needless conflicts
|
||||
# with outdated, non-keg-only versions of formulae being upgraded.
|
||||
formulae_to_install.sort! do |a, b|
|
||||
if !a.keg_only? && b.keg_only?
|
||||
1
|
||||
elsif a.keg_only? && !b.keg_only?
|
||||
-1
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
formulae_to_install.each do |f|
|
||||
Migrator.migrate_if_needed(f)
|
||||
begin
|
||||
upgrade_formula(f)
|
||||
Cleanup.install_formula_clean!(f)
|
||||
rescue UnsatisfiedRequirements => e
|
||||
Homebrew.failed = true
|
||||
onoe "#{f}: #{e}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def upgrade_formula(f)
|
||||
return if args.dry_run?
|
||||
|
||||
if f.opt_prefix.directory?
|
||||
keg = Keg.new(f.opt_prefix.resolved_path)
|
||||
keg_had_linked_opt = true
|
||||
keg_was_linked = keg.linked?
|
||||
end
|
||||
|
||||
formulae_maybe_with_kegs = [f] + f.old_installed_formulae
|
||||
outdated_kegs = formulae_maybe_with_kegs
|
||||
.map(&:linked_keg)
|
||||
.select(&:directory?)
|
||||
.map { |k| Keg.new(k.resolved_path) }
|
||||
linked_kegs = outdated_kegs.select(&:linked?)
|
||||
|
||||
if f.opt_prefix.directory?
|
||||
keg = Keg.new(f.opt_prefix.resolved_path)
|
||||
tab = Tab.for_keg(keg)
|
||||
end
|
||||
|
||||
build_options = BuildOptions.new(Options.create(args.flags_only), f.options)
|
||||
options = build_options.used_options
|
||||
options |= f.build.used_options
|
||||
options &= f.options
|
||||
|
||||
fi = FormulaInstaller.new(f)
|
||||
fi.options = options
|
||||
fi.build_bottle = args.build_bottle?
|
||||
fi.installed_on_request = args.named.present?
|
||||
fi.link_keg ||= keg_was_linked if keg_had_linked_opt
|
||||
if tab
|
||||
fi.build_bottle ||= tab.built_bottle?
|
||||
fi.installed_as_dependency = tab.installed_as_dependency
|
||||
fi.installed_on_request ||= tab.installed_on_request
|
||||
end
|
||||
|
||||
upgrade_version = if f.optlinked?
|
||||
"#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
|
||||
else
|
||||
"-> #{f.pkg_version}"
|
||||
end
|
||||
oh1 "Upgrading #{Formatter.identifier(f.full_specified_name)} #{upgrade_version} #{fi.options.to_a.join(" ")}"
|
||||
|
||||
fi.prelude
|
||||
fi.fetch
|
||||
|
||||
# first we unlink the currently active keg for this formula otherwise it is
|
||||
# possible for the existing build to interfere with the build we are about to
|
||||
# do! Seriously, it happens!
|
||||
outdated_kegs.each(&:unlink)
|
||||
|
||||
fi.install
|
||||
fi.finish
|
||||
rescue FormulaInstallationAlreadyAttemptedError
|
||||
# We already attempted to upgrade f as part of the dependency tree of
|
||||
# another formula. In that case, don't generate an error, just move on.
|
||||
nil
|
||||
rescue CannotInstallFormulaError => e
|
||||
ofail e
|
||||
rescue BuildError => e
|
||||
e.dump
|
||||
puts
|
||||
Homebrew.failed = true
|
||||
rescue DownloadError => e
|
||||
ofail e
|
||||
ensure
|
||||
# restore previous installation state if build failed
|
||||
begin
|
||||
linked_kegs.each(&:link) unless f.latest_version_installed?
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def check_installed_dependents
|
||||
installed_formulae = FormulaInstaller.installed.to_a
|
||||
return if installed_formulae.empty?
|
||||
|
||||
outdated_dependents =
|
||||
installed_formulae.flat_map(&:runtime_installed_formula_dependents)
|
||||
.select(&:outdated?)
|
||||
return if outdated_dependents.blank?
|
||||
|
||||
outdated_dependents -= installed_formulae if args.dry_run?
|
||||
|
||||
upgradeable_dependents =
|
||||
outdated_dependents.reject(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
pinned_dependents =
|
||||
outdated_dependents.select(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
|
||||
if pinned_dependents.present?
|
||||
plural = "dependent".pluralize(pinned_dependents.count)
|
||||
ohai "Not upgrading #{pinned_dependents.count} pinned #{plural}:"
|
||||
puts(pinned_dependents.map do |f|
|
||||
"#{f.full_specified_name} #{f.pkg_version}"
|
||||
end.join(", "))
|
||||
end
|
||||
|
||||
# Print the upgradable dependents.
|
||||
if upgradeable_dependents.blank?
|
||||
ohai "No outdated dependents to upgrade!" unless args.dry_run?
|
||||
else
|
||||
plural = "dependent".pluralize(upgradeable_dependents.count)
|
||||
verb = args.dry_run? ? "Would upgrade" : "Upgrading"
|
||||
ohai "#{verb} #{upgradeable_dependents.count} #{plural}:"
|
||||
formulae_upgrades = upgradeable_dependents.map do |f|
|
||||
name = f.full_specified_name
|
||||
if f.optlinked?
|
||||
"#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
|
||||
else
|
||||
"#{name} #{f.pkg_version}"
|
||||
end
|
||||
end
|
||||
puts formulae_upgrades.join(", ")
|
||||
end
|
||||
|
||||
upgrade_formulae(upgradeable_dependents)
|
||||
|
||||
# Assess the dependents tree again now we've upgraded.
|
||||
oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run?
|
||||
broken_dependents = CacheStoreDatabase.use(:linkage) do |db|
|
||||
installed_formulae.flat_map(&:runtime_installed_formula_dependents)
|
||||
.select do |f|
|
||||
keg = f.opt_or_installed_prefix_keg
|
||||
next unless keg
|
||||
|
||||
LinkageChecker.new(keg, cache_db: db)
|
||||
.broken_library_linkage?
|
||||
end.compact
|
||||
end
|
||||
if broken_dependents.blank?
|
||||
if args.dry_run?
|
||||
ohai "No currently broken dependents found!"
|
||||
opoo "If they are broken by the upgrade they will also be upgraded or reinstalled."
|
||||
else
|
||||
ohai "No broken dependents found!"
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
reinstallable_broken_dependents =
|
||||
broken_dependents.reject(&:outdated?)
|
||||
.reject(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
outdated_pinned_broken_dependents =
|
||||
broken_dependents.select(&:outdated?)
|
||||
.select(&:pinned?)
|
||||
.sort { |a, b| depends_on(a, b) }
|
||||
|
||||
# Print the pinned dependents.
|
||||
if outdated_pinned_broken_dependents.present?
|
||||
count = outdated_pinned_broken_dependents.count
|
||||
plural = "dependent".pluralize(outdated_pinned_broken_dependents.count)
|
||||
onoe "Not reinstalling #{count} broken and outdated, but pinned #{plural}:"
|
||||
$stderr.puts(outdated_pinned_broken_dependents.map do |f|
|
||||
"#{f.full_specified_name} #{f.pkg_version}"
|
||||
end.join(", "))
|
||||
end
|
||||
|
||||
# Print the broken dependents.
|
||||
if reinstallable_broken_dependents.blank?
|
||||
ohai "No broken dependents to reinstall!"
|
||||
else
|
||||
count = reinstallable_broken_dependents.count
|
||||
plural = "dependent".pluralize(reinstallable_broken_dependents.count)
|
||||
ohai "Reinstalling #{count} broken #{plural} from source:"
|
||||
puts reinstallable_broken_dependents.map(&:full_specified_name)
|
||||
.join(", ")
|
||||
end
|
||||
|
||||
return if args.dry_run?
|
||||
|
||||
reinstallable_broken_dependents.each do |f|
|
||||
reinstall_formula(f, build_from_source: true)
|
||||
rescue FormulaInstallationAlreadyAttemptedError
|
||||
# We already attempted to reinstall f as part of the dependency tree of
|
||||
# another formula. In that case, don't generate an error, just move on.
|
||||
nil
|
||||
rescue CannotInstallFormulaError => e
|
||||
ofail e
|
||||
rescue BuildError => e
|
||||
e.dump
|
||||
puts
|
||||
Homebrew.failed = true
|
||||
rescue DownloadError => e
|
||||
ofail e
|
||||
end
|
||||
end
|
||||
|
||||
# @private
|
||||
def depends_on(a, b)
|
||||
if a.opt_or_installed_prefix_keg
|
||||
&.runtime_dependencies
|
||||
&.any? { |d| d["full_name"] == b.full_name }
|
||||
1
|
||||
else
|
||||
a <=> b
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -54,21 +54,25 @@ def curl(*args, secrets: [], **options)
|
||||
secrets: secrets
|
||||
end
|
||||
|
||||
def curl_download(*args, to: nil, **options)
|
||||
def curl_download(*args, to: nil, partial: true, **options)
|
||||
destination = Pathname(to)
|
||||
destination.dirname.mkpath
|
||||
|
||||
range_stdout = curl_output("--location", "--range", "0-1",
|
||||
"--dump-header", "-",
|
||||
"--write-out", "%\{http_code}",
|
||||
"--output", "/dev/null", *args, **options).stdout
|
||||
headers, _, http_status = range_stdout.partition("\r\n\r\n")
|
||||
if partial
|
||||
range_stdout = curl_output("--location", "--range", "0-1",
|
||||
"--dump-header", "-",
|
||||
"--write-out", "%\{http_code}",
|
||||
"--output", "/dev/null", *args, **options).stdout
|
||||
headers, _, http_status = range_stdout.partition("\r\n\r\n")
|
||||
|
||||
supports_partial_download = http_status.to_i == 206 # Partial Content
|
||||
if supports_partial_download &&
|
||||
destination.exist? &&
|
||||
destination.size == %r{^.*Content-Range: bytes \d+-\d+/(\d+)\r\n.*$}m.match(headers)&.[](1)&.to_i
|
||||
return # We've already downloaded all the bytes
|
||||
supports_partial_download = http_status.to_i == 206 # Partial Content
|
||||
if supports_partial_download &&
|
||||
destination.exist? &&
|
||||
destination.size == %r{^.*Content-Range: bytes \d+-\d+/(\d+)\r\n.*$}m.match(headers)&.[](1)&.to_i
|
||||
return # We've already downloaded all the bytes
|
||||
end
|
||||
else
|
||||
supports_partial_download = false
|
||||
end
|
||||
|
||||
continue_at = if destination.exist? && supports_partial_download
|
||||
|
||||
@ -231,6 +231,15 @@ module GitHub
|
||||
end
|
||||
end
|
||||
|
||||
def open_graphql(query, scopes: [].freeze)
|
||||
data = { query: query }
|
||||
result = open_api("https://api.github.com/graphql", scopes: scopes, data: data, request_method: "POST")
|
||||
|
||||
raise Error, result["errors"].map { |e| "#{e["type"]}: #{e["message"]}" }.join("\n") if result["errors"].present?
|
||||
|
||||
result["data"]
|
||||
end
|
||||
|
||||
def raise_api_error(output, errors, http_code, headers, scopes)
|
||||
json = begin
|
||||
JSON.parse(output)
|
||||
@ -429,6 +438,48 @@ module GitHub
|
||||
open_api(uri) { |json| json.fetch("items", []) }
|
||||
end
|
||||
|
||||
def approved_reviews(user, repo, pr, commit: nil)
|
||||
query = <<~EOS
|
||||
{ repository(name: "#{repo}", owner: "#{user}") {
|
||||
pullRequest(number: #{pr}) {
|
||||
reviews(states: APPROVED, first: 100) {
|
||||
nodes {
|
||||
author {
|
||||
... on User { email login name databaseId }
|
||||
... on Organization { email login name databaseId }
|
||||
}
|
||||
authorAssociation
|
||||
commit { oid }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOS
|
||||
|
||||
result = open_graphql(query, scopes: ["user:email"])
|
||||
reviews = result["repository"]["pullRequest"]["reviews"]["nodes"]
|
||||
|
||||
reviews.map do |r|
|
||||
next if commit.present? && commit != r["commit"]["oid"]
|
||||
next unless %w[MEMBER OWNER].include? r["authorAssociation"]
|
||||
|
||||
email = if r["author"]["email"].blank?
|
||||
"#{r["author"]["databaseId"]}+#{r["author"]["login"]}@users.noreply.github.com"
|
||||
else
|
||||
r["author"]["email"]
|
||||
end
|
||||
|
||||
name = r["author"]["name"].presence || r["author"]["login"]
|
||||
|
||||
{
|
||||
"email" => email,
|
||||
"name" => name,
|
||||
"login" => r["author"]["login"],
|
||||
}
|
||||
end.compact
|
||||
end
|
||||
|
||||
def dispatch_event(user, repo, event, **payload)
|
||||
url = "#{API_URL}/repos/#{user}/#{repo}/dispatches"
|
||||
open_api(url, data: { event_type: event, client_payload: payload },
|
||||
@ -484,20 +535,21 @@ module GitHub
|
||||
end
|
||||
|
||||
def sponsors_by_tier(user)
|
||||
url = "https://api.github.com/graphql"
|
||||
data = {
|
||||
query: <<~EOS,
|
||||
{
|
||||
organization(login: "#{user}") {
|
||||
sponsorsListing {
|
||||
tiers(first: 100) {
|
||||
nodes {
|
||||
monthlyPriceInDollars
|
||||
adminInfo {
|
||||
sponsorships(first: 100) {
|
||||
totalCount
|
||||
nodes {
|
||||
sponsor { login }
|
||||
query = <<~EOS
|
||||
{ organization(login: "#{user}") {
|
||||
sponsorsListing {
|
||||
tiers(first: 10, orderBy: {field: MONTHLY_PRICE_IN_CENTS, direction: DESC}) {
|
||||
nodes {
|
||||
monthlyPriceInDollars
|
||||
adminInfo {
|
||||
sponsorships(first: 100, includePrivate: true) {
|
||||
totalCount
|
||||
nodes {
|
||||
privacyLevel
|
||||
sponsorEntity {
|
||||
__typename
|
||||
... on Organization { login name }
|
||||
... on User { login name }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -506,9 +558,44 @@ module GitHub
|
||||
}
|
||||
}
|
||||
}
|
||||
EOS
|
||||
}
|
||||
open_api(url, scopes: ["admin:org", "user"], data: data, request_method: "POST")
|
||||
}
|
||||
EOS
|
||||
result = open_graphql(query, scopes: ["admin:org", "user"])
|
||||
|
||||
tiers = result["organization"]["sponsorsListing"]["tiers"]["nodes"]
|
||||
|
||||
tiers.map do |t|
|
||||
tier = t["monthlyPriceInDollars"]
|
||||
raise Error, "Your token needs the 'admin:org' scope to access this API" if t["adminInfo"].nil?
|
||||
|
||||
sponsorships = t["adminInfo"]["sponsorships"]
|
||||
count = sponsorships["totalCount"]
|
||||
sponsors = sponsorships["nodes"].map do |sponsor|
|
||||
next unless sponsor["privacyLevel"] == "PUBLIC"
|
||||
|
||||
se = sponsor["sponsorEntity"]
|
||||
{
|
||||
"name" => se["name"].presence || sponsor["login"],
|
||||
"login" => se["login"],
|
||||
"type" => se["__typename"].downcase,
|
||||
}
|
||||
end.compact
|
||||
|
||||
{
|
||||
"tier" => tier,
|
||||
"count" => count,
|
||||
"sponsors" => sponsors,
|
||||
}
|
||||
end.compact
|
||||
end
|
||||
|
||||
def get_repo_license(user, repo)
|
||||
response = GitHub.open_api("#{GitHub::API_URL}/repos/#{user}/#{repo}/license")
|
||||
return unless response.key?("license")
|
||||
|
||||
response["license"]["spdx_id"]
|
||||
rescue GitHub::HTTPNotFoundError
|
||||
nil
|
||||
end
|
||||
|
||||
def api_errors
|
||||
|
||||
21
Library/Homebrew/vendor/bundle/bundler/setup.rb
vendored
21
Library/Homebrew/vendor/bundle/bundler/setup.rb
vendored
@ -14,18 +14,15 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ast-2.4.1/lib"
|
||||
$:.unshift "#{path}/"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/byebug-11.1.3"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/byebug-11.1.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/connection_pool-2.2.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/json-2.3.0"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/json-2.3.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/json-2.3.1"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/json-2.3.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/docile-1.3.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-html-0.10.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-0.16.1/lib"
|
||||
$:.unshift "#{path}/../../../../../../../../Library/Ruby/Gems/2.6.0/gems/sync-0.5.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tins-1.25.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/term-ansicolor-1.7.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thor-1.0.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/coveralls-0.8.23/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/diff-lcs-1.4.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-html-0.12.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/simplecov-0.18.5/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/url-0.3.2/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/codecov-0.1.17/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/connection_pool-2.2.3/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/diff-lcs-1.4.4/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/universal-darwin-19/2.6.0/unf_ext-0.0.7.7"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unf_ext-0.0.7.7/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unf-0.1.4/lib"
|
||||
@ -67,5 +64,5 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-progressbar-1.10
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/unicode-display_width-1.7.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-0.86.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-performance-1.6.1/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.40.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-rspec-1.41.0/lib"
|
||||
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ruby-macho-2.2.0/lib"
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module RSpec
|
||||
# Wrapper for RSpec example groups
|
||||
class ExampleGroup < Concept
|
||||
# @!method scope_change?(node)
|
||||
#
|
||||
# Detect if the node is an example group or shared example
|
||||
#
|
||||
# Selectors which indicate that we should stop searching
|
||||
#
|
||||
def_node_matcher :scope_change?, (
|
||||
ExampleGroups::ALL + SharedGroups::ALL + Includes::ALL
|
||||
).block_pattern
|
||||
|
||||
def subjects
|
||||
subjects_in_scope(node)
|
||||
end
|
||||
|
||||
def examples
|
||||
examples_in_scope(node).map(&Example.public_method(:new))
|
||||
end
|
||||
|
||||
def hooks
|
||||
hooks_in_scope(node).map(&Hook.public_method(:new))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subjects_in_scope(node)
|
||||
node.each_child_node.flat_map do |child|
|
||||
find_subjects(child)
|
||||
end
|
||||
end
|
||||
|
||||
def find_subjects(node)
|
||||
return [] if scope_change?(node)
|
||||
|
||||
if subject?(node)
|
||||
[node]
|
||||
else
|
||||
subjects_in_scope(node)
|
||||
end
|
||||
end
|
||||
|
||||
def hooks_in_scope(node)
|
||||
node.each_child_node.flat_map do |child|
|
||||
find_hooks(child)
|
||||
end
|
||||
end
|
||||
|
||||
def find_hooks(node)
|
||||
return [] if scope_change?(node) || example?(node)
|
||||
|
||||
if hook?(node)
|
||||
[node]
|
||||
else
|
||||
hooks_in_scope(node)
|
||||
end
|
||||
end
|
||||
|
||||
def examples_in_scope(node, &blk)
|
||||
node.each_child_node.flat_map do |child|
|
||||
find_examples(child, &blk)
|
||||
end
|
||||
end
|
||||
|
||||
# Recursively search for examples within the current scope
|
||||
#
|
||||
# Searches node for examples and halts when a scope change is detected
|
||||
#
|
||||
# @param node [RuboCop::Node] node to recursively search for examples
|
||||
#
|
||||
# @return [Array<RuboCop::Node>] discovered example nodes
|
||||
def find_examples(node)
|
||||
return [] if scope_change?(node)
|
||||
|
||||
if example?(node)
|
||||
[node]
|
||||
else
|
||||
examples_in_scope(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -13,6 +13,7 @@ require_relative 'rubocop/rspec/top_level_describe'
|
||||
require_relative 'rubocop/rspec/wording'
|
||||
require_relative 'rubocop/rspec/language'
|
||||
require_relative 'rubocop/rspec/language/node_pattern'
|
||||
require_relative 'rubocop/rspec/top_level_group'
|
||||
require_relative 'rubocop/rspec/concept'
|
||||
require_relative 'rubocop/rspec/example_group'
|
||||
require_relative 'rubocop/rspec/example'
|
||||
@ -43,12 +43,14 @@ module RuboCop
|
||||
def_node_matcher :rails_metadata?, <<-PATTERN
|
||||
(pair
|
||||
(sym :type)
|
||||
(sym {:request :feature :system :routing :view})
|
||||
(sym {
|
||||
:channel :controller :helper :job :mailer :model :request
|
||||
:routing :view :feature :system :mailbox
|
||||
}
|
||||
)
|
||||
)
|
||||
PATTERN
|
||||
|
||||
def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern
|
||||
|
||||
def on_top_level_describe(node, (described_value, _))
|
||||
return if shared_group?(root_node)
|
||||
return if valid_describe?(node)
|
||||
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