@go-to-k/cdkd 0.50.0 → 0.50.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/README.md CHANGED
@@ -170,57 +170,7 @@ the full per-type table.
170
170
  | Attribute enrichment | ✅ | CloudFront OAI, DynamoDB StreamArn, API Gateway RootResourceId, Lambda FunctionUrl, Route53 HealthCheckId, ECR Repository Arn |
171
171
  | CC API null value stripping | ✅ | Removes null values before API calls |
172
172
  | Retry with HTTP status codes | ✅ | 429/503 + cause chain inspection |
173
-
174
- ### Drift detection
175
-
176
- `cdkd drift <stack>` (state-driven; no synth) compares each resource
177
- between the AWS-current snapshot returned by `provider.readCurrentState`
178
- and the **deploy-time AWS snapshot** stored in
179
- `ResourceState.observedProperties`. The observedProperties baseline is
180
- populated automatically on every successful `cdkd deploy` /
181
- `cdkd import`, so console-side changes to keys you did NOT template
182
- (IAM policies attached out-of-band, S3 public-access-block toggled,
183
- etc.) surface as drift instead of being silently ignored.
184
-
185
- State schema `version: 3` (the layout that carries observedProperties)
186
- is auto-migrated on the next write — no user action needed for new
187
- deploys. **For stacks already deployed with an older binary**, the
188
- upgrade story is:
189
-
190
- 1. `cdkd 0.46.x` (or earlier) deployed your stack — state.json is
191
- `version: 2`, no observedProperties on any resource.
192
- 2. Upgrade cdkd to `0.47.0+`. The new binary reads v2 state cleanly,
193
- and `cdkd drift` falls back to comparing against the user-templated
194
- `properties` field (= the pre-v3 behavior) for any resource that
195
- hasn't been refreshed yet.
196
- 3. **Populate observedProperties** for the existing stack — pick one:
197
-
198
- ```bash
199
- # Option A (recommended): explicit refresh, no redeploy.
200
- cdkd state refresh-observed MyStack
201
-
202
- # Option B: trigger an UPDATE on each affected resource via the
203
- # next `cdkd deploy`. NO_CHANGE-skipped resources are NOT refreshed
204
- # by deploy alone — only the ones whose template changed get a
205
- # readCurrentState call. Use this only if you're already changing
206
- # the template; for an upgrade-and-refresh-only flow prefer A.
207
- cdkd deploy MyStack
208
- ```
209
- 4. Re-run `cdkd drift MyStack` — now observed-baseline drift detection
210
- is fully enabled.
211
-
212
- `cdkd state refresh-observed --all` does the same for every stack in
213
- the state bucket; `--dry-run` prints the per-stack refresh count
214
- without touching state. Resolve any drift the comparator finds with
215
- `cdkd drift <stack> --accept` (state ← AWS) or `--revert` (AWS ← state).
216
-
217
- `cdkd deploy --no-capture-observed-state` (or
218
- `cdk.json context.cdkd.captureObservedState: false`) opts out of the
219
- capture entirely if you care more about deploy speed than rich drift
220
- detection — drift then falls back to comparing against `properties`,
221
- the pre-v3 behavior. Bench measurements show roughly +0–4% deploy
222
- time with the capture on (lambda integ within noise; bench-cdk-sample
223
- +3.4% median).
173
+ | Drift detection | ✅ | `cdkd drift` — state vs AWS reality, including console-side changes to keys you didn't template. See [Drift detection](#drift-detection) below. |
224
174
 
225
175
  ## Prerequisites
226
176
 
@@ -287,6 +237,18 @@ That's it. cdkd reads `--app` from `cdk.json` and auto-resolves the state bucket
287
237
 
288
238
  ## Usage
289
239
 
240
+ cdkd has two command families:
241
+
242
+ - **Top-level commands** (`cdkd deploy` / `destroy` / `diff` / `synth` /
243
+ `list` / `import` / `orphan`) require a CDK app — they synthesize
244
+ a template to learn what they're operating on.
245
+ - **`cdkd state ...` subcommands** (`state info` / `list` / `resources`
246
+ / `show` / `orphan` / `destroy` / `migrate` / `refresh-observed`)
247
+ operate on the S3 state bucket only and do NOT need the CDK app —
248
+ use them to inspect / clean up state when the source is gone or
249
+ you don't want to synth. `cdkd state destroy` is the CDK-app-free
250
+ counterpart of `cdkd destroy`.
251
+
290
252
  Options like `--app`, `--state-bucket`, and `--context` can be omitted if configured via `cdk.json` or environment variables (`CDKD_APP`, `CDKD_STATE_BUCKET`).
291
253
 
292
254
  ```bash
@@ -368,6 +330,12 @@ cdkd deploy --no-rollback
368
330
  # Deploy only the specified stack (skip dependency auto-inclusion)
369
331
  cdkd deploy -e MyStack
370
332
 
333
+ # Skip the multi-minute wait on async resources (CloudFront, RDS, NAT GW, etc.)
334
+ cdkd deploy --no-wait
335
+
336
+ # Synth + build + publish assets only (no deploy) — typical CI split
337
+ cdkd publish-assets
338
+
371
339
  # Destroy resources
372
340
  cdkd destroy MyStack
373
341
  cdkd destroy --all --force
@@ -429,183 +397,33 @@ cdkd state destroy --all -y # every stack in the bucket
429
397
  cdkd state destroy MyStack --region us-east-1
430
398
  ```
431
399
 
432
- > **`destroy` vs `orphan`** (matches aws-cdk-cli's new `cdk orphan`):
433
- > `destroy` deletes the AWS resources AND the state record. `orphan` deletes
434
- > ONLY the state record — AWS resources remain intact, just no longer
435
- > tracked by cdkd.
436
- >
437
- > The two `orphan` variants now operate at different granularities:
438
- >
439
- > - `cdkd orphan <constructPath>...` — synth-driven, **per-resource**.
440
- > Removes specific resources from a stack's state file and rewrites every
441
- > sibling reference (Ref / Fn::GetAtt / Fn::Sub / dependencies) so the
442
- > next deploy doesn't re-create the orphan or fail on a stale reference.
443
- > Mirrors `cdk orphan --unstable=orphan`.
444
- > - `cdkd state orphan <stack>...` — state-driven, **whole-stack**. Removes
445
- > the entire state record for a stack from the bucket. Works without the
446
- > CDK app.
447
- >
448
- > `cdkd destroy` (synth-driven, deletes AWS resources + state) and
449
- > `cdkd state destroy` (state-driven, same effect) round out the matrix.
450
-
451
- ## `publish-assets`: synth + build + publish, no deploy
452
-
453
- `cdkd publish-assets` runs the asset half of the deploy pipeline only —
454
- synthesize the CDK app, build any Docker images, upload file assets to
455
- S3, push images to ECR — and stops. No state writes, no provisioning,
456
- no lock acquisition. This is the typical CI split where one runner
457
- builds and uploads assets and a separate runner deploys.
458
-
459
- ```bash
460
- cdkd publish-assets # synth + publish all stacks (or auto-detect single stack)
461
- cdkd publish-assets MyStack # synth + publish a specific stack
462
- cdkd publish-assets --all # synth + publish every stack in the app
463
- cdkd publish-assets 'MyStage/*' # wildcard (CDK display path)
464
- cdkd publish-assets -a cdk.out # skip synth — use a pre-synthesized cloud assembly
465
- ```
466
-
467
- Stack selection follows the same rules as `deploy` / `diff` / `destroy`
468
- (positional > `--stack` > `--all` > auto-detect). Concurrency knobs
469
- are `--asset-publish-concurrency` and `--image-build-concurrency`.
470
- `-a/--app` accepts either a shell command (`"npx ts-node app.ts"`) or
471
- a path to an already-synthesized cloud assembly directory; pointing at
472
- `cdk.out` skips synthesis. See [docs/cli-reference.md](docs/cli-reference.md#publish-assets-synth--build--publish-no-deploy)
473
- for details.
474
-
475
400
  ## `--no-wait`: skip async-resource waits
476
401
 
477
- CloudFront Distributions, RDS Clusters/Instances, ElastiCache, and
478
- NAT Gateways typically take 1–15 minutes for AWS to fully provision.
479
- By default cdkd waits for them to reach a ready state the same
480
- behavior as CloudFormation. Pass `--no-wait` to return as soon as the
481
- create call returns:
482
-
483
- ```bash
484
- cdkd deploy --no-wait
485
- ```
402
+ CloudFront / RDS / ElastiCache / NAT Gateway typically take 1–15
403
+ minutes to fully provision. By default cdkd waits (matching CFn).
404
+ `cdkd deploy --no-wait` returns as soon as the create call returns
405
+ and lets AWS finish in the background handy for CI where nothing
406
+ in the deploy flow needs the resource fully active. **Deploy-only**:
407
+ `cdkd destroy` always waits (NAT in `deleting` state holds ENIs and
408
+ would `DependencyViolation` sibling deletes).
486
409
 
487
- The resource is fully functional once AWS finishes the async
488
- deployment in the background. CloudFormation has no equivalent — once
489
- you submit a stack, you wait for everything.
490
-
491
- NAT Gateway is included as of v0.31. Provisioning typically takes
492
- 1–2 minutes and is the dominant cost in many VPC stacks; with
493
- `cdkd deploy --no-wait`, `CreateNatGateway` returns the `NatGatewayId`
494
- immediately and dependent Routes that only reference the ID can
495
- proceed against a still-`pending` gateway. AWS continues NAT
496
- provisioning asynchronously after the deploy returns. Use this only
497
- when nothing in the deploy flow needs actual NAT-routed egress (e.g.
498
- no Lambda invoked during deploy that hits the internet).
499
-
500
- `--no-wait` is **deploy-only**. `cdkd destroy` always waits for NAT
501
- Gateway to reach `deleted` state — while the gateway is in
502
- `deleting` AWS keeps the ENI / EIP / route-table associations
503
- attached, so any concurrent `DeleteSubnet` / `DeleteInternetGateway`
504
- / `DeleteVpc` returns `DependencyViolation` and the destroy enters a
505
- retry storm. Other `--no-wait` resources (CloudFront / RDS /
506
- ElastiCache) don't apply to destroy either — their providers are
507
- already non-blocking on delete because they're leaves in the destroy
508
- DAG.
410
+ See [docs/cli-reference.md](docs/cli-reference.md#--no-wait-skip-async-resource-waits)
411
+ for per-resource caveats (NAT egress, RDS final-snapshot timing,
412
+ etc.).
509
413
 
510
414
  ## VPC route DependsOn relaxation (on by default)
511
415
 
512
- CDK synth eagerly injects `DependsOn` from VPC Lambdas (and adjacent
513
- IAM Role / Policy / Lambda::Url / EventSourceMapping resources) onto
514
- the private subnet's `DefaultRoute` / `RouteTableAssociation` so that
515
- nothing tries to invoke the Lambda before its egress path to the
516
- internet is up. The dependency is real at *runtime* (a Lambda code
517
- call to a third-party API can't reach the internet without a NAT
518
- route), but it is NOT required at *deploy time* — `CreateFunction` /
519
- `CreateFunctionUrlConfig` / `AddPermission` /
520
- `CreateEventSourceMapping` all accept a function in `Pending` state.
521
-
522
- For VPC + Lambda + CloudFront stacks the strict-CDK-ordering chain is serial:
523
-
524
- ```text
525
- NAT GW (~2-3 min) → DefaultRoute → Lambda → Lambda::Url → Distribution propagation (~3 min)
526
- ```
527
-
528
- cdkd drops the route DependsOn by default so Distribution + Lambda::Url
529
- dispatch right after IAM Role / Subnet are ready and propagate in
530
- parallel with NAT stabilization:
531
-
532
- | Mode | Critical path | Total |
533
- | --- | --- | --- |
534
- | `--no-aggressive-vpc-parallel` (opt-out) | NAT → Lambda → CF (serial) | ~6 min |
535
- | **default** | max(NAT, CF) | **~3 min** |
536
-
537
- Measured **−54.6%** on `tests/integration/bench-cdk-sample` (398.59s
538
- with `--no-aggressive-vpc-parallel` → 181.03s default).
539
-
540
- To opt out (e.g. for a stack with a Custom Resource that synchronously
541
- invokes a VPC Lambda outside cdkd's Lambda-ServiceToken Active wait):
542
-
543
- ```bash
544
- cdkd deploy --no-aggressive-vpc-parallel
545
- ```
546
-
547
- Deploy-only — the relaxation has no effect on destroy ordering (the
548
- route DependsOn doesn't constrain delete-time correctness; Lambda
549
- hyperplane ENI release is the actual destroy bottleneck and is handled
550
- separately by `lambda-vpc-deps.ts`).
416
+ CDK injects defensive `DependsOn` from VPC Lambdas onto private-subnet
417
+ routes. The dependency is real at runtime but NOT required at deploy
418
+ time. cdkd drops it by default so CloudFront + Lambda::Url propagation
419
+ runs in parallel with NAT stabilization (~50% faster on VPC+Lambda+CloudFront
420
+ stacks; bench-cdk-sample 398s 181s). Pass
421
+ `cdkd deploy --no-aggressive-vpc-parallel` to opt out (e.g. when a
422
+ Custom Resource synchronously invokes a VPC Lambda outside cdkd's
423
+ Lambda-ServiceToken Active wait).
551
424
 
552
425
  See [docs/cli-reference.md](docs/cli-reference.md) for the full
553
- type-pair allowlist, implementation pointers, and trade-off notes.
554
-
555
- ## Other CLI flags
556
-
557
- For concurrency knobs (`--concurrency`, `--stack-concurrency`,
558
- `--asset-publish-concurrency`, `--image-build-concurrency`) and
559
- per-resource timeout flags (`--resource-warn-after`,
560
- `--resource-timeout` — including the per-resource-type override syntax
561
- and the rationale for the 30m default), see
562
- **[docs/cli-reference.md](docs/cli-reference.md)**.
563
-
564
- ## Exit codes
565
-
566
- cdkd commands distinguish three outcomes via the process exit code so
567
- CI / bench scripts can react without grepping log output:
568
-
569
- | Exit | Meaning |
570
- |------|---------|
571
- | `0` | Success — command completed and no resources are in an error state |
572
- | `1` | Command-level failure — auth error, bad arguments, synth crash, unhandled exception |
573
- | `2` | **Partial failure** — work completed but one or more resources failed (state.json is preserved, re-running typically resolves it) |
574
-
575
- Exit `2` is currently emitted by `cdkd destroy` and `cdkd state
576
- destroy` when one or more per-resource deletes fail. The summary line
577
- also switches from `✓ Stack X destroyed` to `⚠ Stack X partially
578
- destroyed (...). State preserved — re-run 'cdkd destroy' / 'cdkd
579
- state destroy' to clean up.` so the visual marker matches the exit
580
- code.
581
-
582
- ## Example
583
-
584
- ```typescript
585
- const table = new dynamodb.Table(stack, 'Table', {
586
- partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
587
- });
588
- const fn = new lambda.Function(stack, 'Handler', {
589
- runtime: lambda.Runtime.NODEJS_20_X,
590
- handler: 'index.handler',
591
- code: lambda.Code.fromAsset('lambda'),
592
- environment: { TABLE_NAME: table.tableName },
593
- });
594
- table.grantReadWriteData(fn);
595
- ```
596
-
597
- ```bash
598
- $ cdkd deploy
599
- LambdaStack
600
- ServiceRole CREATE AWS::IAM::Role ✓ (2.1s)
601
- Table CREATE AWS::DynamoDB::Table ✓ (1.8s)
602
- DefaultPolicy CREATE AWS::IAM::Policy ✓ (1.5s)
603
- Handler CREATE AWS::Lambda::Function ✓ (3.4s)
604
-
605
- ✓ Deployed LambdaStack (4 resources, 7.2s)
606
- ```
607
-
608
- Resources are dispatched as soon as their own dependencies complete (event-driven DAG). ServiceRole and Table run in parallel; DefaultPolicy starts the moment ServiceRole is done — without waiting for Table — and Handler starts the moment DefaultPolicy is done.
426
+ type-pair allowlist and trade-off notes.
609
427
 
610
428
  ## Importing existing resources
611
429
 
@@ -629,6 +447,61 @@ modes (auto / selective / hybrid), `--resource-mapping` CDK CLI
629
447
  compatibility, CloudFormation migration flow, provider coverage, and the
630
448
  parity matrix vs upstream `cdk import`.
631
449
 
450
+ ## Drift detection
451
+
452
+ `cdkd drift` (state-driven; no synth) compares each managed resource
453
+ against AWS reality and reports divergence — including console-side
454
+ changes to keys you did NOT template (S3 public-access-block, IAM Role
455
+ tags, Lambda env keys, etc.).
456
+
457
+ ```bash
458
+ cdkd drift # auto-detect single stack, exit 1 if drift
459
+ cdkd drift MyStack --json # machine-readable, for CI gating
460
+ cdkd drift MyStack --accept --yes # state ← AWS (catch up after a console edit)
461
+ cdkd drift MyStack --revert --yes # AWS ← state (undo a console edit)
462
+ cdkd state refresh-observed MyStack # populate the drift baseline without redeploying
463
+ ```
464
+
465
+ See **[docs/cli-reference.md `cdkd drift`](docs/cli-reference.md#cdkd-drift)**
466
+ for the full reference: `--no-capture-observed-state` deploy opt-out
467
+ (per-command vs per-project, mid-flight reversibility), v2→v3 state
468
+ upgrade flow, exit codes, and what changes when capture is off.
469
+
470
+ ## Orphan vs destroy
471
+
472
+ `destroy` deletes the AWS resources **and** the state record;
473
+ `orphan` deletes **only** the state record (AWS resources stay
474
+ intact, just no longer tracked by cdkd). Mirrors aws-cdk-cli's
475
+ `cdk orphan`.
476
+
477
+ Two `orphan` variants at different granularities:
478
+
479
+ - `cdkd orphan <constructPath>...` — synth-driven, **per-resource**.
480
+ Rewrites every sibling reference (Ref / Fn::GetAtt / Fn::Sub /
481
+ dependencies) so the next deploy doesn't re-create the orphan.
482
+ - `cdkd state orphan <stack>...` — state-driven, **whole-stack**.
483
+ Removes the entire state record. Works without the CDK app.
484
+
485
+ Both `cdkd destroy` (synth-driven) and `cdkd state destroy`
486
+ (state-driven, no synth) delete AWS resources + state.
487
+
488
+ ## `publish-assets`: synth + build + publish, no deploy
489
+
490
+ `cdkd publish-assets` runs the asset half of the deploy pipeline
491
+ only — synthesize, build Docker images, upload file assets to S3,
492
+ push images to ECR — and stops. No state writes, no provisioning.
493
+ Typical CI split where one runner builds + uploads assets and a
494
+ separate runner deploys.
495
+
496
+ ```bash
497
+ cdkd publish-assets # all stacks (or auto-detect single stack)
498
+ cdkd publish-assets MyStack # specific stack
499
+ cdkd publish-assets -a cdk.out # skip synth, use pre-synthesized assembly
500
+ ```
501
+
502
+ See [docs/cli-reference.md](docs/cli-reference.md#publish-assets-synth--build--publish-no-deploy)
503
+ for stack-selection rules and concurrency knobs.
504
+
632
505
  ## State Management
633
506
 
634
507
  State is stored in S3 with optimistic locking via S3 Conditional Writes
@@ -678,6 +551,24 @@ After deployment, outputs are resolved and saved to the S3 state file:
678
551
  - cdkd: Outputs saved in S3 state file (e.g., `s3://bucket/cdkd/MyStack/us-east-1/state.json`)
679
552
  - Both resolve intrinsic functions (Ref, Fn::GetAtt, etc.) to actual values
680
553
 
554
+ ## Exit codes
555
+
556
+ cdkd commands distinguish three outcomes via the process exit code so
557
+ CI / bench scripts can react without grepping log output:
558
+
559
+ | Exit | Meaning |
560
+ |------|---------|
561
+ | `0` | Success — command completed and no resources are in an error state |
562
+ | `1` | Command-level failure — auth error, bad arguments, synth crash, unhandled exception |
563
+ | `2` | **Partial failure** — work completed but one or more resources failed (state.json is preserved, re-running typically resolves it) |
564
+
565
+ Exit `2` is currently emitted by `cdkd destroy` and `cdkd state
566
+ destroy` when one or more per-resource deletes fail. The summary line
567
+ also switches from `✓ Stack X destroyed` to `⚠ Stack X partially
568
+ destroyed (...). State preserved — re-run 'cdkd destroy' / 'cdkd
569
+ state destroy' to clean up.` so the visual marker matches the exit
570
+ code.
571
+
681
572
  ## License
682
573
 
683
574
  Apache 2.0