lib/kpm/uninstaller.rb in kpm-0.7.2 vs lib/kpm/uninstaller.rb in kpm-0.8.0

- old
+ new

@@ -1,31 +1,59 @@ +# frozen_string_literal: true + +require 'pathname' + module KPM class Uninstaller def initialize(destination, logger = nil) @logger = logger if @logger.nil? @logger = Logger.new(STDOUT) @logger.level = Logger::INFO end - destination ||= KPM::BaseInstaller::DEFAULT_BUNDLES_DIR - @installed_plugins = Inspector.new.inspect(destination) + @destination = (destination || KPM::BaseInstaller::DEFAULT_BUNDLES_DIR) + refresh_installed_plugins - plugins_installation_path = File.join(destination, 'plugins') + plugins_installation_path = File.join(@destination, 'plugins') @plugins_manager = PluginsManager.new(plugins_installation_path, @logger) - sha1_file_path = File.join(destination, KPM::BaseInstaller::SHA1_FILENAME) + sha1_file_path = File.join(@destination, KPM::BaseInstaller::SHA1_FILENAME) @sha1checker = KPM::Sha1Checker.from_file(sha1_file_path, @logger) end - def uninstall_plugin(plugin, force = false) + def uninstall_plugin(plugin, force = false, version = nil) plugin_info = find_plugin(plugin) raise "No plugin with key/name '#{plugin}' found installed. Try running 'kpm inspect' for more info" unless plugin_info - remove_all_plugin_versions(plugin_info, force) + versions = version.nil? ? plugin_info[:versions].map { |artifact| artifact[:version] } : [version] + remove_plugin_versions(plugin_info, force, versions) end + def uninstall_non_default_plugins(dry_run = false) + plugins = categorize_plugins + + if plugins[:to_be_deleted].empty? + KPM.ui.say 'Nothing to do' + return false + end + + if dry_run + msg = "The following plugin versions would be removed:\n" + msg += plugins[:to_be_deleted].map { |p| " #{p[0][:plugin_name]}: #{p[1]}" }.join("\n") + msg += "\nThe following plugin versions would be kept:\n" + msg += plugins[:to_keep].map { |p| " #{p[0][:plugin_name]}: #{p[1]}" }.join("\n") + KPM.ui.say msg + false + else + plugins[:to_be_deleted].each do |p| + remove_plugin_version(p[0], p[1]) + end + true + end + end + private def find_plugin(plugin) plugin_info = @installed_plugins[plugin] if plugin_info.nil? @@ -38,32 +66,71 @@ end plugin_info end - def remove_all_plugin_versions(plugin_info, force = false) - versions = plugin_info[:versions].map { |artifact| artifact[:version] } + def categorize_plugins + plugins = { to_be_deleted: [], to_keep: [] } + @installed_plugins.each do |_, info| + info[:versions].each do |artifact| + (artifact[:is_default] ? plugins[:to_keep] : plugins[:to_be_deleted]) << [info, artifact[:version]] + end + end + plugins + end + + def remove_plugin_versions(plugin_info, force = false, versions = []) KPM.ui.say "Removing the following versions of the #{plugin_info[:plugin_name]} plugin: #{versions.join(', ')}" if !force && versions.length > 1 - return false unless 'y' == KPM.ui.ask('Are you sure you want to continue?', limited_to: %w(y n)) + return false unless KPM.ui.ask('Are you sure you want to continue?', limited_to: %w[y n]) == 'y' end - FileUtils.rmtree(plugin_info[:plugin_path]) - - @plugins_manager.remove_plugin_identifier_key(plugin_info[:plugin_key]) versions.each do |version| - remove_sha1_entry(plugin_info, version) + remove_plugin_version(plugin_info, version) end - true end + def remove_plugin_version(plugin_info, version) + # Be safe + raise ArgumentError, 'plugin_path is empty' if plugin_info[:plugin_path].empty? + raise ArgumentError, "version is empty (plugin_path=#{plugin_info[:plugin_path]})" if version.empty? + + plugin_version_path = File.expand_path(File.join(plugin_info[:plugin_path], version)) + safe_rmrf(plugin_version_path) + + remove_sha1_entry(plugin_info, version) + + # Remove the identifier if this was the last version installed + refresh_installed_plugins + if @installed_plugins[plugin_info[:plugin_name]][:versions].empty? + safe_rmrf(plugin_info[:plugin_path]) + @plugins_manager.remove_plugin_identifier_key(plugin_info[:plugin_key]) + end + + refresh_installed_plugins + end + def remove_sha1_entry(plugin_info, version) coordinates = KPM::Coordinates.build_coordinates(group_id: plugin_info[:group_id], artifact_id: plugin_info[:artifact_id], packaging: plugin_info[:packaging], classifier: plugin_info[:classifier], version: version) @sha1checker.remove_entry!(coordinates) + end + + def refresh_installed_plugins + @installed_plugins = Inspector.new.inspect(@destination) + end + + def safe_rmrf(dir) + validate_dir_for_rmrf(dir) + FileUtils.rmtree(dir) + end + + def validate_dir_for_rmrf(dir) + raise ArgumentError, "Path #{dir} is not a valid directory" unless File.directory?(dir) + raise ArgumentError, "Path #{dir} is not a subdirectory of #{@destination}" unless Pathname.new(dir).fnmatch?(File.join(@destination, '**')) end end end