#!/usr/bin/env ruby -wU # The purpose of this script is to provide a list of files: # # - in left but not in right # - in right but not in left # - in left and same as in right # - in left but different from right require 'optparse' DEFAULT_PATTERN = ["spec/**/*.*", "app/**/*.*", "lib/**/*.*"] config = { left: './', dir: false, right: './hyrax-webapp', summary: false, patterns_to_check: DEFAULT_PATTERN } command_name = File.basename(__FILE__) consider_replacing_pattern = true nlp = new_line_padding = " " * 37 optparse = OptionParser.new do |options| # This banner is the first line of left help documentation. options.set_banner "Usage: #{command_name} [options]\n\n" \ "#{command_name} - A command line tool for listing directory differences.\n\n" options.on('-r PATH', '--right', String, "The file path that contains the \"on the right\"\n#{nlp}directory.\n#{nlp}Default: #{config[:right]}") do |right| config[:right] = right end options.on('-l PATH', '--left', String, "The file path that contains the \"on the left\"\n#{nlp}directory.\n#{nlp}Default: #{config[:left]}") do |left| config[:left] = left end options.on('-s', '--summary', "Only render a summary.\n#{nlp}Default: #{config[:summary].inspect}") do |summary| config[:summary] = summary end options.on('-d', '--dir-summaries', "Render the directory summaries.\n#{nlp}Default: #{config[:dir].inspect}") do |dir| config[:dir] =dir end options.on('-p STRING', '--pattern', String, %(The pattern(s) to check for variance\n#{nlp}Default:\n#{nlp} #{config[:patterns_to_check].join("\n#{nlp} ")})) do |pattern| if consider_replacing_pattern config[:patterns_to_check] = [] consider_replacing_pattern = false end config[:patterns_to_check] << pattern end options.on_tail('-h', '--help', "You're looking at it!") do warn options exit 1 end end begin optparse.parse! mandatory = %i[left right] missing = mandatory.select { |param| config[param].nil? } raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty? rescue OptionParser::InvalidOption, OptionParser::MissingArgument warn "\e[0m\e[1m\e[31m#{$!}\e[0m\n" warn optparse exit end require 'debug'; binding.break require 'fileutils' in_left_but_not_in_right = [] in_right_but_not_in_left = [] duplicates = [] different = [] left = config.fetch(:left) right = config.fetch(:right) dir_glob_patterns_to_check = config.fetch(:patterns_to_check) summary = config.fetch(:summary) dir_glob_patterns_to_check.each do |pattern| Dir.glob(File.join(left, pattern)).each do |left_path| path = left_path.sub(File.join(left, '/'), '') right_path = File.join(right, path) if File.exist?(right_path) results = `diff #{left_path} #{right_path}`.strip if results.empty? duplicates << path else different << path end else in_left_but_not_in_right << path end end Dir.glob(File.join(right, pattern)).each do |right_path| path = right_path.sub(File.join(right, '/'), '') left_path = File.join(left, path) next if File.exist?(left_path) in_right_but_not_in_left << path end end GitData = Struct.new(:url, :sha, :status, :branch, keyword_init: true) def git_data_for(source) GitData.new( url: `cd #{source}; git remote get-url origin`.strip, sha: `cd #{source}; git log --pretty=format:'%H' -1`.strip, status: `cd #{source}; git status --porcelain`.strip.size.zero? ? "clean" : "dirty", branch: `cd #{source}; git branch --show-current`.strip ) end left_data = git_data_for(left) right_data = git_data_for(right) puts "#"*72 puts "# #{command_name} run context:" puts "#"*72 puts "#" puts "# Working Directory: #{FileUtils.pwd}" puts "#" puts "# Left: #{left}" puts "# URL: #{left_data.url}" puts "# BRANCH: #{left_data.branch}" puts "# SHA: #{left_data.sha}" puts "# STATUS: #{left_data.status}" puts "#" puts "# Right: #{right}" puts "# URL: #{right_data.url}" puts "# BRANCH: #{right_data.branch}" puts "# SHA: #{right_data.sha}" puts "# STATUS: #{right_data.status}" puts "#" puts "# Patterns to Check:" config.fetch(:patterns_to_check).each do |pattern| puts "# * #{pattern}" end puts "#" puts "# = Duplicates: #{duplicates.size}" puts "# < In left but not right: #{in_left_but_not_in_right.size}" if config.fetch(:dir) puts "# < Breakdown:" in_left_but_not_in_right.group_by { |path| path.split("/")[0..1].flatten.join("/") }.each do |path, elements| puts "# < #{path}: #{elements.size}" end puts "#" end puts "# > In right but not left: #{in_right_but_not_in_left.size}" if config.fetch(:dir) puts "# > Breakdown:" in_right_but_not_in_left.group_by { |path| path.split("/")[0..1].flatten.join("/") }.each do |path, elements| puts "# > #{path}: #{elements.size}" end puts "#" end puts "# Δ Different between left and right: #{different.size}" if config.fetch(:dir) puts "# Δ Breakdown:" different.group_by { |path| path.split("/")[0..1].flatten.join("/") }.each do |path, elements| puts "# Δ #{path}: #{elements.size}" end end puts "" unless summary puts "################################################################" puts "# Files in \"left\" that are exact duplicates of \"right\" files" puts "# They are prefixed with a `='" puts "################################################################" duplicates.each do |path| puts "= #{path}" if ENV['RM_DUPS'] File.unlink(path) end end puts "" puts "#################################################" puts "# Files that are in \"left\" but not in \"right\"" puts "# They are prefixed with a `<'" puts "#################################################" in_left_but_not_in_right.each do |path| puts "< #{path}" end puts "" puts "#################################################" puts "# Files that are in \"right\" but not in \"left\"" puts "# They are prefixed with a `>'" puts "#################################################" in_right_but_not_in_left.each do |path| puts "> #{path}" end puts "" puts "####################################################" puts "# Files that are different in \"left\" and \"right\"" puts "# They are prefixed with a `Δ'" puts "####################################################" different.each do |path| puts "Δ #{path}" end end