spring 1.6.2 → 1.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -5
- data/lib/spring/application/boot.rb +2 -1
- data/lib/spring/application.rb +22 -5
- data/lib/spring/application_manager.rb +6 -4
- data/lib/spring/boot.rb +2 -0
- data/lib/spring/client/binstub.rb +1 -1
- data/lib/spring/client/run.rb +61 -38
- data/lib/spring/client/server.rb +18 -0
- data/lib/spring/client.rb +2 -0
- data/lib/spring/env.rb +15 -8
- data/lib/spring/failsafe_thread.rb +14 -0
- data/lib/spring/process_title_updater.rb +1 -1
- data/lib/spring/server.rb +28 -8
- data/lib/spring/test/acceptance_test.rb +46 -7
- data/lib/spring/test/application.rb +8 -3
- data/lib/spring/test/application_generator.rb +5 -0
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/polling.rb +0 -2
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85d4ab3b84aa6f12fb90e262bcd8adf394cb94ef
|
4
|
+
data.tar.gz: 69c83b9fe81004f4c25b9a429bd1222316e1d25e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 491bc17d5c922c974ebe995469c5ebaa099c95351067eab312dad022426feecc006e6dd2211955e390eabb2371392a99e8cdfa39f07dc5b6938f5de73a30d1a7
|
7
|
+
data.tar.gz: 61f6d055f6b22aa94858dbd82227c0c4edbbdd6631a933a8bd5b2ebb0499c3d9c8cff27f1d41c6dca115a084cb0ddbffedef133345385069c87df69673e44ed5
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ boot it every time you run a test, rake task or migration.
|
|
16
16
|
|
17
17
|
## Compatibility
|
18
18
|
|
19
|
-
* Ruby versions: MRI 1.9.3, MRI 2.0, MRI 2.1
|
19
|
+
* Ruby versions: MRI 1.9.3, MRI 2.0, MRI 2.1, MRI 2.2
|
20
20
|
* Rails versions: 4.0+ (in Rails 4.1 and up Spring is included by default)
|
21
21
|
|
22
22
|
Spring makes extensive use of `Process.fork`, so won't be able to
|
@@ -290,6 +290,13 @@ false
|
|
290
290
|
So to avoid this problem, don't save off references to application
|
291
291
|
constants in your initialization code.
|
292
292
|
|
293
|
+
## Using Spring with a containerized development environment
|
294
|
+
|
295
|
+
As of Spring 1.7, there is some support for doing this. See [this
|
296
|
+
example
|
297
|
+
repository](https://github.com/jonleighton/spring-docker-example) for
|
298
|
+
information about how to do it with [Docker](https://www.docker.com/).
|
299
|
+
|
293
300
|
## Configuration
|
294
301
|
|
295
302
|
Spring will read `~/.spring.rb` and `config/spring.rb` for custom
|
@@ -365,13 +372,42 @@ a command runs:
|
|
365
372
|
Spring.quiet = true
|
366
373
|
```
|
367
374
|
|
375
|
+
### Environment variables
|
376
|
+
|
377
|
+
The following environment variables are used by Spring:
|
378
|
+
|
379
|
+
* `DISABLE_SPRING` - If set, Spring will be bypassed and your
|
380
|
+
application will boot in a foreground process
|
381
|
+
* `SPRING_LOG` - The path to a file which Spring will write log messages
|
382
|
+
to.
|
383
|
+
* `SPRING_TMP_PATH` - The directory where Spring should write its temporary
|
384
|
+
files (a pidfile and a socket). By default we use the
|
385
|
+
`XDG_RUNTIME_DIR` environment variable, or else `Dir.tmpdir`, and then
|
386
|
+
create a directory in that named `spring-$UID`. We don't use your
|
387
|
+
Rails application's `tmp/` directory because that may be on a
|
388
|
+
filesystem which doesn't support UNIX sockets.
|
389
|
+
* `SPRING_APPLICATION_ID` - Used to identify distinct Rails
|
390
|
+
applications. By default it is an MD5 hash of the current
|
391
|
+
`RUBY_VERSION`, and the path to your Rails project root.
|
392
|
+
* `SPRING_SOCKET` - The path which should be used for the UNIX socket
|
393
|
+
which Spring uses to communicate with the long-running Spring server
|
394
|
+
process. By default this is `SPRING_TMP_PATH/SPRING_APPLICATION_ID`.
|
395
|
+
* `SPRING_PIDFILE` - The path which should be used to store the pid of
|
396
|
+
the long-running Spring server process. By default this is related to
|
397
|
+
the socket path; if the socket path is `/foo/bar/spring.sock` the
|
398
|
+
pidfile will be `/foo/bar/spring.pid`.
|
399
|
+
* `SPRING_SERVER_COMMAND` - The command to run to start up the spring
|
400
|
+
server when it is not already running. Defaults to `spring _[version]_
|
401
|
+
server --background`.
|
402
|
+
|
368
403
|
## Troubleshooting
|
369
404
|
|
370
405
|
If you want to get more information about what spring is doing, you can
|
371
|
-
|
406
|
+
run spring explicitly in a separate terminal:
|
372
407
|
|
373
408
|
```
|
374
|
-
|
375
|
-
export SPRING_LOG=/tmp/spring.log
|
376
|
-
spring rake -T
|
409
|
+
$ spring server
|
377
410
|
```
|
411
|
+
|
412
|
+
Logging output will be printed to stdout. You can also send log output
|
413
|
+
to a file with the `SPRING_LOG` environment variable.
|
@@ -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 }
|
data/lib/spring/application.rb
CHANGED
@@ -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 =
|
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
|
-
|
153
|
-
|
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
|
|
@@ -300,7 +306,7 @@ module Spring
|
|
300
306
|
@mutex.synchronize { @waiting << pid }
|
301
307
|
|
302
308
|
# Wait in a separate thread so we can run multiple commands at once
|
303
|
-
|
309
|
+
Spring.failsafe_thread {
|
304
310
|
begin
|
305
311
|
_, status = Process.wait2 pid
|
306
312
|
log "#{pid} exited with #{status.exitstatus}"
|
@@ -313,6 +319,17 @@ module Spring
|
|
313
319
|
exit_if_finished
|
314
320
|
end
|
315
321
|
}
|
322
|
+
|
323
|
+
Spring.failsafe_thread {
|
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
|
@@ -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 =
|
7
|
+
@spring_env = spring_env
|
8
8
|
@mutex = Mutex.new
|
9
9
|
@state = :running
|
10
10
|
end
|
@@ -101,9 +101,11 @@ module Spring
|
|
101
101
|
"SPRING_PRELOAD" => preload ? "1" : "0"
|
102
102
|
},
|
103
103
|
"ruby",
|
104
|
+
"-I", File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first),
|
104
105
|
"-I", File.expand_path("../..", __FILE__),
|
105
106
|
"-e", "require 'spring/application/boot'",
|
106
|
-
3 => child_socket
|
107
|
+
3 => child_socket,
|
108
|
+
4 => spring_env.log_file,
|
107
109
|
)
|
108
110
|
end
|
109
111
|
|
@@ -114,7 +116,7 @@ module Spring
|
|
114
116
|
def start_wait_thread(pid, child)
|
115
117
|
Process.detach(pid)
|
116
118
|
|
117
|
-
|
119
|
+
Spring.failsafe_thread {
|
118
120
|
# The recv can raise an ECONNRESET, killing the thread, but that's ok
|
119
121
|
# as if it does we're no longer interested in the child
|
120
122
|
loop do
|
data/lib/spring/boot.rb
CHANGED
@@ -38,7 +38,7 @@ unless defined?(Spring)
|
|
38
38
|
require 'bundler'
|
39
39
|
|
40
40
|
if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
|
41
|
-
Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq }
|
41
|
+
Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
|
42
42
|
gem 'spring', match[1]
|
43
43
|
require 'spring/binstub'
|
44
44
|
end
|
data/lib/spring/client/run.rb
CHANGED
@@ -6,31 +6,36 @@ module Spring
|
|
6
6
|
module Client
|
7
7
|
class Run < Command
|
8
8
|
FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
|
9
|
-
|
9
|
+
CONNECT_TIMEOUT = 1
|
10
|
+
BOOT_TIMEOUT = 20
|
11
|
+
|
12
|
+
attr_reader :server
|
10
13
|
|
11
14
|
def initialize(args)
|
12
15
|
super
|
13
|
-
|
16
|
+
|
17
|
+
@signal_queue = []
|
18
|
+
@server_booted = false
|
14
19
|
end
|
15
20
|
|
16
21
|
def log(message)
|
17
22
|
env.log "[client] #{message}"
|
18
23
|
end
|
19
24
|
|
20
|
-
def
|
21
|
-
@server
|
25
|
+
def connect
|
26
|
+
@server = UNIXSocket.open(env.socket_name)
|
22
27
|
end
|
23
28
|
|
24
29
|
def call
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
begin
|
31
|
+
connect
|
32
|
+
rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
|
28
33
|
cold_run
|
34
|
+
else
|
35
|
+
warm_run
|
29
36
|
end
|
30
|
-
rescue Errno::ECONNRESET
|
31
|
-
exit 1
|
32
37
|
ensure
|
33
|
-
server.close if
|
38
|
+
server.close if server
|
34
39
|
end
|
35
40
|
|
36
41
|
def warm_run
|
@@ -49,6 +54,7 @@ module Spring
|
|
49
54
|
|
50
55
|
def cold_run
|
51
56
|
boot_server
|
57
|
+
connect
|
52
58
|
run
|
53
59
|
end
|
54
60
|
|
@@ -60,24 +66,37 @@ module Spring
|
|
60
66
|
queue_signals
|
61
67
|
connect_to_application(client)
|
62
68
|
run_command(client, application)
|
69
|
+
rescue Errno::ECONNRESET
|
70
|
+
exit 1
|
63
71
|
end
|
64
72
|
|
65
73
|
def boot_server
|
66
74
|
env.socket_path.unlink if env.socket_path.exist?
|
67
75
|
|
68
|
-
pid
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
)
|
76
|
+
pid = Process.spawn(gem_env, env.server_command, out: File::NULL)
|
77
|
+
timeout = Time.now + BOOT_TIMEOUT
|
78
|
+
|
79
|
+
@server_booted = true
|
73
80
|
|
74
81
|
until env.socket_path.exist?
|
75
82
|
_, status = Process.waitpid2(pid, Process::WNOHANG)
|
76
|
-
|
83
|
+
|
84
|
+
if status
|
85
|
+
exit status.exitstatus
|
86
|
+
elsif Time.now > timeout
|
87
|
+
$stderr.puts "Starting Spring server with `#{env.server_command}` " \
|
88
|
+
"timed out after #{BOOT_TIMEOUT} seconds"
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
77
92
|
sleep 0.1
|
78
93
|
end
|
79
94
|
end
|
80
95
|
|
96
|
+
def server_booted?
|
97
|
+
@server_booted
|
98
|
+
end
|
99
|
+
|
81
100
|
def gem_env
|
82
101
|
bundle = Bundler.bundle_path.to_s
|
83
102
|
paths = Gem.path + ENV["GEM_PATH"].to_s.split(File::PATH_SEPARATOR)
|
@@ -97,13 +116,17 @@ module Spring
|
|
97
116
|
def verify_server_version
|
98
117
|
server_version = server.gets.chomp
|
99
118
|
if server_version != env.version
|
100
|
-
$stderr.puts
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
119
|
+
$stderr.puts "There is a version mismatch between the spring client " \
|
120
|
+
"(#{env.version}) and the server (#{server_version})."
|
121
|
+
|
122
|
+
if server_booted?
|
123
|
+
$stderr.puts "We already tried to reboot the server, but the mismatch is still present."
|
124
|
+
exit 1
|
125
|
+
else
|
126
|
+
$stderr.puts "Restarting to resolve."
|
127
|
+
stop_server
|
128
|
+
cold_run
|
129
|
+
end
|
107
130
|
end
|
108
131
|
end
|
109
132
|
|
@@ -111,7 +134,7 @@ ERROR
|
|
111
134
|
server.send_io client
|
112
135
|
send_json server, "args" => args, "default_rails_env" => default_rails_env
|
113
136
|
|
114
|
-
if IO.select([server], [], [],
|
137
|
+
if IO.select([server], [], [], CONNECT_TIMEOUT)
|
115
138
|
server.gets or raise CommandNotFound
|
116
139
|
else
|
117
140
|
raise "Error connecting to Spring server"
|
@@ -138,7 +161,7 @@ ERROR
|
|
138
161
|
if pid && !pid.empty?
|
139
162
|
log "got pid: #{pid}"
|
140
163
|
|
141
|
-
forward_signals(
|
164
|
+
forward_signals(application)
|
142
165
|
status = application.read.to_i
|
143
166
|
|
144
167
|
log "got exit status #{status}"
|
@@ -158,26 +181,26 @@ ERROR
|
|
158
181
|
end
|
159
182
|
end
|
160
183
|
|
161
|
-
def forward_signals(
|
162
|
-
@signal_queue.each { |sig| kill sig,
|
184
|
+
def forward_signals(application)
|
185
|
+
@signal_queue.each { |sig| kill sig, application }
|
163
186
|
|
164
187
|
FORWARDED_SIGNALS.each do |sig|
|
165
|
-
trap(sig) { forward_signal sig,
|
188
|
+
trap(sig) { forward_signal sig, application }
|
166
189
|
end
|
167
|
-
rescue Errno::ESRCH
|
168
190
|
end
|
169
191
|
|
170
|
-
def forward_signal(sig,
|
171
|
-
kill(sig,
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
192
|
+
def forward_signal(sig, application)
|
193
|
+
if kill(sig, application) != 0
|
194
|
+
# If the application process is gone, then don't block the
|
195
|
+
# signal on this process.
|
196
|
+
trap(sig, 'DEFAULT')
|
197
|
+
Process.kill(sig, Process.pid)
|
198
|
+
end
|
177
199
|
end
|
178
200
|
|
179
|
-
def kill(sig,
|
180
|
-
|
201
|
+
def kill(sig, application)
|
202
|
+
application.puts(sig)
|
203
|
+
application.gets.to_i
|
181
204
|
end
|
182
205
|
|
183
206
|
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
|
data/lib/spring/client.rb
CHANGED
@@ -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)
|
data/lib/spring/env.rb
CHANGED
@@ -14,10 +14,10 @@ module Spring
|
|
14
14
|
class Env
|
15
15
|
attr_reader :log_file
|
16
16
|
|
17
|
-
def initialize(
|
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(
|
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
|
-
|
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"] || "#{File.expand_path("../../../bin/spring", __FILE__)} server --background"
|
114
|
+
end
|
108
115
|
end
|
109
116
|
end
|
data/lib/spring/server.rb
CHANGED
@@ -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(
|
20
|
-
@
|
21
|
-
@
|
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)
|
@@ -100,7 +106,7 @@ module Spring
|
|
100
106
|
end
|
101
107
|
end
|
102
108
|
|
103
|
-
@applications.values.map { |a|
|
109
|
+
@applications.values.map { |a| Spring.failsafe_thread { a.stop } }.map(&:join)
|
104
110
|
end
|
105
111
|
|
106
112
|
def write_pidfile
|
@@ -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
|
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
|
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
|
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 !
|
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
|
@@ -400,7 +404,7 @@ module Spring
|
|
400
404
|
end
|
401
405
|
|
402
406
|
test "can define client tasks" do
|
403
|
-
File.write("#{app.
|
407
|
+
File.write("#{app.spring_client_config}", <<-RUBY)
|
404
408
|
Spring::Client::COMMANDS["foo"] = lambda { |args| puts "bar -- \#{args.inspect}" }
|
405
409
|
RUBY
|
406
410
|
assert_success "bin/spring foo --baz", stdout: "bar -- [\"foo\", \"--baz\"]\n"
|
@@ -461,11 +465,11 @@ module Spring
|
|
461
465
|
test "changing the Gemfile works when spring calls into itself" do
|
462
466
|
File.write(app.path("script.rb"), <<-RUBY.strip_heredoc)
|
463
467
|
gemfile = Rails.root.join("Gemfile")
|
464
|
-
File.write(gemfile, "\#{gemfile.read}gem '
|
468
|
+
File.write(gemfile, "\#{gemfile.read}gem 'text'\\n")
|
465
469
|
Bundler.with_clean_env do
|
466
470
|
system(#{app.env.inspect}, "bundle install")
|
467
471
|
end
|
468
|
-
output = `\#{Rails.root.join('bin/rails')} runner 'require "
|
472
|
+
output = `\#{Rails.root.join('bin/rails')} runner 'require "text"; puts "done";'`
|
469
473
|
exit output.include? "done\n"
|
470
474
|
RUBY
|
471
475
|
|
@@ -495,6 +499,41 @@ module Spring
|
|
495
499
|
expr = "p Kernel.private_instance_methods.include?(:raise)"
|
496
500
|
assert_success %(bin/rails runner '#{expr}'), stdout: "true"
|
497
501
|
end
|
502
|
+
|
503
|
+
test "custom bundle path" do
|
504
|
+
bundle_path = app.path(".bundle/#{Bundler.ruby_scope}")
|
505
|
+
bundle_path.dirname.mkpath
|
506
|
+
|
507
|
+
FileUtils.cp_r "#{app.gem_home}/", bundle_path.to_s
|
508
|
+
|
509
|
+
app.run! "bundle install --path .bundle --clean --local"
|
510
|
+
|
511
|
+
assert_speedup do
|
512
|
+
2.times { assert_success "bundle exec rails runner ''" }
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
test "booting a foreground server" do
|
517
|
+
FileUtils.cd(app.root) do
|
518
|
+
assert !spring_env.server_running?
|
519
|
+
assert_success "bin/spring server &"
|
520
|
+
|
521
|
+
Timeout.timeout(10) do
|
522
|
+
sleep 0.1 until spring_env.server_running? && spring_env.socket_path.exist?
|
523
|
+
end
|
524
|
+
|
525
|
+
assert_success app.spring_test_command
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
test "server boot timeout" do
|
530
|
+
app.env["SPRING_SERVER_COMMAND"] = "sleep 1"
|
531
|
+
File.write("#{app.spring_client_config}", %(
|
532
|
+
Spring::Client::Run.const_set(:BOOT_TIMEOUT, 0.1)
|
533
|
+
))
|
534
|
+
|
535
|
+
assert_failure "bin/rails runner ''", stderr: "timed out"
|
536
|
+
end
|
498
537
|
end
|
499
538
|
end
|
500
539
|
end
|
@@ -9,7 +9,8 @@ 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
|
+
@times = nil
|
13
14
|
end
|
14
15
|
|
15
16
|
def exists?
|
@@ -88,6 +89,10 @@ module Spring
|
|
88
89
|
path "config/spring.rb"
|
89
90
|
end
|
90
91
|
|
92
|
+
def spring_client_config
|
93
|
+
path "config/spring_client.rb"
|
94
|
+
end
|
95
|
+
|
91
96
|
def run(command, opts = {})
|
92
97
|
start_time = Time.now
|
93
98
|
|
@@ -116,8 +121,8 @@ module Spring
|
|
116
121
|
@times << (Time.now - start_time) if @times
|
117
122
|
|
118
123
|
output.merge(status: status, command: command)
|
119
|
-
rescue Timeout::Error
|
120
|
-
raise
|
124
|
+
rescue Timeout::Error
|
125
|
+
raise Timeout::Error, "While running command:\n\n#{dump_streams(command, read_streams)}"
|
121
126
|
end
|
122
127
|
|
123
128
|
def with_timing
|
@@ -8,6 +8,7 @@ module Spring
|
|
8
8
|
@version = RailsVersion.new(version_constraint.split(' ').last)
|
9
9
|
@application = Application.new(root)
|
10
10
|
@bundled = false
|
11
|
+
@installed = false
|
11
12
|
end
|
12
13
|
|
13
14
|
def test_root
|
@@ -58,6 +59,10 @@ module Spring
|
|
58
59
|
append_to_file(application.gemfile, "gem 'spring-commands-testunit'")
|
59
60
|
end
|
60
61
|
|
62
|
+
if RUBY_VERSION == "1.9.3"
|
63
|
+
append_to_file(application.gemfile, "gem 'mime-types', '~> 2'")
|
64
|
+
end
|
65
|
+
|
61
66
|
rewrite_file(application.gemfile) do |c|
|
62
67
|
c.sub!("https://rubygems.org", "http://rubygems.org")
|
63
68
|
c.gsub!(/(gem '(byebug|web-console|sdoc|jbuilder)')/, "# \\1")
|
data/lib/spring/version.rb
CHANGED
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.
|
4
|
+
version: 1.7.2
|
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-01
|
11
|
+
date: 2016-07-01 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
|
@@ -85,6 +86,7 @@ files:
|
|
85
86
|
- lib/spring/configuration.rb
|
86
87
|
- lib/spring/env.rb
|
87
88
|
- lib/spring/errors.rb
|
89
|
+
- lib/spring/failsafe_thread.rb
|
88
90
|
- lib/spring/json.rb
|
89
91
|
- lib/spring/process_title_updater.rb
|
90
92
|
- lib/spring/server.rb
|
@@ -119,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
121
|
version: '0'
|
120
122
|
requirements: []
|
121
123
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.
|
124
|
+
rubygems_version: 2.5.1
|
123
125
|
signing_key:
|
124
126
|
specification_version: 4
|
125
127
|
summary: Rails application preloader
|