jekyll-minify 1.0.8 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf13427398f4a1d4d2571367ac83d24d8bb41be50f2f3d2e71b5b446c8e1e491
4
- data.tar.gz: c4cfe9a1ef18183832646894e5fc73346085f1e96d2de77d309e13370c3be741
3
+ metadata.gz: f3cca42f128bfb722359fbe7cad0782cdde8192bc1dbadc889ab09de710e0fba
4
+ data.tar.gz: bba9d326f421608eb24d4d4613947b1ca4427d5a7ef3a599c66c193722d52fe6
5
5
  SHA512:
6
- metadata.gz: aa89fd52f46081b6f350ca763fcd2bc513b88a1a0a385b71038bafea9a228b92cc0aabeee3e75d490466fc1f39dfe2471c4cf9245cc196fabac4fa8a47ceb108
7
- data.tar.gz: 475f8e2578103875942cfbd7467622fa20d5e89be682f27749022708277caa71606935840fcbb0b669ecd119cfb6899075fe544b8b0d2ccf63a7ec3b32f1b31b
6
+ metadata.gz: 8f0826508130a2f2c2ddb1c8a26799412c82752b49066282ceb279526a505f86d91209b88230362bfdc02e53f0b9835df33f6843d49ad797c14591b56f88e013
7
+ data.tar.gz: d3caa8d83001eafc4ada3ab56e6c9210e415af5478892fc255a5d8a6589e4ff0b7f978e82dcd991288b88197519be1f7325a9ad57be34df57ba8e4a69bee7ca9
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Minify
5
+ # OpenTelemetry instrumentation facade for jekyll-minify.
6
+ #
7
+ # All tracing is configured centrally in TRACED_METHODS — no span code lives
8
+ # in business logic classes. To add a span: append one entry. To rename a
9
+ # method: update the one entry. When a method is removed from its class the
10
+ # stale entry raises NoMethodError in tests, signalling the entry to delete.
11
+ #
12
+ # Requires opentelemetry-api at runtime; falls back to a no-op if absent.
13
+ # Users opt in to real tracing by adding opentelemetry-sdk and
14
+ # opentelemetry-exporter-otlp to their site Gemfile and exporting:
15
+ #
16
+ # OTEL_SERVICE_NAME=jekyll-minify
17
+ # OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
18
+ module Instrumentation
19
+ # Name reported to the OTel tracer provider for spans created by this gem.
20
+ #
21
+ # @return [String]
22
+ TRACER_NAME = 'jekyll-minify'
23
+
24
+ # Central instrumentation registry.
25
+ #
26
+ # Each row: [class_name, method_type, method_name, span_name, attribute_proc]
27
+ #
28
+ # method_type:
29
+ # :instance — public instance method
30
+ # :private — private instance method (visibility is preserved on the wrapper)
31
+ # :class — class / module method (prepended on the singleton class)
32
+ #
33
+ # attribute_proc: ->(span, this, args, result) or nil
34
+ # span — OTel span (or NoopSpan); call span.set_attribute after super returns
35
+ # this — receiver object (instance or nil for class methods)
36
+ # args — positional args array as passed to the method
37
+ # result — return value of the original method
38
+ #
39
+ # @return [Array<Array>]
40
+ # :nocov: — attribute procs are configuration data; coverage comes from integration tests
41
+ TRACED_METHODS = [
42
+ ['Jekyll::Minify::Minifier', :class, :minify_site, 'minify.site_minify',
43
+ lambda { |span, _this, args, result|
44
+ span.set_attribute('minify.dest_path', args[0].dest.to_s)
45
+ next unless result
46
+
47
+ types = result.values_at(:html, :css, :js)
48
+ span.set_attribute('minify.total_file_count', types.sum { |t| t[:count] })
49
+ span.set_attribute('minify.total_original_size_bytes', types.sum { |t| t[:original_bytes] })
50
+ span.set_attribute('minify.total_minified_size_bytes', types.sum { |t| t[:minified_bytes] })
51
+ span.set_attribute('minify.error_count', result[:errors].length)
52
+ }],
53
+
54
+ ['Jekyll::Minify::Minifier', :private, :minify_each_file, 'minify.file_discovery',
55
+ lambda { |span, _this, args, _result|
56
+ span.set_attribute('minify.dest_path', args[0].to_s)
57
+ }],
58
+
59
+ ['Jekyll::Minify::Minifier', :private, :minify_with_fallback, 'minify.file_process',
60
+ lambda { |span, _this, args, _result|
61
+ span.set_attribute('minify.file_path', args[0].to_s)
62
+ span.set_attribute('minify.asset_type', args[1].to_s)
63
+ }],
64
+
65
+ ['Jekyll::Minify::Minifier', :private, :minify_html, 'minify.html_minify',
66
+ lambda { |span, _this, args, result|
67
+ span.set_attribute('minify.input_size_bytes', args[0].bytesize)
68
+ span.set_attribute('minify.output_size_bytes', result.to_s.bytesize)
69
+ }],
70
+
71
+ ['Jekyll::Minify::Minifier', :private, :minify_css, 'minify.css_minify',
72
+ lambda { |span, _this, args, result|
73
+ span.set_attribute('minify.input_size_bytes', args[0].bytesize)
74
+ span.set_attribute('minify.output_size_bytes', result.to_s.bytesize)
75
+ }],
76
+
77
+ ['Jekyll::Minify::Minifier', :private, :minify_js, 'minify.js_minify',
78
+ lambda { |span, _this, args, result|
79
+ span.set_attribute('minify.input_size_bytes', args[0].bytesize)
80
+ span.set_attribute('minify.output_size_bytes', result.to_s.bytesize)
81
+ }]
82
+ ].freeze
83
+ # :nocov:
84
+
85
+ # Returns the active OTel tracer, or a no-op tracer if opentelemetry-api is absent.
86
+ #
87
+ # @return [OpenTelemetry::Trace::Tracer, NoopTracer]
88
+ def self.tracer
89
+ @tracer ||=
90
+ if defined?(OpenTelemetry)
91
+ # :nocov:
92
+ OpenTelemetry.tracer_provider.tracer(TRACER_NAME, Jekyll::Minify::VERSION)
93
+ # :nocov:
94
+ else
95
+ NoopTracer.new
96
+ end
97
+ end
98
+
99
+ # Wraps a block in an OTel span.
100
+ #
101
+ # @param span_name [String] Dot-separated span name (e.g. 'minify.html_minify')
102
+ # @param attributes [Hash] Initial span attributes
103
+ # @yieldparam span [OpenTelemetry::Trace::Span, NoopSpan] Active span
104
+ # @return [Object] The return value of the block
105
+ def self.instrument(span_name, attributes: {}, &block)
106
+ tracer.in_span(span_name, attributes: attributes, &block)
107
+ end
108
+
109
+ # Resets the cached tracer and installation flag. Call in tests after changing OTel configuration.
110
+ #
111
+ # @return [void]
112
+ def self.reset!
113
+ @tracer = nil
114
+ @installed = false
115
+ end
116
+
117
+ # Returns true when OTel is requested via standard environment variables.
118
+ #
119
+ # install! guards on this so the prepend wrappers are only applied when
120
+ # a real exporter is configured. In CI and local tests (no OTel env vars)
121
+ # business logic classes are untouched, keeping allow_any_instance_of stubs
122
+ # and other RSpec mechanics fully functional.
123
+ #
124
+ # @return [Boolean]
125
+ def self.enabled?
126
+ ENV.key?('OTEL_EXPORTER_OTLP_ENDPOINT') || ENV.key?('OTEL_SERVICE_NAME')
127
+ end
128
+
129
+ # Installs wrappers on all classes listed in TRACED_METHODS using Module#prepend.
130
+ #
131
+ # Called once at plugin load time (end of jekyll-minify.rb, after all requires),
132
+ # but only when enabled? returns true. Use OTEL_EXPORTER_OTLP_ENDPOINT or
133
+ # OTEL_SERVICE_NAME to activate tracing.
134
+ #
135
+ # @return [void]
136
+ def self.install!
137
+ return if @installed
138
+
139
+ @installed = true
140
+ setup_sdk!
141
+ TRACED_METHODS.group_by { |e| e[0] }.each do |class_name, entries|
142
+ prepend_wrappers(resolve_class(class_name), entries)
143
+ end
144
+ end
145
+
146
+ # Configures the OTel SDK from standard env vars if the SDK gem is available.
147
+ # Silently no-ops when opentelemetry-sdk or opentelemetry-exporter-otlp is
148
+ # not installed. Uses SimpleSpanProcessor (not the SDK default
149
+ # BatchSpanProcessor) since Jekyll is a short-lived CLI process that could
150
+ # exit before a batch timer fires, dropping spans.
151
+ #
152
+ # jekyll-theme-centos loads several instrumented plugins in one process;
153
+ # OpenTelemetry::SDK.configure overwrites the global tracer_provider on
154
+ # every call, so only the first plugin to reach here (see
155
+ # sdk_already_configured?) configures the SDK — later callers reuse that
156
+ # provider. The at_exit/post_write hooks are registered once, by whichever
157
+ # plugin wins, and flush every plugin's spans, not just this one's.
158
+ #
159
+ # @return [void]
160
+ def self.setup_sdk!
161
+ return if sdk_already_configured?
162
+
163
+ # :nocov:
164
+ require 'opentelemetry/sdk'
165
+ require 'opentelemetry/exporter/otlp'
166
+ exporter = OpenTelemetry::Exporter::OTLP::Exporter.new
167
+ processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(exporter)
168
+ OpenTelemetry::SDK.configure { |c| c.add_span_processor(processor) }
169
+
170
+ at_exit { OpenTelemetry.tracer_provider&.shutdown }
171
+ Jekyll::Hooks.register(:site, :post_write, priority: 0) { OpenTelemetry.tracer_provider&.force_flush }
172
+ rescue LoadError
173
+ nil
174
+ # :nocov:
175
+ end
176
+
177
+ # True when some plugin (this one or another) already installed a real
178
+ # SDK-backed tracer_provider, in which case setup_sdk! must not run again.
179
+ #
180
+ # @return [Boolean]
181
+ def self.sdk_already_configured?
182
+ defined?(OpenTelemetry::SDK::Trace::TracerProvider) &&
183
+ OpenTelemetry.tracer_provider.is_a?(OpenTelemetry::SDK::Trace::TracerProvider)
184
+ end
185
+
186
+ # Resolves a dot-separated class name to a constant; returns nil on NameError.
187
+ #
188
+ # @param name [String] fully-qualified class name, e.g. 'Jekyll::Minify::Minifier'
189
+ # @return [Class, nil]
190
+ def self.resolve_class(name)
191
+ name.split('::').reduce(Object) { |m, c| m.const_get(c) }
192
+ rescue NameError
193
+ nil
194
+ end
195
+
196
+ # Applies instance and class-method wrappers to klass; skips if klass is nil.
197
+ #
198
+ # @param klass [Class, nil] the class to wrap
199
+ # @param entries [Array<Array>] TRACED_METHODS rows scoped to klass
200
+ # @return [void]
201
+ def self.prepend_wrappers(klass, entries)
202
+ return unless klass
203
+
204
+ class_entries, instance_entries = entries.partition { |e| e[1] == :class }
205
+ klass.prepend(build_wrapper_module(instance_entries)) unless instance_entries.empty?
206
+ klass.singleton_class.prepend(build_wrapper_module(class_entries)) unless class_entries.empty?
207
+ end
208
+
209
+ # Builds an anonymous module that wraps each listed method in a span.
210
+ #
211
+ # @param entries [Array<Array>] TRACED_METHODS rows to wrap
212
+ # @return [Module] anonymous module ready to be prepended
213
+ def self.build_wrapper_module(entries)
214
+ Module.new do
215
+ entries.each { |entry| Instrumentation.send(:define_traced_method, self, entry) }
216
+ end
217
+ end
218
+
219
+ # Defines a single span-wrapped method on mod for one TRACED_METHODS entry.
220
+ #
221
+ # @param mod [Module] the wrapper module being built
222
+ # @param entry [Array] one TRACED_METHODS row: [class_name, method_type, method_name, span_name, attr_proc]
223
+ # @return [void]
224
+ def self.define_traced_method(mod, entry)
225
+ _class_name, method_type, method_name, span_name, attr_proc = entry
226
+ mod.define_method(method_name) do |*args, **kwargs, &blk|
227
+ Instrumentation.send(:run_traced, span_name, attr_proc, self, args) { super(*args, **kwargs, &blk) }
228
+ end
229
+ mod.send(:private, method_name) if method_type == :private
230
+ end
231
+
232
+ # Runs the original method inside a span, applying attr_proc on success and
233
+ # recording exceptions on the span before re-raising them.
234
+ #
235
+ # @param span_name [String] span name
236
+ # @param attr_proc [Proc, nil] attribute callback: ->(span, receiver, args, result)
237
+ # @param receiver [Object] the wrapped method's receiver (self)
238
+ # @param args [Array] positional arguments passed to the wrapped method
239
+ # @yieldreturn [Object] the original (unwrapped) method's return value
240
+ # @return [Object] the wrapped method's return value
241
+ def self.run_traced(span_name, attr_proc, receiver, args)
242
+ instrument(span_name) do |span|
243
+ result = yield
244
+ attr_proc&.call(span, receiver, args, result)
245
+ result
246
+ rescue StandardError => e
247
+ span.record_exception(e)
248
+ raise
249
+ end
250
+ end
251
+
252
+ private_class_method :build_wrapper_module, :define_traced_method, :run_traced, :prepend_wrappers, :setup_sdk!,
253
+ :sdk_already_configured?
254
+
255
+ # No-op tracer used when opentelemetry-api is not loaded.
256
+ class NoopTracer
257
+ # Yields a NoopSpan and returns the block's value.
258
+ #
259
+ # @param _name [String] span name (ignored)
260
+ # @param _opts [Hash] span options (ignored)
261
+ # @yieldparam span [NoopSpan]
262
+ # @return [Object] the block's return value
263
+ def in_span(_name, **_opts)
264
+ yield NoopSpan.new
265
+ end
266
+ end
267
+
268
+ # No-op span that silently accepts all attribute and event calls.
269
+ class NoopSpan
270
+ # No-op attribute setter. Accepts and ignores any arguments.
271
+ #
272
+ # @return [NoopSpan] self
273
+ def set_attribute(*)
274
+ self
275
+ end
276
+
277
+ # No-op exception recorder. Accepts and ignores any arguments.
278
+ #
279
+ # @return [NoopSpan] self
280
+ def record_exception(*)
281
+ self
282
+ end
283
+
284
+ # No-op status setter. Accepts and ignores any arguments.
285
+ #
286
+ # @return [void]
287
+ def status=(*); end
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Minify
5
+ # The version of the jekyll-minify gem.
6
+ #
7
+ # Updated with each release to reflect the current version. Independent of the
8
+ # gemspec's CI-generated version; used to identify this library's version to
9
+ # the OpenTelemetry tracer provider.
10
+ VERSION = '1.0.6'
11
+ end
12
+ end
data/lib/jekyll-minify.rb CHANGED
@@ -76,6 +76,9 @@ rescue LoadError
76
76
  'Install with: gem install minify_html'
77
77
  end
78
78
 
79
+ require_relative 'jekyll-minify/version'
80
+ require_relative 'jekyll-minify/instrumentation'
81
+
79
82
  module Jekyll
80
83
  # Jekyll plugin for automatic minification of HTML, CSS, and JavaScript.
81
84
  module Minify
@@ -146,7 +149,7 @@ module Jekyll
146
149
  # Returns immediately if with_minify is false or the destination directory does not exist.
147
150
  #
148
151
  # @param site [Jekyll::Site] the Jekyll site object providing dest and config
149
- # @return [void]
152
+ # @return [Hash, nil] the run's @stats, or nil if minification was skipped
150
153
  def run(site)
151
154
  unless plugin_enabled?(site.config)
152
155
  Jekyll.logger.info 'jekyll-minify:', 'Minification disabled (with_minify: false).'
@@ -162,6 +165,7 @@ module Jekyll
162
165
 
163
166
  minify_each_file(dest)
164
167
  log_statistics
168
+ @stats
165
169
  end
166
170
 
167
171
  private
@@ -486,3 +490,5 @@ end
486
490
  Jekyll::Hooks.register :site, :post_write, priority: :low do |site|
487
491
  Jekyll::Minify::Minifier.minify_site(site)
488
492
  end
493
+
494
+ Jekyll::Minify::Instrumentation.install! if Jekyll::Minify::Instrumentation.enabled?
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-minify
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - ReleaseBot
7
+ - Alain Reguera Delgado
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
@@ -43,6 +43,20 @@ dependencies:
43
43
  - - "~>"
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0.15'
46
+ - !ruby/object:Gem::Dependency
47
+ name: opentelemetry-api
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '1.4'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.4'
46
60
  - !ruby/object:Gem::Dependency
47
61
  name: rake
48
62
  requirement: !ruby/object:Gem::Requirement
@@ -171,7 +185,7 @@ dependencies:
171
185
  version: '0.9'
172
186
  description: Jekyll plugin to minify HTML, CSS, and JavaScript files.
173
187
  email:
174
- - group_58921183_bot_c5520aeba366578e2e444dd181ff3e23@noreply.gitlab.com
188
+ - alain.reguera@gmail.com
175
189
  executables: []
176
190
  extensions: []
177
191
  extra_rdoc_files: []
@@ -179,6 +193,8 @@ files:
179
193
  - LICENSE
180
194
  - README.md
181
195
  - lib/jekyll-minify.rb
196
+ - lib/jekyll-minify/instrumentation.rb
197
+ - lib/jekyll-minify/version.rb
182
198
  homepage: https://gitlab.com/CentOS/artwork/centos-web/jekyll-minify
183
199
  licenses:
184
200
  - MIT