@go-to-k/cdkd 0.28.0 → 0.28.2
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 +31 -4
- package/dist/cli.js +69 -20
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.28.2.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.28.0.tgz +0 -0
package/README.md
CHANGED
|
@@ -457,8 +457,10 @@ cdkd state show MyStack --json # raw {state, lock} JSON
|
|
|
457
457
|
|
|
458
458
|
# Orphan one or more RESOURCES from cdkd's state (does NOT delete AWS resources).
|
|
459
459
|
# Per-resource, mirrors aws-cdk-cli's `cdk orphan --unstable=orphan`.
|
|
460
|
-
# Synth-driven — needs --app / cdk.json. Construct paths
|
|
461
|
-
#
|
|
460
|
+
# Synth-driven — needs --app / cdk.json. Construct paths use CDK's L2-style form
|
|
461
|
+
# (`<StackName>/<Path/To/Construct>`); the synthesized `/Resource` suffix is
|
|
462
|
+
# matched implicitly. Passing an L2 wrapper that contains multiple CFn resources
|
|
463
|
+
# orphans every child under it (matches upstream's prefix-match semantics).
|
|
462
464
|
cdkd orphan MyStack/MyTable # confirmation prompt (y/N)
|
|
463
465
|
cdkd orphan MyStack/MyTable --yes
|
|
464
466
|
cdkd orphan MyStack/MyTable MyStack/MyBucket # multiple resources, same stack
|
|
@@ -678,6 +680,20 @@ out of a larger stack — for example, you have one S3 bucket that was
|
|
|
678
680
|
created manually that you want cdkd to manage, while the rest of the
|
|
679
681
|
stack will be deployed fresh.
|
|
680
682
|
|
|
683
|
+
**Selective mode is non-destructive.** When state already exists for
|
|
684
|
+
the stack, listed resources are **merged** into it: unlisted entries
|
|
685
|
+
already in state are preserved (no `--force` needed). `--force` is
|
|
686
|
+
only required when a listed override would overwrite a resource
|
|
687
|
+
already in state — that's the one case where the merge is destructive.
|
|
688
|
+
This is the right command for "I have a deployed stack and want to
|
|
689
|
+
adopt one more resource into it":
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
# Existing state has Queue + Topic; add Bucket without affecting them.
|
|
693
|
+
cdkd import MyStack --resource MyBucket=my-bucket-name
|
|
694
|
+
# Resulting state: Queue + Topic (preserved) + Bucket (newly imported).
|
|
695
|
+
```
|
|
696
|
+
|
|
681
697
|
### Mode 3: hybrid (`--auto` with overrides)
|
|
682
698
|
|
|
683
699
|
```bash
|
|
@@ -698,7 +714,18 @@ the rest by tag automatically.
|
|
|
698
714
|
| ----------- | ----------------------------------------------------------------------------- |
|
|
699
715
|
| `--dry-run` | Preview what would be imported. State is NOT written. |
|
|
700
716
|
| `--yes` | Skip the confirmation prompt before writing state. |
|
|
701
|
-
| `--force` |
|
|
717
|
+
| `--force` | Confirm a destructive write to existing state — see below. |
|
|
718
|
+
|
|
719
|
+
`--force` is only needed when the import would lose data:
|
|
720
|
+
|
|
721
|
+
- **Auto / whole-stack mode + existing state**: required. The resource
|
|
722
|
+
map is rebuilt from the template, so any state entry not re-imported
|
|
723
|
+
is dropped.
|
|
724
|
+
- **Selective mode + listed override already in state**: required.
|
|
725
|
+
The listed entry is overwritten with the new physical id.
|
|
726
|
+
- **Selective mode without a conflict (pure merge)**: not required.
|
|
727
|
+
Unlisted state entries are preserved automatically.
|
|
728
|
+
- **No existing state (first-time import)**: not required.
|
|
702
729
|
|
|
703
730
|
### After import
|
|
704
731
|
|
|
@@ -746,7 +773,7 @@ table to predict behavior when migrating from `cdk import`.
|
|
|
746
773
|
| Bootstrap requirement | Bootstrap v12+ (deploy role needs to read the encrypted staging bucket). | cdkd's own state bucket; no CDK bootstrap version requirement. |
|
|
747
774
|
| Resource-type coverage | Whatever [CloudFormation supports for import](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-import-supported-resources.html). | The set of cdkd providers that implement `import()` (see [CLAUDE.md](CLAUDE.md) for the current list). For any other CC-API-supported type, use `--resource <id>=<physical>` to drive the Cloud Control API fallback. The two lists overlap heavily but are not identical. |
|
|
748
775
|
| Confirmation prompt before writing state | n/a (CloudFormation operates atomically). | Yes — cdkd asks before writing the state file. Skip with `--yes`. |
|
|
749
|
-
| `--force` | "Continue even if the diff includes updates or deletions" — about diff strictness. | "
|
|
776
|
+
| `--force` | "Continue even if the diff includes updates or deletions" — about diff strictness. | "Confirm a destructive write to existing state" — required for auto/whole-stack rebuild and for overwriting a listed entry already in state; not required for a pure selective merge. **Same flag name, different meaning.** |
|
|
750
777
|
| `--dry-run` | Implied by `--no-execute` (creates the changeset without executing). | Native: shows the import plan and exits without writing state. |
|
|
751
778
|
|
|
752
779
|
#### Practical implications when migrating from `cdk import`
|
package/dist/cli.js
CHANGED
|
@@ -33391,12 +33391,25 @@ function readCdkPath(resource) {
|
|
|
33391
33391
|
function buildCdkPathIndex(template) {
|
|
33392
33392
|
const index = /* @__PURE__ */ new Map();
|
|
33393
33393
|
for (const [logicalId, resource] of Object.entries(template.Resources)) {
|
|
33394
|
+
if (resource.Type === "AWS::CDK::Metadata")
|
|
33395
|
+
continue;
|
|
33394
33396
|
const path = readCdkPath(resource);
|
|
33395
33397
|
if (path)
|
|
33396
33398
|
index.set(path, logicalId);
|
|
33397
33399
|
}
|
|
33398
33400
|
return index;
|
|
33399
33401
|
}
|
|
33402
|
+
function resolveCdkPathToLogicalIds(input, index) {
|
|
33403
|
+
const seen = /* @__PURE__ */ new Map();
|
|
33404
|
+
const prefix = `${input}/`;
|
|
33405
|
+
for (const [path, logicalId] of index) {
|
|
33406
|
+
if (path === input || path.startsWith(prefix)) {
|
|
33407
|
+
if (!seen.has(logicalId))
|
|
33408
|
+
seen.set(logicalId, path);
|
|
33409
|
+
}
|
|
33410
|
+
}
|
|
33411
|
+
return [...seen.entries()].map(([logicalId, cdkPath]) => ({ logicalId, cdkPath }));
|
|
33412
|
+
}
|
|
33400
33413
|
|
|
33401
33414
|
// src/analyzer/orphan-rewriter.ts
|
|
33402
33415
|
var AttributeFetcher = class {
|
|
@@ -33952,19 +33965,20 @@ function resolveConstructPaths(paths, stacks) {
|
|
|
33952
33965
|
`All construct paths must reference the same stack. Got '${stack.stackName}' and '${candidate.stackName}'. Run 'cdkd orphan' once per stack.`
|
|
33953
33966
|
);
|
|
33954
33967
|
}
|
|
33955
|
-
const cdkPath = p;
|
|
33956
33968
|
const index = buildCdkPathIndex(candidate.template);
|
|
33957
|
-
const
|
|
33958
|
-
if (
|
|
33969
|
+
const matches = resolveCdkPathToLogicalIds(p, index);
|
|
33970
|
+
if (matches.length === 0) {
|
|
33959
33971
|
const available = [...index.keys()].sort().join("\n ");
|
|
33960
33972
|
throw new Error(
|
|
33961
|
-
`Construct path '${
|
|
33973
|
+
`Construct path '${p}' not found in template for stack '${candidate.stackName}'.
|
|
33962
33974
|
Available paths:
|
|
33963
33975
|
${available}`
|
|
33964
33976
|
);
|
|
33965
33977
|
}
|
|
33966
|
-
|
|
33967
|
-
logicalIds.
|
|
33978
|
+
for (const { logicalId } of matches) {
|
|
33979
|
+
if (!logicalIds.includes(logicalId)) {
|
|
33980
|
+
logicalIds.push(logicalId);
|
|
33981
|
+
}
|
|
33968
33982
|
}
|
|
33969
33983
|
}
|
|
33970
33984
|
if (!stack) {
|
|
@@ -35259,12 +35273,6 @@ async function importCommand(stackArg, options) {
|
|
|
35259
35273
|
}
|
|
35260
35274
|
const targetRegion = stackInfo.region || region;
|
|
35261
35275
|
logger.info(`Target stack: ${stackInfo.stackName} (${targetRegion})`);
|
|
35262
|
-
const existing = await stateBackend.stateExists(stackInfo.stackName, targetRegion);
|
|
35263
|
-
if (existing && !options.force) {
|
|
35264
|
-
throw new Error(
|
|
35265
|
-
`State already exists for stack '${stackInfo.stackName}' (${targetRegion}). Pass --force to overwrite. (cdkd state import rebuilds the resource map from AWS, so the existing state \u2014 including any drift you've manually edited \u2014 will be lost.)`
|
|
35266
|
-
);
|
|
35267
|
-
}
|
|
35268
35276
|
const overrides = parseResourceOverrides(
|
|
35269
35277
|
options.resource,
|
|
35270
35278
|
options.resourceMapping,
|
|
@@ -35279,6 +35287,34 @@ async function importCommand(stackArg, options) {
|
|
|
35279
35287
|
`Selective mode: only importing the ${overrides.size} resource(s) you listed (${[...overrides.keys()].join(", ")}). Pass --auto to also tag-import the rest.`
|
|
35280
35288
|
);
|
|
35281
35289
|
}
|
|
35290
|
+
const existingResult = await stateBackend.getState(stackInfo.stackName, targetRegion);
|
|
35291
|
+
const existingState = existingResult?.state ?? null;
|
|
35292
|
+
const existingEtag = existingResult?.etag;
|
|
35293
|
+
const migrationPending = existingResult?.migrationPending ?? false;
|
|
35294
|
+
if (existingState) {
|
|
35295
|
+
if (!selectiveMode) {
|
|
35296
|
+
if (!options.force) {
|
|
35297
|
+
throw new Error(
|
|
35298
|
+
`State already exists for stack '${stackInfo.stackName}' (${targetRegion}). Auto / whole-stack import rebuilds the entire resource map from the template, which would drop any state entry not re-imported. Pass --force to confirm. To add specific resources without affecting unlisted ones, use --resource <id>=<physicalId> (selective merge \u2014 no --force needed).`
|
|
35299
|
+
);
|
|
35300
|
+
}
|
|
35301
|
+
} else {
|
|
35302
|
+
const conflicts = [...overrides.keys()].filter(
|
|
35303
|
+
(id) => Object.prototype.hasOwnProperty.call(existingState.resources, id)
|
|
35304
|
+
);
|
|
35305
|
+
if (conflicts.length > 0 && !options.force) {
|
|
35306
|
+
throw new Error(
|
|
35307
|
+
`Selective import would overwrite resource(s) already in state: ${conflicts.join(", ")}. Pass --force to confirm the overwrite, or remove these IDs from --resource / --resource-mapping.`
|
|
35308
|
+
);
|
|
35309
|
+
}
|
|
35310
|
+
const preservedCount = Object.keys(existingState.resources).filter(
|
|
35311
|
+
(id) => !overrides.has(id)
|
|
35312
|
+
).length;
|
|
35313
|
+
logger.info(
|
|
35314
|
+
`Merging into existing state for ${stackInfo.stackName} (${targetRegion}): preserving ${preservedCount} unlisted resource(s)` + (conflicts.length > 0 ? `, overwriting ${conflicts.length} listed entry(ies)` : "")
|
|
35315
|
+
);
|
|
35316
|
+
}
|
|
35317
|
+
}
|
|
35282
35318
|
const template = stackInfo.template;
|
|
35283
35319
|
const templateParser = new TemplateParser();
|
|
35284
35320
|
const resources = collectImportableResources(template);
|
|
@@ -35329,8 +35365,12 @@ async function importCommand(stackArg, options) {
|
|
|
35329
35365
|
return;
|
|
35330
35366
|
}
|
|
35331
35367
|
if (!options.yes) {
|
|
35368
|
+
const importedCount = importedRows.length;
|
|
35369
|
+
const preservedCount = selectiveMode && existingState ? Object.keys(existingState.resources).filter((id) => !overrides.has(id)).length : 0;
|
|
35370
|
+
const totalAfter = importedCount + preservedCount;
|
|
35371
|
+
const breakdown = preservedCount > 0 ? ` (${importedCount} new/overwritten + ${preservedCount} preserved)` : "";
|
|
35332
35372
|
const ok = await confirmPrompt3(
|
|
35333
|
-
`Write state for ${stackInfo.stackName} (${targetRegion}) with ${
|
|
35373
|
+
`Write state for ${stackInfo.stackName} (${targetRegion}) with ${totalAfter} resource(s)${breakdown}?`
|
|
35334
35374
|
);
|
|
35335
35375
|
if (!ok) {
|
|
35336
35376
|
logger.info("Import cancelled.");
|
|
@@ -35342,9 +35382,18 @@ async function importCommand(stackArg, options) {
|
|
|
35342
35382
|
targetRegion,
|
|
35343
35383
|
rows,
|
|
35344
35384
|
templateParser,
|
|
35345
|
-
template
|
|
35385
|
+
template,
|
|
35386
|
+
existingState,
|
|
35387
|
+
selectiveMode
|
|
35346
35388
|
);
|
|
35347
|
-
|
|
35389
|
+
const saveOptions = {};
|
|
35390
|
+
if (existingEtag) {
|
|
35391
|
+
saveOptions.expectedEtag = existingEtag;
|
|
35392
|
+
}
|
|
35393
|
+
if (migrationPending) {
|
|
35394
|
+
saveOptions.migrateLegacy = true;
|
|
35395
|
+
}
|
|
35396
|
+
await stateBackend.saveState(stackInfo.stackName, targetRegion, stackState, saveOptions);
|
|
35348
35397
|
logger.info(`\u2713 State written: ${stackInfo.stackName} (${targetRegion})`);
|
|
35349
35398
|
logger.info(
|
|
35350
35399
|
` ${importedRows.length} resource(s) imported. Run 'cdkd diff' to see how the imported state lines up with the template.`
|
|
@@ -35500,8 +35549,8 @@ function collectImportableResources(template) {
|
|
|
35500
35549
|
}
|
|
35501
35550
|
return out;
|
|
35502
35551
|
}
|
|
35503
|
-
function buildStackState(stackName, region, rows, templateParser, template) {
|
|
35504
|
-
const resources = {};
|
|
35552
|
+
function buildStackState(stackName, region, rows, templateParser, template, existingState, selectiveMode) {
|
|
35553
|
+
const resources = selectiveMode && existingState ? { ...existingState.resources } : {};
|
|
35505
35554
|
for (const row of rows) {
|
|
35506
35555
|
if (row.outcome !== "imported" || !row.physicalId)
|
|
35507
35556
|
continue;
|
|
@@ -35522,7 +35571,7 @@ function buildStackState(stackName, region, rows, templateParser, template) {
|
|
|
35522
35571
|
stackName,
|
|
35523
35572
|
region,
|
|
35524
35573
|
resources,
|
|
35525
|
-
outputs: {},
|
|
35574
|
+
outputs: existingState?.outputs ?? {},
|
|
35526
35575
|
lastModified: Date.now()
|
|
35527
35576
|
};
|
|
35528
35577
|
}
|
|
@@ -35597,7 +35646,7 @@ function createImportCommand() {
|
|
|
35597
35646
|
false
|
|
35598
35647
|
).option("--dry-run", "Show planned imports without writing state", false).option(
|
|
35599
35648
|
"--force",
|
|
35600
|
-
"
|
|
35649
|
+
"Confirm a destructive write to existing state. Required for auto / whole-stack import when state already exists (rebuilds the entire resource map). Also required in selective mode if a listed override would overwrite a resource already in state. Not needed for a pure selective merge (adding new resources without touching unlisted entries).",
|
|
35601
35650
|
false
|
|
35602
35651
|
).action(withErrorHandling(importCommand));
|
|
35603
35652
|
[...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
|
|
@@ -35636,7 +35685,7 @@ function reorderArgs(argv) {
|
|
|
35636
35685
|
}
|
|
35637
35686
|
async function main() {
|
|
35638
35687
|
const program = new Command13();
|
|
35639
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.28.
|
|
35688
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.28.2");
|
|
35640
35689
|
program.addCommand(createBootstrapCommand());
|
|
35641
35690
|
program.addCommand(createSynthCommand());
|
|
35642
35691
|
program.addCommand(createListCommand());
|