@go-to-k/cdkd 0.284.61 → 0.284.63
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-DTolzTHQ.js → asg-provider-B9V61Fbq.js} +2 -2
- package/dist/{asg-provider-DTolzTHQ.js.map → asg-provider-B9V61Fbq.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-0d-g0TT4.js → deploy-engine-B-RuozdO.js} +410 -36
- package/dist/deploy-engine-B-RuozdO.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-CrJMVg4e.js → program-alOnKpLV.js} +260 -39
- package/dist/{program-CrJMVg4e.js.map → program-alOnKpLV.js.map} +1 -1
- package/dist/{version-iXYxz3AH.js → version-Cmw63bLc.js} +2 -2
- package/dist/{version-iXYxz3AH.js.map → version-Cmw63bLc.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-0d-g0TT4.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as beginCommandInterruptScope, $n as PARTITION_TABLE, $t as S3StateBackend, A as renderStatefulReason, An as runDockerForeground, At as dynamicReferenceTokens, B as exportAliasCollisionScrubWarning, Bn as resolveCaptureObservedState, Bt as s3BucketDomainName, C as isFinalSnapshotError, Cn as validateContainerRepoName, Cr as PartialFailureError, Ct as requireConfigArray, D as extractDeploymentEventError, Dn as formatDockerLoginError, Dr as StackHasActiveImportsError, Dt as STATE_SOURCED_READBACK_RULES, E as makeCanonicalizePropertiesFn, En as dockerSpawnEnvWithSensitive, Er as ResourceUpdateNotSupportedError, Et as STATE_SOURCED_CROSS_GENERATION_RULES, F as green, Fn as synthesisStatusMessage, Fr as isMarkedNonRetryable, Ft as redactSecretsForState, G as IAMRoleProvider, Gn as stateBucketExistenceConfirmed, Gt as DiffCalculator, H as secretBearingStateKeyWarning, Hn as resolveStateBucketWithDefault, Ht as s3BucketRegionalDomainName, I as red, In as getDefaultStateBucketName, Ir as isRetryableTransientError, It as scrubResourceRecord, J as ProviderRegistry, Jn as CFN_TEMPLATE_URL_LIMIT, Jt as withRetry, K as collectInlinePolicyNamesManagedBySiblings, Kn as warnDeprecatedNoPrefixCliFlag, Kt as INTRINSIC_KEYS, L as yellow, Ln as getLegacyStateBucketName, Lr as isThrottlingError, Lt as classifyReplaySecretRegion, M as bold, Mn as AssetManifestLoader, Mt as isSingleDynamicReferenceToken, N as cyan, Nn as getDockerImageBySourceHash, Nr as normalizeAwsError, Nt as maskSecretsInError, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as getDockerCmd, Or as StackTerminationProtectionError, Ot as TEMPLATE_SOURCED_RULES, P as gray, Pn as Synthesizer, Pr as withErrorHandling, Pt as maskSecretsInText, Q as DEFAULT_STATE_PREFIX, Qn as expectedOwnerParam, Qt as displaySafe, R as collectDeclaredOutputNames, Rn as resolveApp, Rr as markNonRetryable, Rt as producerRegionsFromState, S as createPreDeleteFinalSnapshot, Sn as validateAssetBucketName, Sr as NestedStackChildDirectDestroyError, St as replayWarn, T as unsupportedFinalSnapshotError, Tn as buildDockerImage, Tr as ResourceTimeoutError, Tt as requireConfigString, U as stateKeySecretExposure, Un as resolveStateBucketWithDefaultAndSource, Ut as s3BucketWebsiteUrl, V as isExportAliasCollision, Vn as resolveSkipPrefix, Vt as s3BucketDualStackDomainName, W as getCurrentResourceSecrets, Wn as resolveUseCdkBootstrapAssets, Wt as applyRoleArnIfSet, X as findSilentDropProperties, Xn as findLargeInlineResources, Xt as TemplateParser, Y as findActionableSilentDrops, Yn as MIGRATE_TMP_PREFIX, Yt as DagBuilder, Z as CUSTOM_RESOURCE_RESPONSE_PREFIX, Zn as uploadCfnTemplate, Zt as LockManager, _ as computeImplicitDeleteEdges, _n as ensureAssetStorage, _r as LocalInvokeBuildError$1, _t as assertRegionMatch, a as DeploymentEventsStore, an as AssetPublisher, ar as resolveBucketRegion, at as slowCcOperationTimeoutMs, b as buildFinalSnapshotIdentifier, bn as parseBootstrapMarker, bt as configStringRefusal, c as replayFailedOperations, cn as buildAssetRedirectMap, cr as resetAwsClients, ct as IntrinsicFunctionResolver, d as updatePartialReason, dn as rewriteTemplateAssetReferences, dr as CdkdError, dt as getAccountInfo, en as rebuildClientForBucketRegion, er as canonicalizeRegion, et as endCommandInterruptScope, f as UNSPECIFIED_SKIP_REASON, fn as escapeRegExp, ft as parameterTypeMayLoseSecretIdentity, g as IMPLICIT_DELETE_DEPENDENCIES, gn as assertAssetBucketRegion, gr as DynamicReferenceRegionAmbiguousError, gt as resolveExplicitPhysicalId, h as maskingRetryLogger, hn as BOOTSTRAP_MARKER_PREFIX, hr as DeployCancelledError, ht as normalizeAwsTagsToCfn, i as DeploymentEventsReader, in as shouldRetainResource, it as CloudControlProvider, j as formatResourceLine, jn as runDockerStreaming, jt as errorCauseChain, k as isStatefulRecreateTargetSync, kn as partitionSensitiveEnv, kt as createSecretMasker, l as replayRollback, ln as createAssetRedirectResolver, lr as setAwsClients, lt as carriesDynamicReference, m as withResourceDeadline, mn as AssetModeResolver, mt as WAFv2WebACLProvider, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as importableOutputKeys, nr as AssemblyReader, nt as isInterruptedWaitError, o as planFailedOps, on as stringifyValue, or as AwsClients, ot as disableInstanceApiTermination, p as deleteSkipReason, pn as stripControlChars, pr as CrossAccountSecretRefusalError, pt as refStateLookupFromResource, q as clearOnUpdateRemoval, qn as CFN_TEMPLATE_BODY_LIMIT, qt as describeTypeWithThrottleRetry, r as DeployEngine, rn as importableOutputs, rr as processStackMessages, rt as startInterruptWatch, s as planRollback, sn as WorkGraph, sr as getAwsClients, st as isTerminationProtectionPropagationError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as exportNamesCarriedFrom, tr as derivePartitionAndUrlSuffix, tt as interruptWatchListenerCount, u as updatePartialMessage, un as loadPublishableAssetManifest, ut as cfnRefValueFromPhysicalId, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as getBootstrapMarkerKey, vr as LocalMigrateError, vt as coerceCfnBoolean, w as refusesFinalSnapshot, wn as buildDenyExternalAccessPolicy, wr as ProvisioningError, wt as requireConfigObject, x as ccRoutedFinalSnapshotError, xn as readBootstrapMarkerBody, xr as MissingCdkCliError, xt as readConfigString, y as PRE_DELETE_SNAPSHOT_TYPES, yn as isCrossRegionRedirect, yr as LocalStartServiceError, yt as configBooleanRefusal, z as collectPublishedOutputNames, zn as resolveAutoAssetStorage, zt as s3BucketArn } from "./deploy-engine-B-RuozdO.js";
|
|
2
2
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-Cmw63bLc.js";
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-B9V61Fbq.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";
|
|
@@ -2032,7 +2032,9 @@ function createListCommand() {
|
|
|
2032
2032
|
* cancellation, and GitLab CI / `docker stop` / Kubernetes send SIGTERM
|
|
2033
2033
|
* directly. cdkd's graceful-interrupt handling (the top-level command
|
|
2034
2034
|
* handlers, the deploy engine's partial-state save, and the per-provider
|
|
2035
|
-
* poll-abort listeners in CustomResource /
|
|
2035
|
+
* poll-abort listeners in CustomResource / CloudFront / ACM — `grep -rn
|
|
2036
|
+
* "process.on('SIGINT'" src/provisioning/providers/` regenerates that closed
|
|
2037
|
+
* set, and Route53 is NOT in it) is
|
|
2036
2038
|
* registered on SIGINT only, so an unhandled SIGTERM killed the process
|
|
2037
2039
|
* mid-run, skipped every `finally`, and stranded the stack lock for its
|
|
2038
2040
|
* full TTL.
|
|
@@ -2082,7 +2084,7 @@ function installSigtermForwarder() {
|
|
|
2082
2084
|
* `cdkd deploy` has had one of these since issue #1348 (`deploy.ts`'s
|
|
2083
2085
|
* `topLevelSigintHandler`). The destroy side had none, so the ONLY channel
|
|
2084
2086
|
* carrying "the user interrupted" from `runDestroyForStack` back to the
|
|
2085
|
-
* `--all` loop was `
|
|
2087
|
+
* `--all` loop was `DestroyRunnerResult.interrupted` — a boolean assigned ONCE,
|
|
2086
2088
|
* inside the runner's `try`, after its level loop. Every instant where the
|
|
2087
2089
|
* runner's `draining` flag flips AFTER that assignment is an instant where
|
|
2088
2090
|
* `cdkd destroy --all` went on to delete the NEXT stack after the user asked
|
|
@@ -2106,10 +2108,38 @@ function installSigtermForwarder() {
|
|
|
2106
2108
|
*
|
|
2107
2109
|
* A handler that lives for the whole command closes both, because it is armed
|
|
2108
2110
|
* in every window rather than in the ones the runner happens to span. The two
|
|
2109
|
-
* flags answer different questions and both are kept
|
|
2110
|
-
*
|
|
2111
|
-
*
|
|
2112
|
-
*
|
|
2111
|
+
* flags answer different questions and both are kept, one owner each:
|
|
2112
|
+
*
|
|
2113
|
+
* - PER-STACK, "did THIS stack finish, or is there work left in it?" —
|
|
2114
|
+
* `DestroyRunnerResult.interrupted`, owned by `destroy-runner.ts`. It has FOUR
|
|
2115
|
+
* consumers: that stack's state preservation, its summary line,
|
|
2116
|
+
* `destroy.ts`'s `--purge-events` skip, and — the one earlier revisions of
|
|
2117
|
+
* this list omitted — `NestedStackProvider.delete`, which reads the CHILD's
|
|
2118
|
+
* result to decide whether the child row is `{ outcome: 'skipped' }` on the
|
|
2119
|
+
* parent and whether its failure is `markNonRetryable`.
|
|
2120
|
+
*
|
|
2121
|
+
* That fourth consumer is why the `&& statePreserved` gate below is a
|
|
2122
|
+
* behavior change beyond the exit code: a fully-destroyed child that takes
|
|
2123
|
+
* a tail signal no longer reports `interrupted`, so the parent no longer
|
|
2124
|
+
* marks its row skipped. That is the CORRECT reading — `skipped` means
|
|
2125
|
+
* "cdkd could not address this, the resources may still exist in AWS",
|
|
2126
|
+
* which is false of a child whose every resource was deleted and whose
|
|
2127
|
+
* state record is gone; it would preserve the parent's row for a child that
|
|
2128
|
+
* no longer exists and exit 2 over a completed teardown. Stopping the
|
|
2129
|
+
* surrounding `--all` run is a DIFFERENT question, still answered by this
|
|
2130
|
+
* watch, which the parent command holds for its whole run.
|
|
2131
|
+
* - PER-RUN, "has the user asked this COMMAND to stop?" — this watch. Its
|
|
2132
|
+
* consumers are the loops' `break`s and the terminal exit-code verdict
|
|
2133
|
+
* (which pairs it with `stoppedEarly`, so a signal in a completed run's
|
|
2134
|
+
* tail does not report unfinished work there is none of).
|
|
2135
|
+
*
|
|
2136
|
+
* `--purge-events` moved onto the per-stack answer when the runner's outer
|
|
2137
|
+
* re-sync was gated on `statePreserved`. Before that the per-stack flag could
|
|
2138
|
+
* be true over a stack whose state was already DELETED, so the purge had to
|
|
2139
|
+
* read the command-level one to stay conservative — at the cost of the
|
|
2140
|
+
* opposite error: a tail signal on the LAST stack skipped the purge while the
|
|
2141
|
+
* command exited 0, reporting full success with the events left behind. With
|
|
2142
|
+
* the per-stack flag accurate, one read serves both directions.
|
|
2113
2143
|
*
|
|
2114
2144
|
* Note on the counterpart contract: `deploy-engine.ts` removes its own SIGINT
|
|
2115
2145
|
* handler before releasing its lock, which is safe only because `deploy.ts`'s
|
|
@@ -2123,6 +2153,11 @@ function installSigtermForwarder() {
|
|
|
2123
2153
|
*/
|
|
2124
2154
|
function watchCommandInterrupt(opts) {
|
|
2125
2155
|
let interrupted = false;
|
|
2156
|
+
const promptAbort = new AbortController();
|
|
2157
|
+
function markInterrupted() {
|
|
2158
|
+
interrupted = true;
|
|
2159
|
+
promptAbort.abort();
|
|
2160
|
+
}
|
|
2126
2161
|
let stacksInFlight = 0;
|
|
2127
2162
|
/**
|
|
2128
2163
|
* Is some OTHER listener going to handle this signal gracefully?
|
|
@@ -2145,27 +2180,30 @@ function watchCommandInterrupt(opts) {
|
|
|
2145
2180
|
* - `interrupt-watch.ts`'s shared handler, which is a LATCH rather than a
|
|
2146
2181
|
* graceful owner — deferring to it is exactly the swallow above.
|
|
2147
2182
|
*
|
|
2148
|
-
* A provider's transient wait listener
|
|
2149
|
-
*
|
|
2150
|
-
*
|
|
2151
|
-
*
|
|
2152
|
-
*
|
|
2183
|
+
* A provider's transient wait listener does count, and that is correct
|
|
2184
|
+
* rather than a false positive: three providers install one — grep
|
|
2185
|
+
* `process.on('SIGINT'` under `src/provisioning/providers/**` for the
|
|
2186
|
+
* closed set, today `acm-certificate-provider.ts`,
|
|
2187
|
+
* `cloudfront-distribution-provider.ts` and `custom-resource-provider.ts`,
|
|
2188
|
+
* NOT Route53, whose provider registers none — and each exists only
|
|
2189
|
+
* DURING a provider wait, which is strictly inside the runner handler's own
|
|
2190
|
+
* lifetime, so one cannot be present while the runner's is absent.
|
|
2153
2191
|
*/
|
|
2154
2192
|
function gracefulOwnerArmed() {
|
|
2155
2193
|
return process.listeners("SIGINT").filter((l) => l !== handler).length - interruptWatchListenerCount() > 0;
|
|
2156
2194
|
}
|
|
2157
2195
|
const handler = () => {
|
|
2158
2196
|
if (stacksInFlight > 0 && gracefulOwnerArmed()) {
|
|
2159
|
-
|
|
2197
|
+
markInterrupted();
|
|
2160
2198
|
return;
|
|
2161
2199
|
}
|
|
2162
2200
|
if (interrupted) {
|
|
2163
|
-
process.stderr.write("\nForce-quit.\n");
|
|
2201
|
+
process.stderr.write("\nForce-quit.\nIf the next run reports a stack lock, release it with: cdkd force-unlock <stack-name>\n");
|
|
2164
2202
|
process.exit(130);
|
|
2165
2203
|
}
|
|
2166
|
-
|
|
2204
|
+
markInterrupted();
|
|
2167
2205
|
if (stacksInFlight > 0) {
|
|
2168
|
-
process.stderr.write(`\nInterrupted before ${opts.command} armed its per-stack teardown — quitting now.
|
|
2206
|
+
process.stderr.write(`\nInterrupted before ${opts.command} armed its per-stack teardown — quitting now. No delete was issued for this stack; on an --all run, stacks processed earlier are already destroyed.\nIf the next run reports a stack lock, release it with: cdkd force-unlock <stack-name>\n`);
|
|
2169
2207
|
process.exit(130);
|
|
2170
2208
|
}
|
|
2171
2209
|
process.stderr.write(`\nInterrupted — ${opts.command} will stop before starting another stack (press Ctrl-C again to force-quit)...\n`);
|
|
@@ -2173,6 +2211,7 @@ function watchCommandInterrupt(opts) {
|
|
|
2173
2211
|
process.on("SIGINT", handler);
|
|
2174
2212
|
return {
|
|
2175
2213
|
interrupted: () => interrupted,
|
|
2214
|
+
signal: promptAbort.signal,
|
|
2176
2215
|
async runStack(fn) {
|
|
2177
2216
|
stacksInFlight += 1;
|
|
2178
2217
|
try {
|
|
@@ -2186,6 +2225,22 @@ function watchCommandInterrupt(opts) {
|
|
|
2186
2225
|
}
|
|
2187
2226
|
};
|
|
2188
2227
|
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Did this rejection come from a prompt aborted through
|
|
2230
|
+
* {@link CommandInterruptWatch.signal}?
|
|
2231
|
+
*
|
|
2232
|
+
* Deliberately duck-typed rather than `instanceof`: what `readline/promises`
|
|
2233
|
+
* rejects with is an internal Node error class in some versions and a
|
|
2234
|
+
* `DOMException` in others, and neither is importable. Both spellings carry
|
|
2235
|
+
* `name === 'AbortError'` / `code === 'ABORT_ERR'`, and the caller pairs this
|
|
2236
|
+
* with its own `watch.interrupted()` check, so a same-named error from an
|
|
2237
|
+
* unrelated abort cannot be mistaken for the user's Ctrl-C.
|
|
2238
|
+
*/
|
|
2239
|
+
function isPromptAbortError(error) {
|
|
2240
|
+
if (typeof error !== "object" || error === null) return false;
|
|
2241
|
+
const { name, code } = error;
|
|
2242
|
+
return name === "AbortError" || code === "ABORT_ERR";
|
|
2243
|
+
}
|
|
2189
2244
|
|
|
2190
2245
|
//#endregion
|
|
2191
2246
|
//#region src/deployment/recreate-targets.ts
|
|
@@ -59254,7 +59309,6 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59254
59309
|
}
|
|
59255
59310
|
}
|
|
59256
59311
|
result.skippedEmpty = true;
|
|
59257
|
-
result.interrupted ||= emptyInterrupted;
|
|
59258
59312
|
return result;
|
|
59259
59313
|
}
|
|
59260
59314
|
const needsStrongRefCheck = !!(state.outputs && Object.keys(state.outputs).length > 0);
|
|
@@ -59327,6 +59381,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59327
59381
|
const renderer = getLiveRenderer();
|
|
59328
59382
|
let draining = false;
|
|
59329
59383
|
let lockHeld = false;
|
|
59384
|
+
let statePreserved = false;
|
|
59330
59385
|
const sigintHandler = () => {
|
|
59331
59386
|
if (draining) {
|
|
59332
59387
|
if (lockHeld) {
|
|
@@ -59643,6 +59698,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59643
59698
|
result.interrupted = draining;
|
|
59644
59699
|
await saveChain;
|
|
59645
59700
|
const preserveState = result.errorCount > 0 || result.interrupted || result.skippedCount > 0;
|
|
59701
|
+
statePreserved = preserveState;
|
|
59646
59702
|
if (!preserveState) {
|
|
59647
59703
|
await ctx.stateBackend.deleteState(stackName, regionForState);
|
|
59648
59704
|
logger.debug("State deleted");
|
|
@@ -59690,7 +59746,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59690
59746
|
process.removeListener("SIGINT", sigintHandler);
|
|
59691
59747
|
restoreBaseRegionAndClients();
|
|
59692
59748
|
}
|
|
59693
|
-
result.interrupted ||= draining;
|
|
59749
|
+
result.interrupted ||= draining && statePreserved;
|
|
59694
59750
|
}
|
|
59695
59751
|
return result;
|
|
59696
59752
|
}
|
|
@@ -59907,7 +59963,7 @@ var NestedStackProvider = class {
|
|
|
59907
59963
|
await this.runChildDeploy(ctx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates);
|
|
59908
59964
|
const attributes = await this.readChildOutputsAsAttributes(ctx, childStackName, childRegion);
|
|
59909
59965
|
return {
|
|
59910
|
-
physicalId: this.synthesizeArn(ctx.accountId,
|
|
59966
|
+
physicalId: this.synthesizeArn(ctx.accountId, childRegion, ctx.parentStackName, logicalId),
|
|
59911
59967
|
attributes
|
|
59912
59968
|
};
|
|
59913
59969
|
}
|
|
@@ -59982,9 +60038,11 @@ var NestedStackProvider = class {
|
|
|
59982
60038
|
throw new Error(`AWS::CloudFormation::Stack: attribute '${attributeName}' is not in the recorded Outputs map. Only 'Outputs.<Key>' references to declared Output names on the child template are supported.`);
|
|
59983
60039
|
}
|
|
59984
60040
|
async runChildDeploy(parentCtx, logicalId, childStackName, childRegion, childTemplate, childParameters, grandchildTemplates) {
|
|
60041
|
+
const inheritedSecrets = getCurrentResourceSecrets();
|
|
59985
60042
|
const childEngine = new DeployEngine(parentCtx.stateBackend, parentCtx.lockManager, parentCtx.dagBuilder, parentCtx.diffCalculator, parentCtx.providerRegistry, {
|
|
59986
60043
|
...parentCtx.options ?? {},
|
|
59987
60044
|
parameters: childParameters,
|
|
60045
|
+
...inheritedSecrets && inheritedSecrets.size > 0 && { inheritedSecrets },
|
|
59988
60046
|
parentStackInfo: {
|
|
59989
60047
|
parentStack: parentCtx.parentStackName,
|
|
59990
60048
|
parentLogicalId: logicalId,
|
|
@@ -60014,8 +60072,22 @@ var NestedStackProvider = class {
|
|
|
60014
60072
|
deriveChildStackName(parentStackName, nestedLogicalId) {
|
|
60015
60073
|
return `${parentStackName}~${nestedLogicalId}`;
|
|
60016
60074
|
}
|
|
60017
|
-
|
|
60018
|
-
|
|
60075
|
+
/**
|
|
60076
|
+
* The synthesized `physicalId` recorded on the PARENT's
|
|
60077
|
+
* `AWS::CloudFormation::Stack` row.
|
|
60078
|
+
*
|
|
60079
|
+
* `childRegion` is the region the child stack was deployed into (its own
|
|
60080
|
+
* `StackState.region`), and the segment is LOAD-BEARING as of issue
|
|
60081
|
+
* [#2055](https://github.com/go-to-k/cdkd/issues/2055):
|
|
60082
|
+
* `IntrinsicFunctionResolver` reads it back as the producer region when a
|
|
60083
|
+
* parent property consumes `Fn::GetAtt: [<Child>, 'Outputs.<Name>']` and the
|
|
60084
|
+
* child's persisted output is a redacted `{{resolve:...}}` expression. A
|
|
60085
|
+
* secret NAME is regional, so resolving that expression anywhere but the
|
|
60086
|
+
* child's own region can answer with a different secret. Equal to the
|
|
60087
|
+
* parent's region today; do not collapse the two.
|
|
60088
|
+
*/
|
|
60089
|
+
synthesizeArn(accountId, childRegion, parentStackName, logicalId) {
|
|
60090
|
+
return `arn:cdkd-local:${childRegion}:${accountId}:nested-stack/${parentStackName}/${logicalId}`;
|
|
60019
60091
|
}
|
|
60020
60092
|
extractParameters(properties) {
|
|
60021
60093
|
const params = properties["Parameters"];
|
|
@@ -60057,6 +60129,42 @@ var NestedStackProvider = class {
|
|
|
60057
60129
|
}
|
|
60058
60130
|
return result;
|
|
60059
60131
|
}
|
|
60132
|
+
/**
|
|
60133
|
+
* Surface the child's PERSISTED outputs as the parent's `Outputs.<Key>`
|
|
60134
|
+
* attributes, VERBATIM — including a secret-bearing one, which PR #1899
|
|
60135
|
+
* persists as its unresolved `{{resolve:...}}` expression.
|
|
60136
|
+
*
|
|
60137
|
+
* DELIBERATELY NOT the place the expression is re-resolved (issue
|
|
60138
|
+
* [#2055](https://github.com/go-to-k/cdkd/issues/2055)). Re-resolving here
|
|
60139
|
+
* would be easy — `readChildOutputsAsAttributes` is already async — and it
|
|
60140
|
+
* would trade one bug for a worse one: this map becomes the parent's
|
|
60141
|
+
* `ResourceState.attributes`, and every CONSUMER resource then reads the
|
|
60142
|
+
* plaintext through `Fn::GetAtt` with no entry in its OWN
|
|
60143
|
+
* `recordedSecretValues`, so the deploy engine's save choke point has nothing
|
|
60144
|
+
* to redact it back to and the consumer's `state.json` persists the decrypted
|
|
60145
|
+
* secret. Keeping the expression here keeps BOTH the parent's nested-stack row
|
|
60146
|
+
* and every consumer's record redacted.
|
|
60147
|
+
*
|
|
60148
|
+
* The re-resolution therefore lives at the READ site, in
|
|
60149
|
+
* `IntrinsicFunctionResolver.resolveGetAtt`, where the CONSUMER's resolver
|
|
60150
|
+
* context is in hand — the same seam and the same reason as the
|
|
60151
|
+
* `Fn::ImportValue` / `Fn::GetStackOutput` arms of issue #1934.
|
|
60152
|
+
*
|
|
60153
|
+
* "A read site risks missing a caller" is answered for the RESOLUTION path by
|
|
60154
|
+
* there being exactly one: the flat-key attribute lookup, which
|
|
60155
|
+
* `getAttribute()` below exists to prove is the only route (it throws for
|
|
60156
|
+
* anything that falls through it).
|
|
60157
|
+
*
|
|
60158
|
+
* It is NOT answered for every reader of `ResourceState.attributes` in the
|
|
60159
|
+
* codebase, and an earlier revision of this note claimed it was.
|
|
60160
|
+
* `AttributeFetcher.cacheFallback` in `src/analyzer/orphan-rewriter.ts` reads
|
|
60161
|
+
* `orphan.attributes?.[attribute]` directly under `cdkd orphan --force` and
|
|
60162
|
+
* has no resolver context to re-resolve with, so it splices the token
|
|
60163
|
+
* verbatim into the referring resource's state and warns about exactly that.
|
|
60164
|
+
* The seam decision above is unchanged — putting the plaintext here would
|
|
60165
|
+
* make EVERY consumer persist it — but the count is "one resolution path",
|
|
60166
|
+
* not "one reader".
|
|
60167
|
+
*/
|
|
60060
60168
|
buildOutputsAttributes(outputs) {
|
|
60061
60169
|
const attributes = {};
|
|
60062
60170
|
for (const [key, value] of Object.entries(outputs)) attributes[`Outputs.${key}`] = value;
|
|
@@ -62641,6 +62749,55 @@ function isStackNotFoundError(err) {
|
|
|
62641
62749
|
//#endregion
|
|
62642
62750
|
//#region src/cli/commands/diff-recursive.ts
|
|
62643
62751
|
const logger = getLogger().child("DiffRecursive");
|
|
62752
|
+
/** Every `Ref` target reachable from a value, collected in one walk. */
|
|
62753
|
+
function collectRefTargets(value, refs) {
|
|
62754
|
+
if (Array.isArray(value)) {
|
|
62755
|
+
for (const element of value) collectRefTargets(element, refs);
|
|
62756
|
+
return;
|
|
62757
|
+
}
|
|
62758
|
+
if (!value || typeof value !== "object") return;
|
|
62759
|
+
const obj = value;
|
|
62760
|
+
if (typeof obj["Ref"] === "string") refs.add(obj["Ref"]);
|
|
62761
|
+
for (const nested of Object.values(obj)) collectRefTargets(nested, refs);
|
|
62762
|
+
}
|
|
62763
|
+
/**
|
|
62764
|
+
* Whether ANY condition in `template` references one of `tokenParameterNames`
|
|
62765
|
+
* (issue #1903, review round 2).
|
|
62766
|
+
*
|
|
62767
|
+
* WHY THIS EXISTS AND WHY THE BROAD FORM IS NOT MERELY CONSERVATIVE. The
|
|
62768
|
+
* token-parameter guard originally skipped condition evaluation for the WHOLE
|
|
62769
|
+
* template as soon as any bound parameter was a redacted token, on the argument
|
|
62770
|
+
* that the cost is "a condition-false resource is diffed rather than pruned".
|
|
62771
|
+
* That is only half of it: leaving `conditions` UNDEFINED also means `resolveIf`
|
|
62772
|
+
* finds no entry, warns, and takes the FALSE branch for every `Fn::If` in every
|
|
62773
|
+
* property value — so a condition-TRUE property diffs as a spurious UPDATE
|
|
62774
|
+
* (perpetual, and `--fail` exits 1) and condition-gated resources surface as
|
|
62775
|
+
* phantom CREATEs. And it fired whenever ANY parameter was token-valued, even
|
|
62776
|
+
* when not one condition mentioned it.
|
|
62777
|
+
*
|
|
62778
|
+
* So the skip is narrowed to what it can actually get wrong: a condition whose
|
|
62779
|
+
* verdict DEPENDS on a value this side does not have.
|
|
62780
|
+
*
|
|
62781
|
+
* NO TRANSITIVE `{Condition: X}` WALK, and its absence is a reachability
|
|
62782
|
+
* argument rather than an omission. An `Fn::And` over a NAMED condition (issue
|
|
62783
|
+
* #840's shape) does inherit that condition's dependency — but the named
|
|
62784
|
+
* condition is itself an entry in this same `Conditions` map, and this scan
|
|
62785
|
+
* visits EVERY entry, so the dependency is already found directly on the
|
|
62786
|
+
* referenced condition. A chain walk was written first and measured dead: with
|
|
62787
|
+
* it removed, a template whose `IsDev` wraps `{Condition: IsProd}` while only
|
|
62788
|
+
* `IsProd` names the token parameter still answers `true`.
|
|
62789
|
+
*/
|
|
62790
|
+
function conditionsDependOnTokenParameters(template, tokenParameterNames) {
|
|
62791
|
+
const templateConditions = template.Conditions;
|
|
62792
|
+
if (!templateConditions || typeof templateConditions !== "object") return false;
|
|
62793
|
+
if (tokenParameterNames.size === 0) return false;
|
|
62794
|
+
for (const definition of Object.values(templateConditions)) {
|
|
62795
|
+
const refs = /* @__PURE__ */ new Set();
|
|
62796
|
+
collectRefTargets(definition, refs);
|
|
62797
|
+
for (const ref of refs) if (tokenParameterNames.has(ref)) return true;
|
|
62798
|
+
}
|
|
62799
|
+
return false;
|
|
62800
|
+
}
|
|
62644
62801
|
/** Empty template used to diff a removed nested child's state → all DELETE. */
|
|
62645
62802
|
const EMPTY_TEMPLATE = { Resources: {} };
|
|
62646
62803
|
/**
|
|
@@ -62731,6 +62888,11 @@ async function computeStackDiff(currentState, template, region, stackName, state
|
|
|
62731
62888
|
const intrinsicResolver = new IntrinsicFunctionResolver(region, { cfnFallback: cfnFallback ?? true });
|
|
62732
62889
|
let mergedParameters = parameters;
|
|
62733
62890
|
let parametersBound = false;
|
|
62891
|
+
const tokenParameterNames = new Set(Object.entries(parameters ?? {}).filter(([, value]) => carriesDynamicReference(value)).map(([name]) => name));
|
|
62892
|
+
for (const name of tokenParameterNames) {
|
|
62893
|
+
const declaredType = (template.Parameters?.[name])?.Type;
|
|
62894
|
+
if (typeof declaredType === "string" && parameterTypeMayLoseSecretIdentity(declaredType)) logger.warn(`Stack ${stackName}: parameter '${name}' is declared 'Type: ${declaredType}' and is fed a secret dynamic reference. 'cdkd deploy' refuses this — declare it 'Type: String' — and this diff keeps the unresolved reference rather than coercing it.`);
|
|
62895
|
+
}
|
|
62734
62896
|
try {
|
|
62735
62897
|
const userParameters = {};
|
|
62736
62898
|
for (const [name, value] of Object.entries(parameters ?? {})) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") userParameters[name] = String(value);
|
|
@@ -62739,13 +62901,14 @@ async function computeStackDiff(currentState, template, region, stackName, state
|
|
|
62739
62901
|
...parameters,
|
|
62740
62902
|
...templateParameters
|
|
62741
62903
|
};
|
|
62904
|
+
for (const name of tokenParameterNames) mergedParameters[name] = (parameters ?? {})[name];
|
|
62742
62905
|
parametersBound = true;
|
|
62743
62906
|
} catch (error) {
|
|
62744
62907
|
logger.debug(`Diff parameter binding for stack ${stackName} is partial: ${error instanceof Error ? error.message : String(error)}`);
|
|
62745
62908
|
}
|
|
62746
62909
|
let effectiveTemplate = template;
|
|
62747
62910
|
let conditions;
|
|
62748
|
-
if (parametersBound) try {
|
|
62911
|
+
if (parametersBound && !conditionsDependOnTokenParameters(template, tokenParameterNames)) try {
|
|
62749
62912
|
conditions = await intrinsicResolver.evaluateConditions({
|
|
62750
62913
|
template,
|
|
62751
62914
|
resources: currentState.resources,
|
|
@@ -62818,7 +62981,8 @@ async function resolveChildStackParameters(parentStackRow, parentTemplate, paren
|
|
|
62818
62981
|
stateBackend,
|
|
62819
62982
|
stackName: parentStackName,
|
|
62820
62983
|
bestEffort: true,
|
|
62821
|
-
...parentParameters && { parameters: parentParameters }
|
|
62984
|
+
...parentParameters && { parameters: parentParameters },
|
|
62985
|
+
skipDynamicReferences: true
|
|
62822
62986
|
});
|
|
62823
62987
|
} catch {}
|
|
62824
62988
|
return resolved;
|
|
@@ -68750,6 +68914,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68750
68914
|
let totalSkipped = 0;
|
|
68751
68915
|
let interrupted = false;
|
|
68752
68916
|
const runInterrupted = () => interrupted || interruptWatch.interrupted();
|
|
68917
|
+
let stoppedEarly = false;
|
|
68753
68918
|
let stackNames;
|
|
68754
68919
|
if (options.all) stackNames = candidateStacks.map((s) => s.stackName);
|
|
68755
68920
|
else if (stackPatterns.length > 0) stackNames = matchStacks(candidateStacks, stackPatterns).map((s) => s.stackName);
|
|
@@ -68790,7 +68955,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68790
68955
|
arr.push(ref);
|
|
68791
68956
|
stateRefsByName.set(ref.stackName, arr);
|
|
68792
68957
|
}
|
|
68793
|
-
for (const stackName of stackNames) {
|
|
68958
|
+
for (const [stackIndex, stackName] of stackNames.entries()) {
|
|
68794
68959
|
logger.info(`\nPreparing to destroy stack: ${stackName}`);
|
|
68795
68960
|
const refs = stateRefsByName.get(stackName) ?? [];
|
|
68796
68961
|
const synthStack = appStacks.find((s) => s.stackName === stackName);
|
|
@@ -68826,7 +68991,10 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68826
68991
|
totalErrors++;
|
|
68827
68992
|
continue;
|
|
68828
68993
|
}
|
|
68829
|
-
if (runInterrupted())
|
|
68994
|
+
if (runInterrupted()) {
|
|
68995
|
+
stoppedEarly = true;
|
|
68996
|
+
break;
|
|
68997
|
+
}
|
|
68830
68998
|
const eventRecorder = startRunRecorder({
|
|
68831
68999
|
backend: stateBackend,
|
|
68832
69000
|
stackName,
|
|
@@ -68834,6 +69002,8 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68834
69002
|
command: "destroy"
|
|
68835
69003
|
});
|
|
68836
69004
|
let destroyRunResult = "SUCCEEDED";
|
|
69005
|
+
let stackInterrupted = false;
|
|
69006
|
+
let stackCancelled = false;
|
|
68837
69007
|
try {
|
|
68838
69008
|
const result = await interruptWatch.runStack(() => withNestedStackContext({
|
|
68839
69009
|
stateBackend,
|
|
@@ -68877,6 +69047,8 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68877
69047
|
})));
|
|
68878
69048
|
totalErrors += result.errorCount;
|
|
68879
69049
|
totalSkipped += result.skippedCount;
|
|
69050
|
+
stackInterrupted = result.interrupted;
|
|
69051
|
+
stackCancelled = result.cancelled === true;
|
|
68880
69052
|
if (result.interrupted) interrupted = true;
|
|
68881
69053
|
destroyRunResult = result.errorCount > 0 || result.skippedCount > 0 ? "FAILED" : "SUCCEEDED";
|
|
68882
69054
|
eventRecorder.record({
|
|
@@ -68901,12 +69073,15 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68901
69073
|
await purgeEventsAfterDestroy(new DeploymentEventsReader(stateBackend), stackName, stackTargetRegion, {
|
|
68902
69074
|
purgeEvents: options.purgeEvents,
|
|
68903
69075
|
runResult: destroyRunResult,
|
|
68904
|
-
interrupted:
|
|
69076
|
+
interrupted: stackInterrupted || stackCancelled
|
|
68905
69077
|
}, logger);
|
|
68906
|
-
if (runInterrupted())
|
|
69078
|
+
if (runInterrupted()) {
|
|
69079
|
+
if (stackIndex < stackNames.length - 1) stoppedEarly = true;
|
|
69080
|
+
break;
|
|
69081
|
+
}
|
|
68907
69082
|
}
|
|
68908
69083
|
if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd destroy' to retry. If the same resource keeps failing, 'cdkd state orphan <stack>' removes the state record without deleting AWS resources.`);
|
|
68909
|
-
if (
|
|
69084
|
+
if (interrupted || interruptWatch.interrupted() && stoppedEarly) throw new PartialFailureError(`Destroy interrupted by Ctrl-C. State preserved — re-run 'cdkd destroy' to finish.`);
|
|
68910
69085
|
if (totalSkipped > 0) throw new PartialFailureError(`Destroy skipped ${totalSkipped} entr${totalSkipped === 1 ? "y" : "ies"} cdkd could not address, so the underlying resources may still exist in AWS. A skipped nested stack counts as ONE entry and may cover several of its own resources — the per-stack summaries above give the exact breakdown. State preserved (the records are kept). Repair the physicalId in state.json and re-run 'cdkd destroy', or delete the resources by hand and drop the records with 'cdkd state orphan <stack>'.`);
|
|
68911
69086
|
} finally {
|
|
68912
69087
|
unforwardSigterm();
|
|
@@ -73505,12 +73680,24 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73505
73680
|
process.stdout.write(`\nWARNING: This destroys ${stackNames.length} stack(s) and removes their state records:\n`);
|
|
73506
73681
|
for (const name of stackNames) process.stdout.write(` - ${name}\n`);
|
|
73507
73682
|
process.stdout.write("\n");
|
|
73683
|
+
if (process.stdin.isTTY !== true) throw new CdkdError("The state destroy --all confirmation prompt cannot run in a non-interactive environment. Pass --yes / -y to confirm the batch, or run the command from a real terminal.", "NON_INTERACTIVE_CONFIRM");
|
|
73508
73684
|
const rl = readline$1.createInterface({
|
|
73509
73685
|
input: process.stdin,
|
|
73510
73686
|
output: process.stdout
|
|
73511
73687
|
});
|
|
73512
|
-
|
|
73513
|
-
|
|
73688
|
+
let answer;
|
|
73689
|
+
try {
|
|
73690
|
+
answer = await rl.question(`Destroy all ${stackNames.length} stack(s)? (y/N): `, { signal: interruptWatch.signal });
|
|
73691
|
+
} catch (error) {
|
|
73692
|
+
if (interruptWatch.interrupted() && isPromptAbortError(error)) {
|
|
73693
|
+
process.stderr.write("\nDestroy cancelled — nothing was destroyed.\n");
|
|
73694
|
+
rl.close();
|
|
73695
|
+
process.exit(130);
|
|
73696
|
+
}
|
|
73697
|
+
throw error;
|
|
73698
|
+
} finally {
|
|
73699
|
+
rl.close();
|
|
73700
|
+
}
|
|
73514
73701
|
const trimmed = answer.trim().toLowerCase();
|
|
73515
73702
|
if (trimmed !== "y" && trimmed !== "yes") {
|
|
73516
73703
|
logger.info("Destroy cancelled");
|
|
@@ -73522,7 +73709,31 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73522
73709
|
let totalSkipped = 0;
|
|
73523
73710
|
let interrupted = false;
|
|
73524
73711
|
const runInterrupted = () => interrupted || interruptWatch.interrupted();
|
|
73525
|
-
|
|
73712
|
+
let stoppedEarly = false;
|
|
73713
|
+
/**
|
|
73714
|
+
* Would any stack AFTER `stackIndex` actually have been destroyed?
|
|
73715
|
+
*
|
|
73716
|
+
* `stackIndex < stackNames.length - 1` answers "does an INDEX remain",
|
|
73717
|
+
* which is not the same question and over-claims: with
|
|
73718
|
+
* `--stack-region us-east-1`, a later name whose only state record lives in
|
|
73719
|
+
* eu-west-1 is warn-and-skipped by the `targets.length === 0` branch below
|
|
73720
|
+
* — it was never going to be destroyed, so a signal before it leaves
|
|
73721
|
+
* nothing undone and the run must still exit 0. Measured on
|
|
73722
|
+
* `state destroy --all --yes --stack-region us-east-1` with StackA in
|
|
73723
|
+
* us-east-1 and StackB only in eu-west-1: the index form exited 2 having
|
|
73724
|
+
* dispatched StackA, i.e. every target it had.
|
|
73725
|
+
*
|
|
73726
|
+
* The predicate mirrors that branch's own filter over `stateRefs`, the
|
|
73727
|
+
* same list the loop selects from. `destroy.ts` needs no counterpart: its
|
|
73728
|
+
* `candidateStacks` is already pre-filtered by the set of stack names that
|
|
73729
|
+
* have state, so an index there IS a target.
|
|
73730
|
+
*/
|
|
73731
|
+
const targetRemainsAfter = (stackIndex) => stackNames.slice(stackIndex + 1).some((name) => {
|
|
73732
|
+
const laterRefs = stateRefs.filter((r) => r.stackName === name);
|
|
73733
|
+
if (!options.stackRegion) return laterRefs.length > 0;
|
|
73734
|
+
return laterRefs.some((r) => r.region === options.stackRegion || !r.region);
|
|
73735
|
+
});
|
|
73736
|
+
for (const [stackIndex, stackName] of stackNames.entries()) {
|
|
73526
73737
|
const refs = stateRefs.filter((r) => r.stackName === stackName);
|
|
73527
73738
|
let targets;
|
|
73528
73739
|
if (options.stackRegion) {
|
|
@@ -73536,14 +73747,17 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73536
73747
|
const regions = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
73537
73748
|
throw new Error(`Stack '${stackName}' has state in multiple regions: ${regions}. Use --stack-region <region> to pick one.`);
|
|
73538
73749
|
}
|
|
73539
|
-
for (const ref of targets) {
|
|
73750
|
+
for (const [refIndex, ref] of targets.entries()) {
|
|
73540
73751
|
logger.info(`\nPreparing to destroy stack: ${stackName}${ref.region ? ` (${ref.region})` : ""}`);
|
|
73541
73752
|
const stateResult = await setup.stateBackend.getState(stackName, ref.region ?? setup.region);
|
|
73542
73753
|
if (!stateResult) {
|
|
73543
73754
|
logger.warn(`No state found for stack ${stackName}${ref.region ? ` in ${ref.region}` : ""}, skipping`);
|
|
73544
73755
|
continue;
|
|
73545
73756
|
}
|
|
73546
|
-
if (runInterrupted())
|
|
73757
|
+
if (runInterrupted()) {
|
|
73758
|
+
stoppedEarly = true;
|
|
73759
|
+
break;
|
|
73760
|
+
}
|
|
73547
73761
|
const result = await interruptWatch.runStack(() => withNestedStackContext({
|
|
73548
73762
|
stateBackend: setup.stateBackend,
|
|
73549
73763
|
lockManager: setup.lockManager,
|
|
@@ -73586,12 +73800,18 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73586
73800
|
totalErrors += result.errorCount;
|
|
73587
73801
|
totalSkipped += result.skippedCount;
|
|
73588
73802
|
if (result.interrupted) interrupted = true;
|
|
73589
|
-
if (runInterrupted())
|
|
73803
|
+
if (runInterrupted()) {
|
|
73804
|
+
if (refIndex < targets.length - 1) stoppedEarly = true;
|
|
73805
|
+
break;
|
|
73806
|
+
}
|
|
73807
|
+
}
|
|
73808
|
+
if (runInterrupted()) {
|
|
73809
|
+
if (targetRemainsAfter(stackIndex)) stoppedEarly = true;
|
|
73810
|
+
break;
|
|
73590
73811
|
}
|
|
73591
|
-
if (runInterrupted()) break;
|
|
73592
73812
|
}
|
|
73593
73813
|
if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd state destroy' to retry. If the same resource keeps failing, 'cdkd state orphan <stack>' removes the state record without deleting AWS resources.`);
|
|
73594
|
-
if (
|
|
73814
|
+
if (interrupted || interruptWatch.interrupted() && stoppedEarly) throw new PartialFailureError(`Destroy interrupted by Ctrl-C. State preserved — re-run 'cdkd state destroy' to finish.`);
|
|
73595
73815
|
if (totalSkipped > 0) throw new PartialFailureError(`Destroy skipped ${totalSkipped} entr${totalSkipped === 1 ? "y" : "ies"} cdkd could not address, so the underlying resources may still exist in AWS. A skipped nested stack counts as ONE entry and may cover several of its own resources — the per-stack summaries above give the exact breakdown. State preserved (the records are kept). Repair the physicalId in state.json and re-run 'cdkd state destroy', or delete the resources by hand and drop the records with 'cdkd state orphan <stack>'.`);
|
|
73596
73816
|
} finally {
|
|
73597
73817
|
unforwardSigterm();
|
|
@@ -76213,6 +76433,7 @@ var AttributeFetcher = class {
|
|
|
76213
76433
|
};
|
|
76214
76434
|
}
|
|
76215
76435
|
this.logger.warn(`--force: live fetch failed for '${orphanLogicalId}.${attribute}' (${reason}); falling back to cached value from state.attributes.`);
|
|
76436
|
+
if (carriesDynamicReference(cached)) this.logger.warn(`--force: the cached value for '${orphanLogicalId}.${attribute}' is an UNRESOLVED dynamic reference (a nested stack's redacted output, issue #2055). It is being written into the referring resource's state VERBATIM — cdkd cannot re-resolve it from here. Re-run without --force once the live attribute is readable, or fix the referring property by hand.`);
|
|
76216
76437
|
const cacheKey = `${orphanLogicalId}\0${attribute}`;
|
|
76217
76438
|
this.cache.set(cacheKey, cached);
|
|
76218
76439
|
return {
|
|
@@ -87873,4 +88094,4 @@ function buildProgram() {
|
|
|
87873
88094
|
|
|
87874
88095
|
//#endregion
|
|
87875
88096
|
export { buildProgram };
|
|
87876
|
-
//# sourceMappingURL=program-
|
|
88097
|
+
//# sourceMappingURL=program-alOnKpLV.js.map
|