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,160 +1,171 @@
1
- # frozen_string_literal: true
2
-
3
- require "open3"
4
- require "timeout"
5
-
6
- module Makit
7
- module Commands
8
- module Strategies
9
- # Base class for command execution strategies.
10
- #
11
- # Execution strategies define how commands are actually executed -
12
- # synchronously, asynchronously, in parallel, etc. This provides
13
- # flexibility in execution patterns while maintaining a consistent
14
- # interface.
15
- #
16
- # @example Creating custom strategy
17
- # class CustomStrategy < Base
18
- # def execute(request)
19
- # # Custom execution logic
20
- # result = Result.new(command: request.to_shell_command)
21
- # # ... perform execution ...
22
- # result.finish!(exit_code: 0, stdout: "success")
23
- # end
24
- # end
25
- class Base
26
- # Execute a command request.
27
- #
28
- # This method must be implemented by subclasses to provide the actual
29
- # command execution logic. The implementation should create a Result
30
- # object and populate it with execution details.
31
- #
32
- # @param request [Request] the command request to execute
33
- # @return [Result] the execution result
34
- # @raise [NotImplementedError] if not overridden by subclass
35
- def execute(request)
36
- raise NotImplementedError, "#{self.class.name} must implement #execute"
37
- end
38
-
39
- # Execute multiple requests (default: sequential execution).
40
- #
41
- # Override this method in subclasses to provide optimized batch execution
42
- # such as parallel execution.
43
- #
44
- # @param requests [Array<Request>] requests to execute
45
- # @return [Array<Result>] execution results in same order
46
- def execute_batch(requests)
47
- requests.map { |request| execute(request) }
48
- end
49
-
50
- # Check if this strategy can handle the given request.
51
- #
52
- # Override this method to provide conditional strategy selection
53
- # based on request properties.
54
- #
55
- # @param request [Request] the command request
56
- # @return [Boolean] true if strategy can handle the request
57
- def supports?(_request)
58
- true
59
- end
60
-
61
- # Get strategy name for logging and debugging.
62
- #
63
- # @return [String] strategy name
64
- def name
65
- self.class.name.split("::").last
66
- end
67
-
68
- # Get strategy configuration.
69
- #
70
- # @return [Hash] strategy configuration
71
- def config
72
- {}
73
- end
74
-
75
- protected
76
-
77
- # Execute command using Open3 for cross-platform compatibility.
78
- #
79
- # This is a helper method that subclasses can use for actual
80
- # system command execution.
81
- #
82
- # @param request [Request] the command request
83
- # @return [Result] execution result
84
- def execute_with_open3(request)
85
- result = Result.new(
86
- command: request.to_shell_command,
87
- started_at: Time.now,
88
- )
89
-
90
- begin
91
- # Change to specified directory if provided
92
- Dir.chdir(request.directory) do
93
- # Execute command with timeout using Timeout module
94
- stdout, stderr, status = if request.timeout&.positive?
95
- Timeout.timeout(request.timeout) do
96
- Open3.capture3(
97
- request.environment,
98
- request.command,
99
- *request.arguments
100
- )
101
- end
102
- else
103
- Open3.capture3(
104
- request.environment,
105
- request.command,
106
- *request.arguments
107
- )
108
- end
109
-
110
- result.finish!(
111
- exit_code: status.exitstatus,
112
- stdout: stdout,
113
- stderr: stderr,
114
- )
115
- end
116
- rescue Timeout::Error => e
117
- result.finish!(
118
- exit_code: 124, # timeout exit code
119
- stderr: "Command timed out after #{request.timeout} seconds",
120
- ).add_metadata(:timeout, true)
121
- .add_metadata(:error, e.message)
122
- rescue StandardError => e
123
- result.finish!(
124
- exit_code: 1,
125
- stderr: e.message,
126
- ).add_metadata(:error_class, e.class.name)
127
- .add_metadata(:error, e.message)
128
- end
129
-
130
- result
131
- end
132
-
133
- # Validate that the command exists and is executable.
134
- #
135
- # @param command [String] command to validate
136
- # @return [Boolean] true if command is available
137
- def command_available?(command)
138
- system("which #{command} > /dev/null 2>&1") ||
139
- system("where #{command} > nul 2>&1") # Windows
140
- end
141
-
142
- # Get the full path to a command.
143
- #
144
- # @param command [String] command name
145
- # @return [String, nil] full path to command or nil if not found
146
- def which(command)
147
- # Try Unix-style which first
148
- path = `which #{command} 2>/dev/null`.strip
149
- return path unless path.empty?
150
-
151
- # Try Windows-style where
152
- path = `where #{command} 2>nul`.strip
153
- return path unless path.empty?
154
-
155
- nil
156
- end
157
- end
158
- end
159
- end
160
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "timeout"
5
+
6
+ module Makit
7
+ module Commands
8
+ module Strategies
9
+ # Base class for command execution strategies.
10
+ #
11
+ # Execution strategies define how commands are actually executed -
12
+ # synchronously, asynchronously, in parallel, etc. This provides
13
+ # flexibility in execution patterns while maintaining a consistent
14
+ # interface.
15
+ #
16
+ # @example Creating custom strategy
17
+ # class CustomStrategy < Base
18
+ # def execute(request)
19
+ # # Custom execution logic
20
+ # result = Result.new(command: request.to_shell_command)
21
+ # # ... perform execution ...
22
+ # result.finish!(exit_code: 0, stdout: "success")
23
+ # end
24
+ # end
25
+ class Base
26
+ # Execute a command request.
27
+ #
28
+ # This method must be implemented by subclasses to provide the actual
29
+ # command execution logic. The implementation should create a Result
30
+ # object and populate it with execution details.
31
+ #
32
+ # @param request [Request] the command request to execute
33
+ # @return [Result] the execution result
34
+ # @raise [NotImplementedError] if not overridden by subclass
35
+ def execute(request)
36
+ raise NotImplementedError, "#{self.class.name} must implement #execute"
37
+ end
38
+
39
+ # Execute multiple requests (default: sequential execution).
40
+ #
41
+ # Override this method in subclasses to provide optimized batch execution
42
+ # such as parallel execution.
43
+ #
44
+ # @param requests [Array<Request>] requests to execute
45
+ # @return [Array<Result>] execution results in same order
46
+ def execute_batch(requests)
47
+ requests.map { |request| execute(request) }
48
+ end
49
+
50
+ # Check if this strategy can handle the given request.
51
+ #
52
+ # Override this method to provide conditional strategy selection
53
+ # based on request properties.
54
+ #
55
+ # @param request [Request] the command request
56
+ # @return [Boolean] true if strategy can handle the request
57
+ def supports?(_request)
58
+ true
59
+ end
60
+
61
+ # Get strategy name for logging and debugging.
62
+ #
63
+ # @return [String] strategy name
64
+ def name
65
+ self.class.name.split("::").last
66
+ end
67
+
68
+ # Get strategy configuration.
69
+ #
70
+ # @return [Hash] strategy configuration
71
+ def config
72
+ {}
73
+ end
74
+
75
+ protected
76
+
77
+ # Execute command using Open3 for cross-platform compatibility.
78
+ #
79
+ # This is a helper method that subclasses can use for actual
80
+ # system command execution.
81
+ #
82
+ # @param request [Request] the command request
83
+ # @return [Result] execution result
84
+ def execute_with_open3(request)
85
+ result = Result.new(
86
+ command: request.to_shell_command,
87
+ started_at: Time.now,
88
+ )
89
+
90
+ begin
91
+ # Change to specified directory if provided
92
+ Dir.chdir(request.directory) do
93
+ # Execute command with timeout using Timeout module
94
+ stdout, stderr, status = if request.timeout&.positive?
95
+ Timeout.timeout(request.timeout) do
96
+ Open3.capture3(
97
+ request.environment,
98
+ request.command,
99
+ *request.arguments
100
+ )
101
+ end
102
+ else
103
+ Open3.capture3(
104
+ request.environment,
105
+ request.command,
106
+ *request.arguments
107
+ )
108
+ end
109
+
110
+ result.finish!(
111
+ exit_code: status.exitstatus,
112
+ stdout: stdout,
113
+ stderr: stderr,
114
+ )
115
+ end
116
+ rescue Timeout::Error => e
117
+ result.finish!(
118
+ exit_code: 124, # timeout exit code
119
+ stderr: "Command timed out after #{request.timeout} seconds",
120
+ ).add_metadata(:timeout, true)
121
+ .add_metadata(:error, e.message)
122
+ rescue StandardError => e
123
+ result.finish!(
124
+ exit_code: 1,
125
+ stderr: e.message,
126
+ ).add_metadata(:error_class, e.class.name)
127
+ .add_metadata(:error, e.message)
128
+ end
129
+
130
+ result
131
+ end
132
+
133
+ # Validate that the command exists and is executable.
134
+ #
135
+ # @param command [String] command to validate
136
+ # @return [Boolean] true if command is available
137
+ def command_available?(command)
138
+ # Skip validation for dotnet commands on Windows due to path issues
139
+ return true if command == "dotnet" && RUBY_PLATFORM.match?(/mswin|mingw/)
140
+
141
+ # Try Unix-style which first
142
+ return true if system("which #{command} > /dev/null 2>&1")
143
+
144
+ # Try Windows-style where with proper quoting
145
+ return true if system("where \"#{command}\" > nul 2>&1")
146
+
147
+ # Fallback: try to execute the command directly
148
+ system("#{command} --version > nul 2>&1") ||
149
+ system("#{command} --help > nul 2>&1") ||
150
+ system("#{command} /? > nul 2>&1")
151
+ end
152
+
153
+ # Get the full path to a command.
154
+ #
155
+ # @param command [String] command name
156
+ # @return [String, nil] full path to command or nil if not found
157
+ def which(command)
158
+ # Try Unix-style which first
159
+ path = `which #{command} 2>/dev/null`.strip
160
+ return path unless path.empty?
161
+
162
+ # Try Windows-style where
163
+ path = `where #{command} 2>nul`.strip
164
+ return path unless path.empty?
165
+
166
+ nil
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -1,134 +1,139 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
- require_relative "../result"
5
-
6
- module Makit
7
- module Commands
8
- module Strategies
9
- # Synchronous command execution strategy.
10
- #
11
- # This strategy executes commands one at a time, blocking until each
12
- # command completes. This is the simplest and most reliable execution
13
- # pattern, suitable for most use cases.
14
- #
15
- # @example Using synchronous execution
16
- # strategy = Synchronous.new
17
- # request = Request.new(command: "echo", arguments: ["hello"])
18
- # result = strategy.execute(request)
19
- # puts result.stdout # "hello"
20
- class Synchronous < Base
21
- # Initialize synchronous execution strategy.
22
- #
23
- # @param options [Hash] strategy configuration
24
- # @option options [Boolean] :validate_commands (true) whether to validate commands exist
25
- # @option options [Integer] :max_output_size (1048576) maximum output size in bytes
26
- def initialize(**options)
27
- @validate_commands = options.fetch(:validate_commands, true)
28
- @max_output_size = options.fetch(:max_output_size, 1024 * 1024) # 1MB default
29
- end
30
-
31
- # Execute a single command request synchronously.
32
- #
33
- # @param request [Request] the command request to execute
34
- # @return [Result] execution result
35
- def execute(request)
36
- # Skip command validation for basic commands like echo on Windows
37
- # This is a simplified approach for demonstration purposes
38
- if @validate_commands && !basic_command?(request.command) && !command_available?(request.command)
39
- return Result.failure(
40
- command: request.to_shell_command,
41
- error: "Command not found: #{request.command}",
42
- )
43
- end
44
-
45
- # Execute using Open3 for cross-platform support
46
- result = execute_with_open3(request)
47
-
48
- # Truncate output if too large
49
- if result.stdout.bytesize > @max_output_size
50
- original_size = result.stdout.bytesize
51
- truncated_stdout = result.stdout.byteslice(0, @max_output_size)
52
- result.instance_variable_set(:@stdout, "#{truncated_stdout}\n[OUTPUT TRUNCATED]")
53
- result.add_metadata(:output_truncated, true)
54
- result.add_metadata(:original_stdout_size, original_size)
55
- end
56
-
57
- if result.stderr.bytesize > @max_output_size
58
- original_size = result.stderr.bytesize
59
- truncated_stderr = result.stderr.byteslice(0, @max_output_size)
60
- result.instance_variable_set(:@stderr, "#{truncated_stderr}\n[ERROR OUTPUT TRUNCATED]")
61
- result.add_metadata(:stderr_truncated, true)
62
- result.add_metadata(:original_stderr_size, original_size)
63
- end
64
-
65
- # Add strategy metadata
66
- result.add_metadata(:execution_strategy, "synchronous")
67
- result.add_metadata(:validated_command, @validate_commands)
68
-
69
- result
70
- end
71
-
72
- # Execute multiple requests sequentially.
73
- #
74
- # @param requests [Array<Request>] requests to execute
75
- # @return [Array<Result>] execution results in same order
76
- def execute_batch(requests)
77
- results = []
78
-
79
- requests.each_with_index do |request, index|
80
- result = execute(request)
81
- result.add_metadata(:batch_index, index)
82
- result.add_metadata(:batch_size, requests.size)
83
- results << result
84
-
85
- # Stop on first failure if configured
86
- break if result.failure? && fail_fast?
87
- end
88
-
89
- results
90
- end
91
-
92
- # Check if strategy supports the given request.
93
- #
94
- # Synchronous strategy supports all requests.
95
- #
96
- # @param request [Request] the command request
97
- # @return [Boolean] always true
98
- def supports?(_request)
99
- true
100
- end
101
-
102
- # Get strategy configuration.
103
- #
104
- # @return [Hash] strategy configuration
105
- def config
106
- {
107
- name: "synchronous",
108
- validate_commands: @validate_commands,
109
- max_output_size: @max_output_size,
110
- fail_fast: fail_fast?,
111
- }
112
- end
113
-
114
- private
115
-
116
- # Check if command is a basic system command that should always be available.
117
- #
118
- # @param command [String] command name
119
- # @return [Boolean] true if it's a basic command
120
- def basic_command?(command)
121
- %w[echo ruby bundle rake git].include?(command)
122
- end
123
-
124
- # Check if execution should stop on first failure.
125
- #
126
- # @return [Boolean] whether to fail fast
127
- def fail_fast?
128
- # Could be configurable in the future
129
- false
130
- end
131
- end
132
- end
133
- end
134
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "../result"
5
+
6
+ module Makit
7
+ module Commands
8
+ module Strategies
9
+ # Synchronous command execution strategy.
10
+ #
11
+ # This strategy executes commands one at a time, blocking until each
12
+ # command completes. This is the simplest and most reliable execution
13
+ # pattern, suitable for most use cases.
14
+ #
15
+ # @example Using synchronous execution
16
+ # strategy = Synchronous.new
17
+ # request = Request.new(command: "echo", arguments: ["hello"])
18
+ # result = strategy.execute(request)
19
+ # puts result.stdout # "hello"
20
+ class Synchronous < Base
21
+ # Initialize synchronous execution strategy.
22
+ #
23
+ # @param options [Hash] strategy configuration
24
+ # @option options [Boolean] :validate_commands (true) whether to validate commands exist
25
+ # @option options [Integer] :max_output_size (1048576) maximum output size in bytes
26
+ def initialize(**options)
27
+ @validate_commands = false # Always disable validation to avoid path issues
28
+ @max_output_size = options.fetch(:max_output_size, 1024 * 1024) # 1MB default
29
+ end
30
+
31
+ # Execute a single command request synchronously.
32
+ #
33
+ # @param request [Request] the command request to execute
34
+ # @return [Result] execution result
35
+ def execute(request)
36
+ # Skip command validation for basic commands like echo on Windows
37
+ # This is a simplified approach for demonstration purposes
38
+ if @validate_commands && !basic_command?(request.command) && !command_available?(request.command)
39
+ # For dotnet commands, try to execute anyway as validation might be unreliable
40
+ if request.command == "dotnet"
41
+ Makit::Logging.debug("Skipping validation for dotnet command due to potential path issues")
42
+ else
43
+ return Result.failure(
44
+ command: request.to_shell_command,
45
+ error: "Command not found: #{request.command}",
46
+ )
47
+ end
48
+ end
49
+
50
+ # Execute using Open3 for cross-platform support
51
+ result = execute_with_open3(request)
52
+
53
+ # Truncate output if too large
54
+ if result.stdout.bytesize > @max_output_size
55
+ original_size = result.stdout.bytesize
56
+ truncated_stdout = result.stdout.byteslice(0, @max_output_size)
57
+ result.instance_variable_set(:@stdout, "#{truncated_stdout}\n[OUTPUT TRUNCATED]")
58
+ result.add_metadata(:output_truncated, true)
59
+ result.add_metadata(:original_stdout_size, original_size)
60
+ end
61
+
62
+ if result.stderr.bytesize > @max_output_size
63
+ original_size = result.stderr.bytesize
64
+ truncated_stderr = result.stderr.byteslice(0, @max_output_size)
65
+ result.instance_variable_set(:@stderr, "#{truncated_stderr}\n[ERROR OUTPUT TRUNCATED]")
66
+ result.add_metadata(:stderr_truncated, true)
67
+ result.add_metadata(:original_stderr_size, original_size)
68
+ end
69
+
70
+ # Add strategy metadata
71
+ result.add_metadata(:execution_strategy, "synchronous")
72
+ result.add_metadata(:validated_command, @validate_commands)
73
+
74
+ result
75
+ end
76
+
77
+ # Execute multiple requests sequentially.
78
+ #
79
+ # @param requests [Array<Request>] requests to execute
80
+ # @return [Array<Result>] execution results in same order
81
+ def execute_batch(requests)
82
+ results = []
83
+
84
+ requests.each_with_index do |request, index|
85
+ result = execute(request)
86
+ result.add_metadata(:batch_index, index)
87
+ result.add_metadata(:batch_size, requests.size)
88
+ results << result
89
+
90
+ # Stop on first failure if configured
91
+ break if result.failure? && fail_fast?
92
+ end
93
+
94
+ results
95
+ end
96
+
97
+ # Check if strategy supports the given request.
98
+ #
99
+ # Synchronous strategy supports all requests.
100
+ #
101
+ # @param request [Request] the command request
102
+ # @return [Boolean] always true
103
+ def supports?(_request)
104
+ true
105
+ end
106
+
107
+ # Get strategy configuration.
108
+ #
109
+ # @return [Hash] strategy configuration
110
+ def config
111
+ {
112
+ name: "synchronous",
113
+ validate_commands: @validate_commands,
114
+ max_output_size: @max_output_size,
115
+ fail_fast: fail_fast?,
116
+ }
117
+ end
118
+
119
+ private
120
+
121
+ # Check if command is a basic system command that should always be available.
122
+ #
123
+ # @param command [String] command name
124
+ # @return [Boolean] true if it's a basic command
125
+ def basic_command?(command)
126
+ %w[echo ruby bundle rake git dotnet].include?(command)
127
+ end
128
+
129
+ # Check if execution should stop on first failure.
130
+ #
131
+ # @return [Boolean] whether to fail fast
132
+ def fail_fast?
133
+ # Could be configurable in the future
134
+ false
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end