@intentius/chant-lexicon-aws 0.46.0 → 0.50.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.
- package/dist/api/read-client.d.ts +10 -2
- package/dist/api/read-client.d.ts.map +1 -1
- package/dist/codegen/generate-lexicon.d.ts +5 -0
- package/dist/codegen/generate-lexicon.d.ts.map +1 -1
- package/dist/components/host-delivery.d.ts +4 -3
- package/dist/components/host-delivery.d.ts.map +1 -1
- package/dist/composites/agentcore-agent.d.ts +26 -1
- package/dist/composites/agentcore-agent.d.ts.map +1 -1
- package/dist/composites/bucket-deployment.d.ts +82 -0
- package/dist/composites/bucket-deployment.d.ts.map +1 -0
- package/dist/composites/dynamodb-table.d.ts +45 -0
- package/dist/composites/dynamodb-table.d.ts.map +1 -0
- package/dist/composites/ec2-instance-bundle.d.ts +61 -0
- package/dist/composites/ec2-instance-bundle.d.ts.map +1 -0
- package/dist/composites/ecr-repository.d.ts +49 -0
- package/dist/composites/ecr-repository.d.ts.map +1 -0
- package/dist/composites/fargate-service.d.ts +16 -0
- package/dist/composites/fargate-service.d.ts.map +1 -1
- package/dist/composites/index.d.ts +16 -2
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/monitoring-stack.d.ts +99 -0
- package/dist/composites/monitoring-stack.d.ts.map +1 -0
- package/dist/composites/nlb-service.d.ts +48 -0
- package/dist/composites/nlb-service.d.ts.map +1 -0
- package/dist/composites/step-functions-workflow.d.ts +36 -0
- package/dist/composites/step-functions-workflow.d.ts.map +1 -0
- package/dist/deep-observe.d.ts +45 -0
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/deep-topology.d.ts +104 -0
- package/dist/deep-topology.d.ts.map +1 -0
- package/dist/effect-receipt-row.d.ts +92 -0
- package/dist/effect-receipt-row.d.ts.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +14 -6
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/cf-refs.d.ts +17 -0
- package/dist/lint/post-synth/cf-refs.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/waw016.d.ts +10 -2
- package/dist/lint/post-synth/waw016.d.ts.map +1 -1
- package/dist/lint/post-synth/waw061.d.ts +19 -0
- package/dist/lint/post-synth/waw061.d.ts.map +1 -0
- package/dist/lint/post-synth/waw062.d.ts +19 -0
- package/dist/lint/post-synth/waw062.d.ts.map +1 -0
- package/dist/lint/post-synth/waw063.d.ts +35 -0
- package/dist/lint/post-synth/waw063.d.ts.map +1 -0
- package/dist/lint/post-synth/waw064.d.ts +17 -0
- package/dist/lint/post-synth/waw064.d.ts.map +1 -0
- package/dist/lint/post-synth/waw065.d.ts +23 -0
- package/dist/lint/post-synth/waw065.d.ts.map +1 -0
- package/dist/lint/post-synth/waw066.d.ts +25 -0
- package/dist/lint/post-synth/waw066.d.ts.map +1 -0
- package/dist/lint/post-synth/waw067.d.ts +24 -0
- package/dist/lint/post-synth/waw067.d.ts.map +1 -0
- package/dist/lint/post-synth/waw068.d.ts +20 -0
- package/dist/lint/post-synth/waw068.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/meta.json +34 -0
- package/dist/okf/index.md +8 -0
- package/dist/okf/rules/WAW061.md +17 -0
- package/dist/okf/rules/WAW062.md +27 -0
- package/dist/okf/rules/WAW063.md +19 -0
- package/dist/okf/rules/WAW064.md +18 -0
- package/dist/okf/rules/WAW065.md +25 -0
- package/dist/okf/rules/WAW066.md +22 -0
- package/dist/okf/rules/WAW067.md +20 -0
- package/dist/okf/rules/WAW068.md +19 -0
- package/dist/okf/types/Action.md +1 -0
- package/dist/okf/types/AutoScalingGroup.md +1 -0
- package/dist/okf/types/Bucket.md +1 -0
- package/dist/okf/types/EC2Route.md +2 -0
- package/dist/okf/types/ECRRepository.md +1 -0
- package/dist/okf/types/Function.md +1 -0
- package/dist/okf/types/IamPolicy.md +1 -0
- package/dist/okf/types/InternetGateway.md +4 -0
- package/dist/okf/types/LaunchTemplate.md +1 -0
- package/dist/okf/types/LoadBalancer.md +1 -0
- package/dist/okf/types/ManagedPolicy.md +2 -0
- package/dist/okf/types/Map.md +5 -0
- package/dist/okf/types/NatGateway.md +4 -0
- package/dist/okf/types/Role.md +2 -0
- package/dist/okf/types/RouteTable.md +4 -0
- package/dist/okf/types/SecurityGroup.md +1 -0
- package/dist/okf/types/StateMachine.md +4 -0
- package/dist/okf/types/Subnet.md +6 -0
- package/dist/okf/types/SubnetRouteTableAssociation.md +5 -0
- package/dist/okf/types/Table.md +4 -0
- package/dist/okf/types/TransitGateway.md +6 -0
- package/dist/okf/types/TransitGatewayAttachment.md +4 -0
- package/dist/okf/types/TransitGatewayPeeringAttachment.md +4 -0
- package/dist/okf/types/TransitGatewayRoute.md +5 -0
- package/dist/okf/types/TransitGatewayRouteTable.md +4 -0
- package/dist/okf/types/TransitGatewayRouteTableAssociation.md +4 -0
- package/dist/okf/types/TransitGatewayRouteTablePropagation.md +4 -0
- package/dist/okf/types/TransitGatewayVpcAttachment.md +4 -0
- package/dist/okf/types/Type.md +8 -0
- package/dist/okf/types/VPNConnection.md +4 -0
- package/dist/okf/types/VPNGateway.md +4 -0
- package/dist/okf/types/Vpc.md +1 -0
- package/dist/op/activities/index.d.ts +6 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/builders.d.ts +47 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/receipt-store.d.ts +109 -0
- package/dist/receipt-store.d.ts.map +1 -0
- package/dist/rules/cf-refs.ts +40 -0
- package/dist/rules/waw016.ts +37 -16
- package/dist/rules/waw061.ts +67 -0
- package/dist/rules/waw062.ts +0 -0
- package/dist/rules/waw063.ts +0 -0
- package/dist/rules/waw064.ts +52 -0
- package/dist/rules/waw065.ts +110 -0
- package/dist/rules/waw066.ts +155 -0
- package/dist/rules/waw067.ts +114 -0
- package/dist/rules/waw068.ts +78 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/skills/chant-aws-carve-terraform.md +26 -23
- package/dist/spec/parse.d.ts +7 -0
- package/dist/spec/parse.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/api/read-client.test.ts +9 -0
- package/src/api/read-client.ts +15 -2
- package/src/codegen/fallback.ts +1 -0
- package/src/codegen/generate-lexicon.ts +6 -0
- package/src/codegen/sam.ts +9 -0
- package/src/components/host-delivery.ts +4 -3
- package/src/composites/agentcore-agent.ts +35 -13
- package/src/composites/bucket-deployment.test.ts +84 -0
- package/src/composites/bucket-deployment.ts +153 -0
- package/src/composites/composites.test.ts +325 -0
- package/src/composites/dynamodb-table.test.ts +154 -0
- package/src/composites/dynamodb-table.ts +124 -0
- package/src/composites/ec2-instance-bundle.ts +134 -0
- package/src/composites/ecr-repository.test.ts +118 -0
- package/src/composites/ecr-repository.ts +117 -0
- package/src/composites/fargate-service.ts +19 -3
- package/src/composites/index.ts +21 -2
- package/src/composites/monitoring-stack.test.ts +130 -0
- package/src/composites/monitoring-stack.ts +164 -0
- package/src/composites/nlb-service.ts +117 -0
- package/src/composites/step-functions-workflow.ts +127 -0
- package/src/deep-observe.test.ts +201 -1
- package/src/deep-observe.ts +204 -0
- package/src/deep-topology.test.ts +392 -0
- package/src/deep-topology.ts +237 -0
- package/src/effect-receipt-row.test.ts +164 -0
- package/src/effect-receipt-row.ts +147 -0
- package/src/generated/lexicon-aws.json +34 -0
- package/src/index.ts +36 -7
- package/src/lint/audit-catalog.ts +16 -0
- package/src/lint/post-synth/cf-refs.ts +40 -0
- package/src/lint/post-synth/index.ts +16 -0
- package/src/lint/post-synth/waw016.test.ts +61 -4
- package/src/lint/post-synth/waw016.ts +37 -16
- package/src/lint/post-synth/waw061.test.ts +99 -0
- package/src/lint/post-synth/waw061.ts +67 -0
- package/src/lint/post-synth/waw062.test.ts +109 -0
- package/src/lint/post-synth/waw062.ts +0 -0
- package/src/lint/post-synth/waw063.test.ts +220 -0
- package/src/lint/post-synth/waw063.ts +0 -0
- package/src/lint/post-synth/waw064.test.ts +79 -0
- package/src/lint/post-synth/waw064.ts +52 -0
- package/src/lint/post-synth/waw065.test.ts +90 -0
- package/src/lint/post-synth/waw065.ts +110 -0
- package/src/lint/post-synth/waw066.test.ts +108 -0
- package/src/lint/post-synth/waw066.ts +155 -0
- package/src/lint/post-synth/waw067.test.ts +79 -0
- package/src/lint/post-synth/waw067.ts +114 -0
- package/src/lint/post-synth/waw068.test.ts +74 -0
- package/src/lint/post-synth/waw068.ts +78 -0
- package/src/lsp/hover.ts +17 -2
- package/src/op/activities/index.ts +15 -0
- package/src/op/builders.test.ts +61 -0
- package/src/op/builders.ts +72 -0
- package/src/plugin.ts +41 -10
- package/src/receipt-store.test.ts +204 -0
- package/src/receipt-store.ts +315 -0
- package/src/serializer.ts +105 -2
- package/src/skills/chant-aws-carve-terraform.md +26 -23
- package/src/spec/parse.test.ts +74 -0
- package/src/spec/parse.ts +26 -6
package/src/serializer.ts
CHANGED
|
@@ -2,7 +2,19 @@ import type { Declarable, CoreParameter } from "@intentius/chant/declarable";
|
|
|
2
2
|
import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
3
3
|
import type { Serializer, SerializerResult, SerializeContext } from "@intentius/chant/serializer";
|
|
4
4
|
import { ownershipEntries, type OwnershipMarker } from "@intentius/chant/ownership";
|
|
5
|
+
import {
|
|
6
|
+
isEffectReceipt,
|
|
7
|
+
receiptExpectation,
|
|
8
|
+
referenceInputPaths,
|
|
9
|
+
type EffectReceiptDeclaration,
|
|
10
|
+
} from "@intentius/chant/effect-receipt";
|
|
5
11
|
import { AWS_TAG_OWNERSHIP_KEYS, OWNERSHIP_METADATA_KEY } from "./ownership";
|
|
12
|
+
import {
|
|
13
|
+
AWS_EFFECT_RECEIPT_ENTITY_TYPE,
|
|
14
|
+
EFFECT_RECEIPTS_METADATA_KEY,
|
|
15
|
+
RECEIPT_UNRESOLVED_VALUE_NOTE,
|
|
16
|
+
receiptParameterName,
|
|
17
|
+
} from "./effect-receipt-row";
|
|
6
18
|
import type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
7
19
|
import { walkValue, type SerializerVisitor } from "@intentius/chant/serializer-walker";
|
|
8
20
|
import { isChildProject, type ChildProjectInstance } from "@intentius/chant/child-project";
|
|
@@ -154,6 +166,80 @@ function toProperties(
|
|
|
154
166
|
}
|
|
155
167
|
|
|
156
168
|
|
|
169
|
+
/** One rendered receipt row — a CFN-resource-shaped object that lives in the
|
|
170
|
+
* template's `Metadata`, never in `Resources` (#1832: the applier writes from
|
|
171
|
+
* `Resources`, and the `effect()` step is a receipt's sole writer). */
|
|
172
|
+
interface ReceiptRow {
|
|
173
|
+
Type: typeof AWS_EFFECT_RECEIPT_ENTITY_TYPE;
|
|
174
|
+
Properties: {
|
|
175
|
+
Name: string;
|
|
176
|
+
Type: "String";
|
|
177
|
+
Value: string;
|
|
178
|
+
Tags: Array<{ Key: string; Value: string }>;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The rendered `Value`: the synthesis-time expectation when the receipt is
|
|
183
|
+
* fully static, a placeholder note when reference inputs resolve later —
|
|
184
|
+
* never a digest hashed over placeholders (epic #1703, decision 5). */
|
|
185
|
+
function receiptRowValue(receipt: EffectReceiptDeclaration): string {
|
|
186
|
+
if (receipt.flavor === "hash" && referenceInputPaths(receipt).length > 0) {
|
|
187
|
+
return RECEIPT_UNRESOLVED_VALUE_NOTE;
|
|
188
|
+
}
|
|
189
|
+
return receiptExpectation(receipt);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Render the effect receipts (#1835) the build withheld from the apply-bound
|
|
194
|
+
* entity set (`SerializeContext.receipts`, #1832) as `AWS::SSM::Parameter`
|
|
195
|
+
* rows: plain `String`, named `/chant-receipts/<stack>/<env>/<effect>` from
|
|
196
|
+
* the resolved ownership marker (epic decision 4 — the same fields that stamp
|
|
197
|
+
* tags), carrying the ownership tags. Visibility only: the rows go under the
|
|
198
|
+
* template's `Metadata`, and the receipt store (./receipt-store.ts) is what
|
|
199
|
+
* actually writes the parameter — through the `effect()` step, on success,
|
|
200
|
+
* last.
|
|
201
|
+
*
|
|
202
|
+
* The env segment is explicit: a receipt with no resolved `ownership.env` is
|
|
203
|
+
* an error here, never a guessed path.
|
|
204
|
+
*/
|
|
205
|
+
function renderReceiptRows(
|
|
206
|
+
receipts: ReadonlyMap<string, Declarable>,
|
|
207
|
+
ownership: OwnershipMarker | undefined,
|
|
208
|
+
): Record<string, ReceiptRow> {
|
|
209
|
+
const rows: Record<string, ReceiptRow> = {};
|
|
210
|
+
const names = [...receipts.keys()].join(", ");
|
|
211
|
+
if (!ownership) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`aws receipts (${names}): no ownership marker resolved — the receipt path is ` +
|
|
214
|
+
`/chant-receipts/<stack>/<env>/<effect>, derived from the same ownership fields that ` +
|
|
215
|
+
`stamp markers (chant #1703, decision 4). Set ownership: { stack, env } in chant.config.ts.`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (!ownership.env) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`aws receipts (${names}): ownership resolved no env — the receipt path's <env> segment is ` +
|
|
221
|
+
`explicit (chant #1703, decision 4). Set ownership.env in chant.config.ts (a literal, or ` +
|
|
222
|
+
`{ param: "env" } with --param env=<name>).`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
const tags = Object.entries(ownershipEntries(AWS_TAG_OWNERSHIP_KEYS, ownership)).map(
|
|
226
|
+
([Key, Value]) => ({ Key, Value }),
|
|
227
|
+
);
|
|
228
|
+
for (const [name, entity] of receipts) {
|
|
229
|
+
if (!isEffectReceipt(entity)) continue;
|
|
230
|
+
rows[name] = {
|
|
231
|
+
Type: AWS_EFFECT_RECEIPT_ENTITY_TYPE,
|
|
232
|
+
Properties: {
|
|
233
|
+
Name: receiptParameterName(ownership.stack, ownership.env, entity.effect),
|
|
234
|
+
Type: "String",
|
|
235
|
+
Value: receiptRowValue(entity),
|
|
236
|
+
Tags: tags,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return rows;
|
|
241
|
+
}
|
|
242
|
+
|
|
157
243
|
/**
|
|
158
244
|
* Serialize a set of entities into a CFTemplate object (without JSON.stringify).
|
|
159
245
|
*/
|
|
@@ -163,6 +249,7 @@ function serializeToTemplate(
|
|
|
163
249
|
extraParameters?: Record<string, CFParameter>,
|
|
164
250
|
extraOutputs?: Record<string, CFOutput>,
|
|
165
251
|
ownership?: OwnershipMarker,
|
|
252
|
+
receiptRows?: Record<string, ReceiptRow>,
|
|
166
253
|
): CFTemplate {
|
|
167
254
|
const template: CFTemplate = {
|
|
168
255
|
AWSTemplateFormatVersion: "2010-09-09",
|
|
@@ -196,6 +283,14 @@ function serializeToTemplate(
|
|
|
196
283
|
[OWNERSHIP_METADATA_KEY]: ownershipEntries(AWS_TAG_OWNERSHIP_KEYS, ownership),
|
|
197
284
|
};
|
|
198
285
|
}
|
|
286
|
+
// Effect receipt rows (#1835) — visibility only, deliberately outside
|
|
287
|
+
// `Resources`: the applier's desired and prune sets both read `Resources`,
|
|
288
|
+
// and the `effect()` step is a receipt's sole writer (#1832, epic #1703
|
|
289
|
+
// decision 3). The observation leg (plugin.ts) reads the paths back from
|
|
290
|
+
// this block, so the identity is derived exactly once, here.
|
|
291
|
+
if (receiptRows && Object.keys(receiptRows).length > 0) {
|
|
292
|
+
template.Metadata = { ...(template.Metadata ?? {}), [EFFECT_RECEIPTS_METADATA_KEY]: receiptRows };
|
|
293
|
+
}
|
|
199
294
|
for (const [, entity] of entities) {
|
|
200
295
|
if (isDefaultTags(entity)) {
|
|
201
296
|
defaultTagEntries.push(...entity.tags);
|
|
@@ -412,6 +507,14 @@ export const awsSerializer: Serializer = {
|
|
|
412
507
|
serialize(entities: Map<string, Declarable>, outputs?: LexiconOutput[], context?: SerializeContext): string | SerializerResult {
|
|
413
508
|
const ownership = context?.ownership;
|
|
414
509
|
|
|
510
|
+
// Effect receipts (#1835): withheld from `entities` by the build (#1832),
|
|
511
|
+
// rendered here as Metadata rows — never as `Resources` an applier would
|
|
512
|
+
// write. Env-less ownership is an error, not a guessed path segment.
|
|
513
|
+
const receiptRows =
|
|
514
|
+
context?.receipts && context.receipts.size > 0
|
|
515
|
+
? renderReceiptRows(context.receipts, ownership)
|
|
516
|
+
: undefined;
|
|
517
|
+
|
|
415
518
|
// Check if any entities are child projects (nested stacks)
|
|
416
519
|
const childProjects = new Map<string, ChildProjectInstance>();
|
|
417
520
|
let hasChildProjects = false;
|
|
@@ -425,7 +528,7 @@ export const awsSerializer: Serializer = {
|
|
|
425
528
|
|
|
426
529
|
// No nested stacks — use the simple path
|
|
427
530
|
if (!hasChildProjects) {
|
|
428
|
-
const template = serializeToTemplate(entities, outputs, undefined, undefined, ownership);
|
|
531
|
+
const template = serializeToTemplate(entities, outputs, undefined, undefined, ownership, receiptRows);
|
|
429
532
|
return JSON.stringify(template, null, 2);
|
|
430
533
|
}
|
|
431
534
|
|
|
@@ -465,7 +568,7 @@ export const awsSerializer: Serializer = {
|
|
|
465
568
|
}
|
|
466
569
|
|
|
467
570
|
// Serialize the parent template (ChildProjectInstance entities become CF::Stack resources)
|
|
468
|
-
const parentTemplate = serializeToTemplate(entities, outputs, parentParams, undefined, ownership);
|
|
571
|
+
const parentTemplate = serializeToTemplate(entities, outputs, parentParams, undefined, ownership, receiptRows);
|
|
469
572
|
const primary = JSON.stringify(parentTemplate, null, 2);
|
|
470
573
|
|
|
471
574
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
skill: chant-aws-carve-terraform
|
|
3
|
-
description: Demo carving a resource out of Terraform into native chant — advise, emit, bridge, apply — fully offline
|
|
3
|
+
description: Demo carving a resource out of Terraform into native chant — advise, emit, audit, bridge, apply — fully offline
|
|
4
4
|
user-invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ graduation.
|
|
|
15
15
|
## When to use
|
|
16
16
|
|
|
17
17
|
- Someone asks "how does chant move things off Terraform?"
|
|
18
|
-
- You want to show the advise → emit → bridge → apply loop end to end.
|
|
18
|
+
- You want to show the advise → emit → audit → bridge → apply loop end to end.
|
|
19
19
|
- You are evaluating whether a Terraform estate is worth carving.
|
|
20
20
|
|
|
21
21
|
## Preconditions
|
|
@@ -27,7 +27,7 @@ graduation.
|
|
|
27
27
|
## The fastest path: run the bundled demo
|
|
28
28
|
|
|
29
29
|
The `examples/terraform-carve-out` example ships a runnable estate + state and a
|
|
30
|
-
script that runs all
|
|
30
|
+
script that runs all five steps with commentary:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
cd examples/terraform-carve-out
|
|
@@ -54,28 +54,30 @@ From `examples/terraform-carve-out`, with `TF=./terraform`:
|
|
|
54
54
|
--state ./terraform/terraform.tfstate --output ./carveout
|
|
55
55
|
```
|
|
56
56
|
Show `./carveout/src/assets.ts` — a real `new Bucket({ BucketName, Tags })` with
|
|
57
|
-
CloudFormation-style properties mapped from the Terraform state attributes
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
CloudFormation-style properties mapped from the Terraform state attributes,
|
|
58
|
+
and the `aws_s3_bucket_versioning` sub-resource folded into
|
|
59
|
+
`VersioningConfiguration`. Emit also scaffolds `./carveout` into a buildable
|
|
60
|
+
chant project (`chant.config.ts`, `package.json`) and persists a carve
|
|
61
|
+
manifest (`*.carve.json`) — bridge and apply read the target from it, so
|
|
62
|
+
`--select` is only needed once. Explain: a Terraform-managed resource is not
|
|
63
|
+
in any CloudFormation stack, so the correct source of its live shape is the
|
|
64
|
+
state file, not a cloud read.
|
|
65
|
+
|
|
66
|
+
3. **Audit the inherited resource.**
|
|
65
67
|
```bash
|
|
66
|
-
chant
|
|
68
|
+
chant build ./carveout/src --lexicon aws
|
|
67
69
|
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
The first build fails, deliberately: the post-synth audit refuses the
|
|
71
|
+
adopted bucket because Terraform managed it without a public-access block or
|
|
72
|
+
a TLS-only policy. This is a feature of carving: chant immediately tells the
|
|
73
|
+
person what is wrong with what they adopted. Show the fix (add
|
|
74
|
+
`PublicAccessBlockConfiguration` and a companion `S3BucketPolicy` with an
|
|
75
|
+
`aws:SecureTransport = false` Deny — the tutorial page has the exact code),
|
|
76
|
+
then re-run the build and show the valid CloudFormation template.
|
|
75
77
|
|
|
76
78
|
4. **Bridge — patch the surviving Terraform.**
|
|
77
79
|
```bash
|
|
78
|
-
chant carve bridge --from ./terraform --
|
|
80
|
+
chant carve bridge --from ./terraform --output ./carveout
|
|
79
81
|
```
|
|
80
82
|
Show the generated `data "aws_s3_bucket" "assets"` and the rewired survivor.
|
|
81
83
|
Emphasize it is dry-run: nothing in `./terraform` changed. `--apply-rewrites`
|
|
@@ -83,11 +85,12 @@ From `examples/terraform-carve-out`, with `TF=./terraform`:
|
|
|
83
85
|
|
|
84
86
|
5. **Apply — graduation plan.**
|
|
85
87
|
```bash
|
|
86
|
-
chant carve apply --from ./terraform --
|
|
88
|
+
chant carve apply --from ./terraform --output ./carveout --env prod --stack assets
|
|
87
89
|
```
|
|
88
90
|
Show the ownership marker (`chant:managed-by/stack/env`) and the finalized
|
|
89
|
-
runbook.
|
|
90
|
-
|
|
91
|
+
runbook. `--write-source` additionally stamps the marker tags into the
|
|
92
|
+
emitted source. Stress this makes no cloud call — the apply is the person's
|
|
93
|
+
own lifecycle; chant just plans it.
|
|
91
94
|
|
|
92
95
|
## Key points to land
|
|
93
96
|
|
package/src/spec/parse.test.ts
CHANGED
|
@@ -187,6 +187,80 @@ describe("parseCFNSchema", () => {
|
|
|
187
187
|
});
|
|
188
188
|
const result = parseCFNSchema(schema);
|
|
189
189
|
expect(result.resource.deprecatedProperties).toEqual([]);
|
|
190
|
+
expect(result.resource.inferredDeprecations).toEqual([]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// --- Deprecation basis (#1701) ---
|
|
194
|
+
|
|
195
|
+
test("a declared deprecation is not recorded as inferred", () => {
|
|
196
|
+
const schema = JSON.stringify({
|
|
197
|
+
typeName: "AWS::Test::Declared",
|
|
198
|
+
properties: {
|
|
199
|
+
OldProp: { type: "string", description: "An old property." },
|
|
200
|
+
},
|
|
201
|
+
deprecatedProperties: ["/properties/OldProp"],
|
|
202
|
+
additionalProperties: false,
|
|
203
|
+
});
|
|
204
|
+
const result = parseCFNSchema(schema);
|
|
205
|
+
expect(result.resource.deprecatedProperties).toEqual(["OldProp"]);
|
|
206
|
+
expect(result.resource.inferredDeprecations).toEqual([]);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("a description-mined deprecation is recorded as inferred", () => {
|
|
210
|
+
const schema = JSON.stringify({
|
|
211
|
+
typeName: "AWS::Test::Mined",
|
|
212
|
+
properties: {
|
|
213
|
+
OldProp: { type: "string", description: "This property is deprecated. Use NewProp instead." },
|
|
214
|
+
NewProp: { type: "string", description: "The replacement property" },
|
|
215
|
+
},
|
|
216
|
+
additionalProperties: false,
|
|
217
|
+
});
|
|
218
|
+
const result = parseCFNSchema(schema);
|
|
219
|
+
expect(result.resource.deprecatedProperties).toEqual(["OldProp"]);
|
|
220
|
+
expect(result.resource.inferredDeprecations).toEqual(["OldProp"]);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("a property both declared and matched by the regex counts as declared", () => {
|
|
224
|
+
// sampleBucketSchema declares AccessControl and its description says "legacy".
|
|
225
|
+
const result = parseCFNSchema(sampleBucketSchema);
|
|
226
|
+
expect(result.resource.deprecatedProperties).toContain("AccessControl");
|
|
227
|
+
expect(result.resource.inferredDeprecations).not.toContain("AccessControl");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("inferredDeprecations is a subset of deprecatedProperties", () => {
|
|
231
|
+
const schema = JSON.stringify({
|
|
232
|
+
typeName: "AWS::Test::Mixed",
|
|
233
|
+
properties: {
|
|
234
|
+
DeclaredProp: { type: "string", description: "Plain prose." },
|
|
235
|
+
MinedProp: { type: "string", description: "This is a legacy property." },
|
|
236
|
+
},
|
|
237
|
+
deprecatedProperties: ["/properties/DeclaredProp"],
|
|
238
|
+
additionalProperties: false,
|
|
239
|
+
});
|
|
240
|
+
const result = parseCFNSchema(schema);
|
|
241
|
+
const all = new Set(result.resource.deprecatedProperties);
|
|
242
|
+
for (const p of result.resource.inferredDeprecations) {
|
|
243
|
+
expect(all.has(p)).toBe(true);
|
|
244
|
+
}
|
|
245
|
+
expect(result.resource.inferredDeprecations).toEqual(["MinedProp"]);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test("a description naming a sibling's deprecation lands in inferred, not declared", () => {
|
|
249
|
+
// Shape of AWS::Neptune::DBCluster.DBPort, where the description announces
|
|
250
|
+
// that the sibling `Port` is going away and DBPort is the replacement.
|
|
251
|
+
const schema = JSON.stringify({
|
|
252
|
+
typeName: "AWS::Test::Sibling",
|
|
253
|
+
properties: {
|
|
254
|
+
DBPort: {
|
|
255
|
+
type: "integer",
|
|
256
|
+
description: "The port. Note: `Port` will soon be deprecated. Rename it to `DBPort`.",
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
additionalProperties: false,
|
|
260
|
+
});
|
|
261
|
+
const result = parseCFNSchema(schema);
|
|
262
|
+
expect(result.resource.inferredDeprecations).toEqual(["DBPort"]);
|
|
263
|
+
expect(result.resource.deprecatedProperties).toEqual(["DBPort"]);
|
|
190
264
|
});
|
|
191
265
|
|
|
192
266
|
// --- Tagging metadata ---
|
package/src/spec/parse.ts
CHANGED
|
@@ -30,6 +30,13 @@ export type { PropertyConstraints } from "@intentius/chant/codegen/json-schema";
|
|
|
30
30
|
*/
|
|
31
31
|
const MAX_PROPERTY_TYPE_DEPTH = 3;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Prose signals that a property description is describing a deprecation.
|
|
35
|
+
* A hit is a guess, not a reading — see `ParsedResource.inferredDeprecations`.
|
|
36
|
+
*/
|
|
37
|
+
const DEPRECATION_RE =
|
|
38
|
+
/\bdeprecated\b|\blegacy\b|no longer (available|recommended|used|supported)|is not recommended|has been discontinued/i;
|
|
39
|
+
|
|
33
40
|
export interface ParsedProperty {
|
|
34
41
|
name: string;
|
|
35
42
|
tsType: string;
|
|
@@ -63,6 +70,13 @@ export interface ParsedResource {
|
|
|
63
70
|
writeOnly: string[];
|
|
64
71
|
primaryIdentifier: string[];
|
|
65
72
|
deprecatedProperties: string[];
|
|
73
|
+
/**
|
|
74
|
+
* The subset of `deprecatedProperties` that no upstream declaration backs.
|
|
75
|
+
* These names come from {@link DEPRECATION_RE} matching the property
|
|
76
|
+
* description, so they are an inference rather than a reading. Kept apart so
|
|
77
|
+
* a consumer can tell the two apart and calibrate what it says (#1701).
|
|
78
|
+
*/
|
|
79
|
+
inferredDeprecations: string[];
|
|
66
80
|
conditionalCreateOnly: string[];
|
|
67
81
|
replacementStrategy?: "delete_then_create" | "create_then_delete";
|
|
68
82
|
tagging?: { taggable: boolean; tagOnCreate: boolean; tagUpdatable: boolean };
|
|
@@ -160,19 +174,24 @@ export function parseCFNSchema(data: string | Buffer): SchemaParseResult {
|
|
|
160
174
|
}
|
|
161
175
|
}
|
|
162
176
|
|
|
163
|
-
// --- Deprecated properties:
|
|
177
|
+
// --- Deprecated properties: declared upstream, plus description-mined ---
|
|
178
|
+
// The declared half is what the Registry schema states. The mined half is a
|
|
179
|
+
// regex over English prose and is recorded separately (#1701): a description
|
|
180
|
+
// can mention the deprecation of a sibling property, of an enum value, or of
|
|
181
|
+
// something the property merely configures.
|
|
164
182
|
const deprecatedSet = new Set<string>(
|
|
165
183
|
stripPointerPaths(schema.deprecatedProperties ?? []),
|
|
166
184
|
);
|
|
167
|
-
|
|
168
|
-
const DEPRECATION_RE = /\bdeprecated\b|\blegacy\b|no longer (available|recommended|used|supported)|is not recommended|has been discontinued/i;
|
|
185
|
+
const inferredDeprecations: string[] = [];
|
|
169
186
|
|
|
170
187
|
// Mine top-level property descriptions
|
|
171
188
|
if (schema.properties) {
|
|
172
189
|
for (const [name, prop] of Object.entries(schema.properties)) {
|
|
173
|
-
if (prop.description
|
|
174
|
-
|
|
175
|
-
|
|
190
|
+
if (!prop.description || !DEPRECATION_RE.test(prop.description)) continue;
|
|
191
|
+
// A hit upstream already declares adds nothing, and is not an inference.
|
|
192
|
+
if (deprecatedSet.has(name)) continue;
|
|
193
|
+
deprecatedSet.add(name);
|
|
194
|
+
inferredDeprecations.push(name);
|
|
176
195
|
}
|
|
177
196
|
}
|
|
178
197
|
|
|
@@ -211,6 +230,7 @@ export function parseCFNSchema(data: string | Buffer): SchemaParseResult {
|
|
|
211
230
|
writeOnly: stripPointerPaths(schema.writeOnlyProperties ?? []),
|
|
212
231
|
primaryIdentifier: stripPointerPaths(schema.primaryIdentifier ?? []),
|
|
213
232
|
deprecatedProperties: [...deprecatedSet],
|
|
233
|
+
inferredDeprecations,
|
|
214
234
|
conditionalCreateOnly: stripPointerPaths(schema.conditionalCreateOnlyProperties ?? []),
|
|
215
235
|
...(replacementStrategy && { replacementStrategy }),
|
|
216
236
|
...(tagging && { tagging }),
|