regolith 0.1.8 → 0.1.10

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.
data/lib/regolith/cli.rb CHANGED
@@ -1,60 +1,13 @@
1
1
  require 'fileutils'
2
2
  require 'yaml'
3
- require 'psych'
4
3
  require 'erb'
5
4
  require 'timeout'
6
5
  require 'rubygems'
7
- require 'net/http'
8
- require 'uri'
9
- require 'json'
10
- require 'ostruct'
11
- require 'set'
12
6
  require 'rbconfig'
13
7
  require 'shellwords'
8
+ require 'psych'
14
9
 
15
10
  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
-
58
11
  class CLI
59
12
  def initialize(args)
60
13
  @args = args
@@ -70,9 +23,9 @@ module Regolith
70
23
  when 'generate'
71
24
  generate_resource(@subcommand, @name)
72
25
  when 'server', 'up'
73
- start_server(parse_flags(@args[1..-1]))
26
+ start_server
74
27
  when 'down'
75
- stop_services
28
+ stop_all_services
76
29
  when 'restart'
77
30
  restart_service(@subcommand)
78
31
  when 'stop'
@@ -80,36 +33,36 @@ module Regolith
80
33
  when 'ps', 'status'
81
34
  show_status
82
35
  when 'logs'
83
- show_logs(@subcommand, parse_flags(@args[2..-1]))
36
+ show_logs(@subcommand, @args[2] == '-f' || @args[2] == '--follow')
84
37
  when 'exec'
85
- exec_command(@subcommand, @args[2..-1])
38
+ exec_in_service(@subcommand, @args[2..-1])
86
39
  when 'shell'
87
40
  shell_service(@subcommand)
88
- when 'console'
89
- open_console(@subcommand)
90
41
  when 'rails'
91
42
  rails_passthrough(@subcommand, @args[2..-1])
43
+ when 'console'
44
+ open_console(@subcommand)
92
45
  when 'routes'
93
46
  show_routes(@subcommand)
94
47
  when 'open'
95
48
  open_service(@subcommand)
96
- when 'db'
97
- db_command(@subcommand, @name, parse_flags(@args[3..-1]))
49
+ when 'db:create', 'db:migrate', 'db:seed', 'db:reset', 'db:setup', 'db:drop'
50
+ db_command(@command, @subcommand)
98
51
  when 'test'
99
- run_tests(@subcommand, parse_flags(@args[2..-1]))
52
+ run_tests(@subcommand)
100
53
  when 'health'
101
- health_check
54
+ check_health
102
55
  when 'config'
103
- show_config(parse_flags(@args[1..-1]))
56
+ show_config(@subcommand == '--json')
57
+ when 'inspect'
58
+ inspect_config(@subcommand == '--json')
59
+ when 'doctor'
60
+ run_diagnostics
104
61
  when 'prune'
105
- prune_system
62
+ prune_docker
106
63
  when 'rebuild'
107
64
  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'
65
+ when 'version'
113
66
  puts "Regolith #{Regolith::VERSION}"
114
67
  else
115
68
  show_help
@@ -118,23 +71,6 @@ module Regolith
118
71
 
119
72
  private
120
73
 
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
133
- end
134
- end
135
- flags
136
- end
137
-
138
74
  def create_new_app(app_name)
139
75
  unless app_name
140
76
  puts "❌ Error: App name required"
@@ -172,8 +108,6 @@ module Regolith
172
108
  File.write('docker-compose.yml', generate_docker_compose(app_name))
173
109
  File.write('Makefile', generate_makefile)
174
110
  File.write('.bin/regolith', generate_regolith_shim)
175
- File.write('.gitignore', generate_gitignore)
176
- File.write('README.md', generate_readme(app_name))
177
111
  FileUtils.chmod(0755, '.bin/regolith')
178
112
  end
179
113
 
@@ -184,17 +118,16 @@ module Regolith
184
118
  exit 1
185
119
  end
186
120
 
187
- generate_service(resource_name)
188
- end
189
-
190
- def generate_service(service_name)
191
121
  # 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"
122
+ unless resource_name =~ /\A[a-z][a-z0-9_]*\z/
123
+ puts "❌ Invalid service name. Use lowercase letters, digits, and underscores."
195
124
  exit 1
196
125
  end
197
126
 
127
+ generate_service(resource_name)
128
+ end
129
+
130
+ def generate_service(service_name)
198
131
  puts "🔧 Creating service '#{service_name}'..."
199
132
  config = load_regolith_config
200
133
  port = next_available_port
@@ -228,51 +161,49 @@ module Regolith
228
161
  exit 1
229
162
  end
230
163
 
231
- customize_service(service_dir, service_name, port)
232
-
164
+ # Generate custom Gemfile
165
+ puts "🔧 Creating custom Gemfile..."
166
+ generate_gemfile(service_dir)
167
+
168
+ # Vendor Regolith gem - FIXED VERSION
169
+ vendor_regolith_gem(service_dir)
170
+
171
+ puts " → Running bundle install..."
172
+ Dir.chdir(service_dir) do
173
+ unless system("bundle install")
174
+ puts "❌ bundle install failed"
175
+ puts "→ You may be missing system libraries like libyaml-dev libsqlite3-dev build-essential pkg-config"
176
+ puts "→ Try: sudo apt install -y libyaml-dev libsqlite3-dev build-essential pkg-config"
177
+ exit 1
178
+ end
179
+ end
180
+
181
+ patch_rails_app(service_dir, service_name, port)
182
+
233
183
  config['services'][service_name] = {
234
184
  'port' => port,
235
185
  'root' => "./#{service_dir}"
236
186
  }
237
-
238
187
  save_regolith_config(config)
239
188
  update_docker_compose(config)
240
189
 
241
190
  puts "✅ Created service '#{service_name}'"
242
191
  puts "🚀 Service will run on port #{port}"
243
- puts "→ Next: regolith generate service <another_service> or regolith server"
192
+ puts "→ Next: regolith server"
244
193
  end
245
194
 
246
- def next_available_port(start = Regolith.configuration.default_port)
247
- used = load_regolith_config['services'].values.map { |s| s['port'] }.to_set
195
+ def next_available_port(start = 3001)
196
+ config = load_regolith_config
197
+ used_ports = config['services']&.values&.map { |s| s['port'] }.to_set || Set.new
248
198
  port = start
249
- port += 1 while used.include?(port)
199
+ port += 1 while used_ports.include?(port)
250
200
  port
251
201
  end
252
202
 
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)
261
-
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
203
+ def generate_gemfile(service_dir)
204
+ ruby_version = RUBY_VERSION.split('.')[0..1].join('.')
273
205
 
274
- def generate_gemfile(ruby_version)
275
- <<~GEMFILE
206
+ custom_gemfile = <<~GEMFILE
276
207
  source "https://rubygems.org"
277
208
 
278
209
  ruby "~> #{ruby_version}.0"
@@ -281,7 +212,6 @@ module Regolith
281
212
  gem "pg", "~> 1.5"
282
213
  gem "puma", ">= 5.0"
283
214
  gem "rack-cors"
284
- gem "bootsnap", require: false
285
215
 
286
216
  group :development, :test do
287
217
  gem "debug", platforms: %i[ mri mswin mswin64 mingw x64_mingw ], require: "debug/prelude"
@@ -291,162 +221,199 @@ module Regolith
291
221
 
292
222
  gem "regolith", path: "vendor/regolith"
293
223
  GEMFILE
224
+
225
+ File.write("#{service_dir}/Gemfile", custom_gemfile)
294
226
  end
295
227
 
296
228
  def vendor_regolith_gem(service_dir)
297
229
  vendor_dir = File.join(service_dir, "vendor")
298
230
  FileUtils.mkdir_p(vendor_dir)
299
231
 
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}"
232
+ begin
233
+ # Try to find the regolith gem path
234
+ regolith_gem_path = if Gem.loaded_specs['regolith']
235
+ Gem.loaded_specs['regolith'].full_gem_path
236
+ else
237
+ # Fallback: search gem paths
238
+ Gem.path.each do |gem_path|
239
+ regolith_path = File.join(gem_path, 'gems', "regolith-#{Regolith::VERSION}")
240
+ return regolith_path if File.exist?(regolith_path)
241
+ end
242
+ nil
243
+ end
244
+
245
+ if regolith_gem_path && File.exist?(regolith_gem_path)
246
+ regolith_dest = File.join(vendor_dir, "regolith")
247
+ FileUtils.cp_r(regolith_gem_path, regolith_dest)
248
+ puts "📦 Vendored Regolith gem from #{regolith_gem_path}"
249
+ else
250
+ puts "⚠️ Could not find regolith gem path, using system gem instead"
251
+ # Update Gemfile to use system gem
252
+ gemfile_path = File.join(service_dir, "Gemfile")
253
+ gemfile_content = File.read(gemfile_path)
254
+ gemfile_content.gsub!('gem "regolith", path: "vendor/regolith"', 'gem "regolith", "~> 0.1.10"')
255
+ File.write(gemfile_path, gemfile_content)
256
+ end
257
+ rescue => e
258
+ puts "⚠️ Error vendoring regolith gem: #{e.message}"
259
+ puts " Using system gem instead"
260
+ # Update Gemfile to use system gem
261
+ gemfile_path = File.join(service_dir, "Gemfile")
262
+ gemfile_content = File.read(gemfile_path)
263
+ gemfile_content.gsub!('gem "regolith", path: "vendor/regolith"', 'gem "regolith", "~> 0.1.10"')
264
+ File.write(gemfile_path, gemfile_content)
265
+ end
305
266
  end
306
267
 
307
268
  def patch_rails_app(service_dir, service_name, port)
308
- create_initializers(service_dir, service_name)
309
- create_health_controller(service_dir)
269
+ # Add health controller
270
+ add_health_controller(service_dir)
271
+
272
+ # Add health route
310
273
  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)
274
+
275
+ # Create initializer
316
276
  initializer_dir = "#{service_dir}/config/initializers"
317
277
  FileUtils.mkdir_p(initializer_dir)
318
-
319
278
  File.write("#{initializer_dir}/regolith.rb", generate_regolith_initializer(service_name))
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
279
 
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
280
+ # Create Dockerfile
281
+ File.write("#{service_dir}/Dockerfile", generate_dockerfile)
342
282
 
343
- def patch_application_rb(service_dir, service_name, port)
283
+ # Patch application.rb
344
284
  app_rb_path = "#{service_dir}/config/application.rb"
345
285
  app_rb_content = File.read(app_rb_path)
346
286
 
347
287
  cors_config = <<~RUBY
348
288
 
349
289
  # Regolith configuration
290
+ config.middleware.insert_before 0, Rack::Cors do
291
+ allow do
292
+ origins '*'
293
+ resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head]
294
+ end
295
+ end
296
+
350
297
  config.regolith_service_name = '#{service_name}'
351
298
  config.regolith_service_port = #{port}
352
299
  RUBY
353
300
 
354
- app_rb_content.gsub!(/(\n end\n\z)/, "#{cors_config}\1")
301
+ app_rb_content.gsub!(/class Application < Rails::Application.*?
302
+ end/m) do |match|
303
+ match.gsub(/(\n end)$/, "#{cors_config}\1")
304
+ end
305
+
355
306
  File.write(app_rb_path, app_rb_content)
356
307
  end
357
308
 
358
- # Service management commands
359
- def start_server(flags = {})
360
- unless File.exist?('docker-compose.yml')
309
+ def add_health_controller(service_dir)
310
+ controller_dir = File.join(service_dir, "app", "controllers", "regolith")
311
+ FileUtils.mkdir_p(controller_dir)
312
+
313
+ health_controller = <<~RUBY
314
+ module Regolith
315
+ class HealthController < ActionController::API
316
+ def show
317
+ render json: {
318
+ ok: true,
319
+ service: Rails.application.config.regolith_service_name,
320
+ version: Regolith::VERSION,
321
+ time: Time.now.iso8601
322
+ }
323
+ end
324
+ end
325
+ end
326
+ RUBY
327
+
328
+ File.write(File.join(controller_dir, "health_controller.rb"), health_controller)
329
+ end
330
+
331
+ def add_health_route(service_dir)
332
+ routes_path = File.join(service_dir, "config", "routes.rb")
333
+ content = File.read(routes_path)
334
+
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 to file
339
+ File.open(routes_path, "a") { |f| f.puts "\nget '/health', to: 'regolith/health#show'\n" }
340
+ end
341
+ end
342
+
343
+ def start_server
344
+ unless find_regolith_config
361
345
  puts "❌ Error: Not in a Regolith app directory"
362
346
  exit 1
363
347
  end
364
348
 
365
349
  puts "🚀 Starting Regolith services..."
366
-
367
350
  config = load_regolith_config
368
- show_service_info(config)
369
351
 
370
- exec_compose('up', '--build')
352
+ config['services'].each do |name, service|
353
+ puts "🚀 #{name}_service will run at http://localhost:#{service['port']}"
354
+ end
355
+
356
+ puts "🧭 Service registry loaded from config/regolith.yml"
357
+ puts ""
358
+
359
+ exec_compose("up", "--build")
371
360
  end
372
361
 
373
- def stop_services
374
- puts "🛑 Stopping all services..."
375
- exec_compose('down', '-v')
362
+ def stop_all_services
363
+ exec_compose("down")
376
364
  end
377
365
 
378
- def restart_service(service_name = nil)
366
+ def restart_service(service_name)
379
367
  if service_name
380
- ensure_service_exists!(service_name)
381
- puts "🔄 Restarting service '#{service_name}'..."
382
- exec_compose('restart', service_name)
368
+ exec_compose("restart", service_name)
383
369
  else
384
- puts "🔄 Restarting all services..."
385
- exec_compose('restart')
370
+ exec_compose("restart")
386
371
  end
387
372
  end
388
373
 
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')
374
+ def stop_service(service_name)
375
+ unless service_name
376
+ puts "❌ Error: Service name required"
377
+ puts "Usage: regolith stop <service_name>"
378
+ exit 1
397
379
  end
380
+ exec_compose("stop", service_name)
398
381
  end
399
382
 
400
383
  def show_status
401
- puts "📊 Service Status:"
402
- puts
384
+ config = load_regolith_config
403
385
 
404
- # Try different format options for maximum compatibility
386
+ # Try different format options for docker compose ps
405
387
  success = system_compose('ps', '--format', 'table') ||
406
388
  system_compose('ps', '--format', 'json') ||
407
389
  system_compose('ps')
408
390
 
409
- # Show summary counts and health check for exit code
410
- if success
411
- config = load_regolith_config
412
- service_count = config['services'].size
391
+ if success && config['services'].any?
392
+ puts ""
393
+ puts "📋 Summary: #{config['services'].size} service#{'s' if config['services'].size != 1} configured"
413
394
  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
395
+ puts "🚪 Ports: #{ports.join(', ')}"
431
396
  end
397
+
398
+ exit($?.exitstatus) unless success
432
399
  end
433
400
 
434
- def show_logs(service_name = nil, flags = {})
401
+ def show_logs(service_name, follow = false)
435
402
  args = ['logs']
436
- args << '--follow' if flags[:follow] || flags[:f]
437
403
  args << service_name if service_name
438
-
404
+ args << '-f' if follow
439
405
  exec_compose(*args)
440
406
  end
441
407
 
442
- def exec_command(service_name, command_args)
443
- ensure_service_exists!(service_name)
444
-
445
- if command_args.empty?
446
- command_args = ['bash']
408
+ def exec_in_service(service_name, command)
409
+ unless service_name
410
+ puts "❌ Error: Service name required"
411
+ puts "Usage: regolith exec <service_name> <command>"
412
+ exit 1
447
413
  end
448
414
 
449
- exec_compose('exec', service_name, *command_args)
415
+ command = ['bash'] if command.empty?
416
+ exec_compose('exec', service_name, *command)
450
417
  end
451
418
 
452
419
  def shell_service(service_name)
@@ -455,24 +422,25 @@ module Regolith
455
422
  puts "Usage: regolith shell <service_name>"
456
423
  exit 1
457
424
  end
458
-
459
- ensure_service_exists!(service_name)
460
- puts "🐚 Opening shell for #{service_name}_service..."
461
425
  exec_compose('exec', service_name, 'bash')
462
426
  end
463
427
 
464
- # Rails integration commands
465
428
  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>"
429
+ unless service_name
430
+ puts "❌ Error: Service name required"
431
+ puts "Usage: regolith rails <service_name> <rails_command>"
471
432
  exit 1
472
433
  end
473
-
474
- exec_compose('exec', service_name, 'bash', '-lc',
475
- "bundle exec rails #{Shellwords.join(rails_args)}")
434
+
435
+ config = load_regolith_config
436
+ unless config['services'][service_name]
437
+ puts "❌ Error: Service '#{service_name}' not found"
438
+ exit 1
439
+ end
440
+
441
+ rails_args = ['--help'] if rails_args.empty?
442
+ exec_compose('exec', service_name, 'bash', '-lc',
443
+ "bundle exec rails #{Shellwords.join(rails_args)}")
476
444
  end
477
445
 
478
446
  def open_console(service_name)
@@ -482,11 +450,25 @@ module Regolith
482
450
  exit 1
483
451
  end
484
452
 
485
- ensure_service_exists!(service_name)
453
+ config = load_regolith_config
454
+ unless config['services'][service_name]
455
+ puts "❌ Error: Service '#{service_name}' not found"
456
+ exit 1
457
+ end
458
+
486
459
  puts "🧪 Opening Rails console for #{service_name}_service..."
487
460
  exec_compose('exec', service_name, 'rails', 'console')
488
461
  end
489
462
 
463
+ def show_routes(service_name)
464
+ unless service_name
465
+ puts "❌ Error: Service name required"
466
+ puts "Usage: regolith routes <service_name>"
467
+ exit 1
468
+ end
469
+ rails_passthrough(service_name, ['routes'])
470
+ end
471
+
490
472
  def open_service(service_name)
491
473
  unless service_name
492
474
  puts "❌ Error: Service name required"
@@ -494,296 +476,232 @@ module Regolith
494
476
  exit 1
495
477
  end
496
478
 
497
- ensure_service_exists!(service_name)
498
479
  config = load_regolith_config
499
- port = config['services'][service_name]['port']
500
- url = "http://localhost:#{port}"
501
-
480
+ service = config['services'][service_name]
481
+ unless service
482
+ puts "❌ Error: Service '#{service_name}' not found"
483
+ exit 1
484
+ end
485
+
486
+ url = "http://localhost:#{service['port']}"
502
487
  puts "🌐 Opening #{url}..."
503
-
504
- # Cross-platform open command
488
+
505
489
  case RbConfig::CONFIG['host_os']
506
490
  when /mswin|mingw|cygwin/
507
491
  system(%{start "" "#{url}"})
508
492
  when /darwin/
509
- system("open #{url}")
493
+ system("open", url)
494
+ when /linux|bsd/
495
+ system("xdg-open", url)
510
496
  else
511
- system("xdg-open #{url}") || puts("Visit: #{url}")
497
+ puts "Please open #{url} in your browser"
512
498
  end
513
499
  end
514
500
 
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
501
+ def db_command(command, service_or_flag)
502
+ db_action = command.split(':').last
503
+
504
+ if service_or_flag == '--all'
505
+ config = load_regolith_config
506
+ config['services'].each do |service_name, _|
507
+ puts "🗄️ Running db:#{db_action} for #{service_name}..."
508
+ run_single_db_command(service_name, db_action)
527
509
  end
510
+ elsif service_or_flag
511
+ run_single_db_command(service_or_flag, db_action)
528
512
  else
529
- puts "❌ Unknown db action: #{action}"
530
- puts "Available: create, migrate, seed, reset, setup, drop"
513
+ puts "❌ Error: Service name or --all flag required"
514
+ puts "Usage: regolith db:#{db_action} <service_name|--all>"
531
515
  exit 1
532
516
  end
533
517
  end
534
518
 
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
519
+ def run_single_db_command(service_name, action)
520
+ config = load_regolith_config
521
+ unless config['services'][service_name]
522
+ puts " Error: Service '#{service_name}' not found"
523
+ return false
545
524
  end
546
-
547
- puts "✅ All tests passed!" if flags[:all]
525
+
526
+ success = system_compose('exec', service_name, 'rails', "db:#{action}")
527
+ unless success
528
+ puts "❌ db:#{action} failed for #{service_name} (exit code: #{$?.exitstatus})"
529
+ return false
530
+ end
531
+ true
548
532
  end
549
533
 
550
- def show_routes(service_name)
551
- unless service_name
552
- puts "❌ Error: Service name required"
553
- puts "Usage: regolith routes <service_name>"
534
+ def run_tests(service_or_flag)
535
+ if service_or_flag == '--all'
536
+ config = load_regolith_config
537
+ all_passed = true
538
+ config['services'].each do |service_name, _|
539
+ puts "🧪 Running tests for #{service_name}..."
540
+ unless run_single_test(service_name)
541
+ all_passed = false
542
+ end
543
+ end
544
+ exit(1) unless all_passed
545
+ elsif service_or_flag
546
+ exit(1) unless run_single_test(service_or_flag)
547
+ else
548
+ puts "❌ Error: Service name or --all flag required"
549
+ puts "Usage: regolith test <service_name|--all>"
554
550
  exit 1
555
551
  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
552
  end
561
553
 
562
- # Health and monitoring
563
- def health_check
564
- puts "🔍 Health Check Results:"
565
- puts
566
-
554
+ def run_single_test(service_name)
567
555
  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
556
+ unless config['services'][service_name]
557
+ puts "❌ Error: Service '#{service_name}' not found"
558
+ return false
583
559
  end
584
- end
585
560
 
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' }
561
+ success = system_compose('exec', service_name, 'rails', 'test')
562
+ unless success
563
+ puts "❌ Tests failed for #{service_name} (exit code: #{$?.exitstatus})"
564
+ return false
604
565
  end
566
+ true
605
567
  end
606
568
 
607
- # Configuration
608
- def show_config(flags = {})
569
+ def check_health
609
570
  config = load_regolith_config
571
+ puts "🔍 Health Check Results:"
610
572
 
611
- if flags[:json]
612
- puts JSON.pretty_generate(config)
613
- else
614
- puts "📋 Current Configuration:"
615
- puts
616
- puts YAML.dump(config)
573
+ all_healthy = true
574
+ config['services'].each do |service_name, service|
575
+ health_data = check_service_health(service_name, service['port'])
576
+ if health_data
577
+ puts "✅ #{service_name} (port #{service['port']}) - healthy"
578
+ puts " Version: #{health_data['version']}" if health_data['version']
579
+ puts " Last seen: #{Time.parse(health_data['time']).strftime('%H:%M:%S')}" if health_data['time']
580
+ else
581
+ puts "❌ #{service_name} (port #{service['port']}) - unreachable"
582
+ all_healthy = false
583
+ end
617
584
  end
585
+
586
+ exit(1) unless all_healthy
618
587
  end
619
588
 
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"
589
+ def check_service_health(service_name, port)
590
+ require_relative 'service_client'
591
+ response = Regolith::ServiceClient.health(service_name)
592
+ Regolith::ServiceClient.parsed_body(response) if response.code.to_i == 200
593
+ rescue => e
594
+ nil
626
595
  end
627
596
 
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)
597
+ def show_config(json_format = false)
598
+ config = load_regolith_config
599
+ if json_format
600
+ puts JSON.pretty_generate(config)
633
601
  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]}"
602
+ puts YAML.dump(config)
655
603
  end
656
-
657
- puts
658
- check_regolith_config
659
604
  end
660
605
 
661
- def check_regolith_config
662
- puts "📋 Checking Regolith configuration..."
606
+ def inspect_config(json_format = false)
607
+ config = load_regolith_config
663
608
 
664
- if File.exist?('config/regolith.yml')
665
- config = load_regolith_config
666
-
667
- if config['services'].empty?
668
- puts "⚠️ No services configured"
669
- else
670
- puts "✅ Configuration valid (#{config['services'].size} services)"
609
+ if json_format
610
+ inspection = {
611
+ endpoints: {},
612
+ configuration: config
613
+ }
614
+ config['services'].each do |name, service|
615
+ inspection[:endpoints][name] = "http://localhost:#{service['port']}"
671
616
  end
617
+ puts JSON.pretty_generate(inspection)
672
618
  else
673
- puts " No regolith.yml found - not in a Regolith project?"
619
+ puts "📊 Service Endpoints:"
620
+ config['services'].each do |name, service|
621
+ puts " #{name}: http://localhost:#{service['port']}"
622
+ end
623
+ puts ""
624
+ puts "📋 Full Configuration:"
625
+ puts YAML.dump(config)
674
626
  end
675
627
  end
676
628
 
677
- def inspect_services(flags = {})
678
- puts "🔍 Regolith Services Inspection"
679
- puts "=" * 40
680
-
681
- config = load_regolith_config
629
+ def run_diagnostics
630
+ puts "🔍 Regolith System Diagnostics"
631
+ puts ""
682
632
 
683
- if config['services'].empty?
684
- puts "No services configured yet."
685
- return
633
+ # Check Docker
634
+ if system("docker --version > /dev/null 2>&1")
635
+ puts "✅ Docker is available"
636
+ else
637
+ puts "❌ Docker is not available"
686
638
  end
687
639
 
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)
640
+ # Check Docker Compose
641
+ if system("docker compose version > /dev/null 2>&1") ||
642
+ system("docker-compose --version > /dev/null 2>&1")
643
+ puts "✅ Docker Compose is available"
702
644
  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)
645
+ puts "❌ Docker Compose is not available"
712
646
  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]
647
+
648
+ # Check Rails
649
+ if system("rails --version > /dev/null 2>&1")
650
+ puts "✅ Rails is available"
726
651
  else
727
- puts "❌ Docker Compose not found"
728
- exit 1
652
+ puts "❌ Rails is not available"
729
653
  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
654
 
749
- services.each do |service|
750
- ensure_service_exists!(service)
751
- yield(service)
655
+ # Check Ruby version
656
+ puts "✅ Ruby #{RUBY_VERSION}"
657
+
658
+ # Check Regolith config
659
+ if find_regolith_config
660
+ puts "✅ Regolith configuration found"
661
+ config = load_regolith_config
662
+ puts " #{config['services'].size} service(s) configured"
663
+ else
664
+ puts "⚠️ No Regolith configuration found (not in a Regolith app directory)"
752
665
  end
753
666
  end
754
667
 
755
- def ensure_service_exists!(service_name)
756
- config = load_regolith_config
757
- unless config['services'].key?(service_name)
758
- puts "❌ Service '#{service_name}' not found"
759
- puts "Available services: #{config['services'].keys.join(', ')}"
760
- exit 1
761
- end
668
+ def prune_docker
669
+ puts "🧹 Cleaning up Docker resources..."
670
+ system("docker system prune -f")
762
671
  end
763
672
 
764
- def show_service_info(config)
765
- config['services'].each do |name, service|
766
- puts "🚀 #{name}_service: http://localhost:#{service['port']}"
673
+ def rebuild_service(service_name)
674
+ if service_name
675
+ puts "🔨 Rebuilding #{service_name}..."
676
+ exec_compose("build", "--no-cache", service_name)
677
+ else
678
+ puts "🔨 Rebuilding all services..."
679
+ exec_compose("build", "--no-cache")
767
680
  end
768
-
769
- puts "🧭 Service registry: config/regolith.yml"
770
- puts
771
681
  end
772
682
 
773
683
  def load_regolith_config
774
- config_path = Regolith.send(:find_regolith_config)
775
- return { 'services' => {} } unless config_path && File.exist?(config_path)
684
+ config_path = find_regolith_config
685
+ return { 'services' => {} } unless config_path
776
686
 
777
- # Use safe YAML loading
778
687
  config = Psych.safe_load(File.read(config_path), permitted_classes: [], aliases: false) || {}
779
688
  config['services'] ||= {}
780
689
  config
781
690
  end
782
691
 
692
+ def find_regolith_config
693
+ current_dir = Dir.pwd
694
+ while current_dir != '/'
695
+ config_path = File.join(current_dir, 'config', 'regolith.yml')
696
+ return config_path if File.exist?(config_path)
697
+ current_dir = File.dirname(current_dir)
698
+ end
699
+ nil
700
+ end
701
+
783
702
  def save_regolith_config(config)
784
703
  FileUtils.mkdir_p('config')
785
704
  File.write('config/regolith.yml', YAML.dump(config))
786
- Regolith.reload_service_registry!
787
705
  end
788
706
 
789
707
  def update_docker_compose(config)
@@ -791,95 +709,82 @@ module Regolith
791
709
  File.write('docker-compose.yml', docker_compose)
792
710
  end
793
711
 
794
- # File generators
795
712
  def generate_docker_compose(app_name, services = {})
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
713
+ template = <<~YAML
714
+ version: '3.8'
715
+
716
+ networks:
717
+ regolith:
718
+
719
+ services:
720
+ db:
721
+ image: postgres:14
722
+ environment:
723
+ POSTGRES_DB: #{app_name}_development
724
+ POSTGRES_USER: postgres
725
+ POSTGRES_PASSWORD: password
726
+ ports:
727
+ - "5432:5432"
728
+ volumes:
729
+ - postgres_data:/var/lib/postgresql/data
730
+ networks:
731
+ - regolith
732
+ healthcheck:
733
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
734
+ interval: 30s
735
+ timeout: 10s
736
+ retries: 3
737
+
738
+ <% services.each do |name, service| %>
739
+ <%= name %>:
740
+ build:
741
+ context: <%= service['root'] %>
742
+ args:
743
+ BUILD_ENV: development
744
+ ports:
745
+ - "<%= service['port'] %>:3000"
746
+ depends_on:
747
+ db:
748
+ condition: service_healthy
749
+ environment:
750
+ DATABASE_URL: postgres://postgres:password@db:5432/<%= app_name %>_development
751
+ REGOLITH_SERVICE_NAME: <%= name %>
752
+ REGOLITH_SERVICE_PORT: <%= service['port'] %>
753
+ volumes:
754
+ - <%= service['root'] %>:/app
755
+ networks:
756
+ - regolith
757
+ command: bash -c "rm -f tmp/pids/server.pid && bundle install && rails db:prepare && rails server -b 0.0.0.0"
758
+ healthcheck:
759
+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
760
+ interval: 30s
761
+ timeout: 10s
762
+ retries: 3
763
+ <% end %>
764
+
765
+ volumes:
766
+ postgres_data:
767
+ YAML
768
+
769
+ ERB.new(template).result(binding)
858
770
  end
859
771
 
860
772
  def generate_dockerfile
861
773
  <<~DOCKERFILE
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/*
774
+ FROM ruby:3.1
869
775
 
870
776
  WORKDIR /app
871
777
 
872
- # Copy Gemfile and install gems
778
+ RUN apt-get update -qq && apt-get install -y nodejs postgresql-client libyaml-dev libsqlite3-dev build-essential pkg-config curl
779
+
873
780
  COPY Gemfile Gemfile.lock* ./
874
781
 
875
- # Conditional bundler config for dev vs prod
876
782
  ARG BUILD_ENV=development
877
783
  RUN if [ "$BUILD_ENV" = "production" ]; then \\
878
784
  bundle config set --local deployment 'true' && \\
879
785
  bundle config set --local without 'development test'; \\
880
786
  fi && bundle install
881
787
 
882
- # Copy application code
883
788
  COPY . .
884
789
 
885
790
  EXPOSE 3000
@@ -891,7 +796,7 @@ module Regolith
891
796
  def generate_regolith_initializer(service_name)
892
797
  <<~RUBY
893
798
  require 'ostruct'
894
-
799
+
895
800
  # Regolith service configuration
896
801
  Rails.application.configure do
897
802
  config.regolith = OpenStruct.new(
@@ -901,7 +806,6 @@ module Regolith
901
806
  )
902
807
  end
903
808
 
904
- # Load service registry if available
905
809
  if File.exist?(Rails.application.config.regolith.service_registry)
906
810
  REGOLITH_SERVICES = YAML.load_file(Rails.application.config.regolith.service_registry)['services'] || {}
907
811
  else
@@ -910,184 +814,22 @@ module Regolith
910
814
  RUBY
911
815
  end
912
816
 
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
-
1040
817
  def generate_makefile
1041
818
  <<~MAKEFILE
1042
- .PHONY: server up down restart logs console test health doctor
1043
-
1044
- # Start services
1045
- server up:
1046
- regolith server
1047
-
1048
- # Stop services
1049
- down:
1050
- regolith down
819
+ .PHONY: server console build clean
1051
820
 
1052
- # Restart services
1053
- restart:
1054
- regolith restart
821
+ server:
822
+ \tregolith server
1055
823
 
1056
- # View logs
1057
- logs:
1058
- regolith logs -f
1059
-
1060
- # Open console (usage: make console SERVICE=users)
1061
824
  console:
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
825
+ \tregolith console
1072
826
 
1073
- # System diagnostics
1074
- doctor:
1075
- regolith doctor
827
+ build:
828
+ \tdocker-compose build
1076
829
 
1077
- # Database operations
1078
- db-migrate:
1079
- regolith db:migrate --all
1080
-
1081
- db-setup:
1082
- regolith db:setup --all
1083
-
1084
- # Cleanup
1085
830
  clean:
1086
- regolith prune
1087
-
1088
- # Shortcuts
1089
- dev: up
1090
- stop: down
831
+ \tdocker-compose down -v
832
+ \tdocker system prune -f
1091
833
  MAKEFILE
1092
834
  end
1093
835
 
@@ -1098,64 +840,82 @@ module Regolith
1098
840
  RUBY
1099
841
  end
1100
842
 
843
+ def exec_compose(*args)
844
+ if system("docker compose version > /dev/null 2>&1")
845
+ exec("docker", "compose", *args)
846
+ elsif system("docker-compose --version > /dev/null 2>&1")
847
+ exec("docker-compose", *args)
848
+ else
849
+ puts "❌ Error: Neither 'docker compose' nor 'docker-compose' is available"
850
+ exit 1
851
+ end
852
+ end
853
+
854
+ def system_compose(*args)
855
+ if system("docker compose version > /dev/null 2>&1")
856
+ system("docker", "compose", *args)
857
+ elsif system("docker-compose --version > /dev/null 2>&1")
858
+ system("docker-compose", *args)
859
+ else
860
+ puts "❌ Error: Neither 'docker compose' nor 'docker-compose' is available"
861
+ false
862
+ end
863
+ end
864
+
1101
865
  def show_help
1102
866
  puts <<~HELP
1103
- Regolith #{Regolith::VERSION} - Rails for Distributed Systems
867
+ Regolith #{Regolith::VERSION} - Rails Microservices Framework
1104
868
 
1105
869
  USAGE:
1106
870
  regolith <command> [options]
1107
871
 
1108
- PROJECT COMMANDS:
872
+ PROJECT MANAGEMENT:
1109
873
  new <app_name> Create a new Regolith application
1110
874
  generate service <name> Generate a new microservice
1111
875
 
1112
876
  SERVICE MANAGEMENT:
1113
877
  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
878
+ down Stop all services
879
+ restart [service] Restart all services or specific service
880
+ stop <service> Stop a specific service
881
+ ps, status Show service status
882
+ logs [service] [-f] Show logs (optionally follow)
883
+ exec <service> <command> Execute command in service container
884
+ shell <service> Open bash shell in service
1122
885
 
1123
886
  RAILS INTEGRATION:
887
+ rails <service> <command> Run Rails command in service
1124
888
  console <service> Open Rails console for service
1125
- rails <service> <cmd> Run Rails command in service
1126
889
  routes <service> Show routes for service
1127
- open <service> Open service in browser
890
+ open <service> Open service URL in browser
1128
891
 
1129
892
  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
893
+ db:create [service|--all] Create databases
894
+ db:migrate [service|--all] Run migrations
895
+ db:seed [service|--all] Seed databases
896
+ db:reset [service|--all] Reset databases
897
+ db:setup [service|--all] Setup databases
898
+ db:drop [service|--all] Drop databases
1136
899
 
1137
900
  TESTING & HEALTH:
1138
901
  test [service|--all] Run tests
1139
- health Health check all services
902
+ health Check service health
1140
903
 
1141
904
  UTILITIES:
1142
- config [--json] Show current configuration
1143
- inspect [--json] Show services and resolved endpoints
1144
- prune Clean up Docker system
905
+ config [--json] Show configuration
906
+ inspect [--json] Show endpoints and config
1145
907
  doctor Run system diagnostics
1146
- version Show version
908
+ prune Clean up Docker resources
909
+ rebuild [service] Rebuild service images
910
+ version Show version information
1147
911
 
1148
912
  EXAMPLES:
1149
913
  regolith new marketplace
1150
914
  regolith generate service products
1151
915
  regolith server
1152
916
  regolith rails products db:migrate
1153
- regolith routes products
1154
917
  regolith open products
1155
- regolith shell products
1156
- regolith inspect --json
1157
- regolith config --json | jq '.services'
1158
- regolith test --all
918
+ regolith health
1159
919
 
1160
920
  Get started:
1161
921
  regolith new myapp
@@ -1165,4 +925,4 @@ module Regolith
1165
925
  HELP
1166
926
  end
1167
927
  end
1168
- end
928
+ end