Class: TTY::Command::Window::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/tty/command/window/runner.rb

Overview

Spawns the child inside a PTY sized to the window, pumps its output into the block's emulator (and optional log/stream targets), enforces timeouts and builds the final TTY::Command::Result.

Constant Summary collapse

READ_CHUNK =
64 * 1024
SELECT_INTERVAL =
0.25
REAP_DEADLINE =

Post-pump reap budget. If the master closes but the child does not exit promptly (e.g. it double-forked or closed its stdio and kept running), we escalate to SIGKILL after this many seconds rather than blocking the caller forever.

2.0

Instance Method Summary collapse

Constructor Details

#initialize(cmd, options, coordinator, step: nil, &stream_block) ⇒ Runner

Returns a new instance of Runner.

Parameters:

  • cmd (TTY::Command::Cmd)
  • options (WindowOptions)

    validated window options

  • coordinator (Coordinator)
  • step (Step, nil) (defaults to: nil)

    when given, the command renders into the step's existing block instead of a block of its own; the step stays open after the command finishes

  • stream_block (Proc, nil)

    yields (chunk, nil) like tty-command



28
29
30
31
32
33
34
# File 'lib/tty/command/window/runner.rb', line 28

def initialize(cmd, options, coordinator, step: nil, &stream_block)
  @cmd = cmd
  @options = options
  @coordinator = coordinator
  @step = step
  @stream_block = stream_block
end

Instance Method Details

#run!TTY::Command::Result

Returns:

  • (TTY::Command::Result)


37
38
39
40
41
42
43
44
45
46
47
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
# File 'lib/tty/command/window/runner.rb', line 37

def run!
  require "pty"
  rows = @options.lines
  width = @coordinator.width

  if @step
    @block = @step.block
  else
    emulator = Emulator.new(
      rows: rows, cols: width,
      scrollback_limit: @options.scrollback,
      responder: ->(reply) { @session&.write(reply) }
    )
    @block = Block.new(
      emulator: emulator,
      title: @options.title,
      lines: rows,
      on_exit: @options.on_exit,
      interactive: @options.interactive?,
      dump_lines: @options.dump_lines
    )
    @emulator = emulator
  end

  @session = spawn_child(rows, width)
  started = clock
  # Only retain raw bytes when a capture mode actually consumes them.
  # For capture: :screen the emulator grid holds the final state; the
  # raw stream is dead weight and would grow unbounded on long runs
  # (see review/performance.md H5 / P0-1).
  @needs_raw = !@options.capture_screen?
  @raw = @needs_raw ? (+"").force_encoding(Encoding::BINARY) : nil
  @log = @options.output_log && File.open(@options.output_log, "wb")

  if @step
    @step.adopt_session(@session)
  else
    @coordinator.register(@block, child_session: @session)
    InputRouter.attach(@block, @session, @coordinator) if @options.interactive?
  end

  write_initial_input
  timed_out = timed_out_pumping?
  status = reap(timed_out)
  runtime = clock - started

  finish(status, runtime, timed_out)
  raise TTY::Command::TimeoutExceeded if timed_out

  build_result(status, runtime)
ensure
  cleanup
end