fig 2.0.0.pre.alpha.11 → 2.0.0.pre.alpha.12

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: 860a3d53c4807bf0933cfbf65e8dbfa16be7f1bc18c5899d2a2a1b79f2017c0d
4
- data.tar.gz: 143f18d5d23eb8b170841bfc9958769241ab1de373c6fbe15ddbdfb2a5150edb
3
+ metadata.gz: 7f1364feade70b2eff4e25aec350a9cf3f39a874a2b9b05f110039ea3c16c760
4
+ data.tar.gz: '09924664a67b47b7f9fcb744509a661bfdc341546fde5610ce9782889fe0629b'
5
5
  SHA512:
6
- metadata.gz: 19e727be17a0be0136373eb7a3b72670290c5d6a07cc05f03f5a46f66c398a7791fe256ebb0aa3375f010497f49b302d3bf7c0cf88c0a1281900c77a2b21907c
7
- data.tar.gz: 5cac9612445d72cc3ea3958d9baa23a2a1d8229e33427190dd469f8f91b5a431e55d9599417caaf1a86e74c10b2fd9cc5f8fcd8ca11173b5968b3e1dd71e235f
6
+ metadata.gz: 18829c02bca0163f2ebc14144fc77662d202133d01906ffa6947a58959f889fb69c5cbbba09e0aafe534f4812cf28cbd27c840cc3a2253bea7658d2fb7ee1ac8
7
+ data.tar.gz: e062c0df9429ae9df726f40e502c6a58e8e356fe4e0f0a7c9992489fdc4edca1b4f834ba50453e949b5071649d1ca9709b43b67eceedd8053f57e01fce9e10d2
@@ -94,7 +94,7 @@ Standard options (represented as "[...]" above):
94
94
 
95
95
  [-l | --login]
96
96
 
97
- [--log-level LEVEL] [--log-config PATH | --log-to-stdout]
97
+ [--log-level LEVEL] [--log-config PATH | --log-to-stdout] [--verbose]
98
98
  [--figrc PATH] [--no-figrc] [--no-remote-figrc]
99
99
 
100
100
  [--suppress-vcs-comments-in-published-packages]
@@ -79,6 +79,7 @@ class Fig::Command::Options
79
79
  attr_reader :suppress_retrieves
80
80
  attr_reader :update_lock_response
81
81
  attr_reader :variable_to_get
82
+ attr_reader :verbose
82
83
  attr_accessor :version_message
83
84
  attr_accessor :version_plain
84
85
 
@@ -177,6 +178,10 @@ class Fig::Command::Options
177
178
  return @suppress_warning_unused_retrieve
178
179
  end
179
180
 
181
+ def suppress_warning_unused_override?()
182
+ return @suppress_warning_unused_override
183
+ end
184
+
180
185
  private
181
186
 
182
187
  EXTRA_OPTIONS_DESCRIPTION = <<-'END_DESCRIPTION'
@@ -741,6 +746,13 @@ Running commands:
741
746
  @log_level = log_level
742
747
  end
743
748
 
749
+ @parser.on(
750
+ '--verbose',
751
+ 'enable verbose output with timing information'
752
+ ) do
753
+ @verbose = true
754
+ end
755
+
744
756
  @update_lock_response = nil # Nil means wait, but warn.
745
757
  update_lock_responses = [:wait, :fail, :ignore]
746
758
  response_list = update_lock_responses.join(', ')
@@ -774,6 +786,13 @@ Running commands:
774
786
  @suppress_warning_unused_retrieve = true
775
787
  end
776
788
 
789
+ @parser.on(
790
+ '--suppress-warning-unused-override',
791
+ %q<don't complain about an override statement that isn't used>
792
+ ) do
793
+ @suppress_warning_unused_override = true
794
+ end
795
+
777
796
  return
778
797
  end
779
798
 
data/lib/fig/command.rb CHANGED
@@ -22,6 +22,7 @@ require 'fig/runtime_environment'
22
22
  require 'fig/statement/configuration'
23
23
  require 'fig/update_lock'
24
24
  require 'fig/user_input_error'
25
+ require 'fig/verbose_logging'
25
26
  require 'fig/working_directory_maintainer'
26
27
 
27
28
  module Fig; end
@@ -46,6 +47,11 @@ class Fig::Command
46
47
  Fig::Logging.initialize_pre_configuration(
47
48
  @options.log_to_stdout(), @options.log_level(),
48
49
  )
50
+
51
+ # Enable verbose logging if requested
52
+ if @options.verbose
53
+ Fig::VerboseLogging.enable_verbose!
54
+ end
49
55
 
50
56
  actions = @options.actions()
51
57
  if actions.empty?
@@ -188,6 +194,15 @@ class Fig::Command
188
194
  return ! suppressed_warnings.include?('unused retrieve')
189
195
  end
190
196
 
197
+ def check_for_unused_overrides?()
198
+ return false if @options.suppress_warning_unused_override?
199
+
200
+ suppressed_warnings = @application_configuration['suppress warnings']
201
+ return true if not suppressed_warnings
202
+
203
+ return ! suppressed_warnings.include?('unused override')
204
+ end
205
+
191
206
  def configure()
192
207
  set_up_update_lock()
193
208
 
@@ -344,6 +359,14 @@ class Fig::Command
344
359
  }
345
360
  end
346
361
 
362
+ if check_for_unused_overrides?
363
+ Fig::AtExit.add {
364
+ if ! @suppress_further_error_messages
365
+ @environment.check_for_unused_overrides
366
+ end
367
+ }
368
+ end
369
+
347
370
  return
348
371
  end
349
372
 
@@ -17,6 +17,7 @@ class Fig::IncludeBacktrace
17
17
  @parent = parent
18
18
  @descriptor = descriptor
19
19
  @overrides = {}
20
+ @override_statements = {} # map package_name -> Statement::Override
20
21
  end
21
22
 
22
23
  def add_override(statement)
@@ -34,14 +35,25 @@ class Fig::IncludeBacktrace
34
35
  end
35
36
 
36
37
  @overrides[package_name] = new_version
38
+ @override_statements[package_name] = statement
39
+ statement.added_to_environment(true)
37
40
  end
38
41
 
39
42
  # Returns a version.
40
43
  def get_override(package_name, default_version = nil)
41
44
  version = @overrides[package_name]
42
- return version if version
45
+ if version
46
+ statement = @override_statements[package_name]
47
+ statement.referenced(true) if statement
48
+ return version
49
+ end
43
50
 
44
- return @parent.get_override(package_name, default_version) if @parent
51
+ if @parent
52
+ result = @parent.get_override(package_name, default_version)
53
+ # If parent provided an override, it will mark its own statement as referenced
54
+ return result
55
+ end
56
+
45
57
  return default_version
46
58
  end
47
59
 
@@ -73,4 +85,17 @@ class Fig::IncludeBacktrace
73
85
 
74
86
  stack << @descriptor
75
87
  end
88
+
89
+ # Collect all override statements from this backtrace level and parents
90
+ def collect_override_statements(statements = [])
91
+ if @parent
92
+ @parent.collect_override_statements(statements)
93
+ end
94
+
95
+ @override_statements.values.each do |statement|
96
+ statements << statement
97
+ end
98
+
99
+ return statements
100
+ end
76
101
  end
@@ -24,6 +24,7 @@ require 'fig/protocol/artifactory'
24
24
  require 'fig/repository_error'
25
25
  require 'fig/url'
26
26
  require 'fig/user_input_error'
27
+ require 'fig/verbose_logging'
27
28
 
28
29
  module Fig; end
29
30
 
@@ -99,16 +100,20 @@ class Fig::OperatingSystem
99
100
  end
100
101
 
101
102
  def download_list(url)
102
- begin
103
- protocol, uri = decode_protocol url
104
-
105
- return protocol.download_list uri
106
- rescue SocketError => error
107
- Fig::Logging.debug error.message
108
- raise Fig::NetworkError.new "#{url}: #{error.message}"
109
- rescue Errno::ETIMEDOUT => error
110
- Fig::Logging.debug error.message
111
- raise Fig::NetworkError.new "#{url}: #{error.message}"
103
+ Fig::VerboseLogging.time_operation("listing packages from #{url}") do
104
+ begin
105
+ protocol, uri = decode_protocol url
106
+
107
+ result = protocol.download_list uri
108
+ log_repository_operation("listed", url, "#{result.size} entries")
109
+ result
110
+ rescue SocketError => error
111
+ Fig::Logging.debug error.message
112
+ raise Fig::NetworkError.new "#{url}: #{error.message}"
113
+ rescue Errno::ETIMEDOUT => error
114
+ Fig::Logging.debug error.message
115
+ raise Fig::NetworkError.new "#{url}: #{error.message}"
116
+ end
112
117
  end
113
118
  end
114
119
 
@@ -124,15 +129,23 @@ class Fig::OperatingSystem
124
129
  # Returns whether the file was not downloaded because the file already
125
130
  # exists and is already up-to-date.
126
131
  def download(url, path, prompt_for_login)
127
- protocol, uri = decode_protocol url
132
+ Fig::VerboseLogging.time_operation("downloading #{File.basename(url)}") do
133
+ protocol, uri = decode_protocol url
128
134
 
129
- FileUtils.mkdir_p(File.dirname path)
135
+ FileUtils.mkdir_p(File.dirname path)
130
136
 
131
- return protocol.download uri, path, prompt_for_login
137
+ result = protocol.download uri, path, prompt_for_login
138
+ if File.exist?(path)
139
+ size = File.size(path)
140
+ log_asset_operation("downloaded", path, size)
141
+ end
142
+ result
143
+ end
132
144
  end
133
145
 
134
146
  # Returns the basename and full path to the download.
135
147
  def download_resource(url, download_directory)
148
+ log_asset_operation("downloading", url)
136
149
  FileUtils.mkdir_p(download_directory)
137
150
 
138
151
  basename = CGI.unescape Fig::URL.parse(url).path.split('/').last
@@ -144,6 +157,7 @@ class Fig::OperatingSystem
144
157
  end
145
158
 
146
159
  def download_and_unpack_archive(url, download_directory, unpack_directory)
160
+ log_asset_operation("downloading", url)
147
161
  basename, path = download_resource(url, download_directory)
148
162
 
149
163
  case path
@@ -224,32 +238,37 @@ class Fig::OperatingSystem
224
238
  # .tgz
225
239
  # .zip
226
240
  def unpack_archive(directory, archive_path)
227
- FileUtils.mkdir_p directory
228
- Dir.chdir(directory) do
229
- if ! File.exist? archive_path
230
- raise Fig::RepositoryError.new "#{archive_path} does not exist."
231
- end
232
-
233
- running_on_windows = Fig::OperatingSystem.windows?
234
- ::Archive.read_open_filename(archive_path) do |reader|
235
- while entry = reader.next_header
236
- if running_on_windows
237
- check_archive_entry_for_windows entry, archive_path
238
- end
241
+ Fig::VerboseLogging.time_operation("extracting archive #{File.basename(archive_path)}") do
242
+ FileUtils.mkdir_p directory
243
+ archive_size = File.size(archive_path) if File.exist?(archive_path)
244
+ log_asset_operation("extracting", archive_path, archive_size)
245
+
246
+ Dir.chdir(directory) do
247
+ if ! File.exist? archive_path
248
+ raise Fig::RepositoryError.new "#{archive_path} does not exist."
249
+ end
239
250
 
240
- begin
241
- reader.extract(entry)
242
- rescue Archive::Error => exception
243
- # Nice how the error message doesn't include any information about
244
- # what was having the problem.
245
- message = exception.message.sub(/^Extract archive failed: /, '')
246
- new_exception =
247
- Fig::RepositoryError.new(
248
- "Could not extract #{entry.pathname} from #{archive_path}: #{message}"
249
- )
250
-
251
- new_exception.set_backtrace exception.backtrace
252
- raise new_exception
251
+ running_on_windows = Fig::OperatingSystem.windows?
252
+ ::Archive.read_open_filename(archive_path) do |reader|
253
+ while entry = reader.next_header
254
+ if running_on_windows
255
+ check_archive_entry_for_windows entry, archive_path
256
+ end
257
+
258
+ begin
259
+ reader.extract(entry)
260
+ rescue Archive::Error => exception
261
+ # Nice how the error message doesn't include any information about
262
+ # what was having the problem.
263
+ message = exception.message.sub(/^Extract archive failed: /, '')
264
+ new_exception =
265
+ Fig::RepositoryError.new(
266
+ "Could not extract #{entry.pathname} from #{archive_path}: #{message}"
267
+ )
268
+
269
+ new_exception.set_backtrace exception.backtrace
270
+ raise new_exception
271
+ end
253
272
  end
254
273
  end
255
274
  end
@@ -363,4 +382,32 @@ class Fig::OperatingSystem
363
382
 
364
383
  return
365
384
  end
385
+
386
+ def log_repository_operation(operation, url_or_path, details = nil)
387
+ message = "repository #{operation}: #{url_or_path}"
388
+ message += " (#{details})" if details
389
+ Fig::VerboseLogging.verbose message
390
+ end
391
+
392
+ private
393
+
394
+ def log_asset_operation(operation, asset_path, size_bytes = nil)
395
+ message = "asset #{operation}: #{asset_path}"
396
+ message += " (#{format_bytes(size_bytes)})" if size_bytes
397
+ Fig::VerboseLogging.verbose message
398
+ end
399
+
400
+ def format_bytes(bytes)
401
+ return nil unless bytes
402
+
403
+ if bytes < 1024
404
+ "#{bytes}B"
405
+ elsif bytes < 1024 * 1024
406
+ "#{(bytes / 1024.0).round(1)}KB"
407
+ elsif bytes < 1024 * 1024 * 1024
408
+ "#{(bytes / (1024.0 * 1024)).round(1)}MB"
409
+ else
410
+ "#{(bytes / (1024.0 * 1024 * 1024)).round(1)}GB"
411
+ end
412
+ end
366
413
  end
@@ -15,6 +15,7 @@ require 'fig/parser'
15
15
  require 'fig/repository_error'
16
16
  require 'fig/repository_package_publisher'
17
17
  require 'fig/url'
18
+ require 'fig/verbose_logging'
18
19
 
19
20
  module Fig; end
20
21
 
@@ -79,9 +80,12 @@ class Fig::Repository
79
80
  def list_remote_packages
80
81
  check_remote_repository_format()
81
82
 
82
- paths = @operating_system.download_list(remote_download_url())
83
-
84
- return paths.reject { |path| path =~ %r< ^ #{METADATA_SUBDIRECTORY} / >xs }
83
+ Fig::VerboseLogging.time_operation("listing remote packages from #{remote_download_url()}") do
84
+ paths = @operating_system.download_list(remote_download_url())
85
+ filtered_paths = paths.reject { |path| path =~ %r< ^ #{METADATA_SUBDIRECTORY} / >xs }
86
+ Fig::VerboseLogging.verbose "found #{filtered_paths.size} packages at #{remote_download_url()}"
87
+ filtered_paths
88
+ end
85
89
  end
86
90
 
87
91
  def get_package(
@@ -296,25 +300,27 @@ class Fig::Repository
296
300
  end
297
301
 
298
302
  def update_package(descriptor)
299
- temp_dir = package_download_temp_dir(descriptor)
300
- begin
301
- install_package(descriptor, temp_dir)
302
- rescue Fig::FileNotFoundError => error
303
- if @updating_package_definition
304
- Fig::Logging.fatal \
305
- "Package #{descriptor.to_string} not found in remote repository. (Was looking for #{error.path}.)"
306
- else
307
- Fig::Logging.fatal \
308
- "Part of package #{descriptor.to_string} was not downloadable. (Was attempting to get #{error.path}.)"
309
- end
303
+ Fig::VerboseLogging.time_operation("downloading package #{descriptor.to_string}") do
304
+ temp_dir = package_download_temp_dir(descriptor)
305
+ begin
306
+ install_package(descriptor, temp_dir)
307
+ rescue Fig::FileNotFoundError => error
308
+ if @updating_package_definition
309
+ Fig::Logging.fatal \
310
+ "Package #{descriptor.to_string} not found in remote repository. (Was looking for #{error.path}.)"
311
+ else
312
+ Fig::Logging.fatal \
313
+ "Part of package #{descriptor.to_string} was not downloadable. (Was attempting to get #{error.path}.)"
314
+ end
310
315
 
311
- raise Fig::RepositoryError.new
312
- rescue StandardError => exception
313
- Fig::Logging.fatal %Q<Install of #{descriptor.to_string} failed: #{exception}>
316
+ raise Fig::RepositoryError.new
317
+ rescue StandardError => exception
318
+ Fig::Logging.fatal %Q<Install of #{descriptor.to_string} failed: #{exception}>
314
319
 
315
- raise Fig::RepositoryError.new
316
- ensure
317
- FileUtils.rm_rf(temp_dir)
320
+ raise Fig::RepositoryError.new
321
+ ensure
322
+ FileUtils.rm_rf(temp_dir)
323
+ end
318
324
  end
319
325
 
320
326
  return
@@ -423,6 +429,7 @@ class Fig::Repository
423
429
 
424
430
  def download_assets(package, descriptor, temporary_package, temporary_runtime)
425
431
  remote_package_directory = remote_directory_for_package(descriptor)
432
+
426
433
  package.archive_locations.each do
427
434
  |archive_location|
428
435
 
@@ -431,10 +438,14 @@ class Fig::Repository
431
438
  remote_package_directory, [archive_location]
432
439
  )
433
440
  end
434
- @operating_system.download_and_unpack_archive(
435
- archive_location, temporary_package, temporary_runtime
436
- )
441
+
442
+ Fig::VerboseLogging.time_operation("downloading and unpacking archive #{File.basename(archive_location)}") do
443
+ @operating_system.download_and_unpack_archive(
444
+ archive_location, temporary_package, temporary_runtime
445
+ )
446
+ end
437
447
  end
448
+
438
449
  package.resource_locations.each do
439
450
  |resource_location|
440
451
 
@@ -444,12 +455,14 @@ class Fig::Repository
444
455
  )
445
456
  end
446
457
 
447
- basename, path =
448
- @operating_system.download_resource(
449
- resource_location, temporary_package
450
- )
458
+ Fig::VerboseLogging.time_operation("downloading resource #{File.basename(resource_location)}") do
459
+ basename, path =
460
+ @operating_system.download_resource(
461
+ resource_location, temporary_package
462
+ )
451
463
 
452
- @operating_system.copy path, File.join(temporary_runtime, basename)
464
+ @operating_system.copy path, File.join(temporary_runtime, basename)
465
+ end
453
466
  end
454
467
 
455
468
  return
@@ -15,6 +15,7 @@ require 'fig/statement/override'
15
15
  require 'fig/statement/path'
16
16
  require 'fig/statement/set'
17
17
  require 'fig/user_input_error'
18
+ require 'fig/verbose_logging'
18
19
 
19
20
  module Fig; end
20
21
 
@@ -41,6 +42,7 @@ class Fig::RuntimeEnvironment
41
42
  @retrieves = {}
42
43
  @named_packages = {}
43
44
  @working_directory_maintainer = working_directory_maintainer
45
+ @all_override_statements = []
44
46
  end
45
47
 
46
48
  # Returns the value of an environment variable
@@ -94,6 +96,7 @@ class Fig::RuntimeEnvironment
94
96
  return
95
97
  end
96
98
 
99
+ log_config_processing(package.name, package.version, config_name)
97
100
  Fig::Logging.debug(
98
101
  "Applying #{package.to_descriptive_string_with_config config_name}, package depth #{current_package_depth}."
99
102
  )
@@ -173,6 +176,7 @@ class Fig::RuntimeEnvironment
173
176
  include_file_config(package, statement, backtrace, current_package_depth)
174
177
  when Fig::Statement::Override
175
178
  backtrace.add_override(statement)
179
+ @all_override_statements << statement
176
180
  end
177
181
 
178
182
  return
@@ -193,6 +197,15 @@ class Fig::RuntimeEnvironment
193
197
  end
194
198
  end
195
199
 
200
+ def check_for_unused_overrides()
201
+ @all_override_statements.each do |statement|
202
+ if statement.loaded_but_not_referenced?
203
+ Fig::Logging.warn \
204
+ %Q<Override "#{statement.package_name}/#{statement.version}"#{statement.position_string} was never used.>
205
+ end
206
+ end
207
+ end
208
+
196
209
  private
197
210
 
198
211
  def include_config(
@@ -206,7 +219,9 @@ class Fig::RuntimeEnvironment
206
219
  include_statement.included_package.nil? && @suppress_includes == :all
207
220
 
208
221
  package, resolved_descriptor, new_backtrace =
209
- determine_included_package starting_package, include_statement, backtrace
222
+ determine_included_package starting_package, include_statement, backtrace, current_package_depth
223
+
224
+ log_package_include(resolved_descriptor, current_package_depth)
210
225
 
211
226
  return if
212
227
  @suppress_includes == :cross_package \
@@ -225,13 +240,13 @@ class Fig::RuntimeEnvironment
225
240
  package,
226
241
  resolved_descriptor.config || Fig::Package::DEFAULT_CONFIG,
227
242
  new_backtrace,
228
- next_package_depth,
243
+ next_package_depth
229
244
  )
230
245
 
231
246
  return
232
247
  end
233
248
 
234
- def determine_included_package(starting_package, include_statement, backtrace)
249
+ def determine_included_package(starting_package, include_statement, backtrace, current_package_depth = 0)
235
250
  descriptor = include_statement.descriptor
236
251
 
237
252
  if ! include_statement.included_package.nil?
@@ -250,6 +265,9 @@ class Fig::RuntimeEnvironment
250
265
  )
251
266
  override = backtrace.get_override(override_package_name)
252
267
  if override
268
+ log_override_applied(
269
+ override_package_name, descriptor.version, override, current_package_depth
270
+ )
253
271
  resolved_descriptor =
254
272
  Fig::PackageDescriptor.new(
255
273
  override_package_name, override, descriptor.config
@@ -276,7 +294,6 @@ class Fig::RuntimeEnvironment
276
294
  including_package, include_file_statement, backtrace, current_package_depth
277
295
  )
278
296
  return if @suppress_includes.is_a? Symbol
279
-
280
297
  full_path = include_file_statement.full_path_relative_to including_package
281
298
 
282
299
  descriptor =
@@ -583,4 +600,40 @@ class Fig::RuntimeEnvironment
583
600
  message + ( stacktrace.empty? ? '' : "\n#{stacktrace}" )
584
601
  )
585
602
  end
603
+
604
+ private
605
+
606
+ def log_config_processing(package_name, version, config_name)
607
+ if package_name && !package_name.empty?
608
+ Fig::VerboseLogging.verbose "processing config #{config_name} for package #{package_name}/#{version || '<no version>'}"
609
+ else
610
+ Fig::VerboseLogging.verbose "processing config #{config_name} for command-line package"
611
+ end
612
+ end
613
+
614
+ def log_package_include(package_descriptor, depth)
615
+ # Skip logging for synthetic/unnamed packages at root level
616
+ if depth == 0 && package_descriptor.respond_to?(:name) &&
617
+ (package_descriptor.name.nil? || package_descriptor.name.empty?)
618
+ return
619
+ end
620
+
621
+ indent = " " * depth
622
+ if package_descriptor.respond_to?(:to_string)
623
+ descriptor_string = package_descriptor.to_string
624
+ elsif package_descriptor.respond_to?(:name) && package_descriptor.respond_to?(:version)
625
+ name = package_descriptor.name || "<unnamed>"
626
+ version = package_descriptor.version || "<no version>"
627
+ config = package_descriptor.config || "default"
628
+ descriptor_string = "#{name}/#{version}:#{config}"
629
+ else
630
+ descriptor_string = package_descriptor.to_s
631
+ end
632
+ Fig::VerboseLogging.verbose "#{indent}including package: #{descriptor_string}"
633
+ end
634
+
635
+ def log_override_applied(package_name, original_version, override_version, depth)
636
+ indent = " " * depth
637
+ Fig::VerboseLogging.verbose "#{indent}override applied: #{package_name}/#{original_version} -> #{package_name}/#{override_version}"
638
+ end
586
639
  end
@@ -18,6 +18,14 @@ require 'fig/external_program'
18
18
  require 'fig/figrc'
19
19
  require 'fig/logging'
20
20
  require 'fig/repository'
21
+ require 'fig/verbose_logging'
22
+
23
+ # Reset verbose logging state before each test to prevent test pollution
24
+ RSpec.configure do |config|
25
+ config.before(:each) do
26
+ Fig::VerboseLogging.disable_verbose!
27
+ end
28
+ end
21
29
 
22
30
  FIG_SPEC_BASE_DIRECTORY = Dir.mktmpdir 'fig-rspec-'
23
31
  at_exit { FileUtils.rm_rf FIG_SPEC_BASE_DIRECTORY }
@@ -35,6 +35,26 @@ class Fig::Statement::Override < Fig::Statement
35
35
  @version = version
36
36
  end
37
37
 
38
+ def loaded_but_not_referenced?()
39
+ return added_to_environment? && ! referenced?
40
+ end
41
+
42
+ def added_to_environment?()
43
+ return @added_to_environment
44
+ end
45
+
46
+ def added_to_environment(yea_or_nay)
47
+ @added_to_environment = yea_or_nay
48
+ end
49
+
50
+ def referenced?()
51
+ return @referenced
52
+ end
53
+
54
+ def referenced(yea_or_nay)
55
+ @referenced = yea_or_nay
56
+ end
57
+
38
58
  def statement_type()
39
59
  return 'override'
40
60
  end
@@ -0,0 +1,67 @@
1
+ # coding: utf-8
2
+
3
+ require 'fig/logging'
4
+
5
+ module Fig; end
6
+
7
+ # Core verbose logging infrastructure with timing support
8
+ module Fig::VerboseLogging
9
+ @@verbose_enabled = false
10
+
11
+ def self.enable_verbose!
12
+ @@verbose_enabled = true
13
+ end
14
+
15
+ def self.disable_verbose!
16
+ @@verbose_enabled = false
17
+ end
18
+
19
+ def self.verbose_enabled?
20
+ @@verbose_enabled
21
+ end
22
+
23
+ def self.should_log_verbose?
24
+ @@verbose_enabled || Fig::Logging.debug?
25
+ end
26
+
27
+ def self.verbose(message)
28
+ return unless should_log_verbose?
29
+
30
+ if Fig::Logging.info?
31
+ Fig::Logging.info "[VERBOSE] #{message}"
32
+ else
33
+ # fallback to stderr if logging is completely disabled
34
+ $stderr.puts "[VERBOSE] #{message}" if @@verbose_enabled
35
+ end
36
+ end
37
+
38
+ def self.time_operation(operation_name, &block)
39
+ return yield unless should_log_verbose?
40
+
41
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
42
+ verbose "starting #{operation_name}"
43
+
44
+ begin
45
+ result = yield
46
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
47
+ verbose "completed #{operation_name} in #{format_duration(duration)}"
48
+ result
49
+ rescue => error
50
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
51
+ verbose "failed #{operation_name} after #{format_duration(duration)}: #{error.class.name}: #{error.message}"
52
+ raise
53
+ end
54
+ end
55
+
56
+ def self.format_duration(seconds)
57
+ if seconds < 1.0
58
+ "#{(seconds * 1000).round(1)}ms"
59
+ elsif seconds < 60.0
60
+ "#{seconds.round(2)}s"
61
+ else
62
+ minutes = (seconds / 60).floor
63
+ remaining_seconds = seconds - (minutes * 60)
64
+ "#{minutes}m #{remaining_seconds.round(1)}s"
65
+ end
66
+ end
67
+ end
data/lib/fig/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module Fig
4
- VERSION = '2.0.0-alpha.11'.freeze
4
+ VERSION = '2.0.0-alpha.12'.freeze
5
5
  end
@@ -591,6 +591,18 @@ describe 'Fig' do
591
591
  fig(%w<--update-if-missing --include foo/1.2.3 -- hello.bat>)[0].should ==
592
592
  'cheese'
593
593
  end
594
+
595
+ it 'warns on unused override' do
596
+ input = <<-END
597
+ config default
598
+ override unused-package/1.0.0
599
+ set WHATEVER=SOMETHING
600
+ end
601
+ END
602
+ out, err, exit_code = fig(%w<--update-if-missing>, input)
603
+
604
+ err.should =~ /Override "unused-package\/1\.0\.0".*was never used/
605
+ end
594
606
  end
595
607
  end
596
608
  end
@@ -0,0 +1,170 @@
1
+ # coding: utf-8
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ require 'fig/verbose_logging'
6
+ require 'fig/logging'
7
+
8
+ describe 'Fig::VerboseLogging' do
9
+ before(:each) do
10
+ # Reset verbose state and capture output
11
+ Fig::VerboseLogging.class_variable_set(:@@verbose_enabled, false)
12
+ @log_output = StringIO.new
13
+ @stderr_output = StringIO.new
14
+
15
+ allow(Fig::Logging).to receive(:info) do |message|
16
+ @log_output.puts(message) if Fig::Logging.info?
17
+ end
18
+
19
+ allow($stderr).to receive(:puts) do |message|
20
+ @stderr_output.puts(message)
21
+ end
22
+ end
23
+
24
+ describe '.time_operation' do
25
+ it 'logs timing when verbose is enabled' do
26
+ Fig::VerboseLogging.enable_verbose!
27
+ allow(Fig::Logging).to receive(:info?).and_return(true)
28
+
29
+ result = Fig::VerboseLogging.time_operation('test operation') do
30
+ sleep(0.01) # Small delay to ensure measurable time
31
+ 'test result'
32
+ end
33
+
34
+ expect(result).to eq('test result')
35
+ log_content = @log_output.string
36
+ expect(log_content).to include('[VERBOSE] starting test operation')
37
+ expect(log_content).to include('[VERBOSE] completed test operation in')
38
+ expect(log_content).to match(/\d+(\.\d+)?ms/)
39
+ end
40
+
41
+ it 'logs timing when debug logging is enabled (even without verbose flag)' do
42
+ allow(Fig::Logging).to receive(:debug?).and_return(true)
43
+ allow(Fig::Logging).to receive(:info?).and_return(true)
44
+
45
+ result = Fig::VerboseLogging.time_operation('test operation') do
46
+ 'test result'
47
+ end
48
+
49
+ expect(result).to eq('test result')
50
+ log_content = @log_output.string
51
+ expect(log_content).to include('[VERBOSE] starting test operation')
52
+ end
53
+
54
+ it 'does not log when neither verbose nor debug is enabled' do
55
+ allow(Fig::Logging).to receive(:debug?).and_return(false)
56
+ allow(Fig::Logging).to receive(:info?).and_return(true)
57
+
58
+ result = Fig::VerboseLogging.time_operation('test operation') do
59
+ 'test result'
60
+ end
61
+
62
+ expect(result).to eq('test result')
63
+ expect(@log_output.string).to be_empty
64
+ end
65
+
66
+ it 'falls back to stderr when logging is disabled but verbose is enabled' do
67
+ Fig::VerboseLogging.enable_verbose!
68
+ allow(Fig::Logging).to receive(:info?).and_return(false)
69
+
70
+ result = Fig::VerboseLogging.time_operation('test operation') do
71
+ 'test result'
72
+ end
73
+
74
+ expect(result).to eq('test result')
75
+ stderr_content = @stderr_output.string
76
+ expect(stderr_content).to include('[VERBOSE] starting test operation')
77
+ end
78
+
79
+ it 'logs failure timing on exceptions' do
80
+ Fig::VerboseLogging.enable_verbose!
81
+ allow(Fig::Logging).to receive(:info?).and_return(true)
82
+
83
+ expect do
84
+ Fig::VerboseLogging.time_operation('failing operation') do
85
+ raise StandardError, 'test error'
86
+ end
87
+ end.to raise_error(StandardError, 'test error')
88
+
89
+ log_content = @log_output.string
90
+ expect(log_content).to include('[VERBOSE] starting failing operation')
91
+ expect(log_content).to include('[VERBOSE] failed failing operation after')
92
+ expect(log_content).to include('StandardError: test error')
93
+ end
94
+ end
95
+
96
+ describe '.verbose' do
97
+ it 'logs messages when verbose is enabled' do
98
+ Fig::VerboseLogging.enable_verbose!
99
+ allow(Fig::Logging).to receive(:info?).and_return(true)
100
+
101
+ Fig::VerboseLogging.verbose('test message')
102
+
103
+ log_content = @log_output.string
104
+ expect(log_content).to include('[VERBOSE] test message')
105
+ end
106
+
107
+ it 'logs messages when debug logging is enabled (even without verbose flag)' do
108
+ allow(Fig::Logging).to receive(:debug?).and_return(true)
109
+ allow(Fig::Logging).to receive(:info?).and_return(true)
110
+
111
+ Fig::VerboseLogging.verbose('test message')
112
+
113
+ log_content = @log_output.string
114
+ expect(log_content).to include('[VERBOSE] test message')
115
+ end
116
+
117
+ it 'does not log when neither verbose nor debug is enabled' do
118
+ allow(Fig::Logging).to receive(:debug?).and_return(false)
119
+ allow(Fig::Logging).to receive(:info?).and_return(true)
120
+
121
+ Fig::VerboseLogging.verbose('test message')
122
+
123
+ expect(@log_output.string).to be_empty
124
+ end
125
+
126
+ it 'falls back to stderr when logging is disabled but verbose is enabled' do
127
+ Fig::VerboseLogging.enable_verbose!
128
+ allow(Fig::Logging).to receive(:info?).and_return(false)
129
+
130
+ Fig::VerboseLogging.verbose('test message')
131
+
132
+ stderr_content = @stderr_output.string
133
+ expect(stderr_content).to include('[VERBOSE] test message')
134
+ end
135
+ end
136
+
137
+ describe '.should_log_verbose?' do
138
+ it 'returns true when verbose is enabled' do
139
+ Fig::VerboseLogging.enable_verbose!
140
+ expect(Fig::VerboseLogging.should_log_verbose?).to be true
141
+ end
142
+
143
+ it 'returns true when debug logging is enabled' do
144
+ allow(Fig::Logging).to receive(:debug?).and_return(true)
145
+ expect(Fig::VerboseLogging.should_log_verbose?).to be true
146
+ end
147
+
148
+ it 'returns false when neither verbose nor debug is enabled' do
149
+ allow(Fig::Logging).to receive(:debug?).and_return(false)
150
+ expect(Fig::VerboseLogging.should_log_verbose?).to be false
151
+ end
152
+ end
153
+
154
+ describe 'format helpers' do
155
+ it 'formats durations correctly' do
156
+ # Test private method via time_operation
157
+ Fig::VerboseLogging.enable_verbose!
158
+ allow(Fig::Logging).to receive(:info?).and_return(true)
159
+
160
+ # Mock Process.clock_gettime to control duration
161
+ start_time = 1000.0
162
+ allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(start_time, start_time + 0.5)
163
+
164
+ Fig::VerboseLogging.time_operation('test') { 'result' }
165
+
166
+ log_content = @log_output.string
167
+ expect(log_content).to include('500.0ms') # 0.5 seconds formatted as milliseconds
168
+ end
169
+ end
170
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fig
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre.alpha.11
4
+ version: 2.0.0.pre.alpha.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fig Folks
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-25 00:00:00.000000000 Z
10
+ date: 2025-10-07 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bcrypt_pbkdf
@@ -334,7 +334,7 @@ dependencies:
334
334
  description: |-
335
335
  Fig is a utility for configuring environments and managing dependencies across a team of developers. Given a list of packages and a command to run, Fig builds environment variables named in those packages (e.g., CLASSPATH), then executes the command in that environment. The caller's environment is not affected.
336
336
 
337
- Built from git SHA1: 23313c3-dirty
337
+ Built from git SHA1: f7a4d2d-dirty
338
338
  email: maintainer@figpackagemanager.org
339
339
  executables:
340
340
  - fig
@@ -501,6 +501,7 @@ files:
501
501
  - lib/fig/url.rb
502
502
  - lib/fig/url_access_disallowed_error.rb
503
503
  - lib/fig/user_input_error.rb
504
+ - lib/fig/verbose_logging.rb
504
505
  - lib/fig/version.rb
505
506
  - lib/fig/working_directory_maintainer.rb
506
507
  - lib/fig/working_directory_metadata.rb
@@ -541,11 +542,12 @@ files:
541
542
  - spec/statement/asset_spec.rb
542
543
  - spec/statement/configuration_spec.rb
543
544
  - spec/support/formatters/seed_spitter.rb
545
+ - spec/verbose_logging_spec.rb
544
546
  - spec/working_directory_maintainer_spec.rb
545
547
  licenses:
546
548
  - BSD-3-Clause
547
549
  metadata:
548
- git_sha: 23313c3-dirty
550
+ git_sha: f7a4d2d-dirty
549
551
  rdoc_options: []
550
552
  require_paths:
551
553
  - lib
@@ -563,5 +565,5 @@ requirements: []
563
565
  rubygems_version: 3.6.1
564
566
  specification_version: 4
565
567
  summary: 'Utility for configuring environments and managing dependencies across a
566
- team of developers. Built from git SHA1: 23313c3-dirty'
568
+ team of developers. Built from git SHA1: f7a4d2d-dirty'
567
569
  test_files: []