Class: ShopifyCLI::ProcessSupervision

Inherits:
Object
  • Object
show all
Defined in:
lib/shopify_cli/process_supervision.rb

Overview

ProcessSupervision wraps a running process spawned by ‘exec` and keeps track if its `pid` and keeps a log file for it as well

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(identifier, pid: nil, time: Time.now.strftime("%s")) ⇒ ProcessSupervision

:nodoc:



133
134
135
136
137
138
139
140
# File 'lib/shopify_cli/process_supervision.rb', line 133

def initialize(identifier, pid: nil, time: Time.now.strftime("%s")) # :nodoc:
  @identifier = identifier
  @pid = pid
  @time = time
  FileUtils.mkdir_p(ShopifyCLI::ProcessSupervision.run_dir)
  @pid_path = File.join(ShopifyCLI::ProcessSupervision.run_dir, "#{identifier}.pid")
  @log_path = File.join(ShopifyCLI::ProcessSupervision.run_dir, "#{identifier}.log")
end

Instance Attribute Details

#identifierObject (readonly)

a string or a symbol to identify this process by



9
10
11
# File 'lib/shopify_cli/process_supervision.rb', line 9

def identifier
  @identifier
end

#log_pathObject (readonly)

filepath to the logfile for this process



17
18
19
# File 'lib/shopify_cli/process_supervision.rb', line 17

def log_path
  @log_path
end

#pidObject

process ID for the running process



11
12
13
# File 'lib/shopify_cli/process_supervision.rb', line 11

def pid
  @pid
end

#pid_pathObject (readonly)

filepath to the pidfile for this process



15
16
17
# File 'lib/shopify_cli/process_supervision.rb', line 15

def pid_path
  @pid_path
end

#timeObject (readonly)

starttime of the process



13
14
15
# File 'lib/shopify_cli/process_supervision.rb', line 13

def time
  @time
end

Class Method Details

.for_ident(identifier) ⇒ Object

Will find and create a new instance of ProcessSupervision for a running process if it is currently running. It will return nil if the process is not running.

#### Parameters

  • ‘identifier` - a string or a symbol that a process was started with

#### Returns

  • ‘process` - ProcessSupervision instance if the process is running this will be nil if the process is not running.



38
39
40
41
42
43
# File 'lib/shopify_cli/process_supervision.rb', line 38

def for_ident(identifier)
  pid, time = File.read(File.join(ShopifyCLI::ProcessSupervision.run_dir, "#{identifier}.pid")).split(":")
  new(identifier, pid: Integer(pid), time: time)
rescue Errno::ENOENT
  nil
end

.run_dirObject



20
21
22
23
# File 'lib/shopify_cli/process_supervision.rb', line 20

def run_dir
  # is the directory where the pid and logfile are kept
  File.join(ShopifyCLI.cache_dir, "sv")
end

.running?(identifier) ⇒ Boolean

will help identify if your process is still running in the background.

#### Parameters

  • ‘identifier` - a string or symbol to identify the new process by.

#### Returns

  • ‘running` - [true, false]

Returns:

  • (Boolean)


126
127
128
129
130
# File 'lib/shopify_cli/process_supervision.rb', line 126

def running?(identifier)
  process = for_ident(identifier)
  return false unless process
  process.alive?
end

.start(identifier, args, force_spawn: false) ⇒ Object

will fork and spawn a new process that is separate from the current process. This process will keep running beyond the command running so be careful!

#### Parameters

  • ‘identifier` - a string or symbol to identify the new process by.

  • ‘args` - a command to run, either a string or array of strings

  • ‘force_spawn` - whether we want the child process to be a spawn and not a fork, so it is terminated along with

    the parent
    

#### Returns

  • ‘process` - ProcessSupervision instance if the process is running, this will be nil if the process did not start.



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
# File 'lib/shopify_cli/process_supervision.rb', line 61

def start(identifier, args, force_spawn: false)
  return for_ident(identifier) if running?(identifier)

  # Some systems don't support forking process without extra gems, so we resort to spawning a new child process -
  # that means that it dies along with the original process if it is interrupted. If possible, we fork the process
  # so that it doesn't have to be restarted on every run.
  if Process.respond_to?(:fork) && !force_spawn
    fork do
      pid_file = new(identifier, pid: Process.pid)
      pid_file.write
      STDOUT.reopen(pid_file.log_path, "w")
      STDERR.reopen(pid_file.log_path, "w")
      STDIN.reopen("/dev/null", "r")
      Process.setsid
      exec(*args)
    end
  else
    pid_file = new(identifier)

    # Make sure the file exists and is empty, otherwise Windows fails
    File.open(pid_file.log_path, "w") {}
    pid = Process.spawn(
      *args,
      out: pid_file.log_path,
      err: pid_file.log_path,
      in: Context.new.windows? ? "nul" : "/dev/null",
    )
    pid_file.pid = pid
    pid_file.write

    Process.detach(pid)
  end

  sleep(0.1)
  for_ident(identifier)
end

.stop(identifier) ⇒ Object

will attempt to shutdown a running process

#### Parameters

  • ‘identifier` - a string or symbol to identify the new process by.

#### Returns

  • ‘stopped` - [true, false]



109
110
111
112
113
# File 'lib/shopify_cli/process_supervision.rb', line 109

def stop(identifier)
  process = for_ident(identifier)
  return false unless process
  process.stop
end

Instance Method Details

#alive?Boolean

will help identify if your process is still running in the background.

#### Returns

  • ‘alive` - [true, false]

Returns:

  • (Boolean)


168
169
170
# File 'lib/shopify_cli/process_supervision.rb', line 168

def alive?
  stat(pid)
end

#stopObject

will attempt to shutdown a running process

#### Parameters

  • ‘ctx` - the context of this command

#### Returns

  • ‘stopped` - [true, false]



153
154
155
156
157
158
159
# File 'lib/shopify_cli/process_supervision.rb', line 153

def stop
  kill_proc
  unlink
  true
rescue
  false
end

#writeObject

persists the pidfile



175
176
177
178
# File 'lib/shopify_cli/process_supervision.rb', line 175

def write
  FileUtils.mkdir_p(File.dirname(pid_path))
  File.write(pid_path, "#{pid}:#{time}")
end