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