makit 0.0.139 → 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.
- checksums.yaml +4 -4
- data/lib/makit/cli/main.rb +2 -0
- data/lib/makit/cli/strategy_commands.rb +203 -152
- data/lib/makit/commands/request.rb +1 -1
- data/lib/makit/commands/runner.rb +13 -10
- data/lib/makit/commands/strategies/child_process.rb +165 -165
- data/lib/makit/commands/strategies/factory.rb +136 -136
- data/lib/makit/configuration/timeout.rb +74 -0
- data/lib/makit/configuration.rb +1 -0
- data/lib/makit/mp/string_mp.rb +13 -5
- data/lib/makit/rake/trace_controller.rb +173 -173
- data/lib/makit/setup/razorclasslib.rb +1 -1
- data/lib/makit/version.rb +1 -1
- metadata +3 -2
@@ -1,165 +1,165 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'childprocess'
|
4
|
-
require 'tempfile'
|
5
|
-
require_relative "base"
|
6
|
-
|
7
|
-
module Makit
|
8
|
-
module Commands
|
9
|
-
module Strategies
|
10
|
-
# ChildProcess-based command execution strategy
|
11
|
-
#
|
12
|
-
# This strategy uses the ChildProcess gem for robust cross-platform
|
13
|
-
# process management. It provides better handling of timeouts, I/O,
|
14
|
-
# and process lifecycle management compared to Open3.
|
15
|
-
#
|
16
|
-
# @example Basic usage
|
17
|
-
# strategy = ChildProcess.new
|
18
|
-
# result = strategy.execute(request)
|
19
|
-
#
|
20
|
-
# @example With custom options
|
21
|
-
# strategy = ChildProcess.new(
|
22
|
-
# timeout: 60,
|
23
|
-
# max_output_size: 1_000_000
|
24
|
-
# )
|
25
|
-
class ChildProcess < Base
|
26
|
-
# @!attribute [r] timeout
|
27
|
-
# @return [Integer] default timeout in seconds
|
28
|
-
attr_reader :timeout
|
29
|
-
|
30
|
-
# @!attribute [r] max_output_size
|
31
|
-
# @return [Integer] maximum output size in bytes
|
32
|
-
attr_reader :max_output_size
|
33
|
-
|
34
|
-
# Initialize ChildProcess strategy
|
35
|
-
#
|
36
|
-
# @param timeout [Integer] default timeout in seconds (default: 30)
|
37
|
-
# @param max_output_size [Integer] maximum output size in bytes (default: 1MB)
|
38
|
-
def initialize(timeout:
|
39
|
-
@timeout = timeout
|
40
|
-
@max_output_size = max_output_size
|
41
|
-
super(**options)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Execute a command request using ChildProcess
|
45
|
-
#
|
46
|
-
# @param request [Request] the command request to execute
|
47
|
-
# @return [Result] execution result
|
48
|
-
def execute(request)
|
49
|
-
result = Result.new(
|
50
|
-
command: request.to_shell_command,
|
51
|
-
started_at: Time.now,
|
52
|
-
)
|
53
|
-
|
54
|
-
stdout_file = nil
|
55
|
-
stderr_file = nil
|
56
|
-
process = nil
|
57
|
-
|
58
|
-
begin
|
59
|
-
# Create temporary files for output
|
60
|
-
stdout_file = Tempfile.new('makit_stdout')
|
61
|
-
stderr_file = Tempfile.new('makit_stderr')
|
62
|
-
|
63
|
-
# Build the process
|
64
|
-
process = ChildProcess.build(request.command, *request.arguments)
|
65
|
-
|
66
|
-
# Set working directory
|
67
|
-
process.cwd = request.directory if request.directory
|
68
|
-
|
69
|
-
# Set environment variables
|
70
|
-
(request.environment || {}).each do |key, value|
|
71
|
-
process.environment[key] = value.to_s
|
72
|
-
end
|
73
|
-
|
74
|
-
# Set up I/O
|
75
|
-
process.io.stdout = stdout_file
|
76
|
-
process.io.stderr = stderr_file
|
77
|
-
|
78
|
-
# Start the process
|
79
|
-
process.start
|
80
|
-
|
81
|
-
# Wait for completion with timeout
|
82
|
-
timeout_seconds = request.timeout || @timeout
|
83
|
-
process.poll_for_exit(timeout_seconds)
|
84
|
-
|
85
|
-
# Read output
|
86
|
-
stdout_file.rewind
|
87
|
-
stderr_file.rewind
|
88
|
-
stdout = stdout_file.read
|
89
|
-
stderr = stderr_file.read
|
90
|
-
|
91
|
-
# Truncate output if too large
|
92
|
-
stdout = truncate_output(stdout, "stdout")
|
93
|
-
stderr = truncate_output(stderr, "stderr")
|
94
|
-
|
95
|
-
result.finish!(
|
96
|
-
exit_code: process.exit_code,
|
97
|
-
stdout: stdout,
|
98
|
-
stderr: stderr,
|
99
|
-
)
|
100
|
-
|
101
|
-
rescue ChildProcess::TimeoutError
|
102
|
-
# Handle timeout
|
103
|
-
process&.stop
|
104
|
-
result.finish!(
|
105
|
-
exit_code: 124,
|
106
|
-
stderr: "Command timed out after #{timeout_seconds} seconds",
|
107
|
-
).add_metadata(:timeout, true)
|
108
|
-
.add_metadata(:strategy, 'childprocess')
|
109
|
-
|
110
|
-
rescue => e
|
111
|
-
# Handle other errors
|
112
|
-
process&.stop rescue nil
|
113
|
-
result.finish!(
|
114
|
-
exit_code: 1,
|
115
|
-
stderr: e.message,
|
116
|
-
).add_metadata(:error_class, e.class.name)
|
117
|
-
.add_metadata(:strategy, 'childprocess')
|
118
|
-
|
119
|
-
ensure
|
120
|
-
# Clean up resources
|
121
|
-
[stdout_file, stderr_file].each do |file|
|
122
|
-
file&.close
|
123
|
-
file&.unlink rescue nil
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
result
|
128
|
-
end
|
129
|
-
|
130
|
-
# Execute multiple requests using ChildProcess
|
131
|
-
#
|
132
|
-
# @param requests [Array<Request>] requests to execute
|
133
|
-
# @return [Array<Result>] execution results
|
134
|
-
def execute_batch(requests)
|
135
|
-
# For now, execute sequentially to avoid complexity
|
136
|
-
# Could be enhanced to use process pools in the future
|
137
|
-
requests.map { |request| execute(request) }
|
138
|
-
end
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
# Truncate output if it exceeds maximum size
|
143
|
-
#
|
144
|
-
# @param output [String] output to potentially truncate
|
145
|
-
# @param type [String] output type for logging
|
146
|
-
# @return [String] truncated output
|
147
|
-
def truncate_output(output, type)
|
148
|
-
return output if output.bytesize <= @max_output_size
|
149
|
-
|
150
|
-
original_size = output.bytesize
|
151
|
-
truncated = output.byteslice(0, @max_output_size)
|
152
|
-
|
153
|
-
Makit::Logging.warn(
|
154
|
-
"#{type.capitalize} truncated",
|
155
|
-
original_size: original_size,
|
156
|
-
max_size: @max_output_size,
|
157
|
-
strategy: 'childprocess'
|
158
|
-
)
|
159
|
-
|
160
|
-
"#{truncated}\n[#{type.upcase} TRUNCATED - Original size: #{original_size} bytes]"
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'childprocess'
|
4
|
+
require 'tempfile'
|
5
|
+
require_relative "base"
|
6
|
+
|
7
|
+
module Makit
|
8
|
+
module Commands
|
9
|
+
module Strategies
|
10
|
+
# ChildProcess-based command execution strategy
|
11
|
+
#
|
12
|
+
# This strategy uses the ChildProcess gem for robust cross-platform
|
13
|
+
# process management. It provides better handling of timeouts, I/O,
|
14
|
+
# and process lifecycle management compared to Open3.
|
15
|
+
#
|
16
|
+
# @example Basic usage
|
17
|
+
# strategy = ChildProcess.new
|
18
|
+
# result = strategy.execute(request)
|
19
|
+
#
|
20
|
+
# @example With custom options
|
21
|
+
# strategy = ChildProcess.new(
|
22
|
+
# timeout: 60,
|
23
|
+
# max_output_size: 1_000_000
|
24
|
+
# )
|
25
|
+
class ChildProcess < Base
|
26
|
+
# @!attribute [r] timeout
|
27
|
+
# @return [Integer] default timeout in seconds
|
28
|
+
attr_reader :timeout
|
29
|
+
|
30
|
+
# @!attribute [r] max_output_size
|
31
|
+
# @return [Integer] maximum output size in bytes
|
32
|
+
attr_reader :max_output_size
|
33
|
+
|
34
|
+
# Initialize ChildProcess strategy
|
35
|
+
#
|
36
|
+
# @param timeout [Integer] default timeout in seconds (default: 30)
|
37
|
+
# @param max_output_size [Integer] maximum output size in bytes (default: 1MB)
|
38
|
+
def initialize(timeout: Makit::Configuration::Timeout.global_default, max_output_size: 1_000_000, **options)
|
39
|
+
@timeout = timeout
|
40
|
+
@max_output_size = max_output_size
|
41
|
+
super(**options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Execute a command request using ChildProcess
|
45
|
+
#
|
46
|
+
# @param request [Request] the command request to execute
|
47
|
+
# @return [Result] execution result
|
48
|
+
def execute(request)
|
49
|
+
result = Result.new(
|
50
|
+
command: request.to_shell_command,
|
51
|
+
started_at: Time.now,
|
52
|
+
)
|
53
|
+
|
54
|
+
stdout_file = nil
|
55
|
+
stderr_file = nil
|
56
|
+
process = nil
|
57
|
+
|
58
|
+
begin
|
59
|
+
# Create temporary files for output
|
60
|
+
stdout_file = Tempfile.new('makit_stdout')
|
61
|
+
stderr_file = Tempfile.new('makit_stderr')
|
62
|
+
|
63
|
+
# Build the process
|
64
|
+
process = ChildProcess.build(request.command, *request.arguments)
|
65
|
+
|
66
|
+
# Set working directory
|
67
|
+
process.cwd = request.directory if request.directory
|
68
|
+
|
69
|
+
# Set environment variables
|
70
|
+
(request.environment || {}).each do |key, value|
|
71
|
+
process.environment[key] = value.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# Set up I/O
|
75
|
+
process.io.stdout = stdout_file
|
76
|
+
process.io.stderr = stderr_file
|
77
|
+
|
78
|
+
# Start the process
|
79
|
+
process.start
|
80
|
+
|
81
|
+
# Wait for completion with timeout
|
82
|
+
timeout_seconds = request.timeout || @timeout
|
83
|
+
process.poll_for_exit(timeout_seconds)
|
84
|
+
|
85
|
+
# Read output
|
86
|
+
stdout_file.rewind
|
87
|
+
stderr_file.rewind
|
88
|
+
stdout = stdout_file.read
|
89
|
+
stderr = stderr_file.read
|
90
|
+
|
91
|
+
# Truncate output if too large
|
92
|
+
stdout = truncate_output(stdout, "stdout")
|
93
|
+
stderr = truncate_output(stderr, "stderr")
|
94
|
+
|
95
|
+
result.finish!(
|
96
|
+
exit_code: process.exit_code,
|
97
|
+
stdout: stdout,
|
98
|
+
stderr: stderr,
|
99
|
+
)
|
100
|
+
|
101
|
+
rescue ChildProcess::TimeoutError
|
102
|
+
# Handle timeout
|
103
|
+
process&.stop
|
104
|
+
result.finish!(
|
105
|
+
exit_code: 124,
|
106
|
+
stderr: "Command timed out after #{timeout_seconds} seconds",
|
107
|
+
).add_metadata(:timeout, true)
|
108
|
+
.add_metadata(:strategy, 'childprocess')
|
109
|
+
|
110
|
+
rescue => e
|
111
|
+
# Handle other errors
|
112
|
+
process&.stop rescue nil
|
113
|
+
result.finish!(
|
114
|
+
exit_code: 1,
|
115
|
+
stderr: e.message,
|
116
|
+
).add_metadata(:error_class, e.class.name)
|
117
|
+
.add_metadata(:strategy, 'childprocess')
|
118
|
+
|
119
|
+
ensure
|
120
|
+
# Clean up resources
|
121
|
+
[stdout_file, stderr_file].each do |file|
|
122
|
+
file&.close
|
123
|
+
file&.unlink rescue nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
result
|
128
|
+
end
|
129
|
+
|
130
|
+
# Execute multiple requests using ChildProcess
|
131
|
+
#
|
132
|
+
# @param requests [Array<Request>] requests to execute
|
133
|
+
# @return [Array<Result>] execution results
|
134
|
+
def execute_batch(requests)
|
135
|
+
# For now, execute sequentially to avoid complexity
|
136
|
+
# Could be enhanced to use process pools in the future
|
137
|
+
requests.map { |request| execute(request) }
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Truncate output if it exceeds maximum size
|
143
|
+
#
|
144
|
+
# @param output [String] output to potentially truncate
|
145
|
+
# @param type [String] output type for logging
|
146
|
+
# @return [String] truncated output
|
147
|
+
def truncate_output(output, type)
|
148
|
+
return output if output.bytesize <= @max_output_size
|
149
|
+
|
150
|
+
original_size = output.bytesize
|
151
|
+
truncated = output.byteslice(0, @max_output_size)
|
152
|
+
|
153
|
+
Makit::Logging.warn(
|
154
|
+
"#{type.capitalize} truncated",
|
155
|
+
original_size: original_size,
|
156
|
+
max_size: @max_output_size,
|
157
|
+
strategy: 'childprocess'
|
158
|
+
)
|
159
|
+
|
160
|
+
"#{truncated}\n[#{type.upcase} TRUNCATED - Original size: #{original_size} bytes]"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -1,136 +1,136 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "base"
|
4
|
-
require_relative "synchronous"
|
5
|
-
|
6
|
-
module Makit
|
7
|
-
module Commands
|
8
|
-
module Strategies
|
9
|
-
# Factory for creating command execution strategies with fallback support
|
10
|
-
#
|
11
|
-
# This factory allows easy switching between different execution strategies
|
12
|
-
# (ChildProcess, Open3, etc.) with automatic fallback to Open3 if the
|
13
|
-
# preferred strategy fails to load or execute.
|
14
|
-
#
|
15
|
-
# @example Using environment variable to control strategy
|
16
|
-
# # Use ChildProcess (default)
|
17
|
-
# MAKIT_STRATEGY=childprocess rake
|
18
|
-
#
|
19
|
-
# # Force Open3 usage
|
20
|
-
# MAKIT_STRATEGY=open3 rake
|
21
|
-
#
|
22
|
-
# # Auto-detect (try ChildProcess, fallback to Open3)
|
23
|
-
# MAKIT_STRATEGY=auto rake
|
24
|
-
class Factory
|
25
|
-
# Available strategy types
|
26
|
-
STRATEGIES = {
|
27
|
-
'childprocess' => 'ChildProcess',
|
28
|
-
'open3' => 'Synchronous',
|
29
|
-
'auto' => 'AutoDetect'
|
30
|
-
}.freeze
|
31
|
-
|
32
|
-
# Get the configured strategy instance
|
33
|
-
#
|
34
|
-
# @param options [Hash] strategy options
|
35
|
-
# @return [Strategies::Base] configured strategy instance
|
36
|
-
def self.create(options = {})
|
37
|
-
strategy_type = determine_strategy_type
|
38
|
-
|
39
|
-
begin
|
40
|
-
case strategy_type
|
41
|
-
when 'childprocess'
|
42
|
-
create_childprocess_strategy(options)
|
43
|
-
when 'open3'
|
44
|
-
create_open3_strategy(options)
|
45
|
-
when 'auto'
|
46
|
-
create_auto_detect_strategy(options)
|
47
|
-
else
|
48
|
-
Makit::Logging.warn("Unknown strategy '#{strategy_type}', falling back to Open3") if defined?(Makit::Logging)
|
49
|
-
create_open3_strategy(options)
|
50
|
-
end
|
51
|
-
rescue => e
|
52
|
-
# Ultimate fallback - create basic synchronous strategy
|
53
|
-
Makit::Logging.warn("Failed to create strategy: #{e.message}") if defined?(Makit::Logging)
|
54
|
-
Synchronous.new
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Get the current strategy type from environment or default
|
59
|
-
#
|
60
|
-
# @return [String] strategy type
|
61
|
-
def self.determine_strategy_type
|
62
|
-
env_strategy = ENV['MAKIT_STRATEGY']&.downcase
|
63
|
-
|
64
|
-
if env_strategy && STRATEGIES.key?(env_strategy)
|
65
|
-
env_strategy
|
66
|
-
else
|
67
|
-
'auto' # Default to auto-detect
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Create ChildProcess strategy with fallback
|
72
|
-
#
|
73
|
-
# @param options [Hash] strategy options
|
74
|
-
# @return [Strategies::Base] strategy instance
|
75
|
-
def self.create_childprocess_strategy(options)
|
76
|
-
begin
|
77
|
-
require_relative 'child_process'
|
78
|
-
ChildProcess.new(**options)
|
79
|
-
rescue LoadError, StandardError => e
|
80
|
-
Makit::Logging.warn("Failed to load ChildProcess strategy: #{e.message}")
|
81
|
-
Makit::Logging.info("Falling back to Open3 strategy")
|
82
|
-
create_open3_strategy(options)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Create Open3 strategy
|
87
|
-
#
|
88
|
-
# @param options [Hash] strategy options
|
89
|
-
# @return [Strategies::Base] strategy instance
|
90
|
-
def self.create_open3_strategy(options)
|
91
|
-
Synchronous.new(**options)
|
92
|
-
end
|
93
|
-
|
94
|
-
# Create auto-detect strategy that tries ChildProcess first
|
95
|
-
#
|
96
|
-
# @param options [Hash] strategy options
|
97
|
-
# @return [Strategies::Base] strategy instance
|
98
|
-
def self.create_auto_detect_strategy(options)
|
99
|
-
# Always use Open3 for now to avoid circular dependency issues
|
100
|
-
begin
|
101
|
-
Makit::Logging.debug("Using Open3 strategy") if defined?(Makit::Logging)
|
102
|
-
Synchronous.new(**options)
|
103
|
-
rescue => e
|
104
|
-
# Fallback to basic strategy if there are any issues
|
105
|
-
Makit::Logging.warn("Failed to create Open3 strategy: #{e.message}") if defined?(Makit::Logging)
|
106
|
-
Synchronous.new
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# Get information about available strategies
|
111
|
-
#
|
112
|
-
# @return [Hash] strategy information
|
113
|
-
def self.strategy_info
|
114
|
-
{
|
115
|
-
current: determine_strategy_type,
|
116
|
-
available: STRATEGIES.keys,
|
117
|
-
childprocess_available: childprocess_available?,
|
118
|
-
open3_available: true # Always available
|
119
|
-
}
|
120
|
-
end
|
121
|
-
|
122
|
-
# Check if ChildProcess strategy is available
|
123
|
-
#
|
124
|
-
# @return [Boolean] true if ChildProcess can be loaded
|
125
|
-
def self.childprocess_available?
|
126
|
-
begin
|
127
|
-
require 'childprocess'
|
128
|
-
true
|
129
|
-
rescue LoadError
|
130
|
-
false
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require_relative "synchronous"
|
5
|
+
|
6
|
+
module Makit
|
7
|
+
module Commands
|
8
|
+
module Strategies
|
9
|
+
# Factory for creating command execution strategies with fallback support
|
10
|
+
#
|
11
|
+
# This factory allows easy switching between different execution strategies
|
12
|
+
# (ChildProcess, Open3, etc.) with automatic fallback to Open3 if the
|
13
|
+
# preferred strategy fails to load or execute.
|
14
|
+
#
|
15
|
+
# @example Using environment variable to control strategy
|
16
|
+
# # Use ChildProcess (default)
|
17
|
+
# MAKIT_STRATEGY=childprocess rake
|
18
|
+
#
|
19
|
+
# # Force Open3 usage
|
20
|
+
# MAKIT_STRATEGY=open3 rake
|
21
|
+
#
|
22
|
+
# # Auto-detect (try ChildProcess, fallback to Open3)
|
23
|
+
# MAKIT_STRATEGY=auto rake
|
24
|
+
class Factory
|
25
|
+
# Available strategy types
|
26
|
+
STRATEGIES = {
|
27
|
+
'childprocess' => 'ChildProcess',
|
28
|
+
'open3' => 'Synchronous',
|
29
|
+
'auto' => 'AutoDetect'
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
# Get the configured strategy instance
|
33
|
+
#
|
34
|
+
# @param options [Hash] strategy options
|
35
|
+
# @return [Strategies::Base] configured strategy instance
|
36
|
+
def self.create(options = {})
|
37
|
+
strategy_type = determine_strategy_type
|
38
|
+
|
39
|
+
begin
|
40
|
+
case strategy_type
|
41
|
+
when 'childprocess'
|
42
|
+
create_childprocess_strategy(options)
|
43
|
+
when 'open3'
|
44
|
+
create_open3_strategy(options)
|
45
|
+
when 'auto'
|
46
|
+
create_auto_detect_strategy(options)
|
47
|
+
else
|
48
|
+
Makit::Logging.warn("Unknown strategy '#{strategy_type}', falling back to Open3") if defined?(Makit::Logging)
|
49
|
+
create_open3_strategy(options)
|
50
|
+
end
|
51
|
+
rescue => e
|
52
|
+
# Ultimate fallback - create basic synchronous strategy
|
53
|
+
Makit::Logging.warn("Failed to create strategy: #{e.message}") if defined?(Makit::Logging)
|
54
|
+
Synchronous.new
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get the current strategy type from environment or default
|
59
|
+
#
|
60
|
+
# @return [String] strategy type
|
61
|
+
def self.determine_strategy_type
|
62
|
+
env_strategy = ENV['MAKIT_STRATEGY']&.downcase
|
63
|
+
|
64
|
+
if env_strategy && STRATEGIES.key?(env_strategy)
|
65
|
+
env_strategy
|
66
|
+
else
|
67
|
+
'auto' # Default to auto-detect
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create ChildProcess strategy with fallback
|
72
|
+
#
|
73
|
+
# @param options [Hash] strategy options
|
74
|
+
# @return [Strategies::Base] strategy instance
|
75
|
+
def self.create_childprocess_strategy(options)
|
76
|
+
begin
|
77
|
+
require_relative 'child_process'
|
78
|
+
ChildProcess.new(**options)
|
79
|
+
rescue LoadError, StandardError => e
|
80
|
+
Makit::Logging.warn("Failed to load ChildProcess strategy: #{e.message}")
|
81
|
+
Makit::Logging.info("Falling back to Open3 strategy")
|
82
|
+
create_open3_strategy(options)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Create Open3 strategy
|
87
|
+
#
|
88
|
+
# @param options [Hash] strategy options
|
89
|
+
# @return [Strategies::Base] strategy instance
|
90
|
+
def self.create_open3_strategy(options)
|
91
|
+
Synchronous.new(**options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Create auto-detect strategy that tries ChildProcess first
|
95
|
+
#
|
96
|
+
# @param options [Hash] strategy options
|
97
|
+
# @return [Strategies::Base] strategy instance
|
98
|
+
def self.create_auto_detect_strategy(options)
|
99
|
+
# Always use Open3 for now to avoid circular dependency issues
|
100
|
+
begin
|
101
|
+
Makit::Logging.debug("Using Open3 strategy") if defined?(Makit::Logging)
|
102
|
+
Synchronous.new(**options)
|
103
|
+
rescue => e
|
104
|
+
# Fallback to basic strategy if there are any issues
|
105
|
+
Makit::Logging.warn("Failed to create Open3 strategy: #{e.message}") if defined?(Makit::Logging)
|
106
|
+
Synchronous.new
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Get information about available strategies
|
111
|
+
#
|
112
|
+
# @return [Hash] strategy information
|
113
|
+
def self.strategy_info
|
114
|
+
{
|
115
|
+
current: determine_strategy_type,
|
116
|
+
available: STRATEGIES.keys,
|
117
|
+
childprocess_available: childprocess_available?,
|
118
|
+
open3_available: true # Always available
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
# Check if ChildProcess strategy is available
|
123
|
+
#
|
124
|
+
# @return [Boolean] true if ChildProcess can be loaded
|
125
|
+
def self.childprocess_available?
|
126
|
+
begin
|
127
|
+
require 'childprocess'
|
128
|
+
true
|
129
|
+
rescue LoadError
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Makit
|
4
|
+
module Configuration
|
5
|
+
# Configuration for timeouts across the makit system
|
6
|
+
class Timeout
|
7
|
+
# Default timeout values for different operations
|
8
|
+
DEFAULTS = {
|
9
|
+
# Command execution timeouts
|
10
|
+
command: 30,
|
11
|
+
git_clone: 300,
|
12
|
+
git_pull: 120,
|
13
|
+
bundle_install: 300,
|
14
|
+
dotnet_build: 300,
|
15
|
+
dotnet_publish: 300,
|
16
|
+
dotnet_test: 180,
|
17
|
+
dotnet_restore: 120,
|
18
|
+
protoc_generate: 60,
|
19
|
+
gem_build: 300,
|
20
|
+
gem_install: 120,
|
21
|
+
|
22
|
+
# Strategy timeouts
|
23
|
+
childprocess: 30,
|
24
|
+
open3: 30,
|
25
|
+
|
26
|
+
# Example execution timeout
|
27
|
+
example: 30
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
# Get the default timeout for a specific operation
|
31
|
+
#
|
32
|
+
# @param operation [Symbol] the operation type
|
33
|
+
# @return [Integer] timeout in seconds
|
34
|
+
def self.default_for(operation)
|
35
|
+
DEFAULTS[operation] || DEFAULTS[:command]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the global default timeout from environment or fallback
|
39
|
+
#
|
40
|
+
# @return [Integer] global default timeout in seconds
|
41
|
+
def self.global_default
|
42
|
+
ENV['MAKIT_DEFAULT_TIMEOUT']&.to_i || DEFAULTS[:command]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set a custom timeout for an operation
|
46
|
+
#
|
47
|
+
# @param operation [Symbol] the operation type
|
48
|
+
# @param timeout [Integer] timeout in seconds
|
49
|
+
# @return [void]
|
50
|
+
def self.set_timeout(operation, timeout)
|
51
|
+
DEFAULTS[operation] = timeout
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get all configured timeouts
|
55
|
+
#
|
56
|
+
# @return [Hash] all timeout configurations
|
57
|
+
def self.all_timeouts
|
58
|
+
DEFAULTS.dup
|
59
|
+
end
|
60
|
+
|
61
|
+
# Validate a timeout value
|
62
|
+
#
|
63
|
+
# @param timeout [Integer] timeout to validate
|
64
|
+
# @raise [ArgumentError] if timeout is invalid
|
65
|
+
def self.validate_timeout(timeout)
|
66
|
+
raise ArgumentError, "Timeout must be a positive integer" unless timeout.is_a?(Integer) && timeout.positive?
|
67
|
+
|
68
|
+
return unless timeout > 3600 # 1 hour max
|
69
|
+
|
70
|
+
raise ArgumentError, "Timeout cannot exceed 3600 seconds (1 hour)"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|