@go-to-k/cdkd 0.150.1 → 0.150.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/dist/cli.js +50 -9
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -35832,6 +35832,18 @@ async function confirmPrompt$2(prompt) {
|
|
|
35832
35832
|
* itself; we do not try to maintain a closed allowlist here.
|
|
35833
35833
|
*/
|
|
35834
35834
|
const NEVER_IMPORTABLE_TYPES = new Set(["AWS::CDK::Metadata"]);
|
|
35835
|
+
/**
|
|
35836
|
+
* Per-non-root-child lock-acquisition retry budget for the nested-stack
|
|
35837
|
+
* export loop (`runPerStackImportLoop`). `acquireLockWithRetry` retries
|
|
35838
|
+
* `maxRetries` times with `retryDelay` between attempts, so the total wait
|
|
35839
|
+
* ceiling is `maxRetries * retryDelay` ≈ 30s. A genuinely-active concurrent
|
|
35840
|
+
* cdkd run on a child is awaited briefly instead of failing instantly;
|
|
35841
|
+
* `acquireLock`'s own expired-lock auto-cleanup (30-min TTL) covers the
|
|
35842
|
+
* crashed-previous-run case. 30s keeps an interactive `cdkd export` snappy
|
|
35843
|
+
* even across a multi-level tree (each child waits at most this ceiling).
|
|
35844
|
+
*/
|
|
35845
|
+
const CHILD_LOCK_RETRY_MAX_ATTEMPTS = 6;
|
|
35846
|
+
const CHILD_LOCK_RETRY_DELAY_MS = 5e3;
|
|
35835
35847
|
function isNeverImportableType(resourceType) {
|
|
35836
35848
|
if (NEVER_IMPORTABLE_TYPES.has(resourceType)) return true;
|
|
35837
35849
|
if (isCustomResourceType(resourceType)) return true;
|
|
@@ -36430,10 +36442,22 @@ function flattenCdkdStateTreeLeafFirst(tree) {
|
|
|
36430
36442
|
* Top-level cdkd stack names that already conform to CFn's character set
|
|
36431
36443
|
* are passed through unchanged — `cdkd2cfnStackName('MyApp') === 'MyApp'`.
|
|
36432
36444
|
*
|
|
36445
|
+
* The post-substitution result is validated against the same CFn
|
|
36446
|
+
* stack-name regex `parseCfnChildStackNameOverrides` enforces
|
|
36447
|
+
* (`[a-zA-Z][-a-zA-Z0-9]*`). The `~` → `-` swap only handles the v6
|
|
36448
|
+
* separator; a cdkd name that is otherwise CFn-illegal (leading digit /
|
|
36449
|
+
* underscore, embedded `.` or `/`, etc.) would still pass through and
|
|
36450
|
+
* surface as an opaque CFn API rejection deep inside the IMPORT loop.
|
|
36451
|
+
* Throwing here surfaces the problem at orchestrator pre-flight time with
|
|
36452
|
+
* an actionable pointer at the `--cfn-stack-name` / `--cfn-child-stack-name`
|
|
36453
|
+
* override escape hatch.
|
|
36454
|
+
*
|
|
36433
36455
|
* Issue #464 design §9 Q8.
|
|
36434
36456
|
*/
|
|
36435
36457
|
function cdkd2cfnStackName(cdkdStackName) {
|
|
36436
|
-
|
|
36458
|
+
const cfnName = cdkdStackName.replace(/~/g, "-");
|
|
36459
|
+
if (!/^[a-zA-Z][-a-zA-Z0-9]*$/.test(cfnName)) throw new Error(`cdkd stack name '${cdkdStackName}' maps to CFn stack name '${cfnName}', which violates the CloudFormation stack-name constraint [a-zA-Z][-a-zA-Z0-9]* (must start with a letter; no '_', '.', '/', or other special characters). Supply an explicit name via --cfn-stack-name (root) or --cfn-child-stack-name '<cdkdName>=<cfnName>' (nested child).`);
|
|
36460
|
+
return cfnName;
|
|
36437
36461
|
}
|
|
36438
36462
|
/**
|
|
36439
36463
|
* Parse the repeatable `--cfn-child-stack-name <cdkdName>=<cfnName>` CLI
|
|
@@ -37327,10 +37351,15 @@ async function collectImportFailureSummary(cfnClient, stackName) {
|
|
|
37327
37351
|
* `cdkd:nested-export-flip` tag is the only delta). This is the canonical
|
|
37328
37352
|
* AWS workaround for the IMPORT_COMPLETE → UPDATE_COMPLETE transition.
|
|
37329
37353
|
*
|
|
37330
|
-
* Idempotency: each invocation uses a fresh ISO 8601 timestamp
|
|
37331
|
-
*
|
|
37332
|
-
*
|
|
37333
|
-
*
|
|
37354
|
+
* Idempotency: each invocation uses a fresh ISO 8601 timestamp PLUS an
|
|
37355
|
+
* 8-char random UUID slice as the tag value so AWS never returns "No
|
|
37356
|
+
* updates are to be performed" (which would leave the stack stuck in
|
|
37357
|
+
* `IMPORT_COMPLETE`). The timestamp alone is insufficient — two rapid
|
|
37358
|
+
* back-to-back flips on the same stack within the same millisecond (e.g.
|
|
37359
|
+
* a retry after a transient SDK error) would otherwise emit identical
|
|
37360
|
+
* values; the UUID suffix guarantees uniqueness regardless of clock
|
|
37361
|
+
* resolution. The tag accumulates across retries — harmless and easy to
|
|
37362
|
+
* reap manually under the `cdkd:` prefix.
|
|
37334
37363
|
*
|
|
37335
37364
|
* Exported for unit testing.
|
|
37336
37365
|
*/
|
|
@@ -37340,7 +37369,7 @@ async function flipStackToUpdateComplete(cfnClient, cfnStackName) {
|
|
|
37340
37369
|
UsePreviousTemplate: true,
|
|
37341
37370
|
Tags: [{
|
|
37342
37371
|
Key: "cdkd:nested-export-flip",
|
|
37343
|
-
Value: (/* @__PURE__ */ new Date()).toISOString()
|
|
37372
|
+
Value: `${(/* @__PURE__ */ new Date()).toISOString()}-${randomUUID().slice(0, 8)}`
|
|
37344
37373
|
}],
|
|
37345
37374
|
Capabilities: [
|
|
37346
37375
|
"CAPABILITY_IAM",
|
|
@@ -37566,7 +37595,11 @@ async function runPerStackImportLoop(args) {
|
|
|
37566
37595
|
try {
|
|
37567
37596
|
for (const node of leafFirst) {
|
|
37568
37597
|
if (node.stackName === rootStackName) continue;
|
|
37569
|
-
|
|
37598
|
+
try {
|
|
37599
|
+
await deps.lockManager.acquireLockWithRetry(node.stackName, node.region, deps.lockOwner, "export", CHILD_LOCK_RETRY_MAX_ATTEMPTS, CHILD_LOCK_RETRY_DELAY_MS);
|
|
37600
|
+
} catch (err) {
|
|
37601
|
+
throw new Error(`Could not acquire lock for nested-stack child '${node.stackName}' (${node.region}) — another cdkd process held it through the retry window. Wait for it to finish, or run 'cdkd force-unlock ${node.stackName}' if you are certain no other process is active. No CloudFormation changeset has been submitted; cdkd state is unchanged.`, { cause: err instanceof Error ? err : void 0 });
|
|
37602
|
+
}
|
|
37570
37603
|
acquiredChildLocks.push({
|
|
37571
37604
|
stackName: node.stackName,
|
|
37572
37605
|
region: node.region
|
|
@@ -37575,6 +37608,7 @@ async function runPerStackImportLoop(args) {
|
|
|
37575
37608
|
const cfnArnByCdkdName = /* @__PURE__ */ new Map();
|
|
37576
37609
|
const uploadCleanups = [];
|
|
37577
37610
|
const importedStacks = [];
|
|
37611
|
+
const sessionIntrinsicSkipped = /* @__PURE__ */ new Map();
|
|
37578
37612
|
try {
|
|
37579
37613
|
for (let i = 0; i < perStackPlans.length; i++) {
|
|
37580
37614
|
const plan = perStackPlans[i];
|
|
@@ -37588,7 +37622,10 @@ async function runPerStackImportLoop(args) {
|
|
|
37588
37622
|
const parentPlan = perStackPlans.find((p) => p.cdkdName === parentStackName);
|
|
37589
37623
|
if (!parentPlan) throw new Error(`runPerStackImportLoop: child '${plan.cdkdName}' references parent '${parentStackName}' which is not in the per-stack plan list — tree shape is inconsistent.`);
|
|
37590
37624
|
const extracted = extractChildImportParameters(parentPlan.template, parentLogicalId);
|
|
37591
|
-
if (extracted.intrinsicSkipped.length > 0)
|
|
37625
|
+
if (extracted.intrinsicSkipped.length > 0) {
|
|
37626
|
+
sessionIntrinsicSkipped.set(plan.cdkdName, extracted.intrinsicSkipped);
|
|
37627
|
+
logger.warn(` Child '${plan.cdkdName}': skipping intrinsic-valued Parameter(s) ${extracted.intrinsicSkipped.join(", ")} (intrinsic-resolution at leaf-IMPORT time is a deferred follow-up). The child template's Parameter Default values must cover these — otherwise CFn will reject the IMPORT.`);
|
|
37628
|
+
}
|
|
37592
37629
|
stackParameters = extracted.params;
|
|
37593
37630
|
}
|
|
37594
37631
|
const phase1ATemplate = filterTemplateForImport(plan.template, plan.phase1Imports);
|
|
@@ -37695,6 +37732,10 @@ async function runPerStackImportLoop(args) {
|
|
|
37695
37732
|
const lines = stateDeletionFailures.map((f) => ` - cdkd/${f.stackName}/${f.region}/state.json: ${f.reason}`).join("\n");
|
|
37696
37733
|
throw new Error(`${stateDeletionFailures.length} cdkd state record(s) could not be deleted after a successful per-stack IMPORT loop. Every stack in the tree IS CFn-managed (the migration succeeded AWS-side); orphan state remains under:\n${lines}\nRecover with 'cdkd state orphan <stack>' per record.`);
|
|
37697
37734
|
}
|
|
37735
|
+
if (sessionIntrinsicSkipped.size > 0) {
|
|
37736
|
+
const detail = [...sessionIntrinsicSkipped.entries()].map(([cdkdName, params]) => `${cdkdName} (${params.join(", ")})`).join("; ");
|
|
37737
|
+
logger.warn(`${sessionIntrinsicSkipped.size} stack(s) had intrinsic-valued Parameter(s) skipped at IMPORT time: ${detail}. Verify each child template's Parameter Default values cover these before the first 'cdk deploy' — intrinsic resolution at leaf-IMPORT time is a deferred follow-up.`);
|
|
37738
|
+
}
|
|
37698
37739
|
return {
|
|
37699
37740
|
outcome: "success",
|
|
37700
37741
|
importedStacks
|
|
@@ -57588,7 +57629,7 @@ function reorderArgs(argv) {
|
|
|
57588
57629
|
*/
|
|
57589
57630
|
async function main() {
|
|
57590
57631
|
const program = new Command();
|
|
57591
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.150.
|
|
57632
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.150.2");
|
|
57592
57633
|
program.addCommand(createBootstrapCommand());
|
|
57593
57634
|
program.addCommand(createSynthCommand());
|
|
57594
57635
|
program.addCommand(createListCommand());
|