@go-to-k/cdkd 0.289.7 → 0.289.9
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-D6Zyjgrr.js → asg-provider-BNOp-qgj.js} +2 -2
- package/dist/{asg-provider-D6Zyjgrr.js.map → asg-provider-BNOp-qgj.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-DrTOY3f-.js → deploy-engine-BwZFYWGI.js} +199 -68
- package/dist/{deploy-engine-DrTOY3f-.js.map → deploy-engine-BwZFYWGI.js.map} +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-Ca0PiFL-.js → program-gLF8ioT_.js} +30 -57
- package/dist/{program-Ca0PiFL-.js.map → program-gLF8ioT_.js.map} +1 -1
- package/dist/{version-DJGTB4Rm.js → version-DIRhHTEG.js} +2 -2
- package/dist/{version-DJGTB4Rm.js.map → version-DIRhHTEG.js.map} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as isVersionOnlyInvocation, t as getCdkdVersion } from "./version-
|
|
2
|
+
import { n as isVersionOnlyInvocation, t as getCdkdVersion } from "./version-DIRhHTEG.js";
|
|
3
3
|
|
|
4
4
|
//#region src/cli/pipe-close-handler.ts
|
|
5
5
|
/**
|
|
@@ -74,7 +74,7 @@ async function main() {
|
|
|
74
74
|
console.log(getCdkdVersion());
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
const { buildProgram } = await import("./program-
|
|
77
|
+
const { buildProgram } = await import("./program-gLF8ioT_.js");
|
|
78
78
|
const program = buildProgram();
|
|
79
79
|
const args = reorderArgs(process.argv);
|
|
80
80
|
await program.parseAsync(args);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { b as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, y as UNRENDERABLE } from "./logger-DTEUzYP_.js";
|
|
2
2
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-DIRhHTEG.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -1082,8 +1082,8 @@ var ResourceTimeoutError = class ResourceTimeoutError extends CdkdError {
|
|
|
1082
1082
|
operation;
|
|
1083
1083
|
timeoutMs;
|
|
1084
1084
|
constructor(logicalId, resourceType, region, elapsedMs, operation, timeoutMs) {
|
|
1085
|
-
const elapsedLabel = formatDuration(elapsedMs);
|
|
1086
|
-
const timeoutLabel = formatDuration(timeoutMs);
|
|
1085
|
+
const elapsedLabel = formatDuration$1(elapsedMs);
|
|
1086
|
+
const timeoutLabel = formatDuration$1(timeoutMs);
|
|
1087
1087
|
super(`Resource ${logicalId} (${resourceType}) in ${region} timed out after ${timeoutLabel} during ${operation} (elapsed ${elapsedLabel}).\nThis may indicate a stuck Cloud Control polling loop, hung Custom Resource, or
|
|
1088
1088
|
slow ENI provisioning. Re-run with --resource-timeout ${resourceType}=<DURATION>\nto bump the budget for this resource type only, or --verbose to see the
|
|
1089
1089
|
underlying provider activity.`, "RESOURCE_TIMEOUT");
|
|
@@ -1102,7 +1102,7 @@ underlying provider activity.`, "RESOURCE_TIMEOUT");
|
|
|
1102
1102
|
* (`30m`, `1h30m`, `45s`). Used by {@link ResourceTimeoutError} so the
|
|
1103
1103
|
* error message stays compact.
|
|
1104
1104
|
*/
|
|
1105
|
-
function formatDuration(ms) {
|
|
1105
|
+
function formatDuration$1(ms) {
|
|
1106
1106
|
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
1107
1107
|
const totalMinutes = Math.round(ms / 6e4);
|
|
1108
1108
|
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
@@ -9239,13 +9239,39 @@ const CUSTOM_RESOURCE_RESPONSE_PREFIX = "custom-resource-responses";
|
|
|
9239
9239
|
* `for nested-stack child`), so a user grepping CI logs for one of them found
|
|
9240
9240
|
* two of three. `subject` now varies only the noun.
|
|
9241
9241
|
*/
|
|
9242
|
-
/**
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9242
|
+
/**
|
|
9243
|
+
* The ONE rendering of a lock's deadline: `expires in 1m23s` / `expired 45s
|
|
9244
|
+
* ago` for a finite `expiresAt`, `expires at an unknown time` otherwise
|
|
9245
|
+
* (issue #3085). Consumed by this module's contention refusal, by
|
|
9246
|
+
* `LockManager`'s three sites (the expired-lock takeover warning, the acquire
|
|
9247
|
+
* retry line, the final `LockError`) and by `cdkd state show`'s lock row, so
|
|
9248
|
+
* a record renders the same everywhere it is shown.
|
|
9249
|
+
*
|
|
9250
|
+
* Takes the RAW `expiresAt`, never a difference: the field is an unchecked
|
|
9251
|
+
* cast, and `Number.isFinite` on the raw value is the test `isLockExpired`
|
|
9252
|
+
* makes, so what this SAYS agrees with what the expiry check DOES. Subtracting
|
|
9253
|
+
* first (the previous `formatRemaining(info.expiresAt - Date.now())`) coerced
|
|
9254
|
+
* a numeric string to a real deadline here while the check called it expired
|
|
9255
|
+
* — the one input that still split the renderers after issue #3083. `{}`,
|
|
9256
|
+
* `"soon"`, an absent field, and the `NaN` that `getLockRecord` substitutes
|
|
9257
|
+
* for a coercion that throws (go-to-k/cdkd#2947) all land on the unknown arm.
|
|
9258
|
+
*
|
|
9259
|
+
* Seconds precision, deliberately: this module used to round to `in ~12m`
|
|
9260
|
+
* "without implying more precision than a clock skew allows", but the
|
|
9261
|
+
* `LockError` refusal that advises the same force-unlock decision already
|
|
9262
|
+
* printed `expires in 4m12s`, so the coarser form was a second spelling of the
|
|
9263
|
+
* same fact rather than a guard against skew.
|
|
9264
|
+
*/
|
|
9265
|
+
function formatLockExpiry(expiresAt) {
|
|
9266
|
+
if (!Number.isFinite(expiresAt)) return "expires at an unknown time";
|
|
9267
|
+
const remainingMs = expiresAt - Date.now();
|
|
9268
|
+
return remainingMs > 0 ? `expires in ${formatDuration(remainingMs)}` : `expired ${formatDuration(-remainingMs)} ago`;
|
|
9269
|
+
}
|
|
9270
|
+
/** `1m23s` / `45s` from a non-negative millisecond count. */
|
|
9271
|
+
function formatDuration(ms) {
|
|
9272
|
+
const seconds = Math.floor(ms / 1e3);
|
|
9273
|
+
if (seconds < 60) return `${seconds}s`;
|
|
9274
|
+
return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
|
|
9249
9275
|
}
|
|
9250
9276
|
/**
|
|
9251
9277
|
* Quote a value for a pasteable shell command.
|
|
@@ -9316,10 +9342,10 @@ async function buildLockContentionMessage(args) {
|
|
|
9316
9342
|
const info = await lockManager.getLockInfo(stackName, region);
|
|
9317
9343
|
if (info) {
|
|
9318
9344
|
const operation = info.operation ? `, operation: ${displaySafe(info.operation)}` : "";
|
|
9319
|
-
const expires =
|
|
9345
|
+
const expires = formatLockExpiry(info.expiresAt);
|
|
9320
9346
|
const owner = displaySafe(info.owner);
|
|
9321
9347
|
const holder = owner ? `held by ${owner}${operation}` : `held by an unnamed holder`;
|
|
9322
|
-
held = `${heldClause ? `${heldClause} — ` : ""}${holder},
|
|
9348
|
+
held = `${heldClause ? `${heldClause} — ` : ""}${holder}, ${expires}`;
|
|
9323
9349
|
if (owner) sawHolder = true;
|
|
9324
9350
|
}
|
|
9325
9351
|
} catch {}
|
|
@@ -10500,25 +10526,6 @@ var LockManager = class {
|
|
|
10500
10526
|
return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
|
|
10501
10527
|
}
|
|
10502
10528
|
/**
|
|
10503
|
-
* `expires in 1m23s` / `expired 45s ago` for a finite deadline, `expires at
|
|
10504
|
-
* an unknown time` otherwise — the ONE spelling for a lock's expiry in this
|
|
10505
|
-
* module's messages, so the three sites that render one (the expired-lock
|
|
10506
|
-
* takeover warning, the retry line, the final refusal) cannot disagree.
|
|
10507
|
-
*
|
|
10508
|
-
* The non-finite arm is what stops `expired NaNmNaNs ago` (issue #3083):
|
|
10509
|
-
* `expiresAt` is an unchecked cast, and `getLockRecord` deliberately hands
|
|
10510
|
-
* a coercion that THROWS through as `NaN` (go-to-k/cdkd#2947) rather than
|
|
10511
|
-
* inventing a deadline. The wording matches `formatRemaining` in
|
|
10512
|
-
* `lock-contention-message.ts`, which answered the same input this way
|
|
10513
|
-
* first. `isLockExpired` decides what such a value MEANS (expired); this
|
|
10514
|
-
* only decides what it SAYS.
|
|
10515
|
-
*/
|
|
10516
|
-
formatExpiry(expiresAt) {
|
|
10517
|
-
if (!Number.isFinite(expiresAt)) return "expires at an unknown time";
|
|
10518
|
-
const remainingMs = expiresAt - Date.now();
|
|
10519
|
-
return remainingMs > 0 ? `expires in ${this.formatDuration(remainingMs)}` : `expired ${this.formatDuration(-remainingMs)} ago`;
|
|
10520
|
-
}
|
|
10521
|
-
/**
|
|
10522
10529
|
* Try to acquire a lock for a stack
|
|
10523
10530
|
*
|
|
10524
10531
|
* Uses If-None-Match: "*" to ensure atomic lock acquisition.
|
|
@@ -10573,7 +10580,7 @@ var LockManager = class {
|
|
|
10573
10580
|
} else throw deleteError;
|
|
10574
10581
|
}
|
|
10575
10582
|
const why = Number.isFinite(existing.info.expiresAt) ? "A live cdkd process renews its lock well inside the TTL, so this normally means the previous owner crashed or was suspended" : `Its expiresAt is not a finite number, which cdkd treats as already expired`;
|
|
10576
|
-
this.logger.warn(`Took over an EXPIRED lock for stack: ${stackName} (${region}, owner: ${existing.info.owner}, ${
|
|
10583
|
+
this.logger.warn(`Took over an EXPIRED lock for stack: ${stackName} (${region}, owner: ${existing.info.owner}, ${formatLockExpiry(existing.info.expiresAt)}). ${why} -- if it is in fact still running, both processes are now writing to the same stack.`);
|
|
10577
10584
|
try {
|
|
10578
10585
|
const retryEtag = await this.putLockObject(key, lockInfo);
|
|
10579
10586
|
this.logger.debug(`Lock acquired for stack: ${stackName} (${region}) after expired lock cleanup, owner: ${lockOwner}`);
|
|
@@ -11170,14 +11177,14 @@ var LockManager = class {
|
|
|
11170
11177
|
const lockInfo = await this.getLockInfo(stackName, region);
|
|
11171
11178
|
if (lockInfo) {
|
|
11172
11179
|
if (attempt < maxRetries) {
|
|
11173
|
-
this.logger.info(`Stack '${safeSegment(stackName)}' (${safeSegment(region)}) is locked by ${lockInfo.owner}${lockInfo.operation ? ` (operation: ${lockInfo.operation})` : ""}. Lock ${
|
|
11180
|
+
this.logger.info(`Stack '${safeSegment(stackName)}' (${safeSegment(region)}) is locked by ${lockInfo.owner}${lockInfo.operation ? ` (operation: ${lockInfo.operation})` : ""}. Lock ${formatLockExpiry(lockInfo.expiresAt)}. Retrying in ${this.formatDuration(retryDelay)}... (attempt ${attempt + 1}/${maxRetries})`);
|
|
11174
11181
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
11175
11182
|
continue;
|
|
11176
11183
|
}
|
|
11177
11184
|
}
|
|
11178
11185
|
}
|
|
11179
11186
|
const lockInfo = await this.getLockInfo(stackName, region);
|
|
11180
|
-
const expiry = lockInfo ?
|
|
11187
|
+
const expiry = lockInfo ? formatLockExpiry(lockInfo.expiresAt) : void 0;
|
|
11181
11188
|
const forceUnlockCommand = buildForceUnlockCommand(stackName, region);
|
|
11182
11189
|
const recovery = forceUnlockCommand ? `If you are certain no other process is active, run: ${forceUnlockCommand}` : `If you are certain no other process is active, ${UNREPRODUCIBLE_LOCK_CLAUSE.charAt(0).toLowerCase()}${UNREPRODUCIBLE_LOCK_CLAUSE.slice(1)}`;
|
|
11183
11190
|
const safeStack = safeSegment(stackName);
|
|
@@ -11394,7 +11401,10 @@ const sameGenerationBags = /* @__PURE__ */ new WeakSet();
|
|
|
11394
11401
|
* stored. On the no-change path the newly redacted bag is installed when
|
|
11395
11402
|
* the outputs CHANGED, and the previous `persistedOutputs` is kept
|
|
11396
11403
|
* otherwise; that previous bag is unmarked, which is the answer that arm
|
|
11397
|
-
* wants.
|
|
11404
|
+
* wants. When an output FAILED there, what is installed is either a fresh
|
|
11405
|
+
* merge of this pass's values with the previous bag's carried ones (issue
|
|
11406
|
+
* #2771), mixed provenance and never marked, or — when that merge refuses —
|
|
11407
|
+
* the previous bag kept whole, unmarked as above.
|
|
11398
11408
|
* - the update arm's no-change re-check — a marked `{ ...resolvedProps }`
|
|
11399
11409
|
* compared against the stored record so a stored token reads as a no-op.
|
|
11400
11410
|
* NOT installed; the object the provider is handed is the unmarked original.
|
|
@@ -12179,18 +12189,28 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
12179
12189
|
* and the same generation the bag was resolved from one statement earlier. That
|
|
12180
12190
|
* is the identical pairing `redactRollbackRecord` already makes for the record
|
|
12181
12191
|
* it positions, so the two replay walks now agree about what their source is.
|
|
12192
|
+
* Since issue #3090 the `trustAnyExpression` half of that claim no longer
|
|
12193
|
+
* changes what THIS recorder writes for a reference the pass resolved:
|
|
12194
|
+
* refusal 5 asks the pair table under both rule sets, and a resolved unpinned
|
|
12195
|
+
* `ssm` reference has a pair (only its pin is withheld), so either ruleset
|
|
12196
|
+
* positions it through the span arm's empty frame. What the constants still
|
|
12197
|
+
* decide here is `sourceIsSameGeneration`, which a token-shaped plaintext
|
|
12198
|
+
* reaches (fenced by the replay call sites' own file).
|
|
12182
12199
|
*
|
|
12183
12200
|
* "No public expressions" carries the carve-out {@link PathSourceRules} states
|
|
12184
12201
|
* and this note must not restate without it: `cdkd import` warns and persists
|
|
12185
12202
|
* the RAW template intrinsic, so a public `ssm:` token CAN sit in a record. The
|
|
12186
|
-
* replay then
|
|
12187
|
-
*
|
|
12188
|
-
* resolved
|
|
12189
|
-
*
|
|
12190
|
-
*
|
|
12191
|
-
*
|
|
12192
|
-
*
|
|
12193
|
-
*
|
|
12203
|
+
* replay's POSITION pass then certifies one the deploy default would refuse;
|
|
12204
|
+
* since issue #3090 refusal 5 refuses to RECORD it under either ruleset (a
|
|
12205
|
+
* token resolved as public is never paired), so the child's leaf falls to the
|
|
12206
|
+
* value scan. Before that the cost was bounded to the issue #1901 class — a
|
|
12207
|
+
* spurious UPDATE over a value state should hold resolved, never a
|
|
12208
|
+
* disclosure, since an expression is what gets persisted either way. See the
|
|
12209
|
+
* replay call sites for why gating on {@link isKnownSecretExpression} is the
|
|
12210
|
+
* wrong way to close it.
|
|
12211
|
+
*
|
|
12212
|
+
* FIVE REFUSALS, each degrading to today's behaviour (the child leaf falls to
|
|
12213
|
+
* the plaintext-keyed value scan); the fifth is stated at its line:
|
|
12194
12214
|
*
|
|
12195
12215
|
* 1. REFUSAL — the resolved parameter value is not a WHOLE recorded plaintext.
|
|
12196
12216
|
* A parameter the parent built with an `Fn::Sub` merely EMBEDS the secret,
|
|
@@ -12336,9 +12356,11 @@ const UNFRAMED_SPELLING = Symbol("cdkd.nested-parameter.unframed-spelling");
|
|
|
12336
12356
|
* the value, which the entry provides -- or the child's OWN resolution of
|
|
12337
12357
|
* the same plaintext, in which case the association answers with the leaf's
|
|
12338
12358
|
* own frame, the right reference for it. So a FRAMED association whose value
|
|
12339
|
-
* (iii) refused is inert or correct, never wrong -- a claim
|
|
12340
|
-
*
|
|
12341
|
-
*
|
|
12359
|
+
* (iii) refused is inert or correct, never wrong -- a claim the whole-token
|
|
12360
|
+
* walk's STRING-source half now earns the same way, through refusal 5 (issue
|
|
12361
|
+
* #3090); its intrinsic-source half rests on the reader's condition 3, less
|
|
12362
|
+
* the skeleton arm's own unpinned-`ssm` residual (stated on that arm).
|
|
12363
|
+
* The association's expression is
|
|
12342
12364
|
* the FRAME, not a token -- the one writer into that table that stores a
|
|
12343
12365
|
* non-token, said so on {@link storeAssociation}; its readers return it to be
|
|
12344
12366
|
* persisted, which is exactly what the entry would have written.
|
|
@@ -12397,6 +12419,7 @@ function recordNestedStackParameterExpressions(secrets, resourceType, resolvedPr
|
|
|
12397
12419
|
if (expression === resolvedValue) continue;
|
|
12398
12420
|
const seenResolvingTo = plaintextIndexOf(secrets).get(expression);
|
|
12399
12421
|
if (seenResolvingTo !== void 0 && seenResolvingTo !== resolvedValue) continue;
|
|
12422
|
+
if (typeof sourceLeaf === "string" && resolvedPlaintextOf(secrets, expression) !== resolvedValue) continue;
|
|
12400
12423
|
if (table === void 0) {
|
|
12401
12424
|
table = /* @__PURE__ */ new Map();
|
|
12402
12425
|
nestedStackParameterExpressions.set(secrets, table);
|
|
@@ -28407,7 +28430,7 @@ var CloudControlProvider = class {
|
|
|
28407
28430
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
28408
28431
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
28409
28432
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
28410
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
28433
|
+
const { ASGProvider } = await import("./asg-provider-BNOp-qgj.js").then((n) => n.n);
|
|
28411
28434
|
const asgProvider = new ASGProvider();
|
|
28412
28435
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
28413
28436
|
}
|
|
@@ -35065,10 +35088,10 @@ async function createReplicationGroupFinalSnapshot(client, replicationGroupId, l
|
|
|
35065
35088
|
*
|
|
35066
35089
|
* The deploy side (`DeployEngine.handleOutputResolutionFailure`, default arm)
|
|
35067
35090
|
* warns, stores `undefined` for the key and moves on, so a re-resolved
|
|
35068
|
-
* `state.outputs` lacks it (the no-change path keeps
|
|
35069
|
-
*
|
|
35070
|
-
* its value beside a record — the reader checks absence first and ignores
|
|
35071
|
-
* record then). The diff side resolves outputs with `skipDynamicReferences`, so a
|
|
35091
|
+
* `state.outputs` lacks it (the no-change path keeps a failed key's STORED
|
|
35092
|
+
* value, go-to-k/cdkd#2771, so a key that resolved on an EARLIER deploy can
|
|
35093
|
+
* keep its value beside a record — the reader checks absence first and ignores
|
|
35094
|
+
* the record then). The diff side resolves outputs with `skipDynamicReferences`, so a
|
|
35072
35095
|
* failure that happens INSIDE a secret lookup — a JSON key the secret does not
|
|
35073
35096
|
* hold, a reference assembled from another secret's value — does not reproduce
|
|
35074
35097
|
* there: the value assembles into its token, the key is absent from state, and
|
|
@@ -35149,10 +35172,10 @@ async function createReplicationGroupFinalSnapshot(client, replicationGroupId, l
|
|
|
35149
35172
|
* the writing binary's version is knowable and merely unstored: keeping it
|
|
35150
35173
|
* beside the digest and un-binding on mismatch would close that one. All four
|
|
35151
35174
|
* end the same way: the record keeps binding until the next deploy re-resolves
|
|
35152
|
-
* the output, and that deploy publishes the key
|
|
35153
|
-
*
|
|
35154
|
-
* the previous
|
|
35155
|
-
*
|
|
35175
|
+
* the output, and that deploy publishes the key, a still-unresolved SIBLING
|
|
35176
|
+
* included, since the no-change path persists what resolved rather than
|
|
35177
|
+
* keeping the previous bag whole (go-to-k/cdkd#2771; the two shapes it still
|
|
35178
|
+
* keeps whole are in `src/deployment/no-change-outputs-merge.ts`).
|
|
35156
35179
|
* Documented as the accepted limitation in `docs/cli-diff.md`; it is narrower
|
|
35157
35180
|
* than the pre-#2740 behaviour, where the diff was wrong on EVERY run.
|
|
35158
35181
|
*
|
|
@@ -35437,8 +35460,9 @@ function bindingSkippedOutputs(pristineTemplate, record, changedLogicalIds) {
|
|
|
35437
35460
|
/**
|
|
35438
35461
|
* Whether two records — either possibly absent — describe the same skipped
|
|
35439
35462
|
* set with the same digests. The no-change deploy path persists on a
|
|
35440
|
-
* difference here: it is
|
|
35441
|
-
*
|
|
35463
|
+
* difference here: it is what saves this field when the outputs bag that save
|
|
35464
|
+
* would write is unchanged, which is the usual shape — a skipped output that
|
|
35465
|
+
* never had a stored value leaves the bag equal.
|
|
35442
35466
|
*/
|
|
35443
35467
|
function skippedOutputsEqual(a, b) {
|
|
35444
35468
|
const aKeys = a === null || a === void 0 ? [] : Object.keys(a);
|
|
@@ -35447,6 +35471,95 @@ function skippedOutputsEqual(a, b) {
|
|
|
35447
35471
|
return aKeys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && a?.[k] === b?.[k]);
|
|
35448
35472
|
}
|
|
35449
35473
|
|
|
35474
|
+
//#endregion
|
|
35475
|
+
//#region src/deployment/no-change-outputs-merge.ts
|
|
35476
|
+
/**
|
|
35477
|
+
* True when `value` is a string carrying a SECRET-BEARING CloudFormation
|
|
35478
|
+
* dynamic reference: `{{resolve:secretsmanager:` or `{{resolve:ssm-secure:`,
|
|
35479
|
+
* the two spellings that are secret regardless of what they point at.
|
|
35480
|
+
*
|
|
35481
|
+
* A plain `{{resolve:ssm:...}}` is deliberately excluded. Per issue #1901 it is
|
|
35482
|
+
* classified by the parameter's TYPE, and a `String` / `StringList` parameter is
|
|
35483
|
+
* PUBLIC and legitimately persisted RESOLVED. Treating it as a signal would fire
|
|
35484
|
+
* on a perfectly ordinary record, and since `cdkd diff`'s legacy verdict is
|
|
35485
|
+
* record-WIDE, that would withhold every previous value in the stack and tell
|
|
35486
|
+
* the user to run `cdkd scrub`, which would find nothing to fix. The residual is
|
|
35487
|
+
* a pre-#1901 SecureString `ssm:` record, a strictly narrower gap than the false
|
|
35488
|
+
* positive this exclusion removes.
|
|
35489
|
+
*/
|
|
35490
|
+
function isSecretBearingReferenceString(value) {
|
|
35491
|
+
if (typeof value !== "string") return false;
|
|
35492
|
+
return value.includes("{{resolve:secretsmanager:") || value.includes("{{resolve:ssm-secure:");
|
|
35493
|
+
}
|
|
35494
|
+
/**
|
|
35495
|
+
* True when any TOP-LEVEL value of `bag` is itself a secret-bearing expression
|
|
35496
|
+
* string. Leaf granularity on purpose: a container holding an expression next
|
|
35497
|
+
* to a plaintext leaf is exactly the partially redacted residue `cdkd scrub`
|
|
35498
|
+
* admits it can leave, and it must not earn the exoneration.
|
|
35499
|
+
*/
|
|
35500
|
+
function bagHoldsSecretExpression(bag) {
|
|
35501
|
+
if (bag === void 0 || bag === null) return false;
|
|
35502
|
+
return Object.values(bag).some((value) => isSecretBearingReferenceString(value));
|
|
35503
|
+
}
|
|
35504
|
+
function hasOwn(bag, key) {
|
|
35505
|
+
return Object.prototype.hasOwnProperty.call(bag, key);
|
|
35506
|
+
}
|
|
35507
|
+
/**
|
|
35508
|
+
* Decide the bag the no-change path persists when `resolved` holds at least
|
|
35509
|
+
* one unresolved output. See the module doc for the three rules and the two
|
|
35510
|
+
* refusals.
|
|
35511
|
+
*/
|
|
35512
|
+
function mergeNoChangeOutputs(input) {
|
|
35513
|
+
const { persisted, resolved, declaredOutputs, previousExportNames, resolvedExportNames } = input;
|
|
35514
|
+
const failedKeys = Object.keys(resolved).filter((key) => resolved[key] === void 0);
|
|
35515
|
+
const exportNameOf = (outputKey) => declaredOutputs !== void 0 && hasOwn(declaredOutputs, outputKey) ? declaredOutputs[outputKey]?.Export?.Name : void 0;
|
|
35516
|
+
for (const outputKey of failedKeys) {
|
|
35517
|
+
const exportName = exportNameOf(outputKey);
|
|
35518
|
+
if (exportName !== null && typeof exportName === "object" && hasOwn(persisted, outputKey)) return {
|
|
35519
|
+
kind: "kept",
|
|
35520
|
+
reason: "intrinsic-export-name"
|
|
35521
|
+
};
|
|
35522
|
+
}
|
|
35523
|
+
const outputs = Object.create(null);
|
|
35524
|
+
for (const [key, value] of Object.entries(resolved)) if (value !== void 0) outputs[key] = value;
|
|
35525
|
+
const carriedKeys = [];
|
|
35526
|
+
const exportNames = [...resolvedExportNames];
|
|
35527
|
+
const addExport = (name) => {
|
|
35528
|
+
exportNames.push(name);
|
|
35529
|
+
};
|
|
35530
|
+
for (const outputKey of failedKeys) {
|
|
35531
|
+
const exportName = exportNameOf(outputKey);
|
|
35532
|
+
if (hasOwn(persisted, outputKey)) {
|
|
35533
|
+
outputs[outputKey] = persisted[outputKey];
|
|
35534
|
+
carriedKeys.push(outputKey);
|
|
35535
|
+
if (exportName === outputKey && previousExportNames.has(outputKey)) addExport(outputKey);
|
|
35536
|
+
}
|
|
35537
|
+
if (typeof exportName !== "string") continue;
|
|
35538
|
+
if (previousExportNames.has(exportName) && hasOwn(persisted, exportName) && !hasOwn(resolved, exportName) && !hasOwn(outputs, exportName)) {
|
|
35539
|
+
outputs[exportName] = persisted[exportName];
|
|
35540
|
+
carriedKeys.push(exportName);
|
|
35541
|
+
addExport(exportName);
|
|
35542
|
+
}
|
|
35543
|
+
}
|
|
35544
|
+
if (carriedKeys.length > 0 && bagHoldsSecretExpression(outputs) && !bagHoldsSecretExpression(persisted)) return {
|
|
35545
|
+
kind: "kept",
|
|
35546
|
+
reason: "mixed-generation"
|
|
35547
|
+
};
|
|
35548
|
+
return {
|
|
35549
|
+
kind: "merged",
|
|
35550
|
+
outputs,
|
|
35551
|
+
exportNames,
|
|
35552
|
+
carriedKeys
|
|
35553
|
+
};
|
|
35554
|
+
}
|
|
35555
|
+
/** The sentence the deploy engine appends to its keep-whole warning. */
|
|
35556
|
+
function keptWholeReasonText(reason) {
|
|
35557
|
+
switch (reason) {
|
|
35558
|
+
case "intrinsic-export-name": return "An unresolved output declares an intrinsic Export.Name, so the export key it published before cannot be identified and carried.";
|
|
35559
|
+
case "mixed-generation": return "Persisting only the outputs that resolved would put a redacted secret reference beside a carried value in a bag that held none, which would make that value read as redacted.";
|
|
35560
|
+
}
|
|
35561
|
+
}
|
|
35562
|
+
|
|
35450
35563
|
//#endregion
|
|
35451
35564
|
//#region src/analyzer/implicit-delete-deps.ts
|
|
35452
35565
|
/**
|
|
@@ -38934,14 +39047,32 @@ var DeployEngine = class DeployEngine {
|
|
|
38934
39047
|
let persistedOutputs = currentState.outputs ?? {};
|
|
38935
39048
|
if (!this.options.dryRun) {
|
|
38936
39049
|
const resolvedOutputs = this.redactOutputs(await withSharedDrainBudget(() => this.resolveOutputs(effectiveTemplate, currentState.resources, stackName, outputsDigestSource, parameterValues, conditions)));
|
|
38937
|
-
const resolutionFailed = Object.values(resolvedOutputs).some((v) => v === void 0);
|
|
38938
|
-
const outputsChanged = !resolutionFailed && !outputMapsEqual(persistedOutputs, resolvedOutputs);
|
|
38939
|
-
const currentEffectiveExports = new Set(importableOutputKeys(currentState));
|
|
38940
|
-
const resolvedExportSet = new Set(this.resolvedExportNames);
|
|
38941
|
-
const exportSetChanged = !resolutionFailed && (currentEffectiveExports.size !== resolvedExportSet.size || [...resolvedExportSet].some((k) => !currentEffectiveExports.has(k)));
|
|
38942
|
-
if (resolutionFailed && !outputMapsEqual(persistedOutputs, resolvedOutputs)) this.logger.warn("Outputs changed but one or more could not be resolved; keeping the previously persisted outputs. A downstream Fn::ImportValue may fail until the next deploy.");
|
|
38943
39050
|
const observedRefresh = this.observedCaptureTasks.size > 0;
|
|
38944
39051
|
if (observedRefresh) await this.drainObservedCaptures(currentState.resources);
|
|
39052
|
+
const resolutionFailed = Object.values(resolvedOutputs).some((v) => v === void 0);
|
|
39053
|
+
const currentEffectiveExports = new Set(importableOutputKeys(currentState));
|
|
39054
|
+
let merge = resolutionFailed ? mergeNoChangeOutputs({
|
|
39055
|
+
persisted: persistedOutputs,
|
|
39056
|
+
resolved: resolvedOutputs,
|
|
39057
|
+
declaredOutputs: effectiveTemplate.Outputs,
|
|
39058
|
+
previousExportNames: currentEffectiveExports,
|
|
39059
|
+
resolvedExportNames: this.resolvedExportNames
|
|
39060
|
+
}) : void 0;
|
|
39061
|
+
if (merge?.kind === "merged") {
|
|
39062
|
+
for (const key of merge.carriedKeys) Reflect.deleteProperty(this.outputsTemplateSource, key);
|
|
39063
|
+
if (merge.carriedKeys.length > 0 && !bagHoldsSecretExpression(persistedOutputs) && bagHoldsSecretExpression(this.redactOutputs(merge.outputs))) merge = {
|
|
39064
|
+
kind: "kept",
|
|
39065
|
+
reason: "mixed-generation"
|
|
39066
|
+
};
|
|
39067
|
+
}
|
|
39068
|
+
if (merge?.kind === "kept") this.outputsSourceUsable = false;
|
|
39069
|
+
const outputsToPersist = merge === void 0 ? resolvedOutputs : merge.kind === "merged" ? merge.outputs : persistedOutputs;
|
|
39070
|
+
const exportNamesToPersist = merge === void 0 ? this.resolvedExportNames : merge.kind === "merged" ? merge.exportNames : void 0;
|
|
39071
|
+
const outputsChanged = !outputMapsEqual(persistedOutputs, outputsToPersist);
|
|
39072
|
+
const persistExportSet = new Set(exportNamesToPersist ?? []);
|
|
39073
|
+
const exportSetChanged = exportNamesToPersist !== void 0 && (currentEffectiveExports.size !== persistExportSet.size || [...persistExportSet].some((k) => !currentEffectiveExports.has(k)));
|
|
39074
|
+
if (merge?.kind === "kept" && !outputMapsEqual(persistedOutputs, resolvedOutputs)) this.logger.warn(`Outputs changed but one or more could not be resolved; keeping the previously persisted outputs. ${keptWholeReasonText(merge.reason)} A downstream Fn::ImportValue may fail until the next deploy.`);
|
|
39075
|
+
else if (merge?.kind === "merged" && merge.carriedKeys.length > 0) this.logger.debug(`Kept the previously persisted value of ${merge.carriedKeys.length} output key(s) that could not be resolved (no-change path, #2771)`);
|
|
38945
39076
|
const skippedOutputsChanged = !skippedOutputsEqual(currentState.skippedOutputs, this.skippedOutputs);
|
|
38946
39077
|
if (observedRefresh || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged) try {
|
|
38947
39078
|
const refreshedState = {
|
|
@@ -38950,8 +39081,8 @@ var DeployEngine = class DeployEngine {
|
|
|
38950
39081
|
stackName: currentState.stackName,
|
|
38951
39082
|
resources: currentState.resources,
|
|
38952
39083
|
...orphansCarriedFrom(currentState),
|
|
38953
|
-
outputs: outputsChanged ?
|
|
38954
|
-
...
|
|
39084
|
+
outputs: outputsChanged ? outputsToPersist : persistedOutputs,
|
|
39085
|
+
...exportNamesToPersist === void 0 ? exportNamesCarriedFrom(currentState) : { exportNames: [...exportNamesToPersist] },
|
|
38955
39086
|
...this.skippedOutputs && { skippedOutputs: { ...this.skippedOutputs } },
|
|
38956
39087
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
38957
39088
|
lastModified: Date.now()
|
|
@@ -40853,5 +40984,5 @@ var DeployEngine = class DeployEngine {
|
|
|
40853
40984
|
};
|
|
40854
40985
|
|
|
40855
40986
|
//#endregion
|
|
40856
|
-
export {
|
|
40857
|
-
//# sourceMappingURL=deploy-engine-
|
|
40987
|
+
export { getCurrentResourceSecrets as $, validateAssetBucketName as $n, DependencyError as $r, findActionableSilentDrops as $t, planOrphanAdoption as A, CUSTOM_RESOURCE_RESPONSE_PREFIX as An, warnDeprecatedNoPrefixCliFlag as Ar, resolveExplicitPhysicalId as At, bold as B, WorkGraph as Bn, derivePartitionAndUrlSuffix as Br, requireConfigObject as Bt, createPreDeleteFinalSnapshot as C, markNonRetryable as Ci, S3StateBackend as Cn, resolveAutoAssetStorage as Cr, coerceParameterTypedValue as Ct, makeCanonicalizePropertiesFn as D, forceQuitRecoveryClause as Dn, resolveStateBucketWithDefaultAndSource as Dr, refStateLookupFromResource as Dt, unsupportedFinalSnapshotError as E, __exportAll as Ei, buildLockContentionMessage as En, resolveStateBucketWithDefault as Er, parameterTypeMayLoseSecretIdentity as Et, WARM_THROUGHPUT_MEMBERS as F, orphansAfterRollback as Fn, uploadCfnTemplate as Fr, configIntegerRefusal as Ft, yellow as G, AssetModeResolver as Gn, AwsClients as Gr, s3BucketArn as Gt, gray as H, createAssetRedirectResolver as Hn, processStackMessages as Hr, classifyReplaySecretRegion as Ht, coerceWarmThroughput as I, orphansCarriedFrom as In, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Ir, configStringRefusal as It, exportAliasCollisionScrubWarning as J, ensureAssetStorage as Jn, setAwsClients as Jr, s3BucketRegionalDomainName as Jt, collectDeclaredOutputNames as K, BOOTSTRAP_MARKER_PREFIX as Kn, getAwsClients as Kr, s3BucketDomainName as Kt, isWarmThroughputDecrease as L, shouldRetainResource as Ln, expectedOwnerParam as Lr, readConfigString as Lt, isStatefulRecreateTargetForReplace as M, exportNamesCarriedFrom as Mn, CFN_TEMPLATE_URL_LIMIT as Mr, coerceCfnBoolean as Mt, isStatefulRecreateTargetSync as N, importableOutputKeys as Nn, MIGRATE_TMP_PREFIX as Nr, coerceCfnInteger as Nt, extractDeploymentEventError as O, formatLockExpiry as On, resolveUseCdkBootstrapAssets as Or, WAFv2WebACLProvider as Ot, renderStatefulReason as P, importableOutputs as Pn, findLargeInlineResources as Pr, configBooleanRefusal as Pt, secretSafeKeyDisplay as Q, readBootstrapMarkerBody as Qn, CrossAccountSecretRefusalError as Qr, INTRINSIC_KEYS as Qt, toFiniteNumber as R, AssetPublisher as Rn, PARTITION_TABLE as Rr, replayWarn as Rt, ccRoutedFinalSnapshotError as S, isTransientServerError as Si, LockManager as Sn, resolveApp as Sr, cfnRefValueFromPhysicalId as St, refusesFinalSnapshot as T, retryClassificationText as Ti, buildForceUnlockCommand as Tn, resolveSkipPrefix as Tr, isUnboundTemplateParameter as Tt, green as U, loadPublishableAssetManifest as Un, clearBucketRegionCache as Ur, producerRegionsFromState as Ut, cyan as V, buildAssetRedirectMap as Vn, AssemblyReader as Vr, requireConfigString as Vt, red as W, rewriteTemplateAssetReferences as Wn, resolveBucketRegion as Wr, withSharedDrainBudget as Wt, secretBearing as X, isCrossRegionRedirect as Xn, CdkdError as Xr, applyRoleArnIfSet as Xt, isExportAliasCollision as Y, getBootstrapMarkerKey as Yn, AssetError as Yr, s3BucketWebsiteUrl as Yt, secretBearingStateKeyWarning as Z, parseBootstrapMarker as Zn, ConfigError as Zr, DiffCalculator as Zt, isSecretBearingReferenceString as _, normalizeAwsError as _i, maskSecretsInText as _n, getDockerImageBySourceHash as _r, deleteSkipReason as _t, DeploymentEventsStore as a, LockError as ai, STATE_SOURCED_BASELINE_RULES as an, describeDockerExecFailure as ar, createMaskedRetryLogger as at, PRE_DELETE_SNAPSHOT_TYPES as b, isRetryableTransientError as bi, redactSecretsForState as bn, getDefaultStateBucketName as br, IntrinsicFunctionResolver as bt, replayFailedOperations as c, ProvisioningError as ci, TEMPLATE_SOURCED_RULES as cn, formatDockerLoginError as cr, beginCommandInterruptScope as ct, updatePartialReason as d, StackHasActiveImportsError as di, dynamicReferenceTokens as dn, redactDockerArgvValues as dr, isInterruptedWaitError as dt, DeployCancelledError as ei, findSilentDropProperties as en, validateContainerRepoName as er, IAMRoleProvider as et, withResourceDeadline as f, StackTerminationProtectionError as fi, errorCauseChain as fn, runDockerForeground as fr, startInterruptWatch as ft, bagHoldsSecretExpression as g, isCdkdError as gi, maskSecretsInError as gn, AssetManifestLoader as gr, deleteIndeterminateGuards as gt, computeImplicitDeleteEdges as h, formatError as hi, markSameGenerationBag as hn, stripControlChars as hr, UNSPECIFIED_SKIP_REASON as ht, DeploymentEventsReader as i, LocalStartServiceError as ii, TemplateParser as in, describeDockerCapturedOutput as ir, wouldReturnToSdkProvider as it, MULTI_REGION_RECREATE_BLOCKED_TYPES as j, DEFAULT_STATE_PREFIX as jn, CFN_TEMPLATE_BODY_LIMIT as jr, assertRegionMatch as jt, makeSiblingClaimReader as k, shellQuote as kn, stateBucketExistenceConfirmed as kr, normalizeAwsTagsToCfn as kt, replayRollback as l, ResourceTimeoutError as li, carriesSecretMask as ln, getDockerCmd as lr, endCommandInterruptScope as lt, IMPLICIT_DELETE_DEPENDENCIES as m, SynthesisError as mi, isSingleDynamicReferenceToken as mn, escapeRegExp$1 as mr, slowCcOperationTimeoutMs as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, IntrinsicResolutionRefusalError as ni, withRetry as nn, describeAwsFailure as nr, clearOnUpdateRemoval as nt, planFailedOps as o, NestedStackChildDirectDestroyError as oi, STATE_SOURCED_CROSS_GENERATION_RULES as on, describeDockerFailure as or, maskDeep as ot, maskingRetryLogger as p, StateError as pi, identityKeyFor as pn, runDockerStreaming as pr, CloudControlProvider as pt, collectPublishedOutputNames as q, assertAssetBucketRegion as qn, resetAwsClients as qr, s3BucketDualStackDomainName as qt, DeployEngine as r, LocalInvokeBuildError as ri, DagBuilder as rn, buildDockerImage as rr, ProviderRegistry as rt, planRollback as s, PartialFailureError as si, STATE_SOURCED_READBACK_RULES as sn, dockerSpawnEnvWithSensitive as sr, maskerOrIdentity as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, DynamicReferenceRegionAmbiguousError as ti, describeTypeWithThrottleRetry as tn, buildDenyExternalAccessPolicy as tr, collectInlinePolicyNamesManagedBySiblings as tt, updatePartialMessage as u, ResourceUpdateNotSupportedError as ui, createSecretMasker as un, partitionSensitiveEnv as ur, interruptWatchListenerCount as ut, bindingSkippedOutputs as v, withErrorHandling as vi, recordMaskOnlyValue as vn, Synthesizer as vr, disableInstanceApiTermination as vt, isFinalSnapshotError as w, markRedactedCause as wi, rebuildClientForBucketRegion as wn, resolveCaptureObservedState as wr, getAccountInfo as wt, buildFinalSnapshotIdentifier as x, isThrottlingError as xi, scrubResourceRecord as xn, getLegacyStateBucketName as xr, carriesDynamicReference as xt, ATOMIC_FINAL_SNAPSHOT_TYPES as y, isMarkedNonRetryable as yi, recoverMaskedOutput as yn, synthesisStatusMessage as yr, isTerminationProtectionPropagationError as yt, formatResourceLine as z, stringifyValue as zn, canonicalizeRegion as zr, requireConfigArray as zt };
|
|
40988
|
+
//# sourceMappingURL=deploy-engine-BwZFYWGI.js.map
|