spring_standalone 0.1.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +418 -0
- data/bin/spring_sa +49 -0
- data/lib/spring_standalone/application.rb +367 -0
- data/lib/spring_standalone/application/boot.rb +19 -0
- data/lib/spring_standalone/application_manager.rb +141 -0
- data/lib/spring_standalone/binstub.rb +13 -0
- data/lib/spring_standalone/boot.rb +10 -0
- data/lib/spring_standalone/client.rb +48 -0
- data/lib/spring_standalone/client/binstub.rb +198 -0
- data/lib/spring_standalone/client/command.rb +18 -0
- data/lib/spring_standalone/client/help.rb +62 -0
- data/lib/spring_standalone/client/rails.rb +34 -0
- data/lib/spring_standalone/client/run.rb +232 -0
- data/lib/spring_standalone/client/server.rb +18 -0
- data/lib/spring_standalone/client/status.rb +30 -0
- data/lib/spring_standalone/client/stop.rb +22 -0
- data/lib/spring_standalone/client/version.rb +11 -0
- data/lib/spring_standalone/command_wrapper.rb +82 -0
- data/lib/spring_standalone/commands.rb +50 -0
- data/lib/spring_standalone/commands/rake.rb +30 -0
- data/lib/spring_standalone/configuration.rb +58 -0
- data/lib/spring_standalone/env.rb +116 -0
- data/lib/spring_standalone/errors.rb +36 -0
- data/lib/spring_standalone/failsafe_thread.rb +14 -0
- data/lib/spring_standalone/json.rb +626 -0
- data/lib/spring_standalone/process_title_updater.rb +65 -0
- data/lib/spring_standalone/server.rb +150 -0
- data/lib/spring_standalone/sid.rb +42 -0
- data/lib/spring_standalone/version.rb +3 -0
- data/lib/spring_standalone/watcher.rb +30 -0
- data/lib/spring_standalone/watcher/abstract.rb +117 -0
- data/lib/spring_standalone/watcher/polling.rb +98 -0
- metadata +106 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module SpringStandalone
|
2
|
+
module Client
|
3
|
+
class Server < Command
|
4
|
+
def self.description
|
5
|
+
"Explicitly start a SpringStandalone server in the foreground"
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
require "spring_standalone/server"
|
10
|
+
SpringStandalone::Server.boot(foreground: foreground?)
|
11
|
+
end
|
12
|
+
|
13
|
+
def foreground?
|
14
|
+
!args.include?("--background")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SpringStandalone
|
2
|
+
module Client
|
3
|
+
class Status < Command
|
4
|
+
def self.description
|
5
|
+
"Show current status."
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
if env.server_running?
|
10
|
+
puts "SpringStandalone is running:"
|
11
|
+
puts
|
12
|
+
print_process env.pid
|
13
|
+
application_pids.each { |pid| print_process pid }
|
14
|
+
else
|
15
|
+
puts "SpringStandalone is not running."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def print_process(pid)
|
20
|
+
puts `ps -p #{pid} -o pid= -o command=`
|
21
|
+
end
|
22
|
+
|
23
|
+
def application_pids
|
24
|
+
candidates = `ps -ax -o ppid= -o pid=`.lines
|
25
|
+
candidates.select { |l| l =~ /^(\s+)?#{env.pid} / }
|
26
|
+
.map { |l| l.split(" ").last }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spring_standalone/version"
|
2
|
+
|
3
|
+
module SpringStandalone
|
4
|
+
module Client
|
5
|
+
class Stop < Command
|
6
|
+
def self.description
|
7
|
+
"Stop all SpringStandalone processes for this project."
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
case env.stop
|
12
|
+
when :stopped
|
13
|
+
puts "SpringStandalone stopped."
|
14
|
+
when :killed
|
15
|
+
$stderr.puts "SpringStandalone did not stop; killing forcibly."
|
16
|
+
when :not_running
|
17
|
+
puts "SpringStandalone is not running"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "spring_standalone/configuration"
|
2
|
+
|
3
|
+
module SpringStandalone
|
4
|
+
class CommandWrapper
|
5
|
+
attr_reader :name, :command
|
6
|
+
|
7
|
+
def initialize(name, command = nil)
|
8
|
+
@name = name
|
9
|
+
@command = command
|
10
|
+
@setup = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def description
|
14
|
+
if command.respond_to?(:description)
|
15
|
+
command.description
|
16
|
+
else
|
17
|
+
"Runs the #{name} command"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup?
|
22
|
+
@setup
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup
|
26
|
+
if !setup? && command.respond_to?(:setup)
|
27
|
+
command.setup
|
28
|
+
@setup = true
|
29
|
+
return true
|
30
|
+
else
|
31
|
+
@setup = true
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def call
|
37
|
+
if command.respond_to?(:call)
|
38
|
+
command.call
|
39
|
+
else
|
40
|
+
load exec
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def gem_name
|
45
|
+
if command.respond_to?(:gem_name)
|
46
|
+
command.gem_name
|
47
|
+
else
|
48
|
+
exec_name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def exec_name
|
53
|
+
if command.respond_to?(:exec_name)
|
54
|
+
command.exec_name
|
55
|
+
else
|
56
|
+
name
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def binstub
|
61
|
+
SpringStandalone.application_root_path.join(binstub_name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def binstub_name
|
65
|
+
"bin/#{name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def exec
|
69
|
+
if binstub.exist?
|
70
|
+
binstub.to_s
|
71
|
+
else
|
72
|
+
Gem.bin_path(gem_name, exec_name)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def env(args)
|
77
|
+
if command.respond_to?(:env)
|
78
|
+
command.env(args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "spring_standalone/watcher"
|
2
|
+
require "spring_standalone/command_wrapper"
|
3
|
+
|
4
|
+
module SpringStandalone
|
5
|
+
@commands = {}
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_reader :commands
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_command(name, command = nil)
|
12
|
+
commands[name] = CommandWrapper.new(name, command)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.command?(name)
|
16
|
+
commands.include? name
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.command(name)
|
20
|
+
commands.fetch name
|
21
|
+
end
|
22
|
+
|
23
|
+
# require "spring_standalone/commands/rails"
|
24
|
+
require "spring_standalone/commands/rake"
|
25
|
+
|
26
|
+
# Load custom commands, if any.
|
27
|
+
# needs to be at the end to allow modification of existing commands.
|
28
|
+
config = File.expand_path("~/.spring_standalone.rb")
|
29
|
+
require config if File.exist?(config)
|
30
|
+
|
31
|
+
# If the config/spring.rb contains requires for commands from other gems,
|
32
|
+
# then we need to be under bundler.
|
33
|
+
require "bundler/setup"
|
34
|
+
|
35
|
+
# Auto-require any SpringStandalone extensions which are in the Gemfile
|
36
|
+
Gem::Specification.map(&:name).grep(/^spring_standalone-/).each do |command|
|
37
|
+
begin
|
38
|
+
require command
|
39
|
+
rescue LoadError => error
|
40
|
+
if error.message.include?(command)
|
41
|
+
require command.gsub("-", "/")
|
42
|
+
else
|
43
|
+
raise
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
config = File.expand_path("./config/spring_standalone.rb")
|
49
|
+
require config if File.exist?(config)
|
50
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SpringStandalone
|
2
|
+
module Commands
|
3
|
+
class Rake
|
4
|
+
class << self
|
5
|
+
attr_accessor :environment_matchers
|
6
|
+
end
|
7
|
+
|
8
|
+
self.environment_matchers = {
|
9
|
+
:default => "test",
|
10
|
+
/^test($|:)/ => "test"
|
11
|
+
}
|
12
|
+
|
13
|
+
def env(args)
|
14
|
+
# This is an adaption of the matching that Rake itself does.
|
15
|
+
# See: https://github.com/jimweirich/rake/blob/3754a7639b3f42c2347857a0878beb3523542aee/lib/rake/application.rb#L691-L692
|
16
|
+
if env = args.grep(/^(APP|RACK)_ENV=(.*)$/m).last
|
17
|
+
return env.split("=").last
|
18
|
+
end
|
19
|
+
|
20
|
+
self.class.environment_matchers.each do |matcher, environment|
|
21
|
+
return environment if matcher === (args.first || :default)
|
22
|
+
end
|
23
|
+
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
SpringStandalone.register_command "rake", Rake.new
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "spring_standalone/errors"
|
2
|
+
|
3
|
+
module SpringStandalone
|
4
|
+
class << self
|
5
|
+
attr_accessor :application_root, :quiet
|
6
|
+
|
7
|
+
def gemfile
|
8
|
+
if /\s1.9.[0-9]/ === Bundler.ruby_scope.gsub(/[\/\s]+/,'')
|
9
|
+
ENV["BUNDLE_GEMFILE"] || "Gemfile"
|
10
|
+
else
|
11
|
+
Bundler.default_gemfile
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def after_fork_callbacks
|
16
|
+
@after_fork_callbacks ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_fork(&block)
|
20
|
+
after_fork_callbacks << block
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify_environment
|
24
|
+
application_root_path
|
25
|
+
end
|
26
|
+
|
27
|
+
def application_root_path
|
28
|
+
@application_root_path ||= begin
|
29
|
+
if application_root
|
30
|
+
path = Pathname.new(File.expand_path(application_root))
|
31
|
+
else
|
32
|
+
path = project_root_path
|
33
|
+
end
|
34
|
+
|
35
|
+
# raise MissingApplication.new(path) unless path.join("config/application.rb").exist?
|
36
|
+
path
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def project_root_path
|
41
|
+
@project_root_path ||= find_project_root(Pathname.new(File.expand_path(Dir.pwd)))
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def find_project_root(current_dir)
|
47
|
+
if current_dir.join(gemfile).exist?
|
48
|
+
current_dir
|
49
|
+
elsif current_dir.root?
|
50
|
+
raise UnknownProject.new(Dir.pwd)
|
51
|
+
else
|
52
|
+
find_project_root(current_dir.parent)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
self.quiet = false
|
58
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "digest/md5"
|
3
|
+
|
4
|
+
require "spring_standalone/version"
|
5
|
+
require "spring_standalone/sid"
|
6
|
+
require "spring_standalone/configuration"
|
7
|
+
|
8
|
+
module SpringStandalone
|
9
|
+
IGNORE_SIGNALS = %w(INT QUIT)
|
10
|
+
STOP_TIMEOUT = 2 # seconds
|
11
|
+
|
12
|
+
class Env
|
13
|
+
attr_reader :log_file
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
@root = options[:root]
|
17
|
+
@project_root = options[:root]
|
18
|
+
@log_file = options[:log_file] || File.open(ENV["SPRING_LOG"] || File::NULL, "a")
|
19
|
+
end
|
20
|
+
|
21
|
+
def root
|
22
|
+
@root ||= SpringStandalone.application_root_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def project_root
|
26
|
+
@project_root ||= SpringStandalone.project_root_path
|
27
|
+
end
|
28
|
+
|
29
|
+
def version
|
30
|
+
SpringStandalone::VERSION
|
31
|
+
end
|
32
|
+
|
33
|
+
def tmp_path
|
34
|
+
require "tmpdir"
|
35
|
+
path = Pathname.new(
|
36
|
+
ENV["SPRING_TMP_PATH"] ||
|
37
|
+
File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}")
|
38
|
+
)
|
39
|
+
require "fileutils"
|
40
|
+
FileUtils.mkdir_p(path) unless path.exist?
|
41
|
+
path
|
42
|
+
end
|
43
|
+
|
44
|
+
def application_id
|
45
|
+
ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def socket_path
|
49
|
+
Pathname.new(ENV["SPRING_SOCKET"] || tmp_path.join(application_id))
|
50
|
+
end
|
51
|
+
|
52
|
+
def socket_name
|
53
|
+
socket_path.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def pidfile_path
|
57
|
+
Pathname.new(ENV["SPRING_PIDFILE"] || socket_path.dirname.join("#{socket_path.basename(".*")}.pid"))
|
58
|
+
end
|
59
|
+
|
60
|
+
def pid
|
61
|
+
pidfile_path.exist? ? pidfile_path.read.to_i : nil
|
62
|
+
rescue Errno::ENOENT
|
63
|
+
# This can happen if the pidfile is removed after we check it
|
64
|
+
# exists
|
65
|
+
end
|
66
|
+
|
67
|
+
def app_name
|
68
|
+
root.basename
|
69
|
+
end
|
70
|
+
|
71
|
+
def server_running?
|
72
|
+
pidfile = pidfile_path.open('r+')
|
73
|
+
!pidfile.flock(File::LOCK_EX | File::LOCK_NB)
|
74
|
+
rescue Errno::ENOENT
|
75
|
+
false
|
76
|
+
ensure
|
77
|
+
if pidfile
|
78
|
+
pidfile.flock(File::LOCK_UN)
|
79
|
+
pidfile.close
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def log(message)
|
84
|
+
log_file.puts "[#{Time.now}] [#{Process.pid}] #{message}"
|
85
|
+
log_file.flush
|
86
|
+
end
|
87
|
+
|
88
|
+
def stop
|
89
|
+
if server_running?
|
90
|
+
timeout = Time.now + STOP_TIMEOUT
|
91
|
+
kill 'TERM'
|
92
|
+
sleep 0.1 until !server_running? || Time.now >= timeout
|
93
|
+
|
94
|
+
if server_running?
|
95
|
+
kill 'KILL'
|
96
|
+
:killed
|
97
|
+
else
|
98
|
+
:stopped
|
99
|
+
end
|
100
|
+
else
|
101
|
+
:not_running
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def kill(sig)
|
106
|
+
pid = self.pid
|
107
|
+
Process.kill(sig, pid) if pid
|
108
|
+
rescue Errno::ESRCH
|
109
|
+
# already dead
|
110
|
+
end
|
111
|
+
|
112
|
+
def server_command
|
113
|
+
ENV["SPRING_SERVER_COMMAND"] || "#{File.expand_path("../../../bin/spring_sa", __FILE__)} server --background"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SpringStandalone
|
2
|
+
class ClientError < StandardError; end
|
3
|
+
|
4
|
+
class UnknownProject < StandardError
|
5
|
+
attr_reader :current_dir
|
6
|
+
|
7
|
+
def initialize(current_dir)
|
8
|
+
@current_dir = current_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
"SpringStandalone was unable to locate the root of your project. There was no Gemfile " \
|
13
|
+
"present in the current directory (#{current_dir}) or any of the parent " \
|
14
|
+
"directories."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# class MissingApplication < ClientError
|
19
|
+
# attr_reader :project_root
|
20
|
+
#
|
21
|
+
# def initialize(project_root)
|
22
|
+
# @project_root = project_root
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def message
|
26
|
+
# "SpringStandalone was unable to find your config/application.rb file. " \
|
27
|
+
# "Your project root was detected at #{project_root}, so SpringStandalone " \
|
28
|
+
# "looked for #{project_root}/config/application.rb but it doesn't exist. You can " \
|
29
|
+
# "configure the root of your application by setting SpringStandalone.application_root in " \
|
30
|
+
# "config/spring.rb."
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
|
34
|
+
class CommandNotFound < ClientError
|
35
|
+
end
|
36
|
+
end
|