nvoi 0.1.8 → 0.2.0

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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -5
  3. data/Gemfile.lock +17 -8
  4. data/Rakefile +1 -1
  5. data/lib/nvoi/cli/config/command.rb +46 -41
  6. data/lib/nvoi/cli/credentials/edit/command.rb +20 -20
  7. data/lib/nvoi/cli/credentials/show/command.rb +1 -1
  8. data/lib/nvoi/cli/db/command.rb +10 -10
  9. data/lib/nvoi/cli/delete/command.rb +2 -2
  10. data/lib/nvoi/cli/deploy/command.rb +2 -2
  11. data/lib/nvoi/cli/deploy/steps/configure_tunnel.rb +2 -2
  12. data/lib/nvoi/cli/deploy/steps/provision_server.rb +1 -1
  13. data/lib/nvoi/cli/deploy/steps/provision_volume.rb +1 -1
  14. data/lib/nvoi/cli/exec/command.rb +3 -3
  15. data/lib/nvoi/cli/logs/command.rb +2 -2
  16. data/lib/nvoi/cli/onboard/command.rb +176 -622
  17. data/lib/nvoi/cli/onboard/steps/app.rb +108 -0
  18. data/lib/nvoi/cli/onboard/steps/app_name.rb +26 -0
  19. data/lib/nvoi/cli/onboard/steps/compute.rb +139 -0
  20. data/lib/nvoi/cli/onboard/steps/database.rb +97 -0
  21. data/lib/nvoi/cli/onboard/steps/domain.rb +48 -0
  22. data/lib/nvoi/cli/onboard/steps/env.rb +67 -0
  23. data/lib/nvoi/cli/onboard/ui.rb +84 -0
  24. data/lib/nvoi/cli/unlock/command.rb +2 -2
  25. data/lib/nvoi/cli.rb +0 -32
  26. data/lib/nvoi/configuration/app_service.rb +54 -0
  27. data/lib/nvoi/configuration/application.rb +44 -0
  28. data/lib/nvoi/configuration/builder.rb +417 -0
  29. data/lib/nvoi/configuration/database.rb +56 -0
  30. data/lib/nvoi/configuration/deploy.rb +15 -0
  31. data/lib/nvoi/{objects/service_spec.rb → configuration/deployment.rb} +4 -3
  32. data/lib/nvoi/{objects/config_override.rb → configuration/override.rb} +4 -4
  33. data/lib/nvoi/configuration/providers.rb +78 -0
  34. data/lib/nvoi/configuration/result.rb +43 -0
  35. data/lib/nvoi/configuration/root.rb +234 -0
  36. data/lib/nvoi/configuration/server.rb +39 -0
  37. data/lib/nvoi/configuration/service.rb +62 -0
  38. data/lib/nvoi/external/cloud/aws.rb +12 -12
  39. data/lib/nvoi/external/cloud/hetzner.rb +7 -7
  40. data/lib/nvoi/external/cloud/scaleway.rb +7 -7
  41. data/lib/nvoi/external/cloud/types.rb +42 -0
  42. data/lib/nvoi/external/database/mysql.rb +1 -1
  43. data/lib/nvoi/external/database/postgres.rb +1 -1
  44. data/lib/nvoi/external/database/provider.rb +1 -1
  45. data/lib/nvoi/external/database/sqlite.rb +1 -1
  46. data/lib/nvoi/external/database/types.rb +55 -0
  47. data/lib/nvoi/external/dns/cloudflare.rb +6 -6
  48. data/lib/nvoi/external/dns/types.rb +24 -0
  49. data/lib/nvoi/utils/config_loader.rb +12 -12
  50. data/lib/nvoi/utils/credential_store.rb +4 -4
  51. data/lib/nvoi/utils/env_resolver.rb +3 -3
  52. data/lib/nvoi/utils/namer.rb +2 -2
  53. data/lib/nvoi/utils/presence.rb +23 -0
  54. data/lib/nvoi/version.rb +1 -1
  55. data/lib/nvoi.rb +2 -17
  56. metadata +95 -58
  57. data/.claude/todo/refactor/00-overview.md +0 -171
  58. data/.claude/todo/refactor/01-objects.md +0 -96
  59. data/.claude/todo/refactor/02-utils.md +0 -143
  60. data/.claude/todo/refactor/03-external-cloud.md +0 -164
  61. data/.claude/todo/refactor/04-external-dns.md +0 -104
  62. data/.claude/todo/refactor/05-external.md +0 -133
  63. data/.claude/todo/refactor/06-cli.md +0 -123
  64. data/.claude/todo/refactor/07-cli-deploy-command.md +0 -177
  65. data/.claude/todo/refactor/08-cli-deploy-steps.md +0 -201
  66. data/.claude/todo/refactor/09-cli-delete-command.md +0 -169
  67. data/.claude/todo/refactor/10-cli-exec-command.md +0 -157
  68. data/.claude/todo/refactor/11-cli-credentials-command.md +0 -190
  69. data/.claude/todo/refactor/12-cli-db-command.md +0 -128
  70. data/.claude/todo/refactor/_target.md +0 -79
  71. data/.claude/todo/refactor-execution/00-entrypoint.md +0 -49
  72. data/.claude/todo/refactor-execution/01-objects.md +0 -42
  73. data/.claude/todo/refactor-execution/02-utils.md +0 -41
  74. data/.claude/todo/refactor-execution/03-external-cloud.md +0 -38
  75. data/.claude/todo/refactor-execution/04-external-dns.md +0 -35
  76. data/.claude/todo/refactor-execution/05-external-other.md +0 -46
  77. data/.claude/todo/refactor-execution/06-cli-deploy.md +0 -45
  78. data/.claude/todo/refactor-execution/07-cli-delete.md +0 -43
  79. data/.claude/todo/refactor-execution/08-cli-exec.md +0 -30
  80. data/.claude/todo/refactor-execution/09-cli-credentials.md +0 -34
  81. data/.claude/todo/refactor-execution/10-cli-db.md +0 -31
  82. data/.claude/todo/refactor-execution/11-cli-router.md +0 -44
  83. data/.claude/todo/refactor-execution/12-cleanup.md +0 -120
  84. data/.claude/todo/refactor-execution/_monitoring-strategy.md +0 -126
  85. data/.claude/todo/scaleway.impl.md +0 -644
  86. data/.claude/todo/scaleway.reference.md +0 -520
  87. data/.claude/todos/buckets.md +0 -41
  88. data/.claude/todos.md +0 -550
  89. data/ingest +0 -0
  90. data/lib/nvoi/config_api/actions/app.rb +0 -53
  91. data/lib/nvoi/config_api/actions/compute_provider.rb +0 -55
  92. data/lib/nvoi/config_api/actions/database.rb +0 -70
  93. data/lib/nvoi/config_api/actions/domain_provider.rb +0 -40
  94. data/lib/nvoi/config_api/actions/env.rb +0 -32
  95. data/lib/nvoi/config_api/actions/init.rb +0 -67
  96. data/lib/nvoi/config_api/actions/secret.rb +0 -32
  97. data/lib/nvoi/config_api/actions/server.rb +0 -66
  98. data/lib/nvoi/config_api/actions/service.rb +0 -52
  99. data/lib/nvoi/config_api/actions/volume.rb +0 -40
  100. data/lib/nvoi/config_api/base.rb +0 -38
  101. data/lib/nvoi/config_api/result.rb +0 -26
  102. data/lib/nvoi/config_api.rb +0 -93
  103. data/lib/nvoi/objects/configuration.rb +0 -483
  104. data/lib/nvoi/objects/database.rb +0 -56
  105. data/lib/nvoi/objects/dns.rb +0 -14
  106. data/lib/nvoi/objects/firewall.rb +0 -11
  107. data/lib/nvoi/objects/network.rb +0 -11
  108. data/lib/nvoi/objects/server.rb +0 -14
  109. data/lib/nvoi/objects/tunnel.rb +0 -14
  110. data/lib/nvoi/objects/volume.rb +0 -17
@@ -1,169 +0,0 @@
1
- # 09 - CLI: Delete Command
2
-
3
- ## Priority: FIFTH (parallel with deploy)
4
-
5
- ---
6
-
7
- ## Current State
8
-
9
- | File | Lines | Purpose |
10
- | ------------------- | ----- | -------------------------------------- |
11
- | `service/delete.rb` | 235 | DeleteService - teardown all resources |
12
-
13
- ---
14
-
15
- ## Target Structure
16
-
17
- ```
18
- lib/nvoi/cli/delete/
19
- ├── command.rb
20
- └── steps/
21
- ├── teardown_tunnel.rb
22
- ├── teardown_dns.rb
23
- ├── detach_volumes.rb
24
- ├── teardown_server.rb
25
- ├── teardown_volume.rb
26
- ├── teardown_firewall.rb
27
- └── teardown_network.rb
28
- ```
29
-
30
- ---
31
-
32
- ## What Delete Does (Current)
33
-
34
- ```ruby
35
- def run
36
- detach_volumes # Must happen before server deletion
37
- delete_all_servers # Delete servers from all groups
38
- delete_volumes # Now safe to delete
39
- delete_firewall # With retry (might still be attached)
40
- delete_network
41
- delete_cloudflare_resources # Tunnels + DNS records
42
- end
43
- ```
44
-
45
- ---
46
-
47
- ## Target Flow
48
-
49
- ```ruby
50
- # cli/delete/command.rb
51
- class Command
52
- def run
53
- config = Utils::ConfigLoader.new.load(config_path)
54
- provider = External::Cloud.for(config)
55
- cloudflare = External::DNS::Cloudflare.new(config.cloudflare)
56
- log = Utils::Logger.new
57
-
58
- # Order matters!
59
- Steps::DetachVolumes.new(config, provider, log).run
60
- Steps::TeardownServer.new(config, provider, log).run
61
- Steps::TeardownVolume.new(config, provider, log).run
62
- Steps::TeardownFirewall.new(config, provider, log).run
63
- Steps::TeardownNetwork.new(config, provider, log).run
64
- Steps::TeardownTunnel.new(config, cloudflare, log).run
65
- Steps::TeardownDNS.new(config, cloudflare, log).run
66
-
67
- log.success "Cleanup complete"
68
- end
69
- end
70
- ```
71
-
72
- ---
73
-
74
- ## DRY Opportunities
75
-
76
- ### 1. Shared Volume Name Collection
77
-
78
- Both `detach_volumes` and `delete_volumes` call `collect_volume_names`.
79
- Extract to utils or pass as step output:
80
-
81
- ```ruby
82
- volumes = Steps::DetachVolumes.new(...).run # returns volume list
83
- Steps::TeardownVolume.new(...).run(volumes)
84
- ```
85
-
86
- ### 2. Shared Hostname Builder
87
-
88
- `build_hostname` is duplicated. Already planned for `utils/namer.rb`.
89
-
90
- ### 3. Merge Tunnel + DNS Teardown
91
-
92
- Both iterate over app services with domains. Could be single step:
93
-
94
- ```ruby
95
- Steps::TeardownCloudflare.new(config, cloudflare, log).run
96
- # Deletes both tunnels and DNS records
97
- ```
98
-
99
- ### 4. Error Tolerance
100
-
101
- Delete operations should be idempotent. Current code does:
102
-
103
- ```ruby
104
- rescue StandardError => e
105
- @log.warning "Failed to delete: %s", e.message
106
- end
107
- ```
108
-
109
- Keep this pattern - deletion should continue even if some resources are already gone.
110
-
111
- ---
112
-
113
- ## Step Details
114
-
115
- ### detach_volumes.rb (~40 lines)
116
-
117
- - Collect all volume names
118
- - For each: find volume, detach if attached
119
-
120
- ### teardown_server.rb (~50 lines)
121
-
122
- - For each server group:
123
- - For each server in group:
124
- - Find and delete server
125
-
126
- ### teardown_volume.rb (~40 lines)
127
-
128
- - For each volume name:
129
- - Find and delete volume
130
-
131
- ### teardown_firewall.rb (~30 lines)
132
-
133
- - Find firewall by name
134
- - Delete with retry (may still be detaching)
135
-
136
- ### teardown_network.rb (~20 lines)
137
-
138
- - Find network by name
139
- - Delete
140
-
141
- ### teardown_tunnel.rb (~40 lines)
142
-
143
- - For each app service with domain:
144
- - Find and delete tunnel
145
-
146
- ### teardown_dns.rb (~40 lines)
147
-
148
- - For each app service with domain:
149
- - Find zone
150
- - Find and delete CNAME record
151
-
152
- ---
153
-
154
- ## Migration Steps
155
-
156
- 1. Create `lib/nvoi/cli/delete/command.rb`
157
- 2. Create `lib/nvoi/cli/delete/steps/` directory
158
- 3. Extract each operation from `service/delete.rb` into separate step
159
- 4. Move `collect_volume_names` to command (shared state)
160
- 5. Delete `service/delete.rb`
161
-
162
- ---
163
-
164
- ## Estimated Effort
165
-
166
- - **Lines to reorganize:** 235
167
- - **Files created:** 8 (command + 7 steps)
168
- - **Files deleted:** 1
169
- - **DRY savings:** ~30 lines (shared hostname, merged tunnel+dns option)
@@ -1,157 +0,0 @@
1
- # 10 - CLI: Exec Command
2
-
3
- ## Priority: FIFTH (parallel with deploy/delete)
4
-
5
- ---
6
-
7
- ## Current State
8
-
9
- | File | Lines | Purpose |
10
- |------|-------|---------|
11
- | `service/exec.rb` | 145 | ExecService - remote command execution |
12
-
13
- ---
14
-
15
- ## Target Structure
16
-
17
- ```
18
- lib/nvoi/cli/exec/
19
- └── command.rb # Single file, no steps needed
20
- ```
21
-
22
- ---
23
-
24
- ## What Exec Does
25
-
26
- Three modes:
27
- 1. **Single server:** `nvoi exec "ls -la"` → run on main server
28
- 2. **All servers:** `nvoi exec --all "ls -la"` → run on all servers in parallel
29
- 3. **Interactive:** `nvoi exec -i` → open SSH shell
30
-
31
- ---
32
-
33
- ## Target Implementation
34
-
35
- ```ruby
36
- # cli/exec/command.rb
37
- module Nvoi
38
- module CLI
39
- module Exec
40
- class Command
41
- def initialize(options)
42
- @options = options
43
- @log = Utils::Logger.new
44
- end
45
-
46
- def run(args)
47
- config = Utils::ConfigLoader.new.load(config_path)
48
- provider = External::Cloud.for(config)
49
-
50
- if @options[:interactive]
51
- open_shell(config, provider)
52
- elsif @options[:all]
53
- run_all(config, provider, args.join(" "))
54
- else
55
- run_single(config, provider, args.join(" "), @options[:server])
56
- end
57
- end
58
-
59
- private
60
-
61
- def run_single(config, provider, command, server_name)
62
- server = find_server(config, provider, server_name)
63
- ssh = External::SSH.new(server.public_ipv4, config.ssh_key_path)
64
-
65
- @log.info "Executing on %s: %s", server.name, command
66
- ssh.execute(command, stream: true)
67
- @log.success "Command completed"
68
- end
69
-
70
- def run_all(config, provider, command)
71
- servers = all_servers(config, provider)
72
- @log.info "Executing on %d servers", servers.size
73
-
74
- threads = servers.map do |server|
75
- Thread.new do
76
- ssh = External::SSH.new(server.public_ipv4, config.ssh_key_path)
77
- output = ssh.execute(command)
78
- [server.name, output]
79
- end
80
- end
81
-
82
- threads.each do |t|
83
- name, output = t.value
84
- output.lines.each { |line| puts "[#{name}] #{line}" }
85
- end
86
- end
87
-
88
- def open_shell(config, provider)
89
- server = find_server(config, provider, @options[:server])
90
- ssh = External::SSH.new(server.public_ipv4, config.ssh_key_path)
91
- ssh.open_shell
92
- end
93
-
94
- def find_server(config, provider, name)
95
- actual_name = resolve_server_name(config, name)
96
- provider.find_server(actual_name) or raise ServiceError, "server not found: #{actual_name}"
97
- end
98
-
99
- def resolve_server_name(config, name)
100
- return config.server_name if name == "main" || name.nil?
101
- # Parse "worker-1" → server_name("worker", 1)
102
- # ...
103
- end
104
-
105
- def all_servers(config, provider)
106
- config.deploy.application.servers.flat_map do |group, cfg|
107
- (1..cfg.count).map { |i| provider.find_server(config.namer.server_name(group, i)) }
108
- end.compact
109
- end
110
- end
111
- end
112
- end
113
- end
114
- ```
115
-
116
- ---
117
-
118
- ## DRY Opportunities
119
-
120
- ### 1. Server Name Resolution
121
- `resolve_server_name` logic is useful. Could go to `utils/namer.rb`:
122
- ```ruby
123
- # utils/namer.rb
124
- def resolve_server_reference(ref)
125
- # "main" → server_name for master
126
- # "worker-1" → server_name("worker", 1)
127
- end
128
- ```
129
-
130
- ### 2. Server Collection
131
- `all_servers` pattern could be a config method:
132
- ```ruby
133
- # objects/config.rb
134
- def each_server
135
- deploy.application.servers.each do |group, cfg|
136
- (1..cfg.count).each { |i| yield(group, i) }
137
- end
138
- end
139
- ```
140
-
141
- ---
142
-
143
- ## Migration Steps
144
-
145
- 1. Create `lib/nvoi/cli/exec/command.rb`
146
- 2. Move logic from `service/exec.rb`
147
- 3. Extract `resolve_server_name` to `utils/namer.rb`
148
- 4. Delete `service/exec.rb`
149
-
150
- ---
151
-
152
- ## Estimated Effort
153
-
154
- - **Lines to reorganize:** 145
155
- - **Files created:** 1
156
- - **Files deleted:** 1
157
- - **Net change:** ~0 (simple move + namespace change)
@@ -1,190 +0,0 @@
1
- # 11 - CLI: Credentials Commands
2
-
3
- ## Priority: FIFTH (parallel with others)
4
-
5
- ---
6
-
7
- ## Current State
8
-
9
- | File | Lines | Purpose |
10
- | ------------------------- | ----- | ------------------------------- |
11
- | `cli.rb` (CredentialsCLI) | ~55 | Thor subcommand for credentials |
12
- | `credentials/editor.rb` | ~80 | Edit/show encrypted credentials |
13
- | `credentials/manager.rb` | ~150 | Load/save encrypted files |
14
- | `credentials/crypto.rb` | ~100 | AES encryption |
15
-
16
- **Total: ~385 lines**
17
-
18
- ---
19
-
20
- ## Target Structure
21
-
22
- ```
23
- lib/nvoi/cli/credentials/
24
- ├── edit/
25
- │ └── command.rb # nvoi credentials edit
26
- └── show/
27
- └── command.rb # nvoi credentials show
28
- ```
29
-
30
- Note: `crypto.rb` and `manager.rb` move to `utils/` (see 02-utils.md)
31
-
32
- ---
33
-
34
- ## What Each Command Does
35
-
36
- ### credentials edit
37
-
38
- 1. Load or create credentials file
39
- 2. Decrypt to temp file
40
- 3. Open in $EDITOR
41
- 4. Validate YAML on save
42
- 5. Re-encrypt
43
- 6. Update .gitignore if first run
44
-
45
- ### credentials show
46
-
47
- 1. Load credentials file
48
- 2. Decrypt
49
- 3. Print to stdout
50
-
51
- ---
52
-
53
- ## Target Implementation
54
-
55
- ```ruby
56
- # cli/credentials/edit/command.rb
57
- module Nvoi
58
- module CLI
59
- module Credentials
60
- module Edit
61
- class Command
62
- def initialize(options)
63
- @options = options
64
- @log = Utils::Logger.new
65
- end
66
-
67
- def run
68
- working_dir = resolve_working_dir
69
- store = Utils::CredentialStore.new(working_dir, @options[:credentials], @options[:master_key])
70
-
71
- if store.exists?
72
- edit_existing(store)
73
- else
74
- create_new(store)
75
- end
76
- end
77
-
78
- private
79
-
80
- def edit_existing(store)
81
- content = store.read
82
- new_content = open_in_editor(content)
83
- validate_yaml!(new_content)
84
- store.write(new_content)
85
- @log.success "Credentials updated"
86
- end
87
-
88
- def create_new(store)
89
- @log.info "Creating new credentials file"
90
- template = default_template
91
- new_content = open_in_editor(template)
92
- validate_yaml!(new_content)
93
- store.write(new_content)
94
- store.update_gitignore
95
- @log.success "Credentials created"
96
- @log.warning "Keep your master key safe!"
97
- end
98
-
99
- def open_in_editor(content)
100
- # Write to temp file, exec $EDITOR, read back
101
- end
102
-
103
- def validate_yaml!(content)
104
- YAML.safe_load(content)
105
- rescue Psych::SyntaxError => e
106
- raise ConfigError, "Invalid YAML: #{e.message}"
107
- end
108
- end
109
- end
110
- end
111
- end
112
- end
113
- ```
114
-
115
- ```ruby
116
- # cli/credentials/show/command.rb
117
- module Nvoi
118
- module CLI
119
- module Credentials
120
- module Show
121
- class Command
122
- def initialize(options)
123
- @options = options
124
- end
125
-
126
- def run
127
- working_dir = resolve_working_dir
128
- store = Utils::CredentialStore.new(working_dir, @options[:credentials], @options[:master_key])
129
- puts store.read
130
- end
131
- end
132
- end
133
- end
134
- end
135
- end
136
- ```
137
-
138
- ---
139
-
140
- ## DRY Opportunities
141
-
142
- ### 1. Merge Crypto + Manager → CredentialStore
143
-
144
- Already planned in 02-utils.md. Single class:
145
-
146
- ```ruby
147
- # utils/crypto.rb
148
- class CredentialStore
149
- def initialize(working_dir, enc_path = nil, key_path = nil)
150
- def exists? → bool
151
- def read → String
152
- def write(content)
153
- def update_gitignore
154
- end
155
- ```
156
-
157
- ### 2. Remove Editor Class
158
-
159
- Current `credentials/editor.rb` is thin wrapper. Logic goes directly into command.
160
-
161
- ### 3. YAML Validation
162
-
163
- Move to utils for reuse:
164
-
165
- ```ruby
166
- # utils/config_loader.rb
167
- def self.validate_yaml!(content)
168
- YAML.safe_load(content, permitted_classes: [Symbol])
169
- end
170
- ```
171
-
172
- ---
173
-
174
- ## Migration Steps
175
-
176
- 1. Move `credentials/crypto.rb` + `credentials/manager.rb` → `utils/crypto.rb` (02-utils.md)
177
- 2. Create `lib/nvoi/cli/credentials/edit/command.rb`
178
- 3. Create `lib/nvoi/cli/credentials/show/command.rb`
179
- 4. Move editor logic into `edit/command.rb`
180
- 5. Delete `credentials/editor.rb`
181
- 6. Update `cli.rb` to route to new commands
182
-
183
- ---
184
-
185
- ## Estimated Effort
186
-
187
- - **Lines to reorganize:** ~385
188
- - **Files created:** 2
189
- - **Files deleted:** 3 (`editor.rb`, `manager.rb`, `crypto.rb` → merged to utils)
190
- - **Net reduction:** ~50 lines (removed wrapper, merged classes)
@@ -1,128 +0,0 @@
1
- # 12 - CLI: Database Commands
2
-
3
- ## Priority: FIFTH (parallel with others)
4
-
5
- ---
6
-
7
- ## Current State (NEW from main)
8
-
9
- | File | Lines | Purpose |
10
- |------|-------|---------|
11
- | `service/db.rb` | 285 | DbService - database branch operations |
12
- | `database/provider.rb` | 160 | Base provider + factory + structs |
13
- | `database/postgres.rb` | ~100 | Postgres dump/restore |
14
- | `database/mysql.rb` | ~100 | MySQL dump/restore |
15
- | `database/sqlite.rb` | ~80 | SQLite dump/restore |
16
-
17
- **Total: ~725 lines**
18
-
19
- ---
20
-
21
- ## What It Does
22
-
23
- Database "branching" = snapshots for backup/restore:
24
-
25
- ```bash
26
- nvoi db branch create [name] # Dump current DB to file
27
- nvoi db branch list # List saved branches
28
- nvoi db branch restore <id> # Restore branch to new DB
29
- nvoi db branch download <id> # Download dump locally
30
- ```
31
-
32
- ---
33
-
34
- ## Target Structure
35
-
36
- ```
37
- lib/nvoi/cli/db/
38
- ├── command.rb # Router for subcommands
39
- ├── branch/
40
- │ ├── create/
41
- │ │ └── command.rb
42
- │ ├── list/
43
- │ │ └── command.rb
44
- │ ├── restore/
45
- │ │ └── command.rb
46
- │ └── download/
47
- │ └── command.rb
48
- ```
49
-
50
- OR simpler (single file since operations are small):
51
-
52
- ```
53
- lib/nvoi/cli/db/
54
- └── command.rb # All branch operations
55
- ```
56
-
57
- ---
58
-
59
- ## Database Module → External
60
-
61
- The `database/` providers are external adapters (talk to DBs via kubectl exec):
62
-
63
- ```
64
- lib/nvoi/external/database/
65
- ├── provider.rb # Base + factory
66
- ├── postgres.rb
67
- ├── mysql.rb
68
- └── sqlite.rb
69
- ```
70
-
71
- **Note:** These use `kubectl exec` to run dump/restore commands in pods. They're external adapters like cloud providers.
72
-
73
- ---
74
-
75
- ## Objects to Extract
76
-
77
- From `database/provider.rb`:
78
-
79
- ```ruby
80
- # objects/database.rb
81
- Credentials = Struct.new(:user, :password, :host, :port, :database, :path, keyword_init: true)
82
- DumpOptions = Struct.new(:pod_name, :database, :user, :password, :host_path, keyword_init: true)
83
- RestoreOptions = Struct.new(:pod_name, :database, :user, :password, :source_db, :host_path, keyword_init: true)
84
- CreateOptions = Struct.new(:pod_name, :database, :user, :password, keyword_init: true)
85
- Branch = Struct.new(:id, :created_at, :size, :adapter, :database, keyword_init: true)
86
- BranchMetadata # class with branches array
87
- ```
88
-
89
- ---
90
-
91
- ## DRY Opportunities
92
-
93
- ### 1. SSH Helper Pattern
94
- `DbService#with_ssh` creates SSH executor. Same pattern in other services.
95
- Extract to base or mixin:
96
-
97
- ```ruby
98
- # In command base or utils
99
- def with_master_ssh(config, provider)
100
- server_ip = get_master_server_ip(config, provider)
101
- ssh = External::SSH.new(server_ip, config.ssh_key_path)
102
- yield ssh
103
- end
104
- ```
105
-
106
- ### 2. Database Credentials Resolution
107
- `Config::DatabaseHelper.get_credentials` is called from multiple places.
108
- Keep in config or move to database provider factory.
109
-
110
- ---
111
-
112
- ## Migration Steps
113
-
114
- 1. Create `lib/nvoi/external/database/` directory
115
- 2. Move `database/*.rb` → `external/database/`
116
- 3. Extract Structs to `objects/database.rb`
117
- 4. Create `lib/nvoi/cli/db/command.rb`
118
- 5. Move logic from `service/db.rb` → `cli/db/command.rb`
119
- 6. Delete `service/db.rb`
120
-
121
- ---
122
-
123
- ## Estimated Effort
124
-
125
- - **Lines to reorganize:** ~725
126
- - **Files created:** 5 (1 command + 4 external/database)
127
- - **Files deleted/moved:** 5
128
- - **Net change:** minimal (reorganization)
@@ -1,79 +0,0 @@
1
- lib/nvoi/
2
-
3
- ├── cli.rb # Thor routing only
4
-
5
- ├── cli/
6
- │ ├── deploy/
7
- │ │ ├── command.rb
8
- │ │ └── steps/
9
- │ │ ├── provision_network.rb
10
- │ │ ├── provision_server.rb
11
- │ │ ├── provision_volume.rb
12
- │ │ ├── setup_k3s.rb
13
- │ │ ├── configure_tunnel.rb
14
- │ │ ├── build_image.rb
15
- │ │ ├── deploy_service.rb
16
- │ │ └── cleanup_images.rb
17
- │ │
18
- │ ├── delete/
19
- │ │ ├── command.rb
20
- │ │ └── steps/
21
- │ │ ├── teardown_tunnel.rb
22
- │ │ ├── teardown_dns.rb
23
- │ │ ├── detach_volumes.rb
24
- │ │ ├── teardown_server.rb
25
- │ │ ├── teardown_volume.rb
26
- │ │ ├── teardown_firewall.rb
27
- │ │ └── teardown_network.rb
28
- │ │
29
- │ ├── exec/
30
- │ │ └── command.rb
31
- │ │
32
- │ └── credentials/
33
- │ ├── edit/
34
- │ │ └── command.rb
35
- │ └── show/
36
- │ └── command.rb
37
-
38
- ├── external/
39
- │ ├── cloud/
40
- │ │ ├── base.rb
41
- │ │ ├── hetzner.rb
42
- │ │ ├── aws.rb
43
- │ │ └── scaleway.rb
44
- │ ├── dns/
45
- │ │ └── cloudflare.rb
46
- │ ├── ssh.rb
47
- │ ├── kubectl.rb
48
- │ └── containerd.rb
49
-
50
- ├── objects/
51
- │ ├── server.rb
52
- │ ├── network.rb
53
- │ ├── firewall.rb
54
- │ ├── volume.rb
55
- │ ├── tunnel.rb
56
- │ ├── dns_record.rb
57
- │ ├── zone.rb
58
- │ ├── service_spec.rb
59
- │ └── config.rb
60
-
61
- └── utils/
62
- ├── namer.rb
63
- ├── env_resolver.rb
64
- ├── crypto.rb
65
- ├── templates.rb
66
- ├── logger.rb
67
- └── constants.rb
68
-
69
- ---
70
-
71
- Summary:
72
-
73
- | Folder | Purpose | Rule |
74
- | ------------- | ---------------------- | ------------------------------------- |
75
- | cli/ | Command entrypoints | 1 command = 1 command.rb |
76
- | cli/\*/steps/ | Multi-step pipelines | Only for commands with ordered phases |
77
- | external/ | Outside world adapters | APIs, SSH, CLIs |
78
- | objects/ | Data structures | No behavior, just shape |
79
- | utils/ | Stateless helpers | Used everywhere |