Files
chromebrew/commands/remove.rb
SupeChicken666 659a0db126 crew; Include file size in file list (#12515)
* crew; Include file size in file list

Signed-off-by: SupeChicken666 <supechicken666@gmail.com>

* Bump version

Signed-off-by: SupeChicken666 <supechicken666@gmail.com>

* Use `.grep` instead of `.reject`

Signed-off-by: SupeChicken666 <supechicken666@gmail.com>

* Bump version

Signed-off-by: SupeChicken666 <supechicken666@gmail.com>

---------

Signed-off-by: SupeChicken666 <supechicken666@gmail.com>
Co-authored-by: Satadru Pramanik, DO, MPH, MEng <satadru@gmail.com>
2025-08-17 19:41:03 +00:00

134 lines
5.7 KiB
Ruby

require 'fileutils'
require 'json'
require_relative '../lib/const'
require_relative '../lib/convenience_functions'
require_relative '../lib/package'
require_relative '../lib/package_utils'
class Command
def self.remove(pkg, verbose: false, force: false, only_remove_files: false)
device_json = JSON.load_file(File.join(CREW_CONFIG_PATH, 'device.json'))
# Make sure the package is actually installed before we attempt to remove it.
unless PackageUtils.installed?(pkg.name)
puts "Package #{pkg.name} isn't installed.".lightred
return
end
# Do not remove any packages in CREW_ESSENTIAL_PACKAGES, as those are needed for ruby and thus crew to run.
# CREW_ESSENTIAL_PACKAGES is nil if overriding package upgrade list...
return if CREW_ESSENTIAL_PACKAGES.nil?
if CREW_ESSENTIAL_PACKAGES.include?(pkg.name) && !force
# Exit with failure if attempt to remove an essential package
# is made.
abort <<~ESSENTIAL_PACKAGE_WARNING_EOF.gsub(/^(?=\w)/, ' ').chomp.lightred
#{pkg.name.capitalize} is considered an essential package needed for
Chromebrew to function and thus cannot be removed.
ESSENTIAL_PACKAGE_WARNING_EOF
end
# Check whether the removal breaks dependency of other installed packages
unless force
pkgs_that_need_it = []
device_json['installed_packages'].each do |installed_pkg_info|
pkg_file = File.join(CREW_PACKAGES_PATH, "#{installed_pkg_info['name']}.rb")
installed_pkg = Package.load_package(pkg_file)
pkgs_that_need_it << installed_pkg.name if installed_pkg.dependencies.key?(pkg.name)
end
if pkgs_that_need_it.any?
abort <<~EOT.chomp.lightred
#{pkg.name.capitalize} is required by the following installed packages:
#{pkgs_that_need_it.join("\n ")}
Use `crew remove --force` if you meant to remove it.
EOT
end
end
# Perform any operations required prior to package removal.
pkg.preremove unless only_remove_files
# Use gem to first try to remove gems...
if pkg.name.start_with?('ruby_')
@ruby_gem_name = pkg.name.sub('ruby_', '').sub('_', '-')
if Kernel.system "gem list -i \"^#{@ruby_gem_name}\$\"", %i[out err] => File::NULL
puts "Uninstalling #{@ruby_gem_name} before removing gem files. It's ok if this fails.".orange
system "gem uninstall -aIx --abort-on-dependent #{@ruby_gem_name}", exception: false
end
end
# Use pip to first try to remove python packages if package uses pip.
if pkg.name.start_with?('py3_')
pkg_file = Dir["{#{CREW_LOCAL_REPO_ROOT}/packages,#{CREW_PACKAGES_PATH}}/#{pkg.name}.rb"].max { |a, b| File.mtime(a) <=> File.mtime(b) }
system "python3 -s -m pip uninstall #{pkg.name.gsub('py3_', '')} -y", exception: false if Kernel.system "grep -q \"^require 'buildsystems/pip\" #{pkg_file}"
end
# Remove the files and directories installed by the package.
unless pkg.is_fake?
Dir.chdir CREW_CONFIG_PATH do
# Remove all files installed by the package in CREW_PREFIX and HOME.
#
# Exceptions:
# 1. The file exists in another installed package.
filelist_path = File.join(CREW_META_PATH, "#{pkg.name}.filelist")
if File.file?(filelist_path)
filelist = File.readlines(filelist_path, chomp: true).grep(/^(?!#)/)
overlap_files = ConvenienceFunctions.determine_conflicts(pkg.name, filelist_path, verbose: verbose)
# essential_files should only contain filelists for packages already installed.
essential_files = CREW_ESSENTIAL_PACKAGES.flat_map { |f| File.readlines(File.join(CREW_META_PATH, "#{f}.filelist"), chomp: true) if File.file?(File.join(CREW_META_PATH, "#{f}.filelist")) }
overlap_essential_files = filelist & essential_files
files_to_remove = filelist - overlap_files.values.flatten - overlap_essential_files
if overlap_essential_files.any?
warn "The following file(s) will not be deleted as they are required for Chromebrew to work properly:\n".orange
warn overlap_essential_files.join("\n").orange
warn "\n\n"
end
if overlap_files.any?
warn "The following file(s) in other packages will not be deleted during the removal of #{pkg.name}:\n".orange
overlap_files.each_pair do |pkg_name, files|
files.each { |file| puts "#{pkg_name}: #{file}".orange }
end
puts
end
files_to_remove.each do |file|
puts "Removing file #{file}".yellow if verbose
FileUtils.remove_file file, exception: false
end
FileUtils.remove_file filelist_path
end
# Remove all directories installed by the package.
dlist = File.join(CREW_META_PATH, "#{pkg.name}.directorylist")
if File.file?(dlist)
File.foreach(dlist, chomp: true) do |line|
next unless Dir.exist?(line) && Dir.empty?(line) && line.include?(CREW_PREFIX)
puts "Removing directory #{line}".yellow if verbose
FileUtils.remove_dir line, exception: false
end
FileUtils.remove_file dlist
end
end
end
# Remove the package from the list of installed packages in device.json.
puts "Removing package #{pkg.name} from device.json".yellow if verbose
device_json['installed_packages'].delete_if { |entry| entry['name'] == pkg.name }
# Update device.json with our changes.
ConvenienceFunctions.save_json(device_json)
# Perform any operations required after package removal.
pkg.postremove unless only_remove_files
puts "#{pkg.name} removed!".lightgreen
end
end