Class: RailVerdict::ProcessRunner

Inherits:
Object
  • Object
show all
Defined in:
lib/rail_verdict/process_runner.rb

Defined Under Namespace

Classes: DirectoryError, Registry, RunResult

Constant Summary collapse

DEFAULT_TIMEOUT_SECONDS =
30.0
DEFAULT_MAX_STDOUT_BYTES =
4 * 1024 * 1024
DEFAULT_MAX_STDERR_BYTES =
64 * 1024
READ_CHUNK_BYTES =
64 * 1024
TERM_GRACE_SECONDS =
0.1
REAP_POLL_SECONDS =
0.01
ENV_ALLOWLIST =
%w[
  PATH HOME GEM_HOME GEM_PATH LANG RAILVERDICT_MINITEST_OUTPUT
].freeze
FORCED_ENV =
{ "LC_ALL" => "C.UTF-8", "TZ" => "UTC" }.freeze

Class Method Summary collapse

Class Method Details

.build_envObject



67
68
69
70
71
72
73
74
# File 'lib/rail_verdict/process_runner.rb', line 67

def build_env
  env = ENV.keys.to_h { |key| [key, nil] }
  ENV_ALLOWLIST.each do |key|
    value = ENV[key]
    env[key] = value unless value.nil?
  end
  env.merge(FORCED_ENV)
end

.registryObject



63
64
65
# File 'lib/rail_verdict/process_runner.rb', line 63

def registry
  @registry ||= Registry.new
end

.run(executable, argv, chdir:, timeout_seconds: DEFAULT_TIMEOUT_SECONDS, max_stdout_bytes: DEFAULT_MAX_STDOUT_BYTES, max_stderr_bytes: DEFAULT_MAX_STDERR_BYTES) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
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
# File 'lib/rail_verdict/process_runner.rb', line 26

def run(executable, argv, chdir:, timeout_seconds: DEFAULT_TIMEOUT_SECONDS,
        max_stdout_bytes: DEFAULT_MAX_STDOUT_BYTES, max_stderr_bytes: DEFAULT_MAX_STDERR_BYTES)
  directory = verify_directory(chdir)
  argv = argv.map { |element| validate_argv_element(element) }
  env = build_env

  stdout_read, stdout_write = IO.pipe
  stderr_read, stderr_write = IO.pipe
  pid = nil

  begin
    pid = Process.spawn(
      env,
      [executable, File.basename(executable)],
      *argv,
      chdir: directory,
      pgroup: true,
      in: :close,
      out: stdout_write,
      err: stderr_write
    )
  rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, ArgumentError => error
    return spawn_failure_result(error)
  end

  registry.register(pid)
  stdout_write.close
  stderr_write.close

  execute_child(pid, stdout_read, stderr_read, timeout_seconds, max_stdout_bytes, max_stderr_bytes)
ensure
  registry.unregister(pid) if pid
  [stdout_read, stdout_write, stderr_read, stderr_write].compact.each do |io|
    io.close unless io.closed?
  end
end