spring 1.3.6 → 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/LICENSE.txt +2 -2
- data/README.md +59 -6
- data/bin/spring +2 -1
- data/lib/spring/application/boot.rb +2 -1
- data/lib/spring/application.rb +25 -6
- data/lib/spring/application_manager.rb +6 -4
- data/lib/spring/boot.rb +2 -0
- data/lib/spring/client/binstub.rb +23 -11
- data/lib/spring/client/run.rb +62 -39
- data/lib/spring/client/server.rb +18 -0
- data/lib/spring/client.rb +11 -1
- data/lib/spring/configuration.rb +3 -1
- 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 +249 -56
- data/lib/spring/test/application.rb +9 -4
- data/lib/spring/test/application_generator.rb +22 -4
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/polling.rb +0 -2
- data/lib/spring/watcher.rb +1 -1
- metadata +22 -6
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/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2012 Jon Leighton
|
1
|
+
Copyright (c) 2012-2016 Jon Leighton
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
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
|
@@ -48,7 +48,7 @@ code into relevant existing executables. The snippet looks like this:
|
|
48
48
|
|
49
49
|
``` ruby
|
50
50
|
begin
|
51
|
-
load File.expand_path(
|
51
|
+
load File.expand_path('../spring', __FILE__)
|
52
52
|
rescue LoadError
|
53
53
|
end
|
54
54
|
```
|
@@ -73,6 +73,7 @@ Let's run a test:
|
|
73
73
|
|
74
74
|
```
|
75
75
|
$ time bin/rake test test/controllers/posts_controller_test.rb
|
76
|
+
Running via Spring preloader in process 2734
|
76
77
|
Run options:
|
77
78
|
|
78
79
|
# Running tests:
|
@@ -103,6 +104,7 @@ The next run is faster:
|
|
103
104
|
|
104
105
|
```
|
105
106
|
$ time bin/rake test test/controllers/posts_controller_test.rb
|
107
|
+
Running via Spring preloader in process 8352
|
106
108
|
Run options:
|
107
109
|
|
108
110
|
# Running tests:
|
@@ -147,6 +149,7 @@ environment gets booted up:
|
|
147
149
|
|
148
150
|
```
|
149
151
|
$ bin/rake routes
|
152
|
+
Running via Spring preloader in process 2363
|
150
153
|
posts GET /posts(.:format) posts#index
|
151
154
|
POST /posts(.:format) posts#create
|
152
155
|
new_post GET /posts/new(.:format) posts#new
|
@@ -227,6 +230,8 @@ You can add these to your Gemfile for additional commands:
|
|
227
230
|
running `Test::Unit` tests on Rails 3, since only Rails 4 allows you
|
228
231
|
to use `rake test path/to/test` to run a particular test/directory.
|
229
232
|
* [spring-commands-teaspoon](https://github.com/alejandrobabio/spring-commands-teaspoon.git)
|
233
|
+
* [spring-commands-m](https://github.com/gabrieljoelc/spring-commands-m.git)
|
234
|
+
* [spring-commands-rubocop](https://github.com/p0deje/spring-commands-rubocop)
|
230
235
|
|
231
236
|
## Use without adding to bundle
|
232
237
|
|
@@ -285,6 +290,13 @@ false
|
|
285
290
|
So to avoid this problem, don't save off references to application
|
286
291
|
constants in your initialization code.
|
287
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
|
+
|
288
300
|
## Configuration
|
289
301
|
|
290
302
|
Spring will read `~/.spring.rb` and `config/spring.rb` for custom
|
@@ -294,6 +306,9 @@ settings. Note that `~/.spring.rb` is loaded *before* bundler, but
|
|
294
306
|
projects without having to be added to the project's Gemfile, require
|
295
307
|
them in your `~/.spring.rb`.
|
296
308
|
|
309
|
+
`config/spring_client.rb` is also loaded before bundler and before a
|
310
|
+
server process is started, it can be used to add new top-level commands.
|
311
|
+
|
297
312
|
### Application root
|
298
313
|
|
299
314
|
Spring must know how to find your Rails application. If you have a
|
@@ -348,13 +363,51 @@ installing the
|
|
348
363
|
[spring-watcher-listen](https://github.com/jonleighton/spring-watcher-listen)
|
349
364
|
gem.
|
350
365
|
|
366
|
+
### Quiet output
|
367
|
+
|
368
|
+
To disable the "Running via Spring preloader" message which is shown each time
|
369
|
+
a command runs:
|
370
|
+
|
371
|
+
``` ruby
|
372
|
+
Spring.quiet = true
|
373
|
+
```
|
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
|
+
|
351
403
|
## Troubleshooting
|
352
404
|
|
353
405
|
If you want to get more information about what spring is doing, you can
|
354
|
-
|
406
|
+
run spring explicitly in a separate terminal:
|
355
407
|
|
356
408
|
```
|
357
|
-
|
358
|
-
export SPRING_LOG=/tmp/spring.log
|
359
|
-
spring rake -T
|
409
|
+
$ spring server
|
360
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.
|
data/bin/spring
CHANGED
@@ -43,6 +43,7 @@ if defined?(Gem)
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
lib = File.expand_path("../../lib", __FILE__)
|
47
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) # enable local development
|
47
48
|
require 'spring/client'
|
48
49
|
Spring::Client.run(ARGV)
|
@@ -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
|
@@ -99,7 +99,7 @@ module Spring
|
|
99
99
|
@preloaded = :success
|
100
100
|
rescue Exception => e
|
101
101
|
@preloaded = :failure
|
102
|
-
watcher.add e.backtrace.map { |line| line
|
102
|
+
watcher.add e.backtrace.map { |line| line[/^(.*)\:\d+/, 1] }
|
103
103
|
raise e unless initialized?
|
104
104
|
ensure
|
105
105
|
watcher.add loaded_application_features
|
@@ -149,14 +149,22 @@ 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
|
|
166
|
+
STDERR.puts "Running via Spring preloader in process #{Process.pid}" unless Spring.quiet
|
167
|
+
|
160
168
|
ARGV.replace(args)
|
161
169
|
$0 = command.exec_name
|
162
170
|
|
@@ -298,7 +306,7 @@ module Spring
|
|
298
306
|
@mutex.synchronize { @waiting << pid }
|
299
307
|
|
300
308
|
# Wait in a separate thread so we can run multiple commands at once
|
301
|
-
|
309
|
+
Spring.failsafe_thread {
|
302
310
|
begin
|
303
311
|
_, status = Process.wait2 pid
|
304
312
|
log "#{pid} exited with #{status.exitstatus}"
|
@@ -311,6 +319,17 @@ module Spring
|
|
311
319
|
exit_if_finished
|
312
320
|
end
|
313
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
|
+
}
|
314
333
|
end
|
315
334
|
|
316
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
@@ -13,8 +13,9 @@ module Spring
|
|
13
13
|
# should cause the "unsprung" version of the command to run.
|
14
14
|
LOADER = <<CODE
|
15
15
|
begin
|
16
|
-
load File.expand_path(
|
17
|
-
rescue LoadError
|
16
|
+
load File.expand_path('../spring', __FILE__)
|
17
|
+
rescue LoadError => e
|
18
|
+
raise unless e.message.include?('spring')
|
18
19
|
end
|
19
20
|
CODE
|
20
21
|
|
@@ -33,19 +34,26 @@ CODE
|
|
33
34
|
# It gets overwritten when you run the `spring binstub` command.
|
34
35
|
|
35
36
|
unless defined?(Spring)
|
36
|
-
require
|
37
|
-
require
|
37
|
+
require 'rubygems'
|
38
|
+
require 'bundler'
|
38
39
|
|
39
|
-
if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
|
40
|
-
Gem.paths = {
|
41
|
-
gem
|
42
|
-
require
|
40
|
+
if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
|
41
|
+
Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
|
42
|
+
gem 'spring', match[1]
|
43
|
+
require 'spring/binstub'
|
43
44
|
end
|
44
45
|
end
|
45
46
|
CODE
|
46
47
|
|
47
48
|
OLD_BINSTUB = %{if !Process.respond_to?(:fork) || Gem::Specification.find_all_by_name("spring").empty?}
|
48
49
|
|
50
|
+
BINSTUB_VARIATIONS = Regexp.union [
|
51
|
+
%{begin\n load File.expand_path("../spring", __FILE__)\nrescue LoadError\nend\n},
|
52
|
+
%{begin\n load File.expand_path('../spring', __FILE__)\nrescue LoadError\nend\n},
|
53
|
+
%{begin\n spring_bin_path = File.expand_path('../spring', __FILE__)\n load spring_bin_path\nrescue LoadError => e\n raise unless e.message.end_with? spring_bin_path, 'spring/binstub'\nend\n},
|
54
|
+
LOADER
|
55
|
+
]
|
56
|
+
|
49
57
|
class Item
|
50
58
|
attr_reader :command, :existing
|
51
59
|
|
@@ -72,8 +80,12 @@ CODE
|
|
72
80
|
fallback = nil if fallback.include?("exec")
|
73
81
|
generate(fallback)
|
74
82
|
status "upgraded"
|
75
|
-
elsif existing
|
83
|
+
elsif existing.include?(LOADER)
|
76
84
|
status "spring already present"
|
85
|
+
elsif existing =~ BINSTUB_VARIATIONS
|
86
|
+
upgraded = existing.sub(BINSTUB_VARIATIONS, LOADER)
|
87
|
+
File.write(command.binstub, upgraded)
|
88
|
+
status "upgraded"
|
77
89
|
else
|
78
90
|
head, shebang, tail = existing.partition(SHEBANG)
|
79
91
|
|
@@ -108,7 +120,7 @@ CODE
|
|
108
120
|
|
109
121
|
def remove
|
110
122
|
if existing
|
111
|
-
File.write(command.binstub, existing.sub(
|
123
|
+
File.write(command.binstub, existing.sub(BINSTUB_VARIATIONS, ""))
|
112
124
|
status "spring removed"
|
113
125
|
end
|
114
126
|
end
|
@@ -117,7 +129,7 @@ CODE
|
|
117
129
|
attr_reader :bindir, :items
|
118
130
|
|
119
131
|
def self.description
|
120
|
-
"Generate spring based binstubs. Use --all to generate a binstub for all known commands."
|
132
|
+
"Generate spring based binstubs. Use --all to generate a binstub for all known commands. Use --remove to revert."
|
121
133
|
end
|
122
134
|
|
123
135
|
def self.rails_command
|
data/lib/spring/client/run.rb
CHANGED
@@ -5,32 +5,37 @@ require "bundler"
|
|
5
5
|
module Spring
|
6
6
|
module Client
|
7
7
|
class Run < Command
|
8
|
-
FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO) & Signal.list.keys
|
9
|
-
|
8
|
+
FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
|
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,17 +9,21 @@ 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
|
15
16
|
COMMANDS = {
|
16
17
|
"help" => Client::Help,
|
18
|
+
"-h" => Client::Help,
|
19
|
+
"--help" => Client::Help,
|
17
20
|
"binstub" => Client::Binstub,
|
18
21
|
"stop" => Client::Stop,
|
19
22
|
"status" => Client::Status,
|
20
23
|
"rails" => Client::Rails,
|
21
24
|
"-v" => Client::Version,
|
22
|
-
"--version" => Client::Version
|
25
|
+
"--version" => Client::Version,
|
26
|
+
"server" => Client::Server,
|
23
27
|
}
|
24
28
|
|
25
29
|
def self.run(args)
|
@@ -36,3 +40,9 @@ module Spring
|
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
43
|
+
|
44
|
+
# allow users to add hooks that do not run in the server
|
45
|
+
# or modify start/stop
|
46
|
+
if File.exist?("config/spring_client.rb")
|
47
|
+
require "./config/spring_client.rb"
|
48
|
+
end
|
data/lib/spring/configuration.rb
CHANGED
@@ -2,7 +2,7 @@ require "spring/errors"
|
|
2
2
|
|
3
3
|
module Spring
|
4
4
|
class << self
|
5
|
-
attr_accessor :application_root
|
5
|
+
attr_accessor :application_root, :quiet
|
6
6
|
|
7
7
|
def gemfile
|
8
8
|
ENV['BUNDLE_GEMFILE'] || "Gemfile"
|
@@ -49,4 +49,6 @@ module Spring
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
|
+
|
53
|
+
self.quiet = false
|
52
54
|
end
|
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
|