stable-cli-rails 0.6.8 → 0.6.9
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 +140 -5
- data/lib/stable/db_manager.rb +121 -0
- data/lib/stable.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: 2d0788683f7e8f99ac96bf00779599a7003e7c2f866a5a124a0fbbbca56c7a1a
|
|
4
|
+
data.tar.gz: 2c4d7c5ae3f0ee878e9f8c85660df787c340782eef1a1ea3c9c60460180c3259
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad3f5ba69aca2cca188f8a138e370a63547cf040a6ad715509d2afe7c6347fc1ed292073b2286d63dc7e446b1728a2bca29eae7db053751325fcdb9b6ac8ac2e
|
|
7
|
+
data.tar.gz: 02670b007fef74d07357f9bbc65526c1ac8256341f4c32c0e18cac37c0e55e45ef6d96efe16b4450f222a800b6e79f185a0f5888bb810d33423d5d9fe9ca61fd
|
data/lib/stable/cli.rb
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
4
|
require 'etc'
|
|
5
|
+
require 'tempfile'
|
|
5
6
|
require 'fileutils'
|
|
7
|
+
require 'io/console'
|
|
6
8
|
require_relative 'scanner'
|
|
7
9
|
require_relative 'registry'
|
|
8
10
|
|
|
@@ -26,6 +28,9 @@ module Stable
|
|
|
26
28
|
method_option :rails, type: :string, desc: 'Rails version to install (optional)'
|
|
27
29
|
method_option :port, type: :numeric, desc: 'Port to run Rails app on'
|
|
28
30
|
method_option :skip_ssl, type: :boolean, default: false, desc: 'Skip HTTPS setup'
|
|
31
|
+
method_option :db, type: :string, desc: 'Database name to create and integrate'
|
|
32
|
+
method_option :postgres, type: :boolean, default: false, desc: 'Use Postgres for the database'
|
|
33
|
+
method_option :mysql, type: :boolean, default: false, desc: 'Use MySQL for the database'
|
|
29
34
|
def new(name, ruby: RUBY_VERSION, rails: nil, port: nil)
|
|
30
35
|
port ||= next_free_port
|
|
31
36
|
app_path = File.expand_path(name)
|
|
@@ -75,6 +80,84 @@ module Stable
|
|
|
75
80
|
system("bash -lc '#{ruby_cmd} bundle install --jobs=4 --retry=3'") or abort('bundle install failed')
|
|
76
81
|
end
|
|
77
82
|
|
|
83
|
+
# --- Database integration ---
|
|
84
|
+
if options[:db]
|
|
85
|
+
adapter = if options[:postgres]
|
|
86
|
+
:postgresql
|
|
87
|
+
elsif options[:mysql]
|
|
88
|
+
:mysql
|
|
89
|
+
else
|
|
90
|
+
:postgresql
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
adapter == :postgresql ? 'postgresql' : 'mysql2'
|
|
94
|
+
gem_name = adapter == :postgresql ? 'pg' : 'mysql2'
|
|
95
|
+
|
|
96
|
+
gemfile_path = File.join(app_path, 'Gemfile')
|
|
97
|
+
unless File.read(gemfile_path).include?(gem_name)
|
|
98
|
+
File.open(gemfile_path, 'a') do |f|
|
|
99
|
+
f.puts "\n# Added by Stable CLI"
|
|
100
|
+
f.puts "gem '#{gem_name}'"
|
|
101
|
+
end
|
|
102
|
+
puts "✅ Added '#{gem_name}' gem to Gemfile"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Ensure the gem is installed inside the gemset
|
|
106
|
+
system("bash -lc 'cd #{app_path} && rvm #{ruby}@#{name} do bundle install --jobs=4 --retry=3'") or abort('bundle install failed')
|
|
107
|
+
|
|
108
|
+
# --- Database setup ---
|
|
109
|
+
db = Stable::DBManager.new(options[:db], adapter: adapter)
|
|
110
|
+
creds = []
|
|
111
|
+
|
|
112
|
+
case adapter
|
|
113
|
+
when :postgresql
|
|
114
|
+
db.create
|
|
115
|
+
when :mysql
|
|
116
|
+
creds = create_mysql_db(options[:db]) # creates DB and returns creds
|
|
117
|
+
# Make sure mysql2 gem is loaded for Rails
|
|
118
|
+
system("bash -lc 'cd #{app_path} && rvm #{ruby}@#{name} do bundle exec gem list | grep mysql2'") || abort('mysql2 gem not found in gemset')
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# --- Generate database.yml ---
|
|
122
|
+
db_config_path = File.join(app_path, 'config', 'database.yml')
|
|
123
|
+
base_config = {
|
|
124
|
+
'adapter' => adapter == :postgresql ? 'postgresql' : 'mysql2',
|
|
125
|
+
'encoding' => adapter == :postgresql ? 'unicode' : 'utf8mb4',
|
|
126
|
+
'pool' => 5,
|
|
127
|
+
'database' => options[:db],
|
|
128
|
+
'username' => adapter == :mysql ? creds[:user] : nil,
|
|
129
|
+
'password' => adapter == :mysql ? creds[:password] : nil,
|
|
130
|
+
'host' => 'localhost',
|
|
131
|
+
'auth_plugin' => adapter == :mysql ? 'caching_sha2_password' : ''
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
db_configs = {
|
|
135
|
+
'default' => base_config,
|
|
136
|
+
'development' => base_config,
|
|
137
|
+
'test' => base_config.merge('database' => "#{options[:db]}_test"),
|
|
138
|
+
'production' => base_config.merge('database' => "#{options[:db]}_prod")
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
File.write(db_config_path, db_configs.to_yaml)
|
|
142
|
+
puts "✅ Database '#{db.name}' configured in Rails app"
|
|
143
|
+
|
|
144
|
+
# --- Prepare the database ---
|
|
145
|
+
puts 'Preparing database...'
|
|
146
|
+
system("bash -lc 'cd #{app_path} && rvm #{ruby}@#{name} do bundle exec rails db:prepare'") or abort('Database preparation failed')
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# --- Generate Caddyfile ---
|
|
150
|
+
|
|
151
|
+
puts 'Refreshing bundle environment...'
|
|
152
|
+
system(
|
|
153
|
+
"bash -lc 'cd #{app_path} && #{ruby_cmd} bundle check || #{ruby_cmd} bundle install'"
|
|
154
|
+
) or abort('Bundler setup failed')
|
|
155
|
+
|
|
156
|
+
puts 'Preparing database...'
|
|
157
|
+
system(
|
|
158
|
+
"bash -lc 'cd #{app_path} && #{ruby_cmd} bundle exec rails db:prepare'"
|
|
159
|
+
) or abort('Database preparation failed')
|
|
160
|
+
|
|
78
161
|
# --- Host entry & certificate ---
|
|
79
162
|
add_host_entry(domain)
|
|
80
163
|
generate_cert(domain) unless options[:skip_ssl]
|
|
@@ -412,18 +495,37 @@ module Stable
|
|
|
412
495
|
exit 1
|
|
413
496
|
end
|
|
414
497
|
|
|
498
|
+
# --- Install Caddy ---
|
|
415
499
|
unless system('which caddy > /dev/null')
|
|
416
500
|
puts 'Installing Caddy...'
|
|
417
501
|
system('brew install caddy')
|
|
418
502
|
end
|
|
419
503
|
|
|
420
|
-
|
|
504
|
+
# --- Install mkcert ---
|
|
505
|
+
unless system('which mkcert > /dev/null')
|
|
506
|
+
puts 'Installing mkcert...'
|
|
507
|
+
system('brew install mkcert nss')
|
|
508
|
+
system('mkcert -install')
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# --- Install PostgreSQL ---
|
|
512
|
+
unless system('which psql > /dev/null')
|
|
513
|
+
puts 'Installing PostgreSQL...'
|
|
514
|
+
system('brew install postgresql')
|
|
515
|
+
system('brew services start postgresql')
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# --- Install MySQL ---
|
|
519
|
+
unless system('which mysql > /dev/null')
|
|
520
|
+
puts 'Installing MySQL...'
|
|
521
|
+
system('brew install mysql')
|
|
522
|
+
system('brew services start mysql')
|
|
523
|
+
end
|
|
421
524
|
|
|
422
|
-
puts '
|
|
423
|
-
system('brew install mkcert nss')
|
|
424
|
-
system('mkcert -install')
|
|
525
|
+
puts '✅ All dependencies are installed and running.'
|
|
425
526
|
end
|
|
426
527
|
|
|
528
|
+
|
|
427
529
|
def ensure_caddy_running!
|
|
428
530
|
api_port = 2019
|
|
429
531
|
|
|
@@ -509,7 +611,7 @@ module Stable
|
|
|
509
611
|
end
|
|
510
612
|
end
|
|
511
613
|
|
|
512
|
-
def wait_for_port(port, timeout:
|
|
614
|
+
def wait_for_port(port, timeout: 20)
|
|
513
615
|
require 'socket'
|
|
514
616
|
start = Time.now
|
|
515
617
|
|
|
@@ -619,5 +721,38 @@ module Stable
|
|
|
619
721
|
"rvm #{ruby} do"
|
|
620
722
|
end
|
|
621
723
|
end
|
|
724
|
+
|
|
725
|
+
def create_mysql_db(db_name)
|
|
726
|
+
socket = %w[
|
|
727
|
+
/opt/homebrew/var/mysql/mysql.sock
|
|
728
|
+
/tmp/mysql.sock
|
|
729
|
+
].find { |path| File.exist?(path) } || abort('MySQL socket not found')
|
|
730
|
+
|
|
731
|
+
print 'Enter MySQL root username (default: root): '
|
|
732
|
+
root_user = $stdin.gets.chomp
|
|
733
|
+
root_user = 'root' if root_user.empty?
|
|
734
|
+
|
|
735
|
+
print 'Enter MySQL root password (leave blank if none): '
|
|
736
|
+
root_password = $stdin.noecho(&:gets).chomp
|
|
737
|
+
puts
|
|
738
|
+
|
|
739
|
+
password_arg = root_password.empty? ? '' : "-p#{root_password}"
|
|
740
|
+
|
|
741
|
+
sql = <<~SQL
|
|
742
|
+
CREATE DATABASE IF NOT EXISTS #{db_name};
|
|
743
|
+
FLUSH PRIVILEGES;
|
|
744
|
+
SQL
|
|
745
|
+
|
|
746
|
+
require 'tempfile'
|
|
747
|
+
Tempfile.create(['db_setup', '.sql']) do |file|
|
|
748
|
+
file.write(sql)
|
|
749
|
+
file.flush
|
|
750
|
+
system("mysql --protocol=SOCKET --socket=#{socket} -u #{root_user} #{password_arg} < #{file.path}") or
|
|
751
|
+
abort("Failed to create MySQL DB '#{db_name}'")
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
puts "✅ MySQL database '#{db_name}' created/ready"
|
|
755
|
+
{ user: root_user, password: root_password }
|
|
756
|
+
end
|
|
622
757
|
end
|
|
623
758
|
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Stable
|
|
4
|
+
class DBManager
|
|
5
|
+
attr_reader :name, :adapter
|
|
6
|
+
|
|
7
|
+
def initialize(name, adapter:)
|
|
8
|
+
@name = name
|
|
9
|
+
@adapter = adapter
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Main entry
|
|
13
|
+
def create
|
|
14
|
+
case adapter
|
|
15
|
+
when :postgresql
|
|
16
|
+
ensure_postgres_database
|
|
17
|
+
when :mysql
|
|
18
|
+
ensure_mysql_database
|
|
19
|
+
else
|
|
20
|
+
abort "Unsupported database adapter: #{adapter}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Rails database.yml config
|
|
25
|
+
def rails_config
|
|
26
|
+
case adapter
|
|
27
|
+
when :postgresql
|
|
28
|
+
{
|
|
29
|
+
'adapter' => 'postgresql',
|
|
30
|
+
'encoding' => 'unicode',
|
|
31
|
+
'database' => name,
|
|
32
|
+
'username' => app_user,
|
|
33
|
+
'password' => nil,
|
|
34
|
+
'host' => 'localhost',
|
|
35
|
+
'pool' => 5
|
|
36
|
+
}
|
|
37
|
+
when :mysql
|
|
38
|
+
{
|
|
39
|
+
'adapter' => 'mysql2',
|
|
40
|
+
'encoding' => 'utf8mb4',
|
|
41
|
+
'database' => name,
|
|
42
|
+
'username' => app_user,
|
|
43
|
+
'password' => '',
|
|
44
|
+
'host' => 'localhost',
|
|
45
|
+
'socket' => mysql_socket,
|
|
46
|
+
'pool' => 5
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
abort "Unsupported adapter for Rails config: #{adapter}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# -------------------- PostgreSQL --------------------
|
|
56
|
+
|
|
57
|
+
def ensure_postgres_database
|
|
58
|
+
exists = system(%(psql -lqt | cut -d \\| -f 1 | grep -w #{name} >/dev/null))
|
|
59
|
+
|
|
60
|
+
if exists
|
|
61
|
+
puts "⚠ Postgres database '#{name}' already exists. Skipping creation."
|
|
62
|
+
else
|
|
63
|
+
system("createdb #{name}") or abort("Failed to create Postgres DB '#{name}'")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# -------------------- MySQL --------------------
|
|
68
|
+
|
|
69
|
+
def ensure_mysql_database
|
|
70
|
+
ensure_mysql_root_auth!
|
|
71
|
+
|
|
72
|
+
# Create DB and user
|
|
73
|
+
socket = mysql_socket
|
|
74
|
+
user = app_user
|
|
75
|
+
|
|
76
|
+
system(%(
|
|
77
|
+
mysql --protocol=SOCKET --socket=#{socket} -u root <<SQL
|
|
78
|
+
CREATE DATABASE IF NOT EXISTS #{name};
|
|
79
|
+
CREATE USER IF NOT EXISTS '#{user}'@'localhost' IDENTIFIED BY '';
|
|
80
|
+
GRANT ALL PRIVILEGES ON #{name}.* TO '#{user}'@'localhost';
|
|
81
|
+
FLUSH PRIVILEGES;
|
|
82
|
+
SQL
|
|
83
|
+
)) or abort("Failed to create MySQL DB '#{name}'")
|
|
84
|
+
|
|
85
|
+
puts "✅ MySQL database '#{name}' ready"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Ensure root can connect via socket
|
|
89
|
+
def ensure_mysql_root_auth!
|
|
90
|
+
ok = system("mysql -u root -e 'SELECT 1' >/dev/null 2>&1")
|
|
91
|
+
return if ok
|
|
92
|
+
|
|
93
|
+
puts '⚠ Fixing MySQL root authentication (requires sudo)...'
|
|
94
|
+
socket = mysql_socket
|
|
95
|
+
|
|
96
|
+
system(%(
|
|
97
|
+
sudo mysql --protocol=SOCKET --socket=#{socket} <<SQL
|
|
98
|
+
ALTER USER 'root'@'localhost' IDENTIFIED BY '';
|
|
99
|
+
FLUSH PRIVILEGES;
|
|
100
|
+
SQL
|
|
101
|
+
)) or abort('Failed to repair MySQL root authentication')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Detect MySQL socket on macOS / Linux
|
|
105
|
+
def mysql_socket
|
|
106
|
+
paths = [
|
|
107
|
+
'/opt/homebrew/var/mysql/mysql.sock', # Homebrew macOS
|
|
108
|
+
'/tmp/mysql.sock', # Default
|
|
109
|
+
'/var/run/mysqld/mysqld.sock' # Linux default
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
paths.each { |p| return p if File.exist?(p) }
|
|
113
|
+
abort 'MySQL socket not found. Is MySQL running?'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Default Rails DB user
|
|
117
|
+
def app_user
|
|
118
|
+
ENV['USER'] || 'stable'
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/stable.rb
CHANGED
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.6.
|
|
4
|
+
version: 0.6.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Danny Simfukwe
|
|
@@ -50,6 +50,7 @@ files:
|
|
|
50
50
|
- lib/stable.rb
|
|
51
51
|
- lib/stable/bootstrap.rb
|
|
52
52
|
- lib/stable/cli.rb
|
|
53
|
+
- lib/stable/db_manager.rb
|
|
53
54
|
- lib/stable/paths.rb
|
|
54
55
|
- lib/stable/registry.rb
|
|
55
56
|
- lib/stable/scanner.rb
|