nvoi 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/.claude/todo/refactor/00-overview.md +171 -0
- data/.claude/todo/refactor/01-objects.md +96 -0
- data/.claude/todo/refactor/02-utils.md +143 -0
- data/.claude/todo/refactor/03-external-cloud.md +164 -0
- data/.claude/todo/refactor/04-external-dns.md +104 -0
- data/.claude/todo/refactor/05-external.md +133 -0
- data/.claude/todo/refactor/06-cli.md +123 -0
- data/.claude/todo/refactor/07-cli-deploy-command.md +177 -0
- data/.claude/todo/refactor/08-cli-deploy-steps.md +201 -0
- data/.claude/todo/refactor/09-cli-delete-command.md +169 -0
- data/.claude/todo/refactor/10-cli-exec-command.md +157 -0
- data/.claude/todo/refactor/11-cli-credentials-command.md +190 -0
- data/.claude/todo/refactor/12-cli-db-command.md +128 -0
- data/.claude/todo/refactor/_target.md +79 -0
- data/.claude/todo/refactor-execution/00-entrypoint.md +49 -0
- data/.claude/todo/refactor-execution/01-objects.md +42 -0
- data/.claude/todo/refactor-execution/02-utils.md +41 -0
- data/.claude/todo/refactor-execution/03-external-cloud.md +38 -0
- data/.claude/todo/refactor-execution/04-external-dns.md +35 -0
- data/.claude/todo/refactor-execution/05-external-other.md +46 -0
- data/.claude/todo/refactor-execution/06-cli-deploy.md +45 -0
- data/.claude/todo/refactor-execution/07-cli-delete.md +43 -0
- data/.claude/todo/refactor-execution/08-cli-exec.md +30 -0
- data/.claude/todo/refactor-execution/09-cli-credentials.md +34 -0
- data/.claude/todo/refactor-execution/10-cli-db.md +31 -0
- data/.claude/todo/refactor-execution/11-cli-router.md +44 -0
- data/.claude/todo/refactor-execution/12-cleanup.md +120 -0
- data/.claude/todo/refactor-execution/_monitoring-strategy.md +126 -0
- data/.claude/todo/scaleway.impl.md +644 -0
- data/.claude/todo/scaleway.reference.md +520 -0
- data/.claude/todos.md +550 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +46 -5
- data/Rakefile +1 -1
- data/doc/config-schema.yaml +44 -11
- data/examples/golang/deploy.enc +0 -0
- data/examples/golang/main.go +18 -0
- data/exe/nvoi +3 -1
- data/ingest +0 -0
- data/lib/nvoi/cli/config/command.rb +219 -0
- data/lib/nvoi/cli/credentials/edit/command.rb +384 -0
- data/lib/nvoi/cli/credentials/show/command.rb +35 -0
- data/lib/nvoi/cli/db/command.rb +308 -0
- data/lib/nvoi/cli/delete/command.rb +75 -0
- data/lib/nvoi/cli/delete/steps/detach_volumes.rb +98 -0
- data/lib/nvoi/cli/delete/steps/teardown_dns.rb +50 -0
- data/lib/nvoi/cli/delete/steps/teardown_firewall.rb +46 -0
- data/lib/nvoi/cli/delete/steps/teardown_network.rb +30 -0
- data/lib/nvoi/cli/delete/steps/teardown_server.rb +50 -0
- data/lib/nvoi/cli/delete/steps/teardown_tunnel.rb +44 -0
- data/lib/nvoi/cli/delete/steps/teardown_volume.rb +61 -0
- data/lib/nvoi/cli/deploy/command.rb +184 -0
- data/lib/nvoi/cli/deploy/steps/build_image.rb +27 -0
- data/lib/nvoi/cli/deploy/steps/cleanup_images.rb +42 -0
- data/lib/nvoi/cli/deploy/steps/configure_tunnel.rb +102 -0
- data/lib/nvoi/cli/deploy/steps/deploy_service.rb +399 -0
- data/lib/nvoi/cli/deploy/steps/provision_network.rb +44 -0
- data/lib/nvoi/cli/deploy/steps/provision_server.rb +143 -0
- data/lib/nvoi/cli/deploy/steps/provision_volume.rb +171 -0
- data/lib/nvoi/cli/deploy/steps/setup_k3s.rb +490 -0
- data/lib/nvoi/cli/exec/command.rb +173 -0
- data/lib/nvoi/cli/logs/command.rb +66 -0
- data/lib/nvoi/cli/onboard/command.rb +761 -0
- data/lib/nvoi/cli/unlock/command.rb +72 -0
- data/lib/nvoi/cli.rb +339 -141
- data/lib/nvoi/config_api/actions/app.rb +53 -0
- data/lib/nvoi/config_api/actions/compute_provider.rb +55 -0
- data/lib/nvoi/config_api/actions/database.rb +70 -0
- data/lib/nvoi/config_api/actions/domain_provider.rb +40 -0
- data/lib/nvoi/config_api/actions/env.rb +32 -0
- data/lib/nvoi/config_api/actions/init.rb +67 -0
- data/lib/nvoi/config_api/actions/secret.rb +32 -0
- data/lib/nvoi/config_api/actions/server.rb +66 -0
- data/lib/nvoi/config_api/actions/service.rb +52 -0
- data/lib/nvoi/config_api/actions/volume.rb +40 -0
- data/lib/nvoi/config_api/base.rb +38 -0
- data/lib/nvoi/config_api/result.rb +26 -0
- data/lib/nvoi/config_api.rb +93 -0
- data/lib/nvoi/errors.rb +68 -50
- data/lib/nvoi/external/cloud/aws.rb +450 -0
- data/lib/nvoi/external/cloud/base.rb +99 -0
- data/lib/nvoi/external/cloud/factory.rb +48 -0
- data/lib/nvoi/external/cloud/hetzner.rb +402 -0
- data/lib/nvoi/external/cloud/scaleway.rb +559 -0
- data/lib/nvoi/external/cloud.rb +15 -0
- data/lib/nvoi/external/containerd.rb +86 -0
- data/lib/nvoi/external/database/mysql.rb +84 -0
- data/lib/nvoi/external/database/postgres.rb +82 -0
- data/lib/nvoi/external/database/provider.rb +65 -0
- data/lib/nvoi/external/database/sqlite.rb +72 -0
- data/lib/nvoi/external/database.rb +22 -0
- data/lib/nvoi/external/dns/cloudflare.rb +310 -0
- data/lib/nvoi/external/kubectl.rb +65 -0
- data/lib/nvoi/external/ssh.rb +106 -0
- data/lib/nvoi/objects/config_override.rb +60 -0
- data/lib/nvoi/objects/configuration.rb +483 -0
- data/lib/nvoi/objects/database.rb +56 -0
- data/lib/nvoi/objects/dns.rb +14 -0
- data/lib/nvoi/objects/firewall.rb +11 -0
- data/lib/nvoi/objects/network.rb +11 -0
- data/lib/nvoi/objects/server.rb +14 -0
- data/lib/nvoi/objects/service_spec.rb +26 -0
- data/lib/nvoi/objects/tunnel.rb +14 -0
- data/lib/nvoi/objects/volume.rb +17 -0
- data/lib/nvoi/utils/config_loader.rb +172 -0
- data/lib/nvoi/utils/constants.rb +61 -0
- data/lib/nvoi/{credentials/manager.rb → utils/credential_store.rb} +16 -16
- data/lib/nvoi/{credentials → utils}/crypto.rb +8 -5
- data/lib/nvoi/{config → utils}/env_resolver.rb +10 -2
- data/lib/nvoi/utils/logger.rb +84 -0
- data/lib/nvoi/{config/naming.rb → utils/namer.rb} +37 -25
- data/lib/nvoi/{deployer → utils}/retry.rb +23 -3
- data/lib/nvoi/utils/templates.rb +62 -0
- data/lib/nvoi/version.rb +1 -1
- data/lib/nvoi.rb +27 -55
- data/templates/app-ingress.yaml.erb +3 -1
- data/templates/error-backend.yaml.erb +134 -0
- metadata +121 -44
- data/examples/golang/deploy.yml +0 -54
- data/lib/nvoi/cloudflare/client.rb +0 -287
- data/lib/nvoi/config/config.rb +0 -248
- data/lib/nvoi/config/loader.rb +0 -102
- data/lib/nvoi/config/ssh_keys.rb +0 -82
- data/lib/nvoi/config/types.rb +0 -274
- data/lib/nvoi/constants.rb +0 -59
- data/lib/nvoi/credentials/editor.rb +0 -272
- data/lib/nvoi/deployer/cleaner.rb +0 -36
- data/lib/nvoi/deployer/image_builder.rb +0 -23
- data/lib/nvoi/deployer/infrastructure.rb +0 -126
- data/lib/nvoi/deployer/orchestrator.rb +0 -146
- data/lib/nvoi/deployer/service_deployer.rb +0 -311
- data/lib/nvoi/deployer/tunnel_manager.rb +0 -57
- data/lib/nvoi/deployer/types.rb +0 -8
- data/lib/nvoi/k8s/renderer.rb +0 -44
- data/lib/nvoi/k8s/templates.rb +0 -29
- data/lib/nvoi/logger.rb +0 -72
- data/lib/nvoi/providers/aws.rb +0 -403
- data/lib/nvoi/providers/base.rb +0 -111
- data/lib/nvoi/providers/hetzner.rb +0 -288
- data/lib/nvoi/providers/hetzner_client.rb +0 -170
- data/lib/nvoi/remote/docker_manager.rb +0 -203
- data/lib/nvoi/remote/ssh_executor.rb +0 -72
- data/lib/nvoi/remote/volume_manager.rb +0 -103
- data/lib/nvoi/service/delete.rb +0 -234
- data/lib/nvoi/service/deploy.rb +0 -80
- data/lib/nvoi/service/exec.rb +0 -144
- data/lib/nvoi/service/provider.rb +0 -36
- data/lib/nvoi/steps/application_deployer.rb +0 -26
- data/lib/nvoi/steps/database_provisioner.rb +0 -60
- data/lib/nvoi/steps/k3s_cluster_setup.rb +0 -105
- data/lib/nvoi/steps/k3s_provisioner.rb +0 -351
- data/lib/nvoi/steps/server_provisioner.rb +0 -43
- data/lib/nvoi/steps/services_provisioner.rb +0 -29
- data/lib/nvoi/steps/tunnel_configurator.rb +0 -66
- data/lib/nvoi/steps/volume_provisioner.rb +0 -154
data/doc/config-schema.yaml
CHANGED
|
@@ -187,6 +187,27 @@ components:
|
|
|
187
187
|
minimum: 1
|
|
188
188
|
default: 1
|
|
189
189
|
example: 2
|
|
190
|
+
volumes:
|
|
191
|
+
type: object
|
|
192
|
+
description: Block storage volumes attached to this server (key is volume name)
|
|
193
|
+
additionalProperties:
|
|
194
|
+
$ref: "#/components/schemas/ServerVolumeConfig"
|
|
195
|
+
example:
|
|
196
|
+
database:
|
|
197
|
+
size: 20
|
|
198
|
+
uploads:
|
|
199
|
+
size: 10
|
|
200
|
+
|
|
201
|
+
ServerVolumeConfig:
|
|
202
|
+
type: object
|
|
203
|
+
description: Server-attached volume configuration
|
|
204
|
+
properties:
|
|
205
|
+
size:
|
|
206
|
+
type: integer
|
|
207
|
+
description: Volume size in GB
|
|
208
|
+
minimum: 10
|
|
209
|
+
default: 10
|
|
210
|
+
example: 20
|
|
190
211
|
|
|
191
212
|
AppServiceConfig:
|
|
192
213
|
type: object
|
|
@@ -227,13 +248,15 @@ components:
|
|
|
227
248
|
description: Service-specific environment variables
|
|
228
249
|
additionalProperties:
|
|
229
250
|
type: string
|
|
230
|
-
|
|
251
|
+
mounts:
|
|
231
252
|
type: object
|
|
232
|
-
description:
|
|
253
|
+
description: |
|
|
254
|
+
Mount server volumes into the container.
|
|
255
|
+
Key is volume name (must exist in server's volumes), value is container mount path.
|
|
256
|
+
Note: Services with mounts can only run on a single server.
|
|
233
257
|
additionalProperties:
|
|
234
258
|
type: string
|
|
235
259
|
example:
|
|
236
|
-
data: /app/data
|
|
237
260
|
uploads: /app/public/uploads
|
|
238
261
|
|
|
239
262
|
HealthCheckConfig:
|
|
@@ -294,10 +317,15 @@ components:
|
|
|
294
317
|
type: string
|
|
295
318
|
description: Docker image for database (managed databases only)
|
|
296
319
|
example: postgres:15-alpine
|
|
297
|
-
|
|
298
|
-
type:
|
|
299
|
-
description:
|
|
300
|
-
|
|
320
|
+
mount:
|
|
321
|
+
type: object
|
|
322
|
+
description: |
|
|
323
|
+
Mount a server volume for database data persistence.
|
|
324
|
+
Key is volume name (must exist in server's volumes), value is container mount path.
|
|
325
|
+
additionalProperties:
|
|
326
|
+
type: string
|
|
327
|
+
example:
|
|
328
|
+
database: /var/lib/postgresql/data
|
|
301
329
|
secrets:
|
|
302
330
|
type: object
|
|
303
331
|
description: Database secrets (POSTGRES_PASSWORD, etc.)
|
|
@@ -331,10 +359,15 @@ components:
|
|
|
331
359
|
description: Service environment variables
|
|
332
360
|
additionalProperties:
|
|
333
361
|
type: string
|
|
334
|
-
|
|
335
|
-
type:
|
|
336
|
-
description:
|
|
337
|
-
|
|
362
|
+
mount:
|
|
363
|
+
type: object
|
|
364
|
+
description: |
|
|
365
|
+
Mount a server volume for data persistence.
|
|
366
|
+
Key is volume name (must exist in server's volumes), value is container mount path.
|
|
367
|
+
additionalProperties:
|
|
368
|
+
type: string
|
|
369
|
+
example:
|
|
370
|
+
redis_data: /data
|
|
338
371
|
|
|
339
372
|
SSHKeyConfig:
|
|
340
373
|
type: object
|
data/examples/golang/deploy.enc
CHANGED
|
Binary file
|
data/examples/golang/main.go
CHANGED
|
@@ -97,6 +97,9 @@ func setupRouter() *gin.Engine {
|
|
|
97
97
|
// Main endpoint: creates user on every visit, returns all users
|
|
98
98
|
router.GET("/", handleVisit)
|
|
99
99
|
|
|
100
|
+
// Test custom error pages (nginx intercepts 502, 503, 504)
|
|
101
|
+
router.GET("/error/:code", handleError)
|
|
102
|
+
|
|
100
103
|
return router
|
|
101
104
|
}
|
|
102
105
|
|
|
@@ -164,6 +167,21 @@ func handleVisit(c *gin.Context) {
|
|
|
164
167
|
})
|
|
165
168
|
}
|
|
166
169
|
|
|
170
|
+
// Error handler for testing custom error pages (nginx intercepts 502, 503, 504)
|
|
171
|
+
func handleError(c *gin.Context) {
|
|
172
|
+
code := c.Param("code")
|
|
173
|
+
switch code {
|
|
174
|
+
case "502":
|
|
175
|
+
c.AbortWithStatus(http.StatusBadGateway)
|
|
176
|
+
case "503":
|
|
177
|
+
c.AbortWithStatus(http.StatusServiceUnavailable)
|
|
178
|
+
case "504":
|
|
179
|
+
c.AbortWithStatus(http.StatusGatewayTimeout)
|
|
180
|
+
default:
|
|
181
|
+
c.JSON(http.StatusOK, gin.H{"usage": "/error/{502,503,504}"})
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
167
185
|
// Generate random name for demo purposes
|
|
168
186
|
func generateRandomName() string {
|
|
169
187
|
firstNames := []string{"Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry", "Ivy", "Jack"}
|
data/exe/nvoi
CHANGED
data/ingest
ADDED
|
File without changes
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Nvoi
|
|
4
|
+
class Cli
|
|
5
|
+
module Config
|
|
6
|
+
# Command helper for all config operations
|
|
7
|
+
# Uses CredentialStore for crypto, ConfigApi for transformations
|
|
8
|
+
class Command
|
|
9
|
+
def initialize(options)
|
|
10
|
+
@options = options
|
|
11
|
+
@working_dir = options[:dir] || "."
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Initialize new config
|
|
15
|
+
def init(name, environment)
|
|
16
|
+
result = ConfigApi.init(name:, environment:)
|
|
17
|
+
|
|
18
|
+
if result.failure?
|
|
19
|
+
error("Failed to initialize: #{result.error_message}")
|
|
20
|
+
return
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Write encrypted config
|
|
24
|
+
config_path = File.join(@working_dir, Utils::DEFAULT_ENCRYPTED_FILE)
|
|
25
|
+
key_path = File.join(@working_dir, Utils::DEFAULT_KEY_FILE)
|
|
26
|
+
|
|
27
|
+
File.binwrite(config_path, result.config)
|
|
28
|
+
File.write(key_path, "#{result.master_key}\n", perm: 0o600)
|
|
29
|
+
|
|
30
|
+
update_gitignore
|
|
31
|
+
|
|
32
|
+
success("Created #{Utils::DEFAULT_ENCRYPTED_FILE}")
|
|
33
|
+
success("Created #{Utils::DEFAULT_KEY_FILE} (keep safe, never commit)")
|
|
34
|
+
puts
|
|
35
|
+
puts "Next steps:"
|
|
36
|
+
puts " nvoi config domain set cloudflare --api-token=TOKEN --account-id=ID"
|
|
37
|
+
puts " nvoi config provider set hetzner --api-token=TOKEN --server-type=cx22 --location=fsn1"
|
|
38
|
+
puts " nvoi config server set web --master"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Domain provider
|
|
42
|
+
def domain_set(provider, api_token:, account_id:)
|
|
43
|
+
with_config do |data|
|
|
44
|
+
ConfigApi.set_domain_provider(data, provider:, api_token:, account_id:)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def domain_rm
|
|
49
|
+
with_config do |data|
|
|
50
|
+
ConfigApi.delete_domain_provider(data)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Compute provider
|
|
55
|
+
def provider_set(provider, **opts)
|
|
56
|
+
with_config do |data|
|
|
57
|
+
ConfigApi.set_compute_provider(data, provider:, **opts)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def provider_rm
|
|
62
|
+
with_config do |data|
|
|
63
|
+
ConfigApi.delete_compute_provider(data)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Server
|
|
68
|
+
def server_set(name, master: false, type: nil, location: nil, count: 1)
|
|
69
|
+
with_config do |data|
|
|
70
|
+
ConfigApi.set_server(data, name:, master:, type:, location:, count:)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def server_rm(name)
|
|
75
|
+
with_config do |data|
|
|
76
|
+
ConfigApi.delete_server(data, name:)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Volume
|
|
81
|
+
def volume_set(server, name, size: 10)
|
|
82
|
+
with_config do |data|
|
|
83
|
+
ConfigApi.set_volume(data, server:, name:, size:)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def volume_rm(server, name)
|
|
88
|
+
with_config do |data|
|
|
89
|
+
ConfigApi.delete_volume(data, server:, name:)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# App
|
|
94
|
+
def app_set(name, servers:, **opts)
|
|
95
|
+
with_config do |data|
|
|
96
|
+
ConfigApi.set_app(data, name:, servers:, **opts)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def app_rm(name)
|
|
101
|
+
with_config do |data|
|
|
102
|
+
ConfigApi.delete_app(data, name:)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Database
|
|
107
|
+
def database_set(servers:, adapter:, **opts)
|
|
108
|
+
with_config do |data|
|
|
109
|
+
ConfigApi.set_database(data, servers:, adapter:, **opts)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def database_rm
|
|
114
|
+
with_config do |data|
|
|
115
|
+
ConfigApi.delete_database(data)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Service
|
|
120
|
+
def service_set(name, servers:, image:, **opts)
|
|
121
|
+
with_config do |data|
|
|
122
|
+
ConfigApi.set_service(data, name:, servers:, image:, **opts)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def service_rm(name)
|
|
127
|
+
with_config do |data|
|
|
128
|
+
ConfigApi.delete_service(data, name:)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Secret
|
|
133
|
+
def secret_set(key_name, value)
|
|
134
|
+
with_config do |data|
|
|
135
|
+
ConfigApi.set_secret(data, key: key_name, value:)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def secret_rm(key_name)
|
|
140
|
+
with_config do |data|
|
|
141
|
+
ConfigApi.delete_secret(data, key: key_name)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Env
|
|
146
|
+
def env_set(key_name, value)
|
|
147
|
+
with_config do |data|
|
|
148
|
+
ConfigApi.set_env(data, key: key_name, value:)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def env_rm(key_name)
|
|
153
|
+
with_config do |data|
|
|
154
|
+
ConfigApi.delete_env(data, key: key_name)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def with_config
|
|
161
|
+
store = Utils::CredentialStore.new(
|
|
162
|
+
@working_dir,
|
|
163
|
+
@options[:credentials],
|
|
164
|
+
@options[:master_key]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
unless store.exists?
|
|
168
|
+
error("Config not found: #{store.encrypted_path}")
|
|
169
|
+
error("Run 'nvoi config init --name=myapp' first")
|
|
170
|
+
return
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Read and parse
|
|
174
|
+
yaml = store.read
|
|
175
|
+
data = YAML.safe_load(yaml, permitted_classes: [Symbol])
|
|
176
|
+
|
|
177
|
+
# Transform
|
|
178
|
+
result = yield(data)
|
|
179
|
+
|
|
180
|
+
if result.failure?
|
|
181
|
+
error("#{result.error_type}: #{result.error_message}")
|
|
182
|
+
else
|
|
183
|
+
# Serialize and write
|
|
184
|
+
store.write(YAML.dump(result.data))
|
|
185
|
+
success("Config updated")
|
|
186
|
+
end
|
|
187
|
+
rescue Errors::CredentialError => e
|
|
188
|
+
error(e.message)
|
|
189
|
+
rescue Errors::DecryptionError => e
|
|
190
|
+
error("Decryption failed: #{e.message}")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def update_gitignore
|
|
194
|
+
gitignore_path = File.join(@working_dir, ".gitignore")
|
|
195
|
+
entries = ["deploy.key", ".env", ".env.*", "!.env.example", "!.env.*.example"]
|
|
196
|
+
|
|
197
|
+
existing = File.exist?(gitignore_path) ? File.read(gitignore_path) : ""
|
|
198
|
+
additions = entries.reject { |e| existing.include?(e) }
|
|
199
|
+
|
|
200
|
+
return if additions.empty?
|
|
201
|
+
|
|
202
|
+
File.open(gitignore_path, "a") do |f|
|
|
203
|
+
f.puts "" unless existing.end_with?("\n") || existing.empty?
|
|
204
|
+
f.puts "# NVOI - sensitive files"
|
|
205
|
+
additions.each { |e| f.puts e }
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def success(msg)
|
|
210
|
+
puts "\e[32m✓\e[0m #{msg}"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def error(msg)
|
|
214
|
+
warn "\e[31m✗\e[0m #{msg}"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|