regolith 0.1.9 → 0.1.11

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