@go-to-k/cdkd 0.238.4 → 0.238.5

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.5";
1889
1889
  }
1890
1890
  /**
1891
1891
  * Generate a time-sortable unique run id, e.g.
@@ -47705,6 +47705,72 @@ async function loadStateForStack(stackName, synthRegion, opts) {
47705
47705
  }
47706
47706
  }
47707
47707
  /**
47708
+ * Best-effort read of the region's asset-storage bootstrap marker
47709
+ * (`s3://{stateBucket}/cdkd-bootstrap/{region}.json`) to recover the
47710
+ * cdkd-owned container-asset ECR repository name (issue #1025). Since
47711
+ * `cdkd bootstrap --container-repo <name>` (issue #1011) the repo can
47712
+ * carry ANY name, so the local resolvers' conventional-prefix regex
47713
+ * cannot classify images published to a custom-named repo — the marker
47714
+ * is the only source of truth.
47715
+ *
47716
+ * This is a fast-path optimization for `cdkd local run-task
47717
+ * --from-state` (recognizing a cdk-asset image enables the local
47718
+ * `cdk.out` docker build instead of an ECR pull), so it must NEVER fail
47719
+ * the run: every miss (no bucket, no marker, malformed marker, any AWS
47720
+ * error) logs at debug and returns `undefined` — the caller falls back
47721
+ * to the conventional-prefix regex.
47722
+ *
47723
+ * Marker-key region resolution: the marker records the repo for the
47724
+ * stack's DEPLOY region, so after the explicit CLI overrides (`--region`
47725
+ * highest per the repo convention, then `--stack-region` — the state
47726
+ * disambiguator that names the region whose state is being loaded) the
47727
+ * synth-derived stack region outranks the ambient env region. This
47728
+ * deliberately differs from {@link loadStateForStack}'s bucket-resolution
47729
+ * chain (which is about WHERE the state bucket is, not WHICH region's
47730
+ * data is read).
47731
+ */
47732
+ async function loadBootstrapContainerRepo(synthRegion, opts) {
47733
+ const logger = getLogger();
47734
+ const prefix = opts.logPrefix ?? "--from-state";
47735
+ const region = opts.region ?? opts.stackRegion ?? synthRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? "us-east-1";
47736
+ let stateBucket;
47737
+ try {
47738
+ stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
47739
+ } catch (err) {
47740
+ 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.`);
47741
+ return;
47742
+ }
47743
+ const awsClients = new AwsClients({
47744
+ ...opts.region !== void 0 && { region: opts.region },
47745
+ ...opts.profile !== void 0 && { profile: opts.profile }
47746
+ });
47747
+ setAwsClients(awsClients);
47748
+ try {
47749
+ const stateConfig = {
47750
+ bucket: stateBucket,
47751
+ prefix: opts.statePrefix
47752
+ };
47753
+ const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
47754
+ ...opts.region !== void 0 && { region: opts.region },
47755
+ ...opts.profile !== void 0 && { profile: opts.profile }
47756
+ });
47757
+ const markerKey = getBootstrapMarkerKey(region);
47758
+ const body = await stateBackend.getRawObject(markerKey);
47759
+ if (body === null) {
47760
+ logger.debug(`${prefix}: no bootstrap marker at '${markerKey}' in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
47761
+ return;
47762
+ }
47763
+ const marker = parseBootstrapMarker(body, markerKey);
47764
+ logger.debug(`${prefix}: bootstrap marker for ${region} names container repo '${marker.containerRepo}'.`);
47765
+ return marker.containerRepo;
47766
+ } catch (err) {
47767
+ logger.debug(`${prefix}: bootstrap-marker read failed: ${err instanceof Error ? err.message : String(err)}. Falling back to conventional asset-repo names.`);
47768
+ return;
47769
+ } finally {
47770
+ resetAwsClients();
47771
+ }
47772
+ }
47773
+ /**
47708
47774
  * Build a {@link CrossStackResolver} that walks cdkd's S3 state to look
47709
47775
  * up `Fn::ImportValue` / `Fn::GetStackOutput` references the same way
47710
47776
  * `cdkd deploy`'s `IntrinsicFunctionResolver` does. Returns `undefined`
@@ -48431,6 +48497,7 @@ function detectEcsImageResolutionNeeds(stack) {
48431
48497
  let needsStateResources = false;
48432
48498
  let needsEnvOrSecretSubstitution = false;
48433
48499
  let needsCrossStackResolver = false;
48500
+ let needsAssetRepoMarker = false;
48434
48501
  for (const res of Object.values(resources)) {
48435
48502
  if (res.Type !== "AWS::ECS::TaskDefinition") continue;
48436
48503
  const props = res.Properties ?? {};
@@ -48442,6 +48509,8 @@ function detectEcsImageResolutionNeeds(stack) {
48442
48509
  const need = inspectImageForSubstitutions(image, resources);
48443
48510
  if (need.pseudo) needsPseudoParameters = true;
48444
48511
  if (need.state) needsStateResources = true;
48512
+ const flat = extractImageString(image);
48513
+ if (flat !== void 0 && extractEcrRepoComponent(flat) !== void 0 && !CDK_ASSET_IMAGE_REPO_RE.test(flat)) needsAssetRepoMarker = true;
48445
48514
  if (containerHasIntrinsicEnvOrSecret(co)) needsEnvOrSecretSubstitution = true;
48446
48515
  if (containerHasCrossStackEnvOrSecret(co)) {
48447
48516
  needsEnvOrSecretSubstitution = true;
@@ -48461,7 +48530,8 @@ function detectEcsImageResolutionNeeds(stack) {
48461
48530
  needsPseudoParameters,
48462
48531
  needsStateResources,
48463
48532
  needsEnvOrSecretSubstitution,
48464
- needsCrossStackResolver
48533
+ needsCrossStackResolver,
48534
+ needsAssetRepoMarker
48465
48535
  };
48466
48536
  }
48467
48537
  /**
@@ -48937,11 +49007,53 @@ function buildSubstitutionContextFromImageContext(context) {
48937
49007
  * `cdk.out` build, so both must classify as `kind: 'cdk-asset'`. The `cdk-`
48938
49008
  * qualifier is generalized to `[a-z0-9]+` so a custom-qualifier bootstrap is
48939
49009
  * matched too (previously only the hardcoded `hnb659fds` default was).
49010
+ *
49011
+ * Since `cdkd bootstrap --container-repo <name>` (issue #1011) the
49012
+ * cdkd-owned repo can carry ANY name — the per-region bootstrap marker is
49013
+ * the source of truth. The regex therefore cannot recognize images
49014
+ * published to a custom-named repo; `isCdkAssetImageUri` additionally
49015
+ * matches an ECR-hosted URI whose repository path component equals the
49016
+ * marker-resolved name when the caller supplies it (issue #1025). The
49017
+ * regex stays as the no-marker fallback — `cdkd local run-task` can run
49018
+ * without AWS access, where no marker read is possible.
48940
49019
  */
48941
49020
  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);
49021
+ /**
49022
+ * Extract the repository path component of an ECR-hosted image URI.
49023
+ *
49024
+ * The host part (before the first `/`) must contain `.dkr.ecr.` — this
49025
+ * tolerates both concrete hosts (`123456789012.dkr.ecr.us-east-1.amazonaws.com`)
49026
+ * and placeholder-bearing hosts
49027
+ * (`${AWS::AccountId}.dkr.ecr.${AWS::Region}.${AWS::URLSuffix}`). The repo
49028
+ * component is the substring after the first `/`, stripped of a trailing
49029
+ * `@<digest>` (first `@`) and then a trailing `:<tag>` (last `:` — ECR
49030
+ * repo names cannot contain `:` but CAN contain `/`, so the last colon is
49031
+ * the tag separator). Stripping the digest FIRST handles the combined
49032
+ * `name:tag@digest` form Docker accepts. Returns `undefined` for non-ECR
49033
+ * hosts and empty results.
49034
+ */
49035
+ function extractEcrRepoComponent(uri) {
49036
+ const slash = uri.indexOf("/");
49037
+ if (slash <= 0) return void 0;
49038
+ if (!uri.slice(0, slash).includes(".dkr.ecr.")) return void 0;
49039
+ let repo = uri.slice(slash + 1);
49040
+ const at = repo.indexOf("@");
49041
+ if (at !== -1) repo = repo.slice(0, at);
49042
+ const colon = repo.lastIndexOf(":");
49043
+ if (colon !== -1) repo = repo.slice(0, colon);
49044
+ return repo.length > 0 ? repo : void 0;
49045
+ }
49046
+ /**
49047
+ * True when `uri` embeds a CDK-bootstrap or cdkd-owned container-assets
49048
+ * repo — either via the conventional-name regex, or (issue #1025) via an
49049
+ * exact match between the URI's ECR repository path component and the
49050
+ * bootstrap-marker-resolved `cdkAssetContainerRepo` name, which covers
49051
+ * regions bootstrapped with `cdkd bootstrap --container-repo <name>`.
49052
+ */
49053
+ function isCdkAssetImageUri(uri, cdkAssetContainerRepo) {
49054
+ if (CDK_ASSET_IMAGE_REPO_RE.test(uri)) return true;
49055
+ if (cdkAssetContainerRepo === void 0) return false;
49056
+ return extractEcrRepoComponent(uri) === cdkAssetContainerRepo;
48945
49057
  }
48946
49058
  /**
48947
49059
  * Parse the `Image` field of an ECS container definition.
@@ -48976,14 +49088,14 @@ function isCdkAssetImageUri(uri) {
48976
49088
  */
48977
49089
  function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack, context) {
48978
49090
  const getAttImage = tryResolveImageGetAtt(raw, resources, context);
48979
- if (getAttImage) return classifyResolvedImage(getAttImage);
49091
+ if (getAttImage) return classifyResolvedImage(getAttImage, context?.cdkAssetContainerRepo);
48980
49092
  const joinResolved = tryResolveImageFnJoin(raw, resources, context);
48981
- if (joinResolved.kind === "resolved") return classifyResolvedImage(joinResolved.uri);
49093
+ if (joinResolved.kind === "resolved") return classifyResolvedImage(joinResolved.uri, context?.cdkAssetContainerRepo);
48982
49094
  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
49095
  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
49096
  const flat = extractImageString(raw);
48985
49097
  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)) {
49098
+ if (isCdkAssetImageUri(flat, context?.cdkAssetContainerRepo)) {
48987
49099
  const hashMatch = /:([a-f0-9]{8,})$/.exec(flat);
48988
49100
  const out = { kind: "cdk-asset" };
48989
49101
  if (hashMatch) out.assetHash = hashMatch[1];
@@ -49029,9 +49141,11 @@ function findUnresolvedEcrRepositoryRef(substituted, resources) {
49029
49141
  * Classify a fully-substituted image URI (Tier 1 / Tier 2 produced a
49030
49142
  * concrete string) into the `ResolvedEcsImage` shape. Splits out so the
49031
49143
  * `Fn::GetAtt` path can share the regex-match branches.
49144
+ * `cdkAssetContainerRepo` is the optional marker-resolved custom repo
49145
+ * name (issue #1025) threaded through from the resolution context.
49032
49146
  */
49033
- function classifyResolvedImage(uri) {
49034
- if (isCdkAssetImageUri(uri)) {
49147
+ function classifyResolvedImage(uri, cdkAssetContainerRepo) {
49148
+ if (isCdkAssetImageUri(uri, cdkAssetContainerRepo)) {
49035
49149
  const hashMatch = /:([a-f0-9]{8,})$/.exec(uri);
49036
49150
  const out = { kind: "cdk-asset" };
49037
49151
  if (hashMatch) out.assetHash = hashMatch[1];
@@ -53488,7 +53602,7 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
53488
53602
  const logger = getLogger();
53489
53603
  if (!candidate) return void 0;
53490
53604
  const needs = detectEcsImageResolutionNeeds(candidate);
53491
- if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution) return;
53605
+ if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return;
53492
53606
  const ctx = {};
53493
53607
  const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
53494
53608
  if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
@@ -53516,6 +53630,17 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
53516
53630
  if (loaded) ctx.stateResources = loaded.resources;
53517
53631
  } 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
53632
  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).");
53633
+ if (needs.needsAssetRepoMarker && options.fromState) {
53634
+ const containerRepo = await loadBootstrapContainerRepo(candidate.region, {
53635
+ statePrefix: options.statePrefix,
53636
+ ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
53637
+ ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
53638
+ ...options.region !== void 0 && { region: options.region },
53639
+ ...options.profile !== void 0 && { profile: options.profile },
53640
+ logPrefix: "--from-state"
53641
+ });
53642
+ if (containerRepo !== void 0) ctx.cdkAssetContainerRepo = containerRepo;
53643
+ }
53519
53644
  return ctx;
53520
53645
  }
53521
53646
  function pickCandidateStack(stackPattern, stacks) {
@@ -57058,7 +57183,7 @@ function reorderArgs(argv) {
57058
57183
  async function main() {
57059
57184
  installPipeCloseHandler();
57060
57185
  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");
57186
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.238.5");
57062
57187
  program.addCommand(createBootstrapCommand());
57063
57188
  program.addCommand(createSynthCommand());
57064
57189
  program.addCommand(createListCommand());