@go-to-k/cdkd 0.91.3 → 0.91.4

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 CHANGED
@@ -79615,6 +79615,7 @@ import { Command as Command17 } from "commander";
79615
79615
  import {
79616
79616
  CreateChangeSetCommand,
79617
79617
  DescribeChangeSetCommand,
79618
+ DescribeStackEventsCommand,
79618
79619
  ExecuteChangeSetCommand,
79619
79620
  DescribeStacksCommand as DescribeStacksCommand2,
79620
79621
  DescribeTypeCommand,
@@ -79631,10 +79632,13 @@ var NEVER_IMPORTABLE_TYPES = /* @__PURE__ */ new Set([
79631
79632
  function isNeverImportableType(resourceType) {
79632
79633
  if (NEVER_IMPORTABLE_TYPES.has(resourceType))
79633
79634
  return true;
79634
- if (resourceType.startsWith("Custom::"))
79635
+ if (isCustomResourceType(resourceType))
79635
79636
  return true;
79636
79637
  return false;
79637
79638
  }
79639
+ function isCustomResourceType(resourceType) {
79640
+ return resourceType === "AWS::CloudFormation::CustomResource" || resourceType.startsWith("Custom::");
79641
+ }
79638
79642
  var PRIMARY_IDENTIFIER_FALLBACK = {
79639
79643
  "AWS::S3::Bucket": "BucketName",
79640
79644
  "AWS::IAM::Role": "RoleName",
@@ -79898,6 +79902,12 @@ async function exportCommand(stackArg, options) {
79898
79902
  );
79899
79903
  }
79900
79904
  const phase1Template = filterTemplateForImport(template, phase1Imports);
79905
+ const injectedCount = injectDeletionPolicyForImport(phase1Template);
79906
+ if (injectedCount > 0) {
79907
+ logger.info(
79908
+ `Injected DeletionPolicy: Retain on ${injectedCount} resource(s) without an explicit DeletionPolicy (required by CFn IMPORT). The first \`cdk deploy\` after export will reset each to your CDK-declared value.`
79909
+ );
79910
+ }
79901
79911
  await executeImportChangeSet(
79902
79912
  awsClients.cloudFormation,
79903
79913
  cfnStackName,
@@ -80029,7 +80039,7 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
80029
80039
  }
80030
80040
  }
80031
80041
  function isPhase2CreatableType(resourceType) {
80032
- return resourceType.startsWith("Custom::");
80042
+ return isCustomResourceType(resourceType);
80033
80043
  }
80034
80044
  async function buildImportPlan(state, template, cfnClient) {
80035
80045
  const templateResources = template["Resources"];
@@ -80211,32 +80221,49 @@ function resolveTemplateParameters(template, userOverrides) {
80211
80221
  }
80212
80222
  return { parameters, missing };
80213
80223
  }
80224
+ function injectDeletionPolicyForImport(template) {
80225
+ const resources = template["Resources"];
80226
+ if (!resources || typeof resources !== "object" || Array.isArray(resources)) {
80227
+ return 0;
80228
+ }
80229
+ let injected = 0;
80230
+ for (const [, resource] of Object.entries(resources)) {
80231
+ if (!resource || typeof resource !== "object" || Array.isArray(resource))
80232
+ continue;
80233
+ const r = resource;
80234
+ if (r["DeletionPolicy"] === void 0) {
80235
+ r["DeletionPolicy"] = "Retain";
80236
+ injected++;
80237
+ }
80238
+ }
80239
+ return injected;
80240
+ }
80214
80241
  function filterTemplateForImport(template, plan) {
80215
- const allow = new Set(plan.map((p) => p.logicalId));
80242
+ const allow = new Map(plan.map((p) => [p.logicalId, p]));
80216
80243
  const original = template["Resources"];
80217
80244
  const filteredResources = {};
80218
80245
  for (const [logicalId, resource] of Object.entries(original)) {
80219
- if (allow.has(logicalId)) {
80220
- filteredResources[logicalId] = resource;
80221
- }
80246
+ const entry = allow.get(logicalId);
80247
+ if (!entry)
80248
+ continue;
80249
+ filteredResources[logicalId] = overlayResourceIdentifierOnProperties(resource, entry);
80222
80250
  }
80223
80251
  const result = { ...template, Resources: filteredResources };
80224
- const outputs = template["Outputs"];
80225
- if (outputs && typeof outputs === "object" && !Array.isArray(outputs)) {
80226
- const filteredOutputs = {};
80227
- for (const [name, output] of Object.entries(outputs)) {
80228
- if (referencesOnly(output, allow)) {
80229
- filteredOutputs[name] = output;
80230
- }
80231
- }
80232
- if (Object.keys(filteredOutputs).length > 0) {
80233
- result["Outputs"] = filteredOutputs;
80234
- } else {
80235
- delete result["Outputs"];
80236
- }
80237
- }
80252
+ delete result["Outputs"];
80238
80253
  return result;
80239
80254
  }
80255
+ function overlayResourceIdentifierOnProperties(resource, entry) {
80256
+ if (!resource || typeof resource !== "object" || Array.isArray(resource)) {
80257
+ return resource;
80258
+ }
80259
+ const r = resource;
80260
+ const existingProperties = r["Properties"];
80261
+ const properties = existingProperties && typeof existingProperties === "object" && !Array.isArray(existingProperties) ? { ...existingProperties } : {};
80262
+ for (const [field, value] of Object.entries(entry.resourceIdentifier)) {
80263
+ properties[field] = value;
80264
+ }
80265
+ return { ...r, Properties: properties };
80266
+ }
80240
80267
  function reportDriftBaselineGaps(state, logger) {
80241
80268
  const entries = Object.entries(state.resources ?? {});
80242
80269
  if (entries.length === 0)
@@ -80307,29 +80334,6 @@ function walkForGetStackOutput(node, path3, emit) {
80307
80334
  walkForGetStackOutput(value, path3 ? `${path3}.${key}` : key, emit);
80308
80335
  }
80309
80336
  }
80310
- function referencesOnly(node, allow) {
80311
- if (!node || typeof node !== "object")
80312
- return true;
80313
- if (Array.isArray(node)) {
80314
- return node.every((item) => referencesOnly(item, allow));
80315
- }
80316
- for (const [key, value] of Object.entries(node)) {
80317
- if (key === "Ref" && typeof value === "string") {
80318
- if (!allow.has(value))
80319
- return false;
80320
- continue;
80321
- }
80322
- if (key === "Fn::GetAtt") {
80323
- const target = Array.isArray(value) && typeof value[0] === "string" ? value[0] : typeof value === "string" ? value.split(".")[0] : void 0;
80324
- if (target && !allow.has(target))
80325
- return false;
80326
- continue;
80327
- }
80328
- if (!referencesOnly(value, allow))
80329
- return false;
80330
- }
80331
- return true;
80332
- }
80333
80337
  function printPlan(plan, cfnStackName) {
80334
80338
  const logger = getLogger();
80335
80339
  logger.info("");
@@ -80407,11 +80411,41 @@ async function executeImportChangeSet(cfnClient, stackName, template, plan, para
80407
80411
  { StackName: stackName }
80408
80412
  );
80409
80413
  } catch (err) {
80414
+ const failureSummary = await collectImportFailureSummary(cfnClient, stackName).catch(() => "");
80410
80415
  await cfnClient.send(new DeleteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })).catch(() => {
80411
80416
  });
80417
+ if (failureSummary) {
80418
+ throw new Error(`IMPORT changeset failed:
80419
+ ${failureSummary}`, { cause: err });
80420
+ }
80412
80421
  throw err;
80413
80422
  }
80414
80423
  }
80424
+ async function collectImportFailureSummary(cfnClient, stackName) {
80425
+ const resp = await cfnClient.send(new DescribeStackEventsCommand({ StackName: stackName }));
80426
+ const events = resp.StackEvents ?? [];
80427
+ const failures = [];
80428
+ const seen = /* @__PURE__ */ new Set();
80429
+ for (const e of events) {
80430
+ if (!e.ResourceStatus || !e.ResourceStatus.endsWith("FAILED"))
80431
+ continue;
80432
+ if (!e.LogicalResourceId)
80433
+ continue;
80434
+ if (seen.has(e.LogicalResourceId))
80435
+ continue;
80436
+ seen.add(e.LogicalResourceId);
80437
+ failures.push({
80438
+ logicalId: e.LogicalResourceId,
80439
+ type: e.ResourceType ?? "<unknown>",
80440
+ reason: e.ResourceStatusReason ?? "<no reason reported>"
80441
+ });
80442
+ if (failures.length >= 5)
80443
+ break;
80444
+ }
80445
+ if (failures.length === 0)
80446
+ return "";
80447
+ return failures.map((f) => ` - ${f.logicalId} (${f.type}): ${f.reason}`).join("\n");
80448
+ }
80415
80449
  async function executeUpdateChangeSet(cfnClient, stackName, template, parameters) {
80416
80450
  const logger = getLogger();
80417
80451
  const changeSetName = `cdkd-phase2-${Date.now()}`;
@@ -80593,7 +80627,7 @@ function reorderArgs(argv) {
80593
80627
  }
80594
80628
  async function main() {
80595
80629
  const program = new Command18();
80596
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.91.3");
80630
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.91.4");
80597
80631
  program.addCommand(createBootstrapCommand());
80598
80632
  program.addCommand(createSynthCommand());
80599
80633
  program.addCommand(createListCommand());