diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index 0b3f623432..93f81b2d89 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -1,5 +1,6 @@ require "utils/bottles" require "formula" +require "hbc/cask_loader" module CleanupRefinement refine Pathname do @@ -67,22 +68,50 @@ end using CleanupRefinement module Homebrew - module Cleanup - @disk_cleanup_size = 0 + class Cleanup + extend Predicable - class << self - attr_reader :disk_cleanup_size + attr_predicate :dry_run?, :scrub? + attr_reader :args, :days, :cache + attr_reader :disk_cleanup_size + + def initialize(*args, dry_run: false, scrub: false, days: nil, cache: HOMEBREW_CACHE) + @disk_cleanup_size = 0 + @args = args + @dry_run = dry_run + @scrub = scrub + @days = days + @cache = cache end - module_function + def clean! + if args.empty? + Formula.installed.each do |formula| + cleanup_formula(formula) + end + cleanup_cache + cleanup_logs + return if dry_run? + cleanup_lockfiles + rm_ds_store + else + args.each do |arg| + formula = begin + Formula[arg] + rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError + nil + end - def cleanup - cleanup_cellar - cleanup_cache - cleanup_logs - return if ARGV.dry_run? - cleanup_lockfiles - rm_ds_store + cask = begin + Hbc::CaskLoader.load(arg) + rescue Hbc::CaskUnavailableError + nil + end + + cleanup_formula(formula) if formula + cleanup_cask(cask) if cask + end + end end def update_disk_cleanup_size(path_size) @@ -93,14 +122,12 @@ module Homebrew @unremovable_kegs ||= [] end - def cleanup_cellar(formulae = Formula.installed) - formulae.each(&method(:cleanup_formula)) - end - def cleanup_formula(formula) formula.eligible_kegs_for_cleanup.each(&method(:cleanup_keg)) end + def cleanup_cask(cask); end + def cleanup_keg(keg) cleanup_path(keg) { keg.uninstall } rescue Errno::EACCES => e @@ -113,17 +140,17 @@ module Homebrew def cleanup_logs return unless HOMEBREW_LOGS.directory? HOMEBREW_LOGS.subdirs.each do |dir| - cleanup_path(dir) { dir.rmtree } if dir.prune?(ARGV.value("prune")&.to_i || DEFAULT_LOG_DAYS) + cleanup_path(dir) { dir.rmtree } if dir.prune?(days || DEFAULT_LOG_DAYS) end end - def cleanup_cache(cache = HOMEBREW_CACHE) + def cleanup_cache return unless cache.directory? cache.children.each do |path| next cleanup_path(path) { path.unlink } if path.incomplete? next cleanup_path(path) { FileUtils.rm_rf path } if path.nested_cache? - if path.prune?(ARGV.value("prune")&.to_i) + if path.prune?(days) if path.file? cleanup_path(path) { path.unlink } elsif path.directory? && path.to_s.include?("--") @@ -139,7 +166,7 @@ module Homebrew def cleanup_path(path) disk_usage = path.disk_usage - if ARGV.dry_run? + if dry_run? puts "Would remove: #{path} (#{path.abv})" else puts "Removing: #{path}... (#{path.abv})" diff --git a/Library/Homebrew/cmd/cleanup.rb b/Library/Homebrew/cmd/cleanup.rb index 2fba06e20f..7707de6316 100644 --- a/Library/Homebrew/cmd/cleanup.rb +++ b/Library/Homebrew/cmd/cleanup.rb @@ -12,34 +12,36 @@ #: deleted. If you want to delete those too: `rm -rf $(brew --cache)` require "cleanup" +require "cli_parser" module Homebrew module_function def cleanup - if ARGV.named.empty? - Cleanup.cleanup - else - Cleanup.cleanup_cellar(ARGV.resolved_formulae) + CLI::Parser.parse do + switch "-n", "--dry-run" + switch "-s" + flag "--prune=" end - report_disk_usage unless Cleanup.disk_cleanup_size.zero? - report_unremovable_kegs unless Cleanup.unremovable_kegs.empty? - end + cleanup = Cleanup.new(*args.remaining, dry_run: args.dry_run?, scrub: args.s?, days: args.prune&.to_i) - def report_disk_usage - disk_space = disk_usage_readable(Cleanup.disk_cleanup_size) - if ARGV.dry_run? - ohai "This operation would free approximately #{disk_space} of disk space." - else - ohai "This operation has freed approximately #{disk_space} of disk space." + cleanup.clean! + + unless cleanup.disk_cleanup_size.zero? + disk_space = disk_usage_readable(cleanup.disk_cleanup_size) + if args.dry_run? + ohai "This operation would free approximately #{disk_space} of disk space." + else + ohai "This operation has freed approximately #{disk_space} of disk space." + end end - end - def report_unremovable_kegs + return if cleanup.unremovable_kegs.empty? + ofail <<~EOS Could not cleanup old kegs! Fix your permissions on: - #{Cleanup.unremovable_kegs.join "\n "} + #{cleanup.unremovable_kegs.join "\n "} EOS end end diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index 0d7fba56fe..3ab40b4e1a 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -147,10 +147,6 @@ module Homebrew return unless legacy_cache.writable_real? FileUtils.touch migration_attempted_file - # Cleanup to avoid copying files unnecessarily - ohai "Cleaning up #{legacy_cache}..." - Cleanup.cleanup_cache legacy_cache - # This directory could have been compromised if it's world-writable/ # a symlink/owned by another user so don't copy files in those cases. world_writable = legacy_cache.stat.mode & 0777 == 0777 diff --git a/Library/Homebrew/cmd/upgrade.rb b/Library/Homebrew/cmd/upgrade.rb index 1a7ba28981..ada3ca5e54 100644 --- a/Library/Homebrew/cmd/upgrade.rb +++ b/Library/Homebrew/cmd/upgrade.rb @@ -98,7 +98,7 @@ module Homebrew upgrade_formula(f) next if !ARGV.include?("--cleanup") && !ENV["HOMEBREW_UPGRADE_CLEANUP"] next unless f.installed? - Homebrew::Cleanup.cleanup_formula f + Cleanup.new.cleanup_formula(f) rescue UnsatisfiedRequirements => e Homebrew.failed = true onoe "#{f}: #{e}" diff --git a/Library/Homebrew/test/cleanup_spec.rb b/Library/Homebrew/test/cleanup_spec.rb index 7eabdb82c7..a04fec2641 100644 --- a/Library/Homebrew/test/cleanup_spec.rb +++ b/Library/Homebrew/test/cleanup_spec.rb @@ -43,16 +43,14 @@ describe Homebrew::Cleanup do describe "::cleanup" do it "removes .DS_Store and lock files" do - described_class.cleanup + subject.clean! expect(ds_store).not_to exist expect(lock_file).not_to exist end - it "doesn't remove anything if `--dry-run` is specified" do - ARGV << "--dry-run" - - described_class.cleanup + it "doesn't remove anything if `dry_run` is true" do + described_class.new(dry_run: true).clean! expect(ds_store).to exist expect(lock_file).to exist @@ -61,7 +59,7 @@ describe Homebrew::Cleanup do it "doesn't remove the lock file if it is locked" do lock_file.open(File::RDWR | File::CREAT).flock(File::LOCK_EX | File::LOCK_NB) - described_class.cleanup + subject.clean! expect(lock_file).to exist end @@ -69,10 +67,8 @@ describe Homebrew::Cleanup do context "when it can't remove a keg" do let(:f1) { Class.new(Testball) { version "0.1" }.new } let(:f2) { Class.new(Testball) { version "0.2" }.new } - let(:unremovable_kegs) { [] } before do - described_class.instance_variable_set(:@unremovable_kegs, []) [f1, f2].each do |f| f.brew do f.install @@ -87,13 +83,13 @@ describe Homebrew::Cleanup do end it "doesn't remove any kegs" do - described_class.cleanup_formula f2 + subject.cleanup_formula f2 expect(f1.installed_kegs.size).to eq(2) end it "lists the unremovable kegs" do - described_class.cleanup_formula f2 - expect(described_class.unremovable_kegs).to contain_exactly(f1.installed_kegs[0]) + subject.cleanup_formula f2 + expect(subject.unremovable_kegs).to contain_exactly(f1.installed_kegs[0]) end end end @@ -131,7 +127,7 @@ describe Homebrew::Cleanup do expect(f3).to be_installed expect(f4).to be_installed - described_class.cleanup_formula f3 + subject.cleanup_formula f3 expect(f1).not_to be_installed expect(f2).not_to be_installed @@ -146,21 +142,20 @@ describe Homebrew::Cleanup do path.mkpath end - it "cleans all logs if prune all" do - ARGV << "--prune=all" - described_class.cleanup_logs + it "cleans all logs if prune is 0" do + described_class.new(days: 0).cleanup_logs expect(path).not_to exist end it "cleans up logs if older than 14 days" do allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - 15 * 60 * 60 * 24) - described_class.cleanup_logs + subject.cleanup_logs expect(path).not_to exist end it "does not clean up logs less than 14 days old" do allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - 2 * 60 * 60 * 24) - described_class.cleanup_logs + subject.cleanup_logs expect(path).to exist end end @@ -170,7 +165,7 @@ describe Homebrew::Cleanup do incomplete = (HOMEBREW_CACHE/"something.incomplete") incomplete.mkpath - described_class.cleanup_cache + subject.cleanup_cache expect(incomplete).not_to exist end @@ -179,7 +174,7 @@ describe Homebrew::Cleanup do glide_home = (HOMEBREW_CACHE/"glide_home") glide_home.mkpath - described_class.cleanup_cache + subject.cleanup_cache expect(glide_home).not_to exist end @@ -188,7 +183,7 @@ describe Homebrew::Cleanup do java_cache = (HOMEBREW_CACHE/"java_cache") java_cache.mkpath - described_class.cleanup_cache + subject.cleanup_cache expect(java_cache).not_to exist end @@ -197,7 +192,7 @@ describe Homebrew::Cleanup do npm_cache = (HOMEBREW_CACHE/"npm_cache") npm_cache.mkpath - described_class.cleanup_cache + subject.cleanup_cache expect(npm_cache).not_to exist end @@ -211,9 +206,7 @@ describe Homebrew::Cleanup do gist.mkpath FileUtils.touch svn - allow(ARGV).to receive(:value).with("prune").and_return("all") - - described_class.cleanup_cache + described_class.new(days: 0).cleanup_cache expect(git).not_to exist expect(gist).to exist @@ -223,9 +216,8 @@ describe Homebrew::Cleanup do it "does not clean up directories that are not VCS checkouts" do git = (HOMEBREW_CACHE/"git") git.mkpath - allow(ARGV).to receive(:value).with("prune").and_return("all") - described_class.cleanup_cache + described_class.new(days: 0).cleanup_cache expect(git).to exist end @@ -233,17 +225,15 @@ describe Homebrew::Cleanup do it "cleans up VCS checkout directories with modified time < prune time" do foo = (HOMEBREW_CACHE/"--foo") foo.mkpath - allow(ARGV).to receive(:value).with("prune").and_return("1") allow_any_instance_of(Pathname).to receive(:mtime).and_return(Time.now - 2 * 60 * 60 * 24) - described_class.cleanup_cache + described_class.new(days: 1).cleanup_cache expect(foo).not_to exist end it "does not clean up VCS checkout directories with modified time >= prune time" do foo = (HOMEBREW_CACHE/"--foo") foo.mkpath - allow(ARGV).to receive(:value).with("prune").and_return("1") - described_class.cleanup_cache + described_class.new(days: 1).cleanup_cache expect(foo).to exist end @@ -260,20 +250,19 @@ describe Homebrew::Cleanup do it "cleans up file if outdated" do allow(Utils::Bottles).to receive(:file_outdated?).with(any_args).and_return(true) - described_class.cleanup_cache + subject.cleanup_cache expect(bottle).not_to exist expect(testball).not_to exist end - it "cleans up file if ARGV has -s and formula not installed" do - ARGV << "-s" - described_class.cleanup_cache + it "cleans up file if `scrub` is true and formula not installed" do + described_class.new(scrub: true).cleanup_cache expect(bottle).not_to exist expect(testball).not_to exist end it "cleans up file if stale" do - described_class.cleanup_cache + subject.cleanup_cache expect(bottle).not_to exist expect(testball).not_to exist end