nvoi 0.1.8 → 0.2.1

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 (122) 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/_TODO-rails-example.md +816 -0
  6. data/_TODO-rails-optimization.md +433 -0
  7. data/doc/config-schema.yaml +12 -0
  8. data/examples/apex-wildcard/deploy.yml +1 -0
  9. data/examples/golang-postgres-multi/deploy.yml +1 -0
  10. data/examples/postgres-multi/deploy.yml +1 -0
  11. data/examples/postgres-single/deploy.yml +1 -0
  12. data/examples/rails-single/deploy.yml +1 -0
  13. data/lib/nvoi/cli/config/command.rb +46 -41
  14. data/lib/nvoi/cli/credentials/edit/command.rb +24 -20
  15. data/lib/nvoi/cli/credentials/show/command.rb +1 -1
  16. data/lib/nvoi/cli/db/command.rb +10 -10
  17. data/lib/nvoi/cli/delete/command.rb +2 -2
  18. data/lib/nvoi/cli/deploy/command.rb +2 -2
  19. data/lib/nvoi/cli/deploy/steps/build_image.rb +2 -1
  20. data/lib/nvoi/cli/deploy/steps/configure_tunnel.rb +2 -2
  21. data/lib/nvoi/cli/deploy/steps/deploy_service.rb +7 -4
  22. data/lib/nvoi/cli/deploy/steps/provision_server.rb +1 -1
  23. data/lib/nvoi/cli/deploy/steps/provision_volume.rb +1 -1
  24. data/lib/nvoi/cli/exec/command.rb +3 -3
  25. data/lib/nvoi/cli/logs/command.rb +2 -2
  26. data/lib/nvoi/cli/onboard/command.rb +176 -622
  27. data/lib/nvoi/cli/onboard/steps/app.rb +108 -0
  28. data/lib/nvoi/cli/onboard/steps/app_name.rb +26 -0
  29. data/lib/nvoi/cli/onboard/steps/compute.rb +186 -0
  30. data/lib/nvoi/cli/onboard/steps/database.rb +97 -0
  31. data/lib/nvoi/cli/onboard/steps/domain.rb +48 -0
  32. data/lib/nvoi/cli/onboard/steps/env.rb +67 -0
  33. data/lib/nvoi/cli/onboard/ui.rb +84 -0
  34. data/lib/nvoi/cli/unlock/command.rb +2 -2
  35. data/lib/nvoi/cli.rb +2 -33
  36. data/lib/nvoi/configuration/app_service.rb +54 -0
  37. data/lib/nvoi/configuration/application.rb +44 -0
  38. data/lib/nvoi/configuration/builder.rb +420 -0
  39. data/lib/nvoi/configuration/database.rb +56 -0
  40. data/lib/nvoi/configuration/deploy.rb +15 -0
  41. data/lib/nvoi/{objects/service_spec.rb → configuration/deployment.rb} +4 -3
  42. data/lib/nvoi/{objects/config_override.rb → configuration/override.rb} +4 -4
  43. data/lib/nvoi/configuration/providers.rb +81 -0
  44. data/lib/nvoi/configuration/result.rb +43 -0
  45. data/lib/nvoi/configuration/root.rb +252 -0
  46. data/lib/nvoi/configuration/server.rb +39 -0
  47. data/lib/nvoi/configuration/service.rb +51 -0
  48. data/lib/nvoi/configuration/ssh_key.rb +16 -0
  49. data/lib/nvoi/external/cloud/aws.rb +26 -16
  50. data/lib/nvoi/external/cloud/hetzner.rb +40 -25
  51. data/lib/nvoi/external/cloud/scaleway.rb +10 -8
  52. data/lib/nvoi/external/cloud/types.rb +42 -0
  53. data/lib/nvoi/external/database/mysql.rb +1 -1
  54. data/lib/nvoi/external/database/postgres.rb +1 -1
  55. data/lib/nvoi/external/database/provider.rb +1 -1
  56. data/lib/nvoi/external/database/sqlite.rb +1 -1
  57. data/lib/nvoi/external/database/types.rb +55 -0
  58. data/lib/nvoi/external/dns/cloudflare.rb +11 -11
  59. data/lib/nvoi/external/dns/types.rb +24 -0
  60. data/lib/nvoi/utils/config_loader.rb +12 -12
  61. data/lib/nvoi/utils/credential_store.rb +4 -4
  62. data/lib/nvoi/utils/env_resolver.rb +3 -3
  63. data/lib/nvoi/utils/namer.rb +8 -3
  64. data/lib/nvoi/utils/presence.rb +23 -0
  65. data/lib/nvoi/version.rb +1 -1
  66. data/lib/nvoi.rb +2 -17
  67. metadata +98 -59
  68. data/.claude/todo/refactor/00-overview.md +0 -171
  69. data/.claude/todo/refactor/01-objects.md +0 -96
  70. data/.claude/todo/refactor/02-utils.md +0 -143
  71. data/.claude/todo/refactor/03-external-cloud.md +0 -164
  72. data/.claude/todo/refactor/04-external-dns.md +0 -104
  73. data/.claude/todo/refactor/05-external.md +0 -133
  74. data/.claude/todo/refactor/06-cli.md +0 -123
  75. data/.claude/todo/refactor/07-cli-deploy-command.md +0 -177
  76. data/.claude/todo/refactor/08-cli-deploy-steps.md +0 -201
  77. data/.claude/todo/refactor/09-cli-delete-command.md +0 -169
  78. data/.claude/todo/refactor/10-cli-exec-command.md +0 -157
  79. data/.claude/todo/refactor/11-cli-credentials-command.md +0 -190
  80. data/.claude/todo/refactor/12-cli-db-command.md +0 -128
  81. data/.claude/todo/refactor/_target.md +0 -79
  82. data/.claude/todo/refactor-execution/00-entrypoint.md +0 -49
  83. data/.claude/todo/refactor-execution/01-objects.md +0 -42
  84. data/.claude/todo/refactor-execution/02-utils.md +0 -41
  85. data/.claude/todo/refactor-execution/03-external-cloud.md +0 -38
  86. data/.claude/todo/refactor-execution/04-external-dns.md +0 -35
  87. data/.claude/todo/refactor-execution/05-external-other.md +0 -46
  88. data/.claude/todo/refactor-execution/06-cli-deploy.md +0 -45
  89. data/.claude/todo/refactor-execution/07-cli-delete.md +0 -43
  90. data/.claude/todo/refactor-execution/08-cli-exec.md +0 -30
  91. data/.claude/todo/refactor-execution/09-cli-credentials.md +0 -34
  92. data/.claude/todo/refactor-execution/10-cli-db.md +0 -31
  93. data/.claude/todo/refactor-execution/11-cli-router.md +0 -44
  94. data/.claude/todo/refactor-execution/12-cleanup.md +0 -120
  95. data/.claude/todo/refactor-execution/_monitoring-strategy.md +0 -126
  96. data/.claude/todo/scaleway.impl.md +0 -644
  97. data/.claude/todo/scaleway.reference.md +0 -520
  98. data/.claude/todos/buckets.md +0 -41
  99. data/.claude/todos.md +0 -550
  100. data/Makefile +0 -26
  101. data/ingest +0 -0
  102. data/lib/nvoi/config_api/actions/app.rb +0 -53
  103. data/lib/nvoi/config_api/actions/compute_provider.rb +0 -55
  104. data/lib/nvoi/config_api/actions/database.rb +0 -70
  105. data/lib/nvoi/config_api/actions/domain_provider.rb +0 -40
  106. data/lib/nvoi/config_api/actions/env.rb +0 -32
  107. data/lib/nvoi/config_api/actions/init.rb +0 -67
  108. data/lib/nvoi/config_api/actions/secret.rb +0 -32
  109. data/lib/nvoi/config_api/actions/server.rb +0 -66
  110. data/lib/nvoi/config_api/actions/service.rb +0 -52
  111. data/lib/nvoi/config_api/actions/volume.rb +0 -40
  112. data/lib/nvoi/config_api/base.rb +0 -38
  113. data/lib/nvoi/config_api/result.rb +0 -26
  114. data/lib/nvoi/config_api.rb +0 -93
  115. data/lib/nvoi/objects/configuration.rb +0 -483
  116. data/lib/nvoi/objects/database.rb +0 -56
  117. data/lib/nvoi/objects/dns.rb +0 -14
  118. data/lib/nvoi/objects/firewall.rb +0 -11
  119. data/lib/nvoi/objects/network.rb +0 -11
  120. data/lib/nvoi/objects/server.rb +0 -14
  121. data/lib/nvoi/objects/tunnel.rb +0 -14
  122. data/lib/nvoi/objects/volume.rb +0 -17
data/lib/nvoi/cli.rb CHANGED
@@ -23,7 +23,6 @@ module Nvoi
23
23
 
24
24
  desc "onboard", "Interactive setup wizard"
25
25
  def onboard
26
- require_relative "cli/onboard/command"
27
26
  Cli::Onboard::Command.new.run
28
27
  end
29
28
 
@@ -31,20 +30,17 @@ module Nvoi
31
30
  option :dockerfile_path, desc: "Path to Dockerfile (optional, defaults to ./Dockerfile)"
32
31
  option :config_dir, desc: "Directory containing SSH keys (optional, defaults to ~/.ssh)"
33
32
  def deploy
34
- require_relative "cli/deploy/command"
35
33
  Cli::Deploy::Command.new(options).run
36
34
  end
37
35
 
38
36
  desc "delete", "Delete server, firewall, and network"
39
37
  option :config_dir, desc: "Directory containing SSH keys (optional, defaults to ~/.ssh)"
40
38
  def delete
41
- require_relative "cli/delete/command"
42
39
  Cli::Delete::Command.new(options).run
43
40
  end
44
41
 
45
42
  desc "unlock", "Remove deployment lock (use when deploy hangs)"
46
43
  def unlock
47
- require_relative "cli/unlock/command"
48
44
  Cli::Unlock::Command.new(options).run
49
45
  end
50
46
 
@@ -52,7 +48,6 @@ module Nvoi
52
48
  option :follow, aliases: "-f", type: :boolean, default: false, desc: "Follow log output"
53
49
  option :tail, aliases: "-n", type: :numeric, default: 100, desc: "Number of lines to show"
54
50
  def logs(app_name)
55
- require_relative "cli/logs/command"
56
51
  Cli::Logs::Command.new(options).run(app_name)
57
52
  end
58
53
 
@@ -62,7 +57,6 @@ module Nvoi
62
57
  option :interactive, aliases: "-i", type: :boolean, default: false,
63
58
  desc: "Open interactive SSH shell instead of executing command"
64
59
  def exec(*args)
65
- require_relative "cli/exec/command"
66
60
  Cli::Exec::Command.new(options).run(args)
67
61
  end
68
62
 
@@ -78,19 +72,16 @@ module Nvoi
78
72
 
79
73
  desc "edit", "Edit encrypted credentials"
80
74
  def edit
81
- require_relative "cli/credentials/edit/command"
82
75
  Nvoi::Cli::Credentials::Edit::Command.new(options).run
83
76
  end
84
77
 
85
78
  desc "show", "Show decrypted credentials"
86
79
  def show
87
- require_relative "cli/credentials/show/command"
88
80
  Nvoi::Cli::Credentials::Show::Command.new(options).run
89
81
  end
90
82
 
91
83
  desc "set PATH VALUE", "Set a value at a dot-notation path"
92
84
  def set(path, value)
93
- require_relative "cli/credentials/edit/command"
94
85
  Nvoi::Cli::Credentials::Edit::Command.new(options).set(path, value)
95
86
  end
96
87
  }
@@ -109,7 +100,6 @@ module Nvoi
109
100
  option :name, required: true, desc: "Application name"
110
101
  option :environment, default: "production", desc: "Environment"
111
102
  def init
112
- require_relative "cli/config/command"
113
103
  Nvoi::Cli::Config::Command.new(options).init(options[:name], options[:environment])
114
104
  end
115
105
 
@@ -124,6 +114,7 @@ module Nvoi
124
114
  option :api_token, desc: "API token (hetzner)"
125
115
  option :server_type, desc: "Server type (cx22, etc)"
126
116
  option :server_location, desc: "Location (fsn1, etc)"
117
+ option :architecture, desc: "CPU architecture (x86, arm64)"
127
118
  option :access_key_id, desc: "AWS access key ID"
128
119
  option :secret_access_key, desc: "AWS secret access key"
129
120
  option :region, desc: "AWS region"
@@ -132,9 +123,8 @@ module Nvoi
132
123
  option :project_id, desc: "Scaleway project ID"
133
124
  option :zone, desc: "Scaleway zone"
134
125
  def set(provider)
135
- require_relative "cli/config/command"
136
126
  Nvoi::Cli::Config::Command.new(options).provider_set(provider, **options.slice(
137
- :api_token, :server_type, :server_location,
127
+ :api_token, :server_type, :server_location, :architecture,
138
128
  :access_key_id, :secret_access_key, :region, :instance_type,
139
129
  :secret_key, :project_id, :zone
140
130
  ).transform_keys(&:to_sym).compact)
@@ -142,7 +132,6 @@ module Nvoi
142
132
 
143
133
  desc "rm", "Remove compute provider"
144
134
  def rm
145
- require_relative "cli/config/command"
146
135
  Nvoi::Cli::Config::Command.new(options).provider_rm
147
136
  end
148
137
  }
@@ -158,13 +147,11 @@ module Nvoi
158
147
  option :api_token, required: true, desc: "API token"
159
148
  option :account_id, required: true, desc: "Account ID"
160
149
  def set(provider)
161
- require_relative "cli/config/command"
162
150
  Nvoi::Cli::Config::Command.new(options).domain_set(provider, api_token: options[:api_token], account_id: options[:account_id])
163
151
  end
164
152
 
165
153
  desc "rm", "Remove domain provider"
166
154
  def rm
167
- require_relative "cli/config/command"
168
155
  Nvoi::Cli::Config::Command.new(options).domain_rm
169
156
  end
170
157
  }
@@ -182,13 +169,11 @@ module Nvoi
182
169
  option :location, desc: "Location override"
183
170
  option :count, type: :numeric, default: 1, desc: "Number of servers"
184
171
  def set(name)
185
- require_relative "cli/config/command"
186
172
  Nvoi::Cli::Config::Command.new(options).server_set(name, master: options[:master], type: options[:type], location: options[:location], count: options[:count])
187
173
  end
188
174
 
189
175
  desc "rm NAME", "Remove server"
190
176
  def rm(name)
191
- require_relative "cli/config/command"
192
177
  Nvoi::Cli::Config::Command.new(options).server_rm(name)
193
178
  end
194
179
  }
@@ -203,13 +188,11 @@ module Nvoi
203
188
  desc "set SERVER NAME", "Add or update volume"
204
189
  option :size, type: :numeric, default: 10, desc: "Volume size in GB"
205
190
  def set(server, name)
206
- require_relative "cli/config/command"
207
191
  Nvoi::Cli::Config::Command.new(options).volume_set(server, name, size: options[:size])
208
192
  end
209
193
 
210
194
  desc "rm SERVER NAME", "Remove volume"
211
195
  def rm(server, name)
212
- require_relative "cli/config/command"
213
196
  Nvoi::Cli::Config::Command.new(options).volume_rm(server, name)
214
197
  end
215
198
  }
@@ -229,13 +212,11 @@ module Nvoi
229
212
  option :command, desc: "Run command"
230
213
  option :pre_run_command, desc: "Pre-run command (migrations, etc)"
231
214
  def set(name)
232
- require_relative "cli/config/command"
233
215
  Nvoi::Cli::Config::Command.new(options).app_set(name, **options.slice(:servers, :domain, :subdomain, :port, :command, :pre_run_command).transform_keys(&:to_sym).compact)
234
216
  end
235
217
 
236
218
  desc "rm NAME", "Remove app"
237
219
  def rm(name)
238
- require_relative "cli/config/command"
239
220
  Nvoi::Cli::Config::Command.new(options).app_rm(name)
240
221
  end
241
222
  }
@@ -256,13 +237,11 @@ module Nvoi
256
237
  option :url, desc: "Database URL (alternative to user/pass/db)"
257
238
  option :image, desc: "Custom Docker image"
258
239
  def set
259
- require_relative "cli/config/command"
260
240
  Nvoi::Cli::Config::Command.new(options).database_set(**options.slice(:servers, :adapter, :user, :password, :database, :url, :image).transform_keys(&:to_sym).compact)
261
241
  end
262
242
 
263
243
  desc "rm", "Remove database"
264
244
  def rm
265
- require_relative "cli/config/command"
266
245
  Nvoi::Cli::Config::Command.new(options).database_rm
267
246
  end
268
247
  }
@@ -280,13 +259,11 @@ module Nvoi
280
259
  option :port, type: :numeric, desc: "Port"
281
260
  option :command, desc: "Command"
282
261
  def set(name)
283
- require_relative "cli/config/command"
284
262
  Nvoi::Cli::Config::Command.new(options).service_set(name, **options.slice(:servers, :image, :port, :command).transform_keys(&:to_sym).compact)
285
263
  end
286
264
 
287
265
  desc "rm NAME", "Remove service"
288
266
  def rm(name)
289
- require_relative "cli/config/command"
290
267
  Nvoi::Cli::Config::Command.new(options).service_rm(name)
291
268
  end
292
269
  }
@@ -300,13 +277,11 @@ module Nvoi
300
277
 
301
278
  desc "set KEY VALUE", "Set secret"
302
279
  def set(key, value)
303
- require_relative "cli/config/command"
304
280
  Nvoi::Cli::Config::Command.new(options).secret_set(key, value)
305
281
  end
306
282
 
307
283
  desc "rm KEY", "Remove secret"
308
284
  def rm(key)
309
- require_relative "cli/config/command"
310
285
  Nvoi::Cli::Config::Command.new(options).secret_rm(key)
311
286
  end
312
287
  }
@@ -320,13 +295,11 @@ module Nvoi
320
295
 
321
296
  desc "set KEY VALUE", "Set environment variable"
322
297
  def set(key, value)
323
- require_relative "cli/config/command"
324
298
  Nvoi::Cli::Config::Command.new(options).env_set(key, value)
325
299
  end
326
300
 
327
301
  desc "rm KEY", "Remove environment variable"
328
302
  def rm(key)
329
- require_relative "cli/config/command"
330
303
  Nvoi::Cli::Config::Command.new(options).env_rm(key)
331
304
  end
332
305
  }
@@ -360,26 +333,22 @@ module Nvoi
360
333
 
361
334
  desc "create [NAME]", "Create a new database branch (snapshot)"
362
335
  def create(name = nil)
363
- require_relative "cli/db/command"
364
336
  Nvoi::Cli::Db::Command.new(options).branch_create(name)
365
337
  end
366
338
 
367
339
  desc "list", "List all database branches"
368
340
  def list
369
- require_relative "cli/db/command"
370
341
  Nvoi::Cli::Db::Command.new(options).branch_list
371
342
  end
372
343
 
373
344
  desc "restore ID [NEW_DB_NAME]", "Restore a database branch to a new database"
374
345
  def restore(branch_id, new_db_name = nil)
375
- require_relative "cli/db/command"
376
346
  Nvoi::Cli::Db::Command.new(options).branch_restore(branch_id, new_db_name)
377
347
  end
378
348
 
379
349
  desc "download ID", "Download a database branch dump"
380
350
  option :path, aliases: "-p", desc: "Output file path (default: {branch_id}.sql)"
381
351
  def download(branch_id)
382
- require_relative "cli/db/command"
383
352
  Nvoi::Cli::Db::Command.new(options).branch_download(branch_id)
384
353
  end
385
354
  }
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nvoi
4
+ module Configuration
5
+ # AppService defines a service in the app section
6
+ class AppService
7
+ attr_accessor :servers, :domain, :subdomain, :port, :healthcheck,
8
+ :command, :pre_run_command, :env, :mounts
9
+
10
+ def initialize(data = nil)
11
+ data ||= {}
12
+ @servers = data["servers"] || []
13
+ @domain = data["domain"]
14
+ @subdomain = data["subdomain"]
15
+ @port = data["port"]&.to_i
16
+ @healthcheck = data["healthcheck"] ? HealthCheck.new(data["healthcheck"]) : nil
17
+ @command = data["command"]
18
+ @pre_run_command = data["pre_run_command"]
19
+ @env = data["env"] || {}
20
+ @mounts = data["mounts"] || {}
21
+ end
22
+
23
+ def web?
24
+ @port && @port.positive?
25
+ end
26
+
27
+ def worker?
28
+ !web?
29
+ end
30
+
31
+ def fqdn
32
+ return nil if @domain.blank?
33
+
34
+ @subdomain.blank? ? @domain : "#{@subdomain}.#{@domain}"
35
+ end
36
+
37
+ # HealthCheck defines health check configuration
38
+ class HealthCheck
39
+ attr_accessor :type, :path, :port, :command, :interval, :timeout, :retries
40
+
41
+ def initialize(data = nil)
42
+ data ||= {}
43
+ @type = data["type"]
44
+ @path = data["path"]
45
+ @port = data["port"]&.to_i
46
+ @command = data["command"]
47
+ @interval = data["interval"]
48
+ @timeout = data["timeout"]
49
+ @retries = data["retries"]&.to_i
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nvoi
4
+ module Configuration
5
+ # Application contains application-level configuration
6
+ class Application
7
+ attr_accessor :name, :environment, :domain_provider, :compute_provider,
8
+ :keep_count, :servers, :app, :database, :services, :env,
9
+ :secrets, :ssh_keys
10
+
11
+ def initialize(data = nil)
12
+ data ||= {}
13
+ @name = data["name"]
14
+ @environment = data["environment"] || "production"
15
+ @domain_provider = Providers::DomainProvider.new(data["domain_provider"])
16
+ @compute_provider = Providers::ComputeProvider.new(data["compute_provider"])
17
+ @keep_count = data["keep_count"]&.to_i
18
+ @servers = (data["servers"] || {}).transform_values { |v| Server.new(v) }
19
+ @app = (data["app"] || {}).transform_values { |v| AppService.new(v) }
20
+ @database = data["database"] ? Database.new(data["database"]) : nil
21
+ @services = (data["services"] || {}).transform_values { |v| Service.new(v) }
22
+ @env = data["env"] || {}
23
+ @secrets = data["secrets"] || {}
24
+ @ssh_keys = data["ssh_keys"] ? SshKey.new(data["ssh_keys"]) : nil
25
+ end
26
+
27
+ def app_by_name(name)
28
+ @app[name.to_s]
29
+ end
30
+
31
+ def server_by_name(name)
32
+ @servers[name.to_s]
33
+ end
34
+
35
+ def web_apps
36
+ @app.select { |_, cfg| cfg.web? }
37
+ end
38
+
39
+ def workers
40
+ @app.reject { |_, cfg| cfg.web? }
41
+ end
42
+ end
43
+ end
44
+ end