mirror of
https://github.com/chromebrew/chromebrew.git
synced 2026-01-08 23:18:10 -05:00
crew: Rubyize file conflict algorithm (#11734)
* crew: Rubyize conflict algorithm Signed-off-by: SupeChicken666 <supechicken666@gmail.com> * Update code comments Signed-off-by: SupeChicken666 <supechicken666@gmail.com> * const: Bump version Signed-off-by: SupeChicken666 <supechicken666@gmail.com> * Also check for essential files on removal Signed-off-by: SupeChicken666 <supechicken666@gmail.com> * Fix syntax Signed-off-by: SupeChicken666 <supechicken666@gmail.com> * Fix unit test Signed-off-by: SupeChicken666 <supechicken666@gmail.com> --------- Signed-off-by: SupeChicken666 <supechicken666@gmail.com>
This commit is contained in:
40
bin/crew
40
bin/crew
@@ -766,29 +766,6 @@ def compress_doc(dir)
|
||||
end
|
||||
end
|
||||
|
||||
def determine_conflicts(dir, pkg)
|
||||
conflicts = []
|
||||
if File.file?("#{dir}/filelist")
|
||||
if File.file?(File.join(CREW_META_PATH, "#{pkg}.filelist"))
|
||||
puts 'Checking for conflicts with files from installed packages...'.orange
|
||||
conflictscmd = `grep --exclude=#{File.join(CREW_META_PATH, "#{pkg}.filelist")} --exclude=#{CREW_META_PATH}/\\\*_build.filelist -Fxf #{dir}/filelist #{CREW_META_PATH}/*.filelist`
|
||||
conflicts = conflictscmd.gsub(/(\.filelist|#{CREW_META_PATH})/, '').split("\n")
|
||||
conflicts.reject!(&:empty?)
|
||||
end
|
||||
elsif File.file?(File.join(CREW_META_PATH, "#{pkg}.filelist"))
|
||||
puts "Checking for conflicts of #{pkg} with files from installed packages...".orange
|
||||
conflictscmd = `grep --exclude=#{File.join(CREW_META_PATH, "#{pkg}.filelist")} --exclude=#{CREW_META_PATH}/\\\*_build.filelist -Fxf #{File.join(CREW_META_PATH, "#{pkg}.filelist")} #{CREW_META_PATH}/*.filelist`
|
||||
conflicts = conflictscmd.gsub(/(\.filelist|#{CREW_META_PATH})/, '').split("\n")
|
||||
conflicts.reject!(&:empty?)
|
||||
end
|
||||
if conflicts.any?
|
||||
puts 'There is a conflict with the same file in another package:'.orange
|
||||
puts conflicts.to_s.orange
|
||||
end
|
||||
conflicts.map! { |x| x.to_s.partition(':').last }
|
||||
return conflicts
|
||||
end
|
||||
|
||||
def prepare_package(destdir)
|
||||
# Create the destdir if it does not exist to avoid having to have
|
||||
# this single line in no_compile_needed packages.
|
||||
@@ -858,15 +835,24 @@ def prepare_package(destdir)
|
||||
end
|
||||
|
||||
# check for conflicts with other installed files
|
||||
conflicts = determine_conflicts(Dir.pwd, @pkg.name)
|
||||
conflicts = ConvenienceFunctions.determine_conflicts(@pkg.name, File.join(Dir.pwd, 'filelist'), '_build', verbose: CREW_VERBOSE)
|
||||
|
||||
if conflicts.any?
|
||||
if CREW_CONFLICTS_ONLY_ADVISORY || @pkg.conflicts_ok?
|
||||
puts 'Warning: There is a conflict with the same file in another package.'.orange
|
||||
puts "Warning: There is a conflict with the same file in another package:\n".orange
|
||||
else
|
||||
puts 'Error: There is a conflict with the same file in another package.'.lightred
|
||||
puts "Error: There is a conflict with the same file in another package:\n".lightred
|
||||
errors = true
|
||||
end
|
||||
puts conflicts
|
||||
|
||||
conflicts.each_pair do |pkgName, conflictFiles|
|
||||
if errors
|
||||
conflictFiles.each {|file| puts "#{pkgName}: #{file}".lightred }
|
||||
else
|
||||
conflictFiles.each {|file| puts "#{pkgName}: #{file}".orange }
|
||||
end
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
# abort if errors encountered
|
||||
|
||||
@@ -50,38 +50,38 @@ class Command
|
||||
# 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.
|
||||
# Remove all files installed by the package in CREW_PREFIX and HOME.
|
||||
#
|
||||
# Exceptions:
|
||||
# 1. The file exists in another installed package.
|
||||
# 2. The file is in one of the filelists for packages CREW_ESSENTIAL_PACKAGES.
|
||||
flist = File.join(CREW_META_PATH, "#{pkg.name}.filelist")
|
||||
if File.file?(flist)
|
||||
filelist_path = File.join(CREW_META_PATH, "#{pkg.name}.filelist")
|
||||
if File.file?(filelist_path)
|
||||
filelist = File.readlines(filelist_path, chomp: true)
|
||||
overlap_files = ConvenienceFunctions.determine_conflicts(pkg.name, filelist_path, verbose: verbose)
|
||||
essential_files = CREW_ESSENTIAL_PACKAGES.flat_map {|f| File.readlines(File.join(CREW_META_PATH, "#{f}.filelist"), chomp: true)}
|
||||
overlap_essential_files = filelist & essential_files
|
||||
files_to_remove = filelist - overlap_files.values.flatten - overlap_essential_files
|
||||
|
||||
# When searching for files to delete we exclude the files from CREW_ESSENTIAL_PACKAGES.
|
||||
essential_packages_exclude_froms = ''
|
||||
unless force
|
||||
essential_packages_exclude_froms = CREW_ESSENTIAL_PACKAGES.map { |i| File.file?("#{File.join(CREW_META_PATH, i.to_s)}.filelist") ? "--exclude-from=#{File.join(CREW_META_PATH, i.to_s)}.filelist" : '' }.join(' ')
|
||||
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
|
||||
|
||||
package_files = `grep -h #{essential_packages_exclude_froms} \"^#{CREW_PREFIX}\\|^#{HOME}\" #{flist}`.split("\n").uniq.sort
|
||||
all_other_files = `grep -h --exclude #{pkg.name}.filelist \"^#{CREW_PREFIX}\\|^#{HOME}\" #{CREW_META_PATH}/*.filelist 2>/dev/null`.split("\n").uniq.sort
|
||||
|
||||
# We want the difference of these arrays.
|
||||
unique_to_package_files = package_files - all_other_files
|
||||
|
||||
# We want the intersection of these arrays.
|
||||
package_files_that_overlap = all_other_files & package_files
|
||||
|
||||
unless package_files_that_overlap.empty?
|
||||
puts "The following file(s) in other packages will not be deleted during the removal of #{pkg.name}:".orange
|
||||
puts package_files_that_overlap.join("\n").orange
|
||||
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 |pkgName, files|
|
||||
files.each {|file| puts "#{pkgName}: #{file}".orange }
|
||||
end
|
||||
puts
|
||||
end
|
||||
unique_to_package_files.each do |file|
|
||||
|
||||
files_to_remove.each do |file|
|
||||
puts "Removing file #{file}".yellow if verbose
|
||||
FileUtils.remove_file file, exception: false
|
||||
end
|
||||
FileUtils.remove_file flist
|
||||
|
||||
FileUtils.remove_file filelist_path
|
||||
end
|
||||
|
||||
# Remove all directories installed by the package.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
require 'etc'
|
||||
|
||||
OLD_CREW_VERSION ||= defined?(CREW_VERSION) ? CREW_VERSION : '1.0'
|
||||
CREW_VERSION ||= '1.58.2' unless defined?(CREW_VERSION) && CREW_VERSION == OLD_CREW_VERSION
|
||||
CREW_VERSION ||= '1.58.3' unless defined?(CREW_VERSION) && CREW_VERSION == OLD_CREW_VERSION
|
||||
|
||||
# Kernel architecture.
|
||||
KERN_ARCH ||= Etc.uname[:machine]
|
||||
|
||||
@@ -7,6 +7,26 @@ require_relative 'crewlog'
|
||||
require_relative 'downloader'
|
||||
|
||||
class ConvenienceFunctions
|
||||
def self.determine_conflicts(pkgName, filelist = File.join(CREW_META_PATH, "#{pkgName}.filelist"), excludeSuffix = nil, verbose: false)
|
||||
conflicts = {}
|
||||
target_filelist = File.readlines(filelist, chomp: true)
|
||||
|
||||
puts 'Checking for conflicts with files from installed packages...'.orange if verbose
|
||||
|
||||
Dir[File.join(CREW_META_PATH, "*.filelist")].each do |filelist|
|
||||
filelist_name = File.basename(filelist, ".filelist")
|
||||
|
||||
# skip filelist belongs to the same package/explicitly excluded
|
||||
next if pkgName == filelist_name || (excludeSuffix && filelist_name.end_with?(excludeSuffix))
|
||||
|
||||
# find out identical file paths with intersection
|
||||
conflict = (target_filelist & File.readlines(filelist, chomp: true)).reject(&:empty?)
|
||||
conflicts[filelist_name] = conflict if conflict.any?
|
||||
end
|
||||
|
||||
return conflicts
|
||||
end
|
||||
|
||||
def self.load_symbolized_json
|
||||
return JSON.load_file(File.join(CREW_CONFIG_PATH, 'device.json'), symbolize_names: true).transform_values! { |val| val.is_a?(String) ? val.to_sym : val }
|
||||
end
|
||||
|
||||
@@ -55,6 +55,7 @@ class RemoveCommandTest < Minitest::Test
|
||||
puts 'Testing the verbose removal of normal package xxd_standalone. This should succeed.'
|
||||
|
||||
expected_output = <<~EOT
|
||||
Checking for conflicts with files from installed packages...
|
||||
Removing file #{CREW_PREFIX}/bin/xxd
|
||||
Removing file #{CREW_PREFIX}/share/man/man1/xxd.1.zst
|
||||
Removing package xxd_standalone from device.json
|
||||
|
||||
Reference in New Issue
Block a user