makit 0.0.139 → 0.0.140

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -41
  3. data/exe/makit +5 -5
  4. data/lib/makit/apache.rb +28 -28
  5. data/lib/makit/auto.rb +48 -48
  6. data/lib/makit/cli/build_commands.rb +500 -500
  7. data/lib/makit/cli/generators/base_generator.rb +74 -74
  8. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  9. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  10. data/lib/makit/cli/generators/node_generator.rb +50 -50
  11. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  12. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  13. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  14. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  15. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  16. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
  17. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  18. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  19. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  20. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  21. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  22. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  23. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  24. data/lib/makit/cli/main.rb +69 -67
  25. data/lib/makit/cli/project_commands.rb +868 -868
  26. data/lib/makit/cli/repository_commands.rb +661 -661
  27. data/lib/makit/cli/strategy_commands.rb +51 -0
  28. data/lib/makit/cli/utility_commands.rb +521 -521
  29. data/lib/makit/commands/factory.rb +359 -359
  30. data/lib/makit/commands/middleware/base.rb +73 -73
  31. data/lib/makit/commands/middleware/cache.rb +248 -248
  32. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  33. data/lib/makit/commands/middleware/validator.rb +269 -269
  34. data/lib/makit/commands/request.rb +316 -316
  35. data/lib/makit/commands/result.rb +323 -323
  36. data/lib/makit/commands/runner.rb +385 -385
  37. data/lib/makit/commands/strategies/base.rb +171 -171
  38. data/lib/makit/commands/strategies/child_process.rb +1 -1
  39. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  40. data/lib/makit/commands.rb +50 -50
  41. data/lib/makit/configuration/dotnet_project.rb +12 -12
  42. data/lib/makit/configuration/gitlab_helper.rb +58 -58
  43. data/lib/makit/configuration/project.rb +168 -168
  44. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  45. data/lib/makit/configuration/step.rb +34 -34
  46. data/lib/makit/configuration/timeout.rb +74 -0
  47. data/lib/makit/configuration.rb +15 -14
  48. data/lib/makit/content/default_gitignore.rb +7 -7
  49. data/lib/makit/content/default_gitignore.txt +225 -225
  50. data/lib/makit/content/default_rakefile.rb +13 -13
  51. data/lib/makit/content/gem_rakefile.rb +16 -16
  52. data/lib/makit/context.rb +1 -1
  53. data/lib/makit/data.rb +49 -49
  54. data/lib/makit/directories.rb +140 -140
  55. data/lib/makit/directory.rb +262 -262
  56. data/lib/makit/docs/files.rb +89 -89
  57. data/lib/makit/docs/rake.rb +102 -102
  58. data/lib/makit/dotnet/cli.rb +69 -69
  59. data/lib/makit/dotnet/project.rb +217 -217
  60. data/lib/makit/dotnet/solution.rb +38 -38
  61. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  62. data/lib/makit/dotnet/solution_console.rb +264 -264
  63. data/lib/makit/dotnet/solution_maui.rb +354 -354
  64. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  65. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  66. data/lib/makit/dotnet.rb +102 -102
  67. data/lib/makit/email.rb +90 -90
  68. data/lib/makit/environment.rb +142 -142
  69. data/lib/makit/examples/runner.rb +370 -370
  70. data/lib/makit/exceptions.rb +45 -45
  71. data/lib/makit/fileinfo.rb +24 -24
  72. data/lib/makit/files.rb +43 -43
  73. data/lib/makit/gems.rb +40 -40
  74. data/lib/makit/git/cli.rb +54 -54
  75. data/lib/makit/git/repository.rb +90 -90
  76. data/lib/makit/git.rb +98 -98
  77. data/lib/makit/gitlab_runner.rb +59 -59
  78. data/lib/makit/humanize.rb +137 -137
  79. data/lib/makit/indexer.rb +47 -47
  80. data/lib/makit/logging/configuration.rb +308 -308
  81. data/lib/makit/logging/format_registry.rb +84 -84
  82. data/lib/makit/logging/formatters/base.rb +39 -39
  83. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  84. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  85. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  86. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  87. data/lib/makit/logging/log_request.rb +119 -119
  88. data/lib/makit/logging/logger.rb +199 -199
  89. data/lib/makit/logging/sinks/base.rb +91 -91
  90. data/lib/makit/logging/sinks/console.rb +72 -72
  91. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  92. data/lib/makit/logging/sinks/structured.rb +123 -123
  93. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  94. data/lib/makit/logging.rb +565 -565
  95. data/lib/makit/markdown.rb +75 -75
  96. data/lib/makit/mp/basic_object_mp.rb +17 -17
  97. data/lib/makit/mp/command_mp.rb +13 -13
  98. data/lib/makit/mp/command_request.mp.rb +17 -17
  99. data/lib/makit/mp/project_mp.rb +199 -199
  100. data/lib/makit/mp/string_mp.rb +191 -191
  101. data/lib/makit/nuget.rb +74 -74
  102. data/lib/makit/port.rb +32 -32
  103. data/lib/makit/process.rb +163 -163
  104. data/lib/makit/protoc.rb +107 -107
  105. data/lib/makit/rake/cli.rb +196 -196
  106. data/lib/makit/rake.rb +80 -80
  107. data/lib/makit/ruby/cli.rb +185 -185
  108. data/lib/makit/ruby.rb +25 -25
  109. data/lib/makit/secrets.rb +51 -51
  110. data/lib/makit/serializer.rb +130 -130
  111. data/lib/makit/services/builder.rb +186 -186
  112. data/lib/makit/services/error_handler.rb +226 -226
  113. data/lib/makit/services/repository_manager.rb +231 -231
  114. data/lib/makit/services/validator.rb +112 -112
  115. data/lib/makit/setup/classlib.rb +101 -101
  116. data/lib/makit/setup/gem.rb +268 -268
  117. data/lib/makit/setup/razorclasslib.rb +101 -101
  118. data/lib/makit/setup/runner.rb +54 -54
  119. data/lib/makit/setup.rb +5 -5
  120. data/lib/makit/show.rb +110 -110
  121. data/lib/makit/storage.rb +126 -126
  122. data/lib/makit/symbols.rb +170 -170
  123. data/lib/makit/task_info.rb +130 -130
  124. data/lib/makit/tasks/at_exit.rb +15 -15
  125. data/lib/makit/tasks/build.rb +22 -22
  126. data/lib/makit/tasks/clean.rb +13 -13
  127. data/lib/makit/tasks/configure.rb +10 -10
  128. data/lib/makit/tasks/format.rb +10 -10
  129. data/lib/makit/tasks/hook_manager.rb +443 -443
  130. data/lib/makit/tasks/init.rb +49 -49
  131. data/lib/makit/tasks/integrate.rb +29 -29
  132. data/lib/makit/tasks/pull_incoming.rb +13 -13
  133. data/lib/makit/tasks/setup.rb +13 -13
  134. data/lib/makit/tasks/sync.rb +17 -17
  135. data/lib/makit/tasks/tag.rb +16 -16
  136. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  137. data/lib/makit/tasks/test.rb +22 -22
  138. data/lib/makit/tasks/update.rb +18 -18
  139. data/lib/makit/tasks.rb +20 -20
  140. data/lib/makit/test_cache.rb +239 -239
  141. data/lib/makit/tree.rb +37 -37
  142. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  143. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  144. data/lib/makit/version.rb +99 -99
  145. data/lib/makit/version_util.rb +21 -21
  146. data/lib/makit/wix.rb +95 -95
  147. data/lib/makit/yaml.rb +29 -29
  148. data/lib/makit/zip.rb +17 -17
  149. data/lib/makit copy.rb +44 -44
  150. data/lib/makit.rb +42 -42
  151. metadata +2 -1
@@ -1,521 +1,521 @@
1
- # frozen_string_literal: true
2
-
3
- require "English"
4
- require "clamp"
5
- require "fileutils"
6
-
7
- module Makit
8
- module Cli
9
- # Consolidated utility commands
10
- # Combines: nuget_cache and other utility functionality
11
- class UtilityCommand < Clamp::Command
12
- self.description = <<~DESC
13
- Utility commands for system maintenance and development tools.
14
-
15
- Available operations:
16
- nuget-cache - Manage NuGet package cache
17
- system-info - Display system and environment information
18
- DESC
19
- end
20
-
21
- # Manage NuGet package cache
22
- class NugetCacheCommand < Clamp::Command
23
- self.description = <<~DESC
24
- Manage NuGet package cache and troubleshoot package issues.
25
-
26
- Examples:
27
- makit utility nuget-cache --clear
28
- makit utility nuget-cache --list
29
- makit utility nuget-cache --verify
30
- DESC
31
-
32
- option ["--clear"], :flag, "Clear all NuGet caches"
33
- option ["--list"], :flag, "List NuGet cache locations"
34
- option ["--verify"], :flag, "Verify cache integrity"
35
- option ["--cache-type"], "TYPE", "Specific cache to target (global-packages, http-cache, temp, plugins-cache)"
36
- option ["--dry-run"], :flag, "Show what would be done without actually doing it"
37
- option ["--verbose"], :flag, "Show verbose output"
38
-
39
- def execute
40
- unless nuget_available?
41
- puts "Error: NuGet CLI is not available. Please install .NET SDK or NuGet CLI tools."
42
- exit 1
43
- end
44
-
45
- if list?
46
- list_cache_locations
47
- elsif verify?
48
- verify_cache_integrity
49
- elsif clear?
50
- clear_nuget_caches
51
- else
52
- show_cache_status
53
- end
54
- end
55
-
56
- private
57
-
58
- def nuget_available?
59
- system("dotnet --version > /dev/null 2>&1") || system("nuget help > /dev/null 2>&1")
60
- end
61
-
62
- def show_cache_status
63
- puts "NuGet Cache Status:"
64
- puts "=" * 50
65
-
66
- cache_locations = get_cache_locations
67
- cache_locations.each do |name, path|
68
- if Dir.exist?(path)
69
- size = calculate_directory_size(path)
70
- puts "✅ #{name.ljust(20)} #{path} (#{format_size(size)})"
71
- else
72
- puts "❌ #{name.ljust(20)} #{path} (not found)"
73
- end
74
- end
75
-
76
- puts "\nUse --clear to clear all caches, or --list for detailed information."
77
- end
78
-
79
- def list_cache_locations
80
- puts "NuGet Cache Locations:"
81
- puts "=" * 50
82
-
83
- cache_locations = get_cache_locations
84
- cache_locations.each do |name, path|
85
- puts "\n#{name}:"
86
- puts " Path: #{path}"
87
-
88
- if Dir.exist?(path)
89
- size = calculate_directory_size(path)
90
- file_count = count_files_in_directory(path)
91
- puts " Status: ✅ Exists"
92
- puts " Size: #{format_size(size)}"
93
- puts " Files: #{file_count}"
94
-
95
- if verbose?
96
- recent_files = find_recent_files(path, 5)
97
- if recent_files.any?
98
- puts " Recent files:"
99
- recent_files.each { |file| puts " - #{File.basename(file)}" }
100
- end
101
- end
102
- else
103
- puts " Status: ❌ Not found"
104
- end
105
- end
106
- end
107
-
108
- def verify_cache_integrity
109
- puts "Verifying NuGet cache integrity..."
110
-
111
- if system("dotnet --version > /dev/null 2>&1")
112
- puts " Running: dotnet nuget verify"
113
- success = system("dotnet nuget verify")
114
-
115
- if success
116
- puts "✅ Cache verification completed successfully"
117
- else
118
- puts "❌ Cache verification found issues"
119
- puts "Consider clearing the cache with --clear"
120
- end
121
- else
122
- puts "Manual verification of cache locations..."
123
- cache_locations = get_cache_locations
124
-
125
- cache_locations.each do |name, path|
126
- next unless Dir.exist?(path)
127
-
128
- puts " Checking #{name}..."
129
-
130
- # Basic checks for common corruption indicators
131
- corrupt_files = find_corrupt_files(path)
132
- if corrupt_files.any?
133
- puts " ⚠️ Found #{corrupt_files.length} potentially corrupt files"
134
- corrupt_files.first(3).each { |file| puts " - #{file}" }
135
- puts " ... and #{corrupt_files.length - 3} more" if corrupt_files.length > 3
136
- else
137
- puts " ✅ No obvious corruption detected"
138
- end
139
- end
140
- end
141
- end
142
-
143
- def clear_nuget_caches
144
- if dry_run?
145
- puts "DRY RUN: Would clear the following NuGet caches:"
146
- else
147
- puts "Clearing NuGet caches..."
148
- end
149
-
150
- if cache_type
151
- clear_specific_cache(cache_type)
152
- else
153
- clear_all_caches
154
- end
155
-
156
- if dry_run?
157
- puts "✅ Dry run completed - no caches were actually cleared"
158
- else
159
- puts "✅ NuGet caches cleared successfully"
160
- end
161
- end
162
-
163
- def clear_specific_cache(type)
164
- cache_locations = get_cache_locations
165
- path = cache_locations[type]
166
-
167
- if path.nil?
168
- puts "Error: Unknown cache type '#{type}'"
169
- puts "Valid types: #{cache_locations.keys.join(", ")}"
170
- exit 1
171
- end
172
-
173
- clear_cache_directory(type, path)
174
- end
175
-
176
- def clear_all_caches
177
- # Try using dotnet CLI first
178
- if system("dotnet --version > /dev/null 2>&1") && !cache_type
179
- puts " Using dotnet nuget locals --clear all" if verbose?
180
-
181
- if dry_run?
182
- puts " Would run: dotnet nuget locals --clear all"
183
- else
184
- success = system("dotnet nuget locals --clear all")
185
- if success
186
- puts " ✅ Cleared all caches using dotnet CLI"
187
- return
188
- else
189
- puts " ⚠️ dotnet command failed, falling back to manual cleanup"
190
- end
191
- end
192
- end
193
-
194
- # Manual cleanup
195
- cache_locations = get_cache_locations
196
- cache_locations.each do |name, path|
197
- clear_cache_directory(name, path)
198
- end
199
- end
200
-
201
- def clear_cache_directory(name, path)
202
- if Dir.exist?(path)
203
- size_before = calculate_directory_size(path)
204
-
205
- if dry_run?
206
- puts " Would clear #{name}: #{path} (#{format_size(size_before)})"
207
- else
208
- puts " Clearing #{name}: #{path} (#{format_size(size_before)})" if verbose?
209
-
210
- begin
211
- FileUtils.rm_rf(path)
212
- FileUtils.mkdir_p(path) # Recreate the directory
213
- puts " ✅ Cleared #{name}" if verbose?
214
- rescue StandardError => e
215
- puts " ❌ Failed to clear #{name}: #{e.message}"
216
- end
217
- end
218
- elsif verbose?
219
- puts " #{name} not found: #{path}"
220
- end
221
- end
222
-
223
- def get_cache_locations
224
- # Standard NuGet cache locations
225
- home = Dir.home
226
-
227
- locations = {
228
- "global-packages" => File.join(home, ".nuget", "packages"),
229
- "http-cache" => File.join(home, ".nuget", "v3-cache"),
230
- "temp" => File.join(home, ".nuget", "temp"),
231
- "plugins-cache" => File.join(home, ".nuget", "plugins-cache"),
232
- }
233
-
234
- # Add Windows-specific locations if on Windows
235
- if Gem.win_platform?
236
- ENV["APPDATA"] || File.join(home, "AppData", "Roaming")
237
- local_app_data = ENV["LOCALAPPDATA"] || File.join(home, "AppData", "Local")
238
-
239
- locations["http-cache"] = File.join(local_app_data, "NuGet", "v3-cache")
240
- locations["plugins-cache"] = File.join(local_app_data, "NuGet", "plugins-cache")
241
- end
242
-
243
- locations
244
- end
245
-
246
- def calculate_directory_size(path)
247
- return 0 unless Dir.exist?(path)
248
-
249
- total_size = 0
250
- Dir.glob(File.join(path, "**", "*")).each do |file|
251
- total_size += File.size(file) if File.file?(file)
252
- end
253
- total_size
254
- end
255
-
256
- def count_files_in_directory(path)
257
- return 0 unless Dir.exist?(path)
258
-
259
- Dir.glob(File.join(path, "**", "*")).count { |f| File.file?(f) }
260
- end
261
-
262
- def format_size(bytes)
263
- units = %w[B KB MB GB TB]
264
- size = bytes.to_f
265
- unit_index = 0
266
-
267
- while size >= 1024 && unit_index < units.length - 1
268
- size /= 1024.0
269
- unit_index += 1
270
- end
271
-
272
- format("%.1f %s", size, units[unit_index])
273
- end
274
-
275
- def find_recent_files(path, limit)
276
- return [] unless Dir.exist?(path)
277
-
278
- Dir.glob(File.join(path, "**", "*"))
279
- .select { |f| File.file?(f) }
280
- .sort_by { |f| File.mtime(f) }
281
- .reverse
282
- .first(limit)
283
- end
284
-
285
- def find_corrupt_files(path)
286
- return [] unless Dir.exist?(path)
287
-
288
- corrupt_files = []
289
-
290
- Dir.glob(File.join(path, "**", "*")).each do |file|
291
- next unless File.file?(file)
292
-
293
- # Check for zero-byte files that shouldn't be empty
294
- corrupt_files << file if File.empty?(file) && File.extname(file) =~ /\.(dll|exe|nupkg)$/
295
-
296
- # Check for incomplete downloads (common with network issues)
297
- corrupt_files << file if File.basename(file) =~ /\.tmp$/ || File.basename(file) =~ /\.partial$/
298
- end
299
-
300
- corrupt_files
301
- end
302
- end
303
-
304
- # Display system and environment information
305
- class SystemInfoCommand < Clamp::Command
306
- self.description = <<~DESC
307
- Display system information, environment details, and development tool versions.
308
-
309
- Examples:
310
- makit utility system-info
311
- makit utility system-info --tools-only
312
- makit utility system-info --export
313
- DESC
314
-
315
- option ["--tools-only"], :flag, "Show only development tool versions"
316
- option ["--export"], :flag, "Export information to system-info.txt"
317
- option ["--verbose"], :flag, "Show detailed information"
318
-
319
- def execute
320
- info = collect_system_info
321
-
322
- if export?
323
- export_system_info(info)
324
- else
325
- display_system_info(info)
326
- end
327
- end
328
-
329
- private
330
-
331
- def collect_system_info
332
- info = {}
333
-
334
- unless tools_only?
335
- info[:system] = collect_system_details
336
- info[:ruby] = collect_ruby_info
337
- end
338
-
339
- info[:tools] = collect_development_tools
340
-
341
- info
342
- end
343
-
344
- def collect_system_details
345
- {
346
- os: RbConfig::CONFIG["host_os"],
347
- arch: RbConfig::CONFIG["host_cpu"],
348
- user: ENV["USER"] || ENV.fetch("USERNAME", nil),
349
- shell: ENV["SHELL"] || "Unknown",
350
- pwd: Dir.pwd,
351
- home: Dir.home,
352
- }
353
- end
354
-
355
- def collect_ruby_info
356
- {
357
- version: RUBY_VERSION,
358
- patchlevel: RUBY_PATCHLEVEL,
359
- platform: RUBY_PLATFORM,
360
- engine: RUBY_ENGINE,
361
- gem_version: Gem::VERSION,
362
- bundler_version: get_command_version("bundle --version"),
363
- }
364
- end
365
-
366
- def collect_development_tools
367
- tools = {}
368
-
369
- # Version control
370
- tools[:git] = get_command_version("git --version")
371
-
372
- # Package managers
373
- tools[:npm] = get_command_version("npm --version")
374
- tools[:yarn] = get_command_version("yarn --version")
375
- tools[:pip] = get_command_version("pip --version")
376
-
377
- # Runtime environments
378
- tools[:node] = get_command_version("node --version")
379
- tools[:python] = get_command_version("python --version")
380
- tools[:python3] = get_command_version("python3 --version")
381
-
382
- # Build tools
383
- tools[:make] = get_command_version("make --version")
384
- tools[:cmake] = get_command_version("cmake --version")
385
-
386
- # Language-specific tools
387
- tools[:dotnet] = get_command_version("dotnet --version")
388
- tools[:nuget] = get_command_version("nuget help") # Different format
389
- tools[:cargo] = get_command_version("cargo --version")
390
- tools[:rustc] = get_command_version("rustc --version")
391
- tools[:go] = get_command_version("go version")
392
- tools[:java] = get_command_version("java -version")
393
- tools[:javac] = get_command_version("javac -version")
394
-
395
- # Editors (just check availability)
396
- tools[:code] = command_available?("code --version") ? "Available" : nil
397
- tools[:subl] = command_available?("subl --version") ? "Available" : nil
398
- tools[:vim] = get_command_version("vim --version")
399
- tools[:emacs] = get_command_version("emacs --version")
400
-
401
- # Container tools
402
- tools[:docker] = get_command_version("docker --version")
403
- tools[:podman] = get_command_version("podman --version")
404
-
405
- tools.compact
406
- end
407
-
408
- def get_command_version(command)
409
- output = `#{command} 2>&1`.strip
410
- return nil if $CHILD_STATUS.exitstatus != 0
411
-
412
- # Extract version number from output
413
- version_match = output.match(/(?:v)?(\d+\.[\d.]+(?:-[\w.]+)?)/)
414
- version_match ? version_match[1] : output.split("\n").first
415
- rescue StandardError
416
- nil
417
- end
418
-
419
- def command_available?(command)
420
- system("#{command} > /dev/null 2>&1")
421
- end
422
-
423
- def display_system_info(info)
424
- unless tools_only?
425
- display_system_section(info[:system])
426
- display_ruby_section(info[:ruby])
427
- end
428
-
429
- display_tools_section(info[:tools])
430
- end
431
-
432
- def display_system_section(system_info)
433
- puts "System Information:"
434
- puts "=" * 50
435
- puts "OS: #{system_info[:os]}"
436
- puts "Architecture: #{system_info[:arch]}"
437
- puts "User: #{system_info[:user]}"
438
- puts "Shell: #{system_info[:shell]}" if system_info[:shell] != "Unknown"
439
- puts "Working Directory: #{system_info[:pwd]}"
440
- puts "Home Directory: #{system_info[:home]}"
441
- puts
442
- end
443
-
444
- def display_ruby_section(ruby_info)
445
- puts "Ruby Environment:"
446
- puts "=" * 50
447
- puts "Ruby Version: #{ruby_info[:version]} (patchlevel #{ruby_info[:patchlevel]})"
448
- puts "Platform: #{ruby_info[:platform]}"
449
- puts "Engine: #{ruby_info[:engine]}"
450
- puts "RubyGems Version: #{ruby_info[:gem_version]}"
451
- puts "Bundler Version: #{ruby_info[:bundler_version]}" if ruby_info[:bundler_version]
452
- puts
453
- end
454
-
455
- def display_tools_section(tools_info)
456
- puts "Development Tools:"
457
- puts "=" * 50
458
-
459
- categories = {
460
- "Version Control" => [:git],
461
- "Package Managers" => %i[npm yarn pip],
462
- "Runtime Environments" => %i[node python python3 dotnet java],
463
- "Build Tools" => %i[make cmake cargo rustc go javac],
464
- "Editors" => %i[code subl vim emacs],
465
- "Container Tools" => %i[docker podman],
466
- }
467
-
468
- categories.each do |category, tool_keys|
469
- category_tools = tool_keys.map { |key| [key, tools_info[key]] }.select { |_, version| version }
470
-
471
- next if category_tools.empty?
472
-
473
- puts "#{category}:"
474
- category_tools.each do |tool, version|
475
- puts " #{tool.to_s.ljust(15)} #{version}"
476
- end
477
- puts
478
- end
479
-
480
- # Show any remaining tools not categorized
481
- remaining_tools = tools_info.except(*categories.values.flatten)
482
- return unless remaining_tools.any?
483
-
484
- puts "Other Tools:"
485
- remaining_tools.each do |tool, version|
486
- puts " #{tool.to_s.ljust(15)} #{version}"
487
- end
488
- end
489
-
490
- def export_system_info(info)
491
- filename = "system-info.txt"
492
-
493
- File.open(filename, "w") do |file|
494
- file.puts "System Information Export"
495
- file.puts "Generated: #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
496
- file.puts "=" * 60
497
- file.puts
498
-
499
- unless tools_only?
500
- file.puts "SYSTEM DETAILS:"
501
- info[:system].each { |key, value| file.puts "#{key}: #{value}" }
502
- file.puts
503
-
504
- file.puts "RUBY ENVIRONMENT:"
505
- info[:ruby].each { |key, value| file.puts "#{key}: #{value}" }
506
- file.puts
507
- end
508
-
509
- file.puts "DEVELOPMENT TOOLS:"
510
- info[:tools].each { |tool, version| file.puts "#{tool}: #{version}" }
511
- end
512
-
513
- puts "✅ System information exported to: #{filename}"
514
- end
515
- end
516
-
517
- # Add subcommand declarations after all classes are defined
518
- UtilityCommand.subcommand "nuget-cache", "Manage NuGet package cache", NugetCacheCommand
519
- UtilityCommand.subcommand "system-info", "Display system information", SystemInfoCommand
520
- end
521
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+ require "clamp"
5
+ require "fileutils"
6
+
7
+ module Makit
8
+ module Cli
9
+ # Consolidated utility commands
10
+ # Combines: nuget_cache and other utility functionality
11
+ class UtilityCommand < Clamp::Command
12
+ self.description = <<~DESC
13
+ Utility commands for system maintenance and development tools.
14
+
15
+ Available operations:
16
+ nuget-cache - Manage NuGet package cache
17
+ system-info - Display system and environment information
18
+ DESC
19
+ end
20
+
21
+ # Manage NuGet package cache
22
+ class NugetCacheCommand < Clamp::Command
23
+ self.description = <<~DESC
24
+ Manage NuGet package cache and troubleshoot package issues.
25
+
26
+ Examples:
27
+ makit utility nuget-cache --clear
28
+ makit utility nuget-cache --list
29
+ makit utility nuget-cache --verify
30
+ DESC
31
+
32
+ option ["--clear"], :flag, "Clear all NuGet caches"
33
+ option ["--list"], :flag, "List NuGet cache locations"
34
+ option ["--verify"], :flag, "Verify cache integrity"
35
+ option ["--cache-type"], "TYPE", "Specific cache to target (global-packages, http-cache, temp, plugins-cache)"
36
+ option ["--dry-run"], :flag, "Show what would be done without actually doing it"
37
+ option ["--verbose"], :flag, "Show verbose output"
38
+
39
+ def execute
40
+ unless nuget_available?
41
+ puts "Error: NuGet CLI is not available. Please install .NET SDK or NuGet CLI tools."
42
+ exit 1
43
+ end
44
+
45
+ if list?
46
+ list_cache_locations
47
+ elsif verify?
48
+ verify_cache_integrity
49
+ elsif clear?
50
+ clear_nuget_caches
51
+ else
52
+ show_cache_status
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def nuget_available?
59
+ system("dotnet --version > /dev/null 2>&1") || system("nuget help > /dev/null 2>&1")
60
+ end
61
+
62
+ def show_cache_status
63
+ puts "NuGet Cache Status:"
64
+ puts "=" * 50
65
+
66
+ cache_locations = get_cache_locations
67
+ cache_locations.each do |name, path|
68
+ if Dir.exist?(path)
69
+ size = calculate_directory_size(path)
70
+ puts "✅ #{name.ljust(20)} #{path} (#{format_size(size)})"
71
+ else
72
+ puts "❌ #{name.ljust(20)} #{path} (not found)"
73
+ end
74
+ end
75
+
76
+ puts "\nUse --clear to clear all caches, or --list for detailed information."
77
+ end
78
+
79
+ def list_cache_locations
80
+ puts "NuGet Cache Locations:"
81
+ puts "=" * 50
82
+
83
+ cache_locations = get_cache_locations
84
+ cache_locations.each do |name, path|
85
+ puts "\n#{name}:"
86
+ puts " Path: #{path}"
87
+
88
+ if Dir.exist?(path)
89
+ size = calculate_directory_size(path)
90
+ file_count = count_files_in_directory(path)
91
+ puts " Status: ✅ Exists"
92
+ puts " Size: #{format_size(size)}"
93
+ puts " Files: #{file_count}"
94
+
95
+ if verbose?
96
+ recent_files = find_recent_files(path, 5)
97
+ if recent_files.any?
98
+ puts " Recent files:"
99
+ recent_files.each { |file| puts " - #{File.basename(file)}" }
100
+ end
101
+ end
102
+ else
103
+ puts " Status: ❌ Not found"
104
+ end
105
+ end
106
+ end
107
+
108
+ def verify_cache_integrity
109
+ puts "Verifying NuGet cache integrity..."
110
+
111
+ if system("dotnet --version > /dev/null 2>&1")
112
+ puts " Running: dotnet nuget verify"
113
+ success = system("dotnet nuget verify")
114
+
115
+ if success
116
+ puts "✅ Cache verification completed successfully"
117
+ else
118
+ puts "❌ Cache verification found issues"
119
+ puts "Consider clearing the cache with --clear"
120
+ end
121
+ else
122
+ puts "Manual verification of cache locations..."
123
+ cache_locations = get_cache_locations
124
+
125
+ cache_locations.each do |name, path|
126
+ next unless Dir.exist?(path)
127
+
128
+ puts " Checking #{name}..."
129
+
130
+ # Basic checks for common corruption indicators
131
+ corrupt_files = find_corrupt_files(path)
132
+ if corrupt_files.any?
133
+ puts " ⚠️ Found #{corrupt_files.length} potentially corrupt files"
134
+ corrupt_files.first(3).each { |file| puts " - #{file}" }
135
+ puts " ... and #{corrupt_files.length - 3} more" if corrupt_files.length > 3
136
+ else
137
+ puts " ✅ No obvious corruption detected"
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ def clear_nuget_caches
144
+ if dry_run?
145
+ puts "DRY RUN: Would clear the following NuGet caches:"
146
+ else
147
+ puts "Clearing NuGet caches..."
148
+ end
149
+
150
+ if cache_type
151
+ clear_specific_cache(cache_type)
152
+ else
153
+ clear_all_caches
154
+ end
155
+
156
+ if dry_run?
157
+ puts "✅ Dry run completed - no caches were actually cleared"
158
+ else
159
+ puts "✅ NuGet caches cleared successfully"
160
+ end
161
+ end
162
+
163
+ def clear_specific_cache(type)
164
+ cache_locations = get_cache_locations
165
+ path = cache_locations[type]
166
+
167
+ if path.nil?
168
+ puts "Error: Unknown cache type '#{type}'"
169
+ puts "Valid types: #{cache_locations.keys.join(", ")}"
170
+ exit 1
171
+ end
172
+
173
+ clear_cache_directory(type, path)
174
+ end
175
+
176
+ def clear_all_caches
177
+ # Try using dotnet CLI first
178
+ if system("dotnet --version > /dev/null 2>&1") && !cache_type
179
+ puts " Using dotnet nuget locals --clear all" if verbose?
180
+
181
+ if dry_run?
182
+ puts " Would run: dotnet nuget locals --clear all"
183
+ else
184
+ success = system("dotnet nuget locals --clear all")
185
+ if success
186
+ puts " ✅ Cleared all caches using dotnet CLI"
187
+ return
188
+ else
189
+ puts " ⚠️ dotnet command failed, falling back to manual cleanup"
190
+ end
191
+ end
192
+ end
193
+
194
+ # Manual cleanup
195
+ cache_locations = get_cache_locations
196
+ cache_locations.each do |name, path|
197
+ clear_cache_directory(name, path)
198
+ end
199
+ end
200
+
201
+ def clear_cache_directory(name, path)
202
+ if Dir.exist?(path)
203
+ size_before = calculate_directory_size(path)
204
+
205
+ if dry_run?
206
+ puts " Would clear #{name}: #{path} (#{format_size(size_before)})"
207
+ else
208
+ puts " Clearing #{name}: #{path} (#{format_size(size_before)})" if verbose?
209
+
210
+ begin
211
+ FileUtils.rm_rf(path)
212
+ FileUtils.mkdir_p(path) # Recreate the directory
213
+ puts " ✅ Cleared #{name}" if verbose?
214
+ rescue StandardError => e
215
+ puts " ❌ Failed to clear #{name}: #{e.message}"
216
+ end
217
+ end
218
+ elsif verbose?
219
+ puts " #{name} not found: #{path}"
220
+ end
221
+ end
222
+
223
+ def get_cache_locations
224
+ # Standard NuGet cache locations
225
+ home = Dir.home
226
+
227
+ locations = {
228
+ "global-packages" => File.join(home, ".nuget", "packages"),
229
+ "http-cache" => File.join(home, ".nuget", "v3-cache"),
230
+ "temp" => File.join(home, ".nuget", "temp"),
231
+ "plugins-cache" => File.join(home, ".nuget", "plugins-cache"),
232
+ }
233
+
234
+ # Add Windows-specific locations if on Windows
235
+ if Gem.win_platform?
236
+ ENV["APPDATA"] || File.join(home, "AppData", "Roaming")
237
+ local_app_data = ENV["LOCALAPPDATA"] || File.join(home, "AppData", "Local")
238
+
239
+ locations["http-cache"] = File.join(local_app_data, "NuGet", "v3-cache")
240
+ locations["plugins-cache"] = File.join(local_app_data, "NuGet", "plugins-cache")
241
+ end
242
+
243
+ locations
244
+ end
245
+
246
+ def calculate_directory_size(path)
247
+ return 0 unless Dir.exist?(path)
248
+
249
+ total_size = 0
250
+ Dir.glob(File.join(path, "**", "*")).each do |file|
251
+ total_size += File.size(file) if File.file?(file)
252
+ end
253
+ total_size
254
+ end
255
+
256
+ def count_files_in_directory(path)
257
+ return 0 unless Dir.exist?(path)
258
+
259
+ Dir.glob(File.join(path, "**", "*")).count { |f| File.file?(f) }
260
+ end
261
+
262
+ def format_size(bytes)
263
+ units = %w[B KB MB GB TB]
264
+ size = bytes.to_f
265
+ unit_index = 0
266
+
267
+ while size >= 1024 && unit_index < units.length - 1
268
+ size /= 1024.0
269
+ unit_index += 1
270
+ end
271
+
272
+ format("%.1f %s", size, units[unit_index])
273
+ end
274
+
275
+ def find_recent_files(path, limit)
276
+ return [] unless Dir.exist?(path)
277
+
278
+ Dir.glob(File.join(path, "**", "*"))
279
+ .select { |f| File.file?(f) }
280
+ .sort_by { |f| File.mtime(f) }
281
+ .reverse
282
+ .first(limit)
283
+ end
284
+
285
+ def find_corrupt_files(path)
286
+ return [] unless Dir.exist?(path)
287
+
288
+ corrupt_files = []
289
+
290
+ Dir.glob(File.join(path, "**", "*")).each do |file|
291
+ next unless File.file?(file)
292
+
293
+ # Check for zero-byte files that shouldn't be empty
294
+ corrupt_files << file if File.empty?(file) && File.extname(file) =~ /\.(dll|exe|nupkg)$/
295
+
296
+ # Check for incomplete downloads (common with network issues)
297
+ corrupt_files << file if File.basename(file) =~ /\.tmp$/ || File.basename(file) =~ /\.partial$/
298
+ end
299
+
300
+ corrupt_files
301
+ end
302
+ end
303
+
304
+ # Display system and environment information
305
+ class SystemInfoCommand < Clamp::Command
306
+ self.description = <<~DESC
307
+ Display system information, environment details, and development tool versions.
308
+
309
+ Examples:
310
+ makit utility system-info
311
+ makit utility system-info --tools-only
312
+ makit utility system-info --export
313
+ DESC
314
+
315
+ option ["--tools-only"], :flag, "Show only development tool versions"
316
+ option ["--export"], :flag, "Export information to system-info.txt"
317
+ option ["--verbose"], :flag, "Show detailed information"
318
+
319
+ def execute
320
+ info = collect_system_info
321
+
322
+ if export?
323
+ export_system_info(info)
324
+ else
325
+ display_system_info(info)
326
+ end
327
+ end
328
+
329
+ private
330
+
331
+ def collect_system_info
332
+ info = {}
333
+
334
+ unless tools_only?
335
+ info[:system] = collect_system_details
336
+ info[:ruby] = collect_ruby_info
337
+ end
338
+
339
+ info[:tools] = collect_development_tools
340
+
341
+ info
342
+ end
343
+
344
+ def collect_system_details
345
+ {
346
+ os: RbConfig::CONFIG["host_os"],
347
+ arch: RbConfig::CONFIG["host_cpu"],
348
+ user: ENV["USER"] || ENV.fetch("USERNAME", nil),
349
+ shell: ENV["SHELL"] || "Unknown",
350
+ pwd: Dir.pwd,
351
+ home: Dir.home,
352
+ }
353
+ end
354
+
355
+ def collect_ruby_info
356
+ {
357
+ version: RUBY_VERSION,
358
+ patchlevel: RUBY_PATCHLEVEL,
359
+ platform: RUBY_PLATFORM,
360
+ engine: RUBY_ENGINE,
361
+ gem_version: Gem::VERSION,
362
+ bundler_version: get_command_version("bundle --version"),
363
+ }
364
+ end
365
+
366
+ def collect_development_tools
367
+ tools = {}
368
+
369
+ # Version control
370
+ tools[:git] = get_command_version("git --version")
371
+
372
+ # Package managers
373
+ tools[:npm] = get_command_version("npm --version")
374
+ tools[:yarn] = get_command_version("yarn --version")
375
+ tools[:pip] = get_command_version("pip --version")
376
+
377
+ # Runtime environments
378
+ tools[:node] = get_command_version("node --version")
379
+ tools[:python] = get_command_version("python --version")
380
+ tools[:python3] = get_command_version("python3 --version")
381
+
382
+ # Build tools
383
+ tools[:make] = get_command_version("make --version")
384
+ tools[:cmake] = get_command_version("cmake --version")
385
+
386
+ # Language-specific tools
387
+ tools[:dotnet] = get_command_version("dotnet --version")
388
+ tools[:nuget] = get_command_version("nuget help") # Different format
389
+ tools[:cargo] = get_command_version("cargo --version")
390
+ tools[:rustc] = get_command_version("rustc --version")
391
+ tools[:go] = get_command_version("go version")
392
+ tools[:java] = get_command_version("java -version")
393
+ tools[:javac] = get_command_version("javac -version")
394
+
395
+ # Editors (just check availability)
396
+ tools[:code] = command_available?("code --version") ? "Available" : nil
397
+ tools[:subl] = command_available?("subl --version") ? "Available" : nil
398
+ tools[:vim] = get_command_version("vim --version")
399
+ tools[:emacs] = get_command_version("emacs --version")
400
+
401
+ # Container tools
402
+ tools[:docker] = get_command_version("docker --version")
403
+ tools[:podman] = get_command_version("podman --version")
404
+
405
+ tools.compact
406
+ end
407
+
408
+ def get_command_version(command)
409
+ output = `#{command} 2>&1`.strip
410
+ return nil if $CHILD_STATUS.exitstatus != 0
411
+
412
+ # Extract version number from output
413
+ version_match = output.match(/(?:v)?(\d+\.[\d.]+(?:-[\w.]+)?)/)
414
+ version_match ? version_match[1] : output.split("\n").first
415
+ rescue StandardError
416
+ nil
417
+ end
418
+
419
+ def command_available?(command)
420
+ system("#{command} > /dev/null 2>&1")
421
+ end
422
+
423
+ def display_system_info(info)
424
+ unless tools_only?
425
+ display_system_section(info[:system])
426
+ display_ruby_section(info[:ruby])
427
+ end
428
+
429
+ display_tools_section(info[:tools])
430
+ end
431
+
432
+ def display_system_section(system_info)
433
+ puts "System Information:"
434
+ puts "=" * 50
435
+ puts "OS: #{system_info[:os]}"
436
+ puts "Architecture: #{system_info[:arch]}"
437
+ puts "User: #{system_info[:user]}"
438
+ puts "Shell: #{system_info[:shell]}" if system_info[:shell] != "Unknown"
439
+ puts "Working Directory: #{system_info[:pwd]}"
440
+ puts "Home Directory: #{system_info[:home]}"
441
+ puts
442
+ end
443
+
444
+ def display_ruby_section(ruby_info)
445
+ puts "Ruby Environment:"
446
+ puts "=" * 50
447
+ puts "Ruby Version: #{ruby_info[:version]} (patchlevel #{ruby_info[:patchlevel]})"
448
+ puts "Platform: #{ruby_info[:platform]}"
449
+ puts "Engine: #{ruby_info[:engine]}"
450
+ puts "RubyGems Version: #{ruby_info[:gem_version]}"
451
+ puts "Bundler Version: #{ruby_info[:bundler_version]}" if ruby_info[:bundler_version]
452
+ puts
453
+ end
454
+
455
+ def display_tools_section(tools_info)
456
+ puts "Development Tools:"
457
+ puts "=" * 50
458
+
459
+ categories = {
460
+ "Version Control" => [:git],
461
+ "Package Managers" => %i[npm yarn pip],
462
+ "Runtime Environments" => %i[node python python3 dotnet java],
463
+ "Build Tools" => %i[make cmake cargo rustc go javac],
464
+ "Editors" => %i[code subl vim emacs],
465
+ "Container Tools" => %i[docker podman],
466
+ }
467
+
468
+ categories.each do |category, tool_keys|
469
+ category_tools = tool_keys.map { |key| [key, tools_info[key]] }.select { |_, version| version }
470
+
471
+ next if category_tools.empty?
472
+
473
+ puts "#{category}:"
474
+ category_tools.each do |tool, version|
475
+ puts " #{tool.to_s.ljust(15)} #{version}"
476
+ end
477
+ puts
478
+ end
479
+
480
+ # Show any remaining tools not categorized
481
+ remaining_tools = tools_info.except(*categories.values.flatten)
482
+ return unless remaining_tools.any?
483
+
484
+ puts "Other Tools:"
485
+ remaining_tools.each do |tool, version|
486
+ puts " #{tool.to_s.ljust(15)} #{version}"
487
+ end
488
+ end
489
+
490
+ def export_system_info(info)
491
+ filename = "system-info.txt"
492
+
493
+ File.open(filename, "w") do |file|
494
+ file.puts "System Information Export"
495
+ file.puts "Generated: #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
496
+ file.puts "=" * 60
497
+ file.puts
498
+
499
+ unless tools_only?
500
+ file.puts "SYSTEM DETAILS:"
501
+ info[:system].each { |key, value| file.puts "#{key}: #{value}" }
502
+ file.puts
503
+
504
+ file.puts "RUBY ENVIRONMENT:"
505
+ info[:ruby].each { |key, value| file.puts "#{key}: #{value}" }
506
+ file.puts
507
+ end
508
+
509
+ file.puts "DEVELOPMENT TOOLS:"
510
+ info[:tools].each { |tool, version| file.puts "#{tool}: #{version}" }
511
+ end
512
+
513
+ puts "✅ System information exported to: #{filename}"
514
+ end
515
+ end
516
+
517
+ # Add subcommand declarations after all classes are defined
518
+ UtilityCommand.subcommand "nuget-cache", "Manage NuGet package cache", NugetCacheCommand
519
+ UtilityCommand.subcommand "system-info", "Display system information", SystemInfoCommand
520
+ end
521
+ end