makit 0.0.112 → 0.0.128

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 (147) 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/cli/build_commands.rb +500 -500
  6. data/lib/makit/cli/generators/base_generator.rb +74 -74
  7. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  8. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  9. data/lib/makit/cli/generators/node_generator.rb +50 -50
  10. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  11. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  12. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  13. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  14. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  15. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
  16. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  17. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  18. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  19. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  20. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  21. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  22. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  23. data/lib/makit/cli/main.rb +62 -62
  24. data/lib/makit/cli/project_commands.rb +868 -868
  25. data/lib/makit/cli/repository_commands.rb +661 -661
  26. data/lib/makit/cli/utility_commands.rb +521 -521
  27. data/lib/makit/commands/factory.rb +359 -359
  28. data/lib/makit/commands/middleware/base.rb +73 -73
  29. data/lib/makit/commands/middleware/cache.rb +248 -248
  30. data/lib/makit/commands/middleware/command_logger.rb +311 -320
  31. data/lib/makit/commands/middleware/validator.rb +269 -269
  32. data/lib/makit/commands/request.rb +316 -254
  33. data/lib/makit/commands/result.rb +323 -323
  34. data/lib/makit/commands/runner.rb +368 -337
  35. data/lib/makit/commands/strategies/base.rb +171 -160
  36. data/lib/makit/commands/strategies/synchronous.rb +139 -134
  37. data/lib/makit/commands.rb +50 -51
  38. data/lib/makit/configuration/gitlab_helper.rb +58 -60
  39. data/lib/makit/configuration/project.rb +167 -127
  40. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  41. data/lib/makit/configuration/step.rb +34 -34
  42. data/lib/makit/configuration.rb +14 -14
  43. data/lib/makit/content/default_gitignore.rb +7 -7
  44. data/lib/makit/content/default_gitignore.txt +226 -0
  45. data/lib/makit/content/default_rakefile.rb +13 -13
  46. data/lib/makit/content/gem_rakefile.rb +16 -16
  47. data/lib/makit/context.rb +1 -1
  48. data/lib/makit/data.rb +49 -49
  49. data/lib/makit/directories.rb +140 -141
  50. data/lib/makit/directory.rb +262 -262
  51. data/lib/makit/docs/files.rb +89 -89
  52. data/lib/makit/docs/rake.rb +102 -102
  53. data/lib/makit/dotnet/cli.rb +69 -65
  54. data/lib/makit/dotnet/project.rb +217 -153
  55. data/lib/makit/dotnet/solution.rb +38 -38
  56. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  57. data/lib/makit/dotnet/solution_console.rb +264 -264
  58. data/lib/makit/dotnet/solution_maui.rb +354 -354
  59. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  60. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  61. data/lib/makit/dotnet.rb +102 -102
  62. data/lib/makit/email.rb +90 -90
  63. data/lib/makit/environment.rb +142 -142
  64. data/lib/makit/examples/runner.rb +370 -370
  65. data/lib/makit/exceptions.rb +45 -45
  66. data/lib/makit/fileinfo.rb +24 -24
  67. data/lib/makit/files.rb +43 -43
  68. data/lib/makit/gems.rb +40 -40
  69. data/lib/makit/git/cli.rb +54 -54
  70. data/lib/makit/git/repository.rb +90 -90
  71. data/lib/makit/git.rb +98 -98
  72. data/lib/makit/gitlab_runner.rb +59 -59
  73. data/lib/makit/humanize.rb +137 -137
  74. data/lib/makit/indexer.rb +47 -47
  75. data/lib/makit/logging/configuration.rb +308 -305
  76. data/lib/makit/logging/format_registry.rb +84 -84
  77. data/lib/makit/logging/formatters/base.rb +39 -39
  78. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  79. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  80. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  81. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  82. data/lib/makit/logging/log_request.rb +119 -115
  83. data/lib/makit/logging/logger.rb +199 -163
  84. data/lib/makit/logging/sinks/base.rb +91 -91
  85. data/lib/makit/logging/sinks/console.rb +72 -72
  86. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  87. data/lib/makit/logging/sinks/structured.rb +123 -129
  88. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -303
  89. data/lib/makit/logging.rb +565 -530
  90. data/lib/makit/markdown.rb +75 -75
  91. data/lib/makit/mp/basic_object_mp.rb +17 -17
  92. data/lib/makit/mp/command_mp.rb +13 -13
  93. data/lib/makit/mp/command_request.mp.rb +17 -17
  94. data/lib/makit/mp/project_mp.rb +199 -199
  95. data/lib/makit/mp/string_mp.rb +191 -193
  96. data/lib/makit/nuget.rb +74 -74
  97. data/lib/makit/port.rb +32 -32
  98. data/lib/makit/process.rb +163 -163
  99. data/lib/makit/protoc.rb +107 -107
  100. data/lib/makit/rake/cli.rb +196 -196
  101. data/lib/makit/rake.rb +25 -25
  102. data/lib/makit/ruby/cli.rb +185 -185
  103. data/lib/makit/ruby.rb +25 -25
  104. data/lib/makit/secrets.rb +51 -51
  105. data/lib/makit/serializer.rb +130 -130
  106. data/lib/makit/services/builder.rb +186 -186
  107. data/lib/makit/services/error_handler.rb +226 -226
  108. data/lib/makit/services/repository_manager.rb +231 -229
  109. data/lib/makit/services/validator.rb +112 -112
  110. data/lib/makit/setup/classlib.rb +94 -53
  111. data/lib/makit/setup/gem.rb +268 -263
  112. data/lib/makit/setup/razorclasslib.rb +91 -0
  113. data/lib/makit/setup/runner.rb +54 -45
  114. data/lib/makit/setup.rb +5 -5
  115. data/lib/makit/show.rb +110 -110
  116. data/lib/makit/storage.rb +126 -126
  117. data/lib/makit/symbols.rb +170 -170
  118. data/lib/makit/task_info.rb +128 -128
  119. data/lib/makit/tasks/at_exit.rb +15 -13
  120. data/lib/makit/tasks/build.rb +22 -19
  121. data/lib/makit/tasks/clean.rb +13 -11
  122. data/lib/makit/tasks/configure.rb +10 -0
  123. data/lib/makit/tasks/format.rb +10 -0
  124. data/lib/makit/tasks/hook_manager.rb +391 -393
  125. data/lib/makit/tasks/init.rb +49 -47
  126. data/lib/makit/tasks/integrate.rb +29 -17
  127. data/lib/makit/tasks/pull_incoming.rb +13 -11
  128. data/lib/makit/tasks/setup.rb +13 -6
  129. data/lib/makit/tasks/sync.rb +17 -12
  130. data/lib/makit/tasks/tag.rb +16 -15
  131. data/lib/makit/tasks/task_monkey_patch.rb +81 -79
  132. data/lib/makit/tasks/test.rb +22 -0
  133. data/lib/makit/tasks/update.rb +18 -0
  134. data/lib/makit/tasks.rb +20 -15
  135. data/lib/makit/test_cache.rb +239 -239
  136. data/lib/makit/tree.rb +37 -37
  137. data/lib/makit/v1/makit.v1_pb.rb +35 -34
  138. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  139. data/lib/makit/version.rb +5 -5
  140. data/lib/makit/version_util.rb +21 -21
  141. data/lib/makit/wix.rb +95 -95
  142. data/lib/makit/yaml.rb +29 -29
  143. data/lib/makit/zip.rb +17 -17
  144. data/lib/makit copy.rb +44 -44
  145. data/lib/makit.rb +39 -40
  146. metadata +69 -7
  147. data/lib/makit/commands/middleware/unified_logger.rb +0 -243
@@ -1,370 +1,370 @@
1
- # frozen_string_literal: true
2
-
3
- require "fileutils"
4
- require "parallel"
5
-
6
- module Makit
7
- module Examples
8
- # Centralized management of example discovery, execution, and verification
9
- #
10
- # This class provides a clean interface for running all examples in the
11
- # examples directory, with support for different execution strategies,
12
- # artifact verification, and comprehensive reporting.
13
- #
14
- # @example Basic usage
15
- # runner = Makit::Examples::Runner.new
16
- # runner.run_all
17
- #
18
- # @example Custom configuration
19
- # runner = Makit::Examples::Runner.new(
20
- # strategy: :parallel,
21
- # verify_artifacts: true,
22
- # cleanup_after: false
23
- # )
24
- # runner.run_all
25
- class Runner
26
- # Configuration options
27
- attr_reader :examples_dir, :execution_strategy, :verify_artifacts, :cleanup_after, :filter, :timeout, :verbose
28
-
29
- # Execution results
30
- attr_reader :results, :failed_examples, :passed_examples, :examples
31
-
32
- # Initialize the examples runner
33
- #
34
- # @param options [Hash] configuration options
35
- # @option options [String] :examples_dir directory containing examples (default: "examples")
36
- # @option options [Symbol] :strategy execution strategy (:sequential, :parallel, :filtered)
37
- # @option options [Boolean] :verify_artifacts whether to verify expected artifacts (default: true)
38
- # @option options [Boolean] :cleanup_after whether to clean up artifacts after testing (default: true)
39
- # @option options [Regexp] :filter pattern to filter examples (default: nil)
40
- # @option options [Integer] :timeout timeout per example in seconds (default: 30)
41
- # @option options [Boolean] :verbose whether to show detailed output (default: false)
42
- def initialize(options = {})
43
- @examples_dir = options[:examples_dir] || "examples"
44
- @execution_strategy = options[:strategy] || :sequential
45
- @verify_artifacts = options[:verify_artifacts] || true
46
- @cleanup_after = options[:cleanup_after] || true
47
- @filter = options[:filter]
48
- @timeout = options[:timeout] || 30
49
- @verbose = options[:verbose] || false
50
-
51
- @results = []
52
- @failed_examples = []
53
- @passed_examples = []
54
- @examples = []
55
- end
56
-
57
- # Run all discovered examples
58
- #
59
- # @return [Boolean] true if all examples passed, false otherwise
60
- def run_all
61
- discover_examples
62
- execute_examples
63
- verify_results
64
- report_results
65
- cleanup_if_needed
66
-
67
- @failed_examples.empty?
68
- end
69
-
70
- # Run a specific example
71
- #
72
- # @param example_path [String] path to the example directory
73
- # @return [Hash] result hash with success status and details
74
- def run_example(example_path)
75
- discover_examples if @examples.empty?
76
- example = @examples.find { |ex| ex[:name] == example_path }
77
- return { success: false, error: "Example not found: #{example_path}" } unless example
78
-
79
- execute_single_example(example)
80
- end
81
-
82
- # Discover all examples in the examples directory
83
- def discover_examples
84
- @examples = Dir.glob("#{@examples_dir}/**/Rakefile").map do |rakefile|
85
- example_dir = File.dirname(rakefile)
86
- example_name = example_dir.gsub("#{@examples_dir}/", "")
87
-
88
- {
89
- path: example_dir,
90
- name: example_name,
91
- rakefile: rakefile,
92
- expected_artifacts: determine_expected_artifacts(example_name),
93
- }
94
- end
95
-
96
- # Apply filter if specified
97
- @examples = @examples.select { |ex| ex[:name].match?(@filter) } if @filter
98
-
99
- log "Discovered #{@examples.count} examples" if @verbose
100
- end
101
-
102
- private
103
-
104
- # Execute all examples based on the configured strategy
105
- def execute_examples
106
- case @execution_strategy
107
- when :sequential
108
- execute_sequential
109
- when :parallel
110
- execute_parallel
111
- when :filtered
112
- execute_filtered
113
- else
114
- execute_sequential
115
- end
116
- end
117
-
118
- # Execute examples sequentially
119
- def execute_sequential
120
- @examples.each do |example|
121
- result = execute_single_example(example)
122
-
123
- # Verify artifacts immediately after execution if enabled
124
- verify_example_artifacts(example, result) if @verify_artifacts && result[:success]
125
-
126
- @results << result
127
-
128
- if result[:success]
129
- @passed_examples << example
130
- log " ✅ #{example[:name]} passed".colorize(:green) if @verbose
131
- else
132
- @failed_examples << example
133
- log " ❌ #{example[:name]} failed: #{result[:error]}".colorize(:red) if @verbose
134
- end
135
- end
136
- end
137
-
138
- # Execute examples in parallel
139
- def execute_parallel
140
- results = Parallel.map(@examples, in_threads: 4) do |example|
141
- result = execute_single_example(example)
142
-
143
- # Verify artifacts immediately after execution if enabled
144
- verify_example_artifacts(example, result) if @verify_artifacts && result[:success]
145
-
146
- result
147
- end
148
-
149
- results.each_with_index do |result, index|
150
- @results << result
151
- example = @examples[index]
152
-
153
- if result[:success]
154
- @passed_examples << example
155
- log " ✅ #{example[:name]} passed".colorize(:green) if @verbose
156
- else
157
- @failed_examples << example
158
- log " ❌ #{example[:name]} failed: #{result[:error]}".colorize(:red) if @verbose
159
- end
160
- end
161
- end
162
-
163
- # Execute examples with filtering (same as sequential for now)
164
- def execute_filtered
165
- execute_sequential
166
- end
167
-
168
- # Execute a single example
169
- #
170
- # @param example [Hash] example configuration
171
- # @return [Hash] result hash
172
- def execute_single_example(example)
173
- # Display separator and example path
174
- puts "=" * 80
175
- #puts example[:name]
176
- puts example[:rakefile]
177
-
178
- log " Testing example: #{example[:name]}" if @verbose
179
-
180
- # Store current directory
181
- original_dir = Dir.pwd
182
-
183
- begin
184
- Dir.chdir(example[:path]) do
185
- # Use the default runner with middleware for consistent behavior
186
- runner = Makit::DEFAULT_COMMANDS_RUNNER
187
- custom_logger = Makit::Logging::Logger.new(
188
- Makit::Logging::Sinks::Console.new,
189
- Makit::Logging::Sinks::FileSink.new(log_file: "artifacts/command_examples.log")
190
- )
191
-
192
- begin
193
- request = Makit::Commands::Request.from_string("rake default")
194
- result = runner.execute(request)
195
-
196
- # Log success or error for each example
197
- if result.success?
198
- Makit::Logging.success(example[:name])
199
- else
200
- Makit::Logging.error(example[:name])
201
- end
202
-
203
- {
204
- example: example,
205
- success: result.success?,
206
- exit_code: result.exit_code,
207
- output: result.stdout,
208
- error: result.stderr,
209
- duration: result.duration,
210
- }
211
- rescue StandardError => e
212
- # Log error for exceptions
213
- Makit::Logging.error(example[:name])
214
-
215
- {
216
- example: example,
217
- success: false,
218
- exit_code: -1,
219
- output: "",
220
- error: e.message,
221
- duration: 0,
222
- }
223
- end
224
- end
225
- rescue StandardError => e
226
- # Log error for directory change failures
227
- Makit::Logging.error(example[:name])
228
-
229
- {
230
- example: example,
231
- success: false,
232
- exit_code: -1,
233
- output: "",
234
- error: "Failed to change directory: #{e.message}",
235
- duration: 0,
236
- }
237
- ensure
238
- # Always return to original directory
239
- begin
240
- Dir.chdir(original_dir)
241
- rescue StandardError
242
- nil
243
- end
244
- end
245
- end
246
-
247
- # Determine expected artifacts for an example
248
- #
249
- # @param example_name [String] name of the example
250
- # @return [Array<String>] list of expected artifact paths
251
- def determine_expected_artifacts(example_name)
252
- case example_name
253
- when %r{commands/(default_runner|runner)}
254
- ["artifacts/commands/git_version.log", "artifacts/commands/git_version.txt"]
255
- when "protoc"
256
- ["artifacts/makit.v1_pb.rb"]
257
- when "rake_default"
258
- [".makit.project.json"]
259
- when "run_mp"
260
- [] # No expected artifacts
261
- when "tasks/simple"
262
- [] # No expected artifacts
263
- when "rubygem-foo"
264
- [] # No expected artifacts
265
- else
266
- [] # Default to no expected artifacts
267
- end
268
- end
269
-
270
- # Verify artifacts for a single example immediately after execution
271
- #
272
- # @param example [Hash] example configuration
273
- # @param result [Hash] execution result
274
- def verify_example_artifacts(example, result)
275
- return unless @verify_artifacts
276
-
277
- # Check artifacts in the example directory context
278
- missing_artifacts = example[:expected_artifacts].reject do |artifact|
279
- artifact_path = File.join(example[:path], artifact)
280
- File.exist?(artifact_path)
281
- end
282
-
283
- return unless missing_artifacts.any?
284
-
285
- result[:success] = false
286
- result[:error] = "Missing expected artifacts: #{missing_artifacts.join(", ")}"
287
- end
288
-
289
- # Verify that all results meet expectations
290
- def verify_results
291
- return unless @verify_artifacts
292
-
293
- @results.each do |result|
294
- next unless result[:success]
295
-
296
- example = result[:example]
297
-
298
- # Check artifacts in the example directory context
299
- missing_artifacts = example[:expected_artifacts].reject do |artifact|
300
- artifact_path = File.join(example[:path], artifact)
301
- File.exist?(artifact_path)
302
- end
303
-
304
- next unless missing_artifacts.any?
305
-
306
- result[:success] = false
307
- result[:error] = "Missing expected artifacts: #{missing_artifacts.join(", ")}"
308
-
309
- # Move from passed to failed
310
- @passed_examples.delete(example)
311
- @failed_examples << example
312
- end
313
- end
314
-
315
- # Clean up artifacts if configured
316
- def cleanup_if_needed
317
- return unless @cleanup_after
318
-
319
- @examples.each do |example|
320
- cleanup_example(example)
321
- end
322
- end
323
-
324
- # Clean up artifacts for a specific example
325
- #
326
- # @param example [Hash] example configuration
327
- def cleanup_example(example)
328
- Dir.chdir(example[:path]) do
329
- # Remove artifacts directory if it exists
330
- FileUtils.rm_rf("artifacts")
331
-
332
- # Remove project file if it exists
333
- FileUtils.rm_f(".makit.project.json")
334
- end
335
- rescue StandardError => e
336
- log " Warning: Failed to cleanup #{example[:name]}: #{e.message}" if @verbose
337
- end
338
-
339
- # Report the results of example execution
340
- def report_results
341
- puts "📊 Example Test Results:".colorize(:blue)
342
- puts " ✅ Passed: #{@passed_examples.count}".colorize(:green)
343
- puts " ❌ Failed: #{@failed_examples.count}".colorize(:red)
344
-
345
- if @failed_examples.any?
346
- puts " Failed examples:".colorize(:red)
347
- @failed_examples.each do |example|
348
- result = @results.find { |r| r[:example] == example }
349
- error_msg = result ? result[:error] : "Unknown error"
350
- puts " - #{example[:name]}: #{error_msg}".colorize(:red)
351
- end
352
- end
353
-
354
- return unless @passed_examples.any? && @verbose
355
-
356
- puts " Passed examples:".colorize(:green)
357
- @passed_examples.each do |example|
358
- puts " - #{example[:name]}".colorize(:green)
359
- end
360
- end
361
-
362
- # Log a message if verbose mode is enabled
363
- #
364
- # @param message [String] message to log
365
- def log(message)
366
- puts message if @verbose
367
- end
368
- end
369
- end
370
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "parallel"
5
+
6
+ module Makit
7
+ module Examples
8
+ # Centralized management of example discovery, execution, and verification
9
+ #
10
+ # This class provides a clean interface for running all examples in the
11
+ # examples directory, with support for different execution strategies,
12
+ # artifact verification, and comprehensive reporting.
13
+ #
14
+ # @example Basic usage
15
+ # runner = Makit::Examples::Runner.new
16
+ # runner.run_all
17
+ #
18
+ # @example Custom configuration
19
+ # runner = Makit::Examples::Runner.new(
20
+ # strategy: :parallel,
21
+ # verify_artifacts: true,
22
+ # cleanup_after: false
23
+ # )
24
+ # runner.run_all
25
+ class Runner
26
+ # Configuration options
27
+ attr_reader :examples_dir, :execution_strategy, :verify_artifacts, :cleanup_after, :filter, :timeout, :verbose
28
+
29
+ # Execution results
30
+ attr_reader :results, :failed_examples, :passed_examples, :examples
31
+
32
+ # Initialize the examples runner
33
+ #
34
+ # @param options [Hash] configuration options
35
+ # @option options [String] :examples_dir directory containing examples (default: "examples")
36
+ # @option options [Symbol] :strategy execution strategy (:sequential, :parallel, :filtered)
37
+ # @option options [Boolean] :verify_artifacts whether to verify expected artifacts (default: true)
38
+ # @option options [Boolean] :cleanup_after whether to clean up artifacts after testing (default: true)
39
+ # @option options [Regexp] :filter pattern to filter examples (default: nil)
40
+ # @option options [Integer] :timeout timeout per example in seconds (default: 30)
41
+ # @option options [Boolean] :verbose whether to show detailed output (default: false)
42
+ def initialize(options = {})
43
+ @examples_dir = options[:examples_dir] || "examples"
44
+ @execution_strategy = options[:strategy] || :sequential
45
+ @verify_artifacts = options[:verify_artifacts] || true
46
+ @cleanup_after = options[:cleanup_after] || true
47
+ @filter = options[:filter]
48
+ @timeout = options[:timeout] || 30
49
+ @verbose = options[:verbose] || false
50
+
51
+ @results = []
52
+ @failed_examples = []
53
+ @passed_examples = []
54
+ @examples = []
55
+ end
56
+
57
+ # Run all discovered examples
58
+ #
59
+ # @return [Boolean] true if all examples passed, false otherwise
60
+ def run_all
61
+ discover_examples
62
+ execute_examples
63
+ verify_results
64
+ report_results
65
+ cleanup_if_needed
66
+
67
+ @failed_examples.empty?
68
+ end
69
+
70
+ # Run a specific example
71
+ #
72
+ # @param example_path [String] path to the example directory
73
+ # @return [Hash] result hash with success status and details
74
+ def run_example(example_path)
75
+ discover_examples if @examples.empty?
76
+ example = @examples.find { |ex| ex[:name] == example_path }
77
+ return { success: false, error: "Example not found: #{example_path}" } unless example
78
+
79
+ execute_single_example(example)
80
+ end
81
+
82
+ # Discover all examples in the examples directory
83
+ def discover_examples
84
+ @examples = Dir.glob("#{@examples_dir}/**/Rakefile").map do |rakefile|
85
+ example_dir = File.dirname(rakefile)
86
+ example_name = example_dir.gsub("#{@examples_dir}/", "")
87
+
88
+ {
89
+ path: example_dir,
90
+ name: example_name,
91
+ rakefile: rakefile,
92
+ expected_artifacts: determine_expected_artifacts(example_name),
93
+ }
94
+ end
95
+
96
+ # Apply filter if specified
97
+ @examples = @examples.select { |ex| ex[:name].match?(@filter) } if @filter
98
+
99
+ log "Discovered #{@examples.count} examples" if @verbose
100
+ end
101
+
102
+ private
103
+
104
+ # Execute all examples based on the configured strategy
105
+ def execute_examples
106
+ case @execution_strategy
107
+ when :sequential
108
+ execute_sequential
109
+ when :parallel
110
+ execute_parallel
111
+ when :filtered
112
+ execute_filtered
113
+ else
114
+ execute_sequential
115
+ end
116
+ end
117
+
118
+ # Execute examples sequentially
119
+ def execute_sequential
120
+ @examples.each do |example|
121
+ result = execute_single_example(example)
122
+
123
+ # Verify artifacts immediately after execution if enabled
124
+ verify_example_artifacts(example, result) if @verify_artifacts && result[:success]
125
+
126
+ @results << result
127
+
128
+ if result[:success]
129
+ @passed_examples << example
130
+ log " ✅ #{example[:name]} passed".colorize(:green) if @verbose
131
+ else
132
+ @failed_examples << example
133
+ log " ❌ #{example[:name]} failed: #{result[:error]}".colorize(:red) if @verbose
134
+ end
135
+ end
136
+ end
137
+
138
+ # Execute examples in parallel
139
+ def execute_parallel
140
+ results = Parallel.map(@examples, in_threads: 4) do |example|
141
+ result = execute_single_example(example)
142
+
143
+ # Verify artifacts immediately after execution if enabled
144
+ verify_example_artifacts(example, result) if @verify_artifacts && result[:success]
145
+
146
+ result
147
+ end
148
+
149
+ results.each_with_index do |result, index|
150
+ @results << result
151
+ example = @examples[index]
152
+
153
+ if result[:success]
154
+ @passed_examples << example
155
+ log " ✅ #{example[:name]} passed".colorize(:green) if @verbose
156
+ else
157
+ @failed_examples << example
158
+ log " ❌ #{example[:name]} failed: #{result[:error]}".colorize(:red) if @verbose
159
+ end
160
+ end
161
+ end
162
+
163
+ # Execute examples with filtering (same as sequential for now)
164
+ def execute_filtered
165
+ execute_sequential
166
+ end
167
+
168
+ # Execute a single example
169
+ #
170
+ # @param example [Hash] example configuration
171
+ # @return [Hash] result hash
172
+ def execute_single_example(example)
173
+ # Display separator and example path
174
+ puts "=" * 80
175
+ # puts example[:name]
176
+ puts example[:rakefile]
177
+
178
+ log " Testing example: #{example[:name]}" if @verbose
179
+
180
+ # Store current directory
181
+ original_dir = Dir.pwd
182
+
183
+ begin
184
+ Dir.chdir(example[:path]) do
185
+ # Use the default runner with middleware for consistent behavior
186
+ runner = Makit::DEFAULT_COMMANDS_RUNNER
187
+ Makit::Logging::Logger.new(
188
+ Makit::Logging::Sinks::Console.new,
189
+ Makit::Logging::Sinks::FileSink.new(log_file: "artifacts/command_examples.log")
190
+ )
191
+
192
+ begin
193
+ request = Makit::Commands::Request.from_string("rake default")
194
+ result = runner.execute(request)
195
+
196
+ # Log success or error for each example
197
+ if result.success?
198
+ Makit::Logging.success(example[:name])
199
+ else
200
+ Makit::Logging.error(example[:name])
201
+ end
202
+
203
+ {
204
+ example: example,
205
+ success: result.success?,
206
+ exit_code: result.exit_code,
207
+ output: result.stdout,
208
+ error: result.stderr,
209
+ duration: result.duration,
210
+ }
211
+ rescue StandardError => e
212
+ # Log error for exceptions
213
+ Makit::Logging.error(example[:name])
214
+
215
+ {
216
+ example: example,
217
+ success: false,
218
+ exit_code: -1,
219
+ output: "",
220
+ error: e.message,
221
+ duration: 0,
222
+ }
223
+ end
224
+ end
225
+ rescue StandardError => e
226
+ # Log error for directory change failures
227
+ Makit::Logging.error(example[:name])
228
+
229
+ {
230
+ example: example,
231
+ success: false,
232
+ exit_code: -1,
233
+ output: "",
234
+ error: "Failed to change directory: #{e.message}",
235
+ duration: 0,
236
+ }
237
+ ensure
238
+ # Always return to original directory
239
+ begin
240
+ Dir.chdir(original_dir)
241
+ rescue StandardError
242
+ nil
243
+ end
244
+ end
245
+ end
246
+
247
+ # Determine expected artifacts for an example
248
+ #
249
+ # @param example_name [String] name of the example
250
+ # @return [Array<String>] list of expected artifact paths
251
+ def determine_expected_artifacts(example_name)
252
+ case example_name
253
+ when %r{commands/(default_runner|runner)}
254
+ ["artifacts/commands/git_version.log", "artifacts/commands/git_version.txt"]
255
+ when "protoc"
256
+ ["artifacts/makit.v1_pb.rb"]
257
+ when "rake_default"
258
+ [".makit.project.json"]
259
+ when "run_mp"
260
+ [] # No expected artifacts
261
+ when "tasks/simple"
262
+ [] # No expected artifacts
263
+ when "rubygem-foo"
264
+ [] # No expected artifacts
265
+ else
266
+ [] # Default to no expected artifacts
267
+ end
268
+ end
269
+
270
+ # Verify artifacts for a single example immediately after execution
271
+ #
272
+ # @param example [Hash] example configuration
273
+ # @param result [Hash] execution result
274
+ def verify_example_artifacts(example, result)
275
+ return unless @verify_artifacts
276
+
277
+ # Check artifacts in the example directory context
278
+ missing_artifacts = example[:expected_artifacts].reject do |artifact|
279
+ artifact_path = File.join(example[:path], artifact)
280
+ File.exist?(artifact_path)
281
+ end
282
+
283
+ return unless missing_artifacts.any?
284
+
285
+ result[:success] = false
286
+ result[:error] = "Missing expected artifacts: #{missing_artifacts.join(", ")}"
287
+ end
288
+
289
+ # Verify that all results meet expectations
290
+ def verify_results
291
+ return unless @verify_artifacts
292
+
293
+ @results.each do |result|
294
+ next unless result[:success]
295
+
296
+ example = result[:example]
297
+
298
+ # Check artifacts in the example directory context
299
+ missing_artifacts = example[:expected_artifacts].reject do |artifact|
300
+ artifact_path = File.join(example[:path], artifact)
301
+ File.exist?(artifact_path)
302
+ end
303
+
304
+ next unless missing_artifacts.any?
305
+
306
+ result[:success] = false
307
+ result[:error] = "Missing expected artifacts: #{missing_artifacts.join(", ")}"
308
+
309
+ # Move from passed to failed
310
+ @passed_examples.delete(example)
311
+ @failed_examples << example
312
+ end
313
+ end
314
+
315
+ # Clean up artifacts if configured
316
+ def cleanup_if_needed
317
+ return unless @cleanup_after
318
+
319
+ @examples.each do |example|
320
+ cleanup_example(example)
321
+ end
322
+ end
323
+
324
+ # Clean up artifacts for a specific example
325
+ #
326
+ # @param example [Hash] example configuration
327
+ def cleanup_example(example)
328
+ Dir.chdir(example[:path]) do
329
+ # Remove artifacts directory if it exists
330
+ FileUtils.rm_rf("artifacts")
331
+
332
+ # Remove project file if it exists
333
+ FileUtils.rm_f(".makit.project.json")
334
+ end
335
+ rescue StandardError => e
336
+ log " Warning: Failed to cleanup #{example[:name]}: #{e.message}" if @verbose
337
+ end
338
+
339
+ # Report the results of example execution
340
+ def report_results
341
+ puts "📊 Example Test Results:".colorize(:blue)
342
+ puts " ✅ Passed: #{@passed_examples.count}".colorize(:green)
343
+ puts " ❌ Failed: #{@failed_examples.count}".colorize(:red)
344
+
345
+ if @failed_examples.any?
346
+ puts " Failed examples:".colorize(:red)
347
+ @failed_examples.each do |example|
348
+ result = @results.find { |r| r[:example] == example }
349
+ error_msg = result ? result[:error] : "Unknown error"
350
+ puts " - #{example[:name]}: #{error_msg}".colorize(:red)
351
+ end
352
+ end
353
+
354
+ return unless @passed_examples.any? && @verbose
355
+
356
+ puts " Passed examples:".colorize(:green)
357
+ @passed_examples.each do |example|
358
+ puts " - #{example[:name]}".colorize(:green)
359
+ end
360
+ end
361
+
362
+ # Log a message if verbose mode is enabled
363
+ #
364
+ # @param message [String] message to log
365
+ def log(message)
366
+ puts message if @verbose
367
+ end
368
+ end
369
+ end
370
+ end