stable-cli-rails 0.7.12 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/stable/bootstrap.rb +1 -0
- data/lib/stable/cli.rb +2 -6
- data/lib/stable/commands/doctor.rb +1 -0
- data/lib/stable/commands/list.rb +37 -32
- data/lib/stable/commands/new.rb +1 -0
- data/lib/stable/commands/remove.rb +1 -0
- data/lib/stable/commands/restart.rb +1 -0
- data/lib/stable/commands/setup.rb +1 -0
- data/lib/stable/commands/start.rb +1 -0
- data/lib/stable/commands/stop.rb +1 -0
- data/lib/stable/db_manager.rb +22 -7
- data/lib/stable/paths.rb +10 -1
- data/lib/stable/registry.rb +57 -7
- data/lib/stable/scanner.rb +1 -0
- data/lib/stable/services/app_creator.rb +12 -10
- data/lib/stable/services/app_registry.rb +31 -19
- data/lib/stable/services/app_remover.rb +1 -0
- data/lib/stable/services/app_restarter.rb +1 -0
- data/lib/stable/services/app_starter.rb +28 -4
- data/lib/stable/services/app_stopper.rb +1 -0
- data/lib/stable/services/caddy_manager.rb +1 -0
- data/lib/stable/services/database/base.rb +1 -0
- data/lib/stable/services/database/mysql.rb +1 -0
- data/lib/stable/services/database/postgres.rb +1 -0
- data/lib/stable/services/dependency_checker.rb +58 -9
- data/lib/stable/services/hosts_manager.rb +18 -7
- data/lib/stable/services/process_manager.rb +50 -7
- data/lib/stable/services/ruby.rb +1 -0
- data/lib/stable/services/setup_runner.rb +110 -16
- data/lib/stable/system/shell.rb +1 -0
- data/lib/stable/utils/package_manager.rb +68 -0
- data/lib/stable/utils/platform.rb +105 -0
- data/lib/stable/utils/prompts.rb +1 -0
- data/lib/stable/validators/app_name.rb +1 -0
- data/lib/stable.rb +1 -0
- metadata +6 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55fbeca00cb3d5ac0cf360dd86a8022081e3bf8b00af321c8ac8dec7482e7e5b
|
|
4
|
+
data.tar.gz: a1d3fa2c7d8ffb2ae339e75d4ebdd1aa67e4b1e7b88ef0377ce8d06dc170c94d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e90d7e10d507ea30367e375dc99576f0b9902d77f9d7d2facf45e16fe116fff5eaac586eacec099b664b31f05f25dc33ec83db2d5b89926b0bb1fcf196588c8
|
|
7
|
+
data.tar.gz: 81a7739540c1b55e429867f7c7efd22af8a96559b239710fb290179c67d8ca67e1c6e5fc4448d3f6380cedf61b62fae4c1b737c5f98d532848c63652f2c05dcf
|
data/lib/stable/bootstrap.rb
CHANGED
data/lib/stable/cli.rb
CHANGED
|
@@ -9,12 +9,12 @@ require_relative 'scanner'
|
|
|
9
9
|
require_relative 'registry'
|
|
10
10
|
|
|
11
11
|
module Stable
|
|
12
|
+
# Main CLI class for the Stable command-line interface
|
|
12
13
|
class CLI < Thor
|
|
13
14
|
def initialize(*)
|
|
14
15
|
super
|
|
15
16
|
Stable::Bootstrap.run!
|
|
16
17
|
Services::SetupRunner.ensure_dependencies!
|
|
17
|
-
dedupe_registry!
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def self.exit_on_failure?
|
|
@@ -156,11 +156,7 @@ module Stable
|
|
|
156
156
|
end
|
|
157
157
|
|
|
158
158
|
def port_in_use?(port)
|
|
159
|
-
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def dedupe_registry!
|
|
163
|
-
Services::AppRegistry.dedupe
|
|
159
|
+
Stable::Utils::Platform.port_in_use?(port)
|
|
164
160
|
end
|
|
165
161
|
end
|
|
166
162
|
end
|
data/lib/stable/commands/list.rb
CHANGED
|
@@ -1,47 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../utils/platform'
|
|
4
|
+
|
|
3
5
|
module Stable
|
|
4
6
|
module Commands
|
|
7
|
+
# List command - displays all registered applications
|
|
5
8
|
class List
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
def call
|
|
10
|
+
apps = Services::AppRegistry.all
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
if apps.empty?
|
|
13
|
+
puts 'No apps registered.'
|
|
14
|
+
return
|
|
15
|
+
end
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
print_header
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
apps.each do |app|
|
|
20
|
+
# Determine status based on whether the app is actually running (port check)
|
|
21
|
+
status = app_running?(app) ? 'running' : 'stopped'
|
|
22
|
+
puts format_row(app, status)
|
|
19
23
|
end
|
|
24
|
+
end
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
private
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
puts '-' * 78
|
|
26
|
-
end
|
|
28
|
+
def app_running?(app)
|
|
29
|
+
return false unless app && app[:port]
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
# Check if something is listening on the app's port (cross-platform)
|
|
32
|
+
Stable::Utils::Platform.port_in_use?(app[:port])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def format_row(app, status)
|
|
36
|
+
format(
|
|
37
|
+
'%<name>-18s %<domain>-26s %<port>-8s %<ruby>-10s %<status>-10s',
|
|
38
|
+
name: app[:name],
|
|
39
|
+
domain: app[:domain],
|
|
40
|
+
port: app[:port],
|
|
41
|
+
ruby: app[:ruby],
|
|
42
|
+
status: status
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def print_header
|
|
47
|
+
puts 'APP DOMAIN PORT RUBY STATUS '
|
|
48
|
+
puts '-' * 78
|
|
49
|
+
end
|
|
45
50
|
end
|
|
46
51
|
end
|
|
47
52
|
end
|
data/lib/stable/commands/new.rb
CHANGED
data/lib/stable/commands/stop.rb
CHANGED
data/lib/stable/db_manager.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Stable
|
|
4
|
+
# Database management utilities for Rails applications
|
|
4
5
|
class DBManager
|
|
5
6
|
attr_reader :name, :adapter
|
|
6
7
|
|
|
@@ -101,16 +102,30 @@ module Stable
|
|
|
101
102
|
)) or abort('Failed to repair MySQL root authentication')
|
|
102
103
|
end
|
|
103
104
|
|
|
104
|
-
# Detect MySQL socket on macOS / Linux
|
|
105
|
+
# Detect MySQL socket on macOS / Linux / Windows
|
|
105
106
|
def mysql_socket
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
platform = RUBY_PLATFORM
|
|
108
|
+
|
|
109
|
+
paths = case platform
|
|
110
|
+
when /darwin/ # macOS
|
|
111
|
+
[
|
|
112
|
+
'/opt/homebrew/var/mysql/mysql.sock', # Homebrew macOS
|
|
113
|
+
'/tmp/mysql.sock' # Fallback
|
|
114
|
+
]
|
|
115
|
+
when /linux/
|
|
116
|
+
[
|
|
117
|
+
'/var/run/mysqld/mysqld.sock', # Linux default
|
|
118
|
+
'/tmp/mysql.sock' # Fallback
|
|
119
|
+
]
|
|
120
|
+
when /mingw|mswin|win32/ # Windows
|
|
121
|
+
# Windows typically uses TCP connections, not sockets
|
|
122
|
+
return nil
|
|
123
|
+
else
|
|
124
|
+
['/tmp/mysql.sock'] # Generic fallback
|
|
125
|
+
end
|
|
111
126
|
|
|
112
127
|
paths.each { |p| return p if File.exist?(p) }
|
|
113
|
-
abort 'MySQL socket not found. Is MySQL running?'
|
|
128
|
+
abort 'MySQL socket not found. Is MySQL running? (On Windows, ensure MySQL is configured for TCP connections)'
|
|
114
129
|
end
|
|
115
130
|
|
|
116
131
|
# Default Rails DB user
|
data/lib/stable/paths.rb
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Stable
|
|
4
|
+
# Path utilities for Stable configuration and data directories
|
|
4
5
|
module Paths
|
|
5
6
|
def self.root
|
|
6
|
-
File.expand_path('~/StableCaddy')
|
|
7
|
+
ENV['STABLE_TEST_ROOT'] || File.expand_path('~/StableCaddy')
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def self.caddy_dir
|
|
@@ -21,5 +22,13 @@ module Stable
|
|
|
21
22
|
def self.apps_file
|
|
22
23
|
File.join(root, 'apps.yml')
|
|
23
24
|
end
|
|
25
|
+
|
|
26
|
+
def self.projects_dir
|
|
27
|
+
File.join(root, 'projects')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.app_config_file(app_name)
|
|
31
|
+
File.join(projects_dir, app_name, "#{app_name}.yml")
|
|
32
|
+
end
|
|
24
33
|
end
|
|
25
34
|
end
|
data/lib/stable/registry.rb
CHANGED
|
@@ -4,17 +4,39 @@ require 'yaml'
|
|
|
4
4
|
require 'fileutils'
|
|
5
5
|
|
|
6
6
|
module Stable
|
|
7
|
+
# Application registry for managing Rails app configurations
|
|
7
8
|
class Registry
|
|
8
|
-
def self.
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
def self.apps
|
|
10
|
+
apps = []
|
|
11
|
+
|
|
12
|
+
# Read legacy apps.yml file for backward compatibility
|
|
13
|
+
legacy_file = Stable::Paths.apps_file
|
|
14
|
+
if File.exist?(legacy_file)
|
|
15
|
+
legacy_apps = load_legacy_apps(legacy_file)
|
|
16
|
+
apps.concat(legacy_apps)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Read individual app config files from projects directory
|
|
20
|
+
projects_dir = Stable::Paths.projects_dir
|
|
21
|
+
if Dir.exist?(projects_dir)
|
|
22
|
+
Dir.glob(File.join(projects_dir, '*/')).each do |app_dir|
|
|
23
|
+
app_name = File.basename(app_dir)
|
|
24
|
+
config_file = Stable::Paths.app_config_file(app_name)
|
|
25
|
+
|
|
26
|
+
next unless File.exist?(config_file)
|
|
11
27
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
28
|
+
# Skip if we already have this app from legacy file
|
|
29
|
+
next if apps.any? { |app| app[:name] == app_name }
|
|
30
|
+
|
|
31
|
+
app_config = load_app_config(app_name)
|
|
32
|
+
apps << app_config if app_config
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
apps
|
|
15
37
|
end
|
|
16
38
|
|
|
17
|
-
def self.
|
|
39
|
+
def self.load_legacy_apps(file_path)
|
|
18
40
|
return [] unless File.exist?(file_path)
|
|
19
41
|
|
|
20
42
|
data = YAML.load_file(file_path) || []
|
|
@@ -27,5 +49,33 @@ module Stable
|
|
|
27
49
|
end
|
|
28
50
|
end
|
|
29
51
|
end
|
|
52
|
+
|
|
53
|
+
def self.save_app_config(app_name, config)
|
|
54
|
+
config_file = Stable::Paths.app_config_file(app_name)
|
|
55
|
+
FileUtils.mkdir_p(File.dirname(config_file))
|
|
56
|
+
File.write(config_file, config.to_yaml)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.load_app_config(app_name)
|
|
60
|
+
config_file = Stable::Paths.app_config_file(app_name)
|
|
61
|
+
return nil unless File.exist?(config_file)
|
|
62
|
+
|
|
63
|
+
parse_config_file(config_file)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.remove_app_config(app_name)
|
|
67
|
+
config_file = Stable::Paths.app_config_file(app_name)
|
|
68
|
+
FileUtils.rm_f(config_file)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.parse_config_file(config_file)
|
|
72
|
+
data = YAML.load_file(config_file)
|
|
73
|
+
return nil unless data.is_a?(Hash)
|
|
74
|
+
|
|
75
|
+
data.each_with_object({}) do |(k, v), memo|
|
|
76
|
+
key = k.is_a?(String) ? k.to_sym : k
|
|
77
|
+
memo[key] = v
|
|
78
|
+
end
|
|
79
|
+
end
|
|
30
80
|
end
|
|
31
81
|
end
|
data/lib/stable/scanner.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Stable
|
|
4
4
|
module Services
|
|
5
|
+
# Service for creating new Rails applications
|
|
5
6
|
class AppCreator
|
|
6
7
|
def initialize(name, options)
|
|
7
8
|
@name = name
|
|
@@ -11,17 +12,19 @@ module Stable
|
|
|
11
12
|
def call
|
|
12
13
|
ruby = @options[:ruby] || RUBY_VERSION
|
|
13
14
|
port = @options[:port] || next_free_port
|
|
14
|
-
app_path = File.
|
|
15
|
+
app_path = File.join(Stable::Paths.projects_dir, @name)
|
|
15
16
|
domain = "#{@name}.test"
|
|
16
17
|
|
|
18
|
+
# --- Check if app already exists ---
|
|
19
|
+
config_file = Stable::Paths.app_config_file(@name)
|
|
20
|
+
abort "App '#{@name}' already exists" if File.exist?(config_file)
|
|
21
|
+
|
|
17
22
|
# --- Register app in registry ---
|
|
18
23
|
Services::AppRegistry.add(
|
|
19
24
|
name: @name, path: app_path, domain: domain, port: port,
|
|
20
25
|
ruby: ruby, started_at: nil, pid: nil
|
|
21
26
|
)
|
|
22
27
|
|
|
23
|
-
abort "Folder already exists: #{app_path}" if File.exist?(app_path)
|
|
24
|
-
|
|
25
28
|
# --- Ensure Ruby version & RVM ---
|
|
26
29
|
Ruby.ensure_version(ruby)
|
|
27
30
|
Ruby.ensure_rvm!
|
|
@@ -45,11 +48,9 @@ module Stable
|
|
|
45
48
|
|
|
46
49
|
# --- Create Rails app ---
|
|
47
50
|
puts "Creating Rails app #{@name} (Ruby #{ruby})..."
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
--skip-javascript \
|
|
52
|
-
--skip-solid'")
|
|
51
|
+
rails_cmd = "#{rvm_cmd} rails new #{app_path} " \
|
|
52
|
+
'--skip-importmap --skip-hotwire --skip-javascript --skip-solid'
|
|
53
|
+
System::Shell.run("bash -lc '#{rails_cmd}'")
|
|
53
54
|
|
|
54
55
|
# --- Write ruby version/gemset ---
|
|
55
56
|
Dir.chdir(app_path) do
|
|
@@ -82,7 +83,8 @@ module Stable
|
|
|
82
83
|
System::Shell.run(rvm_run('bundle install --jobs=4 --retry=3', chdir: app_path))
|
|
83
84
|
System::Shell.run(rvm_run('bundle exec rails db:prepare', chdir: app_path))
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
rails_check = "cd #{app_path} && #{rvm_cmd} bundle exec rails runner \"puts Rails.version\""
|
|
87
|
+
rails_version = `bash -lc '#{rails_check}'`.strip.to_f
|
|
86
88
|
|
|
87
89
|
begin
|
|
88
90
|
rails_ver = Gem::Version.new(rails_version)
|
|
@@ -190,7 +192,7 @@ module Stable
|
|
|
190
192
|
end
|
|
191
193
|
|
|
192
194
|
def port_in_use?(port)
|
|
193
|
-
|
|
195
|
+
Stable::Utils::Platform.port_in_use?(port)
|
|
194
196
|
end
|
|
195
197
|
|
|
196
198
|
def wait_for_port(port, timeout: 20)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Stable
|
|
4
4
|
module Services
|
|
5
|
+
# Application registry management service
|
|
5
6
|
class AppRegistry
|
|
6
7
|
class << self
|
|
7
8
|
def all
|
|
@@ -42,32 +43,43 @@ module Stable
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def add(app)
|
|
45
|
-
|
|
46
|
-
apps.reject! { |a| a[:name] == app[:name] }
|
|
47
|
-
apps << app
|
|
48
|
-
Stable::Registry.save(apps)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Remove duplicate app entries (by name) and persist the canonical list
|
|
52
|
-
def dedupe
|
|
53
|
-
apps = Stable::Registry.apps
|
|
54
|
-
apps.uniq! { |a| a[:name] }
|
|
55
|
-
Stable::Registry.save(apps)
|
|
56
|
-
apps
|
|
46
|
+
Stable::Registry.save_app_config(app[:name], app)
|
|
57
47
|
end
|
|
58
48
|
|
|
59
49
|
def remove(name)
|
|
60
|
-
|
|
61
|
-
Stable::Registry.save(apps)
|
|
50
|
+
Stable::Registry.remove_app_config(name)
|
|
62
51
|
end
|
|
63
52
|
|
|
64
53
|
def update(name, attrs)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
app = find(name)
|
|
55
|
+
return unless app
|
|
56
|
+
|
|
57
|
+
updated_app = app.merge(attrs)
|
|
58
|
+
|
|
59
|
+
# Check if this is a legacy app (from apps.yml) or new format app
|
|
60
|
+
config_file = Stable::Paths.app_config_file(name)
|
|
61
|
+
if File.exist?(config_file)
|
|
62
|
+
# New format: update individual config file
|
|
63
|
+
Stable::Registry.save_app_config(name, updated_app)
|
|
64
|
+
else
|
|
65
|
+
# Legacy format: update apps.yml file
|
|
66
|
+
update_legacy_app(name, updated_app)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def update_legacy_app(name, updated_app)
|
|
71
|
+
legacy_file = Stable::Paths.apps_file
|
|
72
|
+
return unless File.exist?(legacy_file)
|
|
73
|
+
|
|
74
|
+
data = YAML.load_file(legacy_file) || []
|
|
75
|
+
idx = data.find_index { |app| app['name'] == name || app[:name] == name }
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
if idx
|
|
78
|
+
# Convert symbols to strings for YAML compatibility
|
|
79
|
+
legacy_format = updated_app.transform_keys(&:to_s)
|
|
80
|
+
data[idx] = legacy_format
|
|
81
|
+
File.write(legacy_file, data.to_yaml)
|
|
82
|
+
end
|
|
71
83
|
end
|
|
72
84
|
|
|
73
85
|
def mark_stopped(name)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Stable
|
|
4
4
|
module Services
|
|
5
|
+
# Service for starting Rails applications
|
|
5
6
|
class AppStarter
|
|
6
7
|
def initialize(name)
|
|
7
8
|
@name = name
|
|
@@ -20,6 +21,14 @@ module Stable
|
|
|
20
21
|
|
|
21
22
|
if app_running?(app)
|
|
22
23
|
puts "#{@name} is already running on https://#{app[:domain]} (port #{port})"
|
|
24
|
+
# Update the registry with the correct PID if it's missing
|
|
25
|
+
if !app[:pid] || !app[:started_at]
|
|
26
|
+
rails_pid = find_rails_pid(port)
|
|
27
|
+
if rails_pid
|
|
28
|
+
AppRegistry.update(app[:name], started_at: Time.now.to_i, pid: rails_pid)
|
|
29
|
+
puts "Updated registry with correct PID (#{rails_pid})"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
23
32
|
return
|
|
24
33
|
end
|
|
25
34
|
|
|
@@ -50,7 +59,9 @@ module Stable
|
|
|
50
59
|
|
|
51
60
|
wait_for_port(port, timeout: 30)
|
|
52
61
|
|
|
53
|
-
|
|
62
|
+
# Find the actual Rails process PID by checking what's listening on the port
|
|
63
|
+
rails_pid = find_rails_pid(port)
|
|
64
|
+
AppRegistry.update(app[:name], started_at: Time.now.to_i, pid: rails_pid)
|
|
54
65
|
|
|
55
66
|
Stable::Services::CaddyManager.add_app(app[:name], skip_ssl: false)
|
|
56
67
|
Stable::Services::CaddyManager.reload
|
|
@@ -68,7 +79,7 @@ module Stable
|
|
|
68
79
|
end
|
|
69
80
|
|
|
70
81
|
def port_in_use?(port)
|
|
71
|
-
|
|
82
|
+
Stable::Utils::Platform.port_in_use?(port)
|
|
72
83
|
end
|
|
73
84
|
|
|
74
85
|
def wait_for_port(port, timeout: 20)
|
|
@@ -86,9 +97,22 @@ module Stable
|
|
|
86
97
|
end
|
|
87
98
|
|
|
88
99
|
def app_running?(app)
|
|
89
|
-
return false unless app
|
|
100
|
+
return false unless app
|
|
101
|
+
|
|
102
|
+
# First check if we have PID info and if the process is alive
|
|
103
|
+
if app[:pid] && app[:started_at]
|
|
104
|
+
return ProcessManager.pid_alive?(app[:pid])
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Fallback to port checking if no PID info available
|
|
108
|
+
return false unless app[:port]
|
|
109
|
+
|
|
110
|
+
Stable::Utils::Platform.port_in_use?(app[:port])
|
|
111
|
+
end
|
|
90
112
|
|
|
91
|
-
|
|
113
|
+
def find_rails_pid(port)
|
|
114
|
+
pids = Stable::Utils::Platform.find_pids_by_port(port)
|
|
115
|
+
pids.first
|
|
92
116
|
end
|
|
93
117
|
end
|
|
94
118
|
end
|