@go-to-k/cdkd 0.91.5 → 0.92.0

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
@@ -453,6 +453,47 @@ Two `orphan` variants at different granularities:
453
453
  Both `cdkd destroy` (synth-driven) and `cdkd state destroy`
454
454
  (state-driven, no synth) delete AWS resources + state.
455
455
 
456
+ ## Stack-name prefix on physical names
457
+
458
+ cdkd prepends the **stack name** to physical names you declare in CDK
459
+ code: `new iam.Role(this, 'CRRole', { roleName: 'my-role' })` in stack
460
+ `MyStack` is created in AWS as `MyStack-my-role`. The prefix protects
461
+ cross-stack uniqueness (two stacks declaring `roleName: 'my-role'`
462
+ otherwise collide on a single AWS account). Pre-PR this behavior was
463
+ **inconsistent**: only IAM Role / User / Group / InstanceProfile and
464
+ ELBv2 LoadBalancer / TargetGroup actually got the prefix; Lambda, S3,
465
+ SNS, SQS, DynamoDB, etc. used the user's declared name as-is.
466
+
467
+ `cdkd deploy --no-prefix-user-supplied-names` opts in to skipping
468
+ the prefix on user-declared physical names, making cdkd consistent
469
+ across all resource types. Off by default for backward compatibility.
470
+
471
+ | | Default (no flag) | `--no-prefix-user-supplied-names` |
472
+ | --- | --- | --- |
473
+ | `new iam.Role({ roleName: 'my-role' })` | `MyStack-my-role` | `my-role` |
474
+ | `new s3.Bucket({ bucketName: 'my-bucket' })` | `my-bucket` (already no prefix — Pattern A) | `my-bucket` (unchanged) |
475
+ | `new iam.Role(...)` (no `roleName`) | `MyStack-CRRole-<hash>` (auto-generated, prefix kept for uniqueness) | `MyStack-CRRole-<hash>` (unchanged) |
476
+
477
+ Resolution chain (highest wins): `--no-prefix-user-supplied-names`
478
+ CLI flag → `CDKD_NO_PREFIX_USER_SUPPLIED_NAMES=true` env var →
479
+ `cdk.json` `context.cdkd.noPrefixUserSuppliedNames: true` → default
480
+ `false`.
481
+
482
+ Affects `cdkd deploy` only. Already-deployed stacks deployed under
483
+ the legacy prefixed-name scheme keep working — the flag only controls
484
+ what AWS resource cdkd creates on **future** deploys. Switching the
485
+ flag mid-flight on an existing stack would propose REPLACEMENT on
486
+ every Pattern B resource (the existing AWS resource has the prefixed
487
+ name; the new template intent has the un-prefixed name).
488
+
489
+ Surfaced by [PR #285 `cdkd export`](https://github.com/go-to-k/cdkd/pull/285)
490
+ where the CFn IMPORT changeset's identifier check would fail on a
491
+ synth `RoleName: 'my-role'` vs the AWS-deployed `MyStack-my-role`;
492
+ the export command currently overlays `ResourceIdentifier` onto
493
+ `Properties` to bridge the gap. A future major-version PR will flip
494
+ the default of `--no-prefix-user-supplied-names` to `true`, after
495
+ which the overlay can be dropped.
496
+
456
497
  ## `--remove-protection`: one-shot bypass for protected resources
457
498
 
458
499
  CDK's `new Stack(app, 'X', { terminationProtection: true })` is honored
package/dist/cli.js CHANGED
@@ -17823,10 +17823,23 @@ function withStackName(stackName, fn) {
17823
17823
  function getCurrentStackName() {
17824
17824
  return stackNameStore.getStore();
17825
17825
  }
17826
+ var skipPrefixStore = new AsyncLocalStorage();
17827
+ function withSkipPrefix(skip, fn) {
17828
+ return skipPrefixStore.run(skip, fn);
17829
+ }
17830
+ function getCurrentSkipPrefix() {
17831
+ return skipPrefixStore.getStore() ?? false;
17832
+ }
17826
17833
  function generateResourceName(name, options) {
17827
- const { maxLength, lowercase = false, allowedPattern = /[^a-zA-Z0-9-]/g } = options;
17834
+ const {
17835
+ maxLength,
17836
+ lowercase = false,
17837
+ allowedPattern = /[^a-zA-Z0-9-]/g,
17838
+ userSupplied = false
17839
+ } = options;
17828
17840
  const currentStackName = stackNameStore.getStore();
17829
- const fullName = currentStackName ? `${currentStackName}-${name}` : name;
17841
+ const shouldPrefix = currentStackName && !(userSupplied && getCurrentSkipPrefix());
17842
+ const fullName = shouldPrefix ? `${currentStackName}-${name}` : name;
17830
17843
  let sanitized = lowercase ? fullName.toLowerCase() : fullName;
17831
17844
  sanitized = sanitized.replace(allowedPattern, "-");
17832
17845
  sanitized = sanitized.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
@@ -17838,6 +17851,12 @@ function generateResourceName(name, options) {
17838
17851
  const prefix = sanitized.substring(0, maxPrefixLength).replace(/-+$/, "");
17839
17852
  return `${prefix}-${hash}`;
17840
17853
  }
17854
+ function generateResourceNameWithFallback(userSuppliedName, logicalId, options) {
17855
+ if (userSuppliedName !== void 0 && userSuppliedName !== "") {
17856
+ return generateResourceName(userSuppliedName, { ...options, userSupplied: true });
17857
+ }
17858
+ return generateResourceName(logicalId, { ...options, userSupplied: false });
17859
+ }
17841
17860
  var FALLBACK_NAME_RULES = {
17842
17861
  "AWS::S3::Bucket": { nameProperty: "BucketName", options: { maxLength: 63, lowercase: true } },
17843
17862
  "AWS::SQS::Queue": { nameProperty: "QueueName", options: { maxLength: 80 } },
@@ -23368,6 +23387,10 @@ var deployOptions = [
23368
23387
  "--no-capture-observed-state",
23369
23388
  "Skip capturing AWS-current properties after each create/update (adds a fire-and-forget readCurrentState per resource so cdkd drift can compare against the real deploy-time AWS snapshot instead of the template). On by default. Disable when deploy speed matters more than rich drift detection \u2014 falls back to comparing against template properties (the pre-v3 behavior)."
23370
23389
  ),
23390
+ new Option(
23391
+ "--no-prefix-user-supplied-names",
23392
+ 'Do NOT prepend the stack name to physical names the user explicitly supplied in their CDK code (e.g. `new iam.Role(this, "X", { roleName: "my-role" })` \u2192 AWS resource named `my-role` instead of `MyStack-my-role`). Auto-generated-name resources (where the user did not declare a physical name) keep the prefix unchanged. Off by default for backward compatibility; enable via this flag, CDKD_NO_PREFIX_USER_SUPPLIED_NAMES=true, or cdk.json context.cdkd.noPrefixUserSuppliedNames=true. Applies to `cdkd deploy` only.'
23393
+ ),
23371
23394
  noWaitOption,
23372
23395
  aggressiveVpcParallelOption,
23373
23396
  new Option(
@@ -23512,6 +23535,19 @@ function resolveCaptureObservedState(cliValue) {
23512
23535
  return v;
23513
23536
  return true;
23514
23537
  }
23538
+ function resolveSkipPrefix(cliValue) {
23539
+ if (cliValue === false)
23540
+ return true;
23541
+ const envValue = process.env["CDKD_NO_PREFIX_USER_SUPPLIED_NAMES"];
23542
+ if (envValue === "true")
23543
+ return true;
23544
+ const cdkJson = loadCdkJson();
23545
+ const cdkdContext = cdkJson?.context?.["cdkd"];
23546
+ const v = cdkdContext?.["noPrefixUserSuppliedNames"];
23547
+ if (typeof v === "boolean" && v === true)
23548
+ return true;
23549
+ return false;
23550
+ }
23515
23551
  function resolveStateBucketWithSource(cliBucket) {
23516
23552
  if (cliBucket)
23517
23553
  return { bucket: cliBucket, source: "cli-flag" };
@@ -27789,8 +27825,9 @@ var IAMRoleProvider = class {
27789
27825
  */
27790
27826
  async create(logicalId, resourceType, properties) {
27791
27827
  this.logger.debug(`Creating IAM role ${logicalId}`);
27792
- const roleName = generateResourceName(
27793
- properties["RoleName"] || logicalId,
27828
+ const roleName = generateResourceNameWithFallback(
27829
+ properties["RoleName"],
27830
+ logicalId,
27794
27831
  { maxLength: 64 }
27795
27832
  );
27796
27833
  const assumeRolePolicyDocument = properties["AssumeRolePolicyDocument"];
@@ -27882,8 +27919,9 @@ var IAMRoleProvider = class {
27882
27919
  */
27883
27920
  async update(logicalId, physicalId, resourceType, properties, previousProperties) {
27884
27921
  this.logger.debug(`Updating IAM role ${logicalId}: ${physicalId}`);
27885
- const newRoleName = generateResourceName(
27886
- properties["RoleName"] || logicalId,
27922
+ const newRoleName = generateResourceNameWithFallback(
27923
+ properties["RoleName"],
27924
+ logicalId,
27887
27925
  { maxLength: 64 }
27888
27926
  );
27889
27927
  const newPath = properties["Path"] || "/";
@@ -29004,8 +29042,9 @@ var IAMInstanceProfileProvider = class {
29004
29042
  */
29005
29043
  async create(logicalId, resourceType, properties) {
29006
29044
  this.logger.debug(`Creating IAM instance profile ${logicalId}`);
29007
- const instanceProfileName = generateResourceName(
29008
- properties["InstanceProfileName"] || logicalId,
29045
+ const instanceProfileName = generateResourceNameWithFallback(
29046
+ properties["InstanceProfileName"],
29047
+ logicalId,
29009
29048
  { maxLength: 128 }
29010
29049
  );
29011
29050
  const path3 = properties["Path"] || "/";
@@ -29399,8 +29438,9 @@ var IAMUserGroupProvider = class {
29399
29438
  // ─── AWS::IAM::User ──────────────────────────────────────────────
29400
29439
  async createUser(logicalId, resourceType, properties) {
29401
29440
  this.logger.debug(`Creating IAM user ${logicalId}`);
29402
- const userName = generateResourceName(
29403
- properties["UserName"] || logicalId,
29441
+ const userName = generateResourceNameWithFallback(
29442
+ properties["UserName"],
29443
+ logicalId,
29404
29444
  { maxLength: 64 }
29405
29445
  );
29406
29446
  try {
@@ -29883,8 +29923,9 @@ var IAMUserGroupProvider = class {
29883
29923
  // ─── AWS::IAM::Group ─────────────────────────────────────────────
29884
29924
  async createGroup(logicalId, resourceType, properties) {
29885
29925
  this.logger.debug(`Creating IAM group ${logicalId}`);
29886
- const groupName = generateResourceName(
29887
- properties["GroupName"] || logicalId,
29926
+ const groupName = generateResourceNameWithFallback(
29927
+ properties["GroupName"],
29928
+ logicalId,
29888
29929
  { maxLength: 128 }
29889
29930
  );
29890
29931
  try {
@@ -48542,9 +48583,11 @@ var ELBv2Provider = class {
48542
48583
  this.logger.debug(`Creating LoadBalancer ${logicalId}`);
48543
48584
  try {
48544
48585
  const tags = this.extractTags(properties);
48545
- const lbName = generateResourceName(properties["Name"] || logicalId, {
48546
- maxLength: 32
48547
- });
48586
+ const lbName = generateResourceNameWithFallback(
48587
+ properties["Name"],
48588
+ logicalId,
48589
+ { maxLength: 32 }
48590
+ );
48548
48591
  const response = await this.getClient().send(
48549
48592
  new CreateLoadBalancerCommand({
48550
48593
  Name: lbName,
@@ -48743,9 +48786,11 @@ var ELBv2Provider = class {
48743
48786
  try {
48744
48787
  const tags = this.extractTags(properties);
48745
48788
  const matcher = properties["Matcher"];
48746
- const tgName = generateResourceName(properties["Name"] || logicalId, {
48747
- maxLength: 32
48748
- });
48789
+ const tgName = generateResourceNameWithFallback(
48790
+ properties["Name"],
48791
+ logicalId,
48792
+ { maxLength: 32 }
48793
+ );
48749
48794
  const response = await this.getClient().send(
48750
48795
  new CreateTargetGroupCommand({
48751
48796
  Name: tgName,
@@ -65438,6 +65483,12 @@ async function deployCommand(stacks, options) {
65438
65483
  if (!options.wait) {
65439
65484
  process.env["CDKD_NO_WAIT"] = "true";
65440
65485
  }
65486
+ const skipPrefix = resolveSkipPrefix(options.prefixUserSuppliedNames);
65487
+ if (skipPrefix) {
65488
+ logger.debug(
65489
+ "Skipping stack-name prefix on user-supplied physical names (--no-prefix-user-supplied-names / CDKD_NO_PREFIX_USER_SUPPLIED_NAMES / cdk.json context.cdkd.noPrefixUserSuppliedNames)"
65490
+ );
65491
+ }
65441
65492
  const app = resolveApp(options.app);
65442
65493
  if (!app) {
65443
65494
  throw new Error(
@@ -65597,6 +65648,9 @@ async function deployCommand(stacks, options) {
65597
65648
  logger.debug(`Work graph: ${summary["asset-publish"]} asset(s), ${summary["stack"]} stack(s)`);
65598
65649
  const bufferStackOutput = targetStacks.length > 1;
65599
65650
  const runStack = async (stackInfo) => {
65651
+ return withSkipPrefix(skipPrefix, () => runStackInner(stackInfo));
65652
+ };
65653
+ const runStackInner = async (stackInfo) => {
65600
65654
  const stackRegion = stackInfo.region || baseRegion;
65601
65655
  logger.info(
65602
65656
  `
@@ -80800,7 +80854,7 @@ function reorderArgs(argv) {
80800
80854
  }
80801
80855
  async function main() {
80802
80856
  const program = new Command18();
80803
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.91.5");
80857
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.92.0");
80804
80858
  program.addCommand(createBootstrapCommand());
80805
80859
  program.addCommand(createSynthCommand());
80806
80860
  program.addCommand(createListCommand());