rodiff 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +79 -6
- data/exe/rodiff +2 -17
- data/lib/rodiff/cli.rb +50 -68
- data/lib/rodiff/configuration.rb +179 -0
- data/lib/rodiff/error.rb +5 -0
- data/lib/rodiff/executable.rb +99 -0
- data/lib/rodiff/helpers/file_finder.rb +39 -0
- data/lib/rodiff/odiff.rb +1 -1
- data/lib/rodiff/version.rb +1 -1
- data/lib/rodiff.rb +3 -3
- metadata +30 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 864417eae37667c26264ea7de3365d12511c4069d89e83c859641de7c00c22cc
|
|
4
|
+
data.tar.gz: 5cc69618a1b4893e10d428a387e85f813b26278496f2601abc5315113726e3a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f77b00f02f5f934de75c153c82c8000641a8e1f6e8edebab6f56c0f42af3a34179ca3796a49f82506747934d6f1cc1d34fed2e027290febad530ca92e5630321
|
|
7
|
+
data.tar.gz: 3551ea66e08e3c8f65bd7ed32fc47f5a3b0681ebd08646edc313284dc9260e04c855a7a9c2cb7d3c7fc1e878f56d40c8fe1b95c06a51a9076a52296bec778357
|
data/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# rodiff
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[![Version][rubygems_badge]][rubygems]
|
|
4
|
+
[![CI][ci_badge]][ci_workflows]
|
|
5
|
+
[![Coverage][coverage_badge]][coverage]
|
|
6
|
+
[![Maintainability][maintainability_badge]][maintainability]
|
|
4
7
|
|
|
8
|
+
A ruby image comparison tool powered by [Odiff](https://github.com/dmtrKovalenko/odiff) in OCamel.
|
|
5
9
|
|
|
6
10
|
## Motivation
|
|
7
11
|
|
|
8
|
-
A strong candidate against the veteran players on the internet like [pixelmatch](https://github.com/mapbox/pixelmatch) and [ImageMagick](https://github.com/ImageMagick/ImageMagick)
|
|
9
|
-
|
|
10
12
|
Impressive [benchmarks](https://github.com/dmtrKovalenko/odiff#benchmarks) from `Odiff`.
|
|
11
13
|
|
|
14
|
+
A strong candidate against the veteran players like [pixelmatch](https://github.com/mapbox/pixelmatch) and [ImageMagick](https://github.com/ImageMagick/ImageMagick)
|
|
12
15
|
|
|
13
16
|
## Getting Started
|
|
14
17
|
|
|
@@ -27,7 +30,7 @@ This gem wraps the [standalone executable](https://github.com/dmtrKovalenko/odif
|
|
|
27
30
|
Supported platforms are:
|
|
28
31
|
- arm64-darwin (macos-arm64)
|
|
29
32
|
- x64-mingw32 (windows-x64)
|
|
30
|
-
- x64-mingw-
|
|
33
|
+
- x64-mingw-ucrt (windows-x64)
|
|
31
34
|
- x86_64-darwin (macos-x64)
|
|
32
35
|
- x86_64-linux (linux-x64)
|
|
33
36
|
|
|
@@ -35,7 +38,7 @@ Supported platforms are:
|
|
|
35
38
|
|
|
36
39
|
If you are not able to use the vendored standalone executables, a local installation of the `Odiff` executable can be configured by setting an environment variable named `ODIFF_INSTALL_DIR` to the directory path containing the executable.
|
|
37
40
|
|
|
38
|
-
For example, if you've installed `odiff`
|
|
41
|
+
For example, if you've installed the [`odiff-bin`](https://github.com/dmtrKovalenko/odiff#cross-platform) npm package and had the binaries downloaded at `/path/to/node_modules/bin/odiff`, then you should set your environment variable like so:
|
|
39
42
|
|
|
40
43
|
``` sh
|
|
41
44
|
ODIFF_INSTALL_DIR=/path/to/node_modules/bin
|
|
@@ -47,6 +50,63 @@ or, for relative paths like `./node_modules/.bin/odiff`:
|
|
|
47
50
|
ODIFF_INSTALL_DIR=node_modules/.bin
|
|
48
51
|
```
|
|
49
52
|
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Rodiff automatically discovers and loads configuration from `.rodiff.yml` files in your project.
|
|
56
|
+
|
|
57
|
+
### Configuration File Discovery
|
|
58
|
+
|
|
59
|
+
Rodiff searches for `.rodiff.yml` starting from the current directory and traversing upward to your home directory:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
/home/username/projects/myapp/src/tests/.rodiff.yml ← checks here first
|
|
63
|
+
/home/username/projects/myapp/src/.rodiff.yml
|
|
64
|
+
/home/username/projects/myapp/.rodiff.yml ← typically found here
|
|
65
|
+
/home/username/projects/.rodiff.yml
|
|
66
|
+
/home/username/.rodiff.yml ← stops here (home directory)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Configuration Options
|
|
70
|
+
|
|
71
|
+
Create a `.rodiff.yml` file in your project root:
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
# File patterns for image comparison (supports glob patterns)
|
|
75
|
+
include_pattern: "screenshots/**/*.png"
|
|
76
|
+
exclude_pattern: "screenshots/archived/**"
|
|
77
|
+
|
|
78
|
+
# Comparison settings
|
|
79
|
+
color_threshold: 0.1 # 0.0 - 1.0 (lower = stricter)
|
|
80
|
+
ignore_antialiasing: false # Ignore antialiasing differences
|
|
81
|
+
output_diff_mask: false # Output black/white mask instead of diff image
|
|
82
|
+
|
|
83
|
+
# Error handling
|
|
84
|
+
fail_if_no_comparison: false # Exit with error if no images found
|
|
85
|
+
exit_code_error: 1 # Exit code for errors (not image differences)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Programmatic Configuration
|
|
89
|
+
|
|
90
|
+
You can also configure Rodiff programmatically:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
Rodiff.configure do |config|
|
|
94
|
+
config.include_pattern = "**/*.png"
|
|
95
|
+
config.color_threshold = 0.15
|
|
96
|
+
config.ignore_antialiasing = true
|
|
97
|
+
end
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or override specific values:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
config = Rodiff::Configuration.new
|
|
104
|
+
config.overrides(
|
|
105
|
+
color_threshold: 0.2,
|
|
106
|
+
output_diff_mask: true
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
50
110
|
## Development
|
|
51
111
|
|
|
52
112
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
@@ -93,4 +153,17 @@ See https://bundler.io/man/bundle-config.1.html for more information.
|
|
|
93
153
|
## License
|
|
94
154
|
|
|
95
155
|
Rodiff is released under the [MIT License](https://opensource.org/licenses/MIT).
|
|
96
|
-
Odiff is released under the [MIT License](https://opensource.org/licenses/MIT).
|
|
156
|
+
Odiff is released under the [MIT License](https://opensource.org/licenses/MIT).
|
|
157
|
+
|
|
158
|
+
## Contributing
|
|
159
|
+
|
|
160
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/ryancyq/rodiff](https://github.com/ryancyq/rodiff).
|
|
161
|
+
|
|
162
|
+
[rubygems_badge]: https://img.shields.io/gem/v/rodiff.svg
|
|
163
|
+
[rubygems]: https://rubygems.org/gems/rodiff
|
|
164
|
+
[ci_badge]: https://github.com/ryancyq/rodiff/actions/workflows/ci.yml/badge.svg
|
|
165
|
+
[ci_workflows]: https://github.com/ryancyq/rodiff/actions/workflows/ci.yml
|
|
166
|
+
[coverage_badge]: https://codecov.io/gh/ryancyq/rodiff/graph/badge.svg?token=SYR7FSDWT5
|
|
167
|
+
[coverage]: https://codecov.io/gh/ryancyq/rodiff
|
|
168
|
+
[maintainability_badge]: https://api.codeclimate.com/v1/badges/d5b1002a1a7162f86a7a/maintainability
|
|
169
|
+
[maintainability]: https://codeclimate.com/github/ryancyq/rodiff/maintainability
|
data/exe/rodiff
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
5
4
|
|
|
6
5
|
require "rodiff/cli"
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
command = [Rodiff::CLI.executable, *ARGV]
|
|
11
|
-
puts "rodiff: #{command.inspect}" if verbose
|
|
12
|
-
|
|
13
|
-
if Gem.win_platform?
|
|
14
|
-
# use system rather than exec as exec inexplicably fails to find the executable on Windows
|
|
15
|
-
# see related https://github.com/rubys/sprockets-esbuild/pull/4
|
|
16
|
-
system(*command, exception: true)
|
|
17
|
-
else
|
|
18
|
-
exec(*command)
|
|
19
|
-
end
|
|
20
|
-
rescue Rodiff::CLI::CommandError => e
|
|
21
|
-
warn("ERROR: #{e.message}")
|
|
22
|
-
exit 1
|
|
23
|
-
end
|
|
7
|
+
# set invoked_via_subcommand to ensure default command is used
|
|
8
|
+
Rodiff::CLI.start(ARGV, invoked_via_subcommand: true)
|
data/lib/rodiff/cli.rb
CHANGED
|
@@ -1,87 +1,69 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "thor"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "shellwords"
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class CommandError < StandardError; end
|
|
7
|
+
require "rodiff/configuration"
|
|
8
|
+
require "rodiff/error"
|
|
9
|
+
require "rodiff/version"
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
module Rodiff
|
|
12
|
+
class CLI < Thor
|
|
13
|
+
package_name "rodiff"
|
|
14
|
+
|
|
15
|
+
default_command :compare
|
|
16
|
+
|
|
17
|
+
desc "version", "Print version"
|
|
18
|
+
map ["-v", "--version"] => :version
|
|
19
|
+
method_option :odiff, type: :boolean, required: false
|
|
20
|
+
def version
|
|
21
|
+
if options.odiff?
|
|
22
|
+
odiff_exec("--version")
|
|
23
|
+
else
|
|
24
|
+
say Rodiff::VERSION
|
|
19
25
|
end
|
|
20
26
|
end
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
If you're using bundler, please make sure you're on the latest bundler version:
|
|
29
|
-
|
|
30
|
-
gem install bundler
|
|
31
|
-
bundle update --bundler
|
|
28
|
+
desc "compare [BASELINE] [VARIANT] [DIFF]", "Compare VARIANT against BASELINE, output to DIFF"
|
|
29
|
+
method_option :verbose, type: :boolean, default: false
|
|
30
|
+
def compare(baseline = nil, variant = nil, diff = nil)
|
|
31
|
+
all_present, all_absent = args_presence(baseline, variant, diff)
|
|
32
|
+
raise ArgumentError, "BASELINE, VARIANT, DIFF must be provided" unless all_present ^ all_absent
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
bundle install
|
|
37
|
-
|
|
38
|
-
See `bundle lock --help` output for details.
|
|
39
|
-
|
|
40
|
-
If you're still seeing this message after taking those steps, try running
|
|
41
|
-
`bundle config` and ensure `force_ruby_platform` isn't set to `true`. See
|
|
42
|
-
https://github.com/ryancyq/rodiff#check-bundle_force_ruby_platform
|
|
43
|
-
for more details.
|
|
44
|
-
MSG
|
|
45
|
-
)
|
|
46
|
-
end
|
|
34
|
+
odiff_exec(baseline, variant, diff)
|
|
35
|
+
rescue Rodiff::Error => e
|
|
36
|
+
raise Thor::Error, "ERROR: #{e.message}"
|
|
47
37
|
end
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
super("ODIFF_INSTALL_DIR is set to #{install_dir}, but that directory does not exist.")
|
|
52
|
-
end
|
|
39
|
+
def self.exit_on_failure?
|
|
40
|
+
true
|
|
53
41
|
end
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
def platform
|
|
57
|
-
%i[cpu os].map { |m| Gem::Platform.local.public_send(m) }.join("-")
|
|
58
|
-
end
|
|
43
|
+
private
|
|
59
44
|
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
def config
|
|
46
|
+
Rodiff.configuration
|
|
47
|
+
end
|
|
62
48
|
|
|
63
|
-
|
|
49
|
+
def args_presence(*args)
|
|
50
|
+
args.each_with_object([true, true]) do |e, acc|
|
|
51
|
+
acc[0] = false if e.nil? || e == ""
|
|
52
|
+
acc[1] = false unless e.nil? || e == ""
|
|
64
53
|
end
|
|
54
|
+
end
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
exe_file = Dir.glob(exe_files_of_platforms).find do |f|
|
|
78
|
-
supported_platform?(File.basename(File.dirname(f)))
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
raise ExecutableNotFound.new(platform, exe_path) if exe_file.nil? || !File.exist?(exe_file)
|
|
83
|
-
|
|
84
|
-
exe_file
|
|
56
|
+
def odiff_exec(*cmd)
|
|
57
|
+
cmd_parts = []
|
|
58
|
+
cmd_parts << config.odiff_exe_path
|
|
59
|
+
cmd_parts.push(*cmd)
|
|
60
|
+
|
|
61
|
+
stdout, stderr, status = Open3.capture3(*cmd_parts)
|
|
62
|
+
if block_given?
|
|
63
|
+
yield stdout, stderr, status
|
|
64
|
+
else
|
|
65
|
+
say_error stderr unless stderr.nil? || stderr == ""
|
|
66
|
+
say stdout unless stdout.nil? || stdout == ""
|
|
85
67
|
end
|
|
86
68
|
end
|
|
87
69
|
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "rodiff/error"
|
|
5
|
+
require "rodiff/executable"
|
|
6
|
+
require "rodiff/helpers/file_finder"
|
|
7
|
+
|
|
8
|
+
module Rodiff
|
|
9
|
+
def self.configuration
|
|
10
|
+
@configuration ||= Rodiff::Configuration.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.configure
|
|
14
|
+
yield configuration
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Configuration
|
|
18
|
+
class UnknownConfiguration < Rodiff::Error; end
|
|
19
|
+
|
|
20
|
+
DOTFILE = ".rodiff.yml"
|
|
21
|
+
GEM_ROOT = if RUBY_VERSION >= "3.1"
|
|
22
|
+
File.dirname(__FILE__, 3).freeze
|
|
23
|
+
else
|
|
24
|
+
File.expand_path(File.join(File.dirname(__FILE__), "..", "..")).freeze
|
|
25
|
+
end
|
|
26
|
+
DEFAULT_CONFIG = File.join(GEM_ROOT, "config", "default.yml").freeze
|
|
27
|
+
SEARCH_ROOT = Dir.home.freeze
|
|
28
|
+
|
|
29
|
+
ATTRS = Module.new.tap { |mod| include mod }
|
|
30
|
+
READER_ATTRS = {
|
|
31
|
+
default_dir: "/"
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
ACCESSOR_ATTRS = {
|
|
35
|
+
include_pattern: "{*,**/*}.jpg",
|
|
36
|
+
exclude_pattern: "",
|
|
37
|
+
compare_pattern: "",
|
|
38
|
+
|
|
39
|
+
ignore_antialiasing: false,
|
|
40
|
+
color_threshold: 0.1,
|
|
41
|
+
output_diff_mask: false,
|
|
42
|
+
|
|
43
|
+
exit_code_odiff: ->(code) { code },
|
|
44
|
+
exit_code_error: 1,
|
|
45
|
+
fail_if_no_comparison: false
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
def self.generate_config(mod, path, line, attrs)
|
|
49
|
+
mod.module_eval(Array(attrs).join(";").to_s, path, line)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.config_attribute(name, type:)
|
|
53
|
+
case type
|
|
54
|
+
when :reader, :writer, :accessor then "attr_#{type} :#{name}"
|
|
55
|
+
when :proxy
|
|
56
|
+
<<-RUBY
|
|
57
|
+
def #{name}
|
|
58
|
+
value_for(:#{name}) { super() }
|
|
59
|
+
end
|
|
60
|
+
RUBY
|
|
61
|
+
else
|
|
62
|
+
raise ArgumentError, "unsupported type #{type.inspect}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
generate_config ATTRS, __FILE__, __LINE__, [
|
|
67
|
+
*READER_ATTRS.keys.map { |key| config_attribute(key, type: :reader) },
|
|
68
|
+
*ACCESSOR_ATTRS.keys.map { |key| config_attribute(key, type: :accessor) }
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
generate_config self, __FILE__, __LINE__, [
|
|
72
|
+
*READER_ATTRS.keys.map { |key| config_attribute(key, type: :proxy) },
|
|
73
|
+
*ACCESSOR_ATTRS.keys.map { |key| config_attribute(key, type: :proxy) }
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
def initialize
|
|
77
|
+
@config_overrides = {}
|
|
78
|
+
|
|
79
|
+
READER_ATTRS.each { |key, value| instance_variable_set("@#{key}", value) }
|
|
80
|
+
ACCESSOR_ATTRS.each { |key, value| instance_variable_set("@#{key}", value) }
|
|
81
|
+
|
|
82
|
+
load_from_file(DEFAULT_CONFIG)
|
|
83
|
+
load_from_file(config_file_override) if config_file_override
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def odiff_exe_path
|
|
87
|
+
@odiff_exe_path ||= Rodiff::Executable.resolve
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def odiff_exe_path=(path)
|
|
91
|
+
@odiff_exe_path = Rodiff::Executable.resolve(exe_path: path)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def overrides(opts = {})
|
|
95
|
+
opts.each_key { |key| validate_config_key!(key) }
|
|
96
|
+
@config_overrides.merge!(opts.transform_keys(&:to_sym))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def config_file_override(start_dir: Dir.pwd)
|
|
102
|
+
@config_file_override ||= begin
|
|
103
|
+
finder = Helpers::FileFinder.new("/")
|
|
104
|
+
finder.find_upwards(DOTFILE, start_dir, SEARCH_ROOT)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def load_from_file(path)
|
|
109
|
+
yml_config = YAML.load_file(path) if File.exist?(path)
|
|
110
|
+
|
|
111
|
+
if yml_config.is_a?(Hash)
|
|
112
|
+
yml_config.each do |key, value|
|
|
113
|
+
validate_config_key!(key.to_sym)
|
|
114
|
+
public_send("#{key}=", value) if respond_to?("#{key}=")
|
|
115
|
+
end
|
|
116
|
+
elsif empty_yml?(yml_config)
|
|
117
|
+
warn "Configuration file #{path} is empty"
|
|
118
|
+
else
|
|
119
|
+
warn "Configuration file #{path} must contain a Hash, got #{yml_config.class}"
|
|
120
|
+
end
|
|
121
|
+
rescue Psych::SyntaxError => e
|
|
122
|
+
raise Rodiff::Error, "Invalid YAML in #{path}: #{e.message}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def empty_yml?(data)
|
|
126
|
+
# Ruby 2.7-3.0 with Psych 3.x
|
|
127
|
+
return data == false if RUBY_VERSION < "3.1"
|
|
128
|
+
|
|
129
|
+
# Ruby 3.1+ with Psych 4.x
|
|
130
|
+
data.nil?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def validate_config_key!(key)
|
|
134
|
+
return if READER_ATTRS.key?(key) || ACCESSOR_ATTRS.key?(key)
|
|
135
|
+
|
|
136
|
+
raise UnknownConfiguration, "unknown config #{key.inspect}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def value_for(key, &block)
|
|
140
|
+
validate_config_key!(key)
|
|
141
|
+
@config_overrides.fetch(key, &block)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def files_from_dir(dir)
|
|
145
|
+
included_files = files_from_glob(file_glob_pattern(dir, include_pattern))
|
|
146
|
+
excluded_files = files_from_glob(file_glob_pattern(dir, exclude_pattern))
|
|
147
|
+
(included_files - excluded_files).uniq
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def files_from_glob(file_glob)
|
|
151
|
+
files = Dir.glob(file_glob)
|
|
152
|
+
files.map { |file| File.expand_path(file) }.sort
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def file_glob_pattern(path, pattern)
|
|
156
|
+
trimmed = "{#{pattern.gsub(%r{\s*,\s*}, ",")}}"
|
|
157
|
+
return trimmed if pattern =~ %r{^(\./)?#{Regexp.escape(path)}} || absolute_pattern?(pattern)
|
|
158
|
+
|
|
159
|
+
File.join(path, trimmed)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def absolute_pattern?(pattern)
|
|
163
|
+
return pattern.start_with?(File::Separator) unless windows?
|
|
164
|
+
return false unless File::ALT_SEPARATOR
|
|
165
|
+
|
|
166
|
+
win_absolute_pattern = %r{\A\w+:#{Regexp.escape(File::ALT_SEPARATOR)}}.match?(pattern)
|
|
167
|
+
win_network_pattern = pattern.start_with?(File::ALT_SEPARATOR * 2)
|
|
168
|
+
win_absolute_pattern || win_network_pattern
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def windows?
|
|
172
|
+
@windows ||= begin
|
|
173
|
+
require "rbconfig"
|
|
174
|
+
os = RbConfig::CONFIG["host_os"]
|
|
175
|
+
os.match?(%r{mingw})
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
data/lib/rodiff/error.rb
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rodiff/odiff"
|
|
4
|
+
require "rodiff/error"
|
|
5
|
+
|
|
6
|
+
module Rodiff
|
|
7
|
+
class Executable
|
|
8
|
+
DEFAULT_DIR = File.expand_path(File.join(__dir__, "..", "..", "exe"))
|
|
9
|
+
LOCAL_INSTALL_DIR_ENV = "ODIFF_INSTALL_DIR"
|
|
10
|
+
|
|
11
|
+
class UnsupportedPlatform < Rodiff::Error
|
|
12
|
+
def initialize(platform)
|
|
13
|
+
super(
|
|
14
|
+
<<~MSG
|
|
15
|
+
odiff does not support the #{platform} platform
|
|
16
|
+
Please install odiff following instructions at https://github.com/dmtrKovalenko/odiff#installation
|
|
17
|
+
MSG
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class ExecutableNotFound < Rodiff::Error
|
|
23
|
+
def initialize(platform, exe_path)
|
|
24
|
+
super(
|
|
25
|
+
<<~MSG
|
|
26
|
+
Cannot find the odiff executable for #{platform} in #{exe_path}
|
|
27
|
+
|
|
28
|
+
If you're using bundler, please make sure you're on the latest bundler version:
|
|
29
|
+
|
|
30
|
+
gem install bundler
|
|
31
|
+
bundle update --bundler
|
|
32
|
+
|
|
33
|
+
Then make sure your lock file includes this platform by running:
|
|
34
|
+
|
|
35
|
+
bundle lock --add-platform #{platform}
|
|
36
|
+
bundle install
|
|
37
|
+
|
|
38
|
+
See `bundle lock --help` output for details.
|
|
39
|
+
|
|
40
|
+
If you're still seeing this message after taking those steps, try running
|
|
41
|
+
`bundle config` and ensure `force_ruby_platform` isn't set to `true`. See
|
|
42
|
+
https://github.com/ryancyq/rodiff#check-bundle_force_ruby_platform
|
|
43
|
+
for more details.
|
|
44
|
+
MSG
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class InstallDirectoryNotFound < Rodiff::Error
|
|
50
|
+
def initialize(install_dir)
|
|
51
|
+
super("#{LOCAL_INSTALL_DIR_ENV} is set to #{install_dir}, but that directory does not exist.")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class << self
|
|
56
|
+
def platform
|
|
57
|
+
%i[cpu os].map { |m| Gem::Platform.local.public_send(m) }.join("-")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def supported_platform?(platform = nil)
|
|
61
|
+
return Gem::Platform.match_gem?(Gem::Platform.new(platform), "rodiff") unless platform.nil?
|
|
62
|
+
|
|
63
|
+
Rodiff::Odiff::PLATFORMS.keys.any? { |p| Gem::Platform.match_gem?(Gem::Platform.new(p), "rodiff") }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def resolve(exe_path: DEFAULT_DIR)
|
|
67
|
+
if (odiff_install_dir = ENV.fetch(LOCAL_INSTALL_DIR_ENV, nil))
|
|
68
|
+
exe_path = odiff_install_dir
|
|
69
|
+
exe_file = expand_local_install_dir(odiff_install_dir)
|
|
70
|
+
elsif supported_platform?
|
|
71
|
+
exe_file = expand_bundled_dir(exe_path)
|
|
72
|
+
else
|
|
73
|
+
raise UnsupportedPlatform, platform
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
raise ExecutableNotFound.new(platform, exe_path) if exe_file.nil? || !File.exist?(exe_file)
|
|
77
|
+
|
|
78
|
+
exe_file
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def expand_local_install_dir(local_install_dir)
|
|
84
|
+
raise InstallDirectoryNotFound, local_install_dir unless File.directory?(local_install_dir)
|
|
85
|
+
|
|
86
|
+
warn "NOTE: using #{LOCAL_INSTALL_DIR_ENV} to find odiff executable: #{local_install_dir}"
|
|
87
|
+
exe_files = File.expand_path(File.join(local_install_dir, "odiff{,.exe}"))
|
|
88
|
+
Dir.glob(exe_files).first
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def expand_bundled_dir(bundled_dir)
|
|
92
|
+
exe_files_of_platforms = File.expand_path(File.join(bundled_dir, "*", "odiff"))
|
|
93
|
+
Dir.glob(exe_files_of_platforms).find do |f|
|
|
94
|
+
supported_platform?(File.basename(File.dirname(f)))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rodiff
|
|
4
|
+
module Helpers
|
|
5
|
+
class FileFinder
|
|
6
|
+
attr_reader :root_dir
|
|
7
|
+
|
|
8
|
+
def initialize(root)
|
|
9
|
+
@root_dir = root
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def find_upwards(filename, start_dir, stop_dir = nil)
|
|
13
|
+
traverse_upwards(filename, start_dir, stop_dir) { |file| return file if file } # return first result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find_top_most(filename, start_dir, stop_dir = nil)
|
|
17
|
+
top_most_file = nil
|
|
18
|
+
traverse_upwards(filename, start_dir, stop_dir) { |file| top_most_file = file }
|
|
19
|
+
top_most_file
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def traverse_upwards(filename, start_dir, stop_dir)
|
|
25
|
+
start = Pathname.new(start_dir).expand_path
|
|
26
|
+
root = Pathname.new(root_dir).expand_path
|
|
27
|
+
return unless start.to_s.start_with?(root.to_s)
|
|
28
|
+
|
|
29
|
+
stop = Pathname.new(stop_dir).expand_path if stop_dir
|
|
30
|
+
start.ascend do |dir|
|
|
31
|
+
file = File.join(dir, filename)
|
|
32
|
+
yield(file) if File.exist?(file)
|
|
33
|
+
|
|
34
|
+
break if dir == stop || dir == root
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/rodiff/odiff.rb
CHANGED
data/lib/rodiff/version.rb
CHANGED
data/lib/rodiff.rb
CHANGED
metadata
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rodiff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryan Chang
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
13
|
-
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: thor
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 1.3.2
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 1.5.0
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: 1.3.2
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 1.5.0
|
|
14
32
|
email:
|
|
15
33
|
- ryancyq@gmail.com
|
|
16
34
|
executables:
|
|
@@ -24,6 +42,10 @@ files:
|
|
|
24
42
|
- exe/rodiff
|
|
25
43
|
- lib/rodiff.rb
|
|
26
44
|
- lib/rodiff/cli.rb
|
|
45
|
+
- lib/rodiff/configuration.rb
|
|
46
|
+
- lib/rodiff/error.rb
|
|
47
|
+
- lib/rodiff/executable.rb
|
|
48
|
+
- lib/rodiff/helpers/file_finder.rb
|
|
27
49
|
- lib/rodiff/odiff.rb
|
|
28
50
|
- lib/rodiff/version.rb
|
|
29
51
|
homepage: https://github.com/ryancyq/rodiff
|
|
@@ -34,7 +56,6 @@ metadata:
|
|
|
34
56
|
allowed_push_host: https://rubygems.org
|
|
35
57
|
changelog_uri: https://github.com/ryancyq/rodiff/blob/main/CHANGELOG.md
|
|
36
58
|
homepage_uri: https://github.com/ryancyq/rodiff
|
|
37
|
-
post_install_message:
|
|
38
59
|
rdoc_options: []
|
|
39
60
|
require_paths:
|
|
40
61
|
- lib
|
|
@@ -49,9 +70,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
49
70
|
- !ruby/object:Gem::Version
|
|
50
71
|
version: 3.2.0
|
|
51
72
|
requirements:
|
|
52
|
-
- odiff,
|
|
53
|
-
rubygems_version:
|
|
54
|
-
signing_key:
|
|
73
|
+
- odiff, >= 3.0
|
|
74
|
+
rubygems_version: 4.0.3
|
|
55
75
|
specification_version: 4
|
|
56
|
-
summary: A ruby
|
|
76
|
+
summary: A ruby image comparison tool powered by ODiff in OCamel
|
|
57
77
|
test_files: []
|