makit 0.0.111 → 0.0.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +41 -41
- data/exe/makit +5 -5
- data/lib/makit/apache.rb +28 -28
- data/lib/makit/cli/build_commands.rb +500 -500
- data/lib/makit/cli/generators/base_generator.rb +74 -74
- data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
- data/lib/makit/cli/generators/generator_factory.rb +49 -49
- data/lib/makit/cli/generators/node_generator.rb +50 -50
- data/lib/makit/cli/generators/ruby_generator.rb +77 -77
- data/lib/makit/cli/generators/rust_generator.rb +50 -50
- data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
- data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
- data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
- data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
- data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
- data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
- data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
- data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
- data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
- data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
- data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
- data/lib/makit/cli/main.rb +62 -62
- data/lib/makit/cli/project_commands.rb +868 -868
- data/lib/makit/cli/repository_commands.rb +661 -661
- data/lib/makit/cli/utility_commands.rb +521 -521
- data/lib/makit/commands/factory.rb +359 -359
- data/lib/makit/commands/middleware/base.rb +73 -73
- data/lib/makit/commands/middleware/cache.rb +248 -248
- data/lib/makit/commands/middleware/command_logger.rb +320 -323
- data/lib/makit/commands/middleware/unified_logger.rb +243 -243
- data/lib/makit/commands/middleware/validator.rb +269 -269
- data/lib/makit/commands/request.rb +254 -254
- data/lib/makit/commands/result.rb +323 -323
- data/lib/makit/commands/runner.rb +337 -317
- data/lib/makit/commands/strategies/base.rb +160 -160
- data/lib/makit/commands/strategies/synchronous.rb +134 -134
- data/lib/makit/commands.rb +51 -42
- data/lib/makit/configuration/gitlab_helper.rb +60 -60
- data/lib/makit/configuration/project.rb +127 -127
- data/lib/makit/configuration/rakefile_helper.rb +43 -43
- data/lib/makit/configuration/step.rb +34 -34
- data/lib/makit/configuration.rb +14 -14
- data/lib/makit/content/default_gitignore.rb +7 -7
- data/lib/makit/content/default_rakefile.rb +13 -13
- data/lib/makit/content/gem_rakefile.rb +16 -16
- data/lib/makit/context.rb +1 -1
- data/lib/makit/data.rb +49 -49
- data/lib/makit/directories.rb +141 -141
- data/lib/makit/directory.rb +262 -262
- data/lib/makit/docs/files.rb +89 -89
- data/lib/makit/docs/rake.rb +102 -102
- data/lib/makit/dotnet/project.rb +153 -153
- data/lib/makit/dotnet/solution.rb +38 -38
- data/lib/makit/dotnet/solution_classlib.rb +239 -239
- data/lib/makit/dotnet/solution_console.rb +264 -264
- data/lib/makit/dotnet/solution_maui.rb +354 -354
- data/lib/makit/dotnet/solution_wasm.rb +275 -275
- data/lib/makit/dotnet/solution_wpf.rb +304 -304
- data/lib/makit/dotnet.rb +102 -102
- data/lib/makit/email.rb +90 -90
- data/lib/makit/environment.rb +142 -142
- data/lib/makit/examples/runner.rb +370 -370
- data/lib/makit/exceptions.rb +45 -45
- data/lib/makit/fileinfo.rb +24 -24
- data/lib/makit/files.rb +43 -43
- data/lib/makit/gems.rb +40 -40
- data/lib/makit/git/cli.rb +54 -54
- data/lib/makit/git/repository.rb +90 -90
- data/lib/makit/git.rb +98 -98
- data/lib/makit/gitlab_runner.rb +59 -59
- data/lib/makit/humanize.rb +137 -137
- data/lib/makit/indexer.rb +47 -47
- data/lib/makit/logging/configuration.rb +305 -305
- data/lib/makit/logging/formatters/base.rb +39 -39
- data/lib/makit/logging/formatters/console_formatter.rb +140 -127
- data/lib/makit/logging/formatters/json_formatter.rb +65 -65
- data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
- data/lib/makit/logging/formatters/text_formatter.rb +64 -64
- data/lib/makit/logging/log_request.rb +115 -115
- data/lib/makit/logging/logger.rb +163 -159
- data/lib/makit/logging/sinks/console.rb +72 -72
- data/lib/makit/logging/sinks/file_sink.rb +92 -92
- data/lib/makit/logging/sinks/unified_file_sink.rb +303 -303
- data/lib/makit/logging.rb +530 -521
- data/lib/makit/markdown.rb +75 -75
- data/lib/makit/mp/basic_object_mp.rb +17 -17
- data/lib/makit/mp/command_mp.rb +13 -13
- data/lib/makit/mp/command_request.mp.rb +17 -17
- data/lib/makit/mp/project_mp.rb +199 -199
- data/lib/makit/mp/string_mp.rb +193 -348
- data/lib/makit/nuget.rb +74 -74
- data/lib/makit/port.rb +32 -32
- data/lib/makit/process.rb +163 -163
- data/lib/makit/protoc.rb +107 -107
- data/lib/makit/rake/cli.rb +196 -196
- data/lib/makit/rake.rb +25 -25
- data/lib/makit/ruby/cli.rb +185 -185
- data/lib/makit/ruby.rb +25 -25
- data/lib/makit/secrets.rb +51 -51
- data/lib/makit/serializer.rb +130 -117
- data/lib/makit/services/builder.rb +186 -186
- data/lib/makit/services/error_handler.rb +226 -226
- data/lib/makit/services/repository_manager.rb +229 -229
- data/lib/makit/services/validator.rb +112 -112
- data/lib/makit/setup/classlib.rb +53 -53
- data/lib/makit/setup/gem.rb +30 -17
- data/lib/makit/setup/runner.rb +45 -40
- data/lib/makit/setup.rb +5 -0
- data/lib/makit/show.rb +110 -110
- data/lib/makit/storage.rb +126 -126
- data/lib/makit/symbols.rb +170 -161
- data/lib/makit/task_info.rb +128 -128
- data/lib/makit/tasks/at_exit.rb +13 -13
- data/lib/makit/tasks/build.rb +19 -18
- data/lib/makit/tasks/clean.rb +11 -11
- data/lib/makit/tasks/hook_manager.rb +393 -239
- data/lib/makit/tasks/init.rb +47 -47
- data/lib/makit/tasks/integrate.rb +17 -15
- data/lib/makit/tasks/pull_incoming.rb +11 -12
- data/lib/makit/tasks/setup.rb +6 -6
- data/lib/makit/tasks/sync.rb +12 -11
- data/lib/makit/tasks/tag.rb +15 -0
- data/lib/makit/tasks/task_monkey_patch.rb +79 -79
- data/lib/makit/tasks.rb +10 -0
- data/lib/makit/test_cache.rb +239 -239
- data/lib/makit/tree.rb +37 -37
- data/lib/makit/v1/makit.v1_pb.rb +34 -34
- data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
- data/lib/makit/version.rb +5 -5
- data/lib/makit/version_util.rb +21 -0
- data/lib/makit/wix.rb +95 -95
- data/lib/makit/yaml.rb +29 -29
- data/lib/makit/zip.rb +17 -17
- data/lib/makit copy.rb +44 -0
- data/lib/makit.rb +40 -8
- metadata +50 -7
- data/lib/makit/command_runner.rb +0 -463
- data/lib/makit/commands/compatibility.rb +0 -365
- data/lib/makit/task_hooks.rb +0 -125
@@ -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
|
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 => 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 => 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 => 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 => 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
|