@go-to-k/cdkd 0.15.0 → 0.16.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/dist/cli.js CHANGED
@@ -1008,15 +1008,31 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
1008
1008
  const legacyName = getLegacyStateBucketName(accountId, region);
1009
1009
  const probe = new S3Client11({ region: "us-east-1" });
1010
1010
  try {
1011
- if (await bucketExists(probe, newName)) {
1011
+ const newExists = await bucketExists(probe, newName);
1012
+ const legacyExists = await bucketExists(probe, legacyName);
1013
+ if (newExists && legacyExists) {
1014
+ const newHasState = await bucketHasAnyState(probe, newName);
1015
+ if (!newHasState) {
1016
+ const legacyHasState = await bucketHasAnyState(probe, legacyName);
1017
+ if (legacyHasState) {
1018
+ logger.warn(
1019
+ `Both '${newName}' (new default) and '${legacyName}' (legacy default) exist, but the new bucket is empty and the legacy one has state. Reading from legacy. Run \`cdkd state migrate --region ${region}\` to copy the state into the new bucket and stop seeing this warning.`
1020
+ );
1021
+ return { bucket: legacyName, source: "default-legacy" };
1022
+ }
1023
+ }
1024
+ logger.debug(`State bucket: ${newName}`);
1025
+ return { bucket: newName, source: "default" };
1026
+ }
1027
+ if (newExists) {
1012
1028
  logger.debug(`State bucket: ${newName}`);
1013
1029
  return { bucket: newName, source: "default" };
1014
1030
  }
1015
- if (await bucketExists(probe, legacyName)) {
1031
+ if (legacyExists) {
1016
1032
  logger.warn(
1017
1033
  `Using legacy state bucket name '${legacyName}'. The default has changed to '${newName}'. To migrate, run:
1018
1034
 
1019
- cdkd state migrate-bucket --region ${region}
1035
+ cdkd state migrate --region ${region}
1020
1036
 
1021
1037
  (add --remove-legacy to delete the legacy bucket after a successful copy; legacy support will be dropped in a future release.)`
1022
1038
  );
@@ -1029,6 +1045,21 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
1029
1045
  probe.destroy();
1030
1046
  }
1031
1047
  }
1048
+ async function bucketHasAnyState(client, bucketName) {
1049
+ const { ListObjectsV2Command: ListObjectsV2Command5 } = await import("@aws-sdk/client-s3");
1050
+ try {
1051
+ const resp = await client.send(
1052
+ new ListObjectsV2Command5({
1053
+ Bucket: bucketName,
1054
+ Prefix: "cdkd/",
1055
+ MaxKeys: 1
1056
+ })
1057
+ );
1058
+ return (resp.KeyCount ?? 0) > 0;
1059
+ } catch {
1060
+ return true;
1061
+ }
1062
+ }
1032
1063
  async function bucketExists(client, bucketName) {
1033
1064
  const { HeadBucketCommand: HeadBucketCommand5 } = await import("@aws-sdk/client-s3");
1034
1065
  try {
@@ -7010,6 +7041,60 @@ Error: ${err.message || "Unknown error"}`,
7010
7041
  }
7011
7042
  return resourceType.startsWith("AWS::");
7012
7043
  }
7044
+ /**
7045
+ * Adopt an already-deployed resource into cdkd state via Cloud Control API.
7046
+ *
7047
+ * Strategy: explicit-override only.
7048
+ * - With `knownPhysicalId` (from `--resource <id>=<physicalId>` or
7049
+ * `--resource-mapping`): call `GetResource(TypeName, Identifier)`,
7050
+ * parse `ResourceModel` (returned as a JSON string by CC API), and
7051
+ * return its keys as `attributes`.
7052
+ * - Without `knownPhysicalId`: return `null`. CC API has no efficient
7053
+ * `aws:cdk:path`-tag lookup — `ListResources` returns identifiers
7054
+ * only, so tag lookup would require one `GetResource` per resource
7055
+ * in the account, plus per-service tag-API calls (which CC API
7056
+ * doesn't expose uniformly). Cost vs. value isn't worth it; users
7057
+ * who need adoption for CC-API-only resource types should pass
7058
+ * `--resource <id>=<physicalId>` for those resources.
7059
+ *
7060
+ * SDK providers (S3, Lambda, IAM Role, etc.) implement their own
7061
+ * `import` with tag-based auto-lookup; this fallback only kicks in for
7062
+ * resource types that don't have a dedicated SDK provider.
7063
+ */
7064
+ async import(input) {
7065
+ if (!input.knownPhysicalId) {
7066
+ return null;
7067
+ }
7068
+ try {
7069
+ const resp = await this.cloudControlClient.send(
7070
+ new GetResourceCommand2({
7071
+ TypeName: input.resourceType,
7072
+ Identifier: input.knownPhysicalId
7073
+ })
7074
+ );
7075
+ let attributes = {};
7076
+ const raw = resp.ResourceDescription?.Properties;
7077
+ if (typeof raw === "string" && raw.length > 0) {
7078
+ try {
7079
+ const parsed = JSON.parse(raw);
7080
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7081
+ attributes = parsed;
7082
+ }
7083
+ } catch (parseErr) {
7084
+ this.logger.debug(
7085
+ `Failed to parse CC API ResourceModel for ${input.resourceType}/${input.knownPhysicalId}: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}`
7086
+ );
7087
+ }
7088
+ }
7089
+ return { physicalId: input.knownPhysicalId, attributes };
7090
+ } catch (error) {
7091
+ const err = error;
7092
+ if (err.name === "ResourceNotFoundException") {
7093
+ return null;
7094
+ }
7095
+ throw error;
7096
+ }
7097
+ }
7013
7098
  };
7014
7099
 
7015
7100
  // src/provisioning/providers/custom-resource-provider.ts
@@ -30222,7 +30307,7 @@ import {
30222
30307
  } from "@aws-sdk/client-s3";
30223
30308
  init_aws_clients();
30224
30309
 
30225
- // src/cli/commands/state-migrate-bucket.ts
30310
+ // src/cli/commands/state-migrate.ts
30226
30311
  import * as readline2 from "node:readline/promises";
30227
30312
  import { Command as Command9 } from "commander";
30228
30313
  import {
@@ -30240,7 +30325,7 @@ import {
30240
30325
  } from "@aws-sdk/client-s3";
30241
30326
  import { GetCallerIdentityCommand as GetCallerIdentityCommand8 } from "@aws-sdk/client-sts";
30242
30327
  init_aws_clients();
30243
- async function stateMigrateBucketCommand(options) {
30328
+ async function stateMigrateCommand(options) {
30244
30329
  const logger = getLogger();
30245
30330
  if (options.verbose)
30246
30331
  logger.setLevel("debug");
@@ -30506,8 +30591,8 @@ async function confirmPrompt(prompt) {
30506
30591
  rl.close();
30507
30592
  }
30508
30593
  }
30509
- function createStateMigrateBucketCommand() {
30510
- const cmd = new Command9("migrate-bucket").description(
30594
+ function createStateMigrateCommand() {
30595
+ const cmd = new Command9("migrate").description(
30511
30596
  "Migrate state from the legacy region-suffixed bucket (cdkd-state-{account}-{region}) to the new region-free default (cdkd-state-{account}). Source bucket is kept by default; pass --remove-legacy to delete it after a successful migration."
30512
30597
  ).option(
30513
30598
  "--region <region>",
@@ -30522,7 +30607,7 @@ function createStateMigrateBucketCommand() {
30522
30607
  "--remove-legacy",
30523
30608
  "Delete the source bucket after successful migration. Default: keep it.",
30524
30609
  false
30525
- ).action(withErrorHandling(stateMigrateBucketCommand));
30610
+ ).action(withErrorHandling(stateMigrateCommand));
30526
30611
  commonOptions.forEach((o) => cmd.addOption(o));
30527
30612
  return cmd;
30528
30613
  }
@@ -31096,7 +31181,7 @@ function formatBucketSource(source) {
31096
31181
  case "default":
31097
31182
  return "default (account ID from STS)";
31098
31183
  case "default-legacy":
31099
- return "default (legacy region-suffixed name; cdkd state migrate-bucket recommended)";
31184
+ return "default (legacy region-suffixed name; cdkd state migrate recommended)";
31100
31185
  }
31101
31186
  }
31102
31187
  async function detectBucketRegion(awsClients, bucket) {
@@ -31211,7 +31296,7 @@ function createStateCommand() {
31211
31296
  cmd.addCommand(createStateShowCommand());
31212
31297
  cmd.addCommand(createStateRmCommand());
31213
31298
  cmd.addCommand(createStateDestroyCommand());
31214
- cmd.addCommand(createStateMigrateBucketCommand());
31299
+ cmd.addCommand(createStateMigrateCommand());
31215
31300
  return cmd;
31216
31301
  }
31217
31302
 
@@ -31576,7 +31661,7 @@ function reorderArgs(argv) {
31576
31661
  }
31577
31662
  async function main() {
31578
31663
  const program = new Command12();
31579
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.15.0");
31664
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.16.1");
31580
31665
  program.addCommand(createBootstrapCommand());
31581
31666
  program.addCommand(createSynthCommand());
31582
31667
  program.addCommand(createListCommand());