regolith 0.1.6 → 0.1.8
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/regolith/cli.rb +883 -233
- data/lib/regolith/service_client.rb +82 -26
- data/lib/regolith/version.rb +1 -1
- data/lib/regolith.rb +8 -8
- metadata +2 -2
data/lib/regolith/cli.rb
CHANGED
@@ -1,10 +1,60 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'yaml'
|
3
|
+
require 'psych'
|
3
4
|
require 'erb'
|
4
5
|
require 'timeout'
|
5
6
|
require 'rubygems'
|
7
|
+
require 'net/http'
|
8
|
+
require 'uri'
|
9
|
+
require 'json'
|
10
|
+
require 'ostruct'
|
11
|
+
require 'set'
|
12
|
+
require 'rbconfig'
|
13
|
+
require 'shellwords'
|
6
14
|
|
7
15
|
module Regolith
|
16
|
+
VERSION = "1.0.7"
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def configuration
|
20
|
+
@configuration ||= OpenStruct.new(
|
21
|
+
timeout: 10,
|
22
|
+
default_port: 3001
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def service_registry
|
27
|
+
@service_registry ||= begin
|
28
|
+
path = find_regolith_config
|
29
|
+
if path && File.exist?(path)
|
30
|
+
config = YAML.load_file(path) || {}
|
31
|
+
config['services'] || {}
|
32
|
+
else
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def reload_service_registry!
|
39
|
+
@service_registry = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def find_regolith_config
|
45
|
+
current_dir = Dir.pwd
|
46
|
+
loop do
|
47
|
+
config_path = File.join(current_dir, 'config', 'regolith.yml')
|
48
|
+
return config_path if File.exist?(config_path)
|
49
|
+
|
50
|
+
parent = File.dirname(current_dir)
|
51
|
+
break if parent == current_dir
|
52
|
+
current_dir = parent
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
8
58
|
class CLI
|
9
59
|
def initialize(args)
|
10
60
|
@args = args
|
@@ -19,21 +69,47 @@ module Regolith
|
|
19
69
|
create_new_app(@subcommand)
|
20
70
|
when 'generate'
|
21
71
|
generate_resource(@subcommand, @name)
|
22
|
-
when 'server'
|
23
|
-
start_server
|
72
|
+
when 'server', 'up'
|
73
|
+
start_server(parse_flags(@args[1..-1]))
|
74
|
+
when 'down'
|
75
|
+
stop_services
|
24
76
|
when 'restart'
|
25
|
-
|
77
|
+
restart_service(@subcommand)
|
26
78
|
when 'stop'
|
27
|
-
|
28
|
-
when '
|
29
|
-
show_logs(@subcommand)
|
30
|
-
when 'status'
|
79
|
+
stop_service(@subcommand)
|
80
|
+
when 'ps', 'status'
|
31
81
|
show_status
|
32
|
-
when '
|
33
|
-
|
82
|
+
when 'logs'
|
83
|
+
show_logs(@subcommand, parse_flags(@args[2..-1]))
|
84
|
+
when 'exec'
|
85
|
+
exec_command(@subcommand, @args[2..-1])
|
86
|
+
when 'shell'
|
87
|
+
shell_service(@subcommand)
|
34
88
|
when 'console'
|
35
89
|
open_console(@subcommand)
|
36
|
-
when '
|
90
|
+
when 'rails'
|
91
|
+
rails_passthrough(@subcommand, @args[2..-1])
|
92
|
+
when 'routes'
|
93
|
+
show_routes(@subcommand)
|
94
|
+
when 'open'
|
95
|
+
open_service(@subcommand)
|
96
|
+
when 'db'
|
97
|
+
db_command(@subcommand, @name, parse_flags(@args[3..-1]))
|
98
|
+
when 'test'
|
99
|
+
run_tests(@subcommand, parse_flags(@args[2..-1]))
|
100
|
+
when 'health'
|
101
|
+
health_check
|
102
|
+
when 'config'
|
103
|
+
show_config(parse_flags(@args[1..-1]))
|
104
|
+
when 'prune'
|
105
|
+
prune_system
|
106
|
+
when 'rebuild'
|
107
|
+
rebuild_service(@subcommand)
|
108
|
+
when 'doctor'
|
109
|
+
run_doctor
|
110
|
+
when 'inspect'
|
111
|
+
inspect_services(parse_flags(@args[1..-1]))
|
112
|
+
when 'version', '--version', '-v'
|
37
113
|
puts "Regolith #{Regolith::VERSION}"
|
38
114
|
else
|
39
115
|
show_help
|
@@ -42,93 +118,21 @@ module Regolith
|
|
42
118
|
|
43
119
|
private
|
44
120
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
121
|
+
def parse_flags(args)
|
122
|
+
flags = {}
|
123
|
+
return flags unless args
|
124
|
+
|
125
|
+
args.each do |arg|
|
126
|
+
if arg.start_with?('--')
|
127
|
+
key, value = arg[2..-1].split('=', 2)
|
128
|
+
flags[key.to_sym] = value || true
|
129
|
+
elsif arg == '-f'
|
130
|
+
flags[:follow] = true
|
131
|
+
elsif arg == '--all'
|
132
|
+
flags[:all] = true
|
57
133
|
end
|
58
|
-
puts "🔄 Restarting #{service_name} service..."
|
59
|
-
system("docker-compose restart #{service_name}")
|
60
|
-
else
|
61
|
-
puts "🔄 Restarting all services..."
|
62
|
-
system("docker-compose restart")
|
63
|
-
end
|
64
|
-
puts "✅ Restart complete"
|
65
|
-
end
|
66
|
-
|
67
|
-
def stop_services
|
68
|
-
unless File.exist?('docker-compose.yml')
|
69
|
-
puts "❌ Error: Not in a Regolith app directory"
|
70
|
-
exit 1
|
71
|
-
end
|
72
|
-
|
73
|
-
puts "🛑 Stopping all services..."
|
74
|
-
system("docker-compose down")
|
75
|
-
puts "✅ All services stopped"
|
76
|
-
end
|
77
|
-
|
78
|
-
def show_logs(service_name)
|
79
|
-
unless File.exist?('docker-compose.yml')
|
80
|
-
puts "❌ Error: Not in a Regolith app directory"
|
81
|
-
exit 1
|
82
|
-
end
|
83
|
-
|
84
|
-
unless service_name
|
85
|
-
puts "❌ Error: Service name required"
|
86
|
-
puts "Usage: regolith logs <service_name>"
|
87
|
-
config = load_regolith_config
|
88
|
-
puts "Available services: #{config['services'].keys.join(', ')}"
|
89
|
-
exit 1
|
90
|
-
end
|
91
|
-
|
92
|
-
config = load_regolith_config
|
93
|
-
unless config['services'][service_name]
|
94
|
-
puts "❌ Error: Service '#{service_name}' not found"
|
95
|
-
puts "Available services: #{config['services'].keys.join(', ')}"
|
96
|
-
exit 1
|
97
|
-
end
|
98
|
-
|
99
|
-
puts "📋 Showing logs for #{service_name} (Press Ctrl+C to exit)..."
|
100
|
-
exec("docker-compose logs -f #{service_name}")
|
101
|
-
end
|
102
|
-
|
103
|
-
def show_status
|
104
|
-
unless File.exist?('docker-compose.yml')
|
105
|
-
puts "❌ Error: Not in a Regolith app directory"
|
106
|
-
exit 1
|
107
|
-
end
|
108
|
-
|
109
|
-
puts "📊 Regolith Service Status:"
|
110
|
-
puts ""
|
111
|
-
|
112
|
-
config = load_regolith_config
|
113
|
-
puts "App: #{config['name']}"
|
114
|
-
puts "Services: #{config['services'].size}"
|
115
|
-
puts ""
|
116
|
-
|
117
|
-
system("docker-compose ps")
|
118
|
-
end
|
119
|
-
|
120
|
-
def clean_environment
|
121
|
-
unless File.exist?('docker-compose.yml')
|
122
|
-
puts "❌ Error: Not in a Regolith app directory"
|
123
|
-
exit 1
|
124
134
|
end
|
125
|
-
|
126
|
-
puts "🧹 Cleaning Docker environment..."
|
127
|
-
puts " → Stopping and removing containers..."
|
128
|
-
system("docker-compose down -v")
|
129
|
-
puts " → Pruning unused Docker resources..."
|
130
|
-
system("docker system prune -f")
|
131
|
-
puts "✅ Environment cleaned"
|
135
|
+
flags
|
132
136
|
end
|
133
137
|
|
134
138
|
def create_new_app(app_name)
|
@@ -168,6 +172,8 @@ module Regolith
|
|
168
172
|
File.write('docker-compose.yml', generate_docker_compose(app_name))
|
169
173
|
File.write('Makefile', generate_makefile)
|
170
174
|
File.write('.bin/regolith', generate_regolith_shim)
|
175
|
+
File.write('.gitignore', generate_gitignore)
|
176
|
+
File.write('README.md', generate_readme(app_name))
|
171
177
|
FileUtils.chmod(0755, '.bin/regolith')
|
172
178
|
end
|
173
179
|
|
@@ -182,9 +188,16 @@ module Regolith
|
|
182
188
|
end
|
183
189
|
|
184
190
|
def generate_service(service_name)
|
191
|
+
# Validate service name
|
192
|
+
unless service_name =~ /\A[a-z][a-z0-9_]*\z/
|
193
|
+
puts "❌ Invalid service name. Use lowercase, digits, and underscores only."
|
194
|
+
puts "Examples: users, user_profiles, api_gateway"
|
195
|
+
exit 1
|
196
|
+
end
|
197
|
+
|
185
198
|
puts "🔧 Creating service '#{service_name}'..."
|
186
199
|
config = load_regolith_config
|
187
|
-
port =
|
200
|
+
port = next_available_port
|
188
201
|
service_dir = "services/#{service_name}_service"
|
189
202
|
|
190
203
|
puts " → Generating Rails API app..."
|
@@ -215,20 +228,60 @@ module Regolith
|
|
215
228
|
exit 1
|
216
229
|
end
|
217
230
|
|
218
|
-
|
219
|
-
|
231
|
+
customize_service(service_dir, service_name, port)
|
232
|
+
|
233
|
+
config['services'][service_name] = {
|
234
|
+
'port' => port,
|
235
|
+
'root' => "./#{service_dir}"
|
236
|
+
}
|
220
237
|
|
221
|
-
|
238
|
+
save_regolith_config(config)
|
239
|
+
update_docker_compose(config)
|
240
|
+
|
241
|
+
puts "✅ Created service '#{service_name}'"
|
242
|
+
puts "🚀 Service will run on port #{port}"
|
243
|
+
puts "→ Next: regolith generate service <another_service> or regolith server"
|
244
|
+
end
|
245
|
+
|
246
|
+
def next_available_port(start = Regolith.configuration.default_port)
|
247
|
+
used = load_regolith_config['services'].values.map { |s| s['port'] }.to_set
|
248
|
+
port = start
|
249
|
+
port += 1 while used.include?(port)
|
250
|
+
port
|
251
|
+
end
|
252
|
+
|
253
|
+
def customize_service(service_dir, service_name, port)
|
254
|
+
# Fix Ruby version detection
|
255
|
+
major_minor = RUBY_VERSION.split(".")[0..1].join(".")
|
256
|
+
|
257
|
+
custom_gemfile = generate_gemfile(major_minor)
|
258
|
+
File.write("#{service_dir}/Gemfile", custom_gemfile)
|
259
|
+
|
260
|
+
vendor_regolith_gem(service_dir)
|
222
261
|
|
223
|
-
|
262
|
+
puts " → Running bundle install..."
|
263
|
+
Dir.chdir(service_dir) do
|
264
|
+
unless system("bundle install")
|
265
|
+
puts "❌ bundle install failed"
|
266
|
+
puts "→ You may be missing system libraries like libyaml-dev build-essential"
|
267
|
+
exit 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
patch_rails_app(service_dir, service_name, port)
|
272
|
+
end
|
273
|
+
|
274
|
+
def generate_gemfile(ruby_version)
|
275
|
+
<<~GEMFILE
|
224
276
|
source "https://rubygems.org"
|
225
277
|
|
226
|
-
ruby "~> #{
|
278
|
+
ruby "~> #{ruby_version}.0"
|
227
279
|
|
228
280
|
gem "rails", "~> 7.2.2.1"
|
229
281
|
gem "pg", "~> 1.5"
|
230
282
|
gem "puma", ">= 5.0"
|
231
283
|
gem "rack-cors"
|
284
|
+
gem "bootsnap", require: false
|
232
285
|
|
233
286
|
group :development, :test do
|
234
287
|
gem "debug", platforms: %i[ mri mswin mswin64 mingw x64_mingw ], require: "debug/prelude"
|
@@ -238,112 +291,491 @@ module Regolith
|
|
238
291
|
|
239
292
|
gem "regolith", path: "vendor/regolith"
|
240
293
|
GEMFILE
|
294
|
+
end
|
241
295
|
|
242
|
-
|
243
|
-
|
244
|
-
# Vendor Regolith gem into service for Docker compatibility
|
296
|
+
def vendor_regolith_gem(service_dir)
|
245
297
|
vendor_dir = File.join(service_dir, "vendor")
|
246
298
|
FileUtils.mkdir_p(vendor_dir)
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
puts "❌ bundle install failed"
|
254
|
-
puts "→ You may be missing system libraries like libyaml-dev libsqlite3-dev build-essential pkg-config"
|
255
|
-
puts "→ Try: sudo apt install -y libyaml-dev libsqlite3-dev build-essential pkg-config"
|
256
|
-
exit 1
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
patch_rails_app(service_dir, service_name, port)
|
261
|
-
|
262
|
-
config['services'][service_name] = {
|
263
|
-
'port' => port,
|
264
|
-
'root' => "./#{service_dir}"
|
265
|
-
}
|
266
|
-
save_regolith_config(config)
|
267
|
-
update_docker_compose(config)
|
268
|
-
|
269
|
-
puts "✅ Created service '#{service_name}'"
|
270
|
-
puts "🚀 Service running on port #{port}"
|
271
|
-
puts "→ Next: regolith generate service <another_service> or regolith server"
|
299
|
+
|
300
|
+
regolith_source = File.expand_path("../..", __dir__)
|
301
|
+
regolith_dest = File.join(vendor_dir, "regolith")
|
302
|
+
|
303
|
+
FileUtils.cp_r(regolith_source, regolith_dest)
|
304
|
+
puts "📦 Vendored Regolith gem into #{regolith_dest}"
|
272
305
|
end
|
273
306
|
|
274
307
|
def patch_rails_app(service_dir, service_name, port)
|
308
|
+
create_initializers(service_dir, service_name)
|
309
|
+
create_health_controller(service_dir)
|
310
|
+
add_health_route(service_dir)
|
311
|
+
File.write("#{service_dir}/Dockerfile", generate_dockerfile)
|
312
|
+
patch_application_rb(service_dir, service_name, port)
|
313
|
+
end
|
314
|
+
|
315
|
+
def create_initializers(service_dir, service_name)
|
275
316
|
initializer_dir = "#{service_dir}/config/initializers"
|
276
317
|
FileUtils.mkdir_p(initializer_dir)
|
318
|
+
|
277
319
|
File.write("#{initializer_dir}/regolith.rb", generate_regolith_initializer(service_name))
|
278
|
-
File.write("#{
|
320
|
+
File.write("#{initializer_dir}/cors.rb", generate_cors_initializer)
|
321
|
+
end
|
322
|
+
|
323
|
+
def create_health_controller(service_dir)
|
324
|
+
controller_dir = "#{service_dir}/app/controllers/regolith"
|
325
|
+
FileUtils.mkdir_p(controller_dir)
|
326
|
+
|
327
|
+
File.write("#{controller_dir}/health_controller.rb", generate_health_controller)
|
328
|
+
end
|
329
|
+
|
330
|
+
def add_health_route(service_dir)
|
331
|
+
routes_path = File.join(service_dir, "config/routes.rb")
|
332
|
+
content = File.read(routes_path)
|
333
|
+
|
334
|
+
# Try to inject before the final end, or append if no clear structure
|
335
|
+
if content.sub!(/end\s*\z/, " get '/health', to: 'regolith/health#show'\nend\n")
|
336
|
+
File.write(routes_path, content)
|
337
|
+
else
|
338
|
+
# Fallback: append inside the draw block
|
339
|
+
File.open(routes_path, "a") { |f| f.puts "get '/health', to: 'regolith/health#show'" }
|
340
|
+
end
|
341
|
+
end
|
279
342
|
|
343
|
+
def patch_application_rb(service_dir, service_name, port)
|
280
344
|
app_rb_path = "#{service_dir}/config/application.rb"
|
281
345
|
app_rb_content = File.read(app_rb_path)
|
282
346
|
|
283
347
|
cors_config = <<~RUBY
|
284
348
|
|
285
349
|
# Regolith configuration
|
286
|
-
config.middleware.insert_before 0, Rack::Cors do
|
287
|
-
allow do
|
288
|
-
origins '*'
|
289
|
-
resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head]
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
350
|
config.regolith_service_name = '#{service_name}'
|
294
351
|
config.regolith_service_port = #{port}
|
295
352
|
RUBY
|
296
353
|
|
297
|
-
app_rb_content.gsub!(/
|
298
|
-
end/m) do |match|
|
299
|
-
match.gsub(/(\n end)$/, "#{cors_config}\1")
|
300
|
-
end
|
301
|
-
|
354
|
+
app_rb_content.gsub!(/(\n end\n\z)/, "#{cors_config}\1")
|
302
355
|
File.write(app_rb_path, app_rb_content)
|
303
356
|
end
|
304
357
|
|
305
|
-
|
358
|
+
# Service management commands
|
359
|
+
def start_server(flags = {})
|
306
360
|
unless File.exist?('docker-compose.yml')
|
307
361
|
puts "❌ Error: Not in a Regolith app directory"
|
308
362
|
exit 1
|
309
363
|
end
|
310
364
|
|
311
365
|
puts "🚀 Starting Regolith services..."
|
366
|
+
|
312
367
|
config = load_regolith_config
|
368
|
+
show_service_info(config)
|
313
369
|
|
314
|
-
|
315
|
-
|
370
|
+
exec_compose('up', '--build')
|
371
|
+
end
|
372
|
+
|
373
|
+
def stop_services
|
374
|
+
puts "🛑 Stopping all services..."
|
375
|
+
exec_compose('down', '-v')
|
376
|
+
end
|
377
|
+
|
378
|
+
def restart_service(service_name = nil)
|
379
|
+
if service_name
|
380
|
+
ensure_service_exists!(service_name)
|
381
|
+
puts "🔄 Restarting service '#{service_name}'..."
|
382
|
+
exec_compose('restart', service_name)
|
383
|
+
else
|
384
|
+
puts "🔄 Restarting all services..."
|
385
|
+
exec_compose('restart')
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def stop_service(service_name = nil)
|
390
|
+
if service_name
|
391
|
+
ensure_service_exists!(service_name)
|
392
|
+
puts "⏹ Stopping service '#{service_name}'..."
|
393
|
+
exec_compose('stop', service_name)
|
394
|
+
else
|
395
|
+
puts "⏹ Stopping all services..."
|
396
|
+
exec_compose('stop')
|
316
397
|
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def show_status
|
401
|
+
puts "📊 Service Status:"
|
402
|
+
puts
|
403
|
+
|
404
|
+
# Try different format options for maximum compatibility
|
405
|
+
success = system_compose('ps', '--format', 'table') ||
|
406
|
+
system_compose('ps', '--format', 'json') ||
|
407
|
+
system_compose('ps')
|
408
|
+
|
409
|
+
# Show summary counts and health check for exit code
|
410
|
+
if success
|
411
|
+
config = load_regolith_config
|
412
|
+
service_count = config['services'].size
|
413
|
+
ports = config['services'].values.map { |s| s['port'] }.sort
|
414
|
+
|
415
|
+
puts
|
416
|
+
puts "📋 Summary: #{service_count} services configured"
|
417
|
+
puts "🚪 Ports: #{ports.join(', ')}" if ports.any?
|
418
|
+
|
419
|
+
# Check health for proper exit code
|
420
|
+
healthy_services = 0
|
421
|
+
config['services'].each do |name, service_config|
|
422
|
+
port = service_config['port']
|
423
|
+
status = check_service_health(port)
|
424
|
+
healthy_services += 1 if status[:healthy]
|
425
|
+
end
|
426
|
+
|
427
|
+
if healthy_services < service_count
|
428
|
+
puts "⚠️ #{service_count - healthy_services} services unhealthy"
|
429
|
+
exit 1
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
317
433
|
|
318
|
-
|
319
|
-
|
434
|
+
def show_logs(service_name = nil, flags = {})
|
435
|
+
args = ['logs']
|
436
|
+
args << '--follow' if flags[:follow] || flags[:f]
|
437
|
+
args << service_name if service_name
|
438
|
+
|
439
|
+
exec_compose(*args)
|
440
|
+
end
|
320
441
|
|
321
|
-
|
442
|
+
def exec_command(service_name, command_args)
|
443
|
+
ensure_service_exists!(service_name)
|
444
|
+
|
445
|
+
if command_args.empty?
|
446
|
+
command_args = ['bash']
|
447
|
+
end
|
448
|
+
|
449
|
+
exec_compose('exec', service_name, *command_args)
|
450
|
+
end
|
451
|
+
|
452
|
+
def shell_service(service_name)
|
453
|
+
unless service_name
|
454
|
+
puts "❌ Error: Service name required"
|
455
|
+
puts "Usage: regolith shell <service_name>"
|
456
|
+
exit 1
|
457
|
+
end
|
458
|
+
|
459
|
+
ensure_service_exists!(service_name)
|
460
|
+
puts "🐚 Opening shell for #{service_name}_service..."
|
461
|
+
exec_compose('exec', service_name, 'bash')
|
462
|
+
end
|
463
|
+
|
464
|
+
# Rails integration commands
|
465
|
+
def rails_passthrough(service_name, rails_args)
|
466
|
+
ensure_service_exists!(service_name)
|
467
|
+
|
468
|
+
if rails_args.empty?
|
469
|
+
puts "❌ Error: Rails command required"
|
470
|
+
puts "Usage: regolith rails <service> <command>"
|
471
|
+
exit 1
|
472
|
+
end
|
473
|
+
|
474
|
+
exec_compose('exec', service_name, 'bash', '-lc',
|
475
|
+
"bundle exec rails #{Shellwords.join(rails_args)}")
|
322
476
|
end
|
323
477
|
|
324
478
|
def open_console(service_name)
|
325
479
|
unless service_name
|
326
480
|
puts "❌ Error: Service name required"
|
327
481
|
puts "Usage: regolith console <service_name>"
|
482
|
+
exit 1
|
483
|
+
end
|
484
|
+
|
485
|
+
ensure_service_exists!(service_name)
|
486
|
+
puts "🧪 Opening Rails console for #{service_name}_service..."
|
487
|
+
exec_compose('exec', service_name, 'rails', 'console')
|
488
|
+
end
|
489
|
+
|
490
|
+
def open_service(service_name)
|
491
|
+
unless service_name
|
492
|
+
puts "❌ Error: Service name required"
|
493
|
+
puts "Usage: regolith open <service_name>"
|
494
|
+
exit 1
|
495
|
+
end
|
496
|
+
|
497
|
+
ensure_service_exists!(service_name)
|
498
|
+
config = load_regolith_config
|
499
|
+
port = config['services'][service_name]['port']
|
500
|
+
url = "http://localhost:#{port}"
|
501
|
+
|
502
|
+
puts "🌐 Opening #{url}..."
|
503
|
+
|
504
|
+
# Cross-platform open command
|
505
|
+
case RbConfig::CONFIG['host_os']
|
506
|
+
when /mswin|mingw|cygwin/
|
507
|
+
system(%{start "" "#{url}"})
|
508
|
+
when /darwin/
|
509
|
+
system("open #{url}")
|
510
|
+
else
|
511
|
+
system("xdg-open #{url}") || puts("Visit: #{url}")
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
# Database commands
|
516
|
+
def db_command(action, target, flags = {})
|
517
|
+
case action
|
518
|
+
when 'create', 'migrate', 'seed', 'reset', 'setup', 'drop'
|
519
|
+
each_target_service(target, flags) do |service|
|
520
|
+
puts "🗄 Running db:#{action} for #{service}..."
|
521
|
+
success = system_compose('exec', service, 'bash', '-lc', "bundle exec rails db:#{action}")
|
522
|
+
|
523
|
+
unless success
|
524
|
+
puts "❌ db:#{action} failed for #{service} (exit code: #{$?.exitstatus})"
|
525
|
+
exit 1 unless flags[:continue_on_failure]
|
526
|
+
end
|
527
|
+
end
|
528
|
+
else
|
529
|
+
puts "❌ Unknown db action: #{action}"
|
530
|
+
puts "Available: create, migrate, seed, reset, setup, drop"
|
531
|
+
exit 1
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
# Testing commands
|
536
|
+
def run_tests(target = nil, flags = {})
|
537
|
+
each_target_service(target, flags) do |service|
|
538
|
+
puts "🧪 Running tests for #{service}..."
|
539
|
+
success = system_compose('exec', service, 'bash', '-lc', 'bundle exec rails test')
|
540
|
+
|
541
|
+
unless success
|
542
|
+
puts "❌ Tests failed for #{service} (exit code: #{$?.exitstatus})"
|
543
|
+
exit 1 unless flags[:continue_on_failure]
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
puts "✅ All tests passed!" if flags[:all]
|
548
|
+
end
|
549
|
+
|
550
|
+
def show_routes(service_name)
|
551
|
+
unless service_name
|
552
|
+
puts "❌ Error: Service name required"
|
553
|
+
puts "Usage: regolith routes <service_name>"
|
554
|
+
exit 1
|
555
|
+
end
|
556
|
+
|
557
|
+
ensure_service_exists!(service_name)
|
558
|
+
puts "🛤 Routes for #{service_name}:"
|
559
|
+
system_compose('exec', service_name, 'bash', '-lc', 'bundle exec rails routes')
|
560
|
+
end
|
561
|
+
|
562
|
+
# Health and monitoring
|
563
|
+
def health_check
|
564
|
+
puts "🔍 Health Check Results:"
|
565
|
+
puts
|
566
|
+
|
567
|
+
config = load_regolith_config
|
568
|
+
|
569
|
+
config['services'].each do |name, service_config|
|
570
|
+
port = service_config['port']
|
571
|
+
status = check_service_health(port)
|
572
|
+
|
573
|
+
status_icon = status[:healthy] ? '✅' : '❌'
|
574
|
+
puts "#{status_icon} #{name} (port #{port}) - #{status[:message]}"
|
575
|
+
|
576
|
+
# Show additional health data if available
|
577
|
+
if status[:data] && status[:data]['version']
|
578
|
+
puts " Version: #{status[:data]['version']}"
|
579
|
+
end
|
580
|
+
if status[:data] && status[:data]['time']
|
581
|
+
puts " Last seen: #{Time.at(status[:data]['time']).strftime('%H:%M:%S')}"
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
def check_service_health(port)
|
587
|
+
begin
|
588
|
+
uri = URI("http://localhost:#{port}/health")
|
589
|
+
response = Net::HTTP.get_response(uri)
|
590
|
+
|
591
|
+
if response.code.to_i == 200
|
592
|
+
# Try to parse structured health data
|
593
|
+
health_data = JSON.parse(response.body) rescue {}
|
594
|
+
{
|
595
|
+
healthy: true,
|
596
|
+
message: 'healthy',
|
597
|
+
data: health_data
|
598
|
+
}
|
599
|
+
else
|
600
|
+
{ healthy: false, message: "HTTP #{response.code}" }
|
601
|
+
end
|
602
|
+
rescue => e
|
603
|
+
{ healthy: false, message: 'unreachable' }
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
# Configuration
|
608
|
+
def show_config(flags = {})
|
609
|
+
config = load_regolith_config
|
610
|
+
|
611
|
+
if flags[:json]
|
612
|
+
puts JSON.pretty_generate(config)
|
613
|
+
else
|
614
|
+
puts "📋 Current Configuration:"
|
615
|
+
puts
|
616
|
+
puts YAML.dump(config)
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
# Maintenance commands
|
621
|
+
def prune_system
|
622
|
+
puts "🧹 Pruning Docker system..."
|
623
|
+
exec_compose('down', '-v')
|
624
|
+
system('docker', 'system', 'prune', '-f')
|
625
|
+
puts "✅ System pruned"
|
626
|
+
end
|
627
|
+
|
628
|
+
def rebuild_service(service_name = nil)
|
629
|
+
if service_name
|
630
|
+
ensure_service_exists!(service_name)
|
631
|
+
puts "🔨 Rebuilding service '#{service_name}'..."
|
632
|
+
exec_compose('build', '--no-cache', service_name)
|
633
|
+
else
|
634
|
+
puts "🔨 Rebuilding all services..."
|
635
|
+
exec_compose('build', '--no-cache')
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
# System diagnostics
|
640
|
+
def run_doctor
|
641
|
+
puts "🩺 Regolith System Doctor"
|
642
|
+
puts "=" * 40
|
643
|
+
|
644
|
+
checks = [
|
645
|
+
{ name: "Docker", check: -> { system('docker --version > /dev/null 2>&1') } },
|
646
|
+
{ name: "Docker Compose", check: -> { docker_compose_available? } },
|
647
|
+
{ name: "Ruby", check: -> { system('ruby --version > /dev/null 2>&1') } },
|
648
|
+
{ name: "Rails", check: -> { system('rails --version > /dev/null 2>&1') } },
|
649
|
+
{ name: "PostgreSQL Client", check: -> { system('psql --version > /dev/null 2>&1') } }
|
650
|
+
]
|
651
|
+
|
652
|
+
checks.each do |check|
|
653
|
+
status = check[:check].call ? "✅" : "❌"
|
654
|
+
puts "#{status} #{check[:name]}"
|
655
|
+
end
|
656
|
+
|
657
|
+
puts
|
658
|
+
check_regolith_config
|
659
|
+
end
|
660
|
+
|
661
|
+
def check_regolith_config
|
662
|
+
puts "📋 Checking Regolith configuration..."
|
663
|
+
|
664
|
+
if File.exist?('config/regolith.yml')
|
328
665
|
config = load_regolith_config
|
329
|
-
|
666
|
+
|
667
|
+
if config['services'].empty?
|
668
|
+
puts "⚠️ No services configured"
|
669
|
+
else
|
670
|
+
puts "✅ Configuration valid (#{config['services'].size} services)"
|
671
|
+
end
|
672
|
+
else
|
673
|
+
puts "❌ No regolith.yml found - not in a Regolith project?"
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
def inspect_services(flags = {})
|
678
|
+
puts "🔍 Regolith Services Inspection"
|
679
|
+
puts "=" * 40
|
680
|
+
|
681
|
+
config = load_regolith_config
|
682
|
+
|
683
|
+
if config['services'].empty?
|
684
|
+
puts "No services configured yet."
|
685
|
+
return
|
686
|
+
end
|
687
|
+
|
688
|
+
if flags[:json]
|
689
|
+
# JSON output for automation
|
690
|
+
inspection_data = {
|
691
|
+
services: config['services'].map do |name, service_config|
|
692
|
+
{
|
693
|
+
name: name,
|
694
|
+
port: service_config['port'],
|
695
|
+
endpoint: "http://localhost:#{service_config['port']}",
|
696
|
+
root: service_config['root']
|
697
|
+
}
|
698
|
+
end,
|
699
|
+
config: config
|
700
|
+
}
|
701
|
+
puts JSON.pretty_generate(inspection_data)
|
702
|
+
else
|
703
|
+
# Human-readable output
|
704
|
+
puts "\n📊 Service Endpoints:"
|
705
|
+
config['services'].each do |name, service_config|
|
706
|
+
port = service_config['port']
|
707
|
+
puts " #{name}: http://localhost:#{port}"
|
708
|
+
end
|
709
|
+
|
710
|
+
puts "\n📋 Full Configuration:"
|
711
|
+
puts YAML.dump(config)
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
# Helper methods
|
716
|
+
def docker_compose_available?
|
717
|
+
system('docker compose version > /dev/null 2>&1') ||
|
718
|
+
system('docker-compose version > /dev/null 2>&1')
|
719
|
+
end
|
720
|
+
|
721
|
+
def docker_compose_command
|
722
|
+
if system('docker compose version > /dev/null 2>&1')
|
723
|
+
%w[docker compose]
|
724
|
+
elsif system('docker-compose version > /dev/null 2>&1')
|
725
|
+
%w[docker-compose]
|
726
|
+
else
|
727
|
+
puts "❌ Docker Compose not found"
|
330
728
|
exit 1
|
331
729
|
end
|
730
|
+
end
|
731
|
+
|
732
|
+
def exec_compose(*args)
|
733
|
+
cmd = docker_compose_command + args
|
734
|
+
exec(*cmd)
|
735
|
+
end
|
736
|
+
|
737
|
+
def system_compose(*args)
|
738
|
+
cmd = docker_compose_command + args
|
739
|
+
system(*cmd)
|
740
|
+
end
|
741
|
+
|
742
|
+
def each_target_service(target, flags = {})
|
743
|
+
services = if target == '--all' || target.nil? || flags[:all]
|
744
|
+
load_regolith_config['services'].keys
|
745
|
+
else
|
746
|
+
[target]
|
747
|
+
end
|
748
|
+
|
749
|
+
services.each do |service|
|
750
|
+
ensure_service_exists!(service)
|
751
|
+
yield(service)
|
752
|
+
end
|
753
|
+
end
|
332
754
|
|
755
|
+
def ensure_service_exists!(service_name)
|
333
756
|
config = load_regolith_config
|
334
|
-
unless config['services']
|
335
|
-
puts "❌
|
757
|
+
unless config['services'].key?(service_name)
|
758
|
+
puts "❌ Service '#{service_name}' not found"
|
336
759
|
puts "Available services: #{config['services'].keys.join(', ')}"
|
337
760
|
exit 1
|
338
761
|
end
|
762
|
+
end
|
339
763
|
|
340
|
-
|
341
|
-
|
764
|
+
def show_service_info(config)
|
765
|
+
config['services'].each do |name, service|
|
766
|
+
puts "🚀 #{name}_service: http://localhost:#{service['port']}"
|
767
|
+
end
|
768
|
+
|
769
|
+
puts "🧭 Service registry: config/regolith.yml"
|
770
|
+
puts
|
342
771
|
end
|
343
772
|
|
344
773
|
def load_regolith_config
|
345
|
-
|
346
|
-
|
774
|
+
config_path = Regolith.send(:find_regolith_config)
|
775
|
+
return { 'services' => {} } unless config_path && File.exist?(config_path)
|
776
|
+
|
777
|
+
# Use safe YAML loading
|
778
|
+
config = Psych.safe_load(File.read(config_path), permitted_classes: [], aliases: false) || {}
|
347
779
|
config['services'] ||= {}
|
348
780
|
config
|
349
781
|
end
|
@@ -351,6 +783,7 @@ module Regolith
|
|
351
783
|
def save_regolith_config(config)
|
352
784
|
FileUtils.mkdir_p('config')
|
353
785
|
File.write('config/regolith.yml', YAML.dump(config))
|
786
|
+
Regolith.reload_service_registry!
|
354
787
|
end
|
355
788
|
|
356
789
|
def update_docker_compose(config)
|
@@ -358,56 +791,96 @@ module Regolith
|
|
358
791
|
File.write('docker-compose.yml', docker_compose)
|
359
792
|
end
|
360
793
|
|
794
|
+
# File generators
|
361
795
|
def generate_docker_compose(app_name, services = {})
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
796
|
+
compose_services = {
|
797
|
+
'db' => {
|
798
|
+
'image' => 'postgres:14',
|
799
|
+
'environment' => {
|
800
|
+
'POSTGRES_DB' => "#{app_name}_development",
|
801
|
+
'POSTGRES_USER' => 'postgres',
|
802
|
+
'POSTGRES_PASSWORD' => 'password'
|
803
|
+
},
|
804
|
+
'ports' => ['5432:5432'],
|
805
|
+
'volumes' => ['postgres_data:/var/lib/postgresql/data'],
|
806
|
+
'networks' => ['regolith'],
|
807
|
+
'healthcheck' => {
|
808
|
+
'test' => ['CMD-SHELL', 'pg_isready -U postgres'],
|
809
|
+
'interval' => '10s',
|
810
|
+
'timeout' => '5s',
|
811
|
+
'retries' => 5
|
812
|
+
}
|
813
|
+
}
|
814
|
+
}
|
815
|
+
|
816
|
+
services.each do |name, service|
|
817
|
+
compose_services[name] = {
|
818
|
+
'build' => {
|
819
|
+
'context' => service['root'],
|
820
|
+
'args' => {
|
821
|
+
'BUILD_ENV' => 'development'
|
822
|
+
}
|
823
|
+
},
|
824
|
+
'ports' => ["#{service['port']}:3000"],
|
825
|
+
'networks' => ['regolith'],
|
826
|
+
'depends_on' => {
|
827
|
+
'db' => {
|
828
|
+
'condition' => 'service_healthy'
|
829
|
+
}
|
830
|
+
},
|
831
|
+
'environment' => {
|
832
|
+
'DATABASE_URL' => "postgres://postgres:password@db:5432/#{app_name}_development",
|
833
|
+
'REGOLITH_SERVICE_NAME' => name,
|
834
|
+
'REGOLITH_SERVICE_PORT' => service['port']
|
835
|
+
},
|
836
|
+
'volumes' => ["#{service['root']}:/app"],
|
837
|
+
'command' => 'bash -c "rm -f tmp/pids/server.pid && bundle install && rails db:prepare && rails server -b 0.0.0.0"',
|
838
|
+
'healthcheck' => {
|
839
|
+
'test' => ['CMD-SHELL', 'curl -f http://localhost:3000/health || exit 1'],
|
840
|
+
'interval' => '30s',
|
841
|
+
'timeout' => '10s',
|
842
|
+
'retries' => 3,
|
843
|
+
'start_period' => '40s'
|
844
|
+
}
|
845
|
+
}
|
846
|
+
end
|
847
|
+
|
848
|
+
{
|
849
|
+
'version' => '3.8',
|
850
|
+
'networks' => {
|
851
|
+
'regolith' => {}
|
852
|
+
},
|
853
|
+
'services' => compose_services,
|
854
|
+
'volumes' => {
|
855
|
+
'postgres_data' => nil
|
856
|
+
}
|
857
|
+
}.to_yaml
|
398
858
|
end
|
399
859
|
|
400
860
|
def generate_dockerfile
|
401
861
|
<<~DOCKERFILE
|
402
|
-
FROM ruby:3.1
|
862
|
+
FROM ruby:3.1-slim
|
863
|
+
|
864
|
+
# Install system dependencies
|
865
|
+
RUN apt-get update -qq && \\
|
866
|
+
apt-get install -y nodejs postgresql-client libyaml-dev build-essential pkg-config curl && \\
|
867
|
+
apt-get clean && \\
|
868
|
+
rm -rf /var/lib/apt/lists/*
|
403
869
|
|
404
870
|
WORKDIR /app
|
405
871
|
|
406
|
-
|
872
|
+
# Copy Gemfile and install gems
|
873
|
+
COPY Gemfile Gemfile.lock* ./
|
407
874
|
|
408
|
-
|
875
|
+
# Conditional bundler config for dev vs prod
|
876
|
+
ARG BUILD_ENV=development
|
877
|
+
RUN if [ "$BUILD_ENV" = "production" ]; then \\
|
878
|
+
bundle config set --local deployment 'true' && \\
|
879
|
+
bundle config set --local without 'development test'; \\
|
880
|
+
fi && bundle install
|
409
881
|
|
410
|
-
|
882
|
+
# Copy application code
|
883
|
+
COPY . .
|
411
884
|
|
412
885
|
EXPOSE 3000
|
413
886
|
|
@@ -423,10 +896,12 @@ module Regolith
|
|
423
896
|
Rails.application.configure do
|
424
897
|
config.regolith = OpenStruct.new(
|
425
898
|
service_name: '#{service_name}',
|
426
|
-
service_registry: Rails.root.join('../../config/regolith.yml')
|
899
|
+
service_registry: Rails.root.join('../../config/regolith.yml'),
|
900
|
+
version: Regolith::VERSION
|
427
901
|
)
|
428
902
|
end
|
429
903
|
|
904
|
+
# Load service registry if available
|
430
905
|
if File.exist?(Rails.application.config.regolith.service_registry)
|
431
906
|
REGOLITH_SERVICES = YAML.load_file(Rails.application.config.regolith.service_registry)['services'] || {}
|
432
907
|
else
|
@@ -435,33 +910,184 @@ module Regolith
|
|
435
910
|
RUBY
|
436
911
|
end
|
437
912
|
|
913
|
+
def generate_cors_initializer
|
914
|
+
<<~RUBY
|
915
|
+
# CORS configuration for microservices
|
916
|
+
Rails.application.config.middleware.insert_before 0, Rack::Cors do
|
917
|
+
allow do
|
918
|
+
origins '*' # Configure appropriately for production
|
919
|
+
resource '*',
|
920
|
+
headers: :any,
|
921
|
+
methods: %i[get post put patch delete options head],
|
922
|
+
expose: %w[Authorization Content-Type],
|
923
|
+
max_age: 600
|
924
|
+
end
|
925
|
+
end
|
926
|
+
RUBY
|
927
|
+
end
|
928
|
+
|
929
|
+
def generate_health_controller
|
930
|
+
<<~RUBY
|
931
|
+
module Regolith
|
932
|
+
class HealthController < ActionController::API
|
933
|
+
def show
|
934
|
+
render json: {
|
935
|
+
ok: true,
|
936
|
+
service: Rails.application.config.regolith_service_name,
|
937
|
+
time: Time.now.to_i,
|
938
|
+
version: Rails.application.config.regolith.version
|
939
|
+
}
|
940
|
+
end
|
941
|
+
end
|
942
|
+
end
|
943
|
+
RUBY
|
944
|
+
end
|
945
|
+
|
946
|
+
def generate_gitignore
|
947
|
+
<<~GITIGNORE
|
948
|
+
# Regolith
|
949
|
+
/services/*/log/*
|
950
|
+
/services/*/tmp/*
|
951
|
+
/services/*/.env*
|
952
|
+
.DS_Store
|
953
|
+
|
954
|
+
# Docker
|
955
|
+
docker-compose.override.yml
|
956
|
+
|
957
|
+
# Logs
|
958
|
+
*.log
|
959
|
+
|
960
|
+
# Runtime data
|
961
|
+
pids
|
962
|
+
*.pid
|
963
|
+
*.seed
|
964
|
+
|
965
|
+
# Environment variables
|
966
|
+
.env*
|
967
|
+
!.env.example
|
968
|
+
GITIGNORE
|
969
|
+
end
|
970
|
+
|
971
|
+
def generate_readme(app_name)
|
972
|
+
<<~MARKDOWN
|
973
|
+
# #{app_name.capitalize}
|
974
|
+
|
975
|
+
A Regolith microservices application built with Rails and Docker.
|
976
|
+
|
977
|
+
## Getting Started
|
978
|
+
|
979
|
+
```bash
|
980
|
+
# Start all services
|
981
|
+
regolith server
|
982
|
+
|
983
|
+
# Generate a new service
|
984
|
+
regolith generate service users
|
985
|
+
|
986
|
+
# Open service in browser
|
987
|
+
regolith open users
|
988
|
+
|
989
|
+
# View service logs
|
990
|
+
regolith logs users -f
|
991
|
+
|
992
|
+
# Run database migrations
|
993
|
+
regolith db:migrate --all
|
994
|
+
|
995
|
+
# Health check
|
996
|
+
regolith health
|
997
|
+
```
|
998
|
+
|
999
|
+
## Services
|
1000
|
+
|
1001
|
+
#{services_documentation}
|
1002
|
+
|
1003
|
+
## Development
|
1004
|
+
|
1005
|
+
```bash
|
1006
|
+
# Open Rails console for a service
|
1007
|
+
regolith console users
|
1008
|
+
|
1009
|
+
# Run Rails commands
|
1010
|
+
regolith rails users db:migrate
|
1011
|
+
regolith rails users routes
|
1012
|
+
|
1013
|
+
# Run tests
|
1014
|
+
regolith test --all
|
1015
|
+
|
1016
|
+
# Execute commands in service
|
1017
|
+
regolith exec users bash
|
1018
|
+
```
|
1019
|
+
|
1020
|
+
## Architecture
|
1021
|
+
|
1022
|
+
- **Rails 7** API-only applications
|
1023
|
+
- **PostgreSQL** for persistence
|
1024
|
+
- **Docker Compose** for orchestration
|
1025
|
+
- **Service registry** for inter-service communication
|
1026
|
+
|
1027
|
+
Built with [Regolith](https://regolith.bio) - Rails for distributed systems.
|
1028
|
+
MARKDOWN
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def services_documentation
|
1032
|
+
config = load_regolith_config
|
1033
|
+
return "No services yet. Run `regolith generate service <name>` to create one." if config['services'].empty?
|
1034
|
+
|
1035
|
+
config['services'].map do |name, service|
|
1036
|
+
"- **#{name}** - http://localhost:#{service['port']}"
|
1037
|
+
end.join("\n")
|
1038
|
+
end
|
1039
|
+
|
438
1040
|
def generate_makefile
|
439
1041
|
<<~MAKEFILE
|
440
|
-
.PHONY: server
|
1042
|
+
.PHONY: server up down restart logs console test health doctor
|
441
1043
|
|
442
|
-
|
1044
|
+
# Start services
|
1045
|
+
server up:
|
443
1046
|
regolith server
|
444
1047
|
|
1048
|
+
# Stop services
|
1049
|
+
down:
|
1050
|
+
regolith down
|
1051
|
+
|
1052
|
+
# Restart services
|
445
1053
|
restart:
|
446
1054
|
regolith restart
|
447
1055
|
|
448
|
-
|
449
|
-
regolith stop
|
450
|
-
|
1056
|
+
# View logs
|
451
1057
|
logs:
|
452
|
-
regolith logs
|
453
|
-
|
454
|
-
status:
|
455
|
-
regolith status
|
1058
|
+
regolith logs -f
|
456
1059
|
|
1060
|
+
# Open console (usage: make console SERVICE=users)
|
457
1061
|
console:
|
458
|
-
|
1062
|
+
@if [ -z "$(SERVICE)" ]; then echo "Usage: make console SERVICE=service_name"; exit 1; fi
|
1063
|
+
regolith console $(SERVICE)
|
1064
|
+
|
1065
|
+
# Run tests
|
1066
|
+
test:
|
1067
|
+
regolith test --all
|
1068
|
+
|
1069
|
+
# Health check
|
1070
|
+
health:
|
1071
|
+
regolith health
|
1072
|
+
|
1073
|
+
# System diagnostics
|
1074
|
+
doctor:
|
1075
|
+
regolith doctor
|
1076
|
+
|
1077
|
+
# Database operations
|
1078
|
+
db-migrate:
|
1079
|
+
regolith db:migrate --all
|
459
1080
|
|
460
|
-
|
461
|
-
|
1081
|
+
db-setup:
|
1082
|
+
regolith db:setup --all
|
462
1083
|
|
1084
|
+
# Cleanup
|
463
1085
|
clean:
|
464
|
-
regolith
|
1086
|
+
regolith prune
|
1087
|
+
|
1088
|
+
# Shortcuts
|
1089
|
+
dev: up
|
1090
|
+
stop: down
|
465
1091
|
MAKEFILE
|
466
1092
|
end
|
467
1093
|
|
@@ -474,38 +1100,62 @@ module Regolith
|
|
474
1100
|
|
475
1101
|
def show_help
|
476
1102
|
puts <<~HELP
|
477
|
-
Regolith #{Regolith::VERSION} -
|
1103
|
+
Regolith #{Regolith::VERSION} - Rails for Distributed Systems
|
478
1104
|
|
479
1105
|
USAGE:
|
480
1106
|
regolith <command> [options]
|
481
1107
|
|
482
|
-
COMMANDS:
|
1108
|
+
PROJECT COMMANDS:
|
483
1109
|
new <app_name> Create a new Regolith application
|
484
1110
|
generate service <name> Generate a new microservice
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
1111
|
+
|
1112
|
+
SERVICE MANAGEMENT:
|
1113
|
+
server, up Start all services with Docker Compose
|
1114
|
+
down Stop and remove all services
|
1115
|
+
restart [service] Restart one or all services
|
1116
|
+
stop [service] Stop one or all services
|
1117
|
+
ps, status Show running containers
|
1118
|
+
logs [service] [-f] View service logs
|
1119
|
+
exec <service> [cmd] Execute command in service container
|
1120
|
+
shell <service> Open shell in service container
|
1121
|
+
rebuild [service] Rebuild service images
|
1122
|
+
|
1123
|
+
RAILS INTEGRATION:
|
1124
|
+
console <service> Open Rails console for service
|
1125
|
+
rails <service> <cmd> Run Rails command in service
|
1126
|
+
routes <service> Show routes for service
|
1127
|
+
open <service> Open service in browser
|
1128
|
+
|
1129
|
+
DATABASE OPERATIONS:
|
1130
|
+
db:migrate [service|--all] Run migrations
|
1131
|
+
db:create [service|--all] Create databases
|
1132
|
+
db:seed [service|--all] Seed databases
|
1133
|
+
db:reset [service|--all] Reset databases
|
1134
|
+
db:setup [service|--all] Setup databases
|
1135
|
+
db:drop [service|--all] Drop databases
|
1136
|
+
|
1137
|
+
TESTING & HEALTH:
|
1138
|
+
test [service|--all] Run tests
|
1139
|
+
health Health check all services
|
1140
|
+
|
1141
|
+
UTILITIES:
|
1142
|
+
config [--json] Show current configuration
|
1143
|
+
inspect [--json] Show services and resolved endpoints
|
1144
|
+
prune Clean up Docker system
|
1145
|
+
doctor Run system diagnostics
|
1146
|
+
version Show version
|
493
1147
|
|
494
1148
|
EXAMPLES:
|
495
|
-
regolith new
|
496
|
-
regolith generate service
|
1149
|
+
regolith new marketplace
|
1150
|
+
regolith generate service products
|
497
1151
|
regolith server
|
498
|
-
regolith
|
499
|
-
regolith
|
500
|
-
regolith
|
501
|
-
regolith
|
502
|
-
|
503
|
-
|
504
|
-
regolith
|
505
|
-
regolith restart element # Restart just element service
|
506
|
-
regolith logs element # Follow element service logs
|
507
|
-
regolith stop # Stop all services
|
508
|
-
regolith clean # Clean containers and volumes
|
1152
|
+
regolith rails products db:migrate
|
1153
|
+
regolith routes products
|
1154
|
+
regolith open products
|
1155
|
+
regolith shell products
|
1156
|
+
regolith inspect --json
|
1157
|
+
regolith config --json | jq '.services'
|
1158
|
+
regolith test --all
|
509
1159
|
|
510
1160
|
Get started:
|
511
1161
|
regolith new myapp
|
@@ -515,4 +1165,4 @@ module Regolith
|
|
515
1165
|
HELP
|
516
1166
|
end
|
517
1167
|
end
|
518
|
-
end
|
1168
|
+
end
|