@go-to-k/cdkd 0.0.1 → 0.0.3

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
@@ -1,45 +1,568 @@
1
- # @go-to-k/cdkd
1
+ # cdkd
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ **cdkd** (CDK Direct) - A from-scratch CDK CLI with its own deployment engine — provisions via AWS SDK instead of CloudFormation.
4
4
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ - **Direct provisioning** via AWS SDK instead of CloudFormation
6
+ - **From-scratch CDK CLI** - synthesis orchestration, asset publishing, context resolution (no aws-cdk / toolkit-lib dependency)
7
+ - **CDK compatible** - use your existing CDK app code as-is
8
+ - **Own deployment engine** - diff calculation, dependency graph, parallel execution, state management (what CloudFormation handles internally)
6
9
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
10
+ ![cdkd demo](https://github.com/user-attachments/assets/0128730d-186d-4bd3-abea-aabc80ba4dd5)
8
11
 
9
- ## Purpose
12
+ > **⚠️ WARNING: NOT PRODUCTION READY**
13
+ >
14
+ > This project is in early development and is **NOT suitable for production use**. Features are incomplete, APIs may change without notice, and there may be bugs that could affect your AWS infrastructure. Use at your own risk in development/testing environments only.
10
15
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@go-to-k/cdkd`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
16
+ > **Note**: This is an experimental/educational project exploring alternative deployment approaches for AWS CDK. It is **not intended to replace** the official AWS CDK CLI, but rather to experiment with direct SDK provisioning as a learning exercise and proof of concept.
15
17
 
16
- ## What is OIDC Trusted Publishing?
18
+ ## Features
17
19
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
20
+ - **Synthesis orchestration**: CDK app subprocess execution, Cloud Assembly parsing, context provider loop
21
+ - **Asset handling**: Self-implemented asset publisher for S3 file assets (ZIP packaging) and Docker images (ECR)
22
+ - **Context resolution**: Self-implemented context provider loop for Vpc.fromLookup(), AZ, SSM, HostedZone, etc.
23
+ - **Hybrid provisioning**: SDK Providers for fast direct API calls, Cloud Control API fallback for broad resource coverage
24
+ - **Diff calculation**: Self-implemented resource/property-level diff between desired template and current state
25
+ - **S3-based state management**: No DynamoDB required, uses S3 conditional writes for locking
26
+ - **DAG-based parallelization**: Analyze `Ref`/`Fn::GetAtt` dependencies and execute in parallel
19
27
 
20
- ## Setup Instructions
28
+ > **Note**: Resource types not covered by either SDK Providers or Cloud Control API cannot be deployed with cdkd. If you encounter an unsupported resource type, deployment will fail with a clear error message.
21
29
 
22
- To properly configure OIDC trusted publishing for this package:
30
+ ## Benchmark
23
31
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
32
+ **cdkd deploys up to ~5x faster than AWS CDK (CloudFormation).**
28
33
 
29
- ## DO NOT USE THIS PACKAGE
34
+ Measured on `us-east-1` with 5 independent resources per stack (fully parallelized by cdkd's DAG scheduler).
30
35
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
+ ### SDK Provider path **4.8x faster** (20.5s vs 98.4s)
36
37
 
37
- ## More Information
38
+ Stack: S3 Bucket, DynamoDB Table, SQS Queue, SNS Topic, SSM Parameter.
38
39
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
40
+ | Phase | cdkd | AWS CDK (CFn) | Speedup |
41
+ | --- | --- | --- | --- |
42
+ | Synthesis | 3.5s | 4.1s | 1.2x |
43
+ | Deploy | 17.0s | 94.4s | **5.5x** |
44
+ | **Total** | **20.5s** | **98.4s** | **4.8x** |
42
45
 
43
- ---
46
+ ### Cloud Control API fallback path — **1.5x faster** (44.6s vs 69.1s)
44
47
 
45
- **Maintained for OIDC setup purposes only**
48
+ Stack: SSM Document × 3 + Athena WorkGroup × 2 (no SDK provider — CC API fallback).
49
+
50
+ | Phase | cdkd | AWS CDK (CFn) | Speedup |
51
+ | --- | --- | --- | --- |
52
+ | Synthesis | 3.7s | 4.2s | 1.1x |
53
+ | Deploy | 40.9s | 64.9s | **1.6x** |
54
+ | **Total** | **44.6s** | **69.1s** | **1.5x** |
55
+
56
+ Reproduce with `./tests/benchmark/run-benchmark.sh all`. See [tests/benchmark/README.md](tests/benchmark/README.md) for details.
57
+
58
+ ## How it works
59
+
60
+ ```
61
+ ┌─────────────────┐
62
+ │ Your CDK App │ (aws-cdk-lib)
63
+ └────────┬────────┘
64
+
65
+
66
+ ┌─────────────────┐
67
+ │ cdkd Synthesis │ Subprocess + Cloud Assembly parser
68
+ └────────┬────────┘
69
+
70
+
71
+ ┌─────────────────┐
72
+ │ CloudFormation │
73
+ │ Template │
74
+ └────────┬────────┘
75
+
76
+
77
+ ┌─────────────────┐
78
+ │ cdkd Engine │
79
+ │ - DAG Analysis │ Dependency graph construction
80
+ │ - Diff Calc │ Compare with existing resources
81
+ │ - Parallel Exec │ Deploy by levels
82
+ └────────┬────────┘
83
+
84
+ ┌────┴────┐
85
+ ▼ ▼
86
+ ┌────────┐ ┌────────┐
87
+ │ SDK │ │ Cloud │
88
+ │Provider│ │Control │ Fallback for many
89
+ │ │ │ API │ additional types
90
+ └────────┘ └────────┘
91
+ ```
92
+
93
+ ### Detailed Processing Flow (`cdkd deploy`)
94
+
95
+ ```
96
+ 1. CLI Layer
97
+ ├── Resolve --app (CLI > CDKD_APP env > cdk.json "app")
98
+ ├── Resolve --state-bucket (CLI > env > cdk.json > auto: cdkd-state-{accountId}-{region})
99
+ └── Initialize AWS clients
100
+
101
+ 2. Synthesis (self-implemented, no CDK CLI dependency)
102
+ ├── Load context (merge order, later wins):
103
+ │ ├── CDK defaults (path-metadata, asset-metadata, version-reporting, bundling-stacks)
104
+ │ ├── ~/.cdk.json "context" field (user defaults)
105
+ │ ├── cdk.json "context" field (project settings)
106
+ │ ├── cdk.context.json (cached lookups, reloaded each iteration)
107
+ │ └── CLI -c key=value (highest priority)
108
+ ├── Execute CDK app as subprocess
109
+ │ ├── child_process.spawn(app command)
110
+ │ ├── Pass env: CDK_OUTDIR, CDK_CONTEXT_JSON, CDK_DEFAULT_REGION/ACCOUNT
111
+ │ └── App writes Cloud Assembly to cdk.out/
112
+ ├── Parse cdk.out/manifest.json
113
+ │ ├── Extract stacks (type: aws:cloudformation:stack)
114
+ │ ├── Extract asset manifests (type: cdk:asset-manifest)
115
+ │ └── Extract stack dependencies
116
+ └── Context provider loop (if missing context detected):
117
+ ├── Resolve via AWS SDK (all CDK context provider types supported)
118
+ ├── Save to cdk.context.json
119
+ └── Re-execute CDK app with updated context
120
+
121
+ 3. Asset Publishing + Deployment (WorkGraph DAG)
122
+ ├── Each asset is a node, each stack deploy is a node
123
+ │ ├── asset-publish nodes: 8 concurrent (file S3 uploads + Docker build+push)
124
+ │ ├── stack nodes: 4 concurrent deployments
125
+ │ ├── Dependencies: asset-publish → stack (all assets complete before deploy)
126
+ │ └── Inter-stack: stack A → stack B (CDK dependency order)
127
+ ├── Region resolved from asset manifest destination (stack's target region)
128
+ ├── Skip if already exists (HeadObject for S3, DescribeImages for ECR)
129
+ ├── Per-stack deploy flow:
130
+ │ ├── Acquire S3 lock (optimistic locking)
131
+ │ ├── Load current state from S3
132
+ │ ├── Build DAG from template (Ref/Fn::GetAtt/DependsOn)
133
+ │ ├── Calculate diff (CREATE/UPDATE/DELETE)
134
+ │ ├── Resolve intrinsic functions (Ref, Fn::Sub, Fn::Join, etc.)
135
+ │ ├── Execute by levels (parallel within each level):
136
+ │ │ ├── SDK Providers (direct API calls, preferred)
137
+ │ │ └── Cloud Control API (fallback, async polling)
138
+ │ ├── Save state after each level (partial state save)
139
+ │ └── Release lock
140
+ └── synth does NOT publish assets or deploy (deploy only)
141
+ ```
142
+
143
+ ## Supported Features
144
+
145
+ ### Intrinsic Functions
146
+
147
+ | Function | Status | Notes |
148
+ |----------|--------|-------|
149
+ | `Ref` | ✅ Supported | Resource physical IDs, Parameters, Pseudo parameters |
150
+ | `Fn::GetAtt` | ✅ Supported | Resource attributes (ARN, DomainName, etc.) |
151
+ | `Fn::Join` | ✅ Supported | String concatenation |
152
+ | `Fn::Sub` | ✅ Supported | Template string substitution |
153
+ | `Fn::Select` | ✅ Supported | Array index selection |
154
+ | `Fn::Split` | ✅ Supported | String splitting |
155
+ | `Fn::If` | ✅ Supported | Conditional values |
156
+ | `Fn::Equals` | ✅ Supported | Equality comparison |
157
+ | `Fn::And` | ✅ Supported | Logical AND (2-10 conditions) |
158
+ | `Fn::Or` | ✅ Supported | Logical OR (2-10 conditions) |
159
+ | `Fn::Not` | ✅ Supported | Logical NOT |
160
+ | `Fn::ImportValue` | ✅ Supported | Cross-stack references via S3 state |
161
+ | `Fn::FindInMap` | ✅ Supported | Mapping lookup |
162
+ | `Fn::GetAZs` | ✅ Supported | Availability Zone list |
163
+ | `Fn::Base64` | ✅ Supported | Base64 encoding |
164
+ | `Fn::Cidr` | ✅ Supported | CIDR address block generation |
165
+
166
+ ### Pseudo Parameters
167
+
168
+ | Parameter | Status |
169
+ |-----------|--------|
170
+ | `AWS::Region` | ✅ |
171
+ | `AWS::AccountId` | ✅ (via STS) |
172
+ | `AWS::Partition` | ✅ |
173
+ | `AWS::URLSuffix` | ✅ |
174
+ | `AWS::NoValue` | ✅ |
175
+ | `AWS::StackName` | ✅ |
176
+ | `AWS::StackId` | ✅ |
177
+
178
+ ### Resource Provisioning
179
+
180
+ | Category | Resource Type | Provider | Status |
181
+ |----------|--------------|----------|--------|
182
+ | **IAM** | AWS::IAM::Role | SDK Provider | ✅ |
183
+ | **IAM** | AWS::IAM::Policy | SDK Provider | ✅ |
184
+ | **IAM** | AWS::IAM::InstanceProfile | SDK Provider | ✅ |
185
+ | **IAM** | AWS::IAM::User | SDK Provider | ✅ |
186
+ | **IAM** | AWS::IAM::Group | SDK Provider | ✅ |
187
+ | **IAM** | AWS::IAM::UserToGroupAddition | SDK Provider | ✅ |
188
+ | **Storage** | AWS::S3::Bucket | SDK Provider | ✅ |
189
+ | **Storage** | AWS::S3::BucketPolicy | SDK Provider | ✅ |
190
+ | **Messaging** | AWS::SQS::Queue | SDK Provider | ✅ |
191
+ | **Messaging** | AWS::SQS::QueuePolicy | SDK Provider | ✅ |
192
+ | **Messaging** | AWS::SNS::Topic | SDK Provider | ✅ |
193
+ | **Messaging** | AWS::SNS::Subscription | SDK Provider | ✅ |
194
+ | **Messaging** | AWS::SNS::TopicPolicy | SDK Provider | ✅ |
195
+ | **Compute** | AWS::Lambda::Function | SDK Provider | ✅ |
196
+ | **Compute** | AWS::Lambda::Permission | SDK Provider | ✅ |
197
+ | **Compute** | AWS::Lambda::Url | SDK Provider | ✅ |
198
+ | **Compute** | AWS::Lambda::EventSourceMapping | SDK Provider | ✅ |
199
+ | **Compute** | AWS::Lambda::LayerVersion | SDK Provider | ✅ |
200
+ | **Database** | AWS::DynamoDB::Table | SDK Provider | ✅ |
201
+ | **Monitoring** | AWS::Logs::LogGroup | SDK Provider | ✅ |
202
+ | **Monitoring** | AWS::CloudWatch::Alarm | SDK Provider | ✅ |
203
+ | **Secrets** | AWS::SecretsManager::Secret | SDK Provider | ✅ |
204
+ | **Config** | AWS::SSM::Parameter | SDK Provider | ✅ |
205
+ | **Events** | AWS::Events::Rule | SDK Provider | ✅ |
206
+ | **Events** | AWS::Events::EventBus | SDK Provider | ✅ |
207
+ | **Networking** | AWS::EC2::VPC | SDK Provider | ✅ |
208
+ | **Networking** | AWS::EC2::Subnet | SDK Provider | ✅ |
209
+ | **Networking** | AWS::EC2::InternetGateway | SDK Provider | ✅ |
210
+ | **Networking** | AWS::EC2::VPCGatewayAttachment | SDK Provider | ✅ |
211
+ | **Networking** | AWS::EC2::RouteTable | SDK Provider | ✅ |
212
+ | **Networking** | AWS::EC2::Route | SDK Provider | ✅ |
213
+ | **Networking** | AWS::EC2::SubnetRouteTableAssociation | SDK Provider | ✅ |
214
+ | **Networking** | AWS::EC2::SecurityGroup | SDK Provider | ✅ |
215
+ | **Networking** | AWS::EC2::SecurityGroupIngress | SDK Provider | ✅ |
216
+ | **Networking** | AWS::EC2::NetworkAcl | SDK Provider | ✅ |
217
+ | **Networking** | AWS::EC2::NetworkAclEntry | SDK Provider | ✅ |
218
+ | **Networking** | AWS::EC2::SubnetNetworkAclAssociation | SDK Provider | ✅ |
219
+ | **Compute** | AWS::EC2::Instance | SDK Provider | ✅ |
220
+ | **API Gateway** | AWS::ApiGateway::Account | SDK Provider | ✅ |
221
+ | **API Gateway** | AWS::ApiGateway::Resource | SDK Provider | ✅ |
222
+ | **API Gateway** | AWS::ApiGateway::Deployment | SDK Provider | ✅ |
223
+ | **API Gateway** | AWS::ApiGateway::Stage | SDK Provider | ✅ |
224
+ | **API Gateway** | AWS::ApiGateway::Method | SDK Provider | ✅ |
225
+ | **API Gateway** | AWS::ApiGateway::Authorizer | SDK Provider | ✅ |
226
+ | **API Gateway** | AWS::ApiGatewayV2::Api | SDK Provider | ✅ |
227
+ | **API Gateway** | AWS::ApiGatewayV2::Stage | SDK Provider | ✅ |
228
+ | **API Gateway** | AWS::ApiGatewayV2::Integration | SDK Provider | ✅ |
229
+ | **API Gateway** | AWS::ApiGatewayV2::Route | SDK Provider | ✅ |
230
+ | **API Gateway** | AWS::ApiGatewayV2::Authorizer | SDK Provider | ✅ |
231
+ | **CDN** | AWS::CloudFront::CloudFrontOriginAccessIdentity | SDK Provider | ✅ |
232
+ | **CDN** | AWS::CloudFront::Distribution | SDK Provider | ✅ |
233
+ | **Orchestration** | AWS::StepFunctions::StateMachine | SDK Provider | ✅ |
234
+ | **Container** | AWS::ECS::Cluster | SDK Provider | ✅ |
235
+ | **Container** | AWS::ECS::TaskDefinition | SDK Provider | ✅ |
236
+ | **Container** | AWS::ECS::Service | SDK Provider | ✅ |
237
+ | **Load Balancing** | AWS::ElasticLoadBalancingV2::LoadBalancer | SDK Provider | ✅ |
238
+ | **Load Balancing** | AWS::ElasticLoadBalancingV2::TargetGroup | SDK Provider | ✅ |
239
+ | **Load Balancing** | AWS::ElasticLoadBalancingV2::Listener | SDK Provider | ✅ |
240
+ | **Database** | AWS::RDS::DBSubnetGroup | SDK Provider | ✅ |
241
+ | **Database** | AWS::RDS::DBCluster | SDK Provider | ✅ |
242
+ | **Database** | AWS::RDS::DBInstance | SDK Provider | ✅ |
243
+ | **DNS** | AWS::Route53::HostedZone | SDK Provider | ✅ |
244
+ | **DNS** | AWS::Route53::RecordSet | SDK Provider | ✅ |
245
+ | **Security** | AWS::WAFv2::WebACL | SDK Provider | ✅ |
246
+ | **Auth** | AWS::Cognito::UserPool | SDK Provider | ✅ |
247
+ | **Cache** | AWS::ElastiCache::CacheCluster | SDK Provider | ✅ |
248
+ | **Cache** | AWS::ElastiCache::SubnetGroup | SDK Provider | ✅ |
249
+ | **Discovery** | AWS::ServiceDiscovery::PrivateDnsNamespace | SDK Provider | ✅ |
250
+ | **Discovery** | AWS::ServiceDiscovery::Service | SDK Provider | ✅ |
251
+ | **GraphQL** | AWS::AppSync::GraphQLApi | SDK Provider | ✅ |
252
+ | **GraphQL** | AWS::AppSync::GraphQLSchema | SDK Provider | ✅ |
253
+ | **GraphQL** | AWS::AppSync::DataSource | SDK Provider | ✅ |
254
+ | **GraphQL** | AWS::AppSync::Resolver | SDK Provider | ✅ |
255
+ | **GraphQL** | AWS::AppSync::ApiKey | SDK Provider | ✅ |
256
+ | **Analytics** | AWS::Glue::Database | SDK Provider | ✅ |
257
+ | **Analytics** | AWS::Glue::Table | SDK Provider | ✅ |
258
+ | **Encryption** | AWS::KMS::Key | SDK Provider | ✅ |
259
+ | **Encryption** | AWS::KMS::Alias | SDK Provider | ✅ |
260
+ | **Streaming** | AWS::Kinesis::Stream | SDK Provider | ✅ |
261
+ | **Streaming** | AWS::KinesisFirehose::DeliveryStream | SDK Provider | ✅ |
262
+ | **Storage** | AWS::EFS::FileSystem | SDK Provider | ✅ |
263
+ | **Storage** | AWS::EFS::MountTarget | SDK Provider | ✅ |
264
+ | **Storage** | AWS::EFS::AccessPoint | SDK Provider | ✅ |
265
+ | **Storage** | AWS::S3Express::DirectoryBucket | SDK Provider | ✅ |
266
+ | **Storage** | AWS::S3Tables::TableBucket | SDK Provider | ✅ |
267
+ | **Storage** | AWS::S3Tables::Namespace | SDK Provider | ✅ |
268
+ | **Storage** | AWS::S3Tables::Table | SDK Provider | ✅ |
269
+ | **Storage** | AWS::S3Vectors::VectorBucket | SDK Provider | ✅ |
270
+ | **Audit** | AWS::CloudTrail::Trail | SDK Provider | ✅ |
271
+ | **CI/CD** | AWS::CodeBuild::Project | SDK Provider | ✅ |
272
+ | **AI/ML** | AWS::BedrockAgentCore::Runtime | SDK Provider | ✅ |
273
+ | **Custom** | Custom::* (Lambda/SNS-backed) | SDK Provider | ✅ |
274
+ | **Other** | All other resource types | Cloud Control | ✅ |
275
+
276
+ ### Other Features
277
+
278
+ | Feature | Status | Notes |
279
+ |---------|--------|-------|
280
+ | CloudFormation Parameters | ✅ | Default values, type coercion |
281
+ | Conditions | ✅ | With logical operators |
282
+ | Cross-stack references | ✅ | Via `Fn::ImportValue` + S3 state |
283
+ | JSON Patch updates | ✅ | RFC 6902, minimal patches |
284
+ | Resource replacement detection | ✅ | 10+ resource types |
285
+ | Dynamic References | ✅ | `{{resolve:secretsmanager:...}}`, `{{resolve:ssm:...}}` |
286
+ | DELETE idempotency | ✅ | Not-found errors treated as success |
287
+ | Asset publishing (S3) | ✅ | Lambda code packages |
288
+ | Asset publishing (ECR) | ✅ | Self-implemented Docker image publishing |
289
+ | Custom Resources (SNS-backed) | ✅ | SNS Topic ServiceToken + S3 response |
290
+ | Custom Resources (CDK Provider) | ✅ | isCompleteHandler/onEventHandler async pattern detection |
291
+ | Rollback | ✅ | --no-rollback flag to skip |
292
+ | DeletionPolicy: Retain | ✅ | Skip deletion for retained resources |
293
+ | UpdateReplacePolicy: Retain | ✅ | Keep old resource on replacement |
294
+ | Implicit delete dependencies | ✅ | VPC/IGW/EventBus/Subnet/RouteTable ordering |
295
+ | Stack dependency resolution | ✅ | Auto-deploy dependency stacks, `-e` to skip |
296
+ | Multi-stack parallel deploy | ✅ | Independent stacks deployed in parallel |
297
+ | Attribute enrichment | ✅ | CloudFront OAI, DynamoDB StreamArn, API Gateway RootResourceId, Lambda FunctionUrl, Route53 HealthCheckId, ECR Repository Arn |
298
+ | CC API null value stripping | ✅ | Removes null values before API calls |
299
+ | Retry with HTTP status codes | ✅ | 429/503 + cause chain inspection |
300
+
301
+ ## Prerequisites
302
+
303
+ - **Node.js** >= 20.0.0
304
+ - **AWS CDK Bootstrap**: You must run `cdk bootstrap` before using cdkd. cdkd uses CDK's bootstrap bucket (`cdk-hnb659fds-assets-*`) for asset uploads (Lambda code, Docker images). Custom bootstrap qualifiers are supported — CDK embeds the correct bucket/repo names in the asset manifest during synthesis.
305
+ - **AWS Credentials**: Configured via environment variables, `~/.aws/credentials`, or `--profile` option
306
+
307
+ ## Installation
308
+
309
+ ### From npm
310
+
311
+ ```bash
312
+ npm i -g @go-to-k/cdkd # latest release
313
+ npm i -g @go-to-k/cdkd@0.0.2 # pin to a specific version
314
+ ```
315
+
316
+ The installed binary is `cdkd` — run it the same way in either install path.
317
+
318
+ > cdkd is an experimental / educational project and is not intended for production use — see the warning at the top of this README. Pin to a specific version if you need reproducible installs.
319
+
320
+ ### From source
321
+
322
+ ```bash
323
+ git clone https://github.com/go-to-k/cdkd.git
324
+ cd cdkd
325
+ pnpm install
326
+ pnpm run build
327
+ npm link
328
+ ```
329
+
330
+ If `cdkd` is not found after `npm link`, set an alias in the current shell:
331
+
332
+ ```bash
333
+ alias cdkd="node $(pwd)/dist/cli.js"
334
+ ```
335
+
336
+ ## Quick Start
337
+
338
+ ```bash
339
+ # Bootstrap (creates S3 state bucket - only needed once per account/region)
340
+ cdkd bootstrap
341
+
342
+ # Deploy your CDK app
343
+ cdkd deploy
344
+
345
+ # Check what would change
346
+ cdkd diff
347
+
348
+ # Tear down
349
+ cdkd destroy
350
+ ```
351
+
352
+ That's it. cdkd reads `--app` from `cdk.json` and auto-resolves the state bucket from your AWS account ID (`cdkd-state-{accountId}-{region}`).
353
+
354
+ ## Usage
355
+
356
+ Options like `--app`, `--state-bucket`, and `--context` can be omitted if configured via `cdk.json` or environment variables (`CDKD_APP`, `CDKD_STATE_BUCKET`).
357
+
358
+ ```bash
359
+ # Bootstrap (create S3 bucket for state)
360
+ cdkd bootstrap \
361
+ --state-bucket my-cdkd-state \
362
+ --region us-east-1
363
+
364
+ # Synthesize only
365
+ cdkd synth --app "npx ts-node app.ts"
366
+
367
+ # Deploy (single stack auto-detected, reads --app from cdk.json)
368
+ cdkd deploy
369
+
370
+ # Deploy specific stack(s)
371
+ cdkd deploy MyStack
372
+ cdkd deploy Stack1 Stack2
373
+
374
+ # Deploy all stacks
375
+ cdkd deploy --all
376
+
377
+ # Deploy with wildcard
378
+ cdkd deploy 'My*'
379
+
380
+ # Deploy with context values
381
+ cdkd deploy -c env=staging -c featureFlag=true
382
+
383
+ # Deploy with explicit options
384
+ cdkd deploy MyStack \
385
+ --app "npx ts-node app.ts" \
386
+ --state-bucket my-cdkd-state \
387
+ --region us-east-1 \
388
+ --verbose
389
+
390
+ # Show diff (what would change)
391
+ cdkd diff MyStack
392
+
393
+ # Dry run (plan only, no changes)
394
+ cdkd deploy --dry-run
395
+
396
+ # Deploy with no rollback on failure (Terraform-style)
397
+ cdkd deploy --no-rollback
398
+
399
+ # Deploy only the specified stack (skip dependency auto-inclusion)
400
+ cdkd deploy -e MyStack
401
+
402
+ # Destroy resources
403
+ cdkd destroy MyStack
404
+ cdkd destroy --all --force
405
+
406
+ # Force-unlock a stale lock from interrupted deploy
407
+ cdkd force-unlock MyStack
408
+ ```
409
+
410
+ ### Concurrency Options
411
+
412
+ | Option | Default | Description |
413
+ | --- | --- | --- |
414
+ | `--concurrency` | 10 | Maximum concurrent resource operations per stack |
415
+ | `--stack-concurrency` | 4 | Maximum concurrent stack deployments |
416
+ | `--asset-publish-concurrency` | 8 | Maximum concurrent asset publish operations (S3 + ECR push) |
417
+ | `--image-build-concurrency` | 4 | Maximum concurrent Docker image builds |
418
+
419
+ ## `--no-wait`
420
+
421
+ By default, cdkd waits for async resources (CloudFront Distribution, RDS Cluster/Instance, ElastiCache) to reach a ready state before completing — the same behavior as CloudFormation.
422
+
423
+ Use `--no-wait` to skip this and return immediately after resource creation:
424
+
425
+ ```bash
426
+ cdkd deploy --no-wait
427
+ ```
428
+
429
+ This can significantly speed up deployments with CloudFront (which takes 3-15 minutes to deploy to edge locations). The resource is fully functional once AWS finishes the async deployment.
430
+
431
+ ## Example
432
+
433
+ ```typescript
434
+ const table = new dynamodb.Table(stack, 'Table', {
435
+ partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
436
+ });
437
+ const fn = new lambda.Function(stack, 'Handler', {
438
+ runtime: lambda.Runtime.NODEJS_20_X,
439
+ handler: 'index.handler',
440
+ code: lambda.Code.fromAsset('lambda'),
441
+ environment: { TABLE_NAME: table.tableName },
442
+ });
443
+ table.grantReadWriteData(fn);
444
+ ```
445
+
446
+ ```bash
447
+ $ cdkd deploy
448
+ LambdaStack
449
+ ServiceRole CREATE AWS::IAM::Role ✓ (2.1s)
450
+ Table CREATE AWS::DynamoDB::Table ✓ (1.8s)
451
+ DefaultPolicy CREATE AWS::IAM::Policy ✓ (1.5s)
452
+ Handler CREATE AWS::Lambda::Function ✓ (3.4s)
453
+
454
+ ✓ Deployed LambdaStack (4 resources, 7.2s)
455
+ ```
456
+
457
+ Resources without dependencies (ServiceRole and Table) are created in parallel.
458
+
459
+ ## Architecture
460
+
461
+ Built on modern AWS tooling:
462
+
463
+ - **Synthesis orchestration** - Executes CDK app as subprocess (synthesis itself is done by aws-cdk-lib), parses Cloud Assembly (manifest.json) directly, context provider loop (missing context → SDK lookup → re-synthesize)
464
+ - **Self-implemented asset publisher** - S3 file upload with ZIP packaging (via `archiver`) and ECR Docker image publishing
465
+ - **AWS SDK v3** - Direct resource provisioning
466
+ - **Cloud Control API** - Fallback resource management for types without SDK Providers
467
+ - **S3 Conditional Writes** - State locking via `If-None-Match`/`If-Match`
468
+
469
+ ## State Management
470
+
471
+ State is stored in S3. Each stack has its own `state.json` and `lock.json`:
472
+
473
+ ```
474
+ s3://{state-bucket}/
475
+ └── {prefix}/ # Default: "cdkd" (configurable via --state-prefix)
476
+ ├── MyStack/
477
+ │ ├── state.json # Resource state
478
+ │ └── lock.json # Exclusive deploy lock
479
+ └── AnotherStack/
480
+ ├── state.json
481
+ └── lock.json
482
+ ```
483
+
484
+ ### Configuration
485
+
486
+ | Setting | CLI | cdk.json | Env var | Default |
487
+ |---------|-----|----------|---------|---------|
488
+ | Bucket | `--state-bucket` | `context.cdkd.stateBucket` | `CDKD_STATE_BUCKET` | `cdkd-state-{accountId}-{region}` |
489
+ | Prefix | `--state-prefix` | - | - | `cdkd` |
490
+
491
+ ### Multi-app isolation
492
+
493
+ The state bucket is shared across all CDK apps in the same account/region by default. To isolate apps, use different prefixes:
494
+
495
+ ```bash
496
+ # App A
497
+ cdkd deploy --state-prefix app-a
498
+
499
+ # App B
500
+ cdkd deploy --state-prefix app-b
501
+ ```
502
+
503
+ > **Note**: `cdkd destroy --all` only targets stacks from the current CDK app (determined by synthesis), not all stacks in the bucket.
504
+
505
+ State schema:
506
+
507
+ ```typescript
508
+ {
509
+ version: 1,
510
+ stackName: "MyStack",
511
+ resources: {
512
+ "MyFunction": {
513
+ physicalId: "arn:aws:lambda:...",
514
+ resourceType: "AWS::Lambda::Function",
515
+ properties: { ... },
516
+ attributes: { Arn: "...", ... }, // For Fn::GetAtt
517
+ dependencies: ["MyBucket"] // For proper deletion order
518
+ }
519
+ },
520
+ outputs: { ... },
521
+ lastModified: 1234567890
522
+ }
523
+ ```
524
+
525
+ ## Stack Outputs
526
+
527
+ CDK's `CfnOutput` constructs are resolved and stored in the state file:
528
+
529
+ ```typescript
530
+ // In your CDK code
531
+ new cdk.CfnOutput(this, 'BucketArn', {
532
+ value: bucket.bucketArn, // Uses Fn::GetAtt internally
533
+ description: 'ARN of the bucket',
534
+ });
535
+ ```
536
+
537
+ After deployment, outputs are resolved and saved to the S3 state file:
538
+
539
+ ```json
540
+ {
541
+ "outputs": {
542
+ "BucketArn": "arn:aws:s3:::actual-bucket-name-xyz"
543
+ }
544
+ }
545
+ ```
546
+
547
+ **Key differences from CloudFormation**:
548
+
549
+ - CloudFormation: Outputs accessible via `aws cloudformation describe-stacks`
550
+ - cdkd: Outputs saved in S3 state file (e.g., `s3://bucket/cdkd/MyStack/state.json`)
551
+ - Both resolve intrinsic functions (Ref, Fn::GetAtt, etc.) to actual values
552
+
553
+ ## Testing
554
+
555
+ - Unit tests covering all layers
556
+ - Integration examples verified with real AWS deployments (see `tests/integration/`)
557
+ - E2E test script for automated deploy/diff/update/destroy cycles
558
+
559
+ ```bash
560
+ pnpm test # Run unit tests
561
+ pnpm run test:coverage # With coverage report
562
+ ```
563
+
564
+ See [docs/testing.md](docs/testing.md) for integration and E2E testing instructions.
565
+
566
+ ## License
567
+
568
+ Apache 2.0