stable-cli-rails 0.8.1 ā 0.8.3
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/cli.rb +9 -20
- data/lib/stable/commands/destroy.rb +105 -0
- data/lib/stable/commands/list.rb +32 -32
- data/lib/stable/commands/upgrade_ruby.rb +163 -0
- data/lib/stable/registry.rb +15 -0
- data/lib/stable/services/app_registry.rb +6 -6
- data/lib/stable/services/app_starter.rb +1 -3
- data/lib/stable/services/caddy_manager.rb +8 -2
- data/lib/stable/services/process_manager.rb +6 -4
- data/lib/stable/services/setup_runner.rb +4 -0
- data/lib/stable/utils/platform.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: adfc3784b0a153d9a5368139267d77c0cfa8a32b71d72e8da43ae5a454220f6a
|
|
4
|
+
data.tar.gz: 861823110de96d0e4d1c8297f13ff2f9630a25649d201f921aaa64f43849888c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9651b1cd3c7127223a5587f31e70175fdf2be8e9ccd8c3e17347b73a1364b9404eda928d7e314513bf5a416b8acf90c31a14eb2d6e7ec719d6f7576887dd933d
|
|
7
|
+
data.tar.gz: 9ab4288661598a9349301e1fa1f96007b0eafdc78eb66886e26f9b555ecf871d55f07fdb9b2ce529d59aff25ee287e655e68a3a5bce001d38c6621eae6c73736
|
data/lib/stable/cli.rb
CHANGED
|
@@ -13,6 +13,8 @@ module Stable
|
|
|
13
13
|
class CLI < Thor
|
|
14
14
|
def initialize(*)
|
|
15
15
|
super
|
|
16
|
+
return if ENV['STABLE_TEST_MODE']
|
|
17
|
+
|
|
16
18
|
Stable::Bootstrap.run!
|
|
17
19
|
Services::SetupRunner.ensure_dependencies!
|
|
18
20
|
end
|
|
@@ -75,6 +77,11 @@ module Stable
|
|
|
75
77
|
Commands::Remove.new(name).call
|
|
76
78
|
end
|
|
77
79
|
|
|
80
|
+
desc 'destroy NAME', 'Permanently delete a Rails app and all its files'
|
|
81
|
+
def destroy(name)
|
|
82
|
+
Commands::Destroy.new(name).call
|
|
83
|
+
end
|
|
84
|
+
|
|
78
85
|
desc 'start NAME', 'Start a Rails app with its correct Ruby version'
|
|
79
86
|
def start(name)
|
|
80
87
|
Commands::Start.new(name).call
|
|
@@ -123,27 +130,9 @@ module Stable
|
|
|
123
130
|
Commands::Doctor.new.call
|
|
124
131
|
end
|
|
125
132
|
|
|
126
|
-
desc 'upgrade-ruby NAME VERSION', '
|
|
133
|
+
desc 'upgrade-ruby NAME VERSION', 'Change Ruby version for an app (upgrade, downgrade, or switch)'
|
|
127
134
|
def upgrade_ruby(name, version)
|
|
128
|
-
|
|
129
|
-
unless app
|
|
130
|
-
puts "No app named #{name}"
|
|
131
|
-
return
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
if Stable::Services::Ruby.rvm_available?
|
|
135
|
-
system("bash -lc 'rvm install #{version}'")
|
|
136
|
-
elsif Stable::Services::Ruby.rbenv_available?
|
|
137
|
-
system("rbenv install #{version}")
|
|
138
|
-
else
|
|
139
|
-
puts 'No Ruby version manager found'
|
|
140
|
-
return
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
File.write(File.join(app[:path], '.ruby-version'), version)
|
|
144
|
-
Services::AppRegistry.update(name, ruby: version)
|
|
145
|
-
|
|
146
|
-
puts "#{name} now uses Ruby #{version}"
|
|
135
|
+
Commands::UpgradeRuby.new(name, version).call
|
|
147
136
|
end
|
|
148
137
|
|
|
149
138
|
private
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'io/console'
|
|
4
|
+
|
|
5
|
+
module Stable
|
|
6
|
+
module Commands
|
|
7
|
+
# Destroy command - permanently deletes a Rails application with confirmation
|
|
8
|
+
class Destroy
|
|
9
|
+
def initialize(name)
|
|
10
|
+
@name = name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
app = Services::AppRegistry.find(@name)
|
|
15
|
+
abort 'App not found' unless app
|
|
16
|
+
|
|
17
|
+
display_warning(app)
|
|
18
|
+
return unless confirm_destruction
|
|
19
|
+
|
|
20
|
+
puts "\nšļø Destroying #{@name}..."
|
|
21
|
+
perform_destruction(app)
|
|
22
|
+
puts "ā
Successfully destroyed #{@name}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def display_warning(app)
|
|
28
|
+
puts "ā ļø WARNING: This will permanently delete the application '#{@name}'"
|
|
29
|
+
puts " Path: #{app[:path]}"
|
|
30
|
+
puts " Domain: #{app[:domain]}"
|
|
31
|
+
puts ' This action CANNOT be undone!'
|
|
32
|
+
puts ''
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def confirm_destruction
|
|
36
|
+
print "Type '#{@name}' to confirm destruction: "
|
|
37
|
+
confirmation = $stdin.gets&.strip
|
|
38
|
+
puts ''
|
|
39
|
+
|
|
40
|
+
if confirmation == @name
|
|
41
|
+
true
|
|
42
|
+
else
|
|
43
|
+
puts "ā Destruction cancelled - confirmation didn't match"
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def perform_destruction(app)
|
|
49
|
+
# Stop the app if running
|
|
50
|
+
Services::ProcessManager.stop(app)
|
|
51
|
+
|
|
52
|
+
# Remove from infrastructure
|
|
53
|
+
Services::HostsManager.remove(app[:domain])
|
|
54
|
+
Services::CaddyManager.remove(app[:domain])
|
|
55
|
+
Services::AppRegistry.remove(@name)
|
|
56
|
+
|
|
57
|
+
# Clean up RVM gemset
|
|
58
|
+
cleanup_rvm_gemset(app)
|
|
59
|
+
|
|
60
|
+
# Delete the project directory
|
|
61
|
+
delete_project_directory(app[:path])
|
|
62
|
+
|
|
63
|
+
# Reload Caddy
|
|
64
|
+
Services::CaddyManager.reload
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def cleanup_rvm_gemset(app)
|
|
68
|
+
# Skip RVM operations in test mode
|
|
69
|
+
return if ENV['STABLE_TEST_MODE']
|
|
70
|
+
|
|
71
|
+
# Only clean up RVM gemsets on Unix-like systems (macOS/Linux)
|
|
72
|
+
# Windows uses different Ruby version managers
|
|
73
|
+
return unless Stable::Utils::Platform.unix?
|
|
74
|
+
|
|
75
|
+
ruby_version = app[:ruby]
|
|
76
|
+
# Handle different ruby version formats (e.g., "3.4.7", "ruby-3.4.7")
|
|
77
|
+
clean_ruby_version = ruby_version.to_s.sub(/^ruby-/, '')
|
|
78
|
+
gemset_name = "#{clean_ruby_version}@#{@name}"
|
|
79
|
+
|
|
80
|
+
puts " Cleaning up RVM gemset #{gemset_name}..."
|
|
81
|
+
begin
|
|
82
|
+
# Use system to run RVM command to delete the gemset
|
|
83
|
+
system("bash -lc 'source ~/.rvm/scripts/rvm && rvm gemset delete #{gemset_name} --force' 2>/dev/null || true")
|
|
84
|
+
puts " ā
RVM gemset #{gemset_name} cleaned up"
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
puts " ā ļø Could not clean up RVM gemset #{gemset_name}: #{e.message}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def delete_project_directory(path)
|
|
91
|
+
if ENV['STABLE_TEST_MODE']
|
|
92
|
+
puts ' Deleting project directory...'
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if File.exist?(path)
|
|
97
|
+
puts ' Deleting project directory...'
|
|
98
|
+
FileUtils.rm_rf(path)
|
|
99
|
+
else
|
|
100
|
+
puts ' Project directory not found (already deleted?)'
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
data/lib/stable/commands/list.rb
CHANGED
|
@@ -6,47 +6,47 @@ module Stable
|
|
|
6
6
|
module Commands
|
|
7
7
|
# List command - displays all registered applications
|
|
8
8
|
class List
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
def call
|
|
10
|
+
apps = Services::AppRegistry.all
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
if apps.empty?
|
|
13
|
+
puts 'No apps registered.'
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
print_header
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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)
|
|
23
|
+
end
|
|
23
24
|
end
|
|
24
|
-
end
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
private
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
def app_running?(app)
|
|
29
|
+
return false unless app && app[:port]
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
def print_header
|
|
47
|
+
puts 'APP DOMAIN PORT RUBY STATUS '
|
|
48
|
+
puts '-' * 78
|
|
49
|
+
end
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'shellwords'
|
|
4
|
+
|
|
5
|
+
module Stable
|
|
6
|
+
module Commands
|
|
7
|
+
# Command for upgrading/downgrading Ruby versions for applications
|
|
8
|
+
class UpgradeRuby
|
|
9
|
+
def initialize(name, version)
|
|
10
|
+
@name = name
|
|
11
|
+
@version = version
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
app = Services::AppRegistry.find(@name)
|
|
16
|
+
unless app
|
|
17
|
+
puts "No app named #{@name}"
|
|
18
|
+
return
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
current_version = app[:ruby] || RUBY_VERSION
|
|
22
|
+
|
|
23
|
+
puts "#{action(current_version, @version)} #{@name} from Ruby #{current_version} to #{@version}..."
|
|
24
|
+
puts ''
|
|
25
|
+
|
|
26
|
+
# Install the target Ruby version if needed
|
|
27
|
+
platform = Stable::Utils::Platform.current
|
|
28
|
+
|
|
29
|
+
if platform == :windows
|
|
30
|
+
puts 'ā ļø Windows detected - Ruby version managers work differently on Windows'
|
|
31
|
+
puts " Please manually install Ruby #{@version} using RubyInstaller or your preferred method"
|
|
32
|
+
puts ' Recommended: https://rubyinstaller.org/'
|
|
33
|
+
puts ' Then update your PATH to use the new Ruby version'
|
|
34
|
+
puts ''
|
|
35
|
+
puts "After installing Ruby #{@version}, update the app configuration manually:"
|
|
36
|
+
puts " - Edit .ruby-version file to contain: #{@version}"
|
|
37
|
+
puts ' - Run: bundle install (in the app directory)'
|
|
38
|
+
return
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if Stable::Services::Ruby.rvm_available?
|
|
42
|
+
puts "Ensuring Ruby #{@version} is available..."
|
|
43
|
+
system("bash -lc 'rvm install #{@version}'") unless ENV['STABLE_TEST_MODE']
|
|
44
|
+
elsif Stable::Services::Ruby.rbenv_available?
|
|
45
|
+
puts "Ensuring Ruby #{@version} is available..."
|
|
46
|
+
system("rbenv install #{@version}") unless ENV['STABLE_TEST_MODE']
|
|
47
|
+
else
|
|
48
|
+
puts 'ā No supported Ruby version manager found'
|
|
49
|
+
puts ' On macOS/Linux, install RVM (https://rvm.io/) or rbenv (https://github.com/rbenv/rbenv)'
|
|
50
|
+
puts ' On Windows, use RubyInstaller (https://rubyinstaller.org/)'
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Clean, simple approach: Remove current Ruby environment and install new one fresh
|
|
55
|
+
puts "š Upgrading #{@name} from Ruby #{current_version} to #{@version}..."
|
|
56
|
+
|
|
57
|
+
# 1. Remove current Ruby version/gemset (like destroy command)
|
|
58
|
+
cleanup_rvm_gemset(app)
|
|
59
|
+
|
|
60
|
+
# 2. Install new Ruby version fresh (like app creator)
|
|
61
|
+
setup_new_ruby_version(app, @version)
|
|
62
|
+
|
|
63
|
+
puts ''
|
|
64
|
+
puts "ā
#{@name} #{past_tense_action(action(current_version, @version))} to Ruby #{@version}!"
|
|
65
|
+
puts " Old gemset cleared, fresh #{@version}@#{@name} gemset created with gems"
|
|
66
|
+
puts ''
|
|
67
|
+
puts "Start with: stable start #{@name}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def cleanup_rvm_gemset(app)
|
|
73
|
+
# Skip RVM operations in test mode
|
|
74
|
+
return if ENV['STABLE_TEST_MODE']
|
|
75
|
+
|
|
76
|
+
# Only clean up RVM gemsets on Unix-like systems (macOS/Linux)
|
|
77
|
+
# Windows uses different Ruby version managers
|
|
78
|
+
return unless Stable::Utils::Platform.unix?
|
|
79
|
+
|
|
80
|
+
ruby_version = app[:ruby]
|
|
81
|
+
# Handle different ruby version formats (e.g., "3.4.7", "ruby-3.4.7")
|
|
82
|
+
clean_ruby_version = ruby_version.to_s.sub(/^ruby-/, '')
|
|
83
|
+
gemset_name = "#{clean_ruby_version}@#{@name}"
|
|
84
|
+
|
|
85
|
+
puts " Cleaning up RVM gemset #{gemset_name}..."
|
|
86
|
+
begin
|
|
87
|
+
# Use system to run RVM command to delete the gemset
|
|
88
|
+
system("bash -lc 'source ~/.rvm/scripts/rvm && rvm gemset delete #{gemset_name} --force' 2>/dev/null || true")
|
|
89
|
+
puts " ā
RVM gemset #{gemset_name} cleaned up"
|
|
90
|
+
rescue StandardError => e
|
|
91
|
+
puts " ā ļø Could not clean up RVM gemset #{gemset_name}: #{e.message}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def setup_new_ruby_version(app, new_version)
|
|
96
|
+
# Follow app_creator.rb pattern exactly
|
|
97
|
+
unless ENV['STABLE_TEST_MODE']
|
|
98
|
+
# Ensure Ruby version & RVM (like app_creator.rb)
|
|
99
|
+
Stable::Services::Ruby.ensure_version(new_version)
|
|
100
|
+
Stable::Services::Ruby.ensure_rvm!
|
|
101
|
+
|
|
102
|
+
# Create gemset (like app_creator.rb)
|
|
103
|
+
Stable::System::Shell.run("bash -lc 'source #{Stable::Services::Ruby.rvm_script} && rvm #{new_version} do rvm gemset create #{@name} || true'")
|
|
104
|
+
|
|
105
|
+
rvm_cmd = Stable::Services::Ruby.rvm_prefix(new_version, @name)
|
|
106
|
+
|
|
107
|
+
# Install Bundler (like app_creator.rb)
|
|
108
|
+
Stable::System::Shell.run("bash -lc '#{rvm_cmd} gem install bundler --no-document'")
|
|
109
|
+
|
|
110
|
+
# Run bundle install (like app_creator.rb)
|
|
111
|
+
Stable::System::Shell.run(rvm_run('bundle install --jobs=4 --retry=3', chdir: app[:path]))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Update app configuration (like app_creator.rb)
|
|
115
|
+
unless ENV['STABLE_TEST_MODE']
|
|
116
|
+
Dir.chdir(app[:path]) do
|
|
117
|
+
File.write('.ruby-version', "#{new_version}\n")
|
|
118
|
+
File.write('.ruby-gemset', "#{@name}\n")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Update registry
|
|
123
|
+
Services::AppRegistry.update(@name, ruby: new_version)
|
|
124
|
+
puts " ā
New Ruby #{new_version} environment set up with gems"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def rvm_run(cmd, chdir: nil)
|
|
128
|
+
cd = chdir ? "cd #{chdir} && " : ''
|
|
129
|
+
"bash -lc '#{cd}source #{Dir.home}/.rvm/scripts/rvm && rvm #{@version}@#{@name} do #{cmd}'"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def action(current_version, new_version)
|
|
133
|
+
current_parts = current_version.split('.').map(&:to_i)
|
|
134
|
+
new_parts = new_version.split('.').map(&:to_i)
|
|
135
|
+
|
|
136
|
+
if new_parts[0] > current_parts[0] ||
|
|
137
|
+
(new_parts[0] == current_parts[0] && new_parts[1] > current_parts[1]) ||
|
|
138
|
+
(new_parts[0] == current_parts[0] && new_parts[1] == current_parts[1] && new_parts[2] > current_parts[2])
|
|
139
|
+
'Upgrading'
|
|
140
|
+
elsif new_parts[0] < current_parts[0] ||
|
|
141
|
+
(new_parts[0] == current_parts[0] && new_parts[1] < current_parts[1]) ||
|
|
142
|
+
(new_parts[0] == current_parts[0] && new_parts[1] == current_parts[1] && new_parts[2] < current_parts[2])
|
|
143
|
+
'Downgrading'
|
|
144
|
+
else
|
|
145
|
+
'Switching'
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def past_tense_action(action)
|
|
150
|
+
case action
|
|
151
|
+
when 'Upgrading'
|
|
152
|
+
'upgraded'
|
|
153
|
+
when 'Downgrading'
|
|
154
|
+
'downgraded'
|
|
155
|
+
when 'Switching'
|
|
156
|
+
'switched'
|
|
157
|
+
else
|
|
158
|
+
'updated'
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
data/lib/stable/registry.rb
CHANGED
|
@@ -66,6 +66,21 @@ module Stable
|
|
|
66
66
|
def self.remove_app_config(app_name)
|
|
67
67
|
config_file = Stable::Paths.app_config_file(app_name)
|
|
68
68
|
FileUtils.rm_f(config_file)
|
|
69
|
+
|
|
70
|
+
# Also remove from legacy apps.yml file for backward compatibility
|
|
71
|
+
remove_from_legacy_file(app_name)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.remove_from_legacy_file(app_name)
|
|
75
|
+
legacy_file = Stable::Paths.apps_file
|
|
76
|
+
return unless File.exist?(legacy_file)
|
|
77
|
+
|
|
78
|
+
data = YAML.load_file(legacy_file) || []
|
|
79
|
+
filtered_data = data.reject { |entry| entry.is_a?(Hash) && entry['name'] == app_name }
|
|
80
|
+
|
|
81
|
+
return unless filtered_data != data
|
|
82
|
+
|
|
83
|
+
File.write(legacy_file, filtered_data.to_yaml)
|
|
69
84
|
end
|
|
70
85
|
|
|
71
86
|
def self.parse_config_file(config_file)
|
|
@@ -74,12 +74,12 @@ module Stable
|
|
|
74
74
|
data = YAML.load_file(legacy_file) || []
|
|
75
75
|
idx = data.find_index { |app| app['name'] == name || app[:name] == name }
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
return unless idx
|
|
78
|
+
|
|
79
|
+
# Convert symbols to strings for YAML compatibility
|
|
80
|
+
legacy_format = updated_app.transform_keys(&:to_s)
|
|
81
|
+
data[idx] = legacy_format
|
|
82
|
+
File.write(legacy_file, data.to_yaml)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def mark_stopped(name)
|
|
@@ -100,9 +100,7 @@ module Stable
|
|
|
100
100
|
return false unless app
|
|
101
101
|
|
|
102
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
|
|
103
|
+
return ProcessManager.pid_alive?(app[:pid]) if app[:pid] && app[:started_at]
|
|
106
104
|
|
|
107
105
|
# Fallback to port checking if no PID info available
|
|
108
106
|
return false unless app[:port]
|
|
@@ -16,7 +16,7 @@ module Stable
|
|
|
16
16
|
content = remove_domain_block(content, domain)
|
|
17
17
|
|
|
18
18
|
atomic_write(caddyfile, content)
|
|
19
|
-
system("caddy fmt --overwrite #{caddyfile}")
|
|
19
|
+
system("caddy fmt --overwrite #{caddyfile}") unless ENV['STABLE_TEST_MODE']
|
|
20
20
|
|
|
21
21
|
reload_if_running
|
|
22
22
|
end
|
|
@@ -38,12 +38,14 @@ module Stable
|
|
|
38
38
|
content << build_block(domain, port, skip_ssl: skip_ssl)
|
|
39
39
|
|
|
40
40
|
atomic_write(caddyfile, content)
|
|
41
|
-
system("caddy fmt --overwrite #{caddyfile}")
|
|
41
|
+
system("caddy fmt --overwrite #{caddyfile}") unless ENV['STABLE_TEST_MODE']
|
|
42
42
|
|
|
43
43
|
ensure_running!
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def reload
|
|
47
|
+
return if ENV['STABLE_TEST_MODE']
|
|
48
|
+
|
|
47
49
|
if system('which caddy > /dev/null')
|
|
48
50
|
pid = Process.spawn("caddy reload --config #{caddyfile}")
|
|
49
51
|
Process.detach(pid.to_i)
|
|
@@ -53,6 +55,8 @@ module Stable
|
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
def ensure_running!
|
|
58
|
+
return if ENV['STABLE_TEST_MODE']
|
|
59
|
+
|
|
56
60
|
if running?
|
|
57
61
|
reload
|
|
58
62
|
else
|
|
@@ -81,6 +85,8 @@ module Stable
|
|
|
81
85
|
|
|
82
86
|
return if valid_pem?(cert_path) && valid_pem?(key_path)
|
|
83
87
|
|
|
88
|
+
return if ENV['STABLE_TEST_MODE'] # Skip cert generation in tests
|
|
89
|
+
|
|
84
90
|
raise 'mkcert not installed' unless system('which mkcert > /dev/null')
|
|
85
91
|
|
|
86
92
|
System::Shell.run(
|
|
@@ -52,7 +52,11 @@ module Stable
|
|
|
52
52
|
if pids.empty?
|
|
53
53
|
puts "No app running on port #{app[:port]}"
|
|
54
54
|
else
|
|
55
|
-
pids.each
|
|
55
|
+
pids.each do |pid|
|
|
56
|
+
Process.kill('TERM', pid.to_i)
|
|
57
|
+
rescue StandardError
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
56
60
|
puts "Stopped #{app[:name]} on port #{app[:port]}"
|
|
57
61
|
end
|
|
58
62
|
|
|
@@ -89,9 +93,7 @@ module Stable
|
|
|
89
93
|
apps.each do |app|
|
|
90
94
|
next unless app[:started_at] && app[:pid]
|
|
91
95
|
|
|
92
|
-
unless pid_alive?(app[:pid])
|
|
93
|
-
AppRegistry.update(app[:name], started_at: nil, pid: nil)
|
|
94
|
-
end
|
|
96
|
+
AppRegistry.update(app[:name], started_at: nil, pid: nil) unless pid_alive?(app[:pid])
|
|
95
97
|
end
|
|
96
98
|
end
|
|
97
99
|
|
|
@@ -11,6 +11,8 @@ module Stable
|
|
|
11
11
|
ensure_apps_registry
|
|
12
12
|
ensure_caddyfile
|
|
13
13
|
# start or ensure caddy is running like original CLI
|
|
14
|
+
return if ENV['STABLE_TEST_MODE']
|
|
15
|
+
|
|
14
16
|
Stable::Services::CaddyManager.ensure_running!
|
|
15
17
|
puts "Caddy home initialized at #{Stable::Paths.root}"
|
|
16
18
|
self.class.ensure_dependencies!
|
|
@@ -44,6 +46,8 @@ module Stable
|
|
|
44
46
|
end
|
|
45
47
|
|
|
46
48
|
def ensure_dependencies!
|
|
49
|
+
return if ENV['STABLE_TEST_MODE']
|
|
50
|
+
|
|
47
51
|
platform = Stable::Utils::Platform.current
|
|
48
52
|
|
|
49
53
|
unless Stable::Utils::PackageManager.available?
|
|
@@ -68,6 +68,7 @@ module Stable
|
|
|
68
68
|
# Use lsof to find PIDs listening on the port
|
|
69
69
|
output = `lsof -i tcp:#{port} -sTCP:LISTEN -t 2>/dev/null`.strip
|
|
70
70
|
return [] if output.empty?
|
|
71
|
+
|
|
71
72
|
output.split("\n").map(&:to_i)
|
|
72
73
|
when :windows
|
|
73
74
|
# On Windows, this is more complex. For now, return empty array
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stable-cli-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Danny Simfukwe
|
|
@@ -37,6 +37,7 @@ files:
|
|
|
37
37
|
- lib/stable.rb
|
|
38
38
|
- lib/stable/bootstrap.rb
|
|
39
39
|
- lib/stable/cli.rb
|
|
40
|
+
- lib/stable/commands/destroy.rb
|
|
40
41
|
- lib/stable/commands/doctor.rb
|
|
41
42
|
- lib/stable/commands/list.rb
|
|
42
43
|
- lib/stable/commands/new.rb
|
|
@@ -45,6 +46,7 @@ files:
|
|
|
45
46
|
- lib/stable/commands/setup.rb
|
|
46
47
|
- lib/stable/commands/start.rb
|
|
47
48
|
- lib/stable/commands/stop.rb
|
|
49
|
+
- lib/stable/commands/upgrade_ruby.rb
|
|
48
50
|
- lib/stable/config/paths.rb
|
|
49
51
|
- lib/stable/db_manager.rb
|
|
50
52
|
- lib/stable/paths.rb
|
|
@@ -91,5 +93,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
91
93
|
requirements: []
|
|
92
94
|
rubygems_version: 3.6.7
|
|
93
95
|
specification_version: 4
|
|
94
|
-
summary: CLI tool to manage local Rails apps with automatic Caddy and
|
|
96
|
+
summary: Zero-config CLI tool to manage local Rails apps with automatic Caddy and
|
|
97
|
+
HTTPS setup
|
|
95
98
|
test_files: []
|