stable-cli-rails 0.7.11 → 0.8.0
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 -5
- data/lib/stable/commands/doctor.rb +1 -0
- data/lib/stable/commands/list.rb +7 -6
- 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 +118 -61
- data/lib/stable/services/app_registry.rb +7 -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 +1 -0
- 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 +12 -2
- 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 +1 -0
- data/lib/stable/services/ruby.rb +10 -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 +76 -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: ab8c647151029030baf0e96737f45f20cc6b067b952b032b98eac46e5e14e158
|
|
4
|
+
data.tar.gz: 5db558cabc869e1f6d6684d26b1d7b08178679194d09ebdbbd42aa67d828aee7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe68968ac73be6725262a224ceda502eaee576c2aa3a8822a2742577e9f5d6fc05f1c3092c6f7f1c0f011218aaa851949863e3473e8ea95de9e6fd91b94f31dc
|
|
7
|
+
data.tar.gz: e333ac1da62ceb50a7a77ec83d1c145900f90b8888249c4307102bc1dfb0e3661e0bf97c1efe5a33be27b1aa409e639278ca91643989c3c2c0954d763c665533
|
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?
|
|
@@ -29,6 +29,7 @@ module Stable
|
|
|
29
29
|
method_option :db, type: :string, desc: 'Database name to create and integrate'
|
|
30
30
|
method_option :postgres, type: :boolean, default: false, desc: 'Use Postgres for the database'
|
|
31
31
|
method_option :mysql, type: :boolean, default: false, desc: 'Use MySQL for the database'
|
|
32
|
+
method_option :full, type: :boolean, default: false, desc: 'Setup full Rails app common generators'
|
|
32
33
|
def new(name, ruby: RUBY_VERSION, rails: nil, port: nil)
|
|
33
34
|
safe_name = Validators::AppName.call!(name)
|
|
34
35
|
Commands::New.new(safe_name, options).call
|
|
@@ -157,9 +158,5 @@ module Stable
|
|
|
157
158
|
def port_in_use?(port)
|
|
158
159
|
system("lsof -i tcp:#{port} > /dev/null 2>&1")
|
|
159
160
|
end
|
|
160
|
-
|
|
161
|
-
def dedupe_registry!
|
|
162
|
-
Services::AppRegistry.dedupe
|
|
163
|
-
end
|
|
164
161
|
end
|
|
165
162
|
end
|
data/lib/stable/commands/list.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Stable
|
|
4
4
|
module Commands
|
|
5
|
+
# List command - displays all registered applications
|
|
5
6
|
class List
|
|
6
7
|
def call
|
|
7
8
|
apps = Services::AppRegistry.all
|
|
@@ -34,12 +35,12 @@ module Stable
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
format(
|
|
37
|
-
'
|
|
38
|
-
app[:name],
|
|
39
|
-
app[:domain],
|
|
40
|
-
app[:port],
|
|
41
|
-
app[:ruby],
|
|
42
|
-
status
|
|
38
|
+
'%<name>-18s %<domain>-26s %<port>-8s %<ruby>-10s %<status>-10s',
|
|
39
|
+
name: app[:name],
|
|
40
|
+
domain: app[:domain],
|
|
41
|
+
port: app[:port],
|
|
42
|
+
ruby: app[:ruby],
|
|
43
|
+
status: status
|
|
43
44
|
)
|
|
44
45
|
end
|
|
45
46
|
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,59 +12,58 @@ 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
|
|
|
17
|
-
# ---
|
|
18
|
-
|
|
19
|
-
|
|
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)
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
# --- Register app in registry ---
|
|
23
|
+
Services::AppRegistry.add(
|
|
24
|
+
name: @name, path: app_path, domain: domain, port: port,
|
|
25
|
+
ruby: ruby, started_at: nil, pid: nil
|
|
26
|
+
)
|
|
22
27
|
|
|
23
|
-
# --- Ensure Ruby
|
|
28
|
+
# --- Ensure Ruby version & RVM ---
|
|
24
29
|
Ruby.ensure_version(ruby)
|
|
25
30
|
Ruby.ensure_rvm!
|
|
26
|
-
|
|
31
|
+
|
|
32
|
+
# --- Create gemset ---
|
|
33
|
+
System::Shell.run("bash -lc 'source #{Ruby.rvm_script} && rvm #{ruby} do rvm gemset create #{@name} || true'")
|
|
27
34
|
|
|
28
35
|
rvm_cmd = Ruby.rvm_prefix(ruby, @name)
|
|
29
36
|
|
|
30
|
-
# --- Install
|
|
31
|
-
|
|
32
|
-
rails_check_cmd = if rails_version
|
|
33
|
-
"#{rvm_cmd} gem list -i rails -v #{rails_version}"
|
|
34
|
-
else
|
|
35
|
-
"#{rvm_cmd} gem list -i rails"
|
|
36
|
-
end
|
|
37
|
+
# --- Install Bundler ---
|
|
38
|
+
System::Shell.run("bash -lc '#{rvm_cmd} gem install bundler --no-document'")
|
|
37
39
|
|
|
40
|
+
# --- Install Rails if missing ---
|
|
41
|
+
rails_version = @options[:rails]
|
|
42
|
+
rails_check_cmd = rails_version ? "#{rvm_cmd} gem list -i rails -v #{rails_version}" : "#{rvm_cmd} gem list -i rails"
|
|
38
43
|
unless system("bash -lc '#{rails_check_cmd}'")
|
|
39
|
-
puts "Installing Rails #{rails_version || 'latest'}
|
|
44
|
+
puts "Installing Rails #{rails_version || 'latest'}..."
|
|
40
45
|
install_cmd = rails_version ? "#{rvm_cmd} gem install rails -v #{rails_version}" : "#{rvm_cmd} gem install rails"
|
|
41
|
-
system("bash -lc '#{install_cmd}'") or abort(
|
|
46
|
+
system("bash -lc '#{install_cmd}'") or abort("Failed to install Rails #{rails_version || ''}")
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
# --- Create Rails app ---
|
|
45
50
|
puts "Creating Rails app #{@name} (Ruby #{ruby})..."
|
|
46
|
-
|
|
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}'")
|
|
47
54
|
|
|
48
|
-
# --- Write ruby version/gemset
|
|
55
|
+
# --- Write ruby version/gemset ---
|
|
49
56
|
Dir.chdir(app_path) do
|
|
50
57
|
File.write('.ruby-version', "#{ruby}\n")
|
|
51
58
|
File.write('.ruby-gemset', "#{@name}\n")
|
|
52
|
-
|
|
53
|
-
puts 'Running bundle install...'
|
|
54
|
-
System::Shell.run("bash -lc '#{rvm_cmd} bundle install --jobs=4 --retry=3'")
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
# --- Database integration ---
|
|
58
62
|
if @options[:db]
|
|
59
|
-
adapter =
|
|
60
|
-
:mysql
|
|
61
|
-
else
|
|
62
|
-
:postgresql
|
|
63
|
-
end
|
|
64
|
-
|
|
63
|
+
adapter = @options[:mysql] ? :mysql : :postgresql
|
|
65
64
|
gem_name = adapter == :postgresql ? 'pg' : 'mysql2'
|
|
66
65
|
gemfile_path = File.join(app_path, 'Gemfile')
|
|
66
|
+
|
|
67
67
|
unless File.read(gemfile_path).include?(gem_name)
|
|
68
68
|
File.open(gemfile_path, 'a') do |f|
|
|
69
69
|
f.puts "\n# Added by Stable CLI"
|
|
@@ -72,17 +72,86 @@ module Stable
|
|
|
72
72
|
puts "✅ Added '#{gem_name}' gem to Gemfile"
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
#
|
|
76
|
-
System::Shell.run(
|
|
75
|
+
# Use correct Ruby/gemset for bundle install
|
|
76
|
+
System::Shell.run(rvm_run('bundle install --jobs=4 --retry=3', chdir: app_path))
|
|
77
77
|
|
|
78
|
-
# run adapter setup which will write database.yml and prepare
|
|
79
78
|
db_adapter = adapter == :mysql ? Database::MySQL : Database::Postgres
|
|
80
|
-
db_adapter.new(app_name: @name, app_path: app_path).setup
|
|
79
|
+
db_adapter.new(app_name: @name, app_path: app_path, ruby: ruby).setup
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# --- Run bundle & DB prepare ---
|
|
83
|
+
System::Shell.run(rvm_run('bundle install --jobs=4 --retry=3', chdir: app_path))
|
|
84
|
+
System::Shell.run(rvm_run('bundle exec rails db:prepare', chdir: app_path))
|
|
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
|
|
88
|
+
|
|
89
|
+
begin
|
|
90
|
+
rails_ver = Gem::Version.new(rails_version)
|
|
91
|
+
rescue ArgumentError
|
|
92
|
+
rails_ver = Gem::Version.new('0.0.0')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# only if Rails >= 7
|
|
96
|
+
if rails_ver >= Gem::Version.new('7.0.0')
|
|
97
|
+
# Gems to re-add
|
|
98
|
+
optional_gems = {
|
|
99
|
+
'importmap-rails' => nil,
|
|
100
|
+
'turbo-rails' => nil,
|
|
101
|
+
'stimulus-rails' => nil,
|
|
102
|
+
'solid_cache' => nil,
|
|
103
|
+
'solid_queue' => nil,
|
|
104
|
+
'solid_cable' => nil
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
gemfile = File.join(app_path, 'Gemfile')
|
|
108
|
+
|
|
109
|
+
# Add gems if not already present
|
|
110
|
+
optional_gems.each do |gem_name, version|
|
|
111
|
+
next if File.read(gemfile).match?(/^gem ['"]#{gem_name}['"]/)
|
|
112
|
+
|
|
113
|
+
File.open(gemfile, 'a') do |f|
|
|
114
|
+
f.puts version ? "gem '#{gem_name}', '#{version}'" : "gem '#{gem_name}'"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Install all new gems
|
|
119
|
+
System::Shell.run(rvm_run('bundle install', chdir: app_path))
|
|
120
|
+
|
|
121
|
+
# Run generators for installed gems
|
|
122
|
+
generators = [
|
|
123
|
+
'importmap:install',
|
|
124
|
+
'turbo:install stimulus:install',
|
|
125
|
+
'solid_cache:install solid_queue:install solid_cable:install'
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
generators.each do |cmd|
|
|
129
|
+
System::Shell.run(rvm_run("bundle exec rails #{cmd}", chdir: app_path))
|
|
130
|
+
end
|
|
81
131
|
end
|
|
82
132
|
|
|
83
|
-
# ---
|
|
84
|
-
|
|
85
|
-
|
|
133
|
+
# --- Add allowed host for Rails < 7.2 ---
|
|
134
|
+
if @options[:rails] && @options[:rails].to_f < 7.2
|
|
135
|
+
env_file = File.join(app_path, 'config/environments/development.rb')
|
|
136
|
+
|
|
137
|
+
if File.exist?(env_file)
|
|
138
|
+
content = File.read(env_file)
|
|
139
|
+
|
|
140
|
+
# Append host config inside the Rails.application.configure block
|
|
141
|
+
updated_content = content.gsub(/Rails\.application\.configure do(.*?)end/mm) do |_match|
|
|
142
|
+
inner = Regexp.last_match(1)
|
|
143
|
+
# Prevent duplicate entries
|
|
144
|
+
unless inner.include?(domain)
|
|
145
|
+
inner += " # allow local .test host for this app\n config.hosts << '#{domain}'\n"
|
|
146
|
+
end
|
|
147
|
+
"Rails.application.configure do#{inner}end"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
File.write(env_file, updated_content)
|
|
151
|
+
else
|
|
152
|
+
warn "Development environment file not found: #{env_file}"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
86
155
|
|
|
87
156
|
# --- Hosts, certs, caddy ---
|
|
88
157
|
Services::HostsManager.add(domain)
|
|
@@ -90,50 +159,31 @@ module Stable
|
|
|
90
159
|
Services::CaddyManager.ensure_running!
|
|
91
160
|
Services::CaddyManager.reload
|
|
92
161
|
|
|
93
|
-
# --- Start
|
|
162
|
+
# --- Start Rails server ---
|
|
94
163
|
puts "Starting Rails server for #{@name} on port #{port}..."
|
|
95
164
|
log_file = File.join(app_path, 'log', 'stable.log')
|
|
96
165
|
FileUtils.mkdir_p(File.dirname(log_file))
|
|
97
166
|
|
|
98
167
|
abort "Port #{port} is already in use. Choose another port." if port_in_use?(port)
|
|
99
168
|
|
|
100
|
-
pid = spawn(
|
|
101
|
-
|
|
169
|
+
pid = spawn(
|
|
170
|
+
'bash', '-lc',
|
|
171
|
+
"cd \"#{app_path}\" && #{rvm_cmd} bundle exec rails s -p #{port} -b 127.0.0.1",
|
|
172
|
+
out: log_file, err: log_file
|
|
173
|
+
)
|
|
102
174
|
Process.detach(pid)
|
|
103
|
-
|
|
104
175
|
AppRegistry.update(@name, started_at: Time.now.to_i, pid: pid)
|
|
105
176
|
|
|
106
177
|
sleep 1.5
|
|
107
178
|
wait_for_port(port)
|
|
108
|
-
prefix = @options[:skip_ssl] ? 'http' : 'https'
|
|
109
|
-
display_domain = if @options[:skip_ssl]
|
|
110
|
-
"#{domain}:#{port}"
|
|
111
|
-
else
|
|
112
|
-
domain
|
|
113
|
-
end
|
|
114
179
|
|
|
180
|
+
prefix = @options[:skip_ssl] ? 'http' : 'https'
|
|
181
|
+
display_domain = @options[:skip_ssl] ? "#{domain}:#{port}" : domain
|
|
115
182
|
puts "✔ #{@name} running at #{prefix}://#{display_domain}"
|
|
116
183
|
end
|
|
117
184
|
|
|
118
185
|
private
|
|
119
186
|
|
|
120
|
-
def create_rails_app
|
|
121
|
-
System::Shell.run("rails new #{@name}")
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def setup_database
|
|
125
|
-
return unless @options[:mysql] || @options[:postgres]
|
|
126
|
-
|
|
127
|
-
adapter =
|
|
128
|
-
if @options[:mysql]
|
|
129
|
-
Database::MySQL
|
|
130
|
-
else
|
|
131
|
-
Database::Postgres
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
adapter.new(@name).setup
|
|
135
|
-
end
|
|
136
|
-
|
|
137
187
|
def next_free_port
|
|
138
188
|
used_ports = Services::AppRegistry.all.map { |a| a[:port] }
|
|
139
189
|
port = 3000
|
|
@@ -158,6 +208,13 @@ module Stable
|
|
|
158
208
|
sleep 0.5
|
|
159
209
|
end
|
|
160
210
|
end
|
|
211
|
+
|
|
212
|
+
def rvm_run(cmd, chdir: nil, ruby: nil)
|
|
213
|
+
ruby ||= @options[:ruby] || RUBY_VERSION
|
|
214
|
+
gemset = @name
|
|
215
|
+
cd = chdir ? "cd #{chdir} && " : ''
|
|
216
|
+
"bash -lc '#{cd}source #{Dir.home}/.rvm/scripts/rvm && rvm #{ruby}@#{gemset} do #{cmd}'"
|
|
217
|
+
end
|
|
161
218
|
end
|
|
162
219
|
end
|
|
163
220
|
end
|
|
@@ -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,19 @@ 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
|
-
return unless idx
|
|
54
|
+
app = find(name)
|
|
55
|
+
return unless app
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
Stable::Registry.
|
|
57
|
+
updated_app = app.merge(attrs)
|
|
58
|
+
Stable::Registry.save_app_config(name, updated_app)
|
|
71
59
|
end
|
|
72
60
|
|
|
73
61
|
def mark_stopped(name)
|