From e16e085e4e6aada8b32198e98e0c8ae1ab7b9963 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Sat, 26 Mar 2016 14:11:35 +0000 Subject: [PATCH] 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. --- CHANGELOG.md | 7 +++++- lib/spring/application.rb | 4 ++-- lib/spring/application/boot.rb | 3 ++- lib/spring/application_manager.rb | 7 +++--- lib/spring/client.rb | 2 ++ lib/spring/client/server.rb | 14 ++++++++++++ lib/spring/env.rb | 8 +++---- lib/spring/server.rb | 34 ++++++++++++++++++++++++------ lib/spring/test/acceptance_test.rb | 25 ++++++++++++++++++---- lib/spring/test/application.rb | 2 +- 10 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 lib/spring/client/server.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 1418427a..e56ceacd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/spring/application.rb b/lib/spring/application.rb index ddcc2d4e..b461d831 100644 --- a/lib/spring/application.rb +++ b/lib/spring/application.rb @@ -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 diff --git a/lib/spring/application/boot.rb b/lib/spring/application/boot.rb index 6804b646..8510b886 100644 --- a/lib/spring/application/boot.rb +++ b/lib/spring/application/boot.rb @@ -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 } diff --git a/lib/spring/application_manager.rb b/lib/spring/application_manager.rb index a05aef4a..bb5bdfad 100644 --- a/lib/spring/application_manager.rb +++ b/lib/spring/application_manager.rb @@ -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 @@ -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 diff --git a/lib/spring/client.rb b/lib/spring/client.rb index 69bb8f5b..065a1aa9 100644 --- a/lib/spring/client.rb +++ b/lib/spring/client.rb @@ -9,6 +9,7 @@ require "spring/client/status" require "spring/client/rails" require "spring/client/version" +require "spring/client/server" module Spring module Client @@ -22,6 +23,7 @@ module Client "rails" => Client::Rails, "-v" => Client::Version, "--version" => Client::Version, + "server" => Client::Server, } def self.run(args) diff --git a/lib/spring/client/server.rb b/lib/spring/client/server.rb new file mode 100644 index 00000000..e096d005 --- /dev/null +++ b/lib/spring/client/server.rb @@ -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 diff --git a/lib/spring/env.rb b/lib/spring/env.rb index 38ae65b3..7f9136f0 100644 --- a/lib/spring/env.rb +++ b/lib/spring/env.rb @@ -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 diff --git a/lib/spring/server.rb b/lib/spring/server.rb index b922199f..be908532 100644 --- a/lib/spring/server.rb +++ b/lib/spring/server.rb @@ -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 @@ -31,8 +36,8 @@ def boot Spring.verify_environment write_pidfile - set_pgid - ignore_signals + set_pgid unless foreground? + ignore_signals unless foreground? set_exit_hook set_process_title start_server @@ -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) @@ -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 diff --git a/lib/spring/test/acceptance_test.rb b/lib/spring/test/acceptance_test.rb index fe10435a..1672a05c 100644 --- a/lib/spring/test/acceptance_test.rb +++ b/lib/spring/test/acceptance_test.rb @@ -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), @@ -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 @@ -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 @@ -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 diff --git a/lib/spring/test/application.rb b/lib/spring/test/application.rb index f6fe74b0..2da25e1e 100644 --- a/lib/spring/test/application.rb +++ b/lib/spring/test/application.rb @@ -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?