@go-to-k/cdkd 0.278.33 → 0.278.35
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/{asg-provider-3kpNYstr.js → asg-provider-8XxBHtzK.js} +2 -2
- package/dist/{asg-provider-3kpNYstr.js.map → asg-provider-8XxBHtzK.js.map} +1 -1
- package/dist/cli.js +219 -66
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BfPXYVg-.js → deploy-engine-CWZlo2Il.js} +72 -4
- package/dist/{deploy-engine-BfPXYVg-.js.map → deploy-engine-CWZlo2Il.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as describeTypeWithThrottleRetry, $t as AwsClients, A as red, At as Synthesizer, B as isTerminationProtectionPropagationError, Bt as resolveUseCdkBootstrapAssets, C as isStatefulRecreateTargetSync, Ct as buildDockerImage, D as cyan, Dt as runDockerStreaming, E as bold, Et as runDockerForeground, F as ProviderRegistry, Ft as resolveAutoAssetStorage, G as normalizeAwsTagsToCfn, Gt as MIGRATE_TMP_PREFIX, H as cfnRefValueFromPhysicalId, Ht as warnDeprecatedNoPrefixCliFlag, I as findActionableSilentDrops, It as resolveCaptureObservedState, J as readConfigString, Jt as expectedOwnerParam, K as resolveExplicitPhysicalId, Kt as findLargeInlineResources, L as CloudControlProvider, Lt as resolveSkipPrefix, M as IAMRoleProvider, Mt as getDefaultStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as getLegacyStateBucketName, O as gray, Ot as AssetManifestLoader, P as clearOnUpdateRemoval, Pt as resolveApp, Q as DiffCalculator, Qt as resolveBucketRegion, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefault, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateContainerRepoName, T as formatResourceLine, Tn as withErrorHandling, Tt as getDockerCmd, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_BODY_LIMIT, V as IntrinsicFunctionResolver, Vt as stateBucketExistenceConfirmed, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_URL_LIMIT, X as requireConfigString, Xt as processStackMessages, Y as requireConfigArray, Yt as AssemblyReader, Z as applyRoleArnIfSet, _ as createPreDeleteFinalSnapshot, _n as ResourceUpdateNotSupportedError, _t as BOOTSTRAP_MARKER_PREFIX, a as DeploymentEventsStore, at as LockManager, b as unsupportedFinalSnapshotError, bt as parseBootstrapMarker, c as replayFailedOperations, cn as LocalInvokeBuildError$1, ct as shouldRetainResource, d as IMPLICIT_DELETE_DEPENDENCIES, dt as WorkGraph, en as getAwsClients, et as withRetry, f as computeImplicitDeleteEdges, fn as MissingCdkCliError, ft as buildAssetRedirectMap, g as ccRoutedFinalSnapshotError, gn as ResourceTimeoutError, gt as AssetModeResolver, h as buildFinalSnapshotIdentifier, hn as ProvisioningError, ht as rewriteTemplateAssetReferences, i as DeploymentEventsReader, in as CdkdError, it as TemplateParser, j as yellow, jt as synthesisStatusMessage, k as green, kt as getDockerImageBySourceHash, l as replayRollback, ln as LocalMigrateError, lt as AssetPublisher, m as PRE_DELETE_SNAPSHOT_TYPES, mn as PartialFailureError, mt as loadPublishableAssetManifest, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as setAwsClients, nt as isThrottlingError, o as planFailedOps, ot as S3StateBackend, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as NestedStackChildDirectDestroyError, pt as createAssetRedirectResolver, q as assertRegionMatch, qt as uploadCfnTemplate, r as DeployEngine, rt as DagBuilder, s as planRollback, sn as DeployCancelledError, st as rebuildClientForBucketRegion, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resetAwsClients, tt as isRetryableTransientError, u as withResourceDeadline, un as LocalStartServiceError, ut as stringifyValue, v as isFinalSnapshotError, vn as StackHasActiveImportsError, vt as ensureAssetStorage, w as renderStatefulReason, wn as normalizeAwsError, wt as formatDockerLoginError, x as extractDeploymentEventError, xt as validateAssetBucketName, y as refusesFinalSnapshot, yn as StackTerminationProtectionError, yt as getBootstrapMarkerKey, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefaultAndSource } from "./deploy-engine-CWZlo2Il.js";
|
|
3
3
|
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-BYMEE-BS.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-8XxBHtzK.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -9689,7 +9689,7 @@ var LambdaUrlProvider = class {
|
|
|
9689
9689
|
this.logger.debug(`Creating Lambda URL ${logicalId}`);
|
|
9690
9690
|
const targetFunctionArn = properties["TargetFunctionArn"];
|
|
9691
9691
|
if (!targetFunctionArn) throw new ProvisioningError(`TargetFunctionArn is required for Lambda URL ${logicalId}`, resourceType, logicalId);
|
|
9692
|
-
const authType = properties["AuthType"]
|
|
9692
|
+
const authType = requireConfigString(properties["AuthType"], "NONE", "AWS::Lambda::Url AuthType");
|
|
9693
9693
|
try {
|
|
9694
9694
|
const cors = properties["Cors"];
|
|
9695
9695
|
const createParams = {
|
|
@@ -9733,7 +9733,7 @@ var LambdaUrlProvider = class {
|
|
|
9733
9733
|
};
|
|
9734
9734
|
const updateParams = {
|
|
9735
9735
|
FunctionName: physicalId,
|
|
9736
|
-
AuthType: properties["AuthType"]
|
|
9736
|
+
AuthType: requireConfigString(properties["AuthType"], "NONE", "AWS::Lambda::Url AuthType")
|
|
9737
9737
|
};
|
|
9738
9738
|
const invokeMode = clearOnUpdateRemoval(properties["InvokeMode"], previousProperties["InvokeMode"], "BUFFERED");
|
|
9739
9739
|
if (invokeMode !== void 0) updateParams.InvokeMode = invokeMode;
|
|
@@ -12317,11 +12317,18 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
12317
12317
|
if (billingMode === "PROVISIONED") createParams.ProvisionedThroughput = derivePerCallProvisionedThroughput(properties, currentRegion);
|
|
12318
12318
|
const streamSpecInput = properties["StreamSpecification"];
|
|
12319
12319
|
const needsStream = replicas.some((r) => r["Region"] !== currentRegion) || replicas.length > 1;
|
|
12320
|
-
if (streamSpecInput
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12320
|
+
if (streamSpecInput != null) {
|
|
12321
|
+
let streamViewType;
|
|
12322
|
+
try {
|
|
12323
|
+
streamViewType = readConfigString(streamSpecInput, "StreamViewType", "NEW_AND_OLD_IMAGES", "AWS::DynamoDB::GlobalTable StreamSpecification");
|
|
12324
|
+
} catch (error) {
|
|
12325
|
+
throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
|
|
12326
|
+
}
|
|
12327
|
+
createParams.StreamSpecification = {
|
|
12328
|
+
StreamEnabled: true,
|
|
12329
|
+
StreamViewType: streamViewType
|
|
12330
|
+
};
|
|
12331
|
+
} else if (needsStream) {
|
|
12325
12332
|
this.logger.info(`Auto-enabling streams (NEW_AND_OLD_IMAGES) on ${logicalId} — required for cross-region replication`);
|
|
12326
12333
|
createParams.StreamSpecification = {
|
|
12327
12334
|
StreamEnabled: true,
|
|
@@ -12577,7 +12584,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
12577
12584
|
if (properties["StreamSpecification"] !== void 0 && !deepEqual$1(properties["StreamSpecification"], previousProperties["StreamSpecification"])) {
|
|
12578
12585
|
flatUpdate.StreamSpecification = {
|
|
12579
12586
|
StreamEnabled: true,
|
|
12580
|
-
StreamViewType: properties["StreamSpecification"]
|
|
12587
|
+
StreamViewType: readConfigString(properties["StreamSpecification"], "StreamViewType", "NEW_AND_OLD_IMAGES", "AWS::DynamoDB::GlobalTable StreamSpecification")
|
|
12581
12588
|
};
|
|
12582
12589
|
flatChanged = true;
|
|
12583
12590
|
}
|
|
@@ -21934,8 +21941,9 @@ var CloudFrontOAIProvider = class {
|
|
|
21934
21941
|
*/
|
|
21935
21942
|
async create(logicalId, resourceType, properties) {
|
|
21936
21943
|
this.logger.debug(`Creating CloudFront OAI ${logicalId}`);
|
|
21937
|
-
const
|
|
21944
|
+
const config = properties["CloudFrontOriginAccessIdentityConfig"];
|
|
21938
21945
|
try {
|
|
21946
|
+
const comment = readConfigString(config, "Comment", "", "AWS::CloudFront::CloudFrontOriginAccessIdentity CloudFrontOriginAccessIdentityConfig");
|
|
21939
21947
|
const oai = (await this.cloudFrontClient.send(new CreateCloudFrontOriginAccessIdentityCommand({ CloudFrontOriginAccessIdentityConfig: {
|
|
21940
21948
|
CallerReference: logicalId,
|
|
21941
21949
|
Comment: comment
|
|
@@ -21970,8 +21978,9 @@ var CloudFrontOAIProvider = class {
|
|
|
21970
21978
|
*/
|
|
21971
21979
|
async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
|
|
21972
21980
|
this.logger.debug(`Updating CloudFront OAI ${logicalId}: ${physicalId}`);
|
|
21973
|
-
const
|
|
21981
|
+
const config = properties["CloudFrontOriginAccessIdentityConfig"];
|
|
21974
21982
|
try {
|
|
21983
|
+
const comment = readConfigString(config, "Comment", "", "AWS::CloudFront::CloudFrontOriginAccessIdentity CloudFrontOriginAccessIdentityConfig");
|
|
21975
21984
|
const getResponse = await this.cloudFrontClient.send(new GetCloudFrontOriginAccessIdentityCommand({ Id: physicalId }));
|
|
21976
21985
|
const etag = getResponse.ETag;
|
|
21977
21986
|
if (!etag) throw new Error("GetCloudFrontOriginAccessIdentity did not return ETag");
|
|
@@ -25035,7 +25044,12 @@ var ECSProvider = class {
|
|
|
25035
25044
|
const newServiceName = properties["ServiceName"];
|
|
25036
25045
|
const oldServiceName = previousProperties["ServiceName"];
|
|
25037
25046
|
if (newServiceName && oldServiceName && newServiceName !== oldServiceName) throw new ProvisioningError(`Cannot update ServiceName for ECS service ${logicalId} (immutable property, requires replacement)`, resourceType, logicalId, physicalId);
|
|
25038
|
-
|
|
25047
|
+
let deploymentControllerType;
|
|
25048
|
+
try {
|
|
25049
|
+
deploymentControllerType = readConfigString(properties["DeploymentController"], "Type", "ECS", "AWS::ECS::Service DeploymentController");
|
|
25050
|
+
} catch (error) {
|
|
25051
|
+
throw new ProvisioningError(error instanceof Error ? error.message : String(error), resourceType, logicalId, physicalId, error instanceof Error ? error : void 0);
|
|
25052
|
+
}
|
|
25039
25053
|
const isEcsController = deploymentControllerType === "ECS";
|
|
25040
25054
|
const loadBalancersChanged = JSON.stringify(previousProperties["LoadBalancers"] ?? null) !== JSON.stringify(properties["LoadBalancers"] ?? null);
|
|
25041
25055
|
const serviceRegistriesChanged = JSON.stringify(previousProperties["ServiceRegistries"] ?? null) !== JSON.stringify(properties["ServiceRegistries"] ?? null);
|
|
@@ -29919,13 +29933,14 @@ var Route53Provider = class {
|
|
|
29919
29933
|
if (!name) throw new ProvisioningError(`Name is required for hosted zone ${logicalId}`, resourceType, logicalId);
|
|
29920
29934
|
try {
|
|
29921
29935
|
const hostedZoneConfig = properties["HostedZoneConfig"];
|
|
29936
|
+
const createComment = readConfigString(hostedZoneConfig, "Comment", "", "AWS::Route53::HostedZone HostedZoneConfig");
|
|
29922
29937
|
const vpcs = properties["VPCs"];
|
|
29923
29938
|
const firstVpc = vpcs && vpcs.length > 0 ? vpcs[0] : void 0;
|
|
29924
29939
|
const response = await this.getClient().send(new CreateHostedZoneCommand({
|
|
29925
29940
|
Name: name,
|
|
29926
29941
|
CallerReference: `${logicalId}-${Date.now()}`,
|
|
29927
|
-
...
|
|
29928
|
-
Comment:
|
|
29942
|
+
...createComment !== "" ? { HostedZoneConfig: {
|
|
29943
|
+
Comment: createComment,
|
|
29929
29944
|
...firstVpc ? { PrivateZone: true } : {}
|
|
29930
29945
|
} } : firstVpc ? { HostedZoneConfig: { PrivateZone: true } } : {},
|
|
29931
29946
|
...firstVpc ? { VPC: {
|
|
@@ -29983,7 +29998,8 @@ var Route53Provider = class {
|
|
|
29983
29998
|
async updateHostedZone(logicalId, physicalId, resourceType, properties, previousProperties) {
|
|
29984
29999
|
this.logger.debug(`Updating Route 53 hosted zone ${logicalId}: ${physicalId}`);
|
|
29985
30000
|
try {
|
|
29986
|
-
const
|
|
30001
|
+
const hostedZoneConfig = properties["HostedZoneConfig"];
|
|
30002
|
+
const comment = readConfigString(hostedZoneConfig, "Comment", "", "AWS::Route53::HostedZone HostedZoneConfig");
|
|
29987
30003
|
await this.getClient().send(new UpdateHostedZoneCommentCommand({
|
|
29988
30004
|
Id: physicalId,
|
|
29989
30005
|
Comment: comment
|
|
@@ -42382,8 +42398,8 @@ var CodeBuildProvider = class {
|
|
|
42382
42398
|
* object literal returned against a declared type IS excess-property checked,
|
|
42383
42399
|
* which turns each member name into a compile-time fact (issue #1386).
|
|
42384
42400
|
*/
|
|
42385
|
-
mapSource(source) {
|
|
42386
|
-
if (
|
|
42401
|
+
mapSource(source, containerPath) {
|
|
42402
|
+
if (source == null) return { type: "NO_SOURCE" };
|
|
42387
42403
|
let buildspec;
|
|
42388
42404
|
if (source["BuildSpec"] !== void 0) {
|
|
42389
42405
|
const bs = source["BuildSpec"];
|
|
@@ -42393,7 +42409,7 @@ var CodeBuildProvider = class {
|
|
|
42393
42409
|
const cfnGitSubmodules = source["GitSubmodulesConfig"];
|
|
42394
42410
|
const cfnBuildStatus = source["BuildStatusConfig"];
|
|
42395
42411
|
return {
|
|
42396
|
-
type: source
|
|
42412
|
+
type: readConfigString(source, "Type", "NO_SOURCE", containerPath),
|
|
42397
42413
|
buildspec,
|
|
42398
42414
|
location: source["Location"],
|
|
42399
42415
|
gitCloneDepth: source["GitCloneDepth"],
|
|
@@ -42411,10 +42427,10 @@ var CodeBuildProvider = class {
|
|
|
42411
42427
|
} : void 0
|
|
42412
42428
|
};
|
|
42413
42429
|
}
|
|
42414
|
-
mapArtifacts(artifacts) {
|
|
42415
|
-
if (
|
|
42430
|
+
mapArtifacts(artifacts, containerPath) {
|
|
42431
|
+
if (artifacts == null) return { type: "NO_ARTIFACTS" };
|
|
42416
42432
|
return {
|
|
42417
|
-
type: artifacts
|
|
42433
|
+
type: readConfigString(artifacts, "Type", "NO_ARTIFACTS", containerPath),
|
|
42418
42434
|
location: artifacts["Location"],
|
|
42419
42435
|
path: artifacts["Path"],
|
|
42420
42436
|
name: artifacts["Name"],
|
|
@@ -42472,9 +42488,9 @@ var CodeBuildProvider = class {
|
|
|
42472
42488
|
};
|
|
42473
42489
|
}
|
|
42474
42490
|
const cfnSecondarySources = properties["SecondarySources"];
|
|
42475
|
-
const secondarySources = cfnSecondarySources ? cfnSecondarySources.map((s) => this.mapSource(s))
|
|
42491
|
+
const secondarySources = cfnSecondarySources == null ? void 0 : requireConfigArray(cfnSecondarySources, "AWS::CodeBuild::Project SecondarySources").map((s) => this.mapSource(s, "AWS::CodeBuild::Project SecondarySources[]"));
|
|
42476
42492
|
const cfnSecondaryArtifacts = properties["SecondaryArtifacts"];
|
|
42477
|
-
const secondaryArtifacts = cfnSecondaryArtifacts ? cfnSecondaryArtifacts.map((a) => this.mapArtifacts(a))
|
|
42493
|
+
const secondaryArtifacts = cfnSecondaryArtifacts == null ? void 0 : requireConfigArray(cfnSecondaryArtifacts, "AWS::CodeBuild::Project SecondaryArtifacts").map((a) => this.mapArtifacts(a, "AWS::CodeBuild::Project SecondaryArtifacts[]"));
|
|
42478
42494
|
const cfnSecondarySourceVersions = properties["SecondarySourceVersions"];
|
|
42479
42495
|
const secondarySourceVersions = cfnSecondarySourceVersions ? cfnSecondarySourceVersions.map((sv) => ({
|
|
42480
42496
|
sourceIdentifier: sv["SourceIdentifier"],
|
|
@@ -42505,10 +42521,10 @@ var CodeBuildProvider = class {
|
|
|
42505
42521
|
}
|
|
42506
42522
|
return {
|
|
42507
42523
|
name,
|
|
42508
|
-
source: this.mapSource(source),
|
|
42524
|
+
source: this.mapSource(source, "AWS::CodeBuild::Project Source"),
|
|
42509
42525
|
environment: {
|
|
42510
|
-
type: environment
|
|
42511
|
-
computeType: environment
|
|
42526
|
+
type: readConfigString(environment, "Type", "LINUX_CONTAINER", "AWS::CodeBuild::Project Environment"),
|
|
42527
|
+
computeType: readConfigString(environment, "ComputeType", "BUILD_GENERAL1_SMALL", "AWS::CodeBuild::Project Environment"),
|
|
42512
42528
|
image: environment?.["Image"],
|
|
42513
42529
|
environmentVariables: envVars ? envVars.map((v) => ({
|
|
42514
42530
|
name: v.Name,
|
|
@@ -42529,7 +42545,7 @@ var CodeBuildProvider = class {
|
|
|
42529
42545
|
} : void 0
|
|
42530
42546
|
},
|
|
42531
42547
|
serviceRole,
|
|
42532
|
-
artifacts: this.mapArtifacts(artifacts),
|
|
42548
|
+
artifacts: this.mapArtifacts(artifacts, "AWS::CodeBuild::Project Artifacts"),
|
|
42533
42549
|
tags: tags ? tags.map((t) => ({
|
|
42534
42550
|
key: t.Key,
|
|
42535
42551
|
value: t.Value
|
|
@@ -49259,48 +49275,128 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
|
|
|
49259
49275
|
}
|
|
49260
49276
|
}
|
|
49261
49277
|
/**
|
|
49262
|
-
*
|
|
49263
|
-
* `--revert`. Strategy:
|
|
49278
|
+
* A top-level property NAME that carries tags.
|
|
49264
49279
|
*
|
|
49265
|
-
*
|
|
49266
|
-
*
|
|
49267
|
-
*
|
|
49268
|
-
*
|
|
49269
|
-
*
|
|
49270
|
-
*
|
|
49280
|
+
* The shape test below is deliberately NOT sufficient on its own: a top-level
|
|
49281
|
+
* `[{ Key, Value }]` list is not tag-exclusive — `LoadBalancerAttributes`,
|
|
49282
|
+
* `TargetGroupAttributes` and SSM `Association.Targets` all match it. No such
|
|
49283
|
+
* property can carry an `aws:`-prefixed or `AmazonECSManaged` key today, so the
|
|
49284
|
+
* carve-out would degrade to the identity of the old wholesale overwrite there
|
|
49285
|
+
* — but borrowing `canonicalizeTagListsDeep`'s heuristic for a WRITE decision
|
|
49286
|
+
* is not justified by its use for a SORT: a sort false positive is harmless, an
|
|
49287
|
+
* append is not.
|
|
49288
|
+
*/
|
|
49289
|
+
function isTagListKey(key) {
|
|
49290
|
+
return key === "Tags" || key.endsWith("Tags");
|
|
49291
|
+
}
|
|
49292
|
+
/** A CFn-shaped tag list: a non-empty array whose every element has a string `Key`. */
|
|
49293
|
+
function isCfnTagList(value) {
|
|
49294
|
+
return Array.isArray(value) && value.length > 0 && value.every((t) => isPlainRecord(t) && typeof t.Key === "string");
|
|
49295
|
+
}
|
|
49296
|
+
/** The `Key` of every entry in a CFn tag list, in list order. */
|
|
49297
|
+
function tagKeys(tags) {
|
|
49298
|
+
return tags.map((t) => t["Key"]);
|
|
49299
|
+
}
|
|
49300
|
+
/**
|
|
49301
|
+
* AWS-SERVICE-authored tag keys a `--revert` must not strip (issue #1501).
|
|
49271
49302
|
*
|
|
49272
|
-
*
|
|
49273
|
-
*
|
|
49303
|
+
* `AmazonECSManaged` is attached by ECS when a capacity provider binds an Auto
|
|
49304
|
+
* Scaling group, and managed scaling stops working without it. That entry is
|
|
49305
|
+
* the load-bearing one.
|
|
49274
49306
|
*
|
|
49275
|
-
*
|
|
49276
|
-
*
|
|
49277
|
-
*
|
|
49278
|
-
*
|
|
49279
|
-
*
|
|
49280
|
-
*
|
|
49281
|
-
|
|
49282
|
-
|
|
49283
|
-
|
|
49284
|
-
|
|
49285
|
-
|
|
49307
|
+
* The `aws:` prefix is DEFENSIVE rather than load-bearing, and the distinction
|
|
49308
|
+
* is worth stating: AWS reserves the prefix and rejects a write of one, and the
|
|
49309
|
+
* 45 providers that route reads through `normalizeAwsTagsToCfn` strip such keys
|
|
49310
|
+
* on the way in — so an `aws:` key can only reach the AWS side of a comparison
|
|
49311
|
+
* via the Cloud Control `readCurrentState` path, which returns the raw model.
|
|
49312
|
+
* It costs nothing to honor and cdkd can never have authored one.
|
|
49313
|
+
*/
|
|
49314
|
+
const SERVICE_MANAGED_TAG_KEYS = /* @__PURE__ */ new Set(["AmazonECSManaged"]);
|
|
49315
|
+
/** Whether a tag key is AWS-service-authored, per {@link SERVICE_MANAGED_TAG_KEYS}. */
|
|
49316
|
+
function isServiceManagedTagKey(key) {
|
|
49317
|
+
return SERVICE_MANAGED_TAG_KEYS.has(key) || key.startsWith("aws:");
|
|
49318
|
+
}
|
|
49319
|
+
/**
|
|
49320
|
+
* Revert a TAG LIST, preserving AWS-SERVICE-authored entries (issue #1501).
|
|
49286
49321
|
*
|
|
49287
|
-
*
|
|
49288
|
-
*
|
|
49289
|
-
*
|
|
49290
|
-
*
|
|
49291
|
-
*
|
|
49292
|
-
*
|
|
49293
|
-
*
|
|
49294
|
-
*
|
|
49295
|
-
*
|
|
49296
|
-
*
|
|
49297
|
-
*
|
|
49322
|
+
* Everything in {@link buildRevertNewProperties} overwrites a drifted top-level
|
|
49323
|
+
* key wholesale, which for `Tags` means "AWS ends up with exactly the tags cdkd
|
|
49324
|
+
* state recorded" — so a service-authored tag added after deploy is stripped.
|
|
49325
|
+
* The concrete case: ECS attaches `AmazonECSManaged` to an ASG when a capacity
|
|
49326
|
+
* provider binds it, and that tag is REQUIRED for managed scaling. Because any
|
|
49327
|
+
* CDK ASG declares at least a `Name` tag, `Tags` is template-DECLARED, so the
|
|
49328
|
+
* #1498 carve-out (undeclared + captured-empty) correctly does not apply — and
|
|
49329
|
+
* the tag list is an ARRAY, which {@link findRevertDroppedAwsKeys}'s walk
|
|
49330
|
+
* compares wholesale and never descends into. Post-revert the ASG kept the
|
|
49331
|
+
* capacity provider with its managed scaling silently broken (verified live
|
|
49332
|
+
* 2026-08-10).
|
|
49298
49333
|
*
|
|
49299
|
-
* The
|
|
49300
|
-
*
|
|
49301
|
-
*
|
|
49302
|
-
*
|
|
49334
|
+
* The semantic: the baseline still WINS for every ordinary tag — one it has and
|
|
49335
|
+
* AWS lost is re-added, one whose value differs is reset, and a
|
|
49336
|
+
* user/console-added tag AWS alone has is still REMOVED. Only a
|
|
49337
|
+
* {@link isServiceManagedTagKey} entry survives.
|
|
49338
|
+
*
|
|
49339
|
+
* **This is the issue's option 2, not its option 1, and the choice was settled
|
|
49340
|
+
* by a live test rather than by taste.** Option 1 (revert the whole tag list as
|
|
49341
|
+
* a diff, so ANY out-of-band add survives) was implemented first and failed the
|
|
49342
|
+
* `drift-revert` integ at its final assertion: that fixture injects an
|
|
49343
|
+
* `IntegInjected` tag and requires `--revert` to strip it, i.e. "revert removes
|
|
49344
|
+
* a console-added tag" is an established contract with a test behind it.
|
|
49345
|
+
* Option 1 would redefine revert semantics for a whole property class — which
|
|
49346
|
+
* is exactly the design pass the issue said it needed — so the narrow safeguard
|
|
49347
|
+
* ships instead, fixing the reported breakage while leaving ordinary tag revert
|
|
49348
|
+
* untouched.
|
|
49349
|
+
*
|
|
49350
|
+
* Scope: TOP-LEVEL tag lists, which is where `buildRevertNewProperties`
|
|
49351
|
+
* operates. A tag list nested inside another property (an EC2 launch template's
|
|
49352
|
+
* `TagSpecifications`) still reverts wholesale.
|
|
49353
|
+
*
|
|
49354
|
+
* Order: baseline entries first in baseline order, then the preserved
|
|
49355
|
+
* service-managed entries. Providers apply tags as a set and the drift
|
|
49356
|
+
* comparator canonicalizes tag-list order on both sides
|
|
49357
|
+
* (`drift-normalize.ts`), so the order is for readability / determinism only.
|
|
49303
49358
|
*/
|
|
49359
|
+
function mergeTagListForRevert(baselineTags, awsTags) {
|
|
49360
|
+
const baselineKeys = new Set(tagKeys(baselineTags));
|
|
49361
|
+
const seen = /* @__PURE__ */ new Set();
|
|
49362
|
+
const preserved = awsTags.filter((t) => {
|
|
49363
|
+
const key = t["Key"];
|
|
49364
|
+
if (baselineKeys.has(key) || !isServiceManagedTagKey(key) || seen.has(key)) return false;
|
|
49365
|
+
seen.add(key);
|
|
49366
|
+
return true;
|
|
49367
|
+
});
|
|
49368
|
+
return [...baselineTags, ...preserved];
|
|
49369
|
+
}
|
|
49370
|
+
/**
|
|
49371
|
+
* The AWS-service-authored tag keys a `--revert` will PRESERVE rather than
|
|
49372
|
+
* strip, i.e. those present on the AWS side of a drifted top-level tag list,
|
|
49373
|
+
* absent from the revert baseline, and {@link isServiceManagedTagKey} (issue
|
|
49374
|
+
* #1501).
|
|
49375
|
+
*
|
|
49376
|
+
* Reported in the plan so the carve-out of {@link mergeTagListForRevert} is
|
|
49377
|
+
* visible BEFORE the confirmation prompt: a user who did want the tag gone
|
|
49378
|
+
* learns here that the revert will not do it.
|
|
49379
|
+
*
|
|
49380
|
+
* @returns dotted `<Key>.<TagKey>` paths, sorted, e.g. `['Tags.AmazonECSManaged']`.
|
|
49381
|
+
*/
|
|
49382
|
+
function findRevertPreservedTagKeys(drifts, desiredProperties, awsProperties) {
|
|
49383
|
+
const preserved = /* @__PURE__ */ new Set();
|
|
49384
|
+
const driftedTopLevelKeys = /* @__PURE__ */ new Set();
|
|
49385
|
+
for (const d of drifts) {
|
|
49386
|
+
const topLevelKey = d.path.split(".", 1)[0];
|
|
49387
|
+
if (topLevelKey) driftedTopLevelKeys.add(topLevelKey);
|
|
49388
|
+
}
|
|
49389
|
+
for (const key of driftedTopLevelKeys) {
|
|
49390
|
+
if (!(key in desiredProperties)) continue;
|
|
49391
|
+
if (!isTagListKey(key)) continue;
|
|
49392
|
+
const desiredValue = desiredProperties[key];
|
|
49393
|
+
const awsValue = awsProperties[key];
|
|
49394
|
+
if (!(isCfnTagList(desiredValue) || Array.isArray(desiredValue) && desiredValue.length === 0) || !isCfnTagList(awsValue)) continue;
|
|
49395
|
+
const baselineKeys = new Set(tagKeys(desiredValue));
|
|
49396
|
+
for (const tagKey of tagKeys(awsValue)) if (!baselineKeys.has(tagKey) && isServiceManagedTagKey(tagKey)) preserved.add(`${key}.${tagKey}`);
|
|
49397
|
+
}
|
|
49398
|
+
return [...preserved].sort();
|
|
49399
|
+
}
|
|
49304
49400
|
/**
|
|
49305
49401
|
* The AWS-authored property paths a `--revert` is about to DROP, for a
|
|
49306
49402
|
* resource whose state predates observed-capture (issue #1478).
|
|
@@ -49377,12 +49473,60 @@ function collectMissingPaths(awsValue, desiredValue, path, out) {
|
|
|
49377
49473
|
function isPlainRecord(value) {
|
|
49378
49474
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
49379
49475
|
}
|
|
49476
|
+
/**
|
|
49477
|
+
* Build the `newProperties` object passed to `provider.update` during
|
|
49478
|
+
* `--revert`. Strategy:
|
|
49479
|
+
*
|
|
49480
|
+
* 1. Start from `awsProperties` (the AWS-current snapshot returned
|
|
49481
|
+
* by `readCurrentState`, which `runRevert` already passes as the
|
|
49482
|
+
* `previousProperties` argument to `provider.update`).
|
|
49483
|
+
* 2. For every top-level key whose subtree contains a drifted path,
|
|
49484
|
+
* overwrite it with the corresponding sub-shape from
|
|
49485
|
+
* `desiredProperties` (the state-recorded `observedProperties`).
|
|
49486
|
+
*
|
|
49487
|
+
* Why "AWS-current base + drifted overlay" instead of "drifted-only
|
|
49488
|
+
* partial":
|
|
49489
|
+
*
|
|
49490
|
+
* Several providers' `update()` implementations diff
|
|
49491
|
+
* `newProperties[K]` against `previousProperties[K]` and treat
|
|
49492
|
+
* `newVal === undefined` as "remove K from AWS" (e.g.
|
|
49493
|
+
* `SNSTopicProvider` calls `SetTopicAttributes(K, '')`,
|
|
49494
|
+
* `IAMRoleProvider.updateManagedPolicies` detaches every previously
|
|
49495
|
+
* attached policy when the new arg is undefined). Passing a
|
|
49496
|
+
* drifted-only partial would silently clear non-drifted attributes
|
|
49497
|
+
* on those providers. Sending the AWS-current value back as the
|
|
49498
|
+
* "new" value for non-drifted keys keeps `JSON.stringify(newVal) ===
|
|
49499
|
+
* JSON.stringify(oldVal)` so the diff is a no-op — no provider
|
|
49500
|
+
* changes required.
|
|
49501
|
+
*
|
|
49502
|
+
* For non-diff providers (e.g. `SQSQueueProvider` blindly pushes
|
|
49503
|
+
* every defined key via `SetQueueAttributes`), the AWS-current
|
|
49504
|
+
* value still gets serialised back to the same string AWS already
|
|
49505
|
+
* has, so the round-trip is a no-op for the AWS resource state.
|
|
49506
|
+
* The one exception is `readCurrentState`'s always-emit
|
|
49507
|
+
* placeholder values — e.g. SQS `RedrivePolicy: {}` — which AWS
|
|
49508
|
+
* rejects as invalid input even though they're round-tripped. That
|
|
49509
|
+
* class of value (Class 2 / structurally-incomplete-when-empty) is
|
|
49510
|
+
* handled by per-provider sanitize at the wire-layer; see the SQS
|
|
49511
|
+
* provider's `serializeRedrivePolicy` helper for the canonical
|
|
49512
|
+
* pattern.
|
|
49513
|
+
*
|
|
49514
|
+
* The drift comparator never produces array-index segments
|
|
49515
|
+
* (`Tags[0].Value`) — array drifts surface as a single entry on the
|
|
49516
|
+
* parent path — so `path.split('.', 1)` is always safe to extract the
|
|
49517
|
+
* top-level key.
|
|
49518
|
+
*/
|
|
49380
49519
|
function buildRevertNewProperties(drifts, desiredProperties, awsProperties) {
|
|
49381
49520
|
const result = { ...awsProperties };
|
|
49382
49521
|
for (const d of drifts) {
|
|
49383
49522
|
const topLevelKey = d.path.split(".", 1)[0];
|
|
49384
49523
|
if (!topLevelKey) continue;
|
|
49385
|
-
if (topLevelKey in desiredProperties)
|
|
49524
|
+
if (topLevelKey in desiredProperties) {
|
|
49525
|
+
const desiredValue = desiredProperties[topLevelKey];
|
|
49526
|
+
const awsValue = awsProperties[topLevelKey];
|
|
49527
|
+
const baselineIsTagList = isCfnTagList(desiredValue) || Array.isArray(desiredValue) && desiredValue.length === 0;
|
|
49528
|
+
result[topLevelKey] = isTagListKey(topLevelKey) && baselineIsTagList && isCfnTagList(awsValue) ? mergeTagListForRevert(desiredValue, awsValue) : desiredValue;
|
|
49529
|
+
}
|
|
49386
49530
|
}
|
|
49387
49531
|
return result;
|
|
49388
49532
|
}
|
|
@@ -49505,6 +49649,15 @@ function printRevertPlan(reports) {
|
|
|
49505
49649
|
process.stdout.write(` → provider.update on ${o.logicalId} (${o.resourceType}): revert ${o.changes.length} ${word}\n`);
|
|
49506
49650
|
for (const change of o.changes) process.stdout.write(` ${change.path}: ${formatScalar(change.awsValue)} -> ${formatScalar(change.stateValue)}\n`);
|
|
49507
49651
|
const stateResource = report.state.resources[o.logicalId];
|
|
49652
|
+
if (stateResource) {
|
|
49653
|
+
const preserved = findRevertPreservedTagKeys(o.changes, stateResource.observedProperties ?? stateResource.properties ?? {}, o.awsProperties);
|
|
49654
|
+
if (preserved.length > 0) {
|
|
49655
|
+
const tagWord = preserved.length === 1 ? "tag" : "tags";
|
|
49656
|
+
process.stdout.write(` ! reverting this tag list KEEPS ${preserved.length} AWS-authored ${tagWord} the baseline does not carry:\n`);
|
|
49657
|
+
for (const path of preserved) process.stdout.write(` ${path}\n`);
|
|
49658
|
+
process.stdout.write(" Every other tag reverts normally. A service may require these (ECS needs AmazonECSManaged for managed scaling); 'aws:'-prefixed keys are AWS-reserved and cannot be removed by hand.\n");
|
|
49659
|
+
}
|
|
49660
|
+
}
|
|
49508
49661
|
if (stateResource && stateResource.observedProperties === void 0) {
|
|
49509
49662
|
const dropped = findRevertDroppedAwsKeys(o.changes, stateResource.properties ?? {}, o.awsProperties);
|
|
49510
49663
|
if (dropped.length > 0) {
|
|
@@ -66463,7 +66616,7 @@ function createMigrateCommand() {
|
|
|
66463
66616
|
*/
|
|
66464
66617
|
function buildProgram() {
|
|
66465
66618
|
const program = new Command();
|
|
66466
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.
|
|
66619
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.35");
|
|
66467
66620
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
66468
66621
|
const { profile } = actionCommand.optsWithGlobals();
|
|
66469
66622
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|