makit 0.0.140 → 0.0.141

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 (153) 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 -69
  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 +203 -203
  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 +388 -385
  37. data/lib/makit/commands/strategies/base.rb +171 -171
  38. data/lib/makit/commands/strategies/child_process.rb +165 -165
  39. data/lib/makit/commands/strategies/factory.rb +136 -136
  40. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  41. data/lib/makit/commands.rb +50 -50
  42. data/lib/makit/configuration/dotnet_project.rb +12 -12
  43. data/lib/makit/configuration/gitlab_helper.rb +58 -58
  44. data/lib/makit/configuration/project.rb +168 -168
  45. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  46. data/lib/makit/configuration/step.rb +34 -34
  47. data/lib/makit/configuration/timeout.rb +74 -74
  48. data/lib/makit/configuration.rb +15 -15
  49. data/lib/makit/content/default_gitignore.rb +7 -7
  50. data/lib/makit/content/default_gitignore.txt +225 -225
  51. data/lib/makit/content/default_rakefile.rb +13 -13
  52. data/lib/makit/content/gem_rakefile.rb +16 -16
  53. data/lib/makit/context.rb +1 -1
  54. data/lib/makit/data.rb +49 -49
  55. data/lib/makit/directories.rb +140 -140
  56. data/lib/makit/directory.rb +262 -262
  57. data/lib/makit/docs/files.rb +89 -89
  58. data/lib/makit/docs/rake.rb +102 -102
  59. data/lib/makit/dotnet/cli.rb +69 -69
  60. data/lib/makit/dotnet/project.rb +217 -217
  61. data/lib/makit/dotnet/solution.rb +38 -38
  62. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  63. data/lib/makit/dotnet/solution_console.rb +264 -264
  64. data/lib/makit/dotnet/solution_maui.rb +354 -354
  65. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  66. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  67. data/lib/makit/dotnet.rb +102 -102
  68. data/lib/makit/email.rb +90 -90
  69. data/lib/makit/environment.rb +142 -142
  70. data/lib/makit/examples/runner.rb +370 -370
  71. data/lib/makit/exceptions.rb +45 -45
  72. data/lib/makit/fileinfo.rb +24 -24
  73. data/lib/makit/files.rb +43 -43
  74. data/lib/makit/gems.rb +40 -40
  75. data/lib/makit/git/cli.rb +54 -54
  76. data/lib/makit/git/repository.rb +90 -90
  77. data/lib/makit/git.rb +98 -98
  78. data/lib/makit/gitlab_runner.rb +59 -59
  79. data/lib/makit/humanize.rb +137 -137
  80. data/lib/makit/indexer.rb +47 -47
  81. data/lib/makit/logging/configuration.rb +308 -308
  82. data/lib/makit/logging/format_registry.rb +84 -84
  83. data/lib/makit/logging/formatters/base.rb +39 -39
  84. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  85. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  86. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  87. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  88. data/lib/makit/logging/log_request.rb +119 -119
  89. data/lib/makit/logging/logger.rb +199 -199
  90. data/lib/makit/logging/sinks/base.rb +91 -91
  91. data/lib/makit/logging/sinks/console.rb +72 -72
  92. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  93. data/lib/makit/logging/sinks/structured.rb +123 -123
  94. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  95. data/lib/makit/logging.rb +565 -565
  96. data/lib/makit/markdown.rb +75 -75
  97. data/lib/makit/mp/basic_object_mp.rb +17 -17
  98. data/lib/makit/mp/command_mp.rb +13 -13
  99. data/lib/makit/mp/command_request.mp.rb +17 -17
  100. data/lib/makit/mp/project_mp.rb +199 -199
  101. data/lib/makit/mp/string_mp.rb +199 -191
  102. data/lib/makit/nuget.rb +74 -74
  103. data/lib/makit/port.rb +32 -32
  104. data/lib/makit/process.rb +163 -163
  105. data/lib/makit/protoc.rb +107 -107
  106. data/lib/makit/rake/cli.rb +196 -196
  107. data/lib/makit/rake/trace_controller.rb +173 -173
  108. data/lib/makit/rake.rb +80 -80
  109. data/lib/makit/ruby/cli.rb +185 -185
  110. data/lib/makit/ruby.rb +25 -25
  111. data/lib/makit/secrets.rb +51 -51
  112. data/lib/makit/serializer.rb +130 -130
  113. data/lib/makit/services/builder.rb +186 -186
  114. data/lib/makit/services/error_handler.rb +226 -226
  115. data/lib/makit/services/repository_manager.rb +231 -231
  116. data/lib/makit/services/validator.rb +112 -112
  117. data/lib/makit/setup/classlib.rb +101 -101
  118. data/lib/makit/setup/gem.rb +268 -268
  119. data/lib/makit/setup/razorclasslib.rb +101 -101
  120. data/lib/makit/setup/runner.rb +54 -54
  121. data/lib/makit/setup.rb +5 -5
  122. data/lib/makit/show.rb +110 -110
  123. data/lib/makit/storage.rb +126 -126
  124. data/lib/makit/symbols.rb +170 -170
  125. data/lib/makit/task_info.rb +130 -130
  126. data/lib/makit/tasks/at_exit.rb +15 -15
  127. data/lib/makit/tasks/build.rb +22 -22
  128. data/lib/makit/tasks/clean.rb +13 -13
  129. data/lib/makit/tasks/configure.rb +10 -10
  130. data/lib/makit/tasks/format.rb +10 -10
  131. data/lib/makit/tasks/hook_manager.rb +443 -443
  132. data/lib/makit/tasks/init.rb +49 -49
  133. data/lib/makit/tasks/integrate.rb +29 -29
  134. data/lib/makit/tasks/pull_incoming.rb +13 -13
  135. data/lib/makit/tasks/setup.rb +13 -13
  136. data/lib/makit/tasks/sync.rb +17 -17
  137. data/lib/makit/tasks/tag.rb +16 -16
  138. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  139. data/lib/makit/tasks/test.rb +22 -22
  140. data/lib/makit/tasks/update.rb +18 -18
  141. data/lib/makit/tasks.rb +20 -20
  142. data/lib/makit/test_cache.rb +239 -239
  143. data/lib/makit/tree.rb +37 -37
  144. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  145. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  146. data/lib/makit/version.rb +99 -99
  147. data/lib/makit/version_util.rb +21 -21
  148. data/lib/makit/wix.rb +95 -95
  149. data/lib/makit/yaml.rb +29 -29
  150. data/lib/makit/zip.rb +17 -17
  151. data/lib/makit copy.rb +44 -44
  152. data/lib/makit.rb +42 -42
  153. metadata +2 -2
@@ -1,73 +1,73 @@
1
- # frozen_string_literal: true
2
-
3
- module Makit
4
- module Commands
5
- module Middleware
6
- # Base class for all command execution middleware.
7
- #
8
- # Middleware provides a way to add cross-cutting concerns like logging,
9
- # caching, validation, and timing to command execution without modifying
10
- # the core execution logic.
11
- #
12
- # @example Creating custom middleware
13
- # class CustomMiddleware < Base
14
- # def call(request, &block)
15
- # # Pre-execution logic
16
- # puts "About to execute: #{request.command}"
17
- #
18
- # # Execute the command
19
- # result = block.call(request)
20
- #
21
- # # Post-execution logic
22
- # puts "Execution completed: #{result.success? ? 'SUCCESS' : 'FAILED'}"
23
- #
24
- # result
25
- # end
26
- # end
27
- class Base
28
- # Execute middleware logic.
29
- #
30
- # This method must be implemented by subclasses to provide the actual
31
- # middleware functionality. The pattern is to perform any pre-execution
32
- # logic, call the block to continue the middleware chain, then perform
33
- # any post-execution logic.
34
- #
35
- # @param request [Request] the command request to process
36
- # @yield [Request] yields the processed request to the next middleware
37
- # @yieldreturn [Result] the execution result
38
- # @return [Result] the final execution result
39
- # @raise [NotImplementedError] if not overridden by subclass
40
- def call(request, &block)
41
- raise NotImplementedError, "#{self.class.name} must implement #call"
42
- end
43
-
44
- # Check if this middleware should be applied to the given request.
45
- #
46
- # Override this method to provide conditional middleware application
47
- # based on request properties.
48
- #
49
- # @param request [Request] the command request
50
- # @return [Boolean] true if middleware should be applied
51
- def applicable?(_request)
52
- true
53
- end
54
-
55
- # Get middleware name for logging and debugging.
56
- #
57
- # @return [String] middleware name
58
- def name
59
- self.class.name.split("::").last
60
- end
61
-
62
- # Get middleware configuration.
63
- #
64
- # Override this method to provide middleware-specific configuration.
65
- #
66
- # @return [Hash] middleware configuration
67
- def config
68
- {}
69
- end
70
- end
71
- end
72
- end
73
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Makit
4
+ module Commands
5
+ module Middleware
6
+ # Base class for all command execution middleware.
7
+ #
8
+ # Middleware provides a way to add cross-cutting concerns like logging,
9
+ # caching, validation, and timing to command execution without modifying
10
+ # the core execution logic.
11
+ #
12
+ # @example Creating custom middleware
13
+ # class CustomMiddleware < Base
14
+ # def call(request, &block)
15
+ # # Pre-execution logic
16
+ # puts "About to execute: #{request.command}"
17
+ #
18
+ # # Execute the command
19
+ # result = block.call(request)
20
+ #
21
+ # # Post-execution logic
22
+ # puts "Execution completed: #{result.success? ? 'SUCCESS' : 'FAILED'}"
23
+ #
24
+ # result
25
+ # end
26
+ # end
27
+ class Base
28
+ # Execute middleware logic.
29
+ #
30
+ # This method must be implemented by subclasses to provide the actual
31
+ # middleware functionality. The pattern is to perform any pre-execution
32
+ # logic, call the block to continue the middleware chain, then perform
33
+ # any post-execution logic.
34
+ #
35
+ # @param request [Request] the command request to process
36
+ # @yield [Request] yields the processed request to the next middleware
37
+ # @yieldreturn [Result] the execution result
38
+ # @return [Result] the final execution result
39
+ # @raise [NotImplementedError] if not overridden by subclass
40
+ def call(request, &block)
41
+ raise NotImplementedError, "#{self.class.name} must implement #call"
42
+ end
43
+
44
+ # Check if this middleware should be applied to the given request.
45
+ #
46
+ # Override this method to provide conditional middleware application
47
+ # based on request properties.
48
+ #
49
+ # @param request [Request] the command request
50
+ # @return [Boolean] true if middleware should be applied
51
+ def applicable?(_request)
52
+ true
53
+ end
54
+
55
+ # Get middleware name for logging and debugging.
56
+ #
57
+ # @return [String] middleware name
58
+ def name
59
+ self.class.name.split("::").last
60
+ end
61
+
62
+ # Get middleware configuration.
63
+ #
64
+ # Override this method to provide middleware-specific configuration.
65
+ #
66
+ # @return [Hash] middleware configuration
67
+ def config
68
+ {}
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,248 +1,248 @@
1
- # frozen_string_literal: true
2
-
3
- require "fileutils"
4
- require "digest"
5
-
6
- module Makit
7
- module Commands
8
- module Middleware
9
- # Caching middleware for command execution results
10
- #
11
- # This middleware provides caching functionality for command execution,
12
- # allowing commands to be cached based on their content and timestamp.
13
- #
14
- # @example Basic usage
15
- # runner = Commands::Runner.new(middleware: [Cache.new])
16
- # result = runner.execute("git --version")
17
- #
18
- # @example With custom cache directory
19
- # cache = Cache.new(cache_dir: "custom/cache")
20
- # runner = Commands::Runner.new(middleware: [cache])
21
- class Cache < Base
22
- # @!attribute [r] cache_dir
23
- # @return [String] cache directory path
24
- attr_reader :cache_dir
25
-
26
- # @!attribute [r] enabled
27
- # @return [Boolean] whether caching is enabled
28
- attr_reader :enabled
29
-
30
- # Initialize caching middleware
31
- #
32
- # @param cache_dir [String] directory to store cache files
33
- # @param enabled [Boolean] whether caching is enabled
34
- # @param options [Hash] additional options
35
- def initialize(cache_dir: nil, enabled: true, **options)
36
- @cache_dir = cache_dir || default_cache_dir
37
- @enabled = enabled
38
- @options = options
39
-
40
- # Ensure cache directory exists
41
- FileUtils.mkdir_p(@cache_dir) if @enabled
42
- end
43
-
44
- # Check if this middleware applies to the given request
45
- #
46
- # @param request [Request] command request
47
- # @return [Boolean] true if caching should be applied
48
- def applicable?(request)
49
- @enabled && request.metadata[:cache_key].present?
50
- end
51
-
52
- # Execute the middleware chain with caching
53
- #
54
- # @param request [Request] command request
55
- # @yield [Request] yields to next middleware or execution
56
- # @return [Result] execution result
57
- def call(request, &block)
58
- return block.call(request) unless applicable?(request)
59
-
60
- cache_key = request.metadata[:cache_key]
61
- cache_timestamp = request.metadata[:cache_timestamp]
62
- cache_file = get_cache_file(cache_key)
63
-
64
- # Check if we have a valid cached result
65
- if cache_file && File.exist?(cache_file) && cache_timestamp
66
- cached_result = load_cached_result(cache_file, cache_timestamp)
67
- return cached_result if cached_result
68
- end
69
-
70
- # Execute the command
71
- result = block.call(request)
72
-
73
- # Cache the result if successful
74
- cache_result(cache_file, result) if result.success?
75
-
76
- result
77
- end
78
-
79
- # Get middleware configuration
80
- #
81
- # @return [Hash] configuration hash
82
- def config
83
- {
84
- cache_dir: @cache_dir,
85
- enabled: @enabled,
86
- options: @options,
87
- }
88
- end
89
-
90
- private
91
-
92
- # Get the default cache directory
93
- #
94
- # @return [String] default cache directory path
95
- def default_cache_dir
96
- File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands", "cache")
97
- end
98
-
99
- # Get cache file path for a given key
100
- #
101
- # @param cache_key [String] cache key
102
- # @return [String] cache file path
103
- def get_cache_file(cache_key)
104
- return nil unless cache_key
105
-
106
- File.join(@cache_dir, "#{cache_key}.pb")
107
- end
108
-
109
- # Load cached result if it's newer than the timestamp
110
- #
111
- # @param cache_file [String] path to cache file
112
- # @param timestamp [Time] minimum timestamp for cache validity
113
- # @return [Result, nil] cached result or nil if not valid
114
- def load_cached_result(cache_file, timestamp)
115
- return nil unless File.exist?(cache_file)
116
-
117
- cache_mtime = File.mtime(cache_file)
118
- return nil if cache_mtime <= timestamp
119
-
120
- begin
121
- # Load the cached command result
122
- cached_command = Makit::Serializer.open(cache_file, Makit::V1::Command)
123
-
124
- # Convert to new Result format
125
- convert_cached_command_to_result(cached_command)
126
- rescue StandardError => e
127
- Makit::Logging.debug("Failed to load cached result", {
128
- cache_file: cache_file,
129
- error: e.message,
130
- })
131
- nil
132
- end
133
- end
134
-
135
- # Cache a successful result
136
- #
137
- # @param cache_file [String] path to cache file
138
- # @param result [Result] result to cache
139
- def cache_result(cache_file, result)
140
- return unless cache_file && result.success?
141
-
142
- begin
143
- # Convert Result to legacy Command format for caching
144
- legacy_command = convert_result_to_legacy_command(result)
145
-
146
- # Save to cache
147
- Makit::Serializer.save_as(cache_file, legacy_command)
148
-
149
- Makit::Logging.debug("Cached command result", {
150
- cache_file: cache_file,
151
- command: result.command,
152
- success: result.success?,
153
- })
154
- rescue StandardError => e
155
- Makit::Logging.warn("Failed to cache result", {
156
- cache_file: cache_file,
157
- error: e.message,
158
- })
159
- end
160
- end
161
-
162
- # Convert cached Command to Result format
163
- #
164
- # @param cached_command [Makit::V1::Command] cached command
165
- # @return [Result] converted result
166
- def convert_cached_command_to_result(cached_command)
167
- # Create a Result object from the cached command
168
- Result.new(
169
- command: "#{cached_command.name} #{cached_command.arguments.join(" ")}",
170
- exit_code: cached_command.exit_code,
171
- stdout: cached_command.output,
172
- stderr: cached_command.error,
173
- started_at: convert_protobuf_timestamp_to_time(cached_command.started_at),
174
- duration: convert_protobuf_duration_to_float(cached_command.duration),
175
- metadata: {
176
- cached: true,
177
- directory: cached_command.directory,
178
- },
179
- )
180
- end
181
-
182
- # Convert Result to legacy Command format for caching
183
- #
184
- # @param result [Result] result to convert
185
- # @return [Makit::V1::Command] legacy command object
186
- def convert_result_to_legacy_command(result)
187
- command_parts = result.command.split
188
- name = command_parts.first
189
- arguments = command_parts[1..] || []
190
-
191
- Makit::V1::Command.new(
192
- name: name,
193
- arguments: arguments,
194
- exit_code: result.exit_code,
195
- output: result.stdout,
196
- error: result.stderr,
197
- started_at: convert_time_to_protobuf_timestamp(result.started_at),
198
- duration: convert_duration_to_protobuf(result.duration),
199
- directory: result.metadata[:directory] || Dir.pwd,
200
- )
201
- end
202
-
203
- # Convert protobuf timestamp to Time
204
- #
205
- # @param timestamp [Google::Protobuf::Timestamp] protobuf timestamp
206
- # @return [Time, nil] converted time
207
- def convert_protobuf_timestamp_to_time(timestamp)
208
- return nil unless timestamp
209
-
210
- Time.at(timestamp.seconds, timestamp.nanos, :nsec)
211
- end
212
-
213
- # Convert protobuf duration to float seconds
214
- #
215
- # @param duration [Google::Protobuf::Duration] protobuf duration
216
- # @return [Float, nil] duration in seconds
217
- def convert_protobuf_duration_to_float(duration)
218
- return nil unless duration
219
-
220
- duration.seconds + (duration.nanos / 1_000_000_000.0)
221
- end
222
-
223
- # Convert Time to protobuf timestamp
224
- #
225
- # @param time [Time] time to convert
226
- # @return [Google::Protobuf::Timestamp] protobuf timestamp
227
- def convert_time_to_protobuf_timestamp(time)
228
- return nil unless time
229
-
230
- Google::Protobuf::Timestamp.new(seconds: time.to_i, nanos: time.nsec)
231
- end
232
-
233
- # Convert duration to protobuf duration
234
- #
235
- # @param duration [Float] duration in seconds
236
- # @return [Google::Protobuf::Duration] protobuf duration
237
- def convert_duration_to_protobuf(duration)
238
- return nil unless duration
239
-
240
- seconds = duration.to_i
241
- nanos = ((duration - seconds) * 1_000_000_000).to_i
242
-
243
- Google::Protobuf::Duration.new(seconds: seconds, nanos: nanos)
244
- end
245
- end
246
- end
247
- end
248
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "digest"
5
+
6
+ module Makit
7
+ module Commands
8
+ module Middleware
9
+ # Caching middleware for command execution results
10
+ #
11
+ # This middleware provides caching functionality for command execution,
12
+ # allowing commands to be cached based on their content and timestamp.
13
+ #
14
+ # @example Basic usage
15
+ # runner = Commands::Runner.new(middleware: [Cache.new])
16
+ # result = runner.execute("git --version")
17
+ #
18
+ # @example With custom cache directory
19
+ # cache = Cache.new(cache_dir: "custom/cache")
20
+ # runner = Commands::Runner.new(middleware: [cache])
21
+ class Cache < Base
22
+ # @!attribute [r] cache_dir
23
+ # @return [String] cache directory path
24
+ attr_reader :cache_dir
25
+
26
+ # @!attribute [r] enabled
27
+ # @return [Boolean] whether caching is enabled
28
+ attr_reader :enabled
29
+
30
+ # Initialize caching middleware
31
+ #
32
+ # @param cache_dir [String] directory to store cache files
33
+ # @param enabled [Boolean] whether caching is enabled
34
+ # @param options [Hash] additional options
35
+ def initialize(cache_dir: nil, enabled: true, **options)
36
+ @cache_dir = cache_dir || default_cache_dir
37
+ @enabled = enabled
38
+ @options = options
39
+
40
+ # Ensure cache directory exists
41
+ FileUtils.mkdir_p(@cache_dir) if @enabled
42
+ end
43
+
44
+ # Check if this middleware applies to the given request
45
+ #
46
+ # @param request [Request] command request
47
+ # @return [Boolean] true if caching should be applied
48
+ def applicable?(request)
49
+ @enabled && request.metadata[:cache_key].present?
50
+ end
51
+
52
+ # Execute the middleware chain with caching
53
+ #
54
+ # @param request [Request] command request
55
+ # @yield [Request] yields to next middleware or execution
56
+ # @return [Result] execution result
57
+ def call(request, &block)
58
+ return block.call(request) unless applicable?(request)
59
+
60
+ cache_key = request.metadata[:cache_key]
61
+ cache_timestamp = request.metadata[:cache_timestamp]
62
+ cache_file = get_cache_file(cache_key)
63
+
64
+ # Check if we have a valid cached result
65
+ if cache_file && File.exist?(cache_file) && cache_timestamp
66
+ cached_result = load_cached_result(cache_file, cache_timestamp)
67
+ return cached_result if cached_result
68
+ end
69
+
70
+ # Execute the command
71
+ result = block.call(request)
72
+
73
+ # Cache the result if successful
74
+ cache_result(cache_file, result) if result.success?
75
+
76
+ result
77
+ end
78
+
79
+ # Get middleware configuration
80
+ #
81
+ # @return [Hash] configuration hash
82
+ def config
83
+ {
84
+ cache_dir: @cache_dir,
85
+ enabled: @enabled,
86
+ options: @options,
87
+ }
88
+ end
89
+
90
+ private
91
+
92
+ # Get the default cache directory
93
+ #
94
+ # @return [String] default cache directory path
95
+ def default_cache_dir
96
+ File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands", "cache")
97
+ end
98
+
99
+ # Get cache file path for a given key
100
+ #
101
+ # @param cache_key [String] cache key
102
+ # @return [String] cache file path
103
+ def get_cache_file(cache_key)
104
+ return nil unless cache_key
105
+
106
+ File.join(@cache_dir, "#{cache_key}.pb")
107
+ end
108
+
109
+ # Load cached result if it's newer than the timestamp
110
+ #
111
+ # @param cache_file [String] path to cache file
112
+ # @param timestamp [Time] minimum timestamp for cache validity
113
+ # @return [Result, nil] cached result or nil if not valid
114
+ def load_cached_result(cache_file, timestamp)
115
+ return nil unless File.exist?(cache_file)
116
+
117
+ cache_mtime = File.mtime(cache_file)
118
+ return nil if cache_mtime <= timestamp
119
+
120
+ begin
121
+ # Load the cached command result
122
+ cached_command = Makit::Serializer.open(cache_file, Makit::V1::Command)
123
+
124
+ # Convert to new Result format
125
+ convert_cached_command_to_result(cached_command)
126
+ rescue StandardError => e
127
+ Makit::Logging.debug("Failed to load cached result", {
128
+ cache_file: cache_file,
129
+ error: e.message,
130
+ })
131
+ nil
132
+ end
133
+ end
134
+
135
+ # Cache a successful result
136
+ #
137
+ # @param cache_file [String] path to cache file
138
+ # @param result [Result] result to cache
139
+ def cache_result(cache_file, result)
140
+ return unless cache_file && result.success?
141
+
142
+ begin
143
+ # Convert Result to legacy Command format for caching
144
+ legacy_command = convert_result_to_legacy_command(result)
145
+
146
+ # Save to cache
147
+ Makit::Serializer.save_as(cache_file, legacy_command)
148
+
149
+ Makit::Logging.debug("Cached command result", {
150
+ cache_file: cache_file,
151
+ command: result.command,
152
+ success: result.success?,
153
+ })
154
+ rescue StandardError => e
155
+ Makit::Logging.warn("Failed to cache result", {
156
+ cache_file: cache_file,
157
+ error: e.message,
158
+ })
159
+ end
160
+ end
161
+
162
+ # Convert cached Command to Result format
163
+ #
164
+ # @param cached_command [Makit::V1::Command] cached command
165
+ # @return [Result] converted result
166
+ def convert_cached_command_to_result(cached_command)
167
+ # Create a Result object from the cached command
168
+ Result.new(
169
+ command: "#{cached_command.name} #{cached_command.arguments.join(" ")}",
170
+ exit_code: cached_command.exit_code,
171
+ stdout: cached_command.output,
172
+ stderr: cached_command.error,
173
+ started_at: convert_protobuf_timestamp_to_time(cached_command.started_at),
174
+ duration: convert_protobuf_duration_to_float(cached_command.duration),
175
+ metadata: {
176
+ cached: true,
177
+ directory: cached_command.directory,
178
+ },
179
+ )
180
+ end
181
+
182
+ # Convert Result to legacy Command format for caching
183
+ #
184
+ # @param result [Result] result to convert
185
+ # @return [Makit::V1::Command] legacy command object
186
+ def convert_result_to_legacy_command(result)
187
+ command_parts = result.command.split
188
+ name = command_parts.first
189
+ arguments = command_parts[1..] || []
190
+
191
+ Makit::V1::Command.new(
192
+ name: name,
193
+ arguments: arguments,
194
+ exit_code: result.exit_code,
195
+ output: result.stdout,
196
+ error: result.stderr,
197
+ started_at: convert_time_to_protobuf_timestamp(result.started_at),
198
+ duration: convert_duration_to_protobuf(result.duration),
199
+ directory: result.metadata[:directory] || Dir.pwd,
200
+ )
201
+ end
202
+
203
+ # Convert protobuf timestamp to Time
204
+ #
205
+ # @param timestamp [Google::Protobuf::Timestamp] protobuf timestamp
206
+ # @return [Time, nil] converted time
207
+ def convert_protobuf_timestamp_to_time(timestamp)
208
+ return nil unless timestamp
209
+
210
+ Time.at(timestamp.seconds, timestamp.nanos, :nsec)
211
+ end
212
+
213
+ # Convert protobuf duration to float seconds
214
+ #
215
+ # @param duration [Google::Protobuf::Duration] protobuf duration
216
+ # @return [Float, nil] duration in seconds
217
+ def convert_protobuf_duration_to_float(duration)
218
+ return nil unless duration
219
+
220
+ duration.seconds + (duration.nanos / 1_000_000_000.0)
221
+ end
222
+
223
+ # Convert Time to protobuf timestamp
224
+ #
225
+ # @param time [Time] time to convert
226
+ # @return [Google::Protobuf::Timestamp] protobuf timestamp
227
+ def convert_time_to_protobuf_timestamp(time)
228
+ return nil unless time
229
+
230
+ Google::Protobuf::Timestamp.new(seconds: time.to_i, nanos: time.nsec)
231
+ end
232
+
233
+ # Convert duration to protobuf duration
234
+ #
235
+ # @param duration [Float] duration in seconds
236
+ # @return [Google::Protobuf::Duration] protobuf duration
237
+ def convert_duration_to_protobuf(duration)
238
+ return nil unless duration
239
+
240
+ seconds = duration.to_i
241
+ nanos = ((duration - seconds) * 1_000_000_000).to_i
242
+
243
+ Google::Protobuf::Duration.new(seconds: seconds, nanos: nanos)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end