@go-to-k/cdkd 0.234.0 → 0.235.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.
@@ -1790,6 +1790,23 @@ function resolveUseCdkBootstrapAssets(cliValue) {
1790
1790
  return (loadCdkJson()?.context?.["cdkd"])?.["useCdkBootstrapAssets"] === true;
1791
1791
  }
1792
1792
  /**
1793
+ * Resolve the effective "auto-create cdkd asset storage on first deploy into
1794
+ * an un-opted-in region" value (issue #1007).
1795
+ *
1796
+ * Mirrors {@link resolveCaptureObservedState}'s `--no-X` shape: Commander
1797
+ * reports `--no-auto-asset-storage` as `autoAssetStorage: false`, and the
1798
+ * implicit default is `true` — so the cdk.json fallback
1799
+ * (`context.cdkd.autoAssetStorage`, boolean) only fires when the CLI value
1800
+ * is the implicit default. Priority: CLI `false` wins > cdk.json boolean >
1801
+ * default `true`.
1802
+ */
1803
+ function resolveAutoAssetStorage(cliValue) {
1804
+ if (cliValue === false) return false;
1805
+ const v = (loadCdkJson()?.context?.["cdkd"])?.["autoAssetStorage"];
1806
+ if (typeof v === "boolean") return v;
1807
+ return true;
1808
+ }
1809
+ /**
1793
1810
  * Pre-parse argv walk that surfaces the deprecation warning when the
1794
1811
  * user explicitly passes the legacy `--no-prefix-user-supplied-names`
1795
1812
  * flag. Commander's auto-negation of `--prefix-user-supplied-names`
@@ -3166,25 +3183,31 @@ async function ensureAssetStorage(options) {
3166
3183
  * are cached per region for the process lifetime (the marker read is one
3167
3184
  * GetObject against a bucket every deploy already reads — design §4.1).
3168
3185
  *
3169
- * In legacy mode, one `logger.info` line per invocation mentions the
3170
- * `cdk gc` hazard and the `cdkd bootstrap` opt-in (info, not warn — existing
3171
- * users are not doing anything wrong; design §12.2).
3186
+ * In legacy mode, one `logger.info` line per legacy region per invocation
3187
+ * mentions the `cdk gc` hazard and the exact `cdkd bootstrap --region <r>`
3188
+ * opt-in command (info, not warn — existing users are not doing anything
3189
+ * wrong; design §12.2). Per-region because opt-in is keyed by each stack's
3190
+ * deploy region: a multi-region app can be opted in for one region and
3191
+ * legacy for another, and a region-less notice reads as a false negative to
3192
+ * a user who just bootstrapped a different region.
3172
3193
  */
3173
3194
  var AssetModeResolver = class {
3174
3195
  logger = getLogger().child("AssetMode");
3175
3196
  cache = /* @__PURE__ */ new Map();
3176
- legacyNoticeShown = false;
3197
+ legacyNoticeShownRegions = /* @__PURE__ */ new Set();
3177
3198
  stateBackend;
3178
3199
  accountId;
3179
3200
  profile;
3180
3201
  useCdkBootstrapAssets;
3181
3202
  suppressLegacyNotice;
3203
+ autoCreate;
3182
3204
  constructor(stateBackend, accountId, opts = {}) {
3183
3205
  this.stateBackend = stateBackend;
3184
3206
  this.accountId = accountId;
3185
3207
  this.profile = opts.profile;
3186
3208
  this.useCdkBootstrapAssets = opts.useCdkBootstrapAssets ?? false;
3187
3209
  this.suppressLegacyNotice = opts.suppressLegacyNotice ?? false;
3210
+ this.autoCreate = opts.autoCreate;
3188
3211
  }
3189
3212
  /**
3190
3213
  * Resolve the asset mode for a deploy region. Concurrent callers for the
@@ -3205,9 +3228,13 @@ var AssetModeResolver = class {
3205
3228
  const markerKey = getBootstrapMarkerKey(region);
3206
3229
  const body = await this.stateBackend.getRawObject(markerKey);
3207
3230
  if (body === null) {
3208
- if (!this.legacyNoticeShown && !this.suppressLegacyNotice) {
3209
- this.legacyNoticeShown = true;
3210
- this.logger.info("Assets are published to the CDK bootstrap bucket/repo, which 'cdk gc' may garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan). Run 'cdkd bootstrap' to create cdkd-owned asset storage that 'cdk gc' never touches.");
3231
+ if (this.autoCreate) {
3232
+ const created = await this.tryAutoCreate(region, markerKey);
3233
+ if (created) return created;
3234
+ }
3235
+ if (!this.legacyNoticeShownRegions.has(region) && !this.suppressLegacyNotice) {
3236
+ this.legacyNoticeShownRegions.add(region);
3237
+ this.logger.info(`Assets for region '${region}' are published to the CDK bootstrap bucket/repo, which 'cdk gc' may garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan). Run 'cdkd bootstrap --region ${region}' to create cdkd-owned asset storage that 'cdk gc' never touches.`);
3211
3238
  }
3212
3239
  return { mode: "legacy" };
3213
3240
  }
@@ -3219,6 +3246,59 @@ var AssetModeResolver = class {
3219
3246
  marker
3220
3247
  };
3221
3248
  }
3249
+ /**
3250
+ * Issue #1007 — first deploy into an un-opted-in region: confirm, then
3251
+ * create the asset bucket + container repo + marker via the same
3252
+ * {@link ensureAssetStorage} `cdkd bootstrap` uses (squatting defense,
3253
+ * idempotent creation, marker-written-last all included). Returns `null`
3254
+ * (= stay legacy) when the user declines or creation fails — the caller
3255
+ * then shows the legacy notice, so the user still learns the exact
3256
+ * `cdkd bootstrap --region <r>` remediation.
3257
+ */
3258
+ async tryAutoCreate(region, markerKey) {
3259
+ let confirmed;
3260
+ try {
3261
+ confirmed = await this.autoCreate.confirm(region);
3262
+ } catch {
3263
+ confirmed = false;
3264
+ }
3265
+ if (!confirmed) return null;
3266
+ let s3Client;
3267
+ let ecrClient;
3268
+ try {
3269
+ s3Client = new S3Client({
3270
+ region,
3271
+ ...this.profile && { profile: this.profile }
3272
+ });
3273
+ ecrClient = new ECRClient({
3274
+ region,
3275
+ ...this.profile && { profile: this.profile }
3276
+ });
3277
+ await ensureAssetStorage({
3278
+ s3Client,
3279
+ ecrClient,
3280
+ stateBackend: this.stateBackend,
3281
+ accountId: this.accountId,
3282
+ region,
3283
+ force: false
3284
+ });
3285
+ const body = await this.stateBackend.getRawObject(markerKey);
3286
+ if (body === null) throw new CdkdError(`bootstrap marker missing at '${markerKey}' right after creation`, "ASSET_STORAGE_MARKER_MISSING");
3287
+ const marker = parseBootstrapMarker(body, markerKey);
3288
+ this.logger.debug(`cdkd asset storage auto-created for region '${region}': ${marker.assetBucket} / ${marker.containerRepo}`);
3289
+ return {
3290
+ mode: "cdkd-assets",
3291
+ marker
3292
+ };
3293
+ } catch (error) {
3294
+ const message = error instanceof Error ? error.message : String(error);
3295
+ this.logger.warn(`Failed to auto-create cdkd asset storage for region '${region}': ${message} Falling back to the CDK bootstrap destinations for this run — run 'cdkd bootstrap --region ${region}' (with S3/ECR create permissions) to opt the region in.`);
3296
+ return null;
3297
+ } finally {
3298
+ s3Client?.destroy();
3299
+ ecrClient?.destroy();
3300
+ }
3301
+ }
3222
3302
  };
3223
3303
 
3224
3304
  //#endregion
@@ -7497,6 +7577,27 @@ async function getAccountInfo(overrideRegion) {
7497
7577
  return cachedAccountInfo;
7498
7578
  }
7499
7579
  }
7580
+ /**
7581
+ * Collect every name referenced (Ref / Fn::Sub placeholder / other intrinsic
7582
+ * argument) by the sections cdkd actually evaluates: Resources, Outputs, and
7583
+ * Conditions. Deliberately excludes `Rules` (assertion-only, never evaluated
7584
+ * by cdkd — CloudFormation evaluates them pre-deployment, cdkd has no
7585
+ * equivalent gate) and `Metadata`. Used to avoid resolving template
7586
+ * parameters nothing consumes.
7587
+ */
7588
+ function collectReferencedParameterNames(template) {
7589
+ const parser = new TemplateParser();
7590
+ const referenced = /* @__PURE__ */ new Set();
7591
+ for (const section of [
7592
+ template.Resources,
7593
+ template.Outputs,
7594
+ template.Conditions
7595
+ ]) {
7596
+ if (!section || typeof section !== "object") continue;
7597
+ for (const name of parser.extractReferences(section)) referenced.add(name);
7598
+ }
7599
+ return referenced;
7600
+ }
7500
7601
  var IntrinsicFunctionResolver = class {
7501
7602
  logger = getLogger().child("IntrinsicFunctionResolver");
7502
7603
  resolverRegion;
@@ -7517,6 +7618,7 @@ var IntrinsicFunctionResolver = class {
7517
7618
  const parameters = {};
7518
7619
  const templateParameters = template.Parameters;
7519
7620
  if (!templateParameters || typeof templateParameters !== "object") return parameters;
7621
+ let referencedNames;
7520
7622
  for (const [name, definition] of Object.entries(templateParameters)) {
7521
7623
  const paramDef = definition;
7522
7624
  if (userParameters && name in userParameters) {
@@ -7529,6 +7631,11 @@ var IntrinsicFunctionResolver = class {
7529
7631
  }
7530
7632
  if ("Default" in paramDef) {
7531
7633
  if (paramDef.Type.startsWith("AWS::SSM::Parameter::Value")) {
7634
+ referencedNames ??= collectReferencedParameterNames(template);
7635
+ if (!referencedNames.has(name)) {
7636
+ this.logger.debug(`Parameter ${name}: skipping SSM resolution (not referenced by Resources/Outputs/Conditions)`);
7637
+ continue;
7638
+ }
7532
7639
  const ssmPath = String(paramDef.Default);
7533
7640
  this.logger.debug(`Parameter ${name}: resolving SSM parameter path ${ssmPath}`);
7534
7641
  const resolved = await this.resolveSSMParameter(ssmPath);
@@ -15736,5 +15843,5 @@ var DeployEngine = class {
15736
15843
  };
15737
15844
 
15738
15845
  //#endregion
15739
- export { AssetManifestLoader as $, DiffCalculator as A, buildAssetRedirectMap as B, findActionableSilentDrops as C, refStateLookupFromResource as D, cfnRefValueFromPhysicalId as E, rebuildClientForBucketRegion as F, BOOTSTRAP_MARKER_PREFIX as G, loadPublishableAssetManifest as H, shouldRetainResource as I, buildDockerImage as J, ensureAssetStorage as K, AssetPublisher as L, TemplateParser as M, LockManager as N, WAFv2WebACLProvider as O, S3StateBackend as P, runDockerStreaming as Q, stringifyValue as R, ProviderRegistry as S, IntrinsicFunctionResolver as T, rewriteTemplateAssetReferences as U, createAssetRedirectResolver as V, AssetModeResolver as W, getDockerCmd as X, formatDockerLoginError as Y, runDockerForeground as Z, green as _, AssemblyReader as _t, withRetry as a, resolveApp as at, IAMRoleProvider as b, computeImplicitDeleteEdges as c, resolveStateBucketWithDefault as ct, isStatefulRecreateTargetSync as d, warnDeprecatedNoPrefixCliFlag as dt, getDockerImageBySourceHash as et, renderStatefulReason as f, CFN_TEMPLATE_BODY_LIMIT as ft, gray as g, uploadCfnTemplate as gt, cyan as h, findLargeInlineResources as ht, withResourceDeadline as i, getLegacyStateBucketName as it, DagBuilder as j, applyRoleArnIfSet as k, extractDeploymentEventError as l, resolveStateBucketWithDefaultAndSource as lt, bold as m, MIGRATE_TMP_PREFIX as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, synthesisStatusMessage as nt, isRetryableTransientError as o, resolveCaptureObservedState as ot, formatResourceLine as p, CFN_TEMPLATE_URL_LIMIT as pt, parseBootstrapMarker as q, DeployEngine as r, getDefaultStateBucketName as rt, IMPLICIT_DELETE_DEPENDENCIES as s, resolveSkipPrefix as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, Synthesizer as tt, MULTI_REGION_RECREATE_BLOCKED_TYPES as u, resolveUseCdkBootstrapAssets as ut, red as v, clearBucketRegionCache as vt, CloudControlProvider as w, collectInlinePolicyNamesManagedBySiblings as x, yellow as y, resolveBucketRegion as yt, WorkGraph as z };
15740
- //# sourceMappingURL=deploy-engine-Dj9mXUQa.js.map
15846
+ export { AssetManifestLoader as $, DiffCalculator as A, buildAssetRedirectMap as B, findActionableSilentDrops as C, refStateLookupFromResource as D, cfnRefValueFromPhysicalId as E, rebuildClientForBucketRegion as F, BOOTSTRAP_MARKER_PREFIX as G, loadPublishableAssetManifest as H, shouldRetainResource as I, buildDockerImage as J, ensureAssetStorage as K, AssetPublisher as L, TemplateParser as M, LockManager as N, WAFv2WebACLProvider as O, S3StateBackend as P, runDockerStreaming as Q, stringifyValue as R, ProviderRegistry as S, IntrinsicFunctionResolver as T, rewriteTemplateAssetReferences as U, createAssetRedirectResolver as V, AssetModeResolver as W, getDockerCmd as X, formatDockerLoginError as Y, runDockerForeground as Z, green as _, uploadCfnTemplate as _t, withRetry as a, resolveApp as at, IAMRoleProvider as b, resolveBucketRegion as bt, computeImplicitDeleteEdges as c, resolveSkipPrefix as ct, isStatefulRecreateTargetSync as d, resolveUseCdkBootstrapAssets as dt, getDockerImageBySourceHash as et, renderStatefulReason as f, warnDeprecatedNoPrefixCliFlag as ft, gray as g, findLargeInlineResources as gt, cyan as h, MIGRATE_TMP_PREFIX as ht, withResourceDeadline as i, getLegacyStateBucketName as it, DagBuilder as j, applyRoleArnIfSet as k, extractDeploymentEventError as l, resolveStateBucketWithDefault as lt, bold as m, CFN_TEMPLATE_URL_LIMIT as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, synthesisStatusMessage as nt, isRetryableTransientError as o, resolveAutoAssetStorage as ot, formatResourceLine as p, CFN_TEMPLATE_BODY_LIMIT as pt, parseBootstrapMarker as q, DeployEngine as r, getDefaultStateBucketName as rt, IMPLICIT_DELETE_DEPENDENCIES as s, resolveCaptureObservedState as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, Synthesizer as tt, MULTI_REGION_RECREATE_BLOCKED_TYPES as u, resolveStateBucketWithDefaultAndSource as ut, red as v, AssemblyReader as vt, CloudControlProvider as w, collectInlinePolicyNamesManagedBySiblings as x, yellow as y, clearBucketRegionCache as yt, WorkGraph as z };
15847
+ //# sourceMappingURL=deploy-engine-Cbi5IzWV.js.map