image_optim 0.31.2 → 0.31.4
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/.github/dependabot.yml +6 -0
- data/.github/workflows/check.yml +50 -20
- data/.github/workflows/rubocop.yml +2 -2
- data/.rubocop.yml +3 -0
- data/CHANGELOG.markdown +8 -0
- data/LICENSE.txt +1 -1
- data/README.markdown +6 -4
- data/image_optim.gemspec +2 -2
- data/lib/image_optim/cache.rb +1 -1
- data/lib/image_optim/config.rb +9 -1
- data/lib/image_optim/runner/option_parser.rb +55 -49
- data/lib/image_optim/runner.rb +1 -1
- data/lib/image_optim/space.rb +1 -1
- data/lib/image_optim/worker/svgo.rb +20 -0
- data/script/update_worker_options_in_readme +16 -3
- data/spec/files/config_with_range.yaml +3 -0
- data/spec/image_optim/config_spec.rb +39 -3
- data/spec/image_optim/runner/option_parser_spec.rb +10 -1
- data/spec/image_optim/worker/svgo_spec.rb +53 -0
- data/spec/image_optim_spec.rb +3 -3
- metadata +9 -5
- data/.github/workflows/codeql.yml +0 -30
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28ea4eea777b5a6eac029c628c28509b70d65f5784ef16466c26b758d189d88b
|
|
4
|
+
data.tar.gz: b4cdec32a6fde2d92b7c3382b14fb90c094e523e46c79fd8fc24d21737348167
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad0b465ae7de080c2af44efaaeea7a7b17b8bc343faa0f8034105b027362abfc952bf72fead0d9c30f368a94cb74a1a7d2e19d49b07c626f49bfb59b99f4de69
|
|
7
|
+
data.tar.gz: 8d7070adae2ff71fe71df2ffaf25addb42acdddca2a427bcfad3f91cd3138d0fccfc2b3284d2cf146dd8f513be9c88a4b9c2f378d6564bcfdf629f5f6ee492cf
|
data/.github/workflows/check.yml
CHANGED
|
@@ -10,47 +10,69 @@ jobs:
|
|
|
10
10
|
strategy:
|
|
11
11
|
matrix:
|
|
12
12
|
ruby:
|
|
13
|
-
- '
|
|
14
|
-
- '2.1'
|
|
15
|
-
- '2.2'
|
|
16
|
-
- '2.3'
|
|
17
|
-
- '2.4'
|
|
18
|
-
- '2.5'
|
|
19
|
-
- '2.6'
|
|
13
|
+
- '1.9.3'
|
|
20
14
|
- '2.7'
|
|
21
15
|
- '3.0'
|
|
22
16
|
- '3.1'
|
|
23
|
-
-
|
|
24
|
-
-
|
|
17
|
+
- '3.2'
|
|
18
|
+
- '3.3'
|
|
25
19
|
- jruby-9.4
|
|
26
20
|
fail-fast: false
|
|
27
21
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@v4
|
|
29
23
|
- uses: ruby/setup-ruby@v1
|
|
30
24
|
with:
|
|
31
25
|
ruby-version: "${{ matrix.ruby }}"
|
|
32
26
|
bundler-cache: true
|
|
33
|
-
- run:
|
|
34
|
-
- run: curl -L "https://github.com/shssoichiro/oxipng/releases/download/v4.0.3/oxipng-4.0.3-x86_64-unknown-linux-musl.tar.gz" | tar -xz -C /usr/local/bin --strip-components 1 --wildcards '*oxipng'
|
|
27
|
+
- run: npm install -g svgo
|
|
35
28
|
- run: bundle exec image_optim --info
|
|
36
29
|
- run: bundle exec rspec
|
|
30
|
+
containers:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
container: ${{ matrix.container }}
|
|
33
|
+
strategy:
|
|
34
|
+
matrix:
|
|
35
|
+
container:
|
|
36
|
+
- debian:buster
|
|
37
|
+
- debian:bullseye
|
|
38
|
+
- debian:bookworm
|
|
39
|
+
# - alpine
|
|
40
|
+
fail-fast: false
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- run: |
|
|
44
|
+
if command -v apt-get &> /dev/null; then
|
|
45
|
+
apt-get update
|
|
46
|
+
apt-get -y install make gcc git curl imagemagick ruby ruby-dev rubygems
|
|
47
|
+
fi
|
|
48
|
+
- run: |
|
|
49
|
+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
|
50
|
+
. ~/.nvm/nvm.sh
|
|
51
|
+
nvm install 20
|
|
52
|
+
echo "PATH=$PATH" >> $GITHUB_ENV
|
|
53
|
+
- run: npm install -g svgo
|
|
54
|
+
- run: gem install bundler || gem install bundler --version '< 2.4'
|
|
55
|
+
- run: bundle install
|
|
56
|
+
- run: bundle exec bin/image_optim --info
|
|
57
|
+
- run: bundle exec rspec
|
|
37
58
|
windows:
|
|
38
59
|
runs-on: windows-latest
|
|
39
60
|
strategy:
|
|
40
61
|
matrix:
|
|
41
62
|
ruby:
|
|
42
|
-
- '2.6'
|
|
43
63
|
- '2.7'
|
|
44
64
|
- '3.0'
|
|
45
65
|
- '3.1'
|
|
66
|
+
- '3.2'
|
|
67
|
+
- '3.3'
|
|
46
68
|
fail-fast: false
|
|
47
69
|
steps:
|
|
48
|
-
- uses: actions/checkout@
|
|
70
|
+
- uses: actions/checkout@v4
|
|
49
71
|
- uses: ruby/setup-ruby@v1
|
|
50
72
|
with:
|
|
51
73
|
ruby-version: "${{ matrix.ruby }}"
|
|
52
74
|
bundler-cache: true
|
|
53
|
-
- uses: actions/cache@
|
|
75
|
+
- uses: actions/cache@v4
|
|
54
76
|
with:
|
|
55
77
|
path: "$HOME/bin"
|
|
56
78
|
key: ${{ runner.os }}
|
|
@@ -65,18 +87,26 @@ jobs:
|
|
|
65
87
|
- run: npm install -g svgo
|
|
66
88
|
- run: bundle exec image_optim --info
|
|
67
89
|
- run: bundle exec rspec
|
|
90
|
+
update_worker_options_in_readme:
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
steps:
|
|
93
|
+
- uses: actions/checkout@v4
|
|
94
|
+
- uses: ruby/setup-ruby@v1
|
|
95
|
+
with:
|
|
96
|
+
ruby-version: '3'
|
|
97
|
+
bundler-cache: true
|
|
98
|
+
- run: script/update_worker_options_in_readme -n
|
|
68
99
|
coverage:
|
|
69
100
|
runs-on: ubuntu-latest
|
|
70
101
|
env:
|
|
71
102
|
CC_TEST_REPORTER_ID: b433c6540d220a2da0663670c9b260806bafdb3a43c6f22b2e81bfb1f87b12fe
|
|
72
103
|
steps:
|
|
73
|
-
- uses: actions/checkout@
|
|
104
|
+
- uses: actions/checkout@v4
|
|
74
105
|
- uses: ruby/setup-ruby@v1
|
|
75
106
|
with:
|
|
76
|
-
ruby-version: '3
|
|
107
|
+
ruby-version: '3'
|
|
77
108
|
bundler-cache: true
|
|
78
|
-
- run:
|
|
79
|
-
-
|
|
80
|
-
- uses: paambaati/codeclimate-action@v2.7.5
|
|
109
|
+
- run: npm install -g svgo
|
|
110
|
+
- uses: paambaati/codeclimate-action@v9
|
|
81
111
|
with:
|
|
82
112
|
coverageCommand: bundle exec rspec
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## unreleased
|
|
4
4
|
|
|
5
|
+
## v0.31.4 (2024-11-19)
|
|
6
|
+
|
|
7
|
+
* Added `--svgo-allow-lossy` and `--svgo-precision` options to use svgo in lossy mode. This sets svgo `--precision`, which can result in substantially smaller svgs (see [#211](https://github.com/toy/image_optim/issues/211) for some experiments). Lower values are more lossy. 3 is the default, but many SVGs will work well even with 0 or 1. Like all worker specific lossy flags, this is also enabled with `--allow-lossy`. [#210](https://github.com/toy/image_optim/issues/210) [#211](https://github.com/toy/image_optim/issues/211) [@gurgeous](https://github.com/gurgeous)
|
|
8
|
+
|
|
9
|
+
## v0.31.3 (2023-02-17)
|
|
10
|
+
|
|
11
|
+
* Support Psych4/Ruby 3.1 changes to use safe_yaml methods by default [#203](https://github.com/toy/image_optim/issues/203) [#204](https://github.com/toy/image_optim/pull/204) [@oscillot](https://github.com/oscillot) [@toy](https://github.com/toy)
|
|
12
|
+
|
|
5
13
|
## v0.31.2 (2022-11-27)
|
|
6
14
|
|
|
7
15
|
* Support jruby 9.4 [@toy](https://github.com/toy)
|
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://rubygems.org/gems/image_optim)
|
|
2
|
-
[](https://github.com/toy/image_optim/actions/workflows/check.yml)
|
|
3
|
+
[](https://github.com/toy/image_optim/actions/workflows/rubocop.yml)
|
|
4
|
+
[](https://github.com/toy/image_optim/actions/workflows/codeql.yml)
|
|
5
5
|
[](https://codeclimate.com/github/toy/image_optim)
|
|
6
6
|
[](https://codeclimate.com/github/toy/image_optim)
|
|
7
7
|
[](https://depfu.com/github/toy/image_optim)
|
|
@@ -375,6 +375,8 @@ Worker has no options
|
|
|
375
375
|
### svgo:
|
|
376
376
|
* `:disable_plugins` — List of plugins to disable *(defaults to `[]`)*
|
|
377
377
|
* `:enable_plugins` — List of plugins to enable *(defaults to `[]`)*
|
|
378
|
+
* `:allow_lossy` — Allow precision option *(defaults to `false`)*
|
|
379
|
+
* `:precision` — Number of digits in the fractional part `0`..`20`, ignored in default/lossless mode *(defaults to `3`)*
|
|
378
380
|
|
|
379
381
|
<!---</worker-options>-->
|
|
380
382
|
|
|
@@ -390,4 +392,4 @@ In separate file [CHANGELOG.markdown](CHANGELOG.markdown).
|
|
|
390
392
|
|
|
391
393
|
## Copyright
|
|
392
394
|
|
|
393
|
-
Copyright (c) 2012-
|
|
395
|
+
Copyright (c) 2012-2024 Ivan Kuchin. See [LICENSE.txt](LICENSE.txt) for details.
|
data/image_optim.gemspec
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
4
|
s.name = 'image_optim'
|
|
5
|
-
s.version = '0.31.
|
|
5
|
+
s.version = '0.31.4'
|
|
6
6
|
s.summary = %q{Command line tool and ruby interface to optimize (lossless compress, optionally lossy) jpeg, png, gif and svg images using external utilities (advpng, gifsicle, jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, oxipng, pngcrush, pngout, pngquant, svgo)}
|
|
7
7
|
s.homepage = "https://github.com/toy/#{s.name}"
|
|
8
8
|
s.authors = ['Ivan Kuchin']
|
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
|
15
15
|
'changelog_uri' => "https://github.com/toy/#{s.name}/blob/master/CHANGELOG.markdown",
|
|
16
16
|
'documentation_uri' => "https://www.rubydoc.info/gems/#{s.name}/#{s.version}",
|
|
17
17
|
'source_code_uri' => "https://github.com/toy/#{s.name}",
|
|
18
|
-
}
|
|
18
|
+
} if s.respond_to?(:metadata=)
|
|
19
19
|
|
|
20
20
|
s.files = `git ls-files`.split("\n")
|
|
21
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/image_optim/cache.rb
CHANGED
data/lib/image_optim/config.rb
CHANGED
|
@@ -33,7 +33,7 @@ class ImageOptim
|
|
|
33
33
|
end
|
|
34
34
|
return {} unless File.size?(full_path)
|
|
35
35
|
|
|
36
|
-
config =
|
|
36
|
+
config = load_yaml_file(full_path)
|
|
37
37
|
unless config.is_a?(Hash)
|
|
38
38
|
fail "expected hash, got #{config.inspect}"
|
|
39
39
|
end
|
|
@@ -43,6 +43,14 @@ class ImageOptim
|
|
|
43
43
|
warn "exception when reading #{full_path}: #{e}"
|
|
44
44
|
{}
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
def load_yaml_file(path)
|
|
48
|
+
if YAML.respond_to?(:safe_load_file)
|
|
49
|
+
YAML.safe_load_file(path, permitted_classes: [Range])
|
|
50
|
+
else
|
|
51
|
+
YAML.load_file(path)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
# Merge config from files with passed options
|
|
@@ -34,8 +34,8 @@ class ImageOptim
|
|
|
34
34
|
def help
|
|
35
35
|
text = super
|
|
36
36
|
|
|
37
|
-
# reserve one column
|
|
38
|
-
columns = terminal_columns - 1
|
|
37
|
+
# reserve one column and limit to 120
|
|
38
|
+
columns = [terminal_columns - 1, 120].min
|
|
39
39
|
# 1 for distance between summary and description
|
|
40
40
|
# 2 for additional indent
|
|
41
41
|
wrapped_indent = summary_indent + (' ' * (summary_width + 1 + 2))
|
|
@@ -43,20 +43,7 @@ class ImageOptim
|
|
|
43
43
|
# don't try to wrap if there is too little space for description
|
|
44
44
|
return text if wrapped_width < 20
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
text.split("\n").each do |line|
|
|
48
|
-
if line.length <= columns
|
|
49
|
-
wrapped << line << "\n"
|
|
50
|
-
else
|
|
51
|
-
indented = line =~ /^\s/
|
|
52
|
-
wrapped << line.slice!(wrap_regex(columns)) << "\n"
|
|
53
|
-
line.scan(wrap_regex(wrapped_width)) do |part|
|
|
54
|
-
wrapped << wrapped_indent if indented
|
|
55
|
-
wrapped << part << "\n"
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
wrapped
|
|
46
|
+
wrapped_text(text, wrapped_width, wrapped_indent, columns)
|
|
60
47
|
end
|
|
61
48
|
|
|
62
49
|
private
|
|
@@ -66,6 +53,27 @@ class ImageOptim
|
|
|
66
53
|
stty_columns ? stty_columns.to_i : `tput cols`.to_i
|
|
67
54
|
end
|
|
68
55
|
|
|
56
|
+
def wrapped_text(text, wrapped_width, wrapped_indent, columns)
|
|
57
|
+
wrapped = []
|
|
58
|
+
text.split("\n").each do |line|
|
|
59
|
+
if line.length <= columns
|
|
60
|
+
wrapped << line << "\n"
|
|
61
|
+
else
|
|
62
|
+
wrapped << line.slice!(wrap_regex(columns)).rstrip << "\n"
|
|
63
|
+
if line =~ /^\s/
|
|
64
|
+
line.scan(wrap_regex(wrapped_width)) do |part|
|
|
65
|
+
wrapped << wrapped_indent << part.rstrip << "\n"
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
line.scan(wrap_regex(columns)) do |part|
|
|
69
|
+
wrapped << part.rstrip << "\n"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
wrapped.join(nil)
|
|
75
|
+
end
|
|
76
|
+
|
|
69
77
|
def wrap_regex(width)
|
|
70
78
|
/.*?.{1,#{width}}(?:\s|\z)/
|
|
71
79
|
end
|
|
@@ -96,30 +104,25 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
|
96
104
|
|
|
|
97
105
|
TEXT
|
|
98
106
|
|
|
99
|
-
op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of '
|
|
100
|
-
'default ones') do |paths|
|
|
107
|
+
op.on('--config-paths PATH1,PATH2', Array, 'Config paths to use instead of default ones') do |paths|
|
|
101
108
|
options[:config_paths] = paths
|
|
102
109
|
end
|
|
103
110
|
|
|
104
111
|
op.separator nil
|
|
105
112
|
|
|
106
|
-
op.on('-r', '-R', '--recursive', 'Recursively scan directories '
|
|
107
|
-
'for images') do |recursive|
|
|
113
|
+
op.on('-r', '-R', '--recursive', 'Recursively scan directories for images') do |recursive|
|
|
108
114
|
options[:recursive] = recursive
|
|
109
115
|
end
|
|
110
116
|
|
|
111
|
-
op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories '
|
|
112
|
-
'(defaults to .*)') do |glob|
|
|
117
|
+
op.on("--exclude-dir 'GLOB'", 'Glob for excluding directories (defaults to .*)') do |glob|
|
|
113
118
|
options[:exclude_dir_glob] = glob
|
|
114
119
|
end
|
|
115
120
|
|
|
116
|
-
op.on("--exclude-file 'GLOB'", 'Glob for excluding files '
|
|
117
|
-
'(defaults to .*)') do |glob|
|
|
121
|
+
op.on("--exclude-file 'GLOB'", 'Glob for excluding files (defaults to .*)') do |glob|
|
|
118
122
|
options[:exclude_file_glob] = glob
|
|
119
123
|
end
|
|
120
124
|
|
|
121
|
-
op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and '
|
|
122
|
-
'files') do |glob|
|
|
125
|
+
op.on("--exclude 'GLOB'", 'Set glob for excluding both directories and files') do |glob|
|
|
123
126
|
options[:exclude_file_glob] = options[:exclude_dir_glob] = glob
|
|
124
127
|
end
|
|
125
128
|
|
|
@@ -129,41 +132,42 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
|
129
132
|
options[:show_progress] = show_progress
|
|
130
133
|
end
|
|
131
134
|
|
|
132
|
-
op.on('--[no-]threads N', Integer, 'Number of threads or disable '
|
|
133
|
-
'(defaults to number of processors)') do |threads|
|
|
135
|
+
op.on('--[no-]threads N', Integer, 'Number of threads or disable (defaults to number of processors)') do |threads|
|
|
134
136
|
options[:threads] = threads
|
|
135
137
|
end
|
|
136
138
|
|
|
137
|
-
op.on(
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
op.on(
|
|
140
|
+
'--[no-]nice N',
|
|
141
|
+
Integer,
|
|
142
|
+
'Nice level, priority of all used tools with higher value meaning lower priority, in range -20..19, negative ' \
|
|
143
|
+
'values can be set only if run by root user (defaults to 10)'
|
|
144
|
+
) do |nice|
|
|
140
145
|
options[:nice] = nice
|
|
141
146
|
end
|
|
142
147
|
|
|
143
|
-
op.on(
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
op.on(
|
|
149
|
+
'--[no-]pack',
|
|
150
|
+
'Require image_optim_pack or disable it, by default image_optim_pack will be used if available, will turn on ' \
|
|
151
|
+
'skip-missing-workers unless explicitly disabled'
|
|
152
|
+
) do |pack|
|
|
146
153
|
options[:pack] = pack
|
|
147
154
|
end
|
|
148
155
|
|
|
149
156
|
op.separator nil
|
|
150
157
|
op.separator ' Caching:'
|
|
151
158
|
|
|
152
|
-
op.on('--cache-dir DIR', 'Cache optimized images '
|
|
153
|
-
'into the specified directory') do |cache_dir|
|
|
159
|
+
op.on('--cache-dir DIR', 'Cache optimized images into the specified directory') do |cache_dir|
|
|
154
160
|
options[:cache_dir] = cache_dir
|
|
155
161
|
end
|
|
156
162
|
|
|
157
|
-
op.on('--cache-worker-digests', 'Cache worker digests '
|
|
158
|
-
'(updating workers invalidates cache)') do |cache_worker_digests|
|
|
163
|
+
op.on('--cache-worker-digests', 'Cache worker digests (updating workers invalidates cache)') do |cache_worker_digests|
|
|
159
164
|
options[:cache_worker_digests] = cache_worker_digests
|
|
160
165
|
end
|
|
161
166
|
|
|
162
167
|
op.separator nil
|
|
163
168
|
op.separator ' Disabling workers:'
|
|
164
169
|
|
|
165
|
-
op.on('--[no-]skip-missing-workers', 'Skip workers with missing or '
|
|
166
|
-
'problematic binaries') do |skip|
|
|
170
|
+
op.on('--[no-]skip-missing-workers', 'Skip workers with missing or problematic binaries') do |skip|
|
|
167
171
|
options[:skip_missing_workers] = skip
|
|
168
172
|
end
|
|
169
173
|
|
|
@@ -177,8 +181,7 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
|
177
181
|
op.separator nil
|
|
178
182
|
op.separator ' Worker options:'
|
|
179
183
|
|
|
180
|
-
op.on('--allow-lossy', 'Allow lossy workers and '
|
|
181
|
-
'optimizations') do |allow_lossy|
|
|
184
|
+
op.on('--allow-lossy', 'Allow lossy workers and optimizations') do |allow_lossy|
|
|
182
185
|
options[:allow_lossy] = allow_lossy
|
|
183
186
|
end
|
|
184
187
|
|
|
@@ -202,13 +205,13 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
|
202
205
|
type, marking = case
|
|
203
206
|
when [TrueClass, FalseClass, ImageOptim::TrueFalseNil].include?(type)
|
|
204
207
|
[type, 'B']
|
|
205
|
-
when
|
|
208
|
+
when type <= Integer
|
|
206
209
|
[Integer, 'N']
|
|
207
|
-
when
|
|
210
|
+
when type <= Array
|
|
208
211
|
[Array, 'a,b,c']
|
|
209
|
-
when
|
|
212
|
+
when type <= String
|
|
210
213
|
[String, 'S']
|
|
211
|
-
when ImageOptim::NonNegativeIntegerRange
|
|
214
|
+
when type == ImageOptim::NonNegativeIntegerRange
|
|
212
215
|
[type, 'M-N']
|
|
213
216
|
else
|
|
214
217
|
fail "Unknown type #{type}"
|
|
@@ -229,9 +232,12 @@ ImageOptim::Runner::OptionParser::DEFINE = proc do |op, options|
|
|
|
229
232
|
op.separator nil
|
|
230
233
|
op.separator ' Common options:'
|
|
231
234
|
|
|
232
|
-
op.on_tail(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
op.on_tail(
|
|
236
|
+
'-v',
|
|
237
|
+
'--verbose',
|
|
238
|
+
'Verbose output (show global and worker config, binary resolution log, information about each tool invocation, ' \
|
|
239
|
+
'backtrace of exception)'
|
|
240
|
+
) do
|
|
235
241
|
options[:verbose] = true
|
|
236
242
|
end
|
|
237
243
|
|
data/lib/image_optim/runner.rb
CHANGED
data/lib/image_optim/space.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'image_optim/option_helpers'
|
|
3
4
|
require 'image_optim/worker'
|
|
4
5
|
|
|
5
6
|
class ImageOptim
|
|
@@ -16,6 +17,24 @@ class ImageOptim
|
|
|
16
17
|
Array(v).map(&:to_s)
|
|
17
18
|
end
|
|
18
19
|
|
|
20
|
+
ALLOW_LOSSY_OPTION =
|
|
21
|
+
option(:allow_lossy, false, 'Allow precision option'){ |v| !!v }
|
|
22
|
+
|
|
23
|
+
PRECISION_OPTION =
|
|
24
|
+
option(:precision, 3, 'Number of digits in the fractional part ' \
|
|
25
|
+
'`0`..`20`, ignored in default/lossless mode') \
|
|
26
|
+
do |v, opt_def|
|
|
27
|
+
if allow_lossy
|
|
28
|
+
OptionHelpers.limit_with_range(v.to_i, 0..20)
|
|
29
|
+
else
|
|
30
|
+
if v != opt_def.default
|
|
31
|
+
warn "#{self.class.bin_sym} #{opt_def.name} #{v} ignored " \
|
|
32
|
+
'in default/lossless mode'
|
|
33
|
+
end
|
|
34
|
+
opt_def.default
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
19
38
|
def optimize(src, dst, options = {})
|
|
20
39
|
args = %W[
|
|
21
40
|
--input #{src}
|
|
@@ -27,6 +46,7 @@ class ImageOptim
|
|
|
27
46
|
enable_plugins.each do |plugin_name|
|
|
28
47
|
args.unshift "--enable=#{plugin_name}"
|
|
29
48
|
end
|
|
49
|
+
args.unshift "--precision=#{precision}" if allow_lossy
|
|
30
50
|
execute(:svgo, args, options) && optimized?(src, dst)
|
|
31
51
|
end
|
|
32
52
|
end
|
|
@@ -51,8 +51,21 @@ def update_readme(text)
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
readme = File.read(README_FILE)
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
program_name = File.basename($PROGRAM_NAME, '.*')
|
|
55
|
+
case ARGV
|
|
56
|
+
when []
|
|
57
|
+
if (readme = update_readme(readme))
|
|
58
|
+
File.write(README_FILE, readme)
|
|
59
|
+
else
|
|
60
|
+
abort 'Did not update worker options'
|
|
61
|
+
end
|
|
62
|
+
when %w[-n]
|
|
63
|
+
updated = update_readme(readme)
|
|
64
|
+
unless updated == readme
|
|
65
|
+
puts "Run #{program_name} to update work options in readme"
|
|
66
|
+
IO.popen('diff -u README.markdown -', 'w'){ |f| f << updated }
|
|
67
|
+
exit 1
|
|
68
|
+
end
|
|
56
69
|
else
|
|
57
|
-
abort
|
|
70
|
+
abort "#{program_name} [-n] (-n to change exit code instead of updating)"
|
|
58
71
|
end
|
|
@@ -223,7 +223,7 @@ describe ImageOptim::Config do
|
|
|
223
223
|
with(path).and_return(full_path)
|
|
224
224
|
expect(File).to receive(:size?).
|
|
225
225
|
with(full_path).and_return(true)
|
|
226
|
-
expect(
|
|
226
|
+
expect(IOConfig).to receive(:load_yaml_file).
|
|
227
227
|
with(full_path).and_return(stringified)
|
|
228
228
|
|
|
229
229
|
expect(IOConfig.read_options(path)).to eq(symbolized)
|
|
@@ -235,7 +235,7 @@ describe ImageOptim::Config do
|
|
|
235
235
|
with(path).and_return(full_path)
|
|
236
236
|
expect(File).to receive(:size?).
|
|
237
237
|
with(full_path).and_return(true)
|
|
238
|
-
expect(
|
|
238
|
+
expect(IOConfig).to receive(:load_yaml_file).
|
|
239
239
|
with(full_path).and_return([:config])
|
|
240
240
|
|
|
241
241
|
expect(IOConfig.read_options(path)).to eq({})
|
|
@@ -247,10 +247,46 @@ describe ImageOptim::Config do
|
|
|
247
247
|
with(path).and_return(full_path)
|
|
248
248
|
expect(File).to receive(:size?).
|
|
249
249
|
with(full_path).and_return(true)
|
|
250
|
-
expect(
|
|
250
|
+
expect(IOConfig).to receive(:load_yaml_file).
|
|
251
251
|
with(full_path).and_raise
|
|
252
252
|
|
|
253
253
|
expect(IOConfig.read_options(path)).to eq({})
|
|
254
254
|
end
|
|
255
255
|
end
|
|
256
|
+
|
|
257
|
+
describe '.load_yaml_file' do
|
|
258
|
+
describe 'selecting method' do
|
|
259
|
+
let(:path){ 'foo' }
|
|
260
|
+
let(:result){ 'bar' }
|
|
261
|
+
let(:yaml){ double }
|
|
262
|
+
|
|
263
|
+
before do
|
|
264
|
+
stub_const('YAML', yaml)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it 'uses YAML.safe_load_file if available' do
|
|
268
|
+
expect(yaml).to receive(:safe_load_file).
|
|
269
|
+
with(path, permitted_classes: [Range]).and_return(result)
|
|
270
|
+
|
|
271
|
+
expect(IOConfig.load_yaml_file(path)).to eq(result)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it 'uses YAML.load if safe_load_file is not available' do
|
|
275
|
+
expect(yaml).to receive(:load_file).
|
|
276
|
+
with(path).and_return(result)
|
|
277
|
+
|
|
278
|
+
expect(IOConfig.load_yaml_file(path)).to eq(result)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'handles yaml that includes `!ruby/range`' do
|
|
283
|
+
path = 'spec/files/config_with_range.yaml'
|
|
284
|
+
|
|
285
|
+
expect(IOConfig.load_yaml_file(path)).to eq({
|
|
286
|
+
'range' => 80..99,
|
|
287
|
+
'number' => 3,
|
|
288
|
+
'string' => 'foo',
|
|
289
|
+
})
|
|
290
|
+
end
|
|
291
|
+
end
|
|
256
292
|
end
|
|
@@ -101,7 +101,16 @@ describe ImageOptim::Runner::OptionParser do
|
|
|
101
101
|
allow(parser).to receive(:terminal_columns).and_return(80)
|
|
102
102
|
|
|
103
103
|
expect(parser.help.split("\n")).
|
|
104
|
-
to all(satisfy{ |line| line.length
|
|
104
|
+
to all(satisfy{ |line| line.length < 80 })
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'wraps texts even for too wide terminals' do
|
|
108
|
+
parser = OptionParser.new({})
|
|
109
|
+
|
|
110
|
+
allow(parser).to receive(:terminal_columns).and_return(1000)
|
|
111
|
+
|
|
112
|
+
expect(parser.help.split("\n")).
|
|
113
|
+
to all(satisfy{ |line| line.length <= 120 })
|
|
105
114
|
end
|
|
106
115
|
end
|
|
107
116
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'image_optim/worker/svgo'
|
|
5
|
+
|
|
6
|
+
describe ImageOptim::Worker::Svgo do
|
|
7
|
+
describe 'precision option' do
|
|
8
|
+
describe 'default' do
|
|
9
|
+
subject{ described_class::PRECISION_OPTION.default }
|
|
10
|
+
|
|
11
|
+
it{ is_expected.to eq(3) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'value' do
|
|
15
|
+
let(:subject){ described_class.new(ImageOptim.new, options).precision }
|
|
16
|
+
|
|
17
|
+
context 'when lossy not allowed' do
|
|
18
|
+
context 'by default' do
|
|
19
|
+
let(:options){ {} }
|
|
20
|
+
|
|
21
|
+
it{ is_expected.to eq(3) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context 'when value is passed through options' do
|
|
25
|
+
let(:options){ {precision: 5} }
|
|
26
|
+
|
|
27
|
+
it 'warns and keeps default' do
|
|
28
|
+
expect_any_instance_of(described_class).
|
|
29
|
+
to receive(:warn).with(%r{ignored in default/lossless mode})
|
|
30
|
+
is_expected.to eq(3)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context 'when lossy allowed' do
|
|
36
|
+
context 'by default' do
|
|
37
|
+
let(:options){ {allow_lossy: true} }
|
|
38
|
+
|
|
39
|
+
it{ is_expected.to eq(3) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context 'when value is passed through options' do
|
|
43
|
+
let(:options){ {allow_lossy: true, precision: 5} }
|
|
44
|
+
|
|
45
|
+
it 'sets the value without warning' do
|
|
46
|
+
expect_any_instance_of(described_class).not_to receive(:warn)
|
|
47
|
+
is_expected.to eq(5)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/spec/image_optim_spec.rb
CHANGED
|
@@ -121,9 +121,9 @@ describe ImageOptim do
|
|
|
121
121
|
it 'sends timeout to every worker' do
|
|
122
122
|
some_path = instance_of(ImageOptim::Path)
|
|
123
123
|
|
|
124
|
-
expect(workers[0]).to receive(:optimize).with(some_path, some_path, timeout: timer)
|
|
125
|
-
expect(workers[1]).to receive(:optimize).with(some_path, some_path, timeout: timer)
|
|
126
|
-
expect(workers[2]).to receive(:optimize).with(some_path, some_path, timeout: timer)
|
|
124
|
+
expect(workers[0]).to receive(:optimize).with(some_path, some_path, {timeout: timer})
|
|
125
|
+
expect(workers[1]).to receive(:optimize).with(some_path, some_path, {timeout: timer})
|
|
126
|
+
expect(workers[2]).to receive(:optimize).with(some_path, some_path, {timeout: timer})
|
|
127
127
|
|
|
128
128
|
image_optim.optimize_image(path)
|
|
129
129
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: image_optim
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.31.
|
|
4
|
+
version: 0.31.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ivan Kuchin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-11-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: fspath
|
|
@@ -173,8 +173,8 @@ executables:
|
|
|
173
173
|
extensions: []
|
|
174
174
|
extra_rdoc_files: []
|
|
175
175
|
files:
|
|
176
|
+
- ".github/dependabot.yml"
|
|
176
177
|
- ".github/workflows/check.yml"
|
|
177
|
-
- ".github/workflows/codeql.yml"
|
|
178
178
|
- ".github/workflows/rubocop.yml"
|
|
179
179
|
- ".gitignore"
|
|
180
180
|
- ".pre-commit-hooks.yaml"
|
|
@@ -232,6 +232,7 @@ files:
|
|
|
232
232
|
- script/template/worker_analysis.erb
|
|
233
233
|
- script/update_worker_options_in_readme
|
|
234
234
|
- script/worker_analysis
|
|
235
|
+
- spec/files/config_with_range.yaml
|
|
235
236
|
- spec/image_optim/bin_resolver/comparable_condition_spec.rb
|
|
236
237
|
- spec/image_optim/bin_resolver/simple_version_spec.rb
|
|
237
238
|
- spec/image_optim/bin_resolver_spec.rb
|
|
@@ -255,6 +256,7 @@ files:
|
|
|
255
256
|
- spec/image_optim/worker/optipng_spec.rb
|
|
256
257
|
- spec/image_optim/worker/oxipng_spec.rb
|
|
257
258
|
- spec/image_optim/worker/pngquant_spec.rb
|
|
259
|
+
- spec/image_optim/worker/svgo_spec.rb
|
|
258
260
|
- spec/image_optim/worker_spec.rb
|
|
259
261
|
- spec/image_optim_spec.rb
|
|
260
262
|
- spec/images/broken_jpeg
|
|
@@ -292,7 +294,7 @@ licenses:
|
|
|
292
294
|
metadata:
|
|
293
295
|
bug_tracker_uri: https://github.com/toy/image_optim/issues
|
|
294
296
|
changelog_uri: https://github.com/toy/image_optim/blob/master/CHANGELOG.markdown
|
|
295
|
-
documentation_uri: https://www.rubydoc.info/gems/image_optim/0.31.
|
|
297
|
+
documentation_uri: https://www.rubydoc.info/gems/image_optim/0.31.4
|
|
296
298
|
source_code_uri: https://github.com/toy/image_optim
|
|
297
299
|
post_install_message: |
|
|
298
300
|
Rails image assets optimization is extracted into image_optim_rails gem
|
|
@@ -311,7 +313,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
311
313
|
- !ruby/object:Gem::Version
|
|
312
314
|
version: '0'
|
|
313
315
|
requirements: []
|
|
314
|
-
rubygems_version: 3.
|
|
316
|
+
rubygems_version: 3.5.23
|
|
315
317
|
signing_key:
|
|
316
318
|
specification_version: 4
|
|
317
319
|
summary: Command line tool and ruby interface to optimize (lossless compress, optionally
|
|
@@ -319,6 +321,7 @@ summary: Command line tool and ruby interface to optimize (lossless compress, op
|
|
|
319
321
|
jhead, jpeg-recompress, jpegoptim, jpegrescan, jpegtran, optipng, oxipng, pngcrush,
|
|
320
322
|
pngout, pngquant, svgo)
|
|
321
323
|
test_files:
|
|
324
|
+
- spec/files/config_with_range.yaml
|
|
322
325
|
- spec/image_optim/bin_resolver/comparable_condition_spec.rb
|
|
323
326
|
- spec/image_optim/bin_resolver/simple_version_spec.rb
|
|
324
327
|
- spec/image_optim/bin_resolver_spec.rb
|
|
@@ -342,6 +345,7 @@ test_files:
|
|
|
342
345
|
- spec/image_optim/worker/optipng_spec.rb
|
|
343
346
|
- spec/image_optim/worker/oxipng_spec.rb
|
|
344
347
|
- spec/image_optim/worker/pngquant_spec.rb
|
|
348
|
+
- spec/image_optim/worker/svgo_spec.rb
|
|
345
349
|
- spec/image_optim/worker_spec.rb
|
|
346
350
|
- spec/image_optim_spec.rb
|
|
347
351
|
- spec/images/broken_jpeg
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
name: codeql
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
pull_request:
|
|
5
|
-
schedule:
|
|
6
|
-
- cron: '43 20 * * 0'
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
analyze:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
permissions:
|
|
12
|
-
actions: read
|
|
13
|
-
contents: read
|
|
14
|
-
security-events: write
|
|
15
|
-
|
|
16
|
-
strategy:
|
|
17
|
-
fail-fast: false
|
|
18
|
-
matrix:
|
|
19
|
-
language: [ 'ruby' ]
|
|
20
|
-
|
|
21
|
-
steps:
|
|
22
|
-
- uses: actions/checkout@v2
|
|
23
|
-
|
|
24
|
-
- uses: github/codeql-action/init@v1
|
|
25
|
-
with:
|
|
26
|
-
languages: ${{ matrix.language }}
|
|
27
|
-
|
|
28
|
-
- uses: github/codeql-action/autobuild@v1
|
|
29
|
-
|
|
30
|
-
- uses: github/codeql-action/analyze@v1
|