@go-to-k/cdkd 0.238.4 → 0.238.6

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
@@ -2,7 +2,7 @@
2
2
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BzO-joNR.js";
3
3
  import { A as withErrorHandling, C as StackHasActiveImportsError, S as ResourceUpdateNotSupportedError, _ as MissingCdkCliError, a as normalizeAwsTagsToCfn, b as ProvisioningError, f as LocalInvokeBuildError$1, i as matchesCdkPath, k as normalizeAwsError, l as CdkdError, m as LocalStartServiceError, n as isTerminationProtectionPropagationError, o as resolveExplicitPhysicalId, p as LocalMigrateError, r as CDK_PATH_TAG, s as assertRegionMatch, t as disableInstanceApiTermination, v as NestedStackChildDirectDestroyError, w as StackTerminationProtectionError, x as ResourceTimeoutError, y as PartialFailureError } from "./ec2-termination-protection-q-WGWgxa.js";
4
4
  import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-B15NAPbL.js";
5
- import { $ as getDockerCmd, A as DiffCalculator, B as buildAssetRedirectMap, C as findActionableSilentDrops, Ct as resolveBucketRegion, D as refStateLookupFromResource, E as cfnRefValueFromPhysicalId, F as rebuildClientForBucketRegion, G as BOOTSTRAP_MARKER_PREFIX, H as loadPublishableAssetManifest, I as shouldRetainResource, J as parseBootstrapMarker, K as ensureAssetStorage, L as AssetPublisher, M as TemplateParser, N as LockManager, O as WAFv2WebACLProvider, P as S3StateBackend, Q as formatDockerLoginError, R as stringifyValue, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as rewriteTemplateAssetReferences, V as createAssetRedirectResolver, W as AssetModeResolver, X as validateContainerRepoName, Y as validateAssetBucketName, Z as buildDockerImage, _ as green, _t as CFN_TEMPLATE_URL_LIMIT, a as withRetry, at as synthesisStatusMessage, b as IAMRoleProvider, bt as uploadCfnTemplate, c as computeImplicitDeleteEdges, ct as resolveApp, d as isStatefulRecreateTargetSync, dt as resolveSkipPrefix, et as runDockerForeground, f as renderStatefulReason, ft as resolveStateBucketWithDefault, g as gray, gt as CFN_TEMPLATE_BODY_LIMIT, h as cyan, ht as warnDeprecatedNoPrefixCliFlag, i as withResourceDeadline, it as Synthesizer, j as DagBuilder, k as applyRoleArnIfSet, l as extractDeploymentEventError, lt as resolveAutoAssetStorage, m as bold, mt as resolveUseCdkBootstrapAssets, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as AssetManifestLoader, o as isRetryableTransientError, ot as getDefaultStateBucketName, p as formatResourceLine, pt as resolveStateBucketWithDefaultAndSource, q as getBootstrapMarkerKey, r as DeployEngine, rt as getDockerImageBySourceHash, s as IMPLICIT_DELETE_DEPENDENCIES, st as getLegacyStateBucketName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as runDockerStreaming, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, ut as resolveCaptureObservedState, v as red, vt as MIGRATE_TMP_PREFIX, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, xt as AssemblyReader, y as yellow, yt as findLargeInlineResources, z as WorkGraph } from "./deploy-engine-CQuZCVUP.js";
5
+ import { $ as getDockerCmd, A as DiffCalculator, B as buildAssetRedirectMap, C as findActionableSilentDrops, Ct as resolveBucketRegion, D as refStateLookupFromResource, E as cfnRefValueFromPhysicalId, F as rebuildClientForBucketRegion, G as BOOTSTRAP_MARKER_PREFIX, H as loadPublishableAssetManifest, I as shouldRetainResource, J as parseBootstrapMarker, K as ensureAssetStorage, L as AssetPublisher, M as TemplateParser, N as LockManager, O as WAFv2WebACLProvider, P as S3StateBackend, Q as formatDockerLoginError, R as stringifyValue, S as ProviderRegistry, T as IntrinsicFunctionResolver, U as rewriteTemplateAssetReferences, V as createAssetRedirectResolver, W as AssetModeResolver, X as validateContainerRepoName, Y as validateAssetBucketName, Z as buildDockerImage, _ as green, _t as CFN_TEMPLATE_URL_LIMIT, a as withRetry, at as synthesisStatusMessage, b as IAMRoleProvider, bt as uploadCfnTemplate, c as computeImplicitDeleteEdges, ct as resolveApp, d as isStatefulRecreateTargetSync, dt as resolveSkipPrefix, et as runDockerForeground, f as renderStatefulReason, ft as resolveStateBucketWithDefault, g as gray, gt as CFN_TEMPLATE_BODY_LIMIT, h as cyan, ht as warnDeprecatedNoPrefixCliFlag, i as withResourceDeadline, it as Synthesizer, j as DagBuilder, k as applyRoleArnIfSet, l as extractDeploymentEventError, lt as resolveAutoAssetStorage, m as bold, mt as resolveUseCdkBootstrapAssets, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as AssetManifestLoader, o as isRetryableTransientError, ot as getDefaultStateBucketName, p as formatResourceLine, pt as resolveStateBucketWithDefaultAndSource, q as getBootstrapMarkerKey, r as DeployEngine, rt as getDockerImageBySourceHash, s as IMPLICIT_DELETE_DEPENDENCIES, st as getLegacyStateBucketName, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as runDockerStreaming, u as MULTI_REGION_RECREATE_BLOCKED_TYPES, ut as resolveCaptureObservedState, v as red, vt as MIGRATE_TMP_PREFIX, w as CloudControlProvider, x as collectInlinePolicyNamesManagedBySiblings, xt as AssemblyReader, y as yellow, yt as findLargeInlineResources, z as WorkGraph } from "./deploy-engine-_tADDg2k.js";
6
6
  import { t as expectedOwnerParam } from "./expected-bucket-owner-BjEfUnkA.js";
7
7
  import { t as ASGProvider } from "./asg-provider-Dbl1_mwT.js";
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -1885,7 +1885,7 @@ const FLUSH_INTERVAL_MS = 2e3;
1885
1885
  const FLUSH_EVENT_THRESHOLD = 50;
1886
1886
  /** Build-time cdkd version, with a dev fallback for non-built contexts. */
1887
1887
  function getCdkdVersion() {
1888
- return "0.238.4";
1888
+ return "0.238.6";
1889
1889
  }
1890
1890
  /**
1891
1891
  * Generate a time-sortable unique run id, e.g.
@@ -46316,12 +46316,18 @@ function formatBucketSource(source) {
46316
46316
  * not-yet-bootstrapped bucket. (Bucket existence is verified separately
46317
46317
  * via {@link setupStateBackend}, so getting here implies the bucket is
46318
46318
  * reachable; `GetBucketLocation` failing is most often a permissions gap.)
46319
+ *
46320
+ * Takes the (region-corrected) S3 client directly — see the issue #1054 note
46321
+ * in {@link stateInfoCommand}. Before that fix this call was made with the
46322
+ * ambient-region client, so a cross-region state bucket silently degraded to
46323
+ * `undefined` ("Region: unknown") here; with the corrected client it now
46324
+ * succeeds and reports the bucket's actual region.
46319
46325
  */
46320
- async function detectBucketRegion(awsClients, bucket) {
46326
+ async function detectBucketRegion(s3, bucket) {
46321
46327
  try {
46322
- const constraint = (await awsClients.s3.send(new GetBucketLocationCommand({
46328
+ const constraint = (await s3.send(new GetBucketLocationCommand({
46323
46329
  Bucket: bucket,
46324
- ...await expectedOwnerParam(awsClients.s3)
46330
+ ...await expectedOwnerParam(s3)
46325
46331
  }))).LocationConstraint;
46326
46332
  if (!constraint) return "us-east-1";
46327
46333
  if (constraint === "EU") return "eu-west-1";
@@ -46342,12 +46348,12 @@ async function detectBucketRegion(awsClients, bucket) {
46342
46348
  * Returns the full set of state-file keys; the count is the unique-stacks
46343
46349
  * tally we want for the `Stacks:` line.
46344
46350
  */
46345
- async function listStateFileKeys(awsClients, bucket, prefix) {
46351
+ async function listStateFileKeys(s3, bucket, prefix) {
46346
46352
  const keys = [];
46347
46353
  let continuationToken;
46348
46354
  const searchPrefix = `${prefix}/`;
46349
46355
  do {
46350
- const resp = await awsClients.s3.send(new ListObjectsV2Command({
46356
+ const resp = await s3.send(new ListObjectsV2Command({
46351
46357
  Bucket: bucket,
46352
46358
  Prefix: searchPrefix,
46353
46359
  ...continuationToken && { ContinuationToken: continuationToken }
@@ -46365,13 +46371,13 @@ async function listStateFileKeys(awsClients, bucket, prefix) {
46365
46371
  * Returns `'unknown'` when no state files exist or parsing fails — we don't
46366
46372
  * want a cosmetic command to crash on an unexpected payload.
46367
46373
  */
46368
- async function readSchemaVersion(awsClients, bucket, keys) {
46374
+ async function readSchemaVersion(s3, bucket, keys) {
46369
46375
  if (keys.length === 0) return "unknown";
46370
46376
  try {
46371
- const resp = await awsClients.s3.send(new GetObjectCommand({
46377
+ const resp = await s3.send(new GetObjectCommand({
46372
46378
  Bucket: bucket,
46373
46379
  Key: keys[0],
46374
- ...await expectedOwnerParam(awsClients.s3)
46380
+ ...await expectedOwnerParam(s3)
46375
46381
  }));
46376
46382
  if (!resp.Body) return "unknown";
46377
46383
  const body = await resp.Body.transformToString();
@@ -46387,13 +46393,13 @@ async function readSchemaVersion(awsClients, bucket, keys) {
46387
46393
  * cosmetic command and should not crash on an unexpected payload (deploy
46388
46394
  * hard-errors on the same marker instead).
46389
46395
  */
46390
- async function listAssetStorageMarkers(awsClients, bucket) {
46396
+ async function listAssetStorageMarkers(s3, bucket) {
46391
46397
  const logger = getLogger();
46392
46398
  const entries = [];
46393
46399
  let continuationToken;
46394
46400
  const keys = [];
46395
46401
  do {
46396
- const resp = await awsClients.s3.send(new ListObjectsV2Command({
46402
+ const resp = await s3.send(new ListObjectsV2Command({
46397
46403
  Bucket: bucket,
46398
46404
  Prefix: BOOTSTRAP_MARKER_PREFIX,
46399
46405
  ...continuationToken && { ContinuationToken: continuationToken }
@@ -46404,10 +46410,10 @@ async function listAssetStorageMarkers(awsClients, bucket) {
46404
46410
  for (const key of keys) {
46405
46411
  const region = key.slice(BOOTSTRAP_MARKER_PREFIX.length, -5);
46406
46412
  try {
46407
- const marker = parseBootstrapMarker(await (await awsClients.s3.send(new GetObjectCommand({
46413
+ const marker = parseBootstrapMarker(await (await s3.send(new GetObjectCommand({
46408
46414
  Bucket: bucket,
46409
46415
  Key: key,
46410
- ...await expectedOwnerParam(awsClients.s3)
46416
+ ...await expectedOwnerParam(s3)
46411
46417
  }))).Body?.transformToString() ?? "", key);
46412
46418
  entries.push({
46413
46419
  region,
@@ -46451,6 +46457,7 @@ async function stateInfoCommand(options) {
46451
46457
  ...options.profile && { profile: options.profile }
46452
46458
  });
46453
46459
  setAwsClients(awsClients);
46460
+ let regionCorrectedS3 = null;
46454
46461
  try {
46455
46462
  const region = options.region || process.env["AWS_REGION"] || "us-east-1";
46456
46463
  const resolved = await resolveStateBucketWithDefaultAndSource(options.stateBucket, region);
@@ -46460,10 +46467,18 @@ async function stateInfoCommand(options) {
46460
46467
  bucket,
46461
46468
  prefix
46462
46469
  }).verifyBucketExists();
46463
- const detectedRegion = await detectBucketRegion(awsClients, bucket);
46464
- const stateFileKeys = await listStateFileKeys(awsClients, bucket, prefix);
46465
- const schemaVersion = await readSchemaVersion(awsClients, bucket, stateFileKeys);
46466
- const assetStorage = await listAssetStorageMarkers(awsClients, bucket);
46470
+ regionCorrectedS3 = await rebuildClientForBucketRegion(awsClients.s3, bucket, {
46471
+ reuseClientCredentials: true,
46472
+ tolerateNonStandardClient: true,
46473
+ onRebuild: ({ bucketRegion, currentRegion }) => {
46474
+ logger.debug(`State bucket '${bucket}' is in '${bucketRegion}' (state-info client was '${String(currentRegion)}'); building a region-corrected S3 client for info reads.`);
46475
+ }
46476
+ });
46477
+ const infoS3 = regionCorrectedS3 ?? awsClients.s3;
46478
+ const detectedRegion = await detectBucketRegion(infoS3, bucket);
46479
+ const stateFileKeys = await listStateFileKeys(infoS3, bucket, prefix);
46480
+ const schemaVersion = await readSchemaVersion(infoS3, bucket, stateFileKeys);
46481
+ const assetStorage = await listAssetStorageMarkers(infoS3, bucket);
46467
46482
  if (options.json) {
46468
46483
  const json = {
46469
46484
  bucket,
@@ -46491,6 +46506,7 @@ async function stateInfoCommand(options) {
46491
46506
  }
46492
46507
  process.stdout.write(`${lines.join("\n")}\n`);
46493
46508
  } finally {
46509
+ regionCorrectedS3?.destroy();
46494
46510
  awsClients.destroy();
46495
46511
  }
46496
46512
  }
@@ -47705,6 +47721,72 @@ async function loadStateForStack(stackName, synthRegion, opts) {
47705
47721
  }
47706
47722
  }
47707
47723
  /**
47724
+ * Best-effort read of the region's asset-storage bootstrap marker
47725
+ * (`s3://{stateBucket}/cdkd-bootstrap/{region}.json`) to recover the
47726
+ * cdkd-owned container-asset ECR repository name (issue #1025). Since
47727
+ * `cdkd bootstrap --container-repo <name>` (issue #1011) the repo can
47728
+ * carry ANY name, so the local resolvers' conventional-prefix regex
47729
+ * cannot classify images published to a custom-named repo — the marker
47730
+ * is the only source of truth.
47731
+ *
47732
+ * This is a fast-path optimization for `cdkd local run-task
47733
+ * --from-state` (recognizing a cdk-asset image enables the local
47734
+ * `cdk.out` docker build instead of an ECR pull), so it must NEVER fail
47735
+ * the run: every miss (no bucket, no marker, malformed marker, any AWS
47736
+ * error) logs at debug and returns `undefined` — the caller falls back
47737
+ * to the conventional-prefix regex.
47738
+ *
47739
+ * Marker-key region resolution: the marker records the repo for the
47740
+ * stack's DEPLOY region, so after the explicit CLI overrides (`--region`
47741
+ * highest per the repo convention, then `--stack-region` — the state
47742
+ * disambiguator that names the region whose state is being loaded) the
47743
+ * synth-derived stack region outranks the ambient env region. This
47744
+ * deliberately differs from {@link loadStateForStack}'s bucket-resolution
47745
+ * chain (which is about WHERE the state bucket is, not WHICH region's
47746
+ * data is read).
47747
+ */
47748
+ async function loadBootstrapContainerRepo(synthRegion, opts) {
47749
+ const logger = getLogger();
47750
+ const prefix = opts.logPrefix ?? "--from-state";
47751
+ const region = opts.region ?? opts.stackRegion ?? synthRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? "us-east-1";
47752
+ let stateBucket;
47753
+ try {
47754
+ stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
47755
+ } catch (err) {
47756
+ logger.debug(`${prefix}: could not resolve state bucket for the bootstrap-marker read: ${err instanceof Error ? err.message : String(err)}. Falling back to conventional asset-repo names.`);
47757
+ return;
47758
+ }
47759
+ const awsClients = new AwsClients({
47760
+ ...opts.region !== void 0 && { region: opts.region },
47761
+ ...opts.profile !== void 0 && { profile: opts.profile }
47762
+ });
47763
+ setAwsClients(awsClients);
47764
+ try {
47765
+ const stateConfig = {
47766
+ bucket: stateBucket,
47767
+ prefix: opts.statePrefix
47768
+ };
47769
+ const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
47770
+ ...opts.region !== void 0 && { region: opts.region },
47771
+ ...opts.profile !== void 0 && { profile: opts.profile }
47772
+ });
47773
+ const markerKey = getBootstrapMarkerKey(region);
47774
+ const body = await stateBackend.getRawObject(markerKey);
47775
+ if (body === null) {
47776
+ logger.debug(`${prefix}: no bootstrap marker at '${markerKey}' in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
47777
+ return;
47778
+ }
47779
+ const marker = parseBootstrapMarker(body, markerKey);
47780
+ logger.debug(`${prefix}: bootstrap marker for ${region} names container repo '${marker.containerRepo}'.`);
47781
+ return marker.containerRepo;
47782
+ } catch (err) {
47783
+ logger.debug(`${prefix}: bootstrap-marker read failed: ${err instanceof Error ? err.message : String(err)}. Falling back to conventional asset-repo names.`);
47784
+ return;
47785
+ } finally {
47786
+ resetAwsClients();
47787
+ }
47788
+ }
47789
+ /**
47708
47790
  * Build a {@link CrossStackResolver} that walks cdkd's S3 state to look
47709
47791
  * up `Fn::ImportValue` / `Fn::GetStackOutput` references the same way
47710
47792
  * `cdkd deploy`'s `IntrinsicFunctionResolver` does. Returns `undefined`
@@ -48431,6 +48513,7 @@ function detectEcsImageResolutionNeeds(stack) {
48431
48513
  let needsStateResources = false;
48432
48514
  let needsEnvOrSecretSubstitution = false;
48433
48515
  let needsCrossStackResolver = false;
48516
+ let needsAssetRepoMarker = false;
48434
48517
  for (const res of Object.values(resources)) {
48435
48518
  if (res.Type !== "AWS::ECS::TaskDefinition") continue;
48436
48519
  const props = res.Properties ?? {};
@@ -48442,6 +48525,8 @@ function detectEcsImageResolutionNeeds(stack) {
48442
48525
  const need = inspectImageForSubstitutions(image, resources);
48443
48526
  if (need.pseudo) needsPseudoParameters = true;
48444
48527
  if (need.state) needsStateResources = true;
48528
+ const flat = extractImageString(image);
48529
+ if (flat !== void 0 && extractEcrRepoComponent(flat) !== void 0 && !CDK_ASSET_IMAGE_REPO_RE.test(flat)) needsAssetRepoMarker = true;
48445
48530
  if (containerHasIntrinsicEnvOrSecret(co)) needsEnvOrSecretSubstitution = true;
48446
48531
  if (containerHasCrossStackEnvOrSecret(co)) {
48447
48532
  needsEnvOrSecretSubstitution = true;
@@ -48461,7 +48546,8 @@ function detectEcsImageResolutionNeeds(stack) {
48461
48546
  needsPseudoParameters,
48462
48547
  needsStateResources,
48463
48548
  needsEnvOrSecretSubstitution,
48464
- needsCrossStackResolver
48549
+ needsCrossStackResolver,
48550
+ needsAssetRepoMarker
48465
48551
  };
48466
48552
  }
48467
48553
  /**
@@ -48937,11 +49023,53 @@ function buildSubstitutionContextFromImageContext(context) {
48937
49023
  * `cdk.out` build, so both must classify as `kind: 'cdk-asset'`. The `cdk-`
48938
49024
  * qualifier is generalized to `[a-z0-9]+` so a custom-qualifier bootstrap is
48939
49025
  * matched too (previously only the hardcoded `hnb659fds` default was).
49026
+ *
49027
+ * Since `cdkd bootstrap --container-repo <name>` (issue #1011) the
49028
+ * cdkd-owned repo can carry ANY name — the per-region bootstrap marker is
49029
+ * the source of truth. The regex therefore cannot recognize images
49030
+ * published to a custom-named repo; `isCdkAssetImageUri` additionally
49031
+ * matches an ECR-hosted URI whose repository path component equals the
49032
+ * marker-resolved name when the caller supplies it (issue #1025). The
49033
+ * regex stays as the no-marker fallback — `cdkd local run-task` can run
49034
+ * without AWS access, where no marker read is possible.
48940
49035
  */
48941
49036
  const CDK_ASSET_IMAGE_REPO_RE = /(?:cdk-[a-z0-9]+|cdkd)-container-assets-/;
48942
- /** True when `uri` embeds a CDK-bootstrap or cdkd-owned container-assets repo. */
48943
- function isCdkAssetImageUri(uri) {
48944
- return CDK_ASSET_IMAGE_REPO_RE.test(uri);
49037
+ /**
49038
+ * Extract the repository path component of an ECR-hosted image URI.
49039
+ *
49040
+ * The host part (before the first `/`) must contain `.dkr.ecr.` — this
49041
+ * tolerates both concrete hosts (`123456789012.dkr.ecr.us-east-1.amazonaws.com`)
49042
+ * and placeholder-bearing hosts
49043
+ * (`${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}`). The repo
49044
+ * component is the substring after the first `/`, stripped of a trailing
49045
+ * `@<digest>` (first `@`) and then a trailing `:<tag>` (last `:` — ECR
49046
+ * repo names cannot contain `:` but CAN contain `/`, so the last colon is
49047
+ * the tag separator). Stripping the digest FIRST handles the combined
49048
+ * `name:tag@digest` form Docker accepts. Returns `undefined` for non-ECR
49049
+ * hosts and empty results.
49050
+ */
49051
+ function extractEcrRepoComponent(uri) {
49052
+ const slash = uri.indexOf("/");
49053
+ if (slash <= 0) return void 0;
49054
+ if (!uri.slice(0, slash).includes(".dkr.ecr.")) return void 0;
49055
+ let repo = uri.slice(slash + 1);
49056
+ const at = repo.indexOf("@");
49057
+ if (at !== -1) repo = repo.slice(0, at);
49058
+ const colon = repo.lastIndexOf(":");
49059
+ if (colon !== -1) repo = repo.slice(0, colon);
49060
+ return repo.length > 0 ? repo : void 0;
49061
+ }
49062
+ /**
49063
+ * True when `uri` embeds a CDK-bootstrap or cdkd-owned container-assets
49064
+ * repo — either via the conventional-name regex, or (issue #1025) via an
49065
+ * exact match between the URI's ECR repository path component and the
49066
+ * bootstrap-marker-resolved `cdkAssetContainerRepo` name, which covers
49067
+ * regions bootstrapped with `cdkd bootstrap --container-repo <name>`.
49068
+ */
49069
+ function isCdkAssetImageUri(uri, cdkAssetContainerRepo) {
49070
+ if (CDK_ASSET_IMAGE_REPO_RE.test(uri)) return true;
49071
+ if (cdkAssetContainerRepo === void 0) return false;
49072
+ return extractEcrRepoComponent(uri) === cdkAssetContainerRepo;
48945
49073
  }
48946
49074
  /**
48947
49075
  * Parse the `Image` field of an ECS container definition.
@@ -48976,14 +49104,14 @@ function isCdkAssetImageUri(uri) {
48976
49104
  */
48977
49105
  function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack, context) {
48978
49106
  const getAttImage = tryResolveImageGetAtt(raw, resources, context);
48979
- if (getAttImage) return classifyResolvedImage(getAttImage);
49107
+ if (getAttImage) return classifyResolvedImage(getAttImage, context?.cdkAssetContainerRepo);
48980
49108
  const joinResolved = tryResolveImageFnJoin(raw, resources, context);
48981
- if (joinResolved.kind === "resolved") return classifyResolvedImage(joinResolved.uri);
49109
+ if (joinResolved.kind === "resolved") return classifyResolvedImage(joinResolved.uri, context?.cdkAssetContainerRepo);
48982
49110
  if (joinResolved.kind === "needs-state") throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' references same-stack ECR repository '${joinResolved.repoLogicalId}' via Fn::Join. cdkd local run-task cannot resolve the repository URI without state — pass --from-state (the stack must have been deployed via cdkd deploy), build via ContainerImage.fromAsset, or pin a public image.`);
48983
49111
  if (joinResolved.kind === "unsupported-join") throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an unsupported Fn::Join Image shape: ${joinResolved.reason}. cdkd local run-task recognizes the canonical CDK 2.x ContainerImage.fromEcrRepository Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`);
48984
49112
  const flat = extractImageString(raw);
48985
49113
  if (!flat) throw new EcsTaskResolutionError(`Container '${containerName}' in task '${taskLogicalId}' has an unparseable Image property. cdkd local run-task v1 supports flat string images, single-key Fn::Sub bodies, and CDK-asset Image references.`);
48986
- if (isCdkAssetImageUri(flat)) {
49114
+ if (isCdkAssetImageUri(flat, context?.cdkAssetContainerRepo)) {
48987
49115
  const hashMatch = /:([a-f0-9]{8,})$/.exec(flat);
48988
49116
  const out = { kind: "cdk-asset" };
48989
49117
  if (hashMatch) out.assetHash = hashMatch[1];
@@ -49029,9 +49157,11 @@ function findUnresolvedEcrRepositoryRef(substituted, resources) {
49029
49157
  * Classify a fully-substituted image URI (Tier 1 / Tier 2 produced a
49030
49158
  * concrete string) into the `ResolvedEcsImage` shape. Splits out so the
49031
49159
  * `Fn::GetAtt` path can share the regex-match branches.
49160
+ * `cdkAssetContainerRepo` is the optional marker-resolved custom repo
49161
+ * name (issue #1025) threaded through from the resolution context.
49032
49162
  */
49033
- function classifyResolvedImage(uri) {
49034
- if (isCdkAssetImageUri(uri)) {
49163
+ function classifyResolvedImage(uri, cdkAssetContainerRepo) {
49164
+ if (isCdkAssetImageUri(uri, cdkAssetContainerRepo)) {
49035
49165
  const hashMatch = /:([a-f0-9]{8,})$/.exec(uri);
49036
49166
  const out = { kind: "cdk-asset" };
49037
49167
  if (hashMatch) out.assetHash = hashMatch[1];
@@ -53488,7 +53618,7 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
53488
53618
  const logger = getLogger();
53489
53619
  if (!candidate) return void 0;
53490
53620
  const needs = detectEcsImageResolutionNeeds(candidate);
53491
- if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
53621
+ if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return;
53492
53622
  const ctx = {};
53493
53623
  const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
53494
53624
  if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
@@ -53516,6 +53646,17 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
53516
53646
  if (loaded) ctx.stateResources = loaded.resources;
53517
53647
  } else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI. Otherwise the resolver will surface its existing error.");
53518
53648
  else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
53649
+ if (needs.needsAssetRepoMarker && options.fromState) {
53650
+ const containerRepo = await loadBootstrapContainerRepo(candidate.region, {
53651
+ statePrefix: options.statePrefix,
53652
+ ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
53653
+ ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
53654
+ ...options.region !== void 0 && { region: options.region },
53655
+ ...options.profile !== void 0 && { profile: options.profile },
53656
+ logPrefix: "--from-state"
53657
+ });
53658
+ if (containerRepo !== void 0) ctx.cdkAssetContainerRepo = containerRepo;
53659
+ }
53519
53660
  return ctx;
53520
53661
  }
53521
53662
  function pickCandidateStack(stackPattern, stacks) {
@@ -57058,7 +57199,7 @@ function reorderArgs(argv) {
57058
57199
  async function main() {
57059
57200
  installPipeCloseHandler();
57060
57201
  const program = new Command();
57061
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.238.4");
57202
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.238.6");
57062
57203
  program.addCommand(createBootstrapCommand());
57063
57204
  program.addCommand(createSynthCommand());
57064
57205
  program.addCommand(createListCommand());