spring 1.6.2 → 1.7.2
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.
- 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
|