@intentius/chant 0.41.20 → 0.42.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.
- package/dist/cli/commands/carve-apply.d.ts +9 -0
- package/dist/cli/commands/carve-apply.d.ts.map +1 -1
- package/dist/cli/commands/carve-bridge.d.ts +7 -0
- package/dist/cli/commands/carve-bridge.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts +7 -0
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/handlers/carve-apply.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/explain.d.ts +11 -0
- package/dist/cli/handlers/explain.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +2 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/docs-rule-scanning.d.ts +11 -0
- package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
- package/dist/codegen/okf-lexicon.d.ts +36 -0
- package/dist/codegen/okf-lexicon.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +2 -1
- package/dist/codegen/package.d.ts.map +1 -1
- package/dist/components/verbs/sbom.d.ts +1 -1
- package/dist/components/verbs/sbom.d.ts.map +1 -1
- package/dist/components/verbs/vuln-gate.d.ts +15 -4
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts +18 -6
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/deep-observation.d.ts +13 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/lexicon.d.ts +3 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/okf.d.ts +57 -0
- package/dist/okf.d.ts.map +1 -0
- package/dist/terraform/__fixtures__/build-graph.d.ts +13 -0
- package/dist/terraform/__fixtures__/build-graph.d.ts.map +1 -0
- package/dist/terraform/adopt-state.d.ts +33 -1
- package/dist/terraform/adopt-state.d.ts.map +1 -1
- package/dist/terraform/bridge.d.ts +7 -1
- package/dist/terraform/bridge.d.ts.map +1 -1
- package/dist/terraform/carve.d.ts +12 -0
- package/dist/terraform/carve.d.ts.map +1 -1
- package/dist/terraform/excise.d.ts +31 -0
- package/dist/terraform/excise.d.ts.map +1 -0
- package/dist/terraform/graduate.d.ts +12 -0
- package/dist/terraform/graduate.d.ts.map +1 -1
- package/dist/terraform/graph.d.ts +35 -5
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/manifest.d.ts +89 -0
- package/dist/terraform/manifest.d.ts.map +1 -0
- package/dist/terraform/parse.d.ts +9 -4
- package/dist/terraform/parse.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +7 -0
- package/dist/terraform/types.d.ts.map +1 -1
- package/dist/terraform/unified-diff.d.ts +11 -0
- package/dist/terraform/unified-diff.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/__snapshots__/okf.test.ts.snap +381 -0
- package/src/cli/commands/carve-apply.test.ts +94 -2
- package/src/cli/commands/carve-apply.ts +62 -11
- package/src/cli/commands/carve-bridge.test.ts +82 -4
- package/src/cli/commands/carve-bridge.ts +79 -11
- package/src/cli/commands/carve-emit-state.test.ts +110 -1
- package/src/cli/commands/carve-emit.ts +184 -8
- package/src/cli/commands/init.ts +1 -1
- package/src/cli/handlers/carve-apply.ts +1 -0
- package/src/cli/handlers/components.ts +4 -2
- package/src/cli/handlers/explain.test.ts +93 -0
- package/src/cli/handlers/explain.ts +60 -0
- package/src/cli/handlers/lifecycle.ts +9 -7
- package/src/cli/main.ts +21 -5
- package/src/cli/mcp/server.test.ts +17 -0
- package/src/cli/mcp/tools/explain.ts +15 -3
- package/src/cli/registry.ts +2 -0
- package/src/codegen/__snapshots__/okf-lexicon.test.ts.snap +170 -0
- package/src/codegen/docs-rule-scanning.ts +35 -14
- package/src/codegen/okf-lexicon.test.ts +248 -0
- package/src/codegen/okf-lexicon.ts +303 -0
- package/src/codegen/package.ts +29 -4
- package/src/components/config-defaults.test.ts +30 -0
- package/src/components/verbs/__fixtures__/grype-with-kev-epss.json +1 -0
- package/src/components/verbs/__fixtures__/trivy-with-kev-epss.json +631 -0
- package/src/components/verbs/exploitability-roundtrip.test.ts +190 -0
- package/src/components/verbs/sbom.ts +1 -1
- package/src/components/verbs/vuln-gate.test.ts +144 -1
- package/src/components/verbs/vuln-gate.ts +61 -10
- package/src/components/verbs/vuln-scan.test.ts +94 -0
- package/src/components/verbs/vuln-scan.ts +44 -9
- package/src/config.test.ts +21 -0
- package/src/config.ts +18 -0
- package/src/deep-observation.test.ts +19 -0
- package/src/deep-observation.ts +32 -13
- package/src/lexicon.ts +3 -0
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +10 -3
- package/src/lifecycle/snapshot.ts +5 -4
- package/src/okf.test.ts +169 -0
- package/src/okf.ts +308 -0
- package/src/terraform/__fixtures__/build-graph.ts +25 -0
- package/src/terraform/adopt-state.test.ts +34 -1
- package/src/terraform/adopt-state.ts +69 -2
- package/src/terraform/bridge.test.ts +38 -14
- package/src/terraform/bridge.ts +48 -14
- package/src/terraform/carve.test.ts +9 -7
- package/src/terraform/carve.ts +0 -0
- package/src/terraform/excise.test.ts +92 -0
- package/src/terraform/excise.ts +139 -0
- package/src/terraform/graduate.test.ts +73 -6
- package/src/terraform/graduate.ts +49 -0
- package/src/terraform/graph.test.ts +56 -10
- package/src/terraform/graph.ts +112 -45
- package/src/terraform/manifest.test.ts +105 -0
- package/src/terraform/manifest.ts +162 -0
- package/src/terraform/parse.test.ts +39 -1
- package/src/terraform/parse.ts +36 -9
- package/src/terraform/score.test.ts +8 -8
- package/src/terraform/state.test.ts +4 -4
- package/src/terraform/types.ts +7 -0
- package/src/terraform/unified-diff.test.ts +77 -0
- package/src/terraform/unified-diff.ts +146 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`buildOkfBundle over shipped examples > aws lambda-s3 emits a conformant, snapshot-stable bundle 1`] = `
|
|
4
|
+
[
|
|
5
|
+
{
|
|
6
|
+
"content": "---
|
|
7
|
+
type: AWS::S3::Bucket
|
|
8
|
+
title: appBucket
|
|
9
|
+
description: aws resource of type AWS::S3::Bucket
|
|
10
|
+
name: appBucket
|
|
11
|
+
lexicon: aws
|
|
12
|
+
kind: resource
|
|
13
|
+
source: main.ts
|
|
14
|
+
composite: LambdaS3
|
|
15
|
+
composite_instance: app
|
|
16
|
+
---
|
|
17
|
+
Declared in \`main.ts\`.
|
|
18
|
+
|
|
19
|
+
## Referenced by
|
|
20
|
+
|
|
21
|
+
- [appFunc](/aws/appFunc.md)
|
|
22
|
+
- [appRole](/aws/appRole.md)
|
|
23
|
+
",
|
|
24
|
+
"path": "aws/appBucket.md",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"content": "---
|
|
28
|
+
type: AWS::Lambda::Function
|
|
29
|
+
title: appFunc
|
|
30
|
+
description: aws resource of type AWS::Lambda::Function
|
|
31
|
+
name: appFunc
|
|
32
|
+
lexicon: aws
|
|
33
|
+
kind: resource
|
|
34
|
+
source: main.ts
|
|
35
|
+
composite: LambdaS3
|
|
36
|
+
composite_instance: app
|
|
37
|
+
---
|
|
38
|
+
Declared in \`main.ts\`.
|
|
39
|
+
|
|
40
|
+
## Depends on
|
|
41
|
+
|
|
42
|
+
- [appBucket](/aws/appBucket.md)
|
|
43
|
+
- [appRole](/aws/appRole.md)
|
|
44
|
+
",
|
|
45
|
+
"path": "aws/appFunc.md",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"content": "---
|
|
49
|
+
type: AWS::IAM::Role
|
|
50
|
+
title: appRole
|
|
51
|
+
description: aws resource of type AWS::IAM::Role
|
|
52
|
+
name: appRole
|
|
53
|
+
lexicon: aws
|
|
54
|
+
kind: resource
|
|
55
|
+
source: main.ts
|
|
56
|
+
composite: LambdaS3
|
|
57
|
+
composite_instance: app
|
|
58
|
+
---
|
|
59
|
+
Declared in \`main.ts\`.
|
|
60
|
+
|
|
61
|
+
## Depends on
|
|
62
|
+
|
|
63
|
+
- [appBucket](/aws/appBucket.md)
|
|
64
|
+
|
|
65
|
+
## Referenced by
|
|
66
|
+
|
|
67
|
+
- [appFunc](/aws/appFunc.md)
|
|
68
|
+
",
|
|
69
|
+
"path": "aws/appRole.md",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"content": "---
|
|
73
|
+
type: Output
|
|
74
|
+
title: bucketArn
|
|
75
|
+
description: aws output of type Output
|
|
76
|
+
name: bucketArn
|
|
77
|
+
lexicon: aws
|
|
78
|
+
kind: output
|
|
79
|
+
source: outputs.ts
|
|
80
|
+
---
|
|
81
|
+
Declared in \`outputs.ts\`.
|
|
82
|
+
",
|
|
83
|
+
"path": "aws/bucketArn.md",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"content": "---
|
|
87
|
+
type: AWS::CloudFormation::Parameter
|
|
88
|
+
title: environment
|
|
89
|
+
description: aws resource of type AWS::CloudFormation::Parameter
|
|
90
|
+
name: environment
|
|
91
|
+
lexicon: aws
|
|
92
|
+
kind: resource
|
|
93
|
+
source: params.ts
|
|
94
|
+
---
|
|
95
|
+
Declared in \`params.ts\`.
|
|
96
|
+
",
|
|
97
|
+
"path": "aws/environment.md",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"content": "---
|
|
101
|
+
type: chant:aws:defaultTags
|
|
102
|
+
title: tags
|
|
103
|
+
description: aws resource of type chant:aws:defaultTags
|
|
104
|
+
name: tags
|
|
105
|
+
lexicon: aws
|
|
106
|
+
kind: resource
|
|
107
|
+
source: tags.ts
|
|
108
|
+
---
|
|
109
|
+
Declared in \`tags.ts\`.
|
|
110
|
+
",
|
|
111
|
+
"path": "aws/tags.md",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"content": "---
|
|
115
|
+
okf_version: '0.2'
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
# Lexicon: aws
|
|
119
|
+
|
|
120
|
+
* [appBucket](/aws/appBucket.md) - aws resource of type AWS::S3::Bucket
|
|
121
|
+
* [appFunc](/aws/appFunc.md) - aws resource of type AWS::Lambda::Function
|
|
122
|
+
* [appRole](/aws/appRole.md) - aws resource of type AWS::IAM::Role
|
|
123
|
+
* [bucketArn](/aws/bucketArn.md) - aws output of type Output
|
|
124
|
+
* [environment](/aws/environment.md) - aws resource of type AWS::CloudFormation::Parameter
|
|
125
|
+
* [tags](/aws/tags.md) - aws resource of type chant:aws:defaultTags
|
|
126
|
+
",
|
|
127
|
+
"path": "index.md",
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
`;
|
|
131
|
+
|
|
132
|
+
exports[`buildOkfBundle over shipped examples > gitlab node-pipeline emits a conformant, snapshot-stable bundle 1`] = `
|
|
133
|
+
[
|
|
134
|
+
{
|
|
135
|
+
"content": "---
|
|
136
|
+
type: GitLab::CI::Job
|
|
137
|
+
title: appBuild
|
|
138
|
+
description: gitlab resource of type GitLab::CI::Job
|
|
139
|
+
name: appBuild
|
|
140
|
+
lexicon: gitlab
|
|
141
|
+
kind: resource
|
|
142
|
+
source: pipeline.ts
|
|
143
|
+
composite: NodePipeline
|
|
144
|
+
composite_instance: app
|
|
145
|
+
---
|
|
146
|
+
Declared in \`pipeline.ts\`.
|
|
147
|
+
",
|
|
148
|
+
"path": "gitlab/appBuild.md",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"content": "---
|
|
152
|
+
type: GitLab::CI::Default
|
|
153
|
+
title: appDefaults
|
|
154
|
+
description: gitlab resource of type GitLab::CI::Default
|
|
155
|
+
name: appDefaults
|
|
156
|
+
lexicon: gitlab
|
|
157
|
+
kind: resource
|
|
158
|
+
source: pipeline.ts
|
|
159
|
+
composite: NodePipeline
|
|
160
|
+
composite_instance: app
|
|
161
|
+
---
|
|
162
|
+
Declared in \`pipeline.ts\`.
|
|
163
|
+
",
|
|
164
|
+
"path": "gitlab/appDefaults.md",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"content": "---
|
|
168
|
+
type: GitLab::CI::Job
|
|
169
|
+
title: appTest
|
|
170
|
+
description: gitlab resource of type GitLab::CI::Job
|
|
171
|
+
name: appTest
|
|
172
|
+
lexicon: gitlab
|
|
173
|
+
kind: resource
|
|
174
|
+
source: pipeline.ts
|
|
175
|
+
composite: NodePipeline
|
|
176
|
+
composite_instance: app
|
|
177
|
+
---
|
|
178
|
+
Declared in \`pipeline.ts\`.
|
|
179
|
+
",
|
|
180
|
+
"path": "gitlab/appTest.md",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"content": "---
|
|
184
|
+
okf_version: '0.2'
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
# Lexicon: gitlab
|
|
188
|
+
|
|
189
|
+
* [appBuild](/gitlab/appBuild.md) - gitlab resource of type GitLab::CI::Job
|
|
190
|
+
* [appDefaults](/gitlab/appDefaults.md) - gitlab resource of type GitLab::CI::Default
|
|
191
|
+
* [appTest](/gitlab/appTest.md) - gitlab resource of type GitLab::CI::Job
|
|
192
|
+
",
|
|
193
|
+
"path": "index.md",
|
|
194
|
+
},
|
|
195
|
+
]
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
exports[`buildOkfBundle over shipped examples > k8s layered-config emits a conformant, snapshot-stable bundle 1`] = `
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
"content": "---
|
|
202
|
+
okf_version: '0.2'
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
# Lexicon: k8s
|
|
206
|
+
|
|
207
|
+
* [devAppDeployment](/k8s/devAppDeployment.md) - k8s resource of type K8s::Apps::Deployment
|
|
208
|
+
* [devAppService](/k8s/devAppService.md) - k8s resource of type K8s::Core::Service
|
|
209
|
+
* [prodAppDeployment](/k8s/prodAppDeployment.md) - k8s resource of type K8s::Apps::Deployment
|
|
210
|
+
* [prodAppIngress](/k8s/prodAppIngress.md) - k8s resource of type K8s::Networking::Ingress
|
|
211
|
+
* [prodAppPdb](/k8s/prodAppPdb.md) - k8s resource of type K8s::Policy::PodDisruptionBudget
|
|
212
|
+
* [prodAppService](/k8s/prodAppService.md) - k8s resource of type K8s::Core::Service
|
|
213
|
+
* [stagingAppDeployment](/k8s/stagingAppDeployment.md) - k8s resource of type K8s::Apps::Deployment
|
|
214
|
+
* [stagingAppIngress](/k8s/stagingAppIngress.md) - k8s resource of type K8s::Networking::Ingress
|
|
215
|
+
* [stagingAppPdb](/k8s/stagingAppPdb.md) - k8s resource of type K8s::Policy::PodDisruptionBudget
|
|
216
|
+
* [stagingAppService](/k8s/stagingAppService.md) - k8s resource of type K8s::Core::Service
|
|
217
|
+
",
|
|
218
|
+
"path": "index.md",
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"content": "---
|
|
222
|
+
type: K8s::Apps::Deployment
|
|
223
|
+
title: devAppDeployment
|
|
224
|
+
description: k8s resource of type K8s::Apps::Deployment
|
|
225
|
+
name: devAppDeployment
|
|
226
|
+
lexicon: k8s
|
|
227
|
+
kind: resource
|
|
228
|
+
source: web.ts
|
|
229
|
+
composite: WebApp
|
|
230
|
+
composite_instance: devApp
|
|
231
|
+
---
|
|
232
|
+
Declared in \`web.ts\`.
|
|
233
|
+
",
|
|
234
|
+
"path": "k8s/devAppDeployment.md",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"content": "---
|
|
238
|
+
type: K8s::Core::Service
|
|
239
|
+
title: devAppService
|
|
240
|
+
description: k8s resource of type K8s::Core::Service
|
|
241
|
+
name: devAppService
|
|
242
|
+
lexicon: k8s
|
|
243
|
+
kind: resource
|
|
244
|
+
source: web.ts
|
|
245
|
+
composite: WebApp
|
|
246
|
+
composite_instance: devApp
|
|
247
|
+
---
|
|
248
|
+
Declared in \`web.ts\`.
|
|
249
|
+
",
|
|
250
|
+
"path": "k8s/devAppService.md",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"content": "---
|
|
254
|
+
type: K8s::Apps::Deployment
|
|
255
|
+
title: prodAppDeployment
|
|
256
|
+
description: k8s resource of type K8s::Apps::Deployment
|
|
257
|
+
name: prodAppDeployment
|
|
258
|
+
lexicon: k8s
|
|
259
|
+
kind: resource
|
|
260
|
+
source: web.ts
|
|
261
|
+
composite: WebApp
|
|
262
|
+
composite_instance: prodApp
|
|
263
|
+
---
|
|
264
|
+
Declared in \`web.ts\`.
|
|
265
|
+
",
|
|
266
|
+
"path": "k8s/prodAppDeployment.md",
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"content": "---
|
|
270
|
+
type: K8s::Networking::Ingress
|
|
271
|
+
title: prodAppIngress
|
|
272
|
+
description: k8s resource of type K8s::Networking::Ingress
|
|
273
|
+
name: prodAppIngress
|
|
274
|
+
lexicon: k8s
|
|
275
|
+
kind: resource
|
|
276
|
+
source: web.ts
|
|
277
|
+
composite: WebApp
|
|
278
|
+
composite_instance: prodApp
|
|
279
|
+
---
|
|
280
|
+
Declared in \`web.ts\`.
|
|
281
|
+
",
|
|
282
|
+
"path": "k8s/prodAppIngress.md",
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"content": "---
|
|
286
|
+
type: K8s::Policy::PodDisruptionBudget
|
|
287
|
+
title: prodAppPdb
|
|
288
|
+
description: k8s resource of type K8s::Policy::PodDisruptionBudget
|
|
289
|
+
name: prodAppPdb
|
|
290
|
+
lexicon: k8s
|
|
291
|
+
kind: resource
|
|
292
|
+
source: web.ts
|
|
293
|
+
composite: WebApp
|
|
294
|
+
composite_instance: prodApp
|
|
295
|
+
---
|
|
296
|
+
Declared in \`web.ts\`.
|
|
297
|
+
",
|
|
298
|
+
"path": "k8s/prodAppPdb.md",
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"content": "---
|
|
302
|
+
type: K8s::Core::Service
|
|
303
|
+
title: prodAppService
|
|
304
|
+
description: k8s resource of type K8s::Core::Service
|
|
305
|
+
name: prodAppService
|
|
306
|
+
lexicon: k8s
|
|
307
|
+
kind: resource
|
|
308
|
+
source: web.ts
|
|
309
|
+
composite: WebApp
|
|
310
|
+
composite_instance: prodApp
|
|
311
|
+
---
|
|
312
|
+
Declared in \`web.ts\`.
|
|
313
|
+
",
|
|
314
|
+
"path": "k8s/prodAppService.md",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"content": "---
|
|
318
|
+
type: K8s::Apps::Deployment
|
|
319
|
+
title: stagingAppDeployment
|
|
320
|
+
description: k8s resource of type K8s::Apps::Deployment
|
|
321
|
+
name: stagingAppDeployment
|
|
322
|
+
lexicon: k8s
|
|
323
|
+
kind: resource
|
|
324
|
+
source: web.ts
|
|
325
|
+
composite: WebApp
|
|
326
|
+
composite_instance: stagingApp
|
|
327
|
+
---
|
|
328
|
+
Declared in \`web.ts\`.
|
|
329
|
+
",
|
|
330
|
+
"path": "k8s/stagingAppDeployment.md",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"content": "---
|
|
334
|
+
type: K8s::Networking::Ingress
|
|
335
|
+
title: stagingAppIngress
|
|
336
|
+
description: k8s resource of type K8s::Networking::Ingress
|
|
337
|
+
name: stagingAppIngress
|
|
338
|
+
lexicon: k8s
|
|
339
|
+
kind: resource
|
|
340
|
+
source: web.ts
|
|
341
|
+
composite: WebApp
|
|
342
|
+
composite_instance: stagingApp
|
|
343
|
+
---
|
|
344
|
+
Declared in \`web.ts\`.
|
|
345
|
+
",
|
|
346
|
+
"path": "k8s/stagingAppIngress.md",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"content": "---
|
|
350
|
+
type: K8s::Policy::PodDisruptionBudget
|
|
351
|
+
title: stagingAppPdb
|
|
352
|
+
description: k8s resource of type K8s::Policy::PodDisruptionBudget
|
|
353
|
+
name: stagingAppPdb
|
|
354
|
+
lexicon: k8s
|
|
355
|
+
kind: resource
|
|
356
|
+
source: web.ts
|
|
357
|
+
composite: WebApp
|
|
358
|
+
composite_instance: stagingApp
|
|
359
|
+
---
|
|
360
|
+
Declared in \`web.ts\`.
|
|
361
|
+
",
|
|
362
|
+
"path": "k8s/stagingAppPdb.md",
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"content": "---
|
|
366
|
+
type: K8s::Core::Service
|
|
367
|
+
title: stagingAppService
|
|
368
|
+
description: k8s resource of type K8s::Core::Service
|
|
369
|
+
name: stagingAppService
|
|
370
|
+
lexicon: k8s
|
|
371
|
+
kind: resource
|
|
372
|
+
source: web.ts
|
|
373
|
+
composite: WebApp
|
|
374
|
+
composite_instance: stagingApp
|
|
375
|
+
---
|
|
376
|
+
Declared in \`web.ts\`.
|
|
377
|
+
",
|
|
378
|
+
"path": "k8s/stagingAppService.md",
|
|
379
|
+
},
|
|
380
|
+
]
|
|
381
|
+
`;
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from "os";
|
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { carveApply, formatCarveApply } from "./carve-apply";
|
|
6
6
|
import { loadHcl2json } from "../../terraform/parse";
|
|
7
|
+
import { readCarveManifest, writeCarveManifest, type CarveManifest } from "../../terraform/manifest";
|
|
7
8
|
|
|
8
9
|
let parserAvailable = false;
|
|
9
10
|
try {
|
|
@@ -30,10 +31,101 @@ async function withEstate<T>(fn: (dir: string) => Promise<T>): Promise<T> {
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
function manifestFor(target: string, dir: string): CarveManifest {
|
|
35
|
+
return {
|
|
36
|
+
version: 1,
|
|
37
|
+
target,
|
|
38
|
+
from: dir,
|
|
39
|
+
boundary: {
|
|
40
|
+
target,
|
|
41
|
+
carveSet: [{ address: target, type: target.split(".")[0] }],
|
|
42
|
+
peelability: 90,
|
|
43
|
+
inbound: [],
|
|
44
|
+
outbound: [],
|
|
45
|
+
reversible: true,
|
|
46
|
+
diagnostics: [],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
33
51
|
describe("carveApply", () => {
|
|
34
|
-
test("requires --from
|
|
52
|
+
test("requires --from; without --select it needs a carve manifest", async () => {
|
|
35
53
|
expect((await carveApply({})).error).toContain("--from");
|
|
36
|
-
|
|
54
|
+
await withEstate(async (dir) => {
|
|
55
|
+
const res = await carveApply({ from: dir });
|
|
56
|
+
expect(res.ok).toBe(false);
|
|
57
|
+
expect(res.error).toContain("--select");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("composes with the carve manifest: target resolved without --select, graduation recorded", async () => {
|
|
62
|
+
if (!parserAvailable) return;
|
|
63
|
+
await withEstate(async (dir) => {
|
|
64
|
+
const out = join(dir, "carveout");
|
|
65
|
+
writeCarveManifest(out, manifestFor("aws_s3_bucket.assets", dir));
|
|
66
|
+
|
|
67
|
+
const res = await carveApply({ from: dir, output: out, env: "prod", stack: "assets" });
|
|
68
|
+
expect(res.ok).toBe(true);
|
|
69
|
+
expect(res.selectFromManifest).toBe(true);
|
|
70
|
+
expect(formatCarveApply(res)).toContain("target from the carve manifest");
|
|
71
|
+
|
|
72
|
+
const m = readCarveManifest(res.manifestPath!)!;
|
|
73
|
+
expect(m.apply!.marker).toEqual({ stack: "assets", env: "prod" });
|
|
74
|
+
expect(m.apply!.ownershipTags["chant:managed-by"]).toBe("chant");
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("--write-source stamps the ownership marker into the emitted source and records it", async () => {
|
|
79
|
+
if (!parserAvailable) return;
|
|
80
|
+
await withEstate(async (dir) => {
|
|
81
|
+
const out = join(dir, "carveout");
|
|
82
|
+
const emitted = join(out, "assets.ts");
|
|
83
|
+
const manifest = manifestFor("aws_s3_bucket.assets", dir);
|
|
84
|
+
manifest.emit = { source: "tfstate", files: [emitted], at: "t" };
|
|
85
|
+
writeCarveManifest(out, manifest);
|
|
86
|
+
writeFileSync(emitted, 'export const assets = new Bucket({\n BucketName: "myapp-assets-prod",\n});\n');
|
|
87
|
+
|
|
88
|
+
const res = await carveApply({ from: dir, output: out, env: "prod", stack: "assets", writeSource: true });
|
|
89
|
+
expect(res.ok).toBe(true);
|
|
90
|
+
expect(res.stamped).toEqual([emitted]);
|
|
91
|
+
|
|
92
|
+
const stamped = readFileSync(emitted, "utf-8");
|
|
93
|
+
expect(stamped).toContain('{"Key":"chant:managed-by","Value":"chant"}');
|
|
94
|
+
expect(stamped).toContain('{"Key":"chant:stack","Value":"assets"}');
|
|
95
|
+
expect(stamped).toContain('{"Key":"chant:env","Value":"prod"}');
|
|
96
|
+
|
|
97
|
+
const m = readCarveManifest(res.manifestPath!)!;
|
|
98
|
+
expect(m.apply!.stampedFiles).toEqual([emitted]);
|
|
99
|
+
expect(formatCarveApply(res)).toContain("Stamped the ownership marker into");
|
|
100
|
+
|
|
101
|
+
// Idempotent: a second graduation leaves the source stable.
|
|
102
|
+
const again = await carveApply({ from: dir, output: out, env: "prod", stack: "assets", writeSource: true });
|
|
103
|
+
expect(again.ok).toBe(true);
|
|
104
|
+
expect(readFileSync(emitted, "utf-8")).toBe(stamped);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("--write-source without an emit record in the manifest is a clear error", async () => {
|
|
109
|
+
if (!parserAvailable) return;
|
|
110
|
+
await withEstate(async (dir) => {
|
|
111
|
+
const out = join(dir, "carveout");
|
|
112
|
+
writeCarveManifest(out, manifestFor("aws_s3_bucket.assets", dir));
|
|
113
|
+
const res = await carveApply({ from: dir, output: out, env: "prod", writeSource: true });
|
|
114
|
+
expect(res.ok).toBe(false);
|
|
115
|
+
expect(res.error).toContain("carve emit");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("falls back to the manifest's persisted boundary when the block is already excised", async () => {
|
|
120
|
+
if (!parserAvailable) return;
|
|
121
|
+
await withEstate(async (dir) => {
|
|
122
|
+
const out = join(dir, "carveout");
|
|
123
|
+
writeCarveManifest(out, manifestFor("aws_s3_bucket.gone", dir));
|
|
124
|
+
|
|
125
|
+
const res = await carveApply({ from: dir, output: out, env: "prod" });
|
|
126
|
+
expect(res.ok).toBe(true);
|
|
127
|
+
expect(res.plan!.target).toBe("aws_s3_bucket.gone");
|
|
128
|
+
});
|
|
37
129
|
});
|
|
38
130
|
|
|
39
131
|
test("plans graduation with ownership marker; writes nothing by default", async () => {
|
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
* lifecycle you brought.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { existsSync, statSync, mkdirSync, writeFileSync } from "fs";
|
|
12
|
+
import { existsSync, statSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
13
13
|
import { join } from "path";
|
|
14
14
|
import { parseTerraformDir, Hcl2JsonNotInstalled } from "../../terraform/parse";
|
|
15
15
|
import { boundaryReport } from "../../terraform/carve";
|
|
16
|
-
import { graduationPlan, type GraduationPlan } from "../../terraform/graduate";
|
|
16
|
+
import { graduationPlan, stampOwnershipIntoSource, type GraduationPlan } from "../../terraform/graduate";
|
|
17
|
+
import { resolveCarveManifest, updateCarveManifest } from "../../terraform/manifest";
|
|
17
18
|
|
|
18
19
|
export interface CarveApplyOptions {
|
|
19
20
|
from?: string;
|
|
21
|
+
/** Optional when the output dir holds a single carve manifest from `carve emit`. */
|
|
20
22
|
select?: string;
|
|
21
23
|
env?: string;
|
|
22
24
|
stack?: string;
|
|
@@ -24,6 +26,8 @@ export interface CarveApplyOptions {
|
|
|
24
26
|
output?: string;
|
|
25
27
|
/** Write the graduation doc to the output dir. */
|
|
26
28
|
write?: boolean;
|
|
29
|
+
/** Stamp the ownership marker into the emitted chant source. */
|
|
30
|
+
writeSource?: boolean;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export interface CarveApplyResult {
|
|
@@ -31,36 +35,77 @@ export interface CarveApplyResult {
|
|
|
31
35
|
error?: string;
|
|
32
36
|
plan?: GraduationPlan;
|
|
33
37
|
written?: string;
|
|
38
|
+
/** Emitted source files the ownership marker was stamped into. */
|
|
39
|
+
stamped?: string[];
|
|
40
|
+
/** The carve state manifest this graduation was recorded into. */
|
|
41
|
+
manifestPath?: string;
|
|
42
|
+
/** True when the target came from the manifest, not --select. */
|
|
43
|
+
selectFromManifest?: boolean;
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
export async function carveApply(opts: CarveApplyOptions): Promise<CarveApplyResult> {
|
|
37
47
|
if (!opts.from) return { ok: false, error: "chant carve apply requires --from <terraform-dir>" };
|
|
38
|
-
if (!opts.select) return { ok: false, error: "chant carve apply requires --select <tf-address>" };
|
|
39
48
|
if (!existsSync(opts.from) || !statSync(opts.from).isDirectory()) {
|
|
40
49
|
return { ok: false, error: `Not a directory: ${opts.from}` };
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
// Compose with the manifest emit/bridge persisted: it supplies the target
|
|
53
|
+
// (and tfstate) when --select is absent, and its persisted boundary stands in
|
|
54
|
+
// when the carved block is already excised from the estate.
|
|
55
|
+
const outDir = opts.output ?? join(opts.from, "carveout");
|
|
56
|
+
const resolved = resolveCarveManifest(outDir, opts.select);
|
|
57
|
+
if (!opts.select && resolved.error) return { ok: false, error: resolved.error };
|
|
58
|
+
const select = opts.select ?? resolved.manifest!.target;
|
|
59
|
+
const statePath = opts.statePath ?? resolved.manifest?.statePath;
|
|
60
|
+
|
|
43
61
|
let plan: GraduationPlan;
|
|
44
62
|
try {
|
|
45
|
-
const graph = await parseTerraformDir(opts.from, { statePath
|
|
46
|
-
const report = boundaryReport(graph,
|
|
47
|
-
if (!report) return { ok: false, error: `${
|
|
63
|
+
const graph = await parseTerraformDir(opts.from, { statePath });
|
|
64
|
+
const report = boundaryReport(graph, select) ?? resolved.manifest?.boundary ?? null;
|
|
65
|
+
if (!report) return { ok: false, error: `${select} not found in ${opts.from}` };
|
|
48
66
|
plan = graduationPlan(report, { stack: opts.stack, env: opts.env });
|
|
49
67
|
} catch (err) {
|
|
50
68
|
if (err instanceof Hcl2JsonNotInstalled) return { ok: false, error: err.message };
|
|
51
69
|
return { ok: false, error: `Failed to plan graduation: ${err instanceof Error ? err.message : String(err)}` };
|
|
52
70
|
}
|
|
53
71
|
|
|
72
|
+
// Stamp the marker into the emitted source (`--write-source`) — the emitted
|
|
73
|
+
// files are known from the manifest, so the tags the plan promises are the
|
|
74
|
+
// tags the source actually carries into the apply.
|
|
75
|
+
let stamped: string[] | undefined;
|
|
76
|
+
if (opts.writeSource) {
|
|
77
|
+
const files = resolved.manifest?.emit?.files ?? [];
|
|
78
|
+
if (files.length === 0) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
error: "--write-source needs the emitted source recorded in the carve manifest — run `chant carve emit` first.",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
stamped = [];
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
if (!existsSync(file)) return { ok: false, error: `Emitted source not found: ${file}` };
|
|
87
|
+
const result = stampOwnershipIntoSource(readFileSync(file, "utf-8"), plan.ownershipTags);
|
|
88
|
+
if (!result) return { ok: false, error: `No constructor to stamp in ${file}.` };
|
|
89
|
+
if (result.changed) writeFileSync(file, result.content);
|
|
90
|
+
stamped.push(file);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
54
94
|
let written: string | undefined;
|
|
55
95
|
if (opts.write) {
|
|
56
|
-
const outDir = opts.output ?? join(opts.from, "carveout");
|
|
57
96
|
mkdirSync(outDir, { recursive: true });
|
|
58
|
-
const slug =
|
|
97
|
+
const slug = select.replace(/[^A-Za-z0-9_]+/g, "-");
|
|
59
98
|
written = join(outDir, `${slug}-graduation.md`);
|
|
60
99
|
writeFileSync(written, renderGraduationDoc(plan) + "\n");
|
|
61
100
|
}
|
|
62
101
|
|
|
63
|
-
|
|
102
|
+
// Record the graduation into an existing manifest; never create one here —
|
|
103
|
+
// plan-only stays plan-only unless a carve flow is already in progress.
|
|
104
|
+
const manifestPath = updateCarveManifest(outDir, select, {
|
|
105
|
+
apply: { marker: plan.marker, ownershipTags: plan.ownershipTags, stampedFiles: stamped, at: new Date().toISOString() },
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return { ok: true, plan, written, stamped, manifestPath, selectFromManifest: !opts.select };
|
|
64
109
|
}
|
|
65
110
|
|
|
66
111
|
function renderGraduationDoc(plan: GraduationPlan): string {
|
|
@@ -86,7 +131,7 @@ export function formatCarveApply(result: CarveApplyResult): string {
|
|
|
86
131
|
if (!result.ok || !result.plan) return result.error ?? "carve apply failed";
|
|
87
132
|
const p = result.plan;
|
|
88
133
|
const L: string[] = [];
|
|
89
|
-
L.push(`Apply graduation for ${p.target} (dial-turn: observe → apply).`);
|
|
134
|
+
L.push(`Apply graduation for ${p.target} (dial-turn: observe → apply)${result.selectFromManifest ? " — target from the carve manifest" : ""}.`);
|
|
90
135
|
L.push(` Ownership marker: stack=${p.marker.stack}${p.marker.env ? `, env=${p.marker.env}` : ""}`);
|
|
91
136
|
L.push(" Ownership tags (stamped on apply):");
|
|
92
137
|
for (const [k, v] of Object.entries(p.ownershipTags)) L.push(` ${k} = ${v}`);
|
|
@@ -97,10 +142,16 @@ export function formatCarveApply(result: CarveApplyResult): string {
|
|
|
97
142
|
for (const w of p.warnings) L.push(` ! ${w}`);
|
|
98
143
|
}
|
|
99
144
|
L.push("");
|
|
145
|
+
if (result.stamped?.length) {
|
|
146
|
+
L.push(`Stamped the ownership marker into: ${result.stamped.join(", ")}`);
|
|
147
|
+
}
|
|
100
148
|
if (result.written) {
|
|
101
149
|
L.push(`Wrote graduation doc: ${result.written}`);
|
|
102
|
-
} else {
|
|
150
|
+
} else if (!result.stamped?.length) {
|
|
103
151
|
L.push("Plan only — no cloud call, nothing written. The apply is your lifecycle (BYOL). Add --write to save this as a doc.");
|
|
104
152
|
}
|
|
153
|
+
if (result.manifestPath) {
|
|
154
|
+
L.push(`Recorded in the carve manifest: ${result.manifestPath}`);
|
|
155
|
+
}
|
|
105
156
|
return L.join("\n");
|
|
106
157
|
}
|