belt 0.3.31 → 0.3.33
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/CHANGELOG.md +18 -0
- data/lib/belt/cli/destroy_command.rb +69 -7
- data/lib/belt/docs/generators.md +28 -0
- data/lib/belt/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: efadc9dd5984c98b474ea0bda5f91617ccb9ad7077582086098d913d1190fb79
|
|
4
|
+
data.tar.gz: 8335192999b2f888a518e3bb542c04e0b23364523ecc788f8e6eda7b4281e136
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7ae2d5e4669eea772061e523bae6422388e356cbe1ecdf86f7d284b034549ac1127a239ac85cb8b3cf1babab27c0232ec61bcfc18dcac8ff54b2b03cb263435
|
|
7
|
+
data.tar.gz: 190079da2ca430955096baa50ea281da2feaee87e6ae24211aab5c26b559eea6f1d1f45d9c9dc627d7ebf5d28418681c13d0398ef744a970580cfe8441368f3b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.32
|
|
4
|
+
|
|
5
|
+
### Enhancement
|
|
6
|
+
|
|
7
|
+
- **`belt destroy environment --full` for fully non-interactive teardown**: Added
|
|
8
|
+
a `--full` / `-y` flag that tears an environment down with zero prompts —
|
|
9
|
+
intended for CI pipelines and agents that spin up ephemeral per-PR
|
|
10
|
+
environments and destroy them on merge. Unlike `--force` (which skips
|
|
11
|
+
terraform and only removes local files), `--full` **always runs
|
|
12
|
+
`terraform destroy`** when terraform state is present, then deletes the local
|
|
13
|
+
`infrastructure/<env>/` files. It also auto-continues past the nested-children
|
|
14
|
+
warning. When no terraform state exists there is nothing to tear down, so the
|
|
15
|
+
destroy step is skipped and only the files are removed.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
belt destroy environment pr-1234 --full
|
|
19
|
+
```
|
|
20
|
+
|
|
3
21
|
## 0.3.31
|
|
4
22
|
|
|
5
23
|
### Bug Fix
|
|
@@ -47,7 +47,7 @@ module Belt
|
|
|
47
47
|
when 'environment'
|
|
48
48
|
name = args.shift
|
|
49
49
|
if name.nil? || name.empty?
|
|
50
|
-
puts 'Usage: belt destroy environment <name> [--force] [--skip-terraform]'
|
|
50
|
+
puts 'Usage: belt destroy environment <name> [--full] [--force] [--skip-terraform]'
|
|
51
51
|
exit 1
|
|
52
52
|
end
|
|
53
53
|
flags = parse_environment_flags(args)
|
|
@@ -79,13 +79,15 @@ module Belt
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def self.parse_environment_flags(args)
|
|
82
|
-
flags = { force: false, skip_terraform: false }
|
|
82
|
+
flags = { force: false, skip_terraform: false, full: false }
|
|
83
83
|
args.each do |arg|
|
|
84
84
|
case arg
|
|
85
85
|
when '--force', '-f'
|
|
86
86
|
flags[:force] = true
|
|
87
87
|
when '--skip-terraform'
|
|
88
88
|
flags[:skip_terraform] = true
|
|
89
|
+
when '--full', '-y'
|
|
90
|
+
flags[:full] = true
|
|
89
91
|
end
|
|
90
92
|
end
|
|
91
93
|
flags
|
|
@@ -114,7 +116,12 @@ module Belt
|
|
|
114
116
|
views Remove React pages for a resource
|
|
115
117
|
|
|
116
118
|
Environment options:
|
|
117
|
-
--
|
|
119
|
+
--full, -y Non-interactive teardown: ALWAYS run terraform
|
|
120
|
+
destroy, then delete local files AND purge the
|
|
121
|
+
remote terraform state from S3, no prompts
|
|
122
|
+
(use this for CI / agents tearing down ephemeral envs)
|
|
123
|
+
--force, -f Skip all prompts but SKIP terraform destroy —
|
|
124
|
+
only deletes local files (leaves cloud resources)
|
|
118
125
|
--skip-terraform Delete local files without running terraform destroy
|
|
119
126
|
|
|
120
127
|
Examples:
|
|
@@ -124,6 +131,7 @@ module Belt
|
|
|
124
131
|
belt d environment staging
|
|
125
132
|
belt d environment dev --skip-terraform
|
|
126
133
|
belt d environment dev --force
|
|
134
|
+
belt d environment pr-1234 --full # full non-interactive teardown
|
|
127
135
|
belt d frontend
|
|
128
136
|
belt d frontend --frontend ops
|
|
129
137
|
belt d views post
|
|
@@ -133,16 +141,18 @@ module Belt
|
|
|
133
141
|
|
|
134
142
|
For environments: if terraform state is detected, you'll be prompted to run
|
|
135
143
|
`terraform destroy` first to tear down cloud resources before removing files.
|
|
144
|
+
Pass --full to run the whole teardown non-interactively (destroy + delete).
|
|
136
145
|
HELP
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
# -- keyword options for destroy flags
|
|
140
|
-
def initialize(generator, name, fields, force: false, skip_terraform: false, frontend_name: nil)
|
|
149
|
+
def initialize(generator, name, fields, force: false, skip_terraform: false, full: false, frontend_name: nil)
|
|
141
150
|
@generator = generator
|
|
142
151
|
@name = name&.downcase&.gsub(/[^a-z0-9_]/, '_')
|
|
143
152
|
@fields = fields
|
|
144
153
|
@force = force
|
|
145
154
|
@skip_terraform = skip_terraform
|
|
155
|
+
@full = full
|
|
146
156
|
@frontend_name = frontend_name
|
|
147
157
|
@app_name = detect_namespace
|
|
148
158
|
@singular_name = @name ? Belt::Inflector.singularize(@name) : nil
|
|
@@ -205,7 +215,7 @@ module Belt
|
|
|
205
215
|
puts ' Consider destroying nested environments first:'
|
|
206
216
|
nested_children.each { |child| puts " belt destroy environment #{child}" }
|
|
207
217
|
puts ''
|
|
208
|
-
unless @force
|
|
218
|
+
unless @force || @full
|
|
209
219
|
print ' Continue anyway? [y/N] '
|
|
210
220
|
response = $stdin.gets&.strip&.downcase
|
|
211
221
|
unless response&.start_with?('y')
|
|
@@ -227,7 +237,12 @@ module Belt
|
|
|
227
237
|
puts "⚠ Environment '#{@name}' appears to have active infrastructure."
|
|
228
238
|
puts " Terraform state was found — resources may still be running.\n\n"
|
|
229
239
|
|
|
230
|
-
if @
|
|
240
|
+
if @full
|
|
241
|
+
# Fully non-interactive teardown (CI / agents): always run terraform
|
|
242
|
+
# destroy, then delete files. This is the flag to reach for when an
|
|
243
|
+
# automated workflow spins an environment up and tears it down.
|
|
244
|
+
run_terraform_destroy(dir)
|
|
245
|
+
elsif @force
|
|
231
246
|
puts ' --force passed, skipping terraform destroy.'
|
|
232
247
|
else
|
|
233
248
|
puts ' Options:'
|
|
@@ -251,7 +266,7 @@ module Belt
|
|
|
251
266
|
end
|
|
252
267
|
|
|
253
268
|
# Final confirmation before deleting files
|
|
254
|
-
unless @force
|
|
269
|
+
unless @force || @full
|
|
255
270
|
print "\nPermanently delete #{dir}/? [y/N] "
|
|
256
271
|
response = $stdin.gets&.strip&.downcase
|
|
257
272
|
unless response&.start_with?('y')
|
|
@@ -260,6 +275,9 @@ module Belt
|
|
|
260
275
|
end
|
|
261
276
|
end
|
|
262
277
|
|
|
278
|
+
# Purge remote state from S3 when --full is used (complete ephemeral cleanup)
|
|
279
|
+
purge_terraform_state(dir) if @full
|
|
280
|
+
|
|
263
281
|
FileUtils.rm_rf(dir)
|
|
264
282
|
@removed << dir
|
|
265
283
|
puts " remove #{dir}/"
|
|
@@ -388,6 +406,50 @@ module Belt
|
|
|
388
406
|
puts ' ✓ Infrastructure destroyed.'
|
|
389
407
|
end
|
|
390
408
|
|
|
409
|
+
# Purge the terraform state file from S3 after a full destroy.
|
|
410
|
+
# This leaves zero trace of ephemeral environments in the state bucket.
|
|
411
|
+
def purge_terraform_state(dir)
|
|
412
|
+
backend_file = File.join(dir, 'backend.tf')
|
|
413
|
+
return unless File.exist?(backend_file)
|
|
414
|
+
|
|
415
|
+
content = File.read(backend_file)
|
|
416
|
+
|
|
417
|
+
# Extract bucket and key from backend config
|
|
418
|
+
bucket_match = content.match(/bucket\s*=\s*"([^"]+)"/)
|
|
419
|
+
key_match = content.match(/key\s*=\s*"([^"]+)"/)
|
|
420
|
+
|
|
421
|
+
return unless bucket_match && key_match
|
|
422
|
+
|
|
423
|
+
bucket = bucket_match[1]
|
|
424
|
+
key = key_match[1]
|
|
425
|
+
|
|
426
|
+
puts ' Purging terraform state from S3...'
|
|
427
|
+
|
|
428
|
+
# Delete the state file
|
|
429
|
+
result = `aws s3 rm "s3://#{bucket}/#{key}" 2>&1`
|
|
430
|
+
if Process.last_status.success?
|
|
431
|
+
puts " ✓ Deleted s3://#{bucket}/#{key}"
|
|
432
|
+
else
|
|
433
|
+
puts " ⚠ Could not delete state file: #{result.strip}" unless result.include?('delete: s3://')
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Also try to delete the state lock file if it exists (DynamoDB-based locking
|
|
437
|
+
# uses a separate mechanism, but some setups have .tflock files)
|
|
438
|
+
lock_key = key.sub(/\.tfstate$/, '.tfstate.lock')
|
|
439
|
+
`aws s3 rm "s3://#{bucket}/#{lock_key}" 2>/dev/null`
|
|
440
|
+
|
|
441
|
+
# Try to clean up the environment's directory in the bucket if empty
|
|
442
|
+
# (e.g., if the key is "myapp/fizzy123/terraform.tfstate", try to remove the "fizzy123" prefix)
|
|
443
|
+
env_prefix = key.sub(%r{/[^/]+$}, '')
|
|
444
|
+
return unless env_prefix != key
|
|
445
|
+
|
|
446
|
+
# List remaining objects under this prefix
|
|
447
|
+
remaining = `aws s3 ls "s3://#{bucket}/#{env_prefix}/" 2>/dev/null`
|
|
448
|
+
return unless Process.last_status.success? && remaining.strip.empty?
|
|
449
|
+
|
|
450
|
+
puts " ✓ State directory s3://#{bucket}/#{env_prefix}/ is now empty"
|
|
451
|
+
end
|
|
452
|
+
|
|
391
453
|
# Load infrastructure/<env>/belt.rb and apply its aws_profile + env vars to
|
|
392
454
|
# the current process so terraform (and any aws CLI calls during destroy)
|
|
393
455
|
# authenticate against the right account. Mirrors DeployCommand / TerraformCommand.
|
data/lib/belt/docs/generators.md
CHANGED
|
@@ -106,6 +106,34 @@ belt destroy frontend --frontend ops
|
|
|
106
106
|
belt destroy views post
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
### Non-interactive environment teardown
|
|
110
|
+
|
|
111
|
+
`belt destroy environment` is interactive by default: it prompts before running
|
|
112
|
+
`terraform destroy` and again before deleting local files. For CI pipelines and
|
|
113
|
+
agents that spin up ephemeral per-PR environments and tear them down on merge,
|
|
114
|
+
use `--full`:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
belt destroy environment pr-1234 --full
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`--full` (alias `-y`) runs the whole teardown with zero prompts: it **always runs
|
|
121
|
+
`terraform destroy`** (when terraform state is present), then deletes the local
|
|
122
|
+
`infrastructure/<env>/` files.
|
|
123
|
+
|
|
124
|
+
Flag comparison:
|
|
125
|
+
|
|
126
|
+
| Flag | terraform destroy | Deletes local files | Prompts |
|
|
127
|
+
|------|-------------------|---------------------|---------|
|
|
128
|
+
| _(none)_ | Prompted | Prompted | Yes |
|
|
129
|
+
| `--full` / `-y` | Always (if state exists) | Yes | No |
|
|
130
|
+
| `--force` / `-f` | **Skipped** (leaves cloud resources) | Yes | No |
|
|
131
|
+
| `--skip-terraform` | Never | Yes | Final delete only |
|
|
132
|
+
|
|
133
|
+
> `--force` is for the "just clean up my local files" case — it intentionally
|
|
134
|
+
> leaves cloud infrastructure running. Reach for `--full` when you want the
|
|
135
|
+
> environment fully gone.
|
|
136
|
+
|
|
109
137
|
## Plugin Generators
|
|
110
138
|
|
|
111
139
|
Gems that follow the Belt plugin contract are auto-discovered:
|
data/lib/belt/version.rb
CHANGED