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,393 +1,391 @@
1
- # frozen_string_literal: true
2
-
3
- require "set"
4
- require "colorize"
5
- require "fileutils"
6
-
7
- # Task hooks system for Makit
8
- #
9
- # This module provides a comprehensive hook system for Rake tasks, allowing
10
- # developers to add pre and post execution hooks globally or for specific tasks.
11
- # It uses monkey patching to intercept all task invocations and execute hooks
12
- # at the appropriate times.
13
- #
14
- # @example Global hooks
15
- # Makit::Tasks::HookManager.add_pre_hook do |task_name|
16
- # Makit::Logging.info("Starting task: #{task_name}")
17
- # end
18
- #
19
- # Makit::Tasks::HookManager.add_post_hook do |task_name, duration, result, error|
20
- # if error
21
- # Makit::Logging.error("Task failed: #{task_name}")
22
- # else
23
- # Makit::Logging.success("Task completed: #{task_name}")
24
- # end
25
- # end
26
- #
27
- # @example Task-specific hooks
28
- # Makit::Tasks::HookManager.add_pre_hook_for(:build) do |task_name|
29
- # Makit::Logging.info("Building project...")
30
- # end
31
- #
32
- # Makit::Tasks::HookManager.add_post_hook_for(:test) do |task_name, duration, result, error|
33
- # Makit::Logging.info("Test execution took #{duration.round(2)}s")
34
- # end
35
- module Makit
36
- module Tasks
37
- class HookManager
38
- @pre_hooks = []
39
- @post_hooks = []
40
- @task_specific_pre_hooks = {}
41
- @task_specific_post_hooks = {}
42
- @excluded_tasks = Set.new
43
- @logged_tasks = Set.new
44
-
45
- # Timing functionality
46
- @timing_enabled = false
47
- @task_stack = []
48
- @performance_log = "artifacts/task_performance.log"
49
- @hooks_installed = false
50
-
51
- class << self
52
- # @return [Array<Proc>] Global pre-task hooks
53
- attr_reader :pre_hooks
54
-
55
- # @return [Array<Proc>] Global post-task hooks
56
- attr_reader :post_hooks
57
-
58
- # @return [Hash<String, Array<Proc>>] Task-specific pre-task hooks
59
- attr_reader :task_specific_pre_hooks
60
-
61
- # @return [Hash<String, Array<Proc>>] Task-specific post-task hooks
62
- attr_reader :task_specific_post_hooks
63
-
64
- # @return [Set<String>] Tasks excluded from hook execution
65
- attr_reader :excluded_tasks
66
-
67
- # @return [Boolean] Whether timing is enabled
68
- attr_reader :timing_enabled
69
-
70
- # @return [Array<Hash>] Stack of currently executing tasks
71
- attr_reader :task_stack
72
-
73
- # @return [String] Path to performance log file
74
- attr_reader :performance_log
75
-
76
- # Add a global pre-task hook
77
- #
78
- # The hook will be executed before every task runs.
79
- #
80
- # @yield [task_name] the name of the task being executed
81
- # @yieldparam task_name [String] the name of the task
82
- # @return [void]
83
- def add_pre_hook(&block)
84
- @pre_hooks << block
85
- end
86
-
87
- # Add a global post-task hook
88
- #
89
- # The hook will be executed after every task completes (success or failure).
90
- #
91
- # @yield [task_name, duration, result, error] hook parameters
92
- # @yieldparam task_name [String] the name of the task
93
- # @yieldparam duration [Float] execution duration in seconds
94
- # @yieldparam result [Object, nil] the result of the task execution
95
- # @yieldparam error [Exception, nil] any error that occurred during execution
96
- # @return [void]
97
- def add_post_hook(&block)
98
- @post_hooks << block
99
- end
100
-
101
- # Add a pre-task hook for a specific task
102
- #
103
- # The hook will only be executed before the specified task runs.
104
- #
105
- # @param task_name [String, Symbol] the name of the task
106
- # @yield [task_name] the name of the task being executed
107
- # @yieldparam task_name [String] the name of the task
108
- # @return [void]
109
- def add_pre_hook_for(task_name, &block)
110
- @task_specific_pre_hooks[task_name.to_s] ||= []
111
- @task_specific_pre_hooks[task_name.to_s] << block
112
- end
113
-
114
- # Add a post-task hook for a specific task
115
- #
116
- # The hook will only be executed after the specified task completes.
117
- #
118
- # @param task_name [String, Symbol] the name of the task
119
- # @yield [task_name, duration, result, error] hook parameters
120
- # @yieldparam task_name [String] the name of the task
121
- # @yieldparam duration [Float] execution duration in seconds
122
- # @yieldparam result [Object, nil] the result of the task execution
123
- # @yieldparam error [Exception, nil] any error that occurred during execution
124
- # @return [void]
125
- def add_post_hook_for(task_name, &block)
126
- @task_specific_post_hooks[task_name.to_s] ||= []
127
- @task_specific_post_hooks[task_name.to_s] << block
128
- end
129
-
130
- # Execute all pre-task hooks for a given task
131
- #
132
- # @param task_name [String] the name of the task
133
- # @return [void]
134
- def execute_pre_hooks(task_name)
135
- return if excluded?(task_name)
136
-
137
- # Track timing if enabled
138
- if timing_enabled?
139
- @task_stack.push({
140
- name: task_name,
141
- start_time: Time.now,
142
- level: @task_stack.length,
143
- })
144
- end
145
-
146
- # Execute global pre-hooks
147
- @pre_hooks.each { |hook| hook.call(task_name) }
148
-
149
- # Execute task-specific pre-hooks
150
- task_hooks = @task_specific_pre_hooks[task_name.to_s] || []
151
- task_hooks.each { |hook| hook.call(task_name) }
152
- end
153
-
154
- # Execute all post-task hooks for a given task
155
- #
156
- # @param task_name [String] the name of the task
157
- # @param duration [Float] execution duration in seconds
158
- # @param result [Object, nil] the result of the task execution
159
- # @param error [Exception, nil] any error that occurred during execution
160
- # @return [void]
161
- def execute_post_hooks(task_name, duration, result, error)
162
- return if excluded?(task_name)
163
-
164
- # Handle timing if enabled
165
- if timing_enabled?
166
- task_info = @task_stack.pop
167
- if task_info && task_info[:name] == task_name
168
- # Log performance for tasks that took longer than 0.1 seconds
169
- log_task_performance(task_name, duration, task_info[:level]) if duration > 0.1
170
- end
171
-
172
- # Log task failure if there was an error
173
- if error
174
- log_task_failure(task_name, duration, error, task_info&.dig(:level) || 0)
175
- end
176
- end
177
-
178
- # Execute global post-hooks
179
- @post_hooks.each { |hook| hook.call(task_name, duration, result, error) }
180
-
181
- # Execute task-specific post-hooks
182
- task_hooks = @task_specific_post_hooks[task_name.to_s] || []
183
- task_hooks.each { |hook| hook.call(task_name, duration, result, error) }
184
- end
185
-
186
- # Clear all hooks
187
- #
188
- # @return [void]
189
- def clear_all_hooks
190
- @pre_hooks.clear
191
- @post_hooks.clear
192
- @task_specific_pre_hooks.clear
193
- @task_specific_post_hooks.clear
194
- end
195
-
196
- # Exclude a task from hook execution
197
- #
198
- # @param task_name [String, Symbol] the name of the task to exclude
199
- # @return [void]
200
- def exclude_task(task_name)
201
- @excluded_tasks.add(task_name.to_s)
202
- end
203
-
204
- # Include a task in hook execution (remove from exclusions)
205
- #
206
- # @param task_name [String, Symbol] the name of the task to include
207
- # @return [void]
208
- def include_task(task_name)
209
- @excluded_tasks.delete(task_name.to_s)
210
- end
211
-
212
- # Check if a task is excluded from hook execution
213
- #
214
- # @param task_name [String, Symbol] the name of the task to check
215
- # @return [Boolean] true if the task is excluded
216
- def excluded?(task_name)
217
- @excluded_tasks.include?(task_name.to_s)
218
- end
219
-
220
- # Get statistics about registered hooks
221
- #
222
- # @return [Hash] hook statistics
223
- def stats
224
- {
225
- global_pre_hooks: @pre_hooks.size,
226
- global_post_hooks: @post_hooks.size,
227
- task_specific_pre_hooks: @task_specific_pre_hooks.values.flatten.size,
228
- task_specific_post_hooks: @task_specific_post_hooks.values.flatten.size,
229
- tasks_with_pre_hooks: @task_specific_pre_hooks.keys,
230
- tasks_with_post_hooks: @task_specific_post_hooks.keys,
231
- timing_enabled: @timing_enabled,
232
- task_stack_depth: @task_stack.size,
233
- }
234
- end
235
-
236
- # Enable task timing and performance logging
237
- #
238
- # @return [void]
239
- def enable_timing!
240
- @timing_enabled = true
241
- puts "🔧 Task timing enabled".colorize(:grey) if defined?(String.instance_method(:colorize))
242
- end
243
-
244
- # Disable task timing and performance logging
245
- #
246
- # @return [void]
247
- def disable_timing!
248
- @timing_enabled = false
249
- end
250
-
251
- # Check if timing is enabled
252
- #
253
- # @return [Boolean] true if timing is enabled
254
- def timing_enabled?
255
- @timing_enabled == true
256
- end
257
-
258
- # Clear performance logs
259
- #
260
- # @return [void]
261
- def clear_performance_logs
262
- FileUtils.rm_f(@performance_log)
263
- FileUtils.rm_f("artifacts/task_failures.log")
264
- end
265
-
266
- # Setup task hooks by monkey patching Rake::Task
267
- #
268
- # This method patches the Rake::Task#execute method to automatically
269
- # call pre and post hooks for all task executions.
270
- #
271
- # @return [void]
272
- def setup!
273
- return if @hooks_installed
274
-
275
- Rake::Task.class_eval do
276
- alias_method :makit_original_execute, :execute
277
-
278
- def execute(args = nil)
279
- start_time = Time.now
280
- error = nil
281
- result = nil
282
-
283
- begin
284
- # Execute pre-hooks
285
- Makit::Tasks::HookManager.execute_pre_hooks(name)
286
-
287
- # Execute the original task
288
- result = makit_original_execute(args)
289
- rescue StandardError => e
290
- error = e
291
- raise
292
- ensure
293
- # Calculate duration
294
- duration = Time.now - start_time
295
-
296
- # Execute post-hooks
297
- Makit::Tasks::HookManager.execute_post_hooks(name, duration, result, error)
298
- end
299
-
300
- result
301
- end
302
- end
303
-
304
- @hooks_installed = true
305
- end
306
-
307
- # Setup default task hooks
308
- #
309
- # This method sets up the default task hooks that provide basic
310
- # task execution feedback. It should be called during initialization.
311
- #
312
- # @return [void]
313
- def setup_default_hooks
314
- # Setup the monkey patching first
315
- setup!
316
-
317
- # Only setup default hooks if no hooks are already registered
318
- return unless @pre_hooks.empty? && @post_hooks.empty?
319
-
320
- # Exclude the init task from hooks to prevent duplication
321
- exclude_task(:init)
322
- exclude_task(:default)
323
-
324
- # Default pre-task hook: log task start to default logger
325
- add_pre_hook do |task_name|
326
- log_task_start(task_name)
327
- end
328
-
329
- # Default post-task hook: log task completion
330
- add_post_hook do |task_name, duration, result, error|
331
- log_task_completion(task_name, duration, result, error)
332
- end
333
- end
334
-
335
- def log_task_start(task_name)
336
- #return if @logged_tasks.include?(task_name)
337
- #@logged_tasks.add(task_name)
338
- #Makit::Logging.info("#{task_name}".colorize(:white).bold)
339
- end
340
-
341
- def log_task_completion(task_name, duration, result, error)
342
- #Makit::Logging.info("#{task_name}".colorize(:white).bold)
343
- #Makit::Logging.info("Task completed".colorize(:green).bold)
344
- #Makit::Logging.info("Duration: #{duration.round(2)}s".colorize(:green).bold)
345
- # Makit::Logging.info("Result: #{result}".colorize(:green).bold)
346
- #Makit::Logging.info("Error: #{error}".colorize(:red).bold)
347
- end
348
-
349
- private
350
-
351
- # Log task performance to file
352
- #
353
- # @param name [String] task name
354
- # @param duration [Float] execution duration
355
- # @param level [Integer] nesting level
356
- # @return [void]
357
- def log_task_performance(name, duration, level)
358
- return if ENV["CI"] == "true" && duration < 1.0
359
-
360
- FileUtils.mkdir_p("artifacts")
361
- File.open(@performance_log, "a") do |f|
362
- f.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")},#{name},#{duration.round(4)},#{level}"
363
- end
364
- rescue StandardError
365
- # Silently fail performance logging to not interrupt tasks
366
- end
367
-
368
- # Log task failure to file
369
- #
370
- # @param name [String] task name
371
- # @param duration [Float] execution duration
372
- # @param error [Exception] the error that occurred
373
- # @param level [Integer] nesting level
374
- # @return [void]
375
- def log_task_failure(name, duration, error, level)
376
- failure_log = "artifacts/task_failures.log"
377
- FileUtils.mkdir_p("artifacts")
378
-
379
- File.open(failure_log, "a") do |f|
380
- timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
381
- message = error.message.gsub(",", ";")
382
- f.puts "#{timestamp},#{name},#{duration.round(4)},#{error.class.name},#{message}"
383
- end
384
- rescue StandardError
385
- # Silently fail error logging
386
- end
387
- end
388
- end
389
- end
390
- end
391
-
392
- # Setup default hooks when the module is loaded
393
- Makit::Tasks::HookManager.setup_default_hooks
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "colorize"
5
+ require "fileutils"
6
+
7
+ # Task hooks system for Makit
8
+ #
9
+ # This module provides a comprehensive hook system for Rake tasks, allowing
10
+ # developers to add pre and post execution hooks globally or for specific tasks.
11
+ # It uses monkey patching to intercept all task invocations and execute hooks
12
+ # at the appropriate times.
13
+ #
14
+ # @example Global hooks
15
+ # Makit::Tasks::HookManager.add_pre_hook do |task_name|
16
+ # Makit::Logging.info("Starting task: #{task_name}")
17
+ # end
18
+ #
19
+ # Makit::Tasks::HookManager.add_post_hook do |task_name, duration, result, error|
20
+ # if error
21
+ # Makit::Logging.error("Task failed: #{task_name}")
22
+ # else
23
+ # Makit::Logging.success("Task completed: #{task_name}")
24
+ # end
25
+ # end
26
+ #
27
+ # @example Task-specific hooks
28
+ # Makit::Tasks::HookManager.add_pre_hook_for(:build) do |task_name|
29
+ # Makit::Logging.info("Building project...")
30
+ # end
31
+ #
32
+ # Makit::Tasks::HookManager.add_post_hook_for(:test) do |task_name, duration, result, error|
33
+ # Makit::Logging.info("Test execution took #{duration.round(2)}s")
34
+ # end
35
+ module Makit
36
+ module Tasks
37
+ class HookManager
38
+ @pre_hooks = []
39
+ @post_hooks = []
40
+ @task_specific_pre_hooks = {}
41
+ @task_specific_post_hooks = {}
42
+ @excluded_tasks = Set.new
43
+ @logged_tasks = Set.new
44
+
45
+ # Timing functionality
46
+ @timing_enabled = false
47
+ @task_stack = []
48
+ @performance_log = "artifacts/task_performance.log"
49
+ @hooks_installed = false
50
+
51
+ class << self
52
+ # @return [Array<Proc>] Global pre-task hooks
53
+ attr_reader :pre_hooks
54
+
55
+ # @return [Array<Proc>] Global post-task hooks
56
+ attr_reader :post_hooks
57
+
58
+ # @return [Hash<String, Array<Proc>>] Task-specific pre-task hooks
59
+ attr_reader :task_specific_pre_hooks
60
+
61
+ # @return [Hash<String, Array<Proc>>] Task-specific post-task hooks
62
+ attr_reader :task_specific_post_hooks
63
+
64
+ # @return [Set<String>] Tasks excluded from hook execution
65
+ attr_reader :excluded_tasks
66
+
67
+ # @return [Boolean] Whether timing is enabled
68
+ attr_reader :timing_enabled
69
+
70
+ # @return [Array<Hash>] Stack of currently executing tasks
71
+ attr_reader :task_stack
72
+
73
+ # @return [String] Path to performance log file
74
+ attr_reader :performance_log
75
+
76
+ # Add a global pre-task hook
77
+ #
78
+ # The hook will be executed before every task runs.
79
+ #
80
+ # @yield [task_name] the name of the task being executed
81
+ # @yieldparam task_name [String] the name of the task
82
+ # @return [void]
83
+ def add_pre_hook(&block)
84
+ @pre_hooks << block
85
+ end
86
+
87
+ # Add a global post-task hook
88
+ #
89
+ # The hook will be executed after every task completes (success or failure).
90
+ #
91
+ # @yield [task_name, duration, result, error] hook parameters
92
+ # @yieldparam task_name [String] the name of the task
93
+ # @yieldparam duration [Float] execution duration in seconds
94
+ # @yieldparam result [Object, nil] the result of the task execution
95
+ # @yieldparam error [Exception, nil] any error that occurred during execution
96
+ # @return [void]
97
+ def add_post_hook(&block)
98
+ @post_hooks << block
99
+ end
100
+
101
+ # Add a pre-task hook for a specific task
102
+ #
103
+ # The hook will only be executed before the specified task runs.
104
+ #
105
+ # @param task_name [String, Symbol] the name of the task
106
+ # @yield [task_name] the name of the task being executed
107
+ # @yieldparam task_name [String] the name of the task
108
+ # @return [void]
109
+ def add_pre_hook_for(task_name, &block)
110
+ @task_specific_pre_hooks[task_name.to_s] ||= []
111
+ @task_specific_pre_hooks[task_name.to_s] << block
112
+ end
113
+
114
+ # Add a post-task hook for a specific task
115
+ #
116
+ # The hook will only be executed after the specified task completes.
117
+ #
118
+ # @param task_name [String, Symbol] the name of the task
119
+ # @yield [task_name, duration, result, error] hook parameters
120
+ # @yieldparam task_name [String] the name of the task
121
+ # @yieldparam duration [Float] execution duration in seconds
122
+ # @yieldparam result [Object, nil] the result of the task execution
123
+ # @yieldparam error [Exception, nil] any error that occurred during execution
124
+ # @return [void]
125
+ def add_post_hook_for(task_name, &block)
126
+ @task_specific_post_hooks[task_name.to_s] ||= []
127
+ @task_specific_post_hooks[task_name.to_s] << block
128
+ end
129
+
130
+ # Execute all pre-task hooks for a given task
131
+ #
132
+ # @param task_name [String] the name of the task
133
+ # @return [void]
134
+ def execute_pre_hooks(task_name)
135
+ return if excluded?(task_name)
136
+
137
+ # Track timing if enabled
138
+ if timing_enabled?
139
+ @task_stack.push({
140
+ name: task_name,
141
+ start_time: Time.now,
142
+ level: @task_stack.length,
143
+ })
144
+ end
145
+
146
+ # Execute global pre-hooks
147
+ @pre_hooks.each { |hook| hook.call(task_name) }
148
+
149
+ # Execute task-specific pre-hooks
150
+ task_hooks = @task_specific_pre_hooks[task_name.to_s] || []
151
+ task_hooks.each { |hook| hook.call(task_name) }
152
+ end
153
+
154
+ # Execute all post-task hooks for a given task
155
+ #
156
+ # @param task_name [String] the name of the task
157
+ # @param duration [Float] execution duration in seconds
158
+ # @param result [Object, nil] the result of the task execution
159
+ # @param error [Exception, nil] any error that occurred during execution
160
+ # @return [void]
161
+ def execute_post_hooks(task_name, duration, result, error)
162
+ return if excluded?(task_name)
163
+
164
+ # Handle timing if enabled
165
+ if timing_enabled?
166
+ task_info = @task_stack.pop
167
+ if task_info && task_info[:name] == task_name && (duration > 0.1)
168
+ # Log performance for tasks that took longer than 0.1 seconds
169
+ log_task_performance(task_name, duration, task_info[:level])
170
+ end
171
+
172
+ # Log task failure if there was an error
173
+ log_task_failure(task_name, duration, error, task_info&.dig(:level) || 0) if error
174
+ end
175
+
176
+ # Execute global post-hooks
177
+ @post_hooks.each { |hook| hook.call(task_name, duration, result, error) }
178
+
179
+ # Execute task-specific post-hooks
180
+ task_hooks = @task_specific_post_hooks[task_name.to_s] || []
181
+ task_hooks.each { |hook| hook.call(task_name, duration, result, error) }
182
+ end
183
+
184
+ # Clear all hooks
185
+ #
186
+ # @return [void]
187
+ def clear_all_hooks
188
+ @pre_hooks.clear
189
+ @post_hooks.clear
190
+ @task_specific_pre_hooks.clear
191
+ @task_specific_post_hooks.clear
192
+ end
193
+
194
+ # Exclude a task from hook execution
195
+ #
196
+ # @param task_name [String, Symbol] the name of the task to exclude
197
+ # @return [void]
198
+ def exclude_task(task_name)
199
+ @excluded_tasks.add(task_name.to_s)
200
+ end
201
+
202
+ # Include a task in hook execution (remove from exclusions)
203
+ #
204
+ # @param task_name [String, Symbol] the name of the task to include
205
+ # @return [void]
206
+ def include_task(task_name)
207
+ @excluded_tasks.delete(task_name.to_s)
208
+ end
209
+
210
+ # Check if a task is excluded from hook execution
211
+ #
212
+ # @param task_name [String, Symbol] the name of the task to check
213
+ # @return [Boolean] true if the task is excluded
214
+ def excluded?(task_name)
215
+ @excluded_tasks.include?(task_name.to_s)
216
+ end
217
+
218
+ # Get statistics about registered hooks
219
+ #
220
+ # @return [Hash] hook statistics
221
+ def stats
222
+ {
223
+ global_pre_hooks: @pre_hooks.size,
224
+ global_post_hooks: @post_hooks.size,
225
+ task_specific_pre_hooks: @task_specific_pre_hooks.values.flatten.size,
226
+ task_specific_post_hooks: @task_specific_post_hooks.values.flatten.size,
227
+ tasks_with_pre_hooks: @task_specific_pre_hooks.keys,
228
+ tasks_with_post_hooks: @task_specific_post_hooks.keys,
229
+ timing_enabled: @timing_enabled,
230
+ task_stack_depth: @task_stack.size,
231
+ }
232
+ end
233
+
234
+ # Enable task timing and performance logging
235
+ #
236
+ # @return [void]
237
+ def enable_timing!
238
+ @timing_enabled = true
239
+ puts "🔧 Task timing enabled".colorize(:grey) if defined?(String.instance_method(:colorize))
240
+ end
241
+
242
+ # Disable task timing and performance logging
243
+ #
244
+ # @return [void]
245
+ def disable_timing!
246
+ @timing_enabled = false
247
+ end
248
+
249
+ # Check if timing is enabled
250
+ #
251
+ # @return [Boolean] true if timing is enabled
252
+ def timing_enabled?
253
+ @timing_enabled == true
254
+ end
255
+
256
+ # Clear performance logs
257
+ #
258
+ # @return [void]
259
+ def clear_performance_logs
260
+ FileUtils.rm_f(@performance_log)
261
+ FileUtils.rm_f("artifacts/task_failures.log")
262
+ end
263
+
264
+ # Setup task hooks by monkey patching Rake::Task
265
+ #
266
+ # This method patches the Rake::Task#execute method to automatically
267
+ # call pre and post hooks for all task executions.
268
+ #
269
+ # @return [void]
270
+ def setup!
271
+ return if @hooks_installed
272
+
273
+ Rake::Task.class_eval do
274
+ alias_method :makit_original_execute, :execute
275
+
276
+ def execute(args = nil)
277
+ start_time = Time.now
278
+ error = nil
279
+ result = nil
280
+
281
+ begin
282
+ # Execute pre-hooks
283
+ Makit::Tasks::HookManager.execute_pre_hooks(name)
284
+
285
+ # Execute the original task
286
+ result = makit_original_execute(args)
287
+ rescue StandardError => e
288
+ error = e
289
+ raise
290
+ ensure
291
+ # Calculate duration
292
+ duration = Time.now - start_time
293
+
294
+ # Execute post-hooks
295
+ Makit::Tasks::HookManager.execute_post_hooks(name, duration, result, error)
296
+ end
297
+
298
+ result
299
+ end
300
+ end
301
+
302
+ @hooks_installed = true
303
+ end
304
+
305
+ # Setup default task hooks
306
+ #
307
+ # This method sets up the default task hooks that provide basic
308
+ # task execution feedback. It should be called during initialization.
309
+ #
310
+ # @return [void]
311
+ def setup_default_hooks
312
+ # Setup the monkey patching first
313
+ setup!
314
+
315
+ # Only setup default hooks if no hooks are already registered
316
+ return unless @pre_hooks.empty? && @post_hooks.empty?
317
+
318
+ # Exclude the init task from hooks to prevent duplication
319
+ exclude_task(:init)
320
+ exclude_task(:default)
321
+
322
+ # Default pre-task hook: log task start to default logger
323
+ add_pre_hook do |task_name|
324
+ log_task_start(task_name)
325
+ end
326
+
327
+ # Default post-task hook: log task completion
328
+ add_post_hook do |task_name, duration, result, error|
329
+ log_task_completion(task_name, duration, result, error)
330
+ end
331
+ end
332
+
333
+ def log_task_start(task_name)
334
+ # return if @logged_tasks.include?(task_name)
335
+ # @logged_tasks.add(task_name)
336
+ # Makit::Logging.info("#{task_name}".colorize(:white).bold)
337
+ end
338
+
339
+ def log_task_completion(task_name, duration, result, error)
340
+ # Makit::Logging.info("#{task_name}".colorize(:white).bold)
341
+ # Makit::Logging.info("Task completed".colorize(:green).bold)
342
+ # Makit::Logging.info("Duration: #{duration.round(2)}s".colorize(:green).bold)
343
+ # Makit::Logging.info("Result: #{result}".colorize(:green).bold)
344
+ # Makit::Logging.info("Error: #{error}".colorize(:red).bold)
345
+ end
346
+
347
+ private
348
+
349
+ # Log task performance to file
350
+ #
351
+ # @param name [String] task name
352
+ # @param duration [Float] execution duration
353
+ # @param level [Integer] nesting level
354
+ # @return [void]
355
+ def log_task_performance(name, duration, level)
356
+ return if ENV["CI"] == "true" && duration < 1.0
357
+
358
+ FileUtils.mkdir_p("artifacts")
359
+ File.open(@performance_log, "a") do |f|
360
+ f.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")},#{name},#{duration.round(4)},#{level}"
361
+ end
362
+ rescue StandardError
363
+ # Silently fail performance logging to not interrupt tasks
364
+ end
365
+
366
+ # Log task failure to file
367
+ #
368
+ # @param name [String] task name
369
+ # @param duration [Float] execution duration
370
+ # @param error [Exception] the error that occurred
371
+ # @param level [Integer] nesting level
372
+ # @return [void]
373
+ def log_task_failure(name, duration, error, _level)
374
+ failure_log = "artifacts/task_failures.log"
375
+ FileUtils.mkdir_p("artifacts")
376
+
377
+ File.open(failure_log, "a") do |f|
378
+ timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
379
+ message = error.message.gsub(",", ";")
380
+ f.puts "#{timestamp},#{name},#{duration.round(4)},#{error.class.name},#{message}"
381
+ end
382
+ rescue StandardError
383
+ # Silently fail error logging
384
+ end
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ # Setup default hooks when the module is loaded
391
+ Makit::Tasks::HookManager.setup_default_hooks