stable-cli-rails 0.8.1 ā 0.8.2
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 +5 -0
- data/lib/stable/commands/destroy.rb +97 -0
- data/lib/stable/commands/list.rb +32 -32
- 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/process_manager.rb +6 -4
- data/lib/stable/utils/platform.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a7c350e5e5b2858a8a8ceb9e35eb9772c0e3416accf9f8e3546ba072ae59833
|
|
4
|
+
data.tar.gz: ed890e3547579a9361c473a8b884166a87d4454f1b89874e53510138f38e43f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f86445108712b6eb9cb99a14f5da5df53e14d11d646d3ccc9525dc23efec767ae2a6a2f6cb68c0ac032f75928992dfb0913580900ebae6d2426660051f5191b
|
|
7
|
+
data.tar.gz: 2965495aa69ea21c4c19c7020222a90ee84214d05530a1b6175e9ce6689dda13d94a508cb708c9d2c2447f8b484b6afe127e7ae19c08efcf5dfefea247757d03
|
data/lib/stable/cli.rb
CHANGED
|
@@ -75,6 +75,11 @@ module Stable
|
|
|
75
75
|
Commands::Remove.new(name).call
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
desc 'destroy NAME', 'Permanently delete a Rails app and all its files'
|
|
79
|
+
def destroy(name)
|
|
80
|
+
Commands::Destroy.new(name).call
|
|
81
|
+
end
|
|
82
|
+
|
|
78
83
|
desc 'start NAME', 'Start a Rails app with its correct Ruby version'
|
|
79
84
|
def start(name)
|
|
80
85
|
Commands::Start.new(name).call
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
# Only clean up RVM gemsets on Unix-like systems (macOS/Linux)
|
|
69
|
+
# Windows uses different Ruby version managers
|
|
70
|
+
return unless Stable::Utils::Platform.unix?
|
|
71
|
+
|
|
72
|
+
ruby_version = app[:ruby]
|
|
73
|
+
# Handle different ruby version formats (e.g., "3.4.7", "ruby-3.4.7")
|
|
74
|
+
clean_ruby_version = ruby_version.to_s.sub(/^ruby-/, '')
|
|
75
|
+
gemset_name = "#{clean_ruby_version}@#{@name}"
|
|
76
|
+
|
|
77
|
+
puts " Cleaning up RVM gemset #{gemset_name}..."
|
|
78
|
+
begin
|
|
79
|
+
# Use system to run RVM command to delete the gemset
|
|
80
|
+
system("bash -lc 'source ~/.rvm/scripts/rvm && rvm gemset delete #{gemset_name} --force' 2>/dev/null || true")
|
|
81
|
+
puts " ā
RVM gemset #{gemset_name} cleaned up"
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
puts " ā ļø Could not clean up RVM gemset #{gemset_name}: #{e.message}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def delete_project_directory(path)
|
|
88
|
+
if File.exist?(path)
|
|
89
|
+
puts ' Deleting project directory...'
|
|
90
|
+
FileUtils.rm_rf(path)
|
|
91
|
+
else
|
|
92
|
+
puts ' Project directory not found (already deleted?)'
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
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
|
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]
|
|
@@ -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
|
|
|
@@ -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.2
|
|
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
|