firespring_dev_commands 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +83 -0
- data/lib/firespring_dev_commands/audit/report/item.rb +33 -0
- data/lib/firespring_dev_commands/audit/report/levels.rb +36 -0
- data/lib/firespring_dev_commands/audit/report.rb +49 -0
- data/lib/firespring_dev_commands/aws/account/info.rb +15 -0
- data/lib/firespring_dev_commands/aws/account.rb +164 -0
- data/lib/firespring_dev_commands/aws/cloudformation/parameters.rb +26 -0
- data/lib/firespring_dev_commands/aws/cloudformation.rb +188 -0
- data/lib/firespring_dev_commands/aws/codepipeline.rb +96 -0
- data/lib/firespring_dev_commands/aws/credentials.rb +136 -0
- data/lib/firespring_dev_commands/aws/login.rb +131 -0
- data/lib/firespring_dev_commands/aws/parameter.rb +32 -0
- data/lib/firespring_dev_commands/aws/profile.rb +55 -0
- data/lib/firespring_dev_commands/aws/s3.rb +42 -0
- data/lib/firespring_dev_commands/aws.rb +10 -0
- data/lib/firespring_dev_commands/boolean.rb +7 -0
- data/lib/firespring_dev_commands/common.rb +112 -0
- data/lib/firespring_dev_commands/daterange.rb +171 -0
- data/lib/firespring_dev_commands/docker/compose.rb +271 -0
- data/lib/firespring_dev_commands/docker/status.rb +38 -0
- data/lib/firespring_dev_commands/docker.rb +276 -0
- data/lib/firespring_dev_commands/dotenv.rb +6 -0
- data/lib/firespring_dev_commands/env.rb +38 -0
- data/lib/firespring_dev_commands/eol/product_version.rb +86 -0
- data/lib/firespring_dev_commands/eol.rb +58 -0
- data/lib/firespring_dev_commands/git/info.rb +13 -0
- data/lib/firespring_dev_commands/git.rb +420 -0
- data/lib/firespring_dev_commands/jira/issue.rb +33 -0
- data/lib/firespring_dev_commands/jira/project.rb +13 -0
- data/lib/firespring_dev_commands/jira/user/type.rb +20 -0
- data/lib/firespring_dev_commands/jira/user.rb +31 -0
- data/lib/firespring_dev_commands/jira.rb +78 -0
- data/lib/firespring_dev_commands/logger.rb +8 -0
- data/lib/firespring_dev_commands/node/audit.rb +39 -0
- data/lib/firespring_dev_commands/node.rb +107 -0
- data/lib/firespring_dev_commands/php/audit.rb +71 -0
- data/lib/firespring_dev_commands/php.rb +109 -0
- data/lib/firespring_dev_commands/rake.rb +24 -0
- data/lib/firespring_dev_commands/ruby/audit.rb +30 -0
- data/lib/firespring_dev_commands/ruby.rb +113 -0
- data/lib/firespring_dev_commands/second.rb +22 -0
- data/lib/firespring_dev_commands/tar/pax_header.rb +49 -0
- data/lib/firespring_dev_commands/tar/type_flag.rb +49 -0
- data/lib/firespring_dev_commands/tar.rb +149 -0
- data/lib/firespring_dev_commands/templates/aws.rb +84 -0
- data/lib/firespring_dev_commands/templates/base_interface.rb +54 -0
- data/lib/firespring_dev_commands/templates/ci.rb +138 -0
- data/lib/firespring_dev_commands/templates/docker/application.rb +177 -0
- data/lib/firespring_dev_commands/templates/docker/default.rb +200 -0
- data/lib/firespring_dev_commands/templates/docker/node/application.rb +145 -0
- data/lib/firespring_dev_commands/templates/docker/php/application.rb +190 -0
- data/lib/firespring_dev_commands/templates/docker/ruby/application.rb +146 -0
- data/lib/firespring_dev_commands/templates/eol.rb +23 -0
- data/lib/firespring_dev_commands/templates/git.rb +147 -0
- data/lib/firespring_dev_commands/version.rb +11 -0
- data/lib/firespring_dev_commands.rb +21 -0
- metadata +436 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
module Dev
|
5
|
+
# Module containing methods for calculating start/stop dates for given ranges
|
6
|
+
module DateRange
|
7
|
+
# Class contains methods for calculating a date range with an hourly interval
|
8
|
+
class Hourly
|
9
|
+
attr_accessor :date
|
10
|
+
|
11
|
+
def initialize(date = nil)
|
12
|
+
@date = date || DateTime.now
|
13
|
+
end
|
14
|
+
|
15
|
+
# The hour interval previous to the current date
|
16
|
+
def previous
|
17
|
+
raise 'not implemented'
|
18
|
+
end
|
19
|
+
|
20
|
+
# The hour interval for the current date
|
21
|
+
def current
|
22
|
+
raise 'not implemented'
|
23
|
+
end
|
24
|
+
|
25
|
+
# The hour interval after the current date
|
26
|
+
def next
|
27
|
+
raise 'not implemented'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Class contains methods for calculating a date range with an daily interval
|
32
|
+
class Daily
|
33
|
+
attr_accessor :date
|
34
|
+
|
35
|
+
def initialize(date = nil)
|
36
|
+
@date = date || DateTime.now
|
37
|
+
end
|
38
|
+
|
39
|
+
# The daily interval previous to the current date
|
40
|
+
def previous
|
41
|
+
@date = date.beginning_of_day - 1
|
42
|
+
current
|
43
|
+
end
|
44
|
+
|
45
|
+
# The daily interval for the current date
|
46
|
+
def current
|
47
|
+
start = date.beginning_of_day
|
48
|
+
stop = date.end_of_day
|
49
|
+
[start, stop]
|
50
|
+
end
|
51
|
+
|
52
|
+
# The daily interval after the current date
|
53
|
+
def next
|
54
|
+
@date = date.end_of_day + 1
|
55
|
+
current
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Class contains methods for calculating a date range with an weekly interval
|
60
|
+
class Weekly
|
61
|
+
attr_accessor :date
|
62
|
+
|
63
|
+
def initialize(date = nil)
|
64
|
+
@date = date || DateTime.now
|
65
|
+
end
|
66
|
+
|
67
|
+
# The weekly interval previous to the current date
|
68
|
+
def previous
|
69
|
+
@date = date.beginning_of_week - 1
|
70
|
+
current
|
71
|
+
end
|
72
|
+
|
73
|
+
# The weekly interval for the current date
|
74
|
+
def current
|
75
|
+
start = date.beginning_of_week
|
76
|
+
stop = date.end_of_week
|
77
|
+
[start, stop]
|
78
|
+
end
|
79
|
+
|
80
|
+
# The weekly interval after the current date
|
81
|
+
def next
|
82
|
+
@date = date.end_of_week + 1
|
83
|
+
current
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Class contains methods for calculating a date range with an monthly interval
|
88
|
+
class Monthly
|
89
|
+
attr_accessor :date
|
90
|
+
|
91
|
+
def initialize(date = nil)
|
92
|
+
@date = date || DateTime.now
|
93
|
+
end
|
94
|
+
|
95
|
+
# The monthly interval previous to the current date
|
96
|
+
def previous
|
97
|
+
@date = date.beginning_of_month - 1
|
98
|
+
current
|
99
|
+
end
|
100
|
+
|
101
|
+
# The monthly interval for the current date
|
102
|
+
def current
|
103
|
+
start = date.beginning_of_month
|
104
|
+
stop = date.end_of_month
|
105
|
+
[start, stop]
|
106
|
+
end
|
107
|
+
|
108
|
+
# The monthly interval after the current date
|
109
|
+
def next
|
110
|
+
@date = date.end_of_month + 1
|
111
|
+
current
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Class contains methods for calculating a date range with an quarterly interval
|
116
|
+
class Quarterly
|
117
|
+
attr_accessor :date
|
118
|
+
|
119
|
+
def initialize(date = nil)
|
120
|
+
@date = date || DateTime.now
|
121
|
+
end
|
122
|
+
|
123
|
+
# The quarterly interval previous to the current date
|
124
|
+
def previous
|
125
|
+
@date = date.beginning_of_quarter - 1
|
126
|
+
current
|
127
|
+
end
|
128
|
+
|
129
|
+
# The quarterly interval for the current date
|
130
|
+
def current
|
131
|
+
start = date.beginning_of_quarter
|
132
|
+
stop = date.end_of_quarter
|
133
|
+
[start, stop]
|
134
|
+
end
|
135
|
+
|
136
|
+
# The quarterly interval after the current date
|
137
|
+
def next
|
138
|
+
@date = date.end_of_quarter + 1
|
139
|
+
current
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Class contains methods for calculating a date range with an yearly interval
|
144
|
+
class Yearly
|
145
|
+
attr_accessor :date
|
146
|
+
|
147
|
+
def initialize(date = nil)
|
148
|
+
@date = date || DateTime.now
|
149
|
+
end
|
150
|
+
|
151
|
+
# The yearly interval previous to the current date
|
152
|
+
def previous
|
153
|
+
@date = date.beginning_of_year - 1
|
154
|
+
current
|
155
|
+
end
|
156
|
+
|
157
|
+
# The yearly interval for the current date
|
158
|
+
def current
|
159
|
+
start = date.beginning_of_year
|
160
|
+
stop = date.end_of_year
|
161
|
+
[start, stop]
|
162
|
+
end
|
163
|
+
|
164
|
+
# The yearly interval after the current date
|
165
|
+
def next
|
166
|
+
@date = date.end_of_year + 1
|
167
|
+
current
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
# Change http timeouts to 1hr
|
2
|
+
ENV['COMPOSE_HTTP_TIMEOUT'] = '3600'
|
3
|
+
ENV['DOCKER_BUILDKIT'] = '1'
|
4
|
+
ENV['COMPOSE_DOCKER_CLI_BUILD'] = '1'
|
5
|
+
|
6
|
+
module Dev
|
7
|
+
class Docker
|
8
|
+
# Class containing methods for interfacing with the docker compose cli
|
9
|
+
class Compose
|
10
|
+
# Config object for setting top level docker compose config options
|
11
|
+
Config = Struct.new(:project_dir, :project_name, :compose_files, :min_version, :max_version) do
|
12
|
+
def initialize
|
13
|
+
self.project_dir = DEV_COMMANDS_ROOT_DIR
|
14
|
+
self.project_name = DEV_COMMANDS_PROJECT_NAME
|
15
|
+
self.compose_files = ["#{DEV_COMMANDS_ROOT_DIR}/docker-compose.yml"]
|
16
|
+
self.min_version = nil
|
17
|
+
self.max_version = nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# Instantiates a new top level config object if one hasn't already been created
|
23
|
+
# Yields that config object to any given block
|
24
|
+
# Returns the resulting config object
|
25
|
+
def config
|
26
|
+
@config ||= Config.new
|
27
|
+
yield(@config) if block_given?
|
28
|
+
@config
|
29
|
+
end
|
30
|
+
|
31
|
+
# Alias the config method to configure for a slightly clearer access syntax
|
32
|
+
alias_method :configure, :config
|
33
|
+
|
34
|
+
# Returns the version of the docker-compose executable on the system
|
35
|
+
def version
|
36
|
+
@version ||= `#{EXECUTABLE_NAME} --version`.match(/version v?([0-9.]+)/)[1]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @todo Change this to "docker compose" when everyone is off v1
|
41
|
+
# The name of the docker compose executable
|
42
|
+
EXECUTABLE_NAME = 'docker-compose'.freeze
|
43
|
+
|
44
|
+
attr_accessor :capture, :compose_files, :environment, :options, :project_dir, :project_name, :services, :user, :volumes
|
45
|
+
|
46
|
+
def initialize(
|
47
|
+
compose_files: self.class.config.compose_files,
|
48
|
+
environment: [],
|
49
|
+
options: [],
|
50
|
+
project_dir: self.class.config.project_dir,
|
51
|
+
project_name: self.class.config.project_name,
|
52
|
+
services: [],
|
53
|
+
user: nil,
|
54
|
+
volumes: [],
|
55
|
+
capture: false
|
56
|
+
)
|
57
|
+
@compose_files = Array(compose_files)
|
58
|
+
@environment = environment
|
59
|
+
@options = Array(options)
|
60
|
+
@project_dir = project_dir
|
61
|
+
@project_name = project_name
|
62
|
+
@services = Array(services)
|
63
|
+
@user = user
|
64
|
+
@volumes = Array(volumes)
|
65
|
+
@capture = capture
|
66
|
+
check_version
|
67
|
+
end
|
68
|
+
|
69
|
+
# Checks the min and max version against the current docker version if they have been configured
|
70
|
+
def check_version
|
71
|
+
min_version = self.class.config.min_version
|
72
|
+
raise "requires #{EXECUTABLE_NAME} version >= #{min_version} (found #{self.class.version})" if min_version &&
|
73
|
+
!Dev::Common.new.version_greater_than(min_version, self.class.version)
|
74
|
+
|
75
|
+
max_version = self.class.config.max_version
|
76
|
+
raise "requires #{EXECUTABLE_NAME} version < #{max_version} (found #{self.class.version})" if max_version &&
|
77
|
+
Dev::Common.new.version_greater_than(max_version, self.class.version)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Pull in supported env settings and call build
|
81
|
+
# Specify PULL=true to force compose to pull all backing images as part of the build
|
82
|
+
# Specify NO_CACHE=true to force compose to build from scratch rather than using build cache
|
83
|
+
def build
|
84
|
+
merge_options('--parallel')
|
85
|
+
merge_env_pull_option
|
86
|
+
merge_env_cache_option
|
87
|
+
execute_command(build_command('build'))
|
88
|
+
end
|
89
|
+
|
90
|
+
# Pull in supported env settings and call up
|
91
|
+
# Specify BUILD=true to force/allow service builds before startup
|
92
|
+
# Specify NO_DEPS=true to only start the given service and ignore starting it's dependencies
|
93
|
+
# Specify DETACHED=false to not detach from the started processes
|
94
|
+
def up
|
95
|
+
merge_env_build_option
|
96
|
+
merge_env_deps_option
|
97
|
+
merge_env_detach_option
|
98
|
+
execute_command(build_command('up'))
|
99
|
+
end
|
100
|
+
|
101
|
+
# Exec into a running container and run the given shell commands
|
102
|
+
# Default to running 'bash' which will start a terminal in the running container
|
103
|
+
def sh(shell_commands = ['bash'])
|
104
|
+
execute_command(build_command('exec', *shell_commands))
|
105
|
+
end
|
106
|
+
|
107
|
+
# Pull in supported env settings and call logs
|
108
|
+
# Specify NO_FOLLOW=true if you want to print current logs and exist
|
109
|
+
# Specify TAIL to pass tail options to the logs command
|
110
|
+
def logs
|
111
|
+
merge_env_follow_option
|
112
|
+
merge_env_tail_option
|
113
|
+
execute_command(build_command('logs'))
|
114
|
+
end
|
115
|
+
|
116
|
+
# Pull in supported env settings and call down
|
117
|
+
# Specify REMOVE_VOLUMES=true to also remove any unused volumes when the containers are stopped
|
118
|
+
def down
|
119
|
+
merge_env_volumes_option
|
120
|
+
execute_command(build_command('down'))
|
121
|
+
end
|
122
|
+
|
123
|
+
# Pull in supported env settings and call stop
|
124
|
+
def stop
|
125
|
+
execute_command(build_command('stop'))
|
126
|
+
end
|
127
|
+
|
128
|
+
# Call the compose exec method passing the given args after it
|
129
|
+
def exec(*args)
|
130
|
+
execute_command(build_command('exec', *args))
|
131
|
+
end
|
132
|
+
|
133
|
+
# Call the compose run method passing the given args after it
|
134
|
+
def run(*args)
|
135
|
+
execute_command(build_command('run', *args))
|
136
|
+
end
|
137
|
+
|
138
|
+
# Call the compose push method
|
139
|
+
def push
|
140
|
+
execute_command(build_command('push'))
|
141
|
+
end
|
142
|
+
|
143
|
+
# Call the compose pull method
|
144
|
+
def pull
|
145
|
+
execute_command(build_command('pull'))
|
146
|
+
end
|
147
|
+
|
148
|
+
# Get the first container matching the given name
|
149
|
+
# If prefix is specified then this method will filter for compose services in the given project only
|
150
|
+
# If status is specified then this method will filter containers in the given status only
|
151
|
+
def container_by_name(service_name, prefix = nil, status: [Docker::Status::RUNNING])
|
152
|
+
prefix ||= project_name
|
153
|
+
containers = ::Docker::Container.all(filters: {status: Array(status), label: ["com.docker.compose.service=#{service_name}"]}.to_json)
|
154
|
+
containers.each do |container|
|
155
|
+
container&.info&.dig('Names')&.each do |name|
|
156
|
+
return container if name.start_with?("/#{prefix}")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
raise "Container not found for #{service_name} with prefix #{prefix}"
|
161
|
+
end
|
162
|
+
|
163
|
+
# Gets the dynamic port which was assigned to the compose service on the original private port
|
164
|
+
def mapped_public_port(service_name, private_port)
|
165
|
+
container = container_by_name(service_name)
|
166
|
+
port_mapping = container.info['Ports'].find { |it| it['PrivatePort'] == private_port }
|
167
|
+
port_mapping['PublicPort']
|
168
|
+
end
|
169
|
+
|
170
|
+
# Merge --no-cache option if nocache is set to true and no existing cache options are present
|
171
|
+
private def merge_env_cache_option
|
172
|
+
return if @options.any? { |it| it.include?('cache') }
|
173
|
+
|
174
|
+
merge_options('--no-cache') if ENV['NO_CACHE'].to_s.strip == 'true'
|
175
|
+
end
|
176
|
+
|
177
|
+
# Merge --pull option if PULL is set to true and no existing pull options are present
|
178
|
+
private def merge_env_pull_option
|
179
|
+
return if @options.any? { |it| it.include?('pull') }
|
180
|
+
|
181
|
+
merge_options('--pull') if ENV['PULL'].to_s.strip == 'true'
|
182
|
+
end
|
183
|
+
|
184
|
+
# Merge --no-build option unless BUILD is set to true and no existing build options are present
|
185
|
+
private def merge_env_build_option
|
186
|
+
return if @options.any? { |it| it.include?('build') }
|
187
|
+
|
188
|
+
merge_options('--no-build') unless ENV['BUILD'].to_s.strip == 'true'
|
189
|
+
end
|
190
|
+
|
191
|
+
# Merge --no-deps option if NO_DEPS is set to true and no existing deps options are present
|
192
|
+
private def merge_env_deps_option
|
193
|
+
return if @options.any? { |it| it.include?('deps') }
|
194
|
+
|
195
|
+
merge_options('--no-deps') if ENV['NO_DEPS'].to_s.strip == 'true'
|
196
|
+
end
|
197
|
+
|
198
|
+
# Merge --detach option unless DETACHED is set to false and no existing detach options are present
|
199
|
+
private def merge_env_detach_option
|
200
|
+
return if @options.any? { |it| it.include?('detach') }
|
201
|
+
|
202
|
+
merge_options('--detach') unless ENV['DETACHED'].to_s.strip == 'false'
|
203
|
+
end
|
204
|
+
|
205
|
+
# Merge -f option unless NO_FOLLOW is set to true and no existing follow options are present
|
206
|
+
private def merge_env_follow_option
|
207
|
+
return if @options.any? { |it| it.include?('follow') }
|
208
|
+
|
209
|
+
merge_options('-f') unless ENV['NO_FOLLOW'].to_s.strip == 'true'
|
210
|
+
end
|
211
|
+
|
212
|
+
# Merge --tail option unless TAIL is empty and no existing tail options are present
|
213
|
+
private def merge_env_tail_option
|
214
|
+
return if @options.any? { |it| it.include?('tail') }
|
215
|
+
|
216
|
+
merge_options('--tail', ENV.fetch('TAIL', nil)) unless ENV['tail'].to_s.strip.empty?
|
217
|
+
end
|
218
|
+
|
219
|
+
# Merge --volumes option if REMOVE_VOLUMES is set to true and no existing volume options are present
|
220
|
+
private def merge_env_volumes_option
|
221
|
+
return if @options.any? { |it| it.include?('volume') }
|
222
|
+
|
223
|
+
merge_options('--volumes') if ENV['REMOVE_VOLUMES'].to_s.strip == 'true'
|
224
|
+
end
|
225
|
+
|
226
|
+
# Merges two arrays removing nested structure and duplicate keys
|
227
|
+
private def merge_options(*opts)
|
228
|
+
@options = (@options + Array(opts)).flatten.uniq
|
229
|
+
end
|
230
|
+
|
231
|
+
# Build the compose command with the given inputs
|
232
|
+
private def build_command(action, *cmd)
|
233
|
+
command = [EXECUTABLE_NAME]
|
234
|
+
command << '--project-directory' << project_dir
|
235
|
+
command << '-p' << project_name if project_name
|
236
|
+
Array(compose_files).compact.each { |file| command << '-f' << file }
|
237
|
+
command << action
|
238
|
+
|
239
|
+
Array(environment).compact.each do |value|
|
240
|
+
command << '-e'
|
241
|
+
command << normalize_command_line_arg(value)
|
242
|
+
end
|
243
|
+
|
244
|
+
Array(volumes).compact.each do |volume|
|
245
|
+
command << '-v'
|
246
|
+
command << normalize_command_line_arg(volume, ':')
|
247
|
+
end
|
248
|
+
|
249
|
+
command << '-u' << user if user
|
250
|
+
command.concat(Array(options).compact)
|
251
|
+
command.concat(Array(services).compact)
|
252
|
+
command.concat(Array(cmd).flatten.compact)
|
253
|
+
command
|
254
|
+
end
|
255
|
+
|
256
|
+
# Normalize the command line inputs for various complex input possibilities
|
257
|
+
# Possible argument types are Array, Hash, or String
|
258
|
+
private def normalize_command_line_arg(arg, separator = '=')
|
259
|
+
return "#{arg.first}#{separator}#{arg.last}" if arg.is_a?(Array)
|
260
|
+
|
261
|
+
arg.to_s
|
262
|
+
end
|
263
|
+
|
264
|
+
# Print the compose command that will be executed and then execute it
|
265
|
+
private def execute_command(command)
|
266
|
+
LOG.debug " > #{command.join(' ')}"
|
267
|
+
::Dev::Common.new.run_command(command, capture: capture)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Dev
|
2
|
+
class Docker
|
3
|
+
# Class containing constants for docker status names and groups
|
4
|
+
class Status
|
5
|
+
# Docker created status name
|
6
|
+
CREATED = :created
|
7
|
+
|
8
|
+
# Docker restarting status name
|
9
|
+
RESTARTING = :restarting
|
10
|
+
|
11
|
+
# Docker running status name
|
12
|
+
RUNNING = :running
|
13
|
+
|
14
|
+
# Docker removing status name
|
15
|
+
REMOVING = :removing
|
16
|
+
|
17
|
+
# Docker paused status name
|
18
|
+
PAUSED = :paused
|
19
|
+
|
20
|
+
# Docker exited status name
|
21
|
+
EXITED = :exited
|
22
|
+
|
23
|
+
# Docker dead status name
|
24
|
+
DEAD = :dead
|
25
|
+
|
26
|
+
# Array containing all available docker statuses
|
27
|
+
ALL = [
|
28
|
+
CREATED,
|
29
|
+
RESTARTING,
|
30
|
+
RUNNING,
|
31
|
+
REMOVING,
|
32
|
+
PAUSED,
|
33
|
+
EXITED,
|
34
|
+
DEAD
|
35
|
+
].freeze
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|