mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
commit
ae7b0c10f3
@ -120,6 +120,11 @@ module Homebrew
|
||||
[:switch, "--overwrite", {
|
||||
description: "Delete files that already exist in the prefix while linking.",
|
||||
}],
|
||||
[:switch, "--ask", {
|
||||
description: "Ask for confirmation before downloading and installing formulae. " \
|
||||
"Print bottles and dependencies download size and install size.",
|
||||
env: :ask,
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
@ -302,6 +307,8 @@ module Homebrew
|
||||
Install.perform_preinstall_checks_once
|
||||
Install.check_cc_argv(args.cc)
|
||||
|
||||
Install.ask(formulae, args: args) if args.ask?
|
||||
|
||||
Install.install_formulae(
|
||||
installed_formulae,
|
||||
build_bottle: args.build_bottle?,
|
||||
|
@ -63,6 +63,11 @@ module Homebrew
|
||||
[:switch, "-g", "--git", {
|
||||
description: "Create a Git repository, useful for creating patches to the software.",
|
||||
}],
|
||||
[:switch, "--ask", {
|
||||
description: "Ask for confirmation before downloading and upgrading formulae. " \
|
||||
"Print bottles and dependencies download size, install and net install size.",
|
||||
env: :ask,
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
@ -126,6 +131,9 @@ module Homebrew
|
||||
unless formulae.empty?
|
||||
Install.perform_preinstall_checks_once
|
||||
|
||||
# If asking the user is enabled, show dependency and size information.
|
||||
Install.ask(formulae, args: args) if args.ask?
|
||||
|
||||
formulae.each do |formula|
|
||||
if formula.pinned?
|
||||
onoe "#{formula.full_name} is pinned. You must unpin it to reinstall."
|
||||
|
@ -71,6 +71,11 @@ module Homebrew
|
||||
[:switch, "--overwrite", {
|
||||
description: "Delete files that already exist in the prefix while linking.",
|
||||
}],
|
||||
[:switch, "--ask", {
|
||||
description: "Ask for confirmation before downloading and upgrading formulae. " \
|
||||
"Print bottles and dependencies download size, install and net install size.",
|
||||
env: :ask,
|
||||
}],
|
||||
].each do |args|
|
||||
options = args.pop
|
||||
send(*args, **options)
|
||||
@ -211,11 +216,14 @@ module Homebrew
|
||||
"#{f.full_specified_name} #{f.pkg_version}"
|
||||
end
|
||||
end
|
||||
puts formulae_upgrades.join("\n")
|
||||
puts formulae_upgrades.join("\n") unless args.ask?
|
||||
end
|
||||
|
||||
Install.perform_preinstall_checks_once
|
||||
|
||||
# Main block: if asking the user is enabled, show dependency and size information.
|
||||
Install.ask(formulae_to_install, args: args) if args.ask?
|
||||
|
||||
Upgrade.upgrade_formulae(
|
||||
formulae_to_install,
|
||||
flags: args.flags_only,
|
||||
|
@ -48,6 +48,11 @@ module Homebrew
|
||||
"trying any other/default URLs.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_ASK: {
|
||||
description: "If set, pass `--ask`to all formulae `brew install`, `brew upgrade` and `brew reinstall` " \
|
||||
"commands.",
|
||||
boolean: true,
|
||||
},
|
||||
HOMEBREW_AUTO_UPDATE_SECS: {
|
||||
description: "Run `brew update` once every `$HOMEBREW_AUTO_UPDATE_SECS` seconds before some commands, " \
|
||||
"e.g. `brew install`, `brew upgrade` and `brew tap`. Alternatively, " \
|
||||
|
@ -459,13 +459,13 @@ module Kernel
|
||||
end
|
||||
|
||||
def disk_usage_readable(size_in_bytes)
|
||||
if size_in_bytes >= 1_073_741_824
|
||||
if size_in_bytes.abs >= 1_073_741_824
|
||||
size = size_in_bytes.to_f / 1_073_741_824
|
||||
unit = "GB"
|
||||
elsif size_in_bytes >= 1_048_576
|
||||
elsif size_in_bytes.abs >= 1_048_576
|
||||
size = size_in_bytes.to_f / 1_048_576
|
||||
unit = "MB"
|
||||
elsif size_in_bytes >= 1_024
|
||||
elsif size_in_bytes.abs >= 1_024
|
||||
size = size_in_bytes.to_f / 1_024
|
||||
unit = "KB"
|
||||
else
|
||||
|
@ -327,6 +327,22 @@ module Homebrew
|
||||
puts formula_names.join(" ")
|
||||
end
|
||||
|
||||
# If asking the user is enabled, show dependency and size information.
|
||||
def ask(formulae, args:)
|
||||
ohai "Looking for bottles..."
|
||||
|
||||
sized_formulae = compute_sized_formulae(formulae, args: args)
|
||||
sizes = compute_total_sizes(sized_formulae, debug: args.debug?)
|
||||
|
||||
puts "#{::Utils.pluralize("Formula", sized_formulae.count, plural: "e")} \
|
||||
(#{sized_formulae.count}): #{sized_formulae.join(", ")}\n\n"
|
||||
puts "Download Size: #{disk_usage_readable(sizes[:download])}"
|
||||
puts "Install Size: #{disk_usage_readable(sizes[:installed])}"
|
||||
puts "Net Install Size: #{disk_usage_readable(sizes[:net])}" if sizes[:net] != 0
|
||||
|
||||
ask_input
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def perform_preinstall_checks(all_fatal: false)
|
||||
@ -363,6 +379,86 @@ module Homebrew
|
||||
|
||||
Upgrade.install_formula(formula_installer, upgrade:)
|
||||
end
|
||||
|
||||
def ask_input
|
||||
ohai "Do you want to proceed with the installation? [Y/y/yes/N/n]"
|
||||
accepted_inputs = %w[y yes]
|
||||
declined_inputs = %w[n no]
|
||||
loop do
|
||||
result = $stdin.gets
|
||||
return unless result
|
||||
|
||||
result = result.chomp.strip.downcase
|
||||
if accepted_inputs.include?(result)
|
||||
break
|
||||
elsif declined_inputs.include?(result)
|
||||
exit 1
|
||||
else
|
||||
puts "Invalid input. Please enter 'Y', 'y', or 'yes' to proceed, or 'N' to abort."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Build a unique list of formulae to size by including:
|
||||
# 1. The original formulae to install.
|
||||
# 2. Their outdated dependents (subject to pruning criteria).
|
||||
# 3. Optionally, any installed formula that depends on one of these and is outdated.
|
||||
def compute_sized_formulae(formulae, args:)
|
||||
sized_formulae = formulae.flat_map do |formula|
|
||||
# Always include the formula itself.
|
||||
formula_list = [formula]
|
||||
|
||||
deps = args.build_from_source? ? formula.deps.build : formula.deps.required
|
||||
|
||||
outdated_dependents = deps.map(&:to_formula).reject(&:pinned?).select do |dep|
|
||||
dep.installed_kegs.empty? || (dep.bottled? && dep.outdated?)
|
||||
end
|
||||
deps.map(&:to_formula).each do |f|
|
||||
outdated_dependents.concat(f.recursive_dependencies.map(&:to_formula).reject(&:pinned?).select do |dep|
|
||||
dep.installed_kegs.empty? || (dep.bottled? && dep.outdated?)
|
||||
end)
|
||||
end
|
||||
formula_list.concat(outdated_dependents)
|
||||
|
||||
formula_list
|
||||
end
|
||||
|
||||
# Add any installed formula that depends on one of the sized formulae and is outdated.
|
||||
unless Homebrew::EnvConfig.no_installed_dependents_check?
|
||||
sized_formulae.concat(Formula.installed.select do |installed_formula|
|
||||
installed_formula.bottled? && installed_formula.outdated? &&
|
||||
installed_formula.deps.required.map(&:to_formula).intersect?(sized_formulae)
|
||||
end)
|
||||
end
|
||||
|
||||
sized_formulae.uniq(&:to_s).compact
|
||||
end
|
||||
|
||||
# Compute the total sizes (download, installed, and net) for the given formulae.
|
||||
def compute_total_sizes(sized_formulae, debug: false)
|
||||
total_download_size = 0
|
||||
total_installed_size = 0
|
||||
total_net_size = 0
|
||||
|
||||
sized_formulae.select(&:bottle).each do |formula|
|
||||
bottle = formula.bottle
|
||||
# Fetch additional bottle metadata (if necessary).
|
||||
bottle.fetch_tab(quiet: !debug)
|
||||
|
||||
total_download_size += bottle.bottle_size.to_i if bottle.bottle_size
|
||||
total_installed_size += bottle.installed_size.to_i if bottle.installed_size
|
||||
|
||||
# Sum disk usage for all installed kegs of the formula.
|
||||
next if formula.installed_kegs.none?
|
||||
|
||||
kegs_dep_size = formula.installed_kegs.sum { |keg| keg.disk_usage.to_i }
|
||||
total_net_size += bottle.installed_size.to_i - kegs_dep_size if bottle.installed_size
|
||||
end
|
||||
|
||||
{ download: total_download_size,
|
||||
installed: total_installed_size,
|
||||
net: total_net_size }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -20,6 +20,9 @@ class Homebrew::Cmd::InstallCmd::Args < Homebrew::CLI::Args
|
||||
sig { returns(T.nilable(String)) }
|
||||
def appdir; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ask?; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def audio_unit_plugindir; end
|
||||
|
||||
|
@ -17,6 +17,9 @@ class Homebrew::Cmd::Reinstall::Args < Homebrew::CLI::Args
|
||||
sig { returns(T.nilable(String)) }
|
||||
def appdir; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ask?; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def audio_unit_plugindir; end
|
||||
|
||||
|
@ -14,6 +14,9 @@ class Homebrew::Cmd::UpgradeCmd::Args < Homebrew::CLI::Args
|
||||
sig { returns(T.nilable(String)) }
|
||||
def appdir; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ask?; end
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
def audio_unit_plugindir; end
|
||||
|
||||
|
@ -28,6 +28,9 @@ module Homebrew::EnvConfig
|
||||
sig { returns(T::Boolean) }
|
||||
def artifact_domain_no_fallback?; end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def ask?; end
|
||||
|
||||
sig { returns(T.nilable(::String)) }
|
||||
def auto_update_secs; end
|
||||
|
||||
|
@ -4,6 +4,7 @@ require "cmd/install"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe Homebrew::Cmd::InstallCmd do
|
||||
include FileUtils
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "installs formulae", :integration_test do
|
||||
@ -84,4 +85,44 @@ RSpec.describe Homebrew::Cmd::InstallCmd do
|
||||
expect(HOMEBREW_CELLAR/"testball1/0.1/bin/test.dSYM/Contents/Resources/DWARF/test").to be_a_file if OS.mac?
|
||||
expect(HOMEBREW_CACHE/"Sources/testball1").to be_a_directory
|
||||
end
|
||||
|
||||
it "installs with asking for user prompts without installed dependent checks", :integration_test do
|
||||
setup_test_formula "testball1"
|
||||
|
||||
expect do
|
||||
brew "install", "--ask", "testball1"
|
||||
end.to output(/.*Formula\s*\(1\):\s*testball1.*/).to_stdout.and not_to_output.to_stderr
|
||||
|
||||
expect(HOMEBREW_CELLAR/"testball1/0.1/bin/test").to be_a_file
|
||||
end
|
||||
|
||||
it "installs with asking for user prompts with installed dependent checks", :integration_test do
|
||||
setup_test_formula "testball1", <<~RUBY
|
||||
depends_on "testball5"
|
||||
# should work as its not building but test doesnt pass if dependant
|
||||
# depends_on "build" => :build
|
||||
depends_on "installed"
|
||||
RUBY
|
||||
setup_test_formula "installed"
|
||||
setup_test_formula "testball5", <<~RUBY
|
||||
depends_on "testball4"
|
||||
RUBY
|
||||
setup_test_formula "testball4", ""
|
||||
setup_test_formula "hiop"
|
||||
setup_test_formula "build"
|
||||
|
||||
# Mock `Formula#any_version_installed?` by creating the tab in a plausible keg directory
|
||||
keg_dir = HOMEBREW_CELLAR/"installed"/"1.0"
|
||||
keg_dir.mkpath
|
||||
touch keg_dir/AbstractTab::FILENAME
|
||||
|
||||
expect do
|
||||
brew "install", "--ask", "testball1"
|
||||
end.to output(/.*Formulae\s*\(3\):\s*testball1\s*,?\s*testball5\s*,?\s*testball4.*/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
|
||||
expect(HOMEBREW_CELLAR/"testball1/0.1/bin/test").to be_a_file
|
||||
expect(HOMEBREW_CELLAR/"testball4/0.1/bin/testball4").to be_a_file
|
||||
expect(HOMEBREW_CELLAR/"testball5/0.1/bin/testball5").to be_a_file
|
||||
end
|
||||
end
|
||||
|
@ -20,4 +20,18 @@ RSpec.describe Homebrew::Cmd::Reinstall do
|
||||
|
||||
expect(foo_dir).to exist
|
||||
end
|
||||
|
||||
it "reinstalls a Formula with ask input", :integration_test do
|
||||
install_test_formula "testball"
|
||||
foo_dir = HOMEBREW_CELLAR/"testball/0.1/bin"
|
||||
expect(foo_dir).to exist
|
||||
FileUtils.rm_r(foo_dir)
|
||||
|
||||
expect { brew "reinstall", "--ask", "testball" }
|
||||
.to output(/.*Formula\s*\(1\):\s*testball.*/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
|
||||
expect(foo_dir).to exist
|
||||
end
|
||||
end
|
||||
|
@ -4,6 +4,7 @@ require "cmd/shared_examples/args_parse"
|
||||
require "cmd/upgrade"
|
||||
|
||||
RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
include FileUtils
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "upgrades a Formula and cleans up old versions", :integration_test do
|
||||
@ -15,4 +16,68 @@ RSpec.describe Homebrew::Cmd::UpgradeCmd do
|
||||
expect(HOMEBREW_CELLAR/"testball/0.1").to be_a_directory
|
||||
expect(HOMEBREW_CELLAR/"testball/0.0.1").not_to exist
|
||||
end
|
||||
|
||||
it "upgrades with asking for user prompts", :integration_test do
|
||||
setup_test_formula "testball"
|
||||
(HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath
|
||||
|
||||
expect do
|
||||
brew "upgrade", "--ask"
|
||||
end.to output(/.*Formula\s*\(1\):\s*testball.*/).to_stdout.and not_to_output.to_stderr
|
||||
|
||||
expect(HOMEBREW_CELLAR/"testball/0.1").to be_a_directory
|
||||
expect(HOMEBREW_CELLAR/"testball/0.0.1").not_to exist
|
||||
end
|
||||
|
||||
it "upgrades with asking for user prompts with dependants checks", :integration_test do
|
||||
setup_test_formula "testball", <<~RUBY
|
||||
depends_on "testball5"
|
||||
# should work as its not building but test doesnt pass if dependant
|
||||
# depends_on "build" => :build
|
||||
depends_on "installed"
|
||||
RUBY
|
||||
setup_test_formula "installed"
|
||||
setup_test_formula "testball5", <<~RUBY
|
||||
depends_on "testball4"
|
||||
RUBY
|
||||
setup_test_formula "testball4"
|
||||
setup_test_formula "hiop"
|
||||
setup_test_formula "build"
|
||||
|
||||
(HOMEBREW_CELLAR/"testball/0.0.1/foo").mkpath
|
||||
(HOMEBREW_CELLAR/"testball5/0.0.1/foo").mkpath
|
||||
(HOMEBREW_CELLAR/"testball4/0.0.1/foo").mkpath
|
||||
|
||||
keg_dir = HOMEBREW_CELLAR/"installed"/"1.0"
|
||||
keg_dir.mkpath
|
||||
touch keg_dir/AbstractTab::FILENAME
|
||||
|
||||
regex = /
|
||||
Formulae\s*\(3\):\s*
|
||||
(
|
||||
testball|testball5|testball4
|
||||
)
|
||||
\s*,\s*
|
||||
(?!\1)
|
||||
(
|
||||
testball|testball5|testball4
|
||||
)
|
||||
\s*,\s*
|
||||
(?!\1|\2)
|
||||
(
|
||||
testball|testball5|testball4
|
||||
)
|
||||
/x
|
||||
expect do
|
||||
brew "upgrade", "--ask"
|
||||
end.to output(regex)
|
||||
.to_stdout.and not_to_output.to_stderr
|
||||
|
||||
expect(HOMEBREW_CELLAR/"testball/0.1").to be_a_directory
|
||||
expect(HOMEBREW_CELLAR/"testball/0.0.1").not_to exist
|
||||
expect(HOMEBREW_CELLAR/"testball5/0.1").to be_a_directory
|
||||
expect(HOMEBREW_CELLAR/"testball5/0.0.1").not_to exist
|
||||
expect(HOMEBREW_CELLAR/"testball4/0.1").to be_a_directory
|
||||
expect(HOMEBREW_CELLAR/"testball4/0.0.1").not_to exist
|
||||
end
|
||||
end
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -134,13 +134,21 @@ RSpec.shared_context "integration test" do # rubocop:disable RSpec/ContextWordin
|
||||
bottle_block: nil, tab_attributes: nil)
|
||||
case name
|
||||
when /^testball/
|
||||
# Use a different tarball for testball2 to avoid lock errors when writing concurrency tests
|
||||
prefix = (name == "testball2") ? "testball2" : "testball"
|
||||
tarball = if OS.linux?
|
||||
TEST_FIXTURE_DIR/"tarballs/#{prefix}-0.1-linux.tbz"
|
||||
case name
|
||||
when "testball4", "testball5"
|
||||
prefix = name
|
||||
program_name = name
|
||||
when "testball2"
|
||||
prefix = name
|
||||
program_name = "test"
|
||||
else
|
||||
TEST_FIXTURE_DIR/"tarballs/#{prefix}-0.1.tbz"
|
||||
prefix = "testball"
|
||||
program_name = "test"
|
||||
end
|
||||
|
||||
tarball_name = "#{prefix}-0.1#{"-linux" if OS.linux?}.tbz"
|
||||
tarball = TEST_FIXTURE_DIR / "tarballs/#{tarball_name}"
|
||||
|
||||
content = <<~RUBY
|
||||
desc "Some test"
|
||||
homepage "https://brew.sh/#{name}"
|
||||
@ -150,12 +158,12 @@ RSpec.shared_context "integration test" do # rubocop:disable RSpec/ContextWordin
|
||||
option "with-foo", "Build with foo"
|
||||
#{bottle_block}
|
||||
def install
|
||||
(prefix/"foo"/"test").write("test") if build.with? "foo"
|
||||
(prefix/"foo"/"#{program_name}").write("#{program_name}") if build.with? "foo"
|
||||
prefix.install Dir["*"]
|
||||
(buildpath/"test.c").write \
|
||||
"#include <stdio.h>\\nint main(){printf(\\"test\\");return 0;}"
|
||||
(buildpath/"#{program_name}.c").write \
|
||||
"#include <stdio.h>\\nint main(){printf(\\"#{program_name}\\");return 0;}"
|
||||
bin.mkpath
|
||||
system ENV.cc, "test.c", "-o", bin/"test"
|
||||
system ENV.cc, "#{program_name}.c", "-o", bin/"#{program_name}"
|
||||
end
|
||||
|
||||
#{content}
|
||||
|
Loading…
x
Reference in New Issue
Block a user