Skip to content

Commit e16e085

Browse files
committed
Add "spring server" command
This command to start a Spring server explicitly in the foreground, which logging to stdout. This will be useful to those who want to run spring more explicitly, but the real impetus was to enable running a spring server inside a Docker container.
1 parent 2cd0c65 commit e16e085

File tree

10 files changed

+83
-23
lines changed

10 files changed

+83
-23
lines changed

CHANGELOG.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
## 1.6.5
1+
## Next release
22

33
* Auto-restart server when server and client versions do not match
4+
* Add `spring server` command to explicitly start a Spring server
5+
process in the foreground, which logging to stdout. This will be
6+
useful to those who want to run spring more explicitly, but the real
7+
impetus was to enable running a spring server inside a Docker
8+
container.
49

510
## 1.6.4
611

lib/spring/application.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ module Spring
66
class Application
77
attr_reader :manager, :watcher, :spring_env, :original_env
88

9-
def initialize(manager, original_env)
9+
def initialize(manager, original_env, spring_env = Env.new)
1010
@manager = manager
1111
@original_env = original_env
12-
@spring_env = Env.new
12+
@spring_env = spring_env
1313
@mutex = Mutex.new
1414
@waiting = Set.new
1515
@preloaded = false

lib/spring/application/boot.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
app = Spring::Application.new(
77
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))
910
)
1011

1112
Signal.trap("TERM") { app.terminate }

lib/spring/application_manager.rb

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ module Spring
22
class ApplicationManager
33
attr_reader :pid, :child, :app_env, :spring_env, :status
44

5-
def initialize(app_env)
5+
def initialize(app_env, spring_env)
66
@app_env = app_env
7-
@spring_env = Env.new
7+
@spring_env = spring_env
88
@mutex = Mutex.new
99
@state = :running
1010
end
@@ -104,7 +104,8 @@ def start_child(preload = false)
104104
"-I", File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first),
105105
"-I", File.expand_path("../..", __FILE__),
106106
"-e", "require 'spring/application/boot'",
107-
3 => child_socket
107+
3 => child_socket,
108+
4 => spring_env.log_file,
108109
)
109110
end
110111

lib/spring/client.rb

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require "spring/client/status"
1010
require "spring/client/rails"
1111
require "spring/client/version"
12+
require "spring/client/server"
1213

1314
module Spring
1415
module Client
@@ -22,6 +23,7 @@ module Client
2223
"rails" => Client::Rails,
2324
"-v" => Client::Version,
2425
"--version" => Client::Version,
26+
"server" => Client::Server,
2527
}
2628

2729
def self.run(args)

lib/spring/client/server.rb

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Spring
2+
module Client
3+
class Server < Command
4+
def call
5+
require "spring/server"
6+
Spring::Server.boot(foreground: true)
7+
end
8+
9+
def self.description
10+
"Explicitly start a Spring server in the foreground"
11+
end
12+
end
13+
end
14+
end

lib/spring/env.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ module Spring
1414
class Env
1515
attr_reader :log_file
1616

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")
2121
end
2222

2323
def root

lib/spring/server.rb

+27-7
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@ module Spring
1010

1111
module Spring
1212
class Server
13-
def self.boot
14-
new.boot
13+
def self.boot(options = {})
14+
new(options).boot
1515
end
1616

1717
attr_reader :env
1818

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) }
2223
@pidfile = env.pidfile_path.open('a')
2324
@mutex = Mutex.new
2425
end
2526

27+
def foreground?
28+
@foreground
29+
end
30+
2631
def log(message)
2732
env.log "[server] #{message}"
2833
end
@@ -31,8 +36,8 @@ def boot
3136
Spring.verify_environment
3237

3338
write_pidfile
34-
set_pgid
35-
ignore_signals
39+
set_pgid unless foreground?
40+
ignore_signals unless foreground?
3641
set_exit_hook
3742
set_process_title
3843
start_server
@@ -42,6 +47,7 @@ def start_server
4247
server = UNIXServer.open(env.socket_name)
4348
log "started on #{env.socket_name}"
4449
loop { serve server.accept }
50+
rescue Interrupt
4551
end
4652

4753
def serve(client)
@@ -126,5 +132,19 @@ def set_process_title
126132
"spring server | #{env.app_name} | started #{distance} ago"
127133
}
128134
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
129149
end
130150
end

lib/spring/test/acceptance_test.rb

+21-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def app
2828
@app ||= Spring::Test::Application.new("#{Spring::Test.root}/apps/tmp")
2929
end
3030

31+
def spring_env
32+
app.spring_env
33+
end
34+
3135
def assert_output(artifacts, expected)
3236
expected.each do |stream, output|
3337
assert artifacts[stream].include?(output),
@@ -92,14 +96,14 @@ def without_gem(name)
9296

9397
test "help message when called without arguments" do
9498
assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
95-
assert app.spring_env.server_running?
99+
assert spring_env.server_running?
96100
end
97101

98102
test "shows help" do
99103
assert_success "bin/spring help", stdout: 'Usage: spring COMMAND [ARGS]'
100104
assert_success "bin/spring -h", stdout: 'Usage: spring COMMAND [ARGS]'
101105
assert_success "bin/spring --help", stdout: 'Usage: spring COMMAND [ARGS]'
102-
refute app.spring_env.server_running?
106+
refute spring_env.server_running?
103107
end
104108

105109
test "tells the user that spring is being used when used automatically via binstubs" do
@@ -184,10 +188,10 @@ def self.omg
184188

185189
test "stop command kills server" do
186190
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"
188192

189193
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"
191195
end
192196

193197
test "custom commands" do
@@ -508,6 +512,19 @@ def exec_name
508512
2.times { assert_success "bundle exec rails runner ''" }
509513
end
510514
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
511528
end
512529
end
513530
end

lib/spring/test/application.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Application
99

1010
def initialize(root)
1111
@root = Pathname.new(root)
12-
@spring_env = Spring::Env.new(root)
12+
@spring_env = Spring::Env.new(root: root)
1313
end
1414

1515
def exists?

0 commit comments

Comments
 (0)