class Rundoc::CodeCommand::Background::ProcessSpawn

This class is responsible for running processes in the background

By default it logs output to a file. This can be used to “wait” for a specific output before continuing:

server = ProcessSpawn("rails server")
server.wait("Use Ctrl-C to stop")

The process can be queried for it’s status to check if it is still booted or not. the process can also be manually stopped:

server = ProcessSpawn("rails server")
server.alive? # => true
server.stop
server.alive? # => false

There are class level methods that can be used to “name” and record background processes. They can be used like this:

server = ProcessSpawn("rails server")
ProcessSpawn.add("muh_server", server)
ProcessSpawn.find("muh_server") # => <# ProcessSpawn instance >
ProcessSpawn.find("foo") # => RuntimeError "Could not find task with name 'foo', ..."

Attributes

tasks[R]
log[R]
pid[R]

Public Class Methods

add(name, value) click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 36
def self.add(name, value)
  raise "Task named #{name.inspect} is already started, choose a different name" if @tasks[name]
  @tasks[name] = value
end
find(name) click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 41
def self.find(name)
  raise "Could not find task with name #{name.inspect}, known task names: #{@tasks.keys.inspect}" unless @tasks[name]
  @tasks[name]
end
new(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1") click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 48
def initialize(command, timeout: 5, log: Tempfile.new("log"), out: "2>&1")
  @command = command
  @timeout_value = timeout
  @log_reference = log # https://twitter.com/schneems/status/1285289971083907075

  @log = Pathname.new(log)
  @log.dirname.mkpath
  FileUtils.touch(@log)

  @command = "/usr/bin/env bash -c #{@command.shellescape} >> #{@log} #{out}"
  @pid = nil
end

Public Instance Methods

alive?() click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 74
def alive?
  return false unless @pid
  Process.kill(0, @pid)
rescue Errno::ESRCH, Errno::EPERM
  false
end
check_alive!() click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 87
def check_alive!
  raise "#{@original_command} has exited unexpectedly: #{@log.read}" unless alive?
end
stop() click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 81
def stop
  return unless alive?
  Process.kill("TERM", -Process.getpgid(@pid))
  Process.wait(@pid)
end
wait(wait_value = nil, timeout_value = @timeout_value) click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 61
def wait(wait_value = nil, timeout_value = @timeout_value)
  call
  return unless wait_value

  Timeout.timeout(Integer(timeout_value)) do
    until @log.read.match(wait_value)
      sleep 0.01
    end
  end
rescue Timeout::Error
  raise "Timeout waiting for #{@command.inspect} to find a match using #{wait_value.inspect} in \n'#{log.read}'"
end

Private Instance Methods

call() click to toggle source
# File lib/rundoc/code_command/background/process_spawn.rb, line 91
        def call
  @pid ||= Process.spawn(@command, pgroup: true)
end