mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-04-16 12:25:53 +00:00
200 lines
5 KiB
Ruby
Executable file
200 lines
5 KiB
Ruby
Executable file
#!/usr/bin/env ruby
|
|
|
|
require 'optparse'
|
|
require 'fileutils'
|
|
require 'digest'
|
|
|
|
class OutputValidator
|
|
def initialize(swift_dir:, ruby_dir:, verbose:, ignore_patterns:, show_details:)
|
|
@swift_dir = swift_dir
|
|
@ruby_dir = ruby_dir
|
|
@differences = []
|
|
@missing_in_ruby = []
|
|
@missing_in_swift = []
|
|
@identical_count = 0
|
|
@verbose = verbose
|
|
@ignore_patterns = ignore_patterns
|
|
@show_details = show_details
|
|
end
|
|
|
|
def validate
|
|
puts "Comparing outputs:"
|
|
puts " Swift: #{@swift_dir}"
|
|
puts " Ruby: #{@ruby_dir}"
|
|
puts ""
|
|
|
|
swift_files = find_html_files(@swift_dir)
|
|
ruby_files = find_html_files(@ruby_dir)
|
|
|
|
puts "Found #{swift_files.length} Swift output files"
|
|
puts "Found #{ruby_files.length} Ruby output files"
|
|
puts ""
|
|
|
|
compare_files(swift_files, ruby_files)
|
|
print_summary
|
|
end
|
|
|
|
private
|
|
|
|
def find_html_files(dir)
|
|
Dir.glob(File.join(dir, '**', '*.{html,xml,json,css,js,png,jpg,jpeg,gif,svg,ico,woff,woff2,ttf,eot}'))
|
|
.map { |f| f.sub("#{dir}/", '') }
|
|
.sort
|
|
end
|
|
|
|
def compare_files(swift_files, ruby_files)
|
|
all_files = (swift_files + ruby_files).uniq.sort
|
|
|
|
all_files.each do |relative_path|
|
|
next if ignored?(relative_path)
|
|
|
|
swift_path = File.join(@swift_dir, relative_path)
|
|
ruby_path = File.join(@ruby_dir, relative_path)
|
|
|
|
if !File.exist?(swift_path)
|
|
@missing_in_swift << relative_path
|
|
elsif !File.exist?(ruby_path)
|
|
@missing_in_ruby << relative_path
|
|
else
|
|
compare_file_contents(relative_path, swift_path, ruby_path)
|
|
end
|
|
end
|
|
end
|
|
|
|
def compare_file_contents(relative_path, swift_path, ruby_path)
|
|
if binary_file?(relative_path)
|
|
swift_content = File.binread(swift_path)
|
|
ruby_content = File.binread(ruby_path)
|
|
else
|
|
swift_content = normalize_html(File.read(swift_path))
|
|
ruby_content = normalize_html(File.read(ruby_path))
|
|
end
|
|
|
|
if swift_content == ruby_content
|
|
@identical_count += 1
|
|
puts "✓ #{relative_path}" if @verbose
|
|
else
|
|
@differences << {
|
|
path: relative_path,
|
|
swift_hash: Digest::SHA256.hexdigest(swift_content),
|
|
ruby_hash: Digest::SHA256.hexdigest(ruby_content),
|
|
swift_size: swift_content.length,
|
|
ruby_size: ruby_content.length
|
|
}
|
|
puts "✗ #{relative_path} (differs)"
|
|
end
|
|
end
|
|
|
|
def ignored?(path)
|
|
return false if @ignore_patterns.empty?
|
|
|
|
@ignore_patterns.any? { |pattern| File.fnmatch?(pattern, path) }
|
|
end
|
|
|
|
def binary_file?(path)
|
|
ext = File.extname(path).downcase
|
|
%w[.png .jpg .jpeg .gif .svg .ico .woff .woff2 .ttf .eot].include?(ext)
|
|
end
|
|
|
|
def normalize_html(content)
|
|
content
|
|
.gsub(/\s+/, ' ')
|
|
.gsub(/>\s+</, '><')
|
|
.gsub(/<lastBuildDate>.*?<\/lastBuildDate>/, '')
|
|
.strip
|
|
end
|
|
|
|
def print_summary
|
|
puts ""
|
|
puts "=" * 60
|
|
puts "VALIDATION SUMMARY"
|
|
puts "=" * 60
|
|
puts ""
|
|
puts "Identical files: #{@identical_count}"
|
|
puts "Different files: #{@differences.length}"
|
|
puts "Missing in Ruby: #{@missing_in_ruby.length}"
|
|
puts "Missing in Swift: #{@missing_in_swift.length}"
|
|
puts ""
|
|
|
|
if @differences.any?
|
|
puts "Files with differences:"
|
|
return unless @show_details
|
|
|
|
@differences.each do |diff|
|
|
puts " #{diff[:path]}"
|
|
puts " Swift: #{diff[:swift_size]} bytes (#{diff[:swift_hash][0..7]}...)"
|
|
puts " Ruby: #{diff[:ruby_size]} bytes (#{diff[:ruby_hash][0..7]}...)"
|
|
end
|
|
puts ""
|
|
end
|
|
|
|
if @missing_in_ruby.any?
|
|
puts "Missing in Ruby output:"
|
|
@missing_in_ruby.each { |path| puts " #{path}" }
|
|
puts ""
|
|
end
|
|
|
|
if @missing_in_swift.any?
|
|
puts "Missing in Swift output:"
|
|
@missing_in_swift.each { |path| puts " #{path}" }
|
|
puts ""
|
|
end
|
|
|
|
success = @differences.empty? && @missing_in_ruby.empty? && @missing_in_swift.empty?
|
|
puts success ? "✅ VALIDATION PASSED" : "❌ VALIDATION FAILED"
|
|
puts ""
|
|
|
|
exit(success ? 0 : 1)
|
|
end
|
|
end
|
|
|
|
options = {
|
|
verbose: false,
|
|
ignore_patterns: [],
|
|
show_details: false
|
|
}
|
|
|
|
parser = OptionParser.new do |opts|
|
|
opts.banner = "Usage: validate-output [options] SWIFT_DIR RUBY_DIR"
|
|
|
|
opts.on('-v', '--verbose', 'Print a ✓ line for every identical file') do
|
|
options[:verbose] = true
|
|
end
|
|
|
|
opts.on('-iPATTERN', '--ignore=PATTERN', 'Ignore files matching the glob (may be repeated)') do |pattern|
|
|
options[:ignore_patterns] << pattern
|
|
end
|
|
|
|
opts.on('--details', 'Include byte counts and hashes for differing files') do
|
|
options[:show_details] = true
|
|
end
|
|
end
|
|
|
|
parser.parse!
|
|
|
|
if ARGV.length != 2
|
|
puts parser
|
|
exit 1
|
|
end
|
|
|
|
swift_dir = ARGV[0]
|
|
ruby_dir = ARGV[1]
|
|
|
|
unless Dir.exist?(swift_dir)
|
|
puts "ERROR: Swift directory not found: #{swift_dir}"
|
|
exit 1
|
|
end
|
|
|
|
unless Dir.exist?(ruby_dir)
|
|
puts "ERROR: Ruby directory not found: #{ruby_dir}"
|
|
exit 1
|
|
end
|
|
|
|
validator = OutputValidator.new(
|
|
swift_dir: swift_dir,
|
|
ruby_dir: ruby_dir,
|
|
verbose: options[:verbose],
|
|
ignore_patterns: options[:ignore_patterns],
|
|
show_details: options[:show_details]
|
|
)
|
|
validator.validate
|