Skip to content

Commit

Permalink
Add "spring server" command
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jonleighton committed Apr 9, 2016
1 parent 2cd0c65 commit e16e085
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 23 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
## 1.6.5
## Next release

* Auto-restart server when server and client versions do not match
* Add `spring server` command to explicitly start a Spring server
process 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.6.4

Expand Down
4 changes: 2 additions & 2 deletions lib/spring/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ module Spring
class Application
attr_reader :manager, :watcher, :spring_env, :original_env

def initialize(manager, original_env)
def initialize(manager, original_env, spring_env = Env.new)
@manager = manager
@original_env = original_env
@spring_env = Env.new
@spring_env = spring_env
@mutex = Mutex.new
@waiting = Set.new
@preloaded = false
Expand Down
3 changes: 2 additions & 1 deletion lib/spring/application/boot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

app = Spring::Application.new(
UNIXSocket.for_fd(3),
Spring::JSON.load(ENV.delete("SPRING_ORIGINAL_ENV").dup)
Spring::JSON.load(ENV.delete("SPRING_ORIGINAL_ENV").dup),
Spring::Env.new(log_file: IO.for_fd(4))
)

Signal.trap("TERM") { app.terminate }
Expand Down
7 changes: 4 additions & 3 deletions lib/spring/application_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ module Spring
class ApplicationManager
attr_reader :pid, :child, :app_env, :spring_env, :status

def initialize(app_env)
def initialize(app_env, spring_env)
@app_env = app_env
@spring_env = Env.new
@spring_env = spring_env
@mutex = Mutex.new
@state = :running
end
Expand Down Expand Up @@ -104,7 +104,8 @@ def start_child(preload = false)
"-I", File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first),
"-I", File.expand_path("../..", __FILE__),
"-e", "require 'spring/application/boot'",
3 => child_socket
3 => child_socket,
4 => spring_env.log_file,
)
end

Expand Down
2 changes: 2 additions & 0 deletions lib/spring/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
require "spring/client/status"
require "spring/client/rails"
require "spring/client/version"
require "spring/client/server"

module Spring
module Client
Expand All @@ -22,6 +23,7 @@ module Client
"rails" => Client::Rails,
"-v" => Client::Version,
"--version" => Client::Version,
"server" => Client::Server,
}

def self.run(args)
Expand Down
14 changes: 14 additions & 0 deletions lib/spring/client/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Spring
module Client
class Server < Command
def call
require "spring/server"
Spring::Server.boot(foreground: true)
end

def self.description
"Explicitly start a Spring server in the foreground"
end
end
end
end
8 changes: 4 additions & 4 deletions lib/spring/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ module Spring
class Env
attr_reader :log_file

def initialize(root = nil)
@root = root
@project_root = root
@log_file = File.open(ENV["SPRING_LOG"] || File::NULL, "a")
def initialize(options = {})
@root = options[:root]
@project_root = options[:root]
@log_file = options[:log_file] || File.open(ENV["SPRING_LOG"] || File::NULL, "a")
end

def root
Expand Down
34 changes: 27 additions & 7 deletions lib/spring/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,24 @@ module Spring

module Spring
class Server
def self.boot
new.boot
def self.boot(options = {})
new(options).boot
end

attr_reader :env

def initialize(env = Env.new)
@env = env
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
def initialize(options = {})
@foreground = options.fetch(:foreground, false)
@env = options[:env] || default_env
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k, env) }
@pidfile = env.pidfile_path.open('a')
@mutex = Mutex.new
end

def foreground?
@foreground
end

def log(message)
env.log "[server] #{message}"
end
Expand All @@ -31,8 +36,8 @@ def boot
Spring.verify_environment

write_pidfile
set_pgid
ignore_signals
set_pgid unless foreground?

This comment has been minimized.

Copy link
@kradima

kradima Aug 28, 2016

@jonleighton May I kindly ask you why do you use set_pgid unless foreground? here? It means that pgid is set for spring server running in background but expected the opposite, that it would be set to the server running in foreground.

I've met this problem when running RubyMine from Toolbox so that it hasn't console and spring failed on set_pgid. Since spring server has ran by default with the --background option this problem should be solved on its own if pgid will be set only for foreground spring server process.

This comment has been minimized.

Copy link
@jonleighton

jonleighton Aug 29, 2016

Author Member

@kradima by default spring is designed to be run from a terminal. the set_pgid method is designed to attach the server process to the current terminal's process group so that the spring server process dies when the terminal is closed (see comments in the code)

This comment has been minimized.

Copy link
@kradima

kradima Aug 29, 2016

@jonleighton Thank you for quick response, I've seen it and probably I'm missing something but the code set_pgid unless foreground? sets set_pgpid if Spring server process isn't running in foreground == running in background, e.g. without terminal to attach.

It seems it should be changed to set_pgpid if foreground?, shouldn't it?

This comment has been minimized.

Copy link
@grosser

grosser Nov 14, 2018

Collaborator

PR #575

ignore_signals unless foreground?
set_exit_hook
set_process_title
start_server
Expand All @@ -42,6 +47,7 @@ def start_server
server = UNIXServer.open(env.socket_name)
log "started on #{env.socket_name}"
loop { serve server.accept }
rescue Interrupt
end

def serve(client)
Expand Down Expand Up @@ -126,5 +132,19 @@ def set_process_title
"spring server | #{env.app_name} | started #{distance} ago"
}
end

private

def default_env
Env.new(log_file: default_log_file)
end

def default_log_file
if foreground? && !ENV["SPRING_LOG"]
$stdout
else
nil
end
end
end
end
25 changes: 21 additions & 4 deletions lib/spring/test/acceptance_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def app
@app ||= Spring::Test::Application.new("#{Spring::Test.root}/apps/tmp")
end

def spring_env
app.spring_env
end

def assert_output(artifacts, expected)
expected.each do |stream, output|
assert artifacts[stream].include?(output),
Expand Down Expand Up @@ -92,14 +96,14 @@ def without_gem(name)

test "help message when called without arguments" do
assert_success "bin/spring", stdout: 'Usage: spring COMMAND [ARGS]'
assert app.spring_env.server_running?
assert spring_env.server_running?
end

test "shows help" do
assert_success "bin/spring help", stdout: 'Usage: spring COMMAND [ARGS]'
assert_success "bin/spring -h", stdout: 'Usage: spring COMMAND [ARGS]'
assert_success "bin/spring --help", stdout: 'Usage: spring COMMAND [ARGS]'
refute app.spring_env.server_running?
refute spring_env.server_running?
end

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

test "stop command kills server" do
app.run app.spring_test_command
assert app.spring_env.server_running?, "The server should be running but it isn't"
assert spring_env.server_running?, "The server should be running but it isn't"

assert_success "bin/spring stop"
assert !app.spring_env.server_running?, "The server should not be running but it is"
assert !spring_env.server_running?, "The server should not be running but it is"
end

test "custom commands" do
Expand Down Expand Up @@ -508,6 +512,19 @@ def exec_name
2.times { assert_success "bundle exec rails runner ''" }
end
end

test "booting a foreground server" do
FileUtils.cd(app.root) do
assert !spring_env.server_running?
app.run "spring server &"

Timeout.timeout(1) do
sleep 0.1 until spring_env.server_running?
end

assert_success app.spring_test_command
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/spring/test/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Application

def initialize(root)
@root = Pathname.new(root)
@spring_env = Spring::Env.new(root)
@spring_env = Spring::Env.new(root: root)
end

def exists?
Expand Down

0 comments on commit e16e085

Please sign in to comment.