image_optim 0.31.2 → 0.31.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://img.shields.io/gem/v/image_optim?logo=rubygems)](https://rubygems.org/gems/image_optim)
|
2
|
-
[![Build Status](https://img.shields.io/github/workflow/status/toy/image_optim/check
|
3
|
-
[![Rubocop](https://img.shields.io/github/workflow/status/toy/image_optim/rubocop
|
4
|
-
[![CodeQL](https://img.shields.io/github/workflow/status/toy/image_optim/codeql
|
2
|
+
[![Build Status](https://img.shields.io/github/actions/workflow/status/toy/image_optim/check.yml?logo=github)](https://github.com/toy/image_optim/actions/workflows/check.yml)
|
3
|
+
[![Rubocop](https://img.shields.io/github/actions/workflow/status/toy/image_optim/rubocop.yml?label=rubocop&logo=rubocop)](https://github.com/toy/image_optim/actions/workflows/rubocop.yml)
|
4
|
+
[![CodeQL](https://img.shields.io/github/actions/workflow/status/toy/image_optim/codeql.yml?label=codeql&logo=github)](https://github.com/toy/image_optim/actions/workflows/codeql.yml)
|
5
5
|
[![Code Climate](https://img.shields.io/codeclimate/maintainability/toy/image_optim?logo=codeclimate)](https://codeclimate.com/github/toy/image_optim)
|
6
6
|
[![Code Climate Coverage](https://img.shields.io/codeclimate/coverage/toy/image_optim?logo=codeclimate)](https://codeclimate.com/github/toy/image_optim)
|
7
7
|
[![Depfu](https://img.shields.io/depfu/toy/image_optim)](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
|