diff --git a/lib/image_optim/bin_resolver.rb b/lib/image_optim/bin_resolver.rb index 57a09738..d64f21c9 100644 --- a/lib/image_optim/bin_resolver.rb +++ b/lib/image_optim/bin_resolver.rb @@ -3,7 +3,20 @@ class ImageOptim class BinNotFoundError < StandardError; end + class BadBinVersion < StandardError; end + class BinResolver + class Bin + attr_reader :name, :version + def initialize(name, version) + @name, @version = name, version + end + + def to_s + "#{@name} #{@version || '-'}" + end + end + attr_reader :dir def initialize @bins = {} @@ -12,10 +25,16 @@ def initialize def resolve!(name) name = name.to_sym + resolving(name) do - @bins[name] = resolve?(name) + @bins[name] = resolve?(name) && Bin.new(name, version(name)) + end + + if @bins[name] + check!(@bins[name]) + else + raise BinNotFoundError, "`#{name}` not found" end - @bins[name] or raise BinNotFoundError, "`#{name}` not found" end VENDOR_PATH = File.expand_path('../../../vendor', __FILE__) @@ -52,6 +71,34 @@ def accessible?(name) capture_output("which #{name.to_s.shellescape}") != '' end + def version(name) + case name.to_sym + when :advpng, :gifsicle, :jpegoptim, :optipng + capture_output("#{name} --version")[/\d+(\.\d+){1,}/] + when :svgo + capture_output("#{name} --version 2>&1")[/\d+(\.\d+){1,}/] + when :jhead + capture_output("#{name} -V")[/\d+(\.\d+){1,}/] + when :jpegtran + capture_output("#{name} -v - 2>&1")[/version (\d+\S*)/, 1] + when :pngcrush + capture_output("#{name} -version 2>&1")[/\d+(\.\d+){1,}/] + when :pngout + date_str = capture_output("#{name} 2>&1")[/[A-Z][a-z]{2} (?: |\d)\d \d{4}/] + Date.parse(date_str).strftime('%Y%m%d') + end + end + + def check!(bin) + case bin.name + when :pngcrush + case bin.version + when '1.7.60'..'1.7.65' + raise BadBinVersion, "`#{bin}` is known to produce broken pngs" + end + end + end + def capture_output(command) `env PATH=#{env_path.shellescape} #{command}` end diff --git a/spec/image_optim/bin_resolver_spec.rb b/spec/image_optim/bin_resolver_spec.rb index 97973f80..001fcef3 100644 --- a/spec/image_optim/bin_resolver_spec.rb +++ b/spec/image_optim/bin_resolver_spec.rb @@ -106,4 +106,20 @@ def with_env(key, value) end.each(&:join) end end + + it "should raise on detection of problematic version" do + with_env 'PNGCRUSH_BIN', nil do + resolver = ImageOptim::BinResolver.new + resolver.should_receive(:accessible?).with(:pngcrush).once.and_return(true) + resolver.should_receive(:version).with(:pngcrush).once.and_return('1.7.60') + FSPath.should_not_receive(:temp_dir) + + 5.times do + expect do + resolver.resolve!(:pngcrush) + end.to raise_error ImageOptim::BadBinVersion + end + resolver.env_path.should == "#{ENV['PATH']}:#{ImageOptim::BinResolver::VENDOR_PATH}" + end + end end