hiiro 0.1.152 → 0.1.154
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/README.md +115 -0
- data/lib/hiiro/service_manager.rb +129 -79
- data/lib/hiiro/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2fdf896084fbec37ad190f81188a0e470aaf30c491acb77df209e3b6d3da6cf
|
|
4
|
+
data.tar.gz: 3310a848c676685907bec54adba6b2eb76be56a811cf73f1dd56fb4a3953ddd7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85fe854d98a6ffa72e9df07b3816bd8a0e20ca044667afabefd8cd5918264804b8d92f85c464301aaf3275be71ba410399ab27499473ffb3b4b163e872040bff
|
|
7
|
+
data.tar.gz: 0ea45cfd61f388f051c50eca1fc6a7f1f46039cd56f6129dc03b12e5a93ad952298225d18ea0cb078aabea04f78fcf4e27ae6662d54abeb45bdaa9ffd0dba590
|
data/README.md
CHANGED
|
@@ -241,3 +241,118 @@ bundle exec rake test
|
|
|
241
241
|
## License
|
|
242
242
|
|
|
243
243
|
MIT
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# COMPLEX SERVICES
|
|
247
|
+
|
|
248
|
+
```sh
|
|
249
|
+
|
|
250
|
+
❯ in this setup...can you configure a couple complex services that use variations, etc. i need examples of how to configure it. and then can you show me how to run it for different variations
|
|
251
|
+
|
|
252
|
+
⏺ Here's an example ~/.config/hiiro/services.yml:
|
|
253
|
+
|
|
254
|
+
# Individual services
|
|
255
|
+
api-rails:
|
|
256
|
+
base_dir: ~/work/api
|
|
257
|
+
host: localhost
|
|
258
|
+
port: 3000
|
|
259
|
+
init:
|
|
260
|
+
- bundle install
|
|
261
|
+
start:
|
|
262
|
+
- bin/rails s -p 3000
|
|
263
|
+
cleanup: []
|
|
264
|
+
env_file: .env.development
|
|
265
|
+
base_env: api-rails.env
|
|
266
|
+
env_vars:
|
|
267
|
+
DATABASE_URL:
|
|
268
|
+
variations:
|
|
269
|
+
local: postgres://localhost:5432/api_dev
|
|
270
|
+
docker: postgres://db:5432/api_dev
|
|
271
|
+
REDIS_URL:
|
|
272
|
+
variations:
|
|
273
|
+
local: redis://localhost:6379
|
|
274
|
+
docker: redis://redis:6379
|
|
275
|
+
|
|
276
|
+
graphql:
|
|
277
|
+
base_dir: ~/work/graphql
|
|
278
|
+
host: localhost
|
|
279
|
+
port: 4000
|
|
280
|
+
init:
|
|
281
|
+
- npm install
|
|
282
|
+
start:
|
|
283
|
+
- npm run dev
|
|
284
|
+
env_file: .env.local
|
|
285
|
+
base_env: graphql.env
|
|
286
|
+
env_vars:
|
|
287
|
+
API_URL:
|
|
288
|
+
variations:
|
|
289
|
+
local: http://localhost:3000
|
|
290
|
+
staging: https://api.staging.example.com
|
|
291
|
+
AUTH_PROVIDER:
|
|
292
|
+
variations:
|
|
293
|
+
local: http://localhost:8080/auth
|
|
294
|
+
staging: https://auth.staging.example.com
|
|
295
|
+
|
|
296
|
+
frontend:
|
|
297
|
+
base_dir: ~/work/frontend
|
|
298
|
+
host: localhost
|
|
299
|
+
port: 8080
|
|
300
|
+
start:
|
|
301
|
+
- npm run dev
|
|
302
|
+
env_file: .env.local
|
|
303
|
+
base_env: frontend.env
|
|
304
|
+
env_vars:
|
|
305
|
+
GRAPHQL_URL:
|
|
306
|
+
variations:
|
|
307
|
+
local: http://localhost:4000/graphql
|
|
308
|
+
staging: https://graphql.staging.example.com/graphql
|
|
309
|
+
|
|
310
|
+
# Service groups
|
|
311
|
+
full-stack:
|
|
312
|
+
services:
|
|
313
|
+
- name: api-rails
|
|
314
|
+
use:
|
|
315
|
+
DATABASE_URL: local
|
|
316
|
+
REDIS_URL: local
|
|
317
|
+
- name: graphql
|
|
318
|
+
use:
|
|
319
|
+
API_URL: local
|
|
320
|
+
AUTH_PROVIDER: local
|
|
321
|
+
- name: frontend
|
|
322
|
+
use:
|
|
323
|
+
GRAPHQL_URL: local
|
|
324
|
+
|
|
325
|
+
staging-fe:
|
|
326
|
+
services:
|
|
327
|
+
- name: graphql
|
|
328
|
+
use:
|
|
329
|
+
API_URL: staging
|
|
330
|
+
AUTH_PROVIDER: staging
|
|
331
|
+
- name: frontend
|
|
332
|
+
use:
|
|
333
|
+
GRAPHQL_URL: local
|
|
334
|
+
|
|
335
|
+
Usage:
|
|
336
|
+
|
|
337
|
+
# Start a single service (defaults to "local" variations)
|
|
338
|
+
h service start api-rails
|
|
339
|
+
|
|
340
|
+
# Start with specific variations
|
|
341
|
+
h service start api-rails --use DATABASE_URL=docker --use REDIS_URL=docker
|
|
342
|
+
|
|
343
|
+
# Start the full local stack (one window, 3 split panes)
|
|
344
|
+
h service start full-stack
|
|
345
|
+
|
|
346
|
+
# Start frontend pointing at staging backends
|
|
347
|
+
h service start staging-fe
|
|
348
|
+
|
|
349
|
+
# No args — fuzzy select from all services + groups
|
|
350
|
+
h service start
|
|
351
|
+
|
|
352
|
+
The key ideas:
|
|
353
|
+
- local is the default variation — if you don't pass --use, each env var gets its local value
|
|
354
|
+
- Groups let you pre-bake variation combos — full-stack vs staging-fe are just different use: presets for the same services
|
|
355
|
+
- Base env templates live in ~/.config/hiiro/env_templates/ (e.g., api-rails.env) — they get copied to base_dir/env_file first, then variations are injected on top
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
```
|
|
@@ -50,50 +50,15 @@ class Hiiro
|
|
|
50
50
|
svc = find_service(svc_name)
|
|
51
51
|
return unless svc
|
|
52
52
|
|
|
53
|
-
env_vars = svc[:env_vars]
|
|
54
|
-
return unless env_vars || svc[:base_env]
|
|
55
|
-
|
|
56
53
|
base_dir = svc[:base_dir]
|
|
57
|
-
|
|
58
|
-
base_env = svc[:base_env]
|
|
59
|
-
|
|
60
|
-
# Copy base env template if configured
|
|
61
|
-
if base_env && env_file && base_dir
|
|
62
|
-
src = File.join(ENV_TEMPLATES_DIR, base_env)
|
|
63
|
-
dest = File.join(base_dir, env_file)
|
|
64
|
-
if File.exist?(src)
|
|
65
|
-
FileUtils.mkdir_p(File.dirname(dest))
|
|
66
|
-
FileUtils.cp(src, dest)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Inject variation values into env file
|
|
71
|
-
return unless env_vars && env_file && base_dir
|
|
54
|
+
return unless base_dir
|
|
72
55
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
env_vars.each do |var_name, var_config|
|
|
77
|
-
next unless var_config.is_a?(Hash) && var_config['variations']
|
|
78
|
-
|
|
79
|
-
variation = (variation_overrides[var_name] || variation_overrides[var_name.to_sym] || 'local').to_s
|
|
80
|
-
value = var_config['variations'][variation]
|
|
81
|
-
next unless value
|
|
56
|
+
env_file_configs = build_env_file_configs(svc)
|
|
57
|
+
return if env_file_configs.empty?
|
|
82
58
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
lines.map! do |line|
|
|
86
|
-
if line.match?(/\A#{Regexp.escape(var_name)}=/)
|
|
87
|
-
replaced = true
|
|
88
|
-
"#{var_name}=#{value}\n"
|
|
89
|
-
else
|
|
90
|
-
line
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
lines << "#{var_name}=#{value}\n" unless replaced
|
|
59
|
+
env_file_configs.each do |efc|
|
|
60
|
+
prepare_single_env(base_dir, efc, variation_overrides)
|
|
94
61
|
end
|
|
95
|
-
|
|
96
|
-
File.write(dest, lines.join)
|
|
97
62
|
end
|
|
98
63
|
|
|
99
64
|
def start_group(name, tmux_info: {}, task_info: {})
|
|
@@ -131,8 +96,6 @@ class Hiiro
|
|
|
131
96
|
member_name = member['name'] || member[:name]
|
|
132
97
|
use_overrides = member['use'] || member[:use] || {}
|
|
133
98
|
|
|
134
|
-
prepare_env(member_name, variation_overrides: use_overrides)
|
|
135
|
-
|
|
136
99
|
svc = find_service(member_name)
|
|
137
100
|
next unless svc
|
|
138
101
|
|
|
@@ -152,7 +115,7 @@ class Hiiro
|
|
|
152
115
|
pane: pane_id,
|
|
153
116
|
)
|
|
154
117
|
|
|
155
|
-
start(member_name, tmux_info: member_tmux_info, task_info: task_info,
|
|
118
|
+
start(member_name, tmux_info: member_tmux_info, task_info: task_info, variation_overrides: use_overrides, skip_window_creation: true)
|
|
156
119
|
end
|
|
157
120
|
true
|
|
158
121
|
end
|
|
@@ -219,11 +182,6 @@ class Hiiro
|
|
|
219
182
|
return false
|
|
220
183
|
end
|
|
221
184
|
|
|
222
|
-
# Prepare env file unless already handled by start_group
|
|
223
|
-
unless skip_env
|
|
224
|
-
prepare_env(svc_name, variation_overrides: variation_overrides)
|
|
225
|
-
end
|
|
226
|
-
|
|
227
185
|
# Run init commands
|
|
228
186
|
if svc[:init]
|
|
229
187
|
svc[:init].each do |cmd|
|
|
@@ -236,6 +194,11 @@ class Hiiro
|
|
|
236
194
|
end
|
|
237
195
|
end
|
|
238
196
|
|
|
197
|
+
# Prepare env files after init (e.g., init may install deps that generate .env templates)
|
|
198
|
+
unless skip_env
|
|
199
|
+
prepare_env(svc_name, variation_overrides: variation_overrides)
|
|
200
|
+
end
|
|
201
|
+
|
|
239
202
|
# Start the service
|
|
240
203
|
start_cmd = svc[:start]
|
|
241
204
|
unless start_cmd
|
|
@@ -248,7 +211,7 @@ class Hiiro
|
|
|
248
211
|
session = tmux_info[:session] || current_tmux_session
|
|
249
212
|
|
|
250
213
|
# Write start commands to an executable tempfile
|
|
251
|
-
script = write_start_script(svc_name, start_cmds
|
|
214
|
+
script = write_start_script(svc_name, start_cmds)
|
|
252
215
|
|
|
253
216
|
if session && !skip_window_creation
|
|
254
217
|
# Create a new tmux window for this service
|
|
@@ -265,6 +228,8 @@ class Hiiro
|
|
|
265
228
|
|
|
266
229
|
if pane_id
|
|
267
230
|
system('tmux', 'send-keys', '-t', pane_id, script, 'Enter')
|
|
231
|
+
elsif base_dir
|
|
232
|
+
system("cd #{base_dir} && #{script} &")
|
|
268
233
|
else
|
|
269
234
|
system("#{script} &")
|
|
270
235
|
end
|
|
@@ -469,8 +434,13 @@ class Hiiro
|
|
|
469
434
|
|
|
470
435
|
h.add_subcmd(:start) do |svc_name=nil, *extra_args|
|
|
471
436
|
unless svc_name
|
|
472
|
-
|
|
473
|
-
|
|
437
|
+
all = sm.services.keys
|
|
438
|
+
if all.empty?
|
|
439
|
+
puts "No services configured"
|
|
440
|
+
next
|
|
441
|
+
end
|
|
442
|
+
svc_name = h.fuzzyfind(all)
|
|
443
|
+
next unless svc_name
|
|
474
444
|
end
|
|
475
445
|
|
|
476
446
|
# Parse --use flags from extra_args
|
|
@@ -513,8 +483,13 @@ class Hiiro
|
|
|
513
483
|
|
|
514
484
|
h.add_subcmd(:stop) do |svc_name=nil|
|
|
515
485
|
unless svc_name
|
|
516
|
-
|
|
517
|
-
|
|
486
|
+
running = sm.running_services.keys
|
|
487
|
+
if running.empty?
|
|
488
|
+
puts "No running services"
|
|
489
|
+
next
|
|
490
|
+
end
|
|
491
|
+
svc_name = h.fuzzyfind(running)
|
|
492
|
+
next unless svc_name
|
|
518
493
|
end
|
|
519
494
|
|
|
520
495
|
sm.stop(svc_name)
|
|
@@ -522,8 +497,13 @@ class Hiiro
|
|
|
522
497
|
|
|
523
498
|
h.add_subcmd(:reset) do |svc_name=nil|
|
|
524
499
|
unless svc_name
|
|
525
|
-
|
|
526
|
-
|
|
500
|
+
running = sm.running_services.keys
|
|
501
|
+
if running.empty?
|
|
502
|
+
puts "No running services"
|
|
503
|
+
next
|
|
504
|
+
end
|
|
505
|
+
svc_name = h.fuzzyfind(running)
|
|
506
|
+
next unless svc_name
|
|
527
507
|
end
|
|
528
508
|
|
|
529
509
|
sm.reset(svc_name)
|
|
@@ -535,8 +515,13 @@ class Hiiro
|
|
|
535
515
|
|
|
536
516
|
h.add_subcmd(:attach) do |svc_name=nil|
|
|
537
517
|
unless svc_name
|
|
538
|
-
|
|
539
|
-
|
|
518
|
+
running = sm.running_services.keys
|
|
519
|
+
if running.empty?
|
|
520
|
+
puts "No running services"
|
|
521
|
+
next
|
|
522
|
+
end
|
|
523
|
+
svc_name = h.fuzzyfind(running)
|
|
524
|
+
next unless svc_name
|
|
540
525
|
end
|
|
541
526
|
|
|
542
527
|
sm.attach(svc_name)
|
|
@@ -586,8 +571,13 @@ class Hiiro
|
|
|
586
571
|
|
|
587
572
|
h.add_subcmd(:status) do |svc_name=nil|
|
|
588
573
|
unless svc_name
|
|
589
|
-
|
|
590
|
-
|
|
574
|
+
running = sm.running_services.keys
|
|
575
|
+
if running.empty?
|
|
576
|
+
puts "No running services"
|
|
577
|
+
next
|
|
578
|
+
end
|
|
579
|
+
svc_name = h.fuzzyfind(running)
|
|
580
|
+
next unless svc_name
|
|
591
581
|
end
|
|
592
582
|
|
|
593
583
|
sm.status(svc_name)
|
|
@@ -601,6 +591,9 @@ class Hiiro
|
|
|
601
591
|
'init' => [],
|
|
602
592
|
'start' => [''],
|
|
603
593
|
'cleanup' => [],
|
|
594
|
+
'env_files' => [
|
|
595
|
+
{ 'env_file' => '.env', 'base_env' => '', 'env_vars' => {} },
|
|
596
|
+
],
|
|
604
597
|
}
|
|
605
598
|
|
|
606
599
|
require 'tempfile'
|
|
@@ -675,29 +668,30 @@ class Hiiro
|
|
|
675
668
|
next
|
|
676
669
|
end
|
|
677
670
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
puts "No
|
|
671
|
+
configs = sm.send(:build_env_file_configs, svc)
|
|
672
|
+
if configs.empty?
|
|
673
|
+
puts "No env files configured for '#{svc[:name]}'"
|
|
681
674
|
next
|
|
682
675
|
end
|
|
683
676
|
|
|
684
|
-
puts "Env
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
677
|
+
puts "Env files for '#{svc[:name]}':"
|
|
678
|
+
configs.each do |efc|
|
|
679
|
+
puts
|
|
680
|
+
puts " #{efc[:env_file] || '(no dest)'}"
|
|
681
|
+
puts " template: #{efc[:base_env]}" if efc[:base_env]
|
|
688
682
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
puts " #{variation}: #{value}"
|
|
692
|
-
end
|
|
693
|
-
end
|
|
683
|
+
env_vars = efc[:env_vars]
|
|
684
|
+
next unless env_vars
|
|
694
685
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
686
|
+
env_vars.each do |var_name, var_config|
|
|
687
|
+
variations = var_config.is_a?(Hash) && (var_config['variations'] || var_config[:variations])
|
|
688
|
+
next unless variations
|
|
689
|
+
|
|
690
|
+
puts " #{var_name}:"
|
|
691
|
+
variations.each do |variation, value|
|
|
692
|
+
puts " #{variation}: #{value}"
|
|
693
|
+
end
|
|
694
|
+
end
|
|
701
695
|
end
|
|
702
696
|
end
|
|
703
697
|
end
|
|
@@ -705,18 +699,74 @@ class Hiiro
|
|
|
705
699
|
|
|
706
700
|
private
|
|
707
701
|
|
|
702
|
+
# Normalize env file config into an array of hashes,
|
|
703
|
+
# supporting both old single-env format and new env_files array
|
|
704
|
+
def build_env_file_configs(svc)
|
|
705
|
+
if svc[:env_files]
|
|
706
|
+
Array(svc[:env_files]).map { |ef| symbolize_keys(ef.is_a?(Hash) ? ef : {}) }
|
|
707
|
+
elsif svc[:env_file] || svc[:base_env] || svc[:env_vars]
|
|
708
|
+
[{ env_file: svc[:env_file], base_env: svc[:base_env], env_vars: svc[:env_vars] }]
|
|
709
|
+
else
|
|
710
|
+
[]
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
def prepare_single_env(base_dir, efc, variation_overrides)
|
|
715
|
+
env_file = efc[:env_file]
|
|
716
|
+
base_env = efc[:base_env]
|
|
717
|
+
env_vars = efc[:env_vars]
|
|
718
|
+
|
|
719
|
+
# Copy base env template if configured
|
|
720
|
+
if base_env && env_file
|
|
721
|
+
src = File.join(ENV_TEMPLATES_DIR, base_env)
|
|
722
|
+
dest = File.join(base_dir, env_file)
|
|
723
|
+
if File.exist?(src)
|
|
724
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
725
|
+
FileUtils.cp(src, dest)
|
|
726
|
+
end
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
# Inject variation values into env file
|
|
730
|
+
return unless env_vars && env_file
|
|
731
|
+
|
|
732
|
+
dest = File.join(base_dir, env_file)
|
|
733
|
+
lines = File.exist?(dest) ? File.readlines(dest) : []
|
|
734
|
+
|
|
735
|
+
env_vars.each do |var_name, var_config|
|
|
736
|
+
var_config = symbolize_keys(var_config) if var_config.is_a?(Hash)
|
|
737
|
+
variations = var_config.is_a?(Hash) && (var_config[:variations] || var_config['variations'])
|
|
738
|
+
next unless variations
|
|
739
|
+
|
|
740
|
+
variation = (variation_overrides[var_name] || variation_overrides[var_name.to_sym] || 'local').to_s
|
|
741
|
+
value = variations[variation]
|
|
742
|
+
next unless value
|
|
743
|
+
|
|
744
|
+
replaced = false
|
|
745
|
+
lines.map! do |line|
|
|
746
|
+
if line.match?(/\A#{Regexp.escape(var_name.to_s)}=/)
|
|
747
|
+
replaced = true
|
|
748
|
+
"#{var_name}=#{value}\n"
|
|
749
|
+
else
|
|
750
|
+
line
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
lines << "#{var_name}=#{value}\n" unless replaced
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
File.write(dest, lines.join)
|
|
757
|
+
end
|
|
758
|
+
|
|
708
759
|
def stale_pane?(pane_id)
|
|
709
760
|
return true unless pane_id
|
|
710
761
|
!system('tmux', 'has-session', '-t', pane_id, [:out, :err] => '/dev/null')
|
|
711
762
|
end
|
|
712
763
|
|
|
713
|
-
def write_start_script(svc_name, cmds
|
|
764
|
+
def write_start_script(svc_name, cmds)
|
|
714
765
|
dir = File.join(STATE_DIR, 'scripts')
|
|
715
766
|
FileUtils.mkdir_p(dir)
|
|
716
767
|
path = File.join(dir, "#{svc_name}.sh")
|
|
717
768
|
|
|
718
769
|
lines = ["#!/usr/bin/env bash", "set -e"]
|
|
719
|
-
lines << "cd #{base_dir}" if base_dir
|
|
720
770
|
lines.concat(cmds)
|
|
721
771
|
|
|
722
772
|
File.write(path, lines.join("\n") + "\n")
|
data/lib/hiiro/version.rb
CHANGED