spring 1.6.4 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7188079e7e8285351c6d8a0604cc9198a2052bfd
4
- data.tar.gz: 2a0d6caceb3960e1ecd88ed86ec7f786b94aa388
3
+ metadata.gz: bf3b9b590f0a6bce1bf2b23dc01cd32884a8d992
4
+ data.tar.gz: 6fd9d0cd5224af34ff7cec5b485140eba506463d
5
5
  SHA512:
6
- metadata.gz: 294d493fe0500ef7e1265fabc0586ecc77be34f95b56504e9c972468eb3378ec8fad63c4793bfcad330274e269aad000463b8d161a3054bede5bbf3514f3a881
7
- data.tar.gz: fe2b858e97d0553e7bcb0b3b1a960587e29a090d584878a7b5cde6622bda0d9bb3920f2610dfb1a767db755ae4f5bf661de212b4405e68e63efb766a8b0ba70e
6
+ metadata.gz: b6c32c4667c1cb86d499332953b78b6933bb5f34d527701e64f52ba00b5155ee89e1d78e552b9c3fec81d827fd5923dfc37ba00910df87f7d9ebca7a698a96e5
7
+ data.tar.gz: 8aa47cb7a911eac35f9eb25db622bddcdc3df93ea1d0f947f160e2e564daff71a2485fa6f3346c66b1c7c813e28ffc2c206c00f248a7ae55cb6a0689e25a1bff
data/README.md CHANGED
@@ -365,13 +365,42 @@ a command runs:
365
365
  Spring.quiet = true
366
366
  ```
367
367
 
368
+ ### Environment variables
369
+
370
+ The following environment variables are used by Spring:
371
+
372
+ * `DISABLE_SPRING` - If set, Spring will be bypassed and your
373
+ application will boot in a foreground process
374
+ * `SPRING_LOG` - The path to a file which Spring will write log messages
375
+ to.
376
+ * `SPRING_TMP_PATH` - The directory where Spring should write its temporary
377
+ files (a pidfile and a socket). By default we use the
378
+ `XDG_RUNTIME_DIR` environment variable, or else `Dir.tmpdir`, and then
379
+ create a directory in that named `spring-$UID`. We don't use your
380
+ Rails application's `tmp/` directory because that may be on a
381
+ filesystem which doesn't support UNIX sockets.
382
+ * `SPRING_APPLICATION_ID` - Used to identify distinct Rails
383
+ applications. By default it is an MD5 hash of the current
384
+ `RUBY_VERSION`, and the path to your Rails project root.
385
+ * `SPRING_SOCKET` - The path which should be used for the UNIX socket
386
+ which Spring uses to communicate with the long-running Spring server
387
+ process. By default this is `SPRING_TMP_PATH/SPRING_APPLICATION_ID`.
388
+ * `SPRING_PIDFILE` - The path which should be used to store the pid of
389
+ the long-running Spring server process. By default this is related to
390
+ the socket path; if the socket path is `/foo/bar/spring.sock` the
391
+ pidfile will be `/foo/bar/spring.pid`.
392
+ * `SPRING_SERVER_COMMAND` - The command to run to start up the spring
393
+ server when it is not already running. Defaults to `spring _[version]_
394
+ server --background`.
395
+
368
396
  ## Troubleshooting
369
397
 
370
398
  If you want to get more information about what spring is doing, you can
371
- specify a log file with the `SPRING_LOG` environment variable:
399
+ run spring explicitly in a separate terminal:
372
400
 
373
401
  ```
374
- spring stop # if spring is already running
375
- export SPRING_LOG=/tmp/spring.log
376
- spring rake -T
402
+ $ spring server
377
403
  ```
404
+
405
+ Logging output will be printed to stdout. You can also send log output
406
+ to a file with the `SPRING_LOG` environment variable.
@@ -6,10 +6,10 @@ module Spring
6
6
  class Application
7
7
  attr_reader :manager, :watcher, :spring_env, :original_env
8
8
 
9
- def initialize(manager, original_env)
9
+ def initialize(manager, original_env, spring_env = Env.new)
10
10
  @manager = manager
11
11
  @original_env = original_env
12
- @spring_env = Env.new
12
+ @spring_env = spring_env
13
13
  @mutex = Mutex.new
14
14
  @waiting = Set.new
15
15
  @preloaded = false
@@ -149,11 +149,17 @@ module Spring
149
149
  setup command
150
150
 
151
151
  if Rails.application.reloaders.any?(&:updated?)
152
- ActionDispatch::Reloader.cleanup!
153
- ActionDispatch::Reloader.prepare!
152
+ # Rails 5.1 forward-compat. AD::R is deprecated to AS::R in Rails 5.
153
+ if defined? ActiveSupport::Reloader
154
+ Rails.application.reloader.reload!
155
+ else
156
+ ActionDispatch::Reloader.cleanup!
157
+ ActionDispatch::Reloader.prepare!
158
+ end
154
159
  end
155
160
 
156
161
  pid = fork {
162
+ Process.setsid
157
163
  IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
158
164
  trap("TERM", "DEFAULT")
159
165
 
@@ -313,6 +319,17 @@ module Spring
313
319
  exit_if_finished
314
320
  end
315
321
  }
322
+
323
+ Thread.new {
324
+ while signal = client.gets.chomp
325
+ begin
326
+ Process.kill(signal, -Process.getpgid(pid))
327
+ client.puts(0)
328
+ rescue Errno::ESRCH
329
+ client.puts(1)
330
+ end
331
+ end
332
+ }
316
333
  end
317
334
 
318
335
  private
@@ -5,7 +5,8 @@ require "spring/application"
5
5
 
6
6
  app = Spring::Application.new(
7
7
  UNIXSocket.for_fd(3),
8
- Spring::JSON.load(ENV.delete("SPRING_ORIGINAL_ENV").dup)
8
+ Spring::JSON.load(ENV.delete("SPRING_ORIGINAL_ENV").dup),
9
+ Spring::Env.new(log_file: IO.for_fd(4))
9
10
  )
10
11
 
11
12
  Signal.trap("TERM") { app.terminate }
@@ -2,9 +2,9 @@ module Spring
2
2
  class ApplicationManager
3
3
  attr_reader :pid, :child, :app_env, :spring_env, :status
4
4
 
5
- def initialize(app_env)
5
+ def initialize(app_env, spring_env)
6
6
  @app_env = app_env
7
- @spring_env = Env.new
7
+ @spring_env = spring_env
8
8
  @mutex = Mutex.new
9
9
  @state = :running
10
10
  end
@@ -104,7 +104,8 @@ module Spring
104
104
  "-I", File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first),
105
105
  "-I", File.expand_path("../..", __FILE__),
106
106
  "-e", "require 'spring/application/boot'",
107
- 3 => child_socket
107
+ 3 => child_socket,
108
+ 4 => spring_env.log_file,
108
109
  )
109
110
  end
110
111
 
@@ -9,6 +9,7 @@ require "spring/client/stop"
9
9
  require "spring/client/status"
10
10
  require "spring/client/rails"
11
11
  require "spring/client/version"
12
+ require "spring/client/server"
12
13
 
13
14
  module Spring
14
15
  module Client
@@ -22,6 +23,7 @@ module Spring
22
23
  "rails" => Client::Rails,
23
24
  "-v" => Client::Version,
24
25
  "--version" => Client::Version,
26
+ "server" => Client::Server,
25
27
  }
26
28
 
27
29
  def self.run(args)
@@ -8,6 +8,8 @@ module Spring
8
8
  FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
9
9
  TIMEOUT = 1
10
10
 
11
+ attr_reader :server
12
+
11
13
  def initialize(args)
12
14
  super
13
15
  @signal_queue = []
@@ -17,20 +19,20 @@ module Spring
17
19
  env.log "[client] #{message}"
18
20
  end
19
21
 
20
- def server
21
- @server ||= UNIXSocket.open(env.socket_name)
22
+ def connect
23
+ @server = UNIXSocket.open(env.socket_name)
22
24
  end
23
25
 
24
26
  def call
25
- if env.server_running?
26
- warm_run
27
- else
27
+ begin
28
+ connect
29
+ rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
28
30
  cold_run
31
+ else
32
+ warm_run
29
33
  end
30
- rescue Errno::ECONNRESET
31
- exit 1
32
34
  ensure
33
- server.close if @server
35
+ server.close if server
34
36
  end
35
37
 
36
38
  def warm_run
@@ -49,6 +51,7 @@ module Spring
49
51
 
50
52
  def cold_run
51
53
  boot_server
54
+ connect
52
55
  run
53
56
  end
54
57
 
@@ -60,16 +63,13 @@ module Spring
60
63
  queue_signals
61
64
  connect_to_application(client)
62
65
  run_command(client, application)
66
+ rescue Errno::ECONNRESET
67
+ exit 1
63
68
  end
64
69
 
65
70
  def boot_server
66
71
  env.socket_path.unlink if env.socket_path.exist?
67
-
68
- pid = Process.spawn(
69
- gem_env,
70
- "ruby",
71
- "-e", "gem 'spring', '#{Spring::VERSION}'; require 'spring/server'; Spring::Server.boot"
72
- )
72
+ pid = Process.spawn(gem_env, env.server_command, out: File::NULL)
73
73
 
74
74
  until env.socket_path.exist?
75
75
  _, status = Process.waitpid2(pid, Process::WNOHANG)
@@ -98,12 +98,12 @@ module Spring
98
98
  server_version = server.gets.chomp
99
99
  if server_version != env.version
100
100
  $stderr.puts <<-ERROR
101
- There is a version mismatch between the spring client and the server.
102
- You should restart the server and make sure to use the same version.
103
-
104
- CLIENT: #{env.version}, SERVER: #{server_version}
101
+ There is a version mismatch between the spring client (#{env.version}) and the server (#{server_version}).
102
+ Restarting to resolve.
105
103
  ERROR
106
- exit 1
104
+
105
+ stop_server
106
+ cold_run
107
107
  end
108
108
  end
109
109
 
@@ -138,7 +138,7 @@ ERROR
138
138
  if pid && !pid.empty?
139
139
  log "got pid: #{pid}"
140
140
 
141
- forward_signals(pid.to_i)
141
+ forward_signals(application)
142
142
  status = application.read.to_i
143
143
 
144
144
  log "got exit status #{status}"
@@ -158,26 +158,26 @@ ERROR
158
158
  end
159
159
  end
160
160
 
161
- def forward_signals(pid)
162
- @signal_queue.each { |sig| kill sig, pid }
161
+ def forward_signals(application)
162
+ @signal_queue.each { |sig| kill sig, application }
163
163
 
164
164
  FORWARDED_SIGNALS.each do |sig|
165
- trap(sig) { forward_signal sig, pid }
165
+ trap(sig) { forward_signal sig, application }
166
166
  end
167
- rescue Errno::ESRCH
168
167
  end
169
168
 
170
- def forward_signal(sig, pid)
171
- kill(sig, pid)
172
- rescue Errno::ESRCH
173
- # If the application process is gone, then don't block the
174
- # signal on this process.
175
- trap(sig, 'DEFAULT')
176
- Process.kill(sig, Process.pid)
169
+ def forward_signal(sig, application)
170
+ if kill(sig, application) != 0
171
+ # If the application process is gone, then don't block the
172
+ # signal on this process.
173
+ trap(sig, 'DEFAULT')
174
+ Process.kill(sig, Process.pid)
175
+ end
177
176
  end
178
177
 
179
- def kill(sig, pid)
180
- Process.kill(sig, -Process.getpgid(pid))
178
+ def kill(sig, application)
179
+ application.puts(sig)
180
+ application.gets.to_i
181
181
  end
182
182
 
183
183
  def send_json(socket, data)
@@ -0,0 +1,18 @@
1
+ module Spring
2
+ module Client
3
+ class Server < Command
4
+ def self.description
5
+ "Explicitly start a Spring server in the foreground"
6
+ end
7
+
8
+ def call
9
+ require "spring/server"
10
+ Spring::Server.boot(foreground: foreground?)
11
+ end
12
+
13
+ def foreground?
14
+ !args.include?("--background")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -14,10 +14,10 @@ module Spring
14
14
  class Env
15
15
  attr_reader :log_file
16
16
 
17
- def initialize(root = nil)
18
- @root = root
19
- @project_root = root
20
- @log_file = File.open(ENV["SPRING_LOG"] || File::NULL, "a")
17
+ def initialize(options = {})
18
+ @root = options[:root]
19
+ @project_root = options[:root]
20
+ @log_file = options[:log_file] || File.open(ENV["SPRING_LOG"] || File::NULL, "a")
21
21
  end
22
22
 
23
23
  def root
@@ -33,17 +33,20 @@ module Spring
33
33
  end
34
34
 
35
35
  def tmp_path
36
- path = Pathname.new(File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}"))
36
+ path = Pathname.new(
37
+ ENV["SPRING_TMP_PATH"] ||
38
+ File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}")
39
+ )
37
40
  FileUtils.mkdir_p(path) unless path.exist?
38
41
  path
39
42
  end
40
43
 
41
44
  def application_id
42
- Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
45
+ ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
43
46
  end
44
47
 
45
48
  def socket_path
46
- tmp_path.join(application_id)
49
+ Pathname.new(ENV["SPRING_SOCKET"] || tmp_path.join(application_id))
47
50
  end
48
51
 
49
52
  def socket_name
@@ -51,7 +54,7 @@ module Spring
51
54
  end
52
55
 
53
56
  def pidfile_path
54
- tmp_path.join("#{application_id}.pid")
57
+ Pathname.new(ENV["SPRING_PIDFILE"] || socket_path.dirname.join("#{socket_path.basename(".*")}.pid"))
55
58
  end
56
59
 
57
60
  def pid
@@ -105,5 +108,9 @@ module Spring
105
108
  rescue Errno::ESRCH
106
109
  # already dead
107
110
  end
111
+
112
+ def server_command
113
+ ENV["SPRING_SERVER_COMMAND"] || "spring _#{Spring::VERSION}_ server --background"
114
+ end
108
115
  end
109
116
  end
@@ -10,19 +10,24 @@ require "spring/commands"
10
10
 
11
11
  module Spring
12
12
  class Server
13
- def self.boot
14
- new.boot
13
+ def self.boot(options = {})
14
+ new(options).boot
15
15
  end
16
16
 
17
17
  attr_reader :env
18
18
 
19
- def initialize(env = Env.new)
20
- @env = env
21
- @applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
19
+ def initialize(options = {})
20
+ @foreground = options.fetch(:foreground, false)
21
+ @env = options[:env] || default_env
22
+ @applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k, env) }
22
23
  @pidfile = env.pidfile_path.open('a')
23
24
  @mutex = Mutex.new
24
25
  end
25
26
 
27
+ def foreground?
28
+ @foreground
29
+ end
30
+
26
31
  def log(message)
27
32
  env.log "[server] #{message}"
28
33
  end
@@ -31,8 +36,8 @@ module Spring
31
36
  Spring.verify_environment
32
37
 
33
38
  write_pidfile
34
- set_pgid
35
- ignore_signals
39
+ set_pgid unless foreground?
40
+ ignore_signals unless foreground?
36
41
  set_exit_hook
37
42
  set_process_title
38
43
  start_server
@@ -42,6 +47,7 @@ module Spring
42
47
  server = UNIXServer.open(env.socket_name)
43
48
  log "started on #{env.socket_name}"
44
49
  loop { serve server.accept }
50
+ rescue Interrupt
45
51
  end
46
52
 
47
53
  def serve(client)
@@ -126,5 +132,19 @@ module Spring
126
132
  "spring server | #{env.app_name} | started #{distance} ago"
127
133
  }
128
134
  end
135
+
136
+ private
137
+
138
+ def default_env
139
+ Env.new(log_file: default_log_file)
140
+ end
141
+
142
+ def default_log_file
143
+ if foreground? && !ENV["SPRING_LOG"]
144
+ $stdout
145
+ else
146
+ nil
147
+ end
148
+ end
129
149
  end
130
150
  end
@@ -28,6 +28,10 @@ module Spring
28
28
  @app ||= Spring::Test::Application.new("#{Spring::Test.root}/apps/tmp")
29
29
  end
30
30
 
31
+ def spring_env
32
+ app.spring_env
33
+ end
34
+
31
35
  def assert_output(artifacts, expected)
32
36
  expected.each do |stream, output|
33
37
  assert artifacts[stream].include?(output),
@@ -92,14 +96,14 @@ module Spring
92
96
 
93
97
  test "help message when called without arguments" do
94
98
  assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
95
- assert app.spring_env.server_running?
99
+ assert spring_env.server_running?
96
100
  end
97
101
 
98
102
  test "shows help" do
99
103
  assert_success "bin/spring help", stdout: 'Usage: spring COMMAND [ARGS]'
100
104
  assert_success "bin/spring -h", stdout: 'Usage: spring COMMAND [ARGS]'
101
105
  assert_success "bin/spring --help", stdout: 'Usage: spring COMMAND [ARGS]'
102
- refute app.spring_env.server_running?
106
+ refute spring_env.server_running?
103
107
  end
104
108
 
105
109
  test "tells the user that spring is being used when used automatically via binstubs" do
@@ -184,10 +188,10 @@ module Spring
184
188
 
185
189
  test "stop command kills server" do
186
190
  app.run app.spring_test_command
187
- assert app.spring_env.server_running?, "The server should be running but it isn't"
191
+ assert spring_env.server_running?, "The server should be running but it isn't"
188
192
 
189
193
  assert_success "bin/spring stop"
190
- assert !app.spring_env.server_running?, "The server should not be running but it is"
194
+ assert !spring_env.server_running?, "The server should not be running but it is"
191
195
  end
192
196
 
193
197
  test "custom commands" do
@@ -508,6 +512,19 @@ module Spring
508
512
  2.times { assert_success "bundle exec rails runner ''" }
509
513
  end
510
514
  end
515
+
516
+ test "booting a foreground server" do
517
+ FileUtils.cd(app.root) do
518
+ assert !spring_env.server_running?
519
+ app.run "spring server &"
520
+
521
+ Timeout.timeout(1) do
522
+ sleep 0.1 until spring_env.server_running?
523
+ end
524
+
525
+ assert_success app.spring_test_command
526
+ end
527
+ end
511
528
  end
512
529
  end
513
530
  end
@@ -9,7 +9,7 @@ module Spring
9
9
 
10
10
  def initialize(root)
11
11
  @root = Pathname.new(root)
12
- @spring_env = Spring::Env.new(root)
12
+ @spring_env = Spring::Env.new(root: root)
13
13
  end
14
14
 
15
15
  def exists?
@@ -117,7 +117,7 @@ module Spring
117
117
 
118
118
  output.merge(status: status, command: command)
119
119
  rescue Timeout::Error => e
120
- raise e, "Output:\n\n#{dump_streams(command, read_streams)}"
120
+ raise Timeout::Error, "While running command:\n\n#{dump_streams(command, read_streams)}"
121
121
  end
122
122
 
123
123
  def with_timing
@@ -58,6 +58,10 @@ module Spring
58
58
  append_to_file(application.gemfile, "gem 'spring-commands-testunit'")
59
59
  end
60
60
 
61
+ if RUBY_VERSION == "1.9.3"
62
+ append_to_file(application.gemfile, "gem 'mime-types', '~> 2'")
63
+ end
64
+
61
65
  rewrite_file(application.gemfile) do |c|
62
66
  c.sub!("https://rubygems.org", "http://rubygems.org")
63
67
  c.gsub!(/(gem '(byebug|web-console|sdoc|jbuilder)')/, "# \\1")
@@ -1,3 +1,3 @@
1
1
  module Spring
2
- VERSION = "1.6.4"
2
+ VERSION = "1.7.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spring
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.4
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Leighton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-26 00:00:00.000000000 Z
11
+ date: 2016-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -75,6 +75,7 @@ files:
75
75
  - lib/spring/client/help.rb
76
76
  - lib/spring/client/rails.rb
77
77
  - lib/spring/client/run.rb
78
+ - lib/spring/client/server.rb
78
79
  - lib/spring/client/status.rb
79
80
  - lib/spring/client/stop.rb
80
81
  - lib/spring/client/version.rb