Class: Makit::Commands::Strategies::ChildProcess

Inherits:
Base
  • Object
show all
Defined in:
lib/makit/commands/strategies/child_process.rb

Overview

ChildProcess-based command execution strategy

This strategy uses the ChildProcess gem for robust cross-platform process management. It provides better handling of timeouts, I/O, and process lifecycle management compared to Open3.

Examples:

Basic usage

strategy = ChildProcess.new
result = strategy.execute(request)

With custom options

strategy = ChildProcess.new(
  timeout: 60,
  max_output_size: 1_000_000
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#config, #name, #supports?

Constructor Details

#initialize(timeout: 30, max_output_size: 1_000_000, **options) ⇒ ChildProcess

Initialize ChildProcess strategy

Parameters:

  • timeout (Integer) (defaults to: 30)

    default timeout in seconds (default: 30)

  • max_output_size (Integer) (defaults to: 1_000_000)

    maximum output size in bytes (default: 1MB)



38
39
40
41
42
# File 'lib/makit/commands/strategies/child_process.rb', line 38

def initialize(timeout: 30, max_output_size: 1_000_000, **options)
  @timeout = timeout
  @max_output_size = max_output_size
  super(**options)
end

Instance Attribute Details

#max_output_sizeObject (readonly)

Returns the value of attribute max_output_size.



32
33
34
# File 'lib/makit/commands/strategies/child_process.rb', line 32

def max_output_size
  @max_output_size
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



28
29
30
# File 'lib/makit/commands/strategies/child_process.rb', line 28

def timeout
  @timeout
end

Instance Method Details

#execute(request) ⇒ Result

Execute a command request using ChildProcess

Parameters:

  • request (Request)

    the command request to execute

Returns:

  • (Result)

    execution result



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/makit/commands/strategies/child_process.rb', line 48

def execute(request)
  result = Result.new(
    command: request.to_shell_command,
    started_at: Time.now,
  )

  stdout_file = nil
  stderr_file = nil
  process = nil

  begin
    # Create temporary files for output
    stdout_file = Tempfile.new('makit_stdout')
    stderr_file = Tempfile.new('makit_stderr')

    # Build the process
    process = ChildProcess.build(request.command, *request.arguments)

    # Set working directory
    process.cwd = request.directory if request.directory

    # Set environment variables
    (request.environment || {}).each do |key, value|
      process.environment[key] = value.to_s
    end

    # Set up I/O
    process.io.stdout = stdout_file
    process.io.stderr = stderr_file

    # Start the process
    process.start

    # Wait for completion with timeout
    timeout_seconds = request.timeout || @timeout
    process.poll_for_exit(timeout_seconds)

    # Read output
    stdout_file.rewind
    stderr_file.rewind
    stdout = stdout_file.read
    stderr = stderr_file.read

    # Truncate output if too large
    stdout = truncate_output(stdout, "stdout")
    stderr = truncate_output(stderr, "stderr")

    result.finish!(
      exit_code: process.exit_code,
      stdout: stdout,
      stderr: stderr,
    )

  rescue ChildProcess::TimeoutError
    # Handle timeout
    process&.stop
    result.finish!(
      exit_code: 124,
      stderr: "Command timed out after #{timeout_seconds} seconds",
    ).(:timeout, true)
    .(:strategy, 'childprocess')

  rescue => e
    # Handle other errors
    process&.stop rescue nil
    result.finish!(
      exit_code: 1,
      stderr: e.message,
    ).(:error_class, e.class.name)
    .(:strategy, 'childprocess')

  ensure
    # Clean up resources
    [stdout_file, stderr_file].each do |file|
      file&.close
      file&.unlink rescue nil
    end
  end

  result
end

#execute_batch(requests) ⇒ Array<Result>

Execute multiple requests using ChildProcess

Parameters:

  • requests (Array<Request>)

    requests to execute

Returns:

  • (Array<Result>)

    execution results



134
135
136
137
138
# File 'lib/makit/commands/strategies/child_process.rb', line 134

def execute_batch(requests)
  # For now, execute sequentially to avoid complexity
  # Could be enhanced to use process pools in the future
  requests.map { |request| execute(request) }
end