mini-clean-kit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/mini-clean-kit.gemspec +12 -0
  3. data/spring-4.7.0/LICENSE.txt +23 -0
  4. data/spring-4.7.0/README.md +467 -0
  5. data/spring-4.7.0/bin/spring +49 -0
  6. data/spring-4.7.0/lib/spring/application/boot.rb +25 -0
  7. data/spring-4.7.0/lib/spring/application.rb +449 -0
  8. data/spring-4.7.0/lib/spring/application_manager.rb +145 -0
  9. data/spring-4.7.0/lib/spring/binstub.rb +13 -0
  10. data/spring-4.7.0/lib/spring/boot.rb +10 -0
  11. data/spring-4.7.0/lib/spring/client/binstub.rb +190 -0
  12. data/spring-4.7.0/lib/spring/client/command.rb +18 -0
  13. data/spring-4.7.0/lib/spring/client/help.rb +62 -0
  14. data/spring-4.7.0/lib/spring/client/rails.rb +34 -0
  15. data/spring-4.7.0/lib/spring/client/run.rb +297 -0
  16. data/spring-4.7.0/lib/spring/client/server.rb +18 -0
  17. data/spring-4.7.0/lib/spring/client/status.rb +30 -0
  18. data/spring-4.7.0/lib/spring/client/stop.rb +22 -0
  19. data/spring-4.7.0/lib/spring/client/version.rb +11 -0
  20. data/spring-4.7.0/lib/spring/client.rb +48 -0
  21. data/spring-4.7.0/lib/spring/command_wrapper.rb +82 -0
  22. data/spring-4.7.0/lib/spring/commands/rails.rb +112 -0
  23. data/spring-4.7.0/lib/spring/commands/rake.rb +30 -0
  24. data/spring-4.7.0/lib/spring/commands.rb +57 -0
  25. data/spring-4.7.0/lib/spring/configuration.rb +99 -0
  26. data/spring-4.7.0/lib/spring/env.rb +115 -0
  27. data/spring-4.7.0/lib/spring/errors.rb +36 -0
  28. data/spring-4.7.0/lib/spring/failsafe_thread.rb +14 -0
  29. data/spring-4.7.0/lib/spring/json.rb +623 -0
  30. data/spring-4.7.0/lib/spring/process_title_updater.rb +65 -0
  31. data/spring-4.7.0/lib/spring/server.rb +162 -0
  32. data/spring-4.7.0/lib/spring/version.rb +3 -0
  33. data/spring-4.7.0/lib/spring/watcher/abstract.rb +117 -0
  34. data/spring-4.7.0/lib/spring/watcher/polling.rb +98 -0
  35. data/spring-4.7.0/lib/spring/watcher.rb +30 -0
  36. metadata +75 -0
@@ -0,0 +1,449 @@
1
+ require "spring/boot"
2
+ require "pty"
3
+
4
+ module Spring
5
+ class Application
6
+ attr_reader :manager, :watcher, :spring_env, :original_env
7
+
8
+ def initialize(manager, original_env, spring_env = Env.new)
9
+ @manager = manager
10
+ @original_env = original_env
11
+ @spring_env = spring_env
12
+ @mutex = Mutex.new
13
+ @waiting = {}
14
+ @clients = {}
15
+ @preloaded = false
16
+ @state = :initialized
17
+ @interrupt = IO.pipe
18
+ end
19
+
20
+ def state(val)
21
+ return if exiting?
22
+ log "#{@state} -> #{val}"
23
+ @state = val
24
+ end
25
+
26
+ def state!(val)
27
+ state val
28
+ @interrupt.last.write "."
29
+ end
30
+
31
+ def spawn_env
32
+ env = JSON.load(ENV["SPRING_SPAWN_ENV"].dup).map { |key, value| "#{key}=#{value}" }
33
+ env.join(", ") if env.any?
34
+ end
35
+
36
+ def app_env
37
+ ENV['RAILS_ENV']
38
+ end
39
+
40
+ def app_name
41
+ spring_env.app_name
42
+ end
43
+
44
+ def log(message)
45
+ spring_env.log "[application:#{app_env}] #{message}"
46
+ end
47
+
48
+ def preloaded?
49
+ @preloaded
50
+ end
51
+
52
+ def preload_failed?
53
+ @preloaded == :failure
54
+ end
55
+
56
+ def exiting?
57
+ @state == :exiting
58
+ end
59
+
60
+ def terminating?
61
+ @state == :terminating
62
+ end
63
+
64
+ def watcher_stale?
65
+ @state == :watcher_stale
66
+ end
67
+
68
+ def initialized?
69
+ @state == :initialized
70
+ end
71
+
72
+ def start_watcher
73
+ @watcher = Spring.watcher
74
+
75
+ @watcher.on_stale do
76
+ state! :watcher_stale
77
+ end
78
+
79
+ if @watcher.respond_to? :on_debug
80
+ @watcher.on_debug do |message|
81
+ spring_env.log "[watcher:#{app_env}] #{message}"
82
+ end
83
+ end
84
+
85
+ @watcher.start
86
+ end
87
+
88
+ def preload
89
+ log "preloading app"
90
+
91
+ begin
92
+ require "spring/commands"
93
+ ensure
94
+ start_watcher
95
+ end
96
+
97
+ require Spring.application_root_path.join("config", "application")
98
+
99
+ unless Rails.respond_to?(:gem_version) && Rails.gem_version >= Gem::Version.new('6.0.0')
100
+ raise "Spring only supports Rails >= 6.0.0"
101
+ end
102
+
103
+ Rails::Application.initializer :ensure_reloading_is_enabled, group: :all do
104
+ next if Spring.dangerously_allow_disabling_reloading
105
+
106
+ if Rails.application.config.cache_classes
107
+ config_name, set_to = if Rails.application.config.respond_to?(:enable_reloading=)
108
+ ["enable_reloading", "true"]
109
+ else
110
+ ["cache_classes", "false"]
111
+ end
112
+ raise <<-MSG.strip_heredoc
113
+ Spring reloads, and therefore needs the application to have reloading enabled.
114
+ Please, set config.#{config_name} to #{set_to} in config/environments/#{Rails.env}.rb.
115
+ (If you understand the trade-offs and want to disable Rails' reloader anyway,
116
+ set `Spring.dangerously_allow_disabling_reloading = true` in config/spring.rb.)
117
+ MSG
118
+ end
119
+ end
120
+
121
+ require Spring.application_root_path.join("config", "environment")
122
+
123
+ invoke_after_environment_load_callbacks
124
+ preload_framework_base_classes
125
+
126
+ disconnect_database
127
+
128
+ @preloaded = :success
129
+ rescue Exception => e
130
+ @preloaded = :failure
131
+ watcher.add e.backtrace.map { |line| line[/^(.*)\:\d+/, 1] }
132
+ raise e unless initialized?
133
+ ensure
134
+ watcher.add loaded_application_features
135
+ watcher.add Spring.gemfile, Spring.gemfile_lock
136
+
137
+ if defined?(Rails) && Rails.application
138
+ watcher.add Rails.application.paths["config/initializers"]
139
+ rails_root = Rails.root.to_s
140
+ Rails::Engine.subclasses.each do |engine|
141
+ if engine.root.to_s.start_with?(rails_root)
142
+ watcher.add engine.paths["config/initializers"].expanded
143
+ end
144
+ end
145
+ watcher.add Rails.application.paths["config/database"]
146
+ if secrets_path = Rails.application.paths["config/secrets"]
147
+ watcher.add secrets_path
148
+ end
149
+ end
150
+ end
151
+
152
+ # Eagerly autoload framework base classes
153
+ FRAMEWORK_BASE_CLASSES = %w[
154
+ ActionMailer::Base
155
+ ActionController::Base
156
+ ActionController::API
157
+ ].freeze
158
+
159
+ def preload_framework_base_classes
160
+ FRAMEWORK_BASE_CLASSES.each do |const|
161
+ Object.const_get(const) if Object.const_defined?(const)
162
+ end
163
+ end
164
+
165
+ def eager_preload
166
+ with_pty do
167
+ # we can't see stderr and there could be issues when it's overflown
168
+ # see https://github.com/rails/spring/issues/396
169
+ STDERR.reopen("/dev/null")
170
+ preload
171
+ end
172
+ end
173
+
174
+ def run
175
+ state :running
176
+ manager.puts
177
+
178
+ loop do
179
+ IO.select [manager, @interrupt.first]
180
+
181
+ if terminating? || watcher_stale? || preload_failed?
182
+ exit
183
+ else
184
+ serve manager.recv_io(UNIXSocket)
185
+ end
186
+ end
187
+ end
188
+
189
+ def serve(client)
190
+ log "got client"
191
+ manager.puts
192
+
193
+ @clients[client] = true
194
+
195
+ _stdout, stderr, _stdin = streams = 3.times.map { client.recv_io }
196
+ [STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
197
+
198
+ if preloaded?
199
+ client.puts(0) # preload success
200
+ else
201
+ begin
202
+ preload
203
+ client.puts(0) # preload success
204
+ rescue Exception
205
+ log "preload failed"
206
+ ignore_client_disconnect { client.puts(1) } # preload failure
207
+ raise
208
+ end
209
+ end
210
+
211
+ args, env = JSON.load(client.read(client.gets.to_i)).values_at("args", "env")
212
+ command = Spring.command(args.shift)
213
+
214
+ connect_database
215
+ setup command
216
+
217
+ if Rails.application.reloaders.any?(&:updated?)
218
+ Rails.application.reloader.reload!
219
+ end
220
+
221
+ pid = fork {
222
+ # Make sure to close other clients otherwise their graceful termination
223
+ # will be impossible due to reference from this fork.
224
+ @clients.each_key { |c| c.close if c != client }
225
+
226
+ Process.setsid
227
+ IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
228
+ trap("TERM", "DEFAULT")
229
+
230
+ unless Spring.quiet
231
+ STDERR.puts "Running via Spring preloader in process #{Process.pid}"
232
+
233
+ if Rails.env.production?
234
+ STDERR.puts "WARNING: Spring is running in production. To fix " \
235
+ "this make sure the spring gem is only present " \
236
+ "in `development` and `test` groups in your Gemfile " \
237
+ "and make sure you always use " \
238
+ "`bundle install --without development test` in production"
239
+ end
240
+ end
241
+
242
+ ARGV.replace(args)
243
+ $0 = command.exec_name
244
+
245
+ # Delete all env vars which are unchanged from before Spring started
246
+ original_env.each { |k, v| ENV.delete k if ENV[k] == v }
247
+
248
+ # Load in the current env vars, except those which *were* changed when Spring started
249
+ env.each { |k, v| ENV[k] ||= v }
250
+
251
+ connect_database
252
+ srand
253
+
254
+ invoke_after_fork_callbacks
255
+ shush_backtraces
256
+
257
+ command.call
258
+ }
259
+
260
+ disconnect_database
261
+
262
+ log "forked #{pid}"
263
+ manager.puts pid
264
+
265
+ wait pid, streams, client
266
+ rescue Exception => e
267
+ if e.is_a?(Errno::EPIPE)
268
+ log "client disconnected (#{e.message}), ignoring command"
269
+ else
270
+ log "exception: #{e}"
271
+ end
272
+ manager.puts unless pid
273
+
274
+ if streams && !e.is_a?(SystemExit)
275
+ ignore_client_disconnect { print_exception(stderr, e) }
276
+ streams.each { |stream| ignore_client_disconnect { stream.close } }
277
+ end
278
+
279
+ ignore_client_disconnect { client.puts(1) if pid }
280
+ client.close
281
+ ensure
282
+ # Redirect STDOUT and STDERR to prevent from keeping the original FDs
283
+ # (i.e. to prevent `spring rake -T | grep db` from hanging forever),
284
+ # even when exception is raised before forking (i.e. preloading).
285
+ reset_streams
286
+ end
287
+
288
+ def terminate
289
+ if exiting?
290
+ # Ensure that we do not ignore subsequent termination attempts
291
+ log "forced exit"
292
+ @waiting.each_key { |pid| Process.kill("TERM", pid) }
293
+ Kernel.exit
294
+ else
295
+ state! :terminating
296
+ end
297
+ end
298
+
299
+ def exit
300
+ state :exiting
301
+ manager.shutdown(:RDWR)
302
+ exit_if_finished
303
+ sleep
304
+ end
305
+
306
+ def exit_if_finished
307
+ @mutex.synchronize {
308
+ Kernel.exit if exiting? && @waiting.empty?
309
+ }
310
+ end
311
+
312
+ # The command might need to require some files in the
313
+ # main process so that they are cached. For example a test command wants to
314
+ # load the helper file once and have it cached.
315
+ def setup(command)
316
+ if command.setup
317
+ watcher.add loaded_application_features # loaded features may have changed
318
+ end
319
+ end
320
+
321
+ def invoke_after_fork_callbacks
322
+ Spring.after_fork_callbacks.each do |callback|
323
+ callback.call
324
+ end
325
+ end
326
+
327
+ def invoke_after_environment_load_callbacks
328
+ Spring.after_environment_load_callbacks.each do |callback|
329
+ callback.call
330
+ end
331
+ end
332
+
333
+ def loaded_application_features
334
+ root = Spring.application_root_path.to_s
335
+ $LOADED_FEATURES.select { |f| f.start_with?(root) }
336
+ end
337
+
338
+ def disconnect_database
339
+ ActiveRecord::Base.remove_connection if active_record_configured?
340
+ end
341
+
342
+ def connect_database
343
+ ActiveRecord::Base.establish_connection if active_record_configured?
344
+ end
345
+
346
+ # This feels very naughty
347
+ def shush_backtraces
348
+ Kernel.module_eval do
349
+ old_raise = Kernel.method(:raise)
350
+ remove_method :raise
351
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.2.0')
352
+ define_method :raise do |*args, **kwargs|
353
+ begin
354
+ old_raise.call(*args, **kwargs)
355
+ ensure
356
+ if $!
357
+ lib = File.expand_path("..", __FILE__)
358
+ $!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
359
+ $!.backtrace_locations.reject! { |line| line.path&.start_with?(lib) } unless $!.backtrace_locations.frozen?
360
+ end
361
+ end
362
+ end
363
+ else
364
+ define_method :raise do |*args|
365
+ begin
366
+ old_raise.call(*args)
367
+ ensure
368
+ if $!
369
+ lib = File.expand_path("..", __FILE__)
370
+ $!.backtrace.reject! { |line| line.start_with?(lib) } unless $!.backtrace.frozen?
371
+ $!.backtrace_locations.reject! { |line| line.path&.start_with?(lib) } unless $!.backtrace_locations.frozen?
372
+ end
373
+ end
374
+ end
375
+ end
376
+ private :raise
377
+ end
378
+ end
379
+
380
+ def print_exception(stream, error)
381
+ first, rest = error.backtrace.first, error.backtrace.drop(1)
382
+ stream.puts("#{first}: #{error} (#{error.class})")
383
+ rest.each { |line| stream.puts("\tfrom #{line}") }
384
+ end
385
+
386
+ def with_pty
387
+ PTY.open do |master, slave|
388
+ [STDOUT, STDERR, STDIN].each { |s| s.reopen slave }
389
+ reader_thread = Spring.failsafe_thread { master.read }
390
+ begin
391
+ yield
392
+ ensure
393
+ reader_thread.kill
394
+ reset_streams
395
+ end
396
+ end
397
+ end
398
+
399
+ def reset_streams
400
+ [STDOUT, STDERR].each { |stream| stream.reopen(spring_env.log_file) }
401
+ STDIN.reopen("/dev/null")
402
+ end
403
+
404
+ def wait(pid, streams, client)
405
+ @mutex.synchronize { @waiting[pid] = true }
406
+
407
+ # Wait in a separate thread so we can run multiple commands at once
408
+ Spring.failsafe_thread {
409
+ begin
410
+ _, status = Process.wait2 pid
411
+ log "#{pid} exited with #{status.exitstatus || status.inspect}"
412
+
413
+ streams.each(&:close)
414
+ client.puts(status.exitstatus || status.to_i)
415
+ client.close
416
+ ensure
417
+ @mutex.synchronize { @waiting.delete pid }
418
+ exit_if_finished
419
+ end
420
+ }
421
+
422
+ Spring.failsafe_thread {
423
+ while signal = client.gets.chomp
424
+ begin
425
+ Process.kill(signal, -Process.getpgid(pid))
426
+ client.puts(0)
427
+ rescue Errno::ESRCH
428
+ client.puts(1)
429
+ end
430
+ end
431
+ }
432
+ end
433
+
434
+ private
435
+
436
+ # Tolerate Errno::EPIPE on writes to the client socket. Once the client
437
+ # disconnects, every subsequent write to it raises — that's expected
438
+ # during `serve`'s status reporting; we'd be talking to a process
439
+ # that's gone.
440
+ def ignore_client_disconnect
441
+ yield
442
+ rescue Errno::EPIPE
443
+ end
444
+
445
+ def active_record_configured?
446
+ defined?(ActiveRecord::Base) && ActiveRecord::Base.configurations.any?
447
+ end
448
+ end
449
+ end
@@ -0,0 +1,145 @@
1
+ module Spring
2
+ class ApplicationManager
3
+ attr_reader :pid, :child, :app_env, :spawn_env, :spring_env, :status
4
+
5
+ def initialize(app_env, spawn_env, spring_env)
6
+ @app_env = app_env
7
+ @spawn_env = spawn_env
8
+ @spring_env = spring_env
9
+ @mutex = Mutex.new
10
+ @state = :running
11
+ @pid = nil
12
+ end
13
+
14
+ def log(message)
15
+ spring_env.log "[application_manager:#{app_env}] #{message}"
16
+ end
17
+
18
+ # We're not using @mutex.synchronize to avoid the weird "<internal:prelude>:10"
19
+ # line which messes with backtraces in e.g. rspec
20
+ def synchronize
21
+ @mutex.lock
22
+ yield
23
+ ensure
24
+ @mutex.unlock
25
+ end
26
+
27
+ def start
28
+ start_child
29
+ end
30
+
31
+ def restart
32
+ return if @state == :stopping
33
+ start_child(true)
34
+ end
35
+
36
+ def alive?
37
+ @pid
38
+ end
39
+
40
+ def with_child
41
+ synchronize do
42
+ if alive?
43
+ begin
44
+ yield
45
+ rescue Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL
46
+ # The child has died but has not been collected by the wait thread yet,
47
+ # so start a new child and try again.
48
+ log "child dead; starting"
49
+ start
50
+ yield
51
+ end
52
+ else
53
+ log "child not running; starting"
54
+ start
55
+ yield
56
+ end
57
+ end
58
+ end
59
+
60
+ # Returns the pid of the process running the command, or nil if the application process died.
61
+ def run(client)
62
+ with_child do
63
+ child.send_io client
64
+ child.gets or raise Errno::EPIPE
65
+ end
66
+
67
+ pid = child.gets.to_i
68
+
69
+ unless pid.zero?
70
+ log "got worker pid #{pid}"
71
+ pid
72
+ end
73
+ rescue Errno::ECONNRESET, Errno::EPIPE => e
74
+ log "#{e} while reading from child; returning no pid"
75
+ nil
76
+ ensure
77
+ client.close
78
+ end
79
+
80
+ def stop
81
+ log "stopping"
82
+ @state = :stopping
83
+
84
+ if pid
85
+ Process.kill('TERM', pid)
86
+ Process.wait(pid)
87
+ end
88
+ rescue Errno::ESRCH, Errno::ECHILD
89
+ # Don't care
90
+ end
91
+
92
+ private
93
+
94
+ def start_child(preload = false)
95
+ @child, child_socket = UNIXSocket.pair
96
+
97
+ Bundler.with_original_env do
98
+ bundler_dir = File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first)
99
+ @pid = Process.spawn(
100
+ {
101
+ "RAILS_ENV" => app_env,
102
+ "RACK_ENV" => app_env,
103
+ "SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
104
+ "SPRING_PRELOAD" => preload ? "1" : "0",
105
+ "SPRING_SPAWN_ENV" => JSON.dump(spawn_env.compact),
106
+ **spawn_env,
107
+ },
108
+ "ruby",
109
+ *(bundler_dir != RbConfig::CONFIG["rubylibdir"] ? ["-I", bundler_dir] : []),
110
+ "-I", File.expand_path("../..", __FILE__),
111
+ "-e", "require 'spring/application/boot'",
112
+ 3 => child_socket,
113
+ 4 => spring_env.log_file,
114
+ )
115
+ end
116
+
117
+ start_wait_thread(pid, child) if child.gets
118
+ child_socket.close
119
+ end
120
+
121
+ def start_wait_thread(pid, child)
122
+ Process.detach(pid)
123
+
124
+ Spring.failsafe_thread {
125
+ # The recv can raise an ECONNRESET, killing the thread, but that's ok
126
+ # as if it does we're no longer interested in the child
127
+ loop do
128
+ IO.select([child])
129
+ peek = child.recv(1, Socket::MSG_PEEK)
130
+ break if peek.nil? || peek.empty?
131
+ sleep 0.01
132
+ end
133
+
134
+ log "child #{pid} shutdown"
135
+
136
+ synchronize {
137
+ if @pid == pid
138
+ @pid = nil
139
+ restart
140
+ end
141
+ }
142
+ }
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,13 @@
1
+ command = File.basename($0)
2
+ bin_path = File.expand_path("../../../bin/spring", __FILE__)
3
+
4
+ if command == "spring"
5
+ load bin_path
6
+ else
7
+ disable = ENV["DISABLE_SPRING"]
8
+
9
+ if Process.respond_to?(:fork) && (disable.nil? || disable.empty? || disable == "0")
10
+ ARGV.unshift(command)
11
+ load bin_path
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require "socket"
2
+ require "thread"
3
+
4
+ require "spring/configuration"
5
+ require "spring/env"
6
+ require "spring/process_title_updater"
7
+ require "spring/json"
8
+ require "spring/watcher"
9
+ require "spring/failsafe_thread"
10
+