@go-to-k/cdkd 0.291.7 → 0.291.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-DqAx9B6g.js → asg-provider-D9EJApN9.js} +2 -2
- package/dist/{asg-provider-DqAx9B6g.js.map → asg-provider-D9EJApN9.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-Gl-b_bNn.js → deploy-engine-TkL9nyq8.js} +548 -105
- package/dist/deploy-engine-TkL9nyq8.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-Cy-pVgf_.js → program-CY92mXcX.js} +281 -65
- package/dist/{program-Cy-pVgf_.js.map → program-CY92mXcX.js.map} +1 -1
- package/dist/{version-oHfbqdBD.js → version-RzKz4JAW.js} +2 -2
- package/dist/{version-oHfbqdBD.js.map → version-RzKz4JAW.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-Gl-b_bNn.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T 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, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-GASr9bAF.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, c as currentStackAwsScope, i as getPreAssumeEnvCredentials, l as runInStackAwsScope, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-jISU4PHo.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-RzKz4JAW.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";
|
|
@@ -1230,6 +1230,182 @@ function isUpdateUnsupportedError(error, logicalId) {
|
|
|
1230
1230
|
return false;
|
|
1231
1231
|
}
|
|
1232
1232
|
|
|
1233
|
+
//#endregion
|
|
1234
|
+
//#region src/utils/pasteable-command.ts
|
|
1235
|
+
/**
|
|
1236
|
+
* The way cdkd builds a `cdkd ...` command it tells an operator to PASTE.
|
|
1237
|
+
*
|
|
1238
|
+
* Thirteen message modules route through it today. The CATEGORIES it does not
|
|
1239
|
+
* cover yet are at the end of this header — categories, not a census: the
|
|
1240
|
+
* population is what go-to-k/cdkd#3436's greps return against the tree of the
|
|
1241
|
+
* day, and a list here would be read as complete and go stale.
|
|
1242
|
+
*
|
|
1243
|
+
* go-to-k/cdkd#3363 measured the class this module closes, and
|
|
1244
|
+
* [#3436](https://github.com/go-to-k/cdkd/issues/3436) records it repo-wide.
|
|
1245
|
+
* Three shapes, each of which a per-site discipline kept re-introducing:
|
|
1246
|
+
*
|
|
1247
|
+
* 1. **A command inside prose quotes.** `drop it whole with '<command>'` puts
|
|
1248
|
+
* the command in a `'...'` span, so an interpolated value carrying `'`
|
|
1249
|
+
* CLOSES that wrapper when the span is pasted WITH its quotes and the rest
|
|
1250
|
+
* runs as shell: `'cdkd state orphan S --state-bucket 'b; printf X; #''`
|
|
1251
|
+
* printed `X`. Quoting the value does not help — the wrapper is what
|
|
1252
|
+
* inverts. So a command is printed UNWRAPPED on its own labelled line, and
|
|
1253
|
+
* this module returns the text for exactly that.
|
|
1254
|
+
*
|
|
1255
|
+
* **UNWRAPPED always; LAST only when the message prints ONE command.** A
|
|
1256
|
+
* message offering a READ and a DESTRUCTIVE template prints one command per
|
|
1257
|
+
* LINE, and there the read ends a line while the TEMPLATE is last — the
|
|
1258
|
+
* rule `.claude/rules/state-malformed-containers.md` states for
|
|
1259
|
+
* go-to-k/cdkd#3516, and a shape this module already serves twice:
|
|
1260
|
+
* `deploy-engine.ts`'s `Inspect it with:` / `Drop the record with:` pair,
|
|
1261
|
+
* and `destroy-runner.ts`'s through `hintFor`. In both, the read command is
|
|
1262
|
+
* correctly NOT last. Collapsing such a pair onto one line is the defect
|
|
1263
|
+
* that rule names: the read must end a line, and the template must be last
|
|
1264
|
+
* with no substituted value after it.
|
|
1265
|
+
* 2. **A bare `<placeholder>`.** `<name>` is two redirections, not a word:
|
|
1266
|
+
* `<name` reads stdin from a file and `>` truncates whatever word follows.
|
|
1267
|
+
* {@link commandHole} quotes it, so it pastes as one literal argument.
|
|
1268
|
+
* 3. **A value whose quote context the PROSE already flipped.** An English
|
|
1269
|
+
* apostrophe earlier in the sentence (`this stack's name`) opens a shell
|
|
1270
|
+
* quote that closes at the value's own opening quote, leaving the value
|
|
1271
|
+
* bare. This module cannot reach that one — a value in prose is not a
|
|
1272
|
+
* command — which is why the rule it belongs to is "a shell-quoted value
|
|
1273
|
+
* goes on a labelled trailing line, never inside a sentence", and why
|
|
1274
|
+
* deleting apostrophes is NOT the remedy (the next sentence re-opens it).
|
|
1275
|
+
*
|
|
1276
|
+
* What this module owns is the ARGUMENT side of shape 1 and shape 2: every
|
|
1277
|
+
* user-controlled value is sanitized, gated on rendering EXACTLY, and either
|
|
1278
|
+
* shell-quoted or replaced by a quoted hole. A value sanitizing would ALTER is
|
|
1279
|
+
* never printed in its altered spelling — that addresses a DIFFERENT record
|
|
1280
|
+
* than the message means — and never silently dropped, which would resolve the
|
|
1281
|
+
* ambient default instead.
|
|
1282
|
+
*
|
|
1283
|
+
* {@link shellQuote} and {@link commandHole} live here rather than in
|
|
1284
|
+
* `../state/lock-contention-message.js`, which still re-exports them for its
|
|
1285
|
+
* own callers: `src/utils/**` imports nothing from `src/state/**`, so the
|
|
1286
|
+
* shared helper could not reach them the other way round.
|
|
1287
|
+
*
|
|
1288
|
+
* ## The CATEGORIES not covered yet, as of go-to-k/cdkd#3436's first half
|
|
1289
|
+
*
|
|
1290
|
+
* Re-derive the members with that issue's greps rather than trusting a list:
|
|
1291
|
+
* these are kinds of site, and each kind has more members than the examples.
|
|
1292
|
+
*
|
|
1293
|
+
* - **Its own copy of the gate.** `buildForceUnlockCommand`
|
|
1294
|
+
* (`state/lock-contention-message.ts`), the `cdkd orphan` properties refusal
|
|
1295
|
+
* (`state/malformed-resources-bag.ts`), `orphanCommandFor` (`cli/commands/
|
|
1296
|
+
* export.ts`), and others in `deployment/deploy-engine.ts`,
|
|
1297
|
+
* `deployment/rollback-executor.ts` and `cli/commands/gc.ts`. They behave the
|
|
1298
|
+
* same way; they are not this function, so a rule change reaches them only by
|
|
1299
|
+
* hand.
|
|
1300
|
+
* - **A command in prose quotes with a RAW value**, outside the modules
|
|
1301
|
+
* migrated here — `provisioning/providers/**` (S3 Tables, Route 53, DynamoDB),
|
|
1302
|
+
* `cli/config-loader.ts` and `cli/commands/orphan.ts` are where the greps land
|
|
1303
|
+
* today.
|
|
1304
|
+
* - **The `cdkd drift` sites of
|
|
1305
|
+
* [#3307](https://github.com/go-to-k/cdkd/issues/3307)**, which take their own
|
|
1306
|
+
* gate in that PR. Note a `cdkd drift` command also appears OUTSIDE that file.
|
|
1307
|
+
* - **A spelled-out template or usage synopsis printing a BARE `<hole>`**, and
|
|
1308
|
+
* the source fence that would find them.
|
|
1309
|
+
*/
|
|
1310
|
+
/**
|
|
1311
|
+
* Quote a value for a pasteable shell command.
|
|
1312
|
+
*
|
|
1313
|
+
* EXPORTED since issue [#2610]: `src/provisioning/replacement-protection-advice.ts`
|
|
1314
|
+
* prints `aws <service> ...` recovery commands naming a resource's physical id,
|
|
1315
|
+
* which is the same hazard one directory over. A second spelling of this
|
|
1316
|
+
* predicate is how the two would come to disagree about which values need
|
|
1317
|
+
* quoting -- the reason `display-safe.ts`'s header gives for not widening a
|
|
1318
|
+
* rule by hand. It is a pure function of its argument and imports nothing.
|
|
1319
|
+
*/
|
|
1320
|
+
function shellQuote(value) {
|
|
1321
|
+
return /^[A-Za-z0-9._/@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* A placeholder for a value a pasteable command could not name, QUOTED.
|
|
1325
|
+
*
|
|
1326
|
+
* A bare `<profile>` is two shell redirections, not a word: pasted, `<profile`
|
|
1327
|
+
* reads stdin from a file named `profile` and `>` sends stdout to whatever
|
|
1328
|
+
* word follows. While the hole was the command's LAST word that was only a
|
|
1329
|
+
* syntax error; with flags appended after it (`--profile <profile>
|
|
1330
|
+
* --state-bucket b`) it ran the command with `--state-bucket` swallowed as a
|
|
1331
|
+
* redirect target and `b` as the profile — a delete against the ambient
|
|
1332
|
+
* bucket, with nothing printed (measured by the maintainer, M4 of the
|
|
1333
|
+
* go-to-k/cdkd#3363 review). Quoted, it pastes as one literal argument and
|
|
1334
|
+
* nothing else — no redirection, no swallowed flag. It is not refused: an
|
|
1335
|
+
* UNFILLED hole runs as that literal value (a stack named `<stack>`, a prefix
|
|
1336
|
+
* `<prefix>`).
|
|
1337
|
+
*/
|
|
1338
|
+
function commandHole(name) {
|
|
1339
|
+
return `'<${name}>'`;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* WHY a value must become a hole, or `undefined` when it may be NAMED.
|
|
1343
|
+
*
|
|
1344
|
+
* FIRST MATCH WINS, in the order written: `empty`, `altered`, `too-long`,
|
|
1345
|
+
* `option-shaped`, `pattern-shaped` (m21 of the go-to-k/cdkd#3499 review). A
|
|
1346
|
+
* value can satisfy several — `-\u001b[x` is both option-shaped and altered —
|
|
1347
|
+
* and the caller renders ONE sentence, so the order decides which. It runs
|
|
1348
|
+
* cheapest-and-most-fundamental first: a value that does not survive rendering
|
|
1349
|
+
* cannot be reasoned about as a command argument at all, so saying "this would
|
|
1350
|
+
* be read as an option" about a spelling that is not what is stored would be
|
|
1351
|
+
* the more misleading of the two true sentences. The order is also the order
|
|
1352
|
+
* the two functions this replaced already applied, which is what keeps the
|
|
1353
|
+
* refactor behaviour-preserving.
|
|
1354
|
+
*/
|
|
1355
|
+
function withholdReason(value, opts) {
|
|
1356
|
+
if (value === "") return "empty";
|
|
1357
|
+
const safe = displaySafe(value, { asciiOnly: true });
|
|
1358
|
+
if (safe !== value) return "altered";
|
|
1359
|
+
if (truncateCodePoints(safe, 1152).truncated) return "too-long";
|
|
1360
|
+
if (value.startsWith("-")) return "option-shaped";
|
|
1361
|
+
if (opts?.patternMatched === true && (value.includes("*") || value.includes("/"))) return "pattern-shaped";
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Build a pasteable `cdkd` command: every user-controlled value shell-quoted
|
|
1365
|
+
* behind an exactness gate, every placeholder quoted, nothing wrapped.
|
|
1366
|
+
*
|
|
1367
|
+
* `extraFlags` is where a caller appends the flags that pin the command to its
|
|
1368
|
+
* own account and key space — in practice `recoveryCommandFlags(recovery)`,
|
|
1369
|
+
* whose `exact` the caller folds into its own decision. They are appended
|
|
1370
|
+
* verbatim, LAST, because they are built by the same rules one layer up.
|
|
1371
|
+
*/
|
|
1372
|
+
function pasteableCommand(verb, args = [], extraFlags = []) {
|
|
1373
|
+
let exact = true;
|
|
1374
|
+
const withheld = [];
|
|
1375
|
+
const parts = [verb];
|
|
1376
|
+
for (const arg of args) {
|
|
1377
|
+
if ("literal" in arg) {
|
|
1378
|
+
parts.push(arg.literal);
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
if (!("value" in arg)) {
|
|
1382
|
+
if ("flag" in arg) parts.push(arg.flag);
|
|
1383
|
+
parts.push(commandHole(arg.hole));
|
|
1384
|
+
exact = false;
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
const reason = withholdReason(arg.value, arg.opts);
|
|
1388
|
+
let rendered;
|
|
1389
|
+
if (reason === void 0) rendered = shellQuote(arg.value);
|
|
1390
|
+
else {
|
|
1391
|
+
rendered = commandHole(arg.hole);
|
|
1392
|
+
exact = false;
|
|
1393
|
+
withheld.push({
|
|
1394
|
+
hole: arg.hole,
|
|
1395
|
+
reason
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
if ("flag" in arg) parts.push(arg.flag);
|
|
1399
|
+
parts.push(rendered);
|
|
1400
|
+
}
|
|
1401
|
+
parts.push(...extraFlags);
|
|
1402
|
+
return {
|
|
1403
|
+
command: parts.join(" "),
|
|
1404
|
+
exact,
|
|
1405
|
+
withheld
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1233
1409
|
//#endregion
|
|
1234
1410
|
//#region src/utils/error-handler.ts
|
|
1235
1411
|
/**
|
|
@@ -1736,14 +1912,18 @@ var ResourceUpdateNotSupportedError = class ResourceUpdateNotSupportedError exte
|
|
|
1736
1912
|
* redeploy, then retry the destroy. A future `--remove-protection`
|
|
1737
1913
|
* flag (separate scope) will provide an explicit one-shot bypass.
|
|
1738
1914
|
*
|
|
1739
|
-
* Note: `cdkd state destroy` (
|
|
1915
|
+
* Note: `cdkd state destroy` (the synth-free destroy) does NOT honor
|
|
1740
1916
|
* `terminationProtection` — the flag is a CDK property not persisted
|
|
1741
1917
|
* in cdkd's state.json. Use `cdkd destroy` when synth is available.
|
|
1742
1918
|
*/
|
|
1743
1919
|
var StackTerminationProtectionError = class StackTerminationProtectionError extends CdkdError {
|
|
1744
1920
|
stackName;
|
|
1745
1921
|
constructor(stackName, cause) {
|
|
1746
|
-
super(`Stack '${stackName}' has terminationProtection: true and cannot be destroyed. Set terminationProtection: false in the CDK code, redeploy, then retry
|
|
1922
|
+
super(`Stack '${stackName}' has terminationProtection: true and cannot be destroyed. Set terminationProtection: false in the CDK code, redeploy, then retry the destroy.\nRetry with: ${pasteableCommand("cdkd destroy", [{
|
|
1923
|
+
value: stackName,
|
|
1924
|
+
hole: "stack",
|
|
1925
|
+
opts: { patternMatched: true }
|
|
1926
|
+
}]).command}`, "STACK_TERMINATION_PROTECTION", cause);
|
|
1747
1927
|
this.stackName = stackName;
|
|
1748
1928
|
this.name = "StackTerminationProtectionError";
|
|
1749
1929
|
Object.setPrototypeOf(this, StackTerminationProtectionError.prototype);
|
|
@@ -1773,8 +1953,11 @@ var StackTerminationProtectionError = class StackTerminationProtectionError exte
|
|
|
1773
1953
|
* 1. `cdkd destroy <parent>` — the normal cascading-destroy path; the
|
|
1774
1954
|
* parent's reverse-DAG walks into the child via
|
|
1775
1955
|
* `NestedStackProvider.delete` and removes both layers atomically.
|
|
1776
|
-
* 2. `cdkd state destroy <child>` —
|
|
1777
|
-
* coupling check.
|
|
1956
|
+
* 2. `cdkd state destroy <child>` — the SYNTH-FREE destroy, with no parent
|
|
1957
|
+
* coupling check. It deletes the child's AWS resources and then its
|
|
1958
|
+
* record; "state-only" named the wrong thing, and this message's own text
|
|
1959
|
+
* was corrected away from that reading (go-to-k/cdkd#3499 review nits).
|
|
1960
|
+
* `cdkd state orphan` is the record-only command. The state-driven entry point intentionally
|
|
1778
1961
|
* bypasses this guard for the same reason `cdkd state destroy`
|
|
1779
1962
|
* bypasses `terminationProtection`: it's the "I know what I'm
|
|
1780
1963
|
* doing" path for cleaning up state when synth is unavailable or
|
|
@@ -1786,7 +1969,16 @@ var NestedStackChildDirectDestroyError = class NestedStackChildDirectDestroyErro
|
|
|
1786
1969
|
parentLogicalId;
|
|
1787
1970
|
constructor(stackName, parentStack, parentLogicalId, cause) {
|
|
1788
1971
|
const logicalIdSuffix = parentLogicalId ? ` (parent's logical id: ${parentLogicalId})` : "";
|
|
1789
|
-
|
|
1972
|
+
const cascadeCommand = pasteableCommand("cdkd destroy", [{
|
|
1973
|
+
value: parentStack,
|
|
1974
|
+
hole: "parent",
|
|
1975
|
+
opts: { patternMatched: true }
|
|
1976
|
+
}]).command;
|
|
1977
|
+
const childDestroyCommand = pasteableCommand("cdkd state destroy", [{
|
|
1978
|
+
value: stackName,
|
|
1979
|
+
hole: "stack"
|
|
1980
|
+
}]).command;
|
|
1981
|
+
super(`Stack '${stackName}' is a nested child of '${parentStack}'${logicalIdSuffix}; directly destroying a nested stack is not supported. Either cascade-delete this child along with its parent, or destroy the child on its own — which deletes its AWS resources and its record, and leaves the parent's reference dangling (the synth-free escape hatch).\nCascade-delete with: ${cascadeCommand}\nDestroy the child alone with: ${childDestroyCommand}`, "NESTED_STACK_CHILD_DIRECT_DESTROY", cause);
|
|
1790
1982
|
this.stackName = stackName;
|
|
1791
1983
|
this.parentStack = parentStack;
|
|
1792
1984
|
if (parentLogicalId !== void 0) this.parentLogicalId = parentLogicalId;
|
|
@@ -3165,6 +3357,86 @@ function assemblyPathEscape(base, bound, candidate) {
|
|
|
3165
3357
|
return resolved;
|
|
3166
3358
|
}
|
|
3167
3359
|
/**
|
|
3360
|
+
* A letter or digit that neither draws as a blank nor reads as a quote.
|
|
3361
|
+
* `\p{L}` alone admits both, so two classes are carved out: the
|
|
3362
|
+
* default-ignorables, which hold letters that draw as a blank (the Hangul
|
|
3363
|
+
* fillers U+3164 and U+FFA0), and the quote-shaped letters — the Spacing
|
|
3364
|
+
* Modifier Letters block (U+02BA reads as `"`, U+02BC as `'`) plus the ones
|
|
3365
|
+
* outside it (U+0374, U+0559, U+07F4-U+07F5, U+A78B-U+A78C, and the halfwidth
|
|
3366
|
+
* sound marks U+FF9E-U+FF9F). Not all of `\p{Lm}`: U+30FC is in it, and it is
|
|
3367
|
+
* an ordinary character of a Japanese directory name.
|
|
3368
|
+
*/
|
|
3369
|
+
const VISIBLE_LETTER = new RegExp(String.raw`^(?![\p{Default_Ignorable_Code_Point}\u02b0-\u02ff\u0374\u0559\u07f4\u07f5\ua78b\ua78c\uff9e\uff9f])[\p{L}\p{N}]$`, "u");
|
|
3370
|
+
/**
|
|
3371
|
+
* A combining mark, which counts only DIRECTLY after a visible letter (or a
|
|
3372
|
+
* mark that itself counted): on a space or on punctuation it draws on its own,
|
|
3373
|
+
* and U+030B / U+030E there look like a quote.
|
|
3374
|
+
*/
|
|
3375
|
+
const COMBINING_MARK = /^\p{M}$/u;
|
|
3376
|
+
/**
|
|
3377
|
+
* The ASCII a bare path may carry besides letters and digits. Everything else —
|
|
3378
|
+
* any whitespace, any quote, any other symbol — takes the boundary, which
|
|
3379
|
+
* costs a legitimate path nothing but a pair of quotes.
|
|
3380
|
+
*/
|
|
3381
|
+
const BARE_PUNCTUATION = /^[/\\._~+@:=-]$/;
|
|
3382
|
+
/**
|
|
3383
|
+
* Classify each code point: `bare` (allowed in a bare path), `shown` (shown as
|
|
3384
|
+
* itself inside the boundary), or `escaped`. An ALLOWLIST, because a denylist
|
|
3385
|
+
* has to enumerate every character that draws as a blank or reads as a quote,
|
|
3386
|
+
* and JS `\s` alone already misses U+2800 and U+3164.
|
|
3387
|
+
*
|
|
3388
|
+
* Each test is ONE character and this walks the value — never `^(...)+$` over
|
|
3389
|
+
* the whole of it: alternatives that overlap (ASCII letters are in both
|
|
3390
|
+
* classes) under a quantifier backtrack exponentially on a long path that
|
|
3391
|
+
* fails near its end, which hangs the process uncatchably.
|
|
3392
|
+
*/
|
|
3393
|
+
function classify(chars) {
|
|
3394
|
+
let afterLetter = false;
|
|
3395
|
+
return chars.map((ch) => {
|
|
3396
|
+
if (COMBINING_MARK.test(ch)) return afterLetter ? "bare" : "escaped";
|
|
3397
|
+
afterLetter = VISIBLE_LETTER.test(ch);
|
|
3398
|
+
if (afterLetter || BARE_PUNCTUATION.test(ch)) return "bare";
|
|
3399
|
+
return ch >= " " && ch <= "~" ? "shown" : "escaped";
|
|
3400
|
+
});
|
|
3401
|
+
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Render a filesystem path that an assembly chose, or that embeds a value it
|
|
3404
|
+
* chose, into cdkd's own prose (go-to-k/cdkd#3509). The caller writes NO
|
|
3405
|
+
* quotes around the result.
|
|
3406
|
+
*
|
|
3407
|
+
* `displaySafe` alone is not enough inside quotes of cdkd's: it is a denylist
|
|
3408
|
+
* of control characters and passes `'`, so a value carrying one closed cdkd's
|
|
3409
|
+
* quote and wrote a clause of its own into the refusal. This keeps a plain path
|
|
3410
|
+
* bare and puts any other one inside a JSON string literal. Inside it, `"` and
|
|
3411
|
+
* `\` are escaped as JSON escapes them, and so is every character that is
|
|
3412
|
+
* neither printable ASCII nor a visible letter — a curly or fullwidth quote
|
|
3413
|
+
* that could pass for the boundary's own closing `"`, and a blank that could
|
|
3414
|
+
* pass for a space. The result stays valid JSON: `JSON.parse` returns the
|
|
3415
|
+
* sanitized value.
|
|
3416
|
+
*
|
|
3417
|
+
* Deliberately NOT `displayIdent`, which go-to-k/cdkd#3506 used for a Stage
|
|
3418
|
+
* path: that one is ASCII-only and capped at 255 code points, and a legitimate
|
|
3419
|
+
* path is neither — a non-ASCII directory name would render as spaces, naming
|
|
3420
|
+
* a path that does not exist. Here nothing is truncated and a non-ASCII letter
|
|
3421
|
+
* is shown as itself. A legitimate path with a space or any symbol outside
|
|
3422
|
+
* `/ \ . _ ~ + @ : = -` (`/Users/me/My Project/cdk.out`,
|
|
3423
|
+
* `C:\Program Files (x86)\app`) renders quoted, and a non-letter non-ASCII
|
|
3424
|
+
* character in it (an emoji, `©`) is shown as its `\u` escape.
|
|
3425
|
+
*/
|
|
3426
|
+
function displayAssemblyPath(value) {
|
|
3427
|
+
const clean = displaySafe(value);
|
|
3428
|
+
const chars = Array.from(clean);
|
|
3429
|
+
const kinds = classify(chars);
|
|
3430
|
+
if (clean === value && chars.length > 0 && kinds.every((k) => k === "bare")) return clean;
|
|
3431
|
+
let body = "";
|
|
3432
|
+
chars.forEach((ch, i) => {
|
|
3433
|
+
if (ch === "\"" || ch === "\\") body += `\\${ch}`;
|
|
3434
|
+
else if (kinds[i] !== "escaped") body += ch;
|
|
3435
|
+
else for (let u = 0; u < ch.length; u++) body += `\\u${ch.charCodeAt(u).toString(16).padStart(4, "0")}`;
|
|
3436
|
+
});
|
|
3437
|
+
return `"${body}"`;
|
|
3438
|
+
}
|
|
3439
|
+
/**
|
|
3168
3440
|
* The shared tail of every containment refusal: what the value resolved to,
|
|
3169
3441
|
* what it escaped, and why that means the assembly is not CDK-generated. Each
|
|
3170
3442
|
* call site supplies its own subject ("Stack 'X' has templateFile='...' which
|
|
@@ -3173,23 +3445,25 @@ function assemblyPathEscape(base, bound, candidate) {
|
|
|
3173
3445
|
* other than loading the file — `renderNestedTemplateTreeDefect` says "deploy"
|
|
3174
3446
|
* or "diff", matching its own sibling refusals.
|
|
3175
3447
|
*
|
|
3176
|
-
* Every
|
|
3177
|
-
* `AssemblyReader`'s own refusals give
|
|
3178
|
-
* FOR a hand-modified assembly, so the
|
|
3179
|
-
* `dir` (below a Stage it derives from
|
|
3180
|
-
* attacker-chosen, and `formatError`
|
|
3181
|
-
* its own `message`.
|
|
3448
|
+
* Every path goes through {@link displayAssemblyPath}, with no quotes of this
|
|
3449
|
+
* function's own, for the reason `AssemblyReader`'s own refusals give
|
|
3450
|
+
* (go-to-k/cdkd#3277): this text exists FOR a hand-modified assembly, so the
|
|
3451
|
+
* candidate, the resolved path and even `dir` (below a Stage it derives from
|
|
3452
|
+
* the manifest's `directoryName`) are all attacker-chosen, and `formatError`
|
|
3453
|
+
* sanitizes only an error's `cause`, never its own `message`.
|
|
3182
3454
|
*/
|
|
3183
3455
|
function renderAssemblyPathEscape(escape, dir, action = "load", provenanceOverride) {
|
|
3184
3456
|
const provenance = provenanceOverride ?? `CDK emits assembly paths that stay inside the assembly directory; one that leaves it indicates the synth output was hand-modified or generated by a non-CDK toolchain. Refusing to ${action}.`;
|
|
3185
3457
|
const base = path.resolve(dir);
|
|
3186
3458
|
const realBase = resolveThroughLinks(base) ?? base;
|
|
3459
|
+
const shownPath = displayAssemblyPath(escape.path);
|
|
3460
|
+
const shownBase = displayAssemblyPath(base);
|
|
3187
3461
|
if (escape.escape === "symlink") {
|
|
3188
|
-
if (escape.realPath === realBase) return `resolves to
|
|
3189
|
-
return `resolves to
|
|
3462
|
+
if (escape.realPath === realBase) return `resolves to ${shownPath}, a symbolic link to the directory ${shownBase} itself rather than to a file inside it. ${provenance}`;
|
|
3463
|
+
return `resolves to ${shownPath}, which leads through a symbolic link to ${displayAssemblyPath(escape.realPath)}, outside ${shownBase}. ${provenance}`;
|
|
3190
3464
|
}
|
|
3191
|
-
if (escape.path === base) return `names the directory
|
|
3192
|
-
return `resolves to
|
|
3465
|
+
if (escape.path === base) return `names the directory ${shownBase} itself rather than a file inside it. ${provenance}`;
|
|
3466
|
+
return `resolves to ${shownPath}, outside ${shownBase}. ${provenance}`;
|
|
3193
3467
|
}
|
|
3194
3468
|
|
|
3195
3469
|
//#endregion
|
|
@@ -7817,12 +8091,25 @@ function formatDockerLoginError(stderr, endpoint) {
|
|
|
7817
8091
|
return trimmed;
|
|
7818
8092
|
}
|
|
7819
8093
|
/**
|
|
7820
|
-
* Env vars the
|
|
7821
|
-
*
|
|
7822
|
-
*
|
|
7823
|
-
*
|
|
7824
|
-
*
|
|
7825
|
-
*
|
|
8094
|
+
* Env vars the container CLI itself reads to decide how / where to run — the
|
|
8095
|
+
* binary {@link getDockerCmd} resolves, i.e. `docker` or whatever `CDK_DOCKER`
|
|
8096
|
+
* names (podman / nerdctl / finch). A resolved ECS secret (or SecureString)
|
|
8097
|
+
* whose NAME collides with one of these must NOT override it in the client's
|
|
8098
|
+
* own process environment: a secret named `DOCKER_HOST` (or podman's
|
|
8099
|
+
* `CONTAINER_HOST`) would redirect the client to a different daemon, and `PATH`
|
|
8100
|
+
* would break locating the binary. See issues
|
|
8101
|
+
* https://github.com/go-to-k/cdkd/issues/2183 and
|
|
8102
|
+
* https://github.com/go-to-k/cdkd/issues/2188.
|
|
8103
|
+
*
|
|
8104
|
+
* RULE for additions: anything a supported container CLIENT (or a credential /
|
|
8105
|
+
* connection helper it execs) reads to decide WHAT CODE IT LOADS, WHAT IT
|
|
8106
|
+
* TRUSTS, or WHERE / HOW IT CONNECTS. The docker CLI's documented set is kept
|
|
8107
|
+
* whole, behaviour toggles included; beyond it, a var that only tunes
|
|
8108
|
+
* behaviour (a storage driver, a snapshotter, a temp dir, an experimental
|
|
8109
|
+
* toggle) is OUT, because dropping a colliding secret costs the user that
|
|
8110
|
+
* secret. The operator's OWN value is never touched — the spawn
|
|
8111
|
+
* env starts from `process.env` — so adding a key costs only a secret of that
|
|
8112
|
+
* name.
|
|
7826
8113
|
*/
|
|
7827
8114
|
const DOCKER_CLIENT_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
7828
8115
|
"PATH",
|
|
@@ -7864,6 +8151,40 @@ const DOCKER_CLIENT_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
|
7864
8151
|
"AWS_CONTAINER_CREDENTIALS_FULL_URI",
|
|
7865
8152
|
"AWS_ROLE_ARN",
|
|
7866
8153
|
"AWS_EC2_METADATA_SERVICE_ENDPOINT",
|
|
8154
|
+
"AWS_ECR_CACHE_DIR",
|
|
8155
|
+
"CONTAINER_HOST",
|
|
8156
|
+
"CONTAINER_CONNECTION",
|
|
8157
|
+
"CONTAINER_SSHKEY",
|
|
8158
|
+
"PODMAN_CONNECTIONS_CONF",
|
|
8159
|
+
"CONTAINER_PROXY",
|
|
8160
|
+
"CONTAINERS_SSH_CONF",
|
|
8161
|
+
"CONTAINERS_CONF",
|
|
8162
|
+
"CONTAINERS_CONF_OVERRIDE",
|
|
8163
|
+
"CONTAINERS_REGISTRIES_CONF",
|
|
8164
|
+
"CONTAINERS_REGISTRIES_CONF_OVERRIDE",
|
|
8165
|
+
"REGISTRIES_CONFIG_PATH",
|
|
8166
|
+
"CONTAINERS_STORAGE_CONF",
|
|
8167
|
+
"CONTAINERS_STORAGE_CONF_OVERRIDE",
|
|
8168
|
+
"CONTAINERS_POLICY_JSON",
|
|
8169
|
+
"STORAGE_OPTS",
|
|
8170
|
+
"CONTAINERS_HELPER_BINARY_DIR",
|
|
8171
|
+
"REGISTRY_AUTH_FILE",
|
|
8172
|
+
"DBUS_SESSION_BUS_ADDRESS",
|
|
8173
|
+
"NOTIFY_SOCKET",
|
|
8174
|
+
"XDG_CONFIG_HOME",
|
|
8175
|
+
"XDG_RUNTIME_DIR",
|
|
8176
|
+
"APPDATA",
|
|
8177
|
+
"PROGRAMDATA",
|
|
8178
|
+
"PROGRAMFILES",
|
|
8179
|
+
"LOCALAPPDATA",
|
|
8180
|
+
"SSH",
|
|
8181
|
+
"CONTAINERD_ADDRESS",
|
|
8182
|
+
"CONTAINERD_NAMESPACE",
|
|
8183
|
+
"NERDCTL_TOML",
|
|
8184
|
+
"CNI_PATH",
|
|
8185
|
+
"NETCONFPATH",
|
|
8186
|
+
"ROOTLESSKIT_STATE_DIR",
|
|
8187
|
+
"NERDCTL_LOG_FILE",
|
|
7867
8188
|
"SSL_CERT_FILE",
|
|
7868
8189
|
"SSL_CERT_DIR",
|
|
7869
8190
|
"GODEBUG",
|
|
@@ -9667,7 +9988,14 @@ function isCrossRegionRedirect(error) {
|
|
|
9667
9988
|
*/
|
|
9668
9989
|
async function assertAssetBucketRegion(s3Client, bucketName, expectedRegion, accountId, cause) {
|
|
9669
9990
|
const want = canonicalizeRegion(expectedRegion);
|
|
9670
|
-
const remedy = `Either bootstrap ${want} with an asset-bucket name unique to it
|
|
9991
|
+
const remedy = `Either bootstrap ${want} with an asset-bucket name unique to it, or run this against the bucket's own region.\nBootstrap with: ${pasteableCommand("cdkd bootstrap", [{
|
|
9992
|
+
flag: "--region",
|
|
9993
|
+
value: want,
|
|
9994
|
+
hole: "region"
|
|
9995
|
+
}, {
|
|
9996
|
+
flag: "--asset-bucket",
|
|
9997
|
+
hole: "name"
|
|
9998
|
+
}]).command}`;
|
|
9671
9999
|
let actual;
|
|
9672
10000
|
const fromHeader = readBucketRegionHeader(cause);
|
|
9673
10001
|
if (fromHeader) actual = canonicalizeRegion(fromHeader);
|
|
@@ -9831,7 +10159,11 @@ function parseBootstrapMarker(body, markerKey) {
|
|
|
9831
10159
|
* (bucket-squatting defense, design §5).
|
|
9832
10160
|
*/
|
|
9833
10161
|
async function verifyAssetStorageExists(marker, accountId, region, opts = {}) {
|
|
9834
|
-
const rebootstrapHint = `
|
|
10162
|
+
const rebootstrapHint = `cdkd never silently falls back to CDK bootstrap asset storage once a region is opted in.\nRecreate it with: ${pasteableCommand("cdkd bootstrap", [{
|
|
10163
|
+
flag: "--region",
|
|
10164
|
+
value: region,
|
|
10165
|
+
hole: "region"
|
|
10166
|
+
}]).command}`;
|
|
9835
10167
|
const clientOpts = {
|
|
9836
10168
|
region,
|
|
9837
10169
|
...opts.profile && { profile: opts.profile }
|
|
@@ -9915,7 +10247,11 @@ async function ensureAssetStorage(options) {
|
|
|
9915
10247
|
const conflicts = [];
|
|
9916
10248
|
if (options.assetBucketName && options.assetBucketName !== existingMarker.assetBucket) conflicts.push(`asset bucket '${existingMarker.assetBucket}' (requested '${options.assetBucketName}')`);
|
|
9917
10249
|
if (options.containerRepoName && options.containerRepoName !== existingMarker.containerRepo) conflicts.push(`container repo '${existingMarker.containerRepo}' (requested '${options.containerRepoName}')`);
|
|
9918
|
-
if (conflicts.length > 0) throw new CdkdError(`Region '${region}' is already bootstrapped with ${conflicts.join(" and ")}. Changing asset storage names would strand the existing storage and every published asset in it —
|
|
10250
|
+
if (conflicts.length > 0) throw new CdkdError(`Region '${region}' is already bootstrapped with ${conflicts.join(" and ")}. Changing asset storage names would strand the existing storage and every published asset in it — tear the region's asset storage down first, then re-run bootstrap with the new names.\nTear it down with: ${pasteableCommand("cdkd bootstrap", [{ literal: "--destroy" }, {
|
|
10251
|
+
flag: "--region",
|
|
10252
|
+
value: region,
|
|
10253
|
+
hole: "region"
|
|
10254
|
+
}]).command}`, "ASSET_STORAGE_NAME_CONFLICT");
|
|
9919
10255
|
}
|
|
9920
10256
|
const assetBucket = options.assetBucketName ?? existingMarker?.assetBucket ?? getCdkdAssetBucketName(accountId, region);
|
|
9921
10257
|
const containerRepo = options.containerRepoName ?? existingMarker?.containerRepo ?? getCdkdContainerRepoName(accountId, region);
|
|
@@ -10105,7 +10441,11 @@ var AssetModeResolver = class {
|
|
|
10105
10441
|
}
|
|
10106
10442
|
if (!this.legacyNoticeShownRegions.has(region) && !this.suppressLegacyNotice) {
|
|
10107
10443
|
this.legacyNoticeShownRegions.add(region);
|
|
10108
|
-
this.logger.info(`Assets for region '${region}' are published to the CDK bootstrap bucket/repo, which 'cdk gc' may garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan).
|
|
10444
|
+
this.logger.info(`Assets for region '${region}' are published to the CDK bootstrap bucket/repo, which 'cdk gc' may garbage-collect (cdkd-deployed stacks have no CloudFormation stack for gc to scan). Create cdkd-owned asset storage that 'cdk gc' never touches:\nBootstrap with: ${pasteableCommand("cdkd bootstrap", [{
|
|
10445
|
+
flag: "--region",
|
|
10446
|
+
value: region,
|
|
10447
|
+
hole: "region"
|
|
10448
|
+
}]).command}`);
|
|
10109
10449
|
}
|
|
10110
10450
|
return { mode: "legacy" };
|
|
10111
10451
|
}
|
|
@@ -10165,7 +10505,11 @@ var AssetModeResolver = class {
|
|
|
10165
10505
|
};
|
|
10166
10506
|
} catch (error) {
|
|
10167
10507
|
const message = error instanceof Error ? error.message : String(error);
|
|
10168
|
-
this.logger.warn(`Failed to auto-create cdkd asset storage for region '${region}': ${message} Falling back to the CDK bootstrap destinations for this run —
|
|
10508
|
+
this.logger.warn(`Failed to auto-create cdkd asset storage for region '${region}': ${message} Falling back to the CDK bootstrap destinations for this run — opt the region in with S3/ECR create permissions.\nBootstrap with: ${pasteableCommand("cdkd bootstrap", [{
|
|
10509
|
+
flag: "--region",
|
|
10510
|
+
value: region,
|
|
10511
|
+
hole: "region"
|
|
10512
|
+
}]).command}`);
|
|
10169
10513
|
return null;
|
|
10170
10514
|
} finally {
|
|
10171
10515
|
s3Client?.destroy();
|
|
@@ -11406,19 +11750,6 @@ function formatDuration(ms) {
|
|
|
11406
11750
|
return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
|
|
11407
11751
|
}
|
|
11408
11752
|
/**
|
|
11409
|
-
* Quote a value for a pasteable shell command.
|
|
11410
|
-
*
|
|
11411
|
-
* EXPORTED since issue [#2610]: `src/provisioning/replacement-protection-advice.ts`
|
|
11412
|
-
* prints `aws <service> ...` recovery commands naming a resource's physical id,
|
|
11413
|
-
* which is the same hazard one directory over. A second spelling of this
|
|
11414
|
-
* predicate is how the two would come to disagree about which values need
|
|
11415
|
-
* quoting -- the reason `display-safe.ts`'s header gives for not widening a
|
|
11416
|
-
* rule by hand. It is a pure function of its argument and imports nothing.
|
|
11417
|
-
*/
|
|
11418
|
-
function shellQuote(value) {
|
|
11419
|
-
return /^[A-Za-z0-9._/@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11420
|
-
}
|
|
11421
|
-
/**
|
|
11422
11753
|
* The `cdkd force-unlock ...` line, carrying every flag that decides which
|
|
11423
11754
|
* lock object it resolves to.
|
|
11424
11755
|
*/
|
|
@@ -11432,24 +11763,6 @@ function buildForceUnlockCommand(stackName, region, recovery) {
|
|
|
11432
11763
|
return [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`, ...recoveryFlags.flags].join(" ");
|
|
11433
11764
|
}
|
|
11434
11765
|
/**
|
|
11435
|
-
* A placeholder for a value a pasteable command could not name, QUOTED.
|
|
11436
|
-
*
|
|
11437
|
-
* A bare `<profile>` is two shell redirections, not a word: pasted, `<profile`
|
|
11438
|
-
* reads stdin from a file named `profile` and `>` sends stdout to whatever
|
|
11439
|
-
* word follows. While the hole was the command's LAST word that was only a
|
|
11440
|
-
* syntax error; with flags appended after it (`--profile <profile>
|
|
11441
|
-
* --state-bucket b`) it ran the command with `--state-bucket` swallowed as a
|
|
11442
|
-
* redirect target and `b` as the profile — a delete against the ambient
|
|
11443
|
-
* bucket, with nothing printed (measured by the maintainer, M4 of the
|
|
11444
|
-
* go-to-k/cdkd#3363 review). Quoted, it pastes as one literal argument and
|
|
11445
|
-
* nothing else — no redirection, no swallowed flag. It is not refused: an
|
|
11446
|
-
* UNFILLED hole runs as that literal value (a stack named `<stack>`, a prefix
|
|
11447
|
-
* `<prefix>`).
|
|
11448
|
-
*/
|
|
11449
|
-
function commandHole(name) {
|
|
11450
|
-
return `'<${name}>'`;
|
|
11451
|
-
}
|
|
11452
|
-
/**
|
|
11453
11766
|
* The flags that pin a pasteable state command to the caller's account and
|
|
11454
11767
|
* bucket: `--profile`, the resolved `--state-bucket`, and a NON-default
|
|
11455
11768
|
* `--state-prefix` (see {@link LockRecoveryContext} for why each is emitted
|
|
@@ -16779,6 +17092,40 @@ function hasPlainPrototype(value) {
|
|
|
16779
17092
|
return proto === Object.prototype || proto === null;
|
|
16780
17093
|
}
|
|
16781
17094
|
/**
|
|
17095
|
+
* Is this a `Date` the value scan may return BY IDENTITY (issue
|
|
17096
|
+
* [#2427](https://github.com/go-to-k/cdkd/issues/2427))?
|
|
17097
|
+
*
|
|
17098
|
+
* The scan redacts what PERSISTS, and `JSON.stringify` persists an unmodified
|
|
17099
|
+
* `Date` as `Date.prototype.toJSON`'s ISO timestamp (or `null`) — never its own
|
|
17100
|
+
* entries. So the only thing to scan is that timestamp — the walk runs it
|
|
17101
|
+
* through the string arms and keeps the `Date` only when they leave it alone —
|
|
17102
|
+
* and rebuilding it just threw the timestamp away. "Unmodified" is three
|
|
17103
|
+
* conjuncts, each closing a shape whose persisted form is NOT that timestamp:
|
|
17104
|
+
* the EXACT prototype, a real `[[DateValue]]` (the `getTime` brand check, which
|
|
17105
|
+
* a `Proxy` or an `Object.create(Date.prototype)` fails), and NO own keys — an
|
|
17106
|
+
* own `toJSON` (enumerable or not) is what `JSON.stringify` would call instead.
|
|
17107
|
+
* No SDK readback or `JSON.parse` produces any of those; they are refused
|
|
17108
|
+
* because the identity return must mean exactly "persists as a timestamp".
|
|
17109
|
+
*
|
|
17110
|
+
* Deliberately NOT "any non-plain object". `JSON.stringify` persists an
|
|
17111
|
+
* arbitrary class instance's OWN ENUMERABLE fields, which is exactly what the
|
|
17112
|
+
* rebuild scans, so returning one by identity would persist a plaintext held in
|
|
17113
|
+
* such a field — a disclosure, traded for fidelity. A `Uint8Array` persists as
|
|
17114
|
+
* the same `{"0":...}` either way. The EXACT prototype (not `instanceof`) keeps
|
|
17115
|
+
* a `Date` subclass on the rebuild: its prototype can override `toJSON`, which
|
|
17116
|
+
* the rebuild drops and identity would honour. A cross-realm `Date` fails the
|
|
17117
|
+
* test too, which is the old flattening rather than a leak.
|
|
17118
|
+
*/
|
|
17119
|
+
function isOrdinaryDate(value) {
|
|
17120
|
+
if (Object.getPrototypeOf(value) !== Date.prototype) return false;
|
|
17121
|
+
try {
|
|
17122
|
+
Date.prototype.getTime.call(value);
|
|
17123
|
+
} catch {
|
|
17124
|
+
return false;
|
|
17125
|
+
}
|
|
17126
|
+
return Reflect.ownKeys(value).length === 0;
|
|
17127
|
+
}
|
|
17128
|
+
/**
|
|
16782
17129
|
* Structural equality over the JSON shapes this module walks (issue #2012).
|
|
16783
17130
|
*
|
|
16784
17131
|
* Own ENUMERABLE keys only, and a key count on both sides, so an inherited
|
|
@@ -17379,21 +17726,22 @@ function asIndex(marks, index) {
|
|
|
17379
17726
|
*
|
|
17380
17727
|
* KEEPING A NON-PLAIN LEAF INTACT takes the prototype guard on the object arm,
|
|
17381
17728
|
* NOT that leaf rule, and an earlier revision of this comment claimed the
|
|
17382
|
-
* opposite — measured wrong. The scan's own walk
|
|
17729
|
+
* opposite — measured wrong. The scan's own walk rebuilt objects and turned a
|
|
17383
17730
|
* `Date` the provider readback carries (`LastModified`) into `{}`; that
|
|
17384
|
-
* flattening
|
|
17385
|
-
* (issue #2427). The object arm runs FIRST here and
|
|
17386
|
-
* `Date`, so without `hasPlainPrototype` this walk did
|
|
17387
|
-
* newly extending #2427 to the EMPTY-map path, where
|
|
17388
|
-
* `drainObservedCaptures` baseline lives and where
|
|
17389
|
-
* the result to the live resource. With the guard
|
|
17390
|
-
* through to `refused`. That is the position passes' own
|
|
17391
|
-
* bag by identity: their object arm carried no prototype
|
|
17392
|
-
*
|
|
17393
|
-
*
|
|
17394
|
-
*
|
|
17395
|
-
*
|
|
17396
|
-
*
|
|
17731
|
+
* flattening predated this module's derived needles on the POPULATED-map path
|
|
17732
|
+
* (issue #2427, since fixed in the scan). The object arm runs FIRST here and
|
|
17733
|
+
* `isPlainObject` admits a `Date`, so without `hasPlainPrototype` this walk did
|
|
17734
|
+
* the flattening ITSELF — newly extending #2427 to the EMPTY-map path, where
|
|
17735
|
+
* the unchanged-resource `drainObservedCaptures` baseline lives and where
|
|
17736
|
+
* `cdkd drift --revert` pushes the result to the live resource. With the guard
|
|
17737
|
+
* a non-plain leaf falls through to `refused`. That is the position passes' own
|
|
17738
|
+
* answer, which is the bag by identity: their object arm carried no prototype
|
|
17739
|
+
* guard of its own until issue
|
|
17740
|
+
* [#2869](https://github.com/go-to-k/cdkd/issues/2869), so a non-plain leaf
|
|
17741
|
+
* whose source subtree carries a reference WAS already flattened one function
|
|
17742
|
+
* earlier and this guard could only keep a `{}` intact. Both halves are guarded
|
|
17743
|
+
* now, and the VALUE scan's own walk keeps a `Date` by identity since issue
|
|
17744
|
+
* #2427, so `scanned` holds the same instance.
|
|
17397
17745
|
*
|
|
17398
17746
|
* The net effect is byte-identical to the FIRST ordering on every input where
|
|
17399
17747
|
* the un-certification did not fire — which is the whole point: it keeps that
|
|
@@ -17947,6 +18295,12 @@ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RU
|
|
|
17947
18295
|
}
|
|
17948
18296
|
if (Array.isArray(value)) return value.map(walk);
|
|
17949
18297
|
if (value !== null && typeof value === "object") {
|
|
18298
|
+
if (isOrdinaryDate(value)) {
|
|
18299
|
+
const persisted = value.toJSON();
|
|
18300
|
+
if (typeof persisted !== "string") return value;
|
|
18301
|
+
const scanned = walk(persisted);
|
|
18302
|
+
return scanned === persisted ? value : scanned;
|
|
18303
|
+
}
|
|
17950
18304
|
const out = Object.create(null);
|
|
17951
18305
|
for (const [k, v] of Object.entries(value)) out[k] = walk(v);
|
|
17952
18306
|
return out;
|
|
@@ -32368,12 +32722,32 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
32368
32722
|
throw error;
|
|
32369
32723
|
}
|
|
32370
32724
|
}
|
|
32371
|
-
/**
|
|
32725
|
+
/**
|
|
32726
|
+
* Lazily-constructed per-region CloudFormation client (issue #1697).
|
|
32727
|
+
*
|
|
32728
|
+
* Built with the ambient clients' {@link AwsClients.credentialConfig}, the
|
|
32729
|
+
* way {@link serviceDiscoveryClient} is (issue
|
|
32730
|
+
* [#1983](https://github.com/go-to-k/cdkd/issues/1983)). An explicit
|
|
32731
|
+
* `AwsClientConfig.credentials` has no environment path — only a LIBRARY
|
|
32732
|
+
* caller passes one, and it never runs the CLI's `AWS_PROFILE` mirror — so a
|
|
32733
|
+
* client built from `awsClientDefaults()` alone ran the CFn fallback reads
|
|
32734
|
+
* under the default chain's identity instead. The explicit `credentials`
|
|
32735
|
+
* spread AFTER the defaults, so they outrank an assumed `--role-arn` role,
|
|
32736
|
+
* matching {@link AwsClients}' own spread order.
|
|
32737
|
+
*
|
|
32738
|
+
* Not routed through {@link clientsForRegion}: the region here is always
|
|
32739
|
+
* the one the caller named, never the ambient's, and a test double without
|
|
32740
|
+
* `credentialConfig` degrades to the default chain rather than throwing.
|
|
32741
|
+
* Keyed by region alone for the reason {@link regionScopedClients} gives:
|
|
32742
|
+
* the credential half is process-wide.
|
|
32743
|
+
*/
|
|
32372
32744
|
getCfnClient(region) {
|
|
32373
32745
|
let client = this.cfnClients[region];
|
|
32374
32746
|
if (!client) {
|
|
32747
|
+
const credentialConfig = getAwsClients().credentialConfig ?? {};
|
|
32375
32748
|
client = new CloudFormationClient({
|
|
32376
|
-
...awsClientDefaults(),
|
|
32749
|
+
...awsClientDefaults({ profile: credentialConfig.profile }),
|
|
32750
|
+
...credentialConfig,
|
|
32377
32751
|
region
|
|
32378
32752
|
});
|
|
32379
32753
|
this.cfnClients[region] = client;
|
|
@@ -35587,7 +35961,7 @@ var CloudControlProvider = class {
|
|
|
35587
35961
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
35588
35962
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
35589
35963
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
35590
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
35964
|
+
const { ASGProvider } = await import("./asg-provider-D9EJApN9.js").then((n) => n.n);
|
|
35591
35965
|
const asgProvider = new ASGProvider();
|
|
35592
35966
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
35593
35967
|
}
|
|
@@ -45153,6 +45527,53 @@ function updatePartialMessage(reason) {
|
|
|
45153
45527
|
//#endregion
|
|
45154
45528
|
//#region src/deployment/rollback-executor.ts
|
|
45155
45529
|
/**
|
|
45530
|
+
* Rollback executor (issue #1183).
|
|
45531
|
+
*
|
|
45532
|
+
* The reusable engine that reverts a list of {@link CompletedOperation}s —
|
|
45533
|
+
* extracted from `DeployEngine` so BOTH callers drive identical semantics:
|
|
45534
|
+
*
|
|
45535
|
+
* - `DeployEngine` runs it in-process after a failed deploy (automatic
|
|
45536
|
+
* rollback, unchanged behavior except the two fixes below).
|
|
45537
|
+
* - The standalone `cdkd rollback` command runs it against a persisted
|
|
45538
|
+
* rollback journal (issue #1183 §journal), so a `--no-rollback` /
|
|
45539
|
+
* interrupted / partially-failed-auto-rollback deploy can be reverted
|
|
45540
|
+
* later.
|
|
45541
|
+
*
|
|
45542
|
+
* The executor deliberately depends only on `ProviderRegistry`, the stack
|
|
45543
|
+
* region, a logger, and an optional event recorder + per-op state-save
|
|
45544
|
+
* hook. It does NOT touch `DagBuilder` / `DiffCalculator` / the synthesizer
|
|
45545
|
+
* / `ExportIndexStore`, so the command can construct it without any of the
|
|
45546
|
+
* engine's synth-side collaborators (rollback never publishes
|
|
45547
|
+
* outputs/exports).
|
|
45548
|
+
*
|
|
45549
|
+
* Two deliberate behavior fixes vs. the pre-extraction in-process path
|
|
45550
|
+
* (both are pre-existing gaps; fixing them once benefits both callers):
|
|
45551
|
+
*
|
|
45552
|
+
* 1. **DeletionPolicy on CREATE rollback** — rolling a CREATE back IS a
|
|
45553
|
+
* delete as far as the policy is concerned (CloudFormation semantics), so
|
|
45554
|
+
* the CURRENT state record's `DeletionPolicy` decides what happens:
|
|
45555
|
+
* - `Retain` → ORPHANED (removed from state, left in AWS). The policy
|
|
45556
|
+
* says KEEP the resource, so cdkd does.
|
|
45557
|
+
* - `Snapshot` → final snapshot, THEN delete (issue #1358), through the
|
|
45558
|
+
* same mechanism matrix as the deploy engine's
|
|
45559
|
+
* `prepareFinalSnapshotForDelete`: atomic delete parameter for the
|
|
45560
|
+
* SDK-routed `ATOMIC_FINAL_SNAPSHOT_TYPES`, an explicit pre-delete
|
|
45561
|
+
* snapshot for `PRE_DELETE_SNAPSHOT_TYPES`, refusal for every other
|
|
45562
|
+
* Snapshot shape (cc-api routing included) unless
|
|
45563
|
+
* `--skip-final-snapshot` opts into the data loss. This arm ORPHANED
|
|
45564
|
+
* alongside `Retain` until #1358: when this file was written cdkd could
|
|
45565
|
+
* not create a final snapshot at all, so leaving the resource behind
|
|
45566
|
+
* was the only non-destructive option — but it silently handed the user
|
|
45567
|
+
* an untracked, billing resource that state no longer knew about.
|
|
45568
|
+
* `src/provisioning/final-snapshot.ts` (#1352 / #1353) removed the
|
|
45569
|
+
* constraint, so the policy is now honored literally.
|
|
45570
|
+
* - `RetainExceptOnCreate` (which exists precisely to allow cleanup of
|
|
45571
|
+
* failed creates) and absent / `Delete` → DELETE.
|
|
45572
|
+
* 2. **Idempotent replay skip rules** — so a partially-failed rollback can
|
|
45573
|
+
* be re-run safely (also harmless for the in-process caller, which
|
|
45574
|
+
* replays each op exactly once).
|
|
45575
|
+
*/
|
|
45576
|
+
/**
|
|
45156
45577
|
* Issue [#1762](https://github.com/go-to-k/cdkd/issues/1762): turn a
|
|
45157
45578
|
* `{ outcome: 'skipped' }` delete into a thrown error at every rollback delete
|
|
45158
45579
|
* arm.
|
|
@@ -46479,7 +46900,14 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
46479
46900
|
createLayer: createProvisionedBy
|
|
46480
46901
|
}) && !deletedNewFirst && createResult.physicalId === current.physicalId;
|
|
46481
46902
|
if (adoptedLiveNewResource) {
|
|
46482
|
-
logger.warn(` ⚠ ${safe(op.logicalId)} (${safe(op.resourceType)}): the re-create returned the LIVE new resource (${displaySafe(current.physicalId)}) instead of re-creating the old one — its Create API is name-idempotent and the new resource still holds the same user-supplied name. Skipping the delete-new step (it would delete that very resource). The old resource's ORIGINAL properties may NOT have been re-applied; state now records the pre-replacement properties, so
|
|
46903
|
+
logger.warn(` ⚠ ${safe(op.logicalId)} (${safe(op.resourceType)}): the re-create returned the LIVE new resource (${displaySafe(current.physicalId)}) instead of re-creating the old one — its Create API is name-idempotent and the new resource still holds the same user-supplied name. Skipping the delete-new step (it would delete that very resource). The old resource's ORIGINAL properties may NOT have been re-applied; state now records the pre-replacement properties, so inspect the drift and run 'cdkd deploy' to reconcile, or rename the resource to make the replacement reversible.\nInspect it with: ${pasteableCommand("cdkd drift", [{
|
|
46904
|
+
value: stackName,
|
|
46905
|
+
hole: "stack"
|
|
46906
|
+
}, {
|
|
46907
|
+
flag: "--stack-region",
|
|
46908
|
+
value: ctx.region,
|
|
46909
|
+
hole: "region"
|
|
46910
|
+
}]).command}`);
|
|
46483
46911
|
result.warnings++;
|
|
46484
46912
|
}
|
|
46485
46913
|
const { observedProperties: _staleObserved, ...prevRecord } = prev;
|
|
@@ -48207,27 +48635,6 @@ var DeployEngine = class DeployEngine {
|
|
|
48207
48635
|
const foreignReads = reads.filter((read) => !isLocal(read));
|
|
48208
48636
|
const hasRefStateRead = reads.some((read) => read.kind === "ref-state-key" && isLocal(read));
|
|
48209
48637
|
/**
|
|
48210
|
-
* A logical id rendered inside the single-quoted command below.
|
|
48211
|
-
*
|
|
48212
|
-
* cdkd validates no logical-id charset, so the id can contain a `'` — and
|
|
48213
|
-
* the line it lands in is meant to be COPY-PASTED into a shell, where an
|
|
48214
|
-
* unescaped one closes the quoting early and the rest of the command
|
|
48215
|
-
* reparses as something else. POSIX single-quote escaping (`'` becomes
|
|
48216
|
-
* `'\\''`) is the fix: close, emit an escaped quote, reopen.
|
|
48217
|
-
*
|
|
48218
|
-
* cdkd never EXECUTES this string — it is advice in an error message — so
|
|
48219
|
-
* this is about the pasted command being correct, not about injection into
|
|
48220
|
-
* cdkd itself (issue #2847 round-5 review).
|
|
48221
|
-
*
|
|
48222
|
-
* SINCE go-to-k/cdkd#3435 no id reaching this arm can contain a `'`:
|
|
48223
|
-
* `isPasteableIdent` admits `[A-Za-z0-9][A-Za-z0-9~_.-]*` only, and the
|
|
48224
|
-
* command is not emitted for anything else. Kept anyway rather than
|
|
48225
|
-
* deleted — dropping a guard on the strength of a NEW guard one line up is
|
|
48226
|
-
* how the pair later comes apart, and this one costs a `replaceAll` on a
|
|
48227
|
-
* short string.
|
|
48228
|
-
*/
|
|
48229
|
-
const quoteSafe = (id) => id.replaceAll("'", `'\\''`);
|
|
48230
|
-
/**
|
|
48231
48638
|
* A logical id rendered as a NAME rather than as a command argument
|
|
48232
48639
|
* (go-to-k/cdkd#3435 security round).
|
|
48233
48640
|
*
|
|
@@ -48242,11 +48649,24 @@ var DeployEngine = class DeployEngine {
|
|
|
48242
48649
|
/** CloudFormation's own logical-id length bound. */
|
|
48243
48650
|
const CFN_LOGICAL_ID_MAX_LENGTH = 255;
|
|
48244
48651
|
const parts = [];
|
|
48652
|
+
/** Pasteable commands, emitted LAST and one per line (go-to-k/cdkd#3436). */
|
|
48653
|
+
const commandLines = [];
|
|
48245
48654
|
if (localTargets.length > 0) {
|
|
48246
48655
|
const pasteableHere = (id) => isPasteableIdent(id) && id.length <= CFN_LOGICAL_ID_MAX_LENGTH;
|
|
48247
48656
|
const pasteable = localTargets.filter(pasteableHere);
|
|
48248
48657
|
const withheld = localTargets.filter((id) => !pasteableHere(id));
|
|
48249
|
-
if (pasteable.length > 0)
|
|
48658
|
+
if (pasteable.length > 0) {
|
|
48659
|
+
parts.push(`Re-import the record that HOLDS the mask (command(s) below).`);
|
|
48660
|
+
for (const id of pasteable) commandLines.push(`Re-import with: ${pasteableCommand("cdkd import", [
|
|
48661
|
+
{ hole: "stack" },
|
|
48662
|
+
{
|
|
48663
|
+
flag: "--resource",
|
|
48664
|
+
value: `${id}=<physicalId>`,
|
|
48665
|
+
hole: "resource"
|
|
48666
|
+
},
|
|
48667
|
+
{ literal: "--force" }
|
|
48668
|
+
]).command}`);
|
|
48669
|
+
}
|
|
48250
48670
|
if (withheld.length > 0) parts.push(`Re-import the record that HOLDS the mask for ${withheld.map(shown).join(", ")}, but the command is withheld: that is not a plain CloudFormation logical id, so a pasted 'cdkd import <stack> --resource <id>=<physicalId> --force' could be reshaped by the shell or name a different resource. Read the id from 'cdkd state show' and quote it yourself.`);
|
|
48251
48671
|
}
|
|
48252
48672
|
if (unclearableTargets.length > 0) parts.push(`Do NOT re-import ${unclearableTargets.map(shown).join(", ")}: a custom resource's import records no attributes, so the masked bag is carried forward unchanged and the refusal repeats. Cause (1) above is the one that applies to it.`);
|
|
@@ -48255,7 +48675,7 @@ var DeployEngine = class DeployEngine {
|
|
|
48255
48675
|
parts.push(`${subject} through ANOTHER stack (an Fn::ImportValue, an Fn::GetStackOutput, or a nested stack's Outputs), whose masked record lives in that stack's state — re-importing anything in this stack cannot clear it; act on the producer stack instead.`);
|
|
48256
48676
|
}
|
|
48257
48677
|
if (hasRefStateRead) parts.push("A 'Ref <LogicalId> (state key <Key>)' entry above is CDKD's own read, not one the template can stop making: CloudFormation defines that resource type's Ref value as that state key rather than the physical id, so the record must be repaired (re-import it, or let a deploy create or update the resource) — the \"stop reading it\" remedy does not apply to such an entry.");
|
|
48258
|
-
return parts.join(" ");
|
|
48678
|
+
return [parts.join(" "), ...commandLines].join("\n");
|
|
48259
48679
|
}
|
|
48260
48680
|
/**
|
|
48261
48681
|
* Refuse to provision a resource whose resolution served a REDACTED attribute
|
|
@@ -48298,7 +48718,7 @@ var DeployEngine = class DeployEngine {
|
|
|
48298
48718
|
refuseRedactedAttributeReads(logicalId, resourceType, context) {
|
|
48299
48719
|
const reads = context.redactedAttributeReads;
|
|
48300
48720
|
if (reads === void 0 || reads.length === 0) return;
|
|
48301
|
-
throw new ProvisioningError(`Cannot resolve ${reads.map((read) => read.display).join(", ")} for ${logicalId}: cdkd's recorded state holds only the redaction mask there, and the value is not recoverable from state. There are two ways a record comes to hold the mask. (1) A custom resource handler declared its response NoEcho: true — the value is generated by the handler, so cdkd has nothing to re-derive it from and must not write the literal mask to AWS. Remedies: force that custom resource to update (change one of its properties, e.g. a nonce / version property) so its handler runs again and supplies the value in this same run; or stop setting NoEcho on that response. If the value comes from ANOTHER stack, the producer and this stack must deploy in ONE run (cdkd deploy --all) with the producer's custom resource actually running — re-deploying the producer by itself does not help, because it re-masks the value on the way into its own state. (2) The resource was adopted by 'cdkd import' through the Cloud Control fallback, which records only the attributes the type's CloudFormation schema declares read-only and masks the rest. Either the attribute named above is not one of them — CloudFormation would reject an Fn::GetAtt naming it too, so stop reading it — or cdkd could not read that schema and masked the whole model, which the import warned about when it happened.
|
|
48721
|
+
throw new ProvisioningError(`Cannot resolve ${reads.map((read) => read.display).join(", ")} for ${logicalId}: cdkd's recorded state holds only the redaction mask there, and the value is not recoverable from state. There are two ways a record comes to hold the mask. (1) A custom resource handler declared its response NoEcho: true — the value is generated by the handler, so cdkd has nothing to re-derive it from and must not write the literal mask to AWS. Remedies: force that custom resource to update (change one of its properties, e.g. a nonce / version property) so its handler runs again and supplies the value in this same run; or stop setting NoEcho on that response. If the value comes from ANOTHER stack, the producer and this stack must deploy in ONE run (cdkd deploy --all) with the producer's custom resource actually running — re-deploying the producer by itself does not help, because it re-masks the value on the way into its own state. (2) The resource was adopted by 'cdkd import' through the Cloud Control fallback, which records only the attributes the type's CloudFormation schema declares read-only and masks the rest. Either the attribute named above is not one of them — CloudFormation would reject an Fn::GetAtt naming it too, so stop reading it — or cdkd could not read that schema and masked the whole model, which the import warned about when it happened. If that warning named a missing cloudformation:DescribeType permission, grant it first. See https://github.com/go-to-k/cdkd/issues/2449. ${DeployEngine.maskedRecordRemedyFor(reads, context.resources)}`, resourceType, logicalId);
|
|
48302
48722
|
}
|
|
48303
48723
|
redactOperationsForJournal(operations) {
|
|
48304
48724
|
return operations.map((op) => {
|
|
@@ -48821,7 +49241,13 @@ var DeployEngine = class DeployEngine {
|
|
|
48821
49241
|
const journal = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
|
|
48822
49242
|
if (journal && journal.segments.length > 0) {
|
|
48823
49243
|
const failedOnly = journal.segments.every((s) => s.operations.length === 0) && journal.segments.some((s) => (s.failedOperations?.length ?? 0) > 0);
|
|
48824
|
-
this.logger.info(failedOnly ? `A previous deploy of '${stackName}' failed and was automatically rolled back. The failed resource may be partially applied —
|
|
49244
|
+
this.logger.info(failedOnly ? `A previous deploy of '${stackName}' failed and was automatically rolled back. The failed resource may be partially applied — revert it, or continue deploying to fix forward (a successful deploy clears this note).\nRevert it with: ${pasteableCommand("cdkd rollback", [{
|
|
49245
|
+
value: stackName,
|
|
49246
|
+
hole: "stack"
|
|
49247
|
+
}, { literal: "--revert-failed" }]).command}` : `A previous deploy of '${stackName}' failed or was interrupted. Revert it, or continue deploying to fix forward.\nRevert it with: ${pasteableCommand("cdkd rollback", [{
|
|
49248
|
+
value: stackName,
|
|
49249
|
+
hole: "stack"
|
|
49250
|
+
}]).command}`);
|
|
48825
49251
|
}
|
|
48826
49252
|
} catch {}
|
|
48827
49253
|
this.stampReasonlessParameterRefusals(currentState.resources, template);
|
|
@@ -49450,7 +49876,10 @@ var DeployEngine = class DeployEngine {
|
|
|
49450
49876
|
return;
|
|
49451
49877
|
}
|
|
49452
49878
|
await this.writeRollbackJournalSegment(stackName, [], failedOperations, "auto-rollback-clean", initialDeploy);
|
|
49453
|
-
this.logger.info(`The automatic rollback restored the pre-deploy state. The failed resource's pre-failure record was kept — if it was left partially applied,
|
|
49879
|
+
this.logger.info(`The automatic rollback restored the pre-deploy state. The failed resource's pre-failure record was kept — if it was left partially applied, revert it.\nRevert it with: ${pasteableCommand("cdkd rollback", [{
|
|
49880
|
+
value: stackName,
|
|
49881
|
+
hole: "stack"
|
|
49882
|
+
}, { literal: "--revert-failed" }]).command}`);
|
|
49454
49883
|
}
|
|
49455
49884
|
/** Build the {@link RollbackExecutorContext} from the engine's fields. */
|
|
49456
49885
|
rollbackExecutorContext(previousState) {
|
|
@@ -50179,7 +50608,21 @@ var DeployEngine = class DeployEngine {
|
|
|
50179
50608
|
const skipPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
50180
50609
|
renderer.removeTask(logicalId);
|
|
50181
50610
|
this.logger.info(`${skipPrefix}${formatResourceLine("skipped", logicalId, resourceType, `skipped (${deleteSkipped})`)}`);
|
|
50182
|
-
this.logger.warn(deleteSkippedMessage(logicalId, currentResource.physicalId, deleteSkipped, "while removing it from the template") + `. Its cdkd state record was KEPT, so the next 'cdkd deploy' re-attempts the delete. Repair the record first (
|
|
50611
|
+
this.logger.warn(deleteSkippedMessage(logicalId, currentResource.physicalId, deleteSkipped, "while removing it from the template") + `. Its cdkd state record was KEPT, so the next 'cdkd deploy' re-attempts the delete. Repair the record first (for a nested stack it is the CHILD's own state, whose other resources may already be gone), or delete the resource by hand and drop the record.\nInspect it with: ${pasteableCommand("cdkd state show", [{
|
|
50612
|
+
value: stackName,
|
|
50613
|
+
hole: "stack"
|
|
50614
|
+
}, {
|
|
50615
|
+
flag: "--stack-region",
|
|
50616
|
+
value: this.stackRegion,
|
|
50617
|
+
hole: "region"
|
|
50618
|
+
}]).command}\nDrop the record with: ${pasteableCommand("cdkd state orphan", [{
|
|
50619
|
+
value: stackName,
|
|
50620
|
+
hole: "stack"
|
|
50621
|
+
}, {
|
|
50622
|
+
flag: "--stack-region",
|
|
50623
|
+
value: this.stackRegion,
|
|
50624
|
+
hole: "region"
|
|
50625
|
+
}]).command}`);
|
|
50183
50626
|
if (counts) counts.deleteSkipped++;
|
|
50184
50627
|
return { deleteSkipped };
|
|
50185
50628
|
}
|
|
@@ -50837,5 +51280,5 @@ var DeployEngine = class DeployEngine {
|
|
|
50837
51280
|
};
|
|
50838
51281
|
|
|
50839
51282
|
//#endregion
|
|
50840
|
-
export { yellow as $,
|
|
50841
|
-
//# sourceMappingURL=deploy-engine-
|
|
51283
|
+
export { yellow as $, markRedactedCause as $a, PARTITION_TABLE as $i, repairMalformedResourcePropertiesForReadOnly as $n, AssetModeResolver as $r, requireConfigObject as $t, createPreDeleteFinalSnapshot as A, LocalStartServiceError as Aa, getLegacyStateBucketName as Ai, malformedOutputsRefusalMessage as An, buildForceUnlockCommand as Ar, carriesDynamicReference as At, isStatefulRecreateTargetSync as B, StateError as Ba, ssmResolvedValueType as Bi, refuseMalformedOrphansForDestroy as Bn, importableOutputKeys as Br, isSettledInstanceState as Bt, namesAnyDeclaredParameter as C, ConfigError as Ca, escapeRegExp$1 as Ci, isReadableResourceEntry as Cn, recoverMaskedOutput as Cr, slowCcOperationTimeoutMs as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, DynamicReferenceRegionAmbiguousError as Da, Synthesizer as Di, malformedOrphanRecordsWarning as Dn, S3StateBackend as Dr, disableInstanceApiTermination as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, DeployCancelledError as Ea, getDockerImageBySourceHash as Ei, malformedLocalOutputsWarning as En, LockManager as Er, deleteSkipReason as Et, extractDeploymentEventError as F, ProvisioningError as Fa, resolveStateBucketWithDefault as Fi, malformedResourcesWarning as Fn, DEFAULT_STATE_PREFIX as Fr, isImpossibleEmptyStoredAttribute as Ft, toCfnInteger as G, withErrorHandling as Ga, uploadCfnTemplate as Gi, refuseMalformedResourceEntries as Gn, shouldRetainResource as Gr, coerceCfnBoolean as Gt, WARM_THROUGHPUT_MEMBERS as H, formatError as Ha, CFN_TEMPLATE_URL_LIMIT as Hi, refuseMalformedOutputs as Hn, isReadableBag as Hr, normalizeAwsTagsToCfn as Ht, makeSiblingClaimReader as I, ResourceTimeoutError as Ia, resolveStateBucketWithDefaultAndSource as Ii, malformedStateRefusalMessage as In, exportNamesCarriedFrom as Ir, isNamelessDynamicReferenceError as It, bold as J, isMarkedNonRetryable as Ja, injectiveKeyPrefix as Ji, refuseMalformedResourcesForDestroy as Jn, WorkGraph as Jr, configIntegerRefusal as Jt, toFiniteNumber as K, pasteableCommand as Ka, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Ki, refuseMalformedResourceEntriesForOrphan as Kn, AssetPublisher as Kr, coerceCfnInteger as Kt, planOrphanAdoption as L, ResourceUpdateNotSupportedError as La, resolveUseCdkBootstrapAssets as Li, refuseDivergentRecordRegionForDestroy as Ln, hasReadableExportSet as Lr, isUnboundTemplateParameter as Lt, refusesFinalSnapshot as M, MalformedProducerRecordRefusalError as Ma, resolveAutoAssetStorage as Mi, malformedRenderedContainersWarning as Mn, forceQuitRecoveryClause as Mr, cfnRefValueFromPhysicalId as Mt, unsupportedFinalSnapshotError as N, NestedStackChildDirectDestroyError as Na, resolveCaptureObservedState as Ni, malformedResourceEntriesWarning as Nn, formatLockExpiry as Nr, coerceParameterTypedValue as Nt, buildFinalSnapshotIdentifier as O, IntrinsicResolutionRefusalError as Oa, synthesisStatusMessage as Oi, malformedOrphansRefusalMessage as On, rebuildClientForBucketRegion as Or, isTerminationProtectionPropagationError as Ot, makeCanonicalizePropertiesFn as P, PartialFailureError as Pa, resolveSkipPrefix as Pi, malformedResourcePropertiesWarning as Pn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Pr, getAccountInfo as Pt, red as Q, markNonRetryable as Qa, expectedOwnerParam as Qi, repairMalformedResourceEntriesForReadOnly as Qn, rewriteTemplateAssetReferences as Qr, requireConfigArray as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, StackHasActiveImportsError as Ra, stateBucketExistenceConfirmed as Ri, refuseMalformedNestedChildOutputs as Rn, hasReasonlessBaselineRefusal as Rr, parameterTypeMayLoseSecretIdentity as Rt, computeParameterTaint as S, CdkdError as Sa, runDockerStreaming as Si, hasReadableResources as Sn, recordMaskOnlyValue as Sr, isWaitAbandonedError as St, resourcesNamingDeclaredParameter as T, DependencyError as Ta, AssetManifestLoader as Ti, malformedExportSourceWarning as Tn, scrubResourceRecord as Tr, deleteIndeterminateGuards as Tt, coerceWarmThroughput as U, isCdkdError as Ua, MIGRATE_TMP_PREFIX as Ui, refuseMalformedOutputsForDestroy as Un, orphansAfterRollback as Ur, resolveExplicitPhysicalId as Ut, renderStatefulReason as V, SynthesisError as Va, CFN_TEMPLATE_BODY_LIMIT as Vi, refuseMalformedOrphansForOrphan as Vn, importableOutputs as Vr, WAFv2WebACLProvider as Vt, isWarmThroughputDecrease as W, normalizeAwsError as Wa, findLargeInlineResources as Wi, refuseMalformedResourceAttributesForOrphan as Wn, orphansCarriedFrom as Wr, assertRegionMatch as Wt, gray as X, isThrottlingError as Xa, producerRecordKey as Xi, repairMalformedOrphansForReadOnly as Xn, createAssetRedirectResolver as Xr, readConfigString as Xt, cyan as Y, isRetryableTransientError as Ya, producerCoordinateKey as Yi, refuseMalformedState as Yn, buildAssetRedirectMap as Yr, configStringRefusal as Yt, green as Z, isTransientServerError as Za, decodeListingKey as Zi, repairMalformedOutputsForReadOnly as Zn, loadPublishableAssetManifest as Zr, replayWarn as Zt, bagHoldsSecretExpression as _, getAwsClients as _a, formatDockerLoginError as _i, UNREADABLE_ORPHANS_CONTAINER_ROW as _n, identityKeyFor as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, processStackMessages as aa, parseBootstrapMarker as ai, s3BucketDomainName as an, describeTypeWithThrottleRetry as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, setAwsClients as ba, redactDockerArgvValues as bi, hasReadableOrphans as bn, maskSecretsInError as br, startInterruptWatch as bt, replayFailedOperations as c, hasPlainPrototype$1 as ca, validateContainerRepoName as ci, s3BucketWebsiteUrl as cn, TemplateParser as cr, IAMRoleProvider as ct, updatePartialMessage as d, absoluteAssemblyPathEscape as da, resolveDockerContextDirectory as di, applyCallerIdentityCredentials as dn, STATE_SOURCED_READBACK_RULES as dr, ProviderRegistry as dt, canonicalizeRegion as ea, BOOTSTRAP_MARKER_PREFIX as ei, requireConfigString as en, retryClassificationText as eo, repairMalformedResourcesForReadOnly as er, collectDeclaredOutputNames as et, updatePartialReason as f, renderAssemblyPathEscape as fa, warnManifestExecutable as fi, callerEnvCredentials as fn, TEMPLATE_SOURCED_RULES as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, AwsClients as ga, dockerSpawnEnvWithSensitive as gi, STATE_RESOURCES_MALFORMED as gn, errorCauseChain as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, resolveBucketRegion as ha, describeDockerFailure as hi, INTRINSIC_KEYS as hn, dynamicReferenceTokens as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, globMatches as ia, isCrossRegionRedirect as ii, s3BucketArn as in, findSilentDropProperties as ir, secretBearing as it, isFinalSnapshotError as j, LockError as ja, resolveApp as ji, malformedOutputsWarning as jn, buildLockContentionMessage as jr, carriesFetchableDynamicReference as jt, ccRoutedFinalSnapshotError as k, LocalInvokeBuildError as ka, getDefaultStateBucketName as ki, malformedOrphansWarning as kn, UNREPRODUCIBLE_LOCK_CLAUSE as kr, IntrinsicFunctionResolver as kt, replayRollback as l, nullPrototypeRecord as la, buildDenyExternalAccessPolicy as li, applyRoleArnIfSet as ln, STATE_SOURCED_BASELINE_RULES as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, clearBucketRegionCache as ma, describeDockerExecFailure as mi, DiffCalculator as mn, createSecretMasker as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, AssemblyReader as na, ensureAssetStorage as ni, producerRegionsFromState as nn, safeStringify$1 as no, safeStackName as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, defineOwnKey as oa, readBootstrapMarkerBody as oi, s3BucketDualStackDomainName as on, withRetry as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, resolveAssemblyPath as pa, describeDockerCapturedOutput as pi, envCredentialsAreAssumedRole as pn, carriesSecretMask as pr, createMaskedRetryLogger as pt, formatResourceLine as q, shellQuote as qa, injectiveKey as qi, refuseMalformedResourcePropertiesForOrphan as qn, stringifyValue as qr, configBooleanRefusal as qt, DeployEngine as r, failedStageNote as ra, getBootstrapMarkerKey as ri, withSharedDrainBudget as rn, __exportAll as ro, findActionableSilentDrops as rr, isExportAliasCollision as rt, planRollback as s, hasOwnKey as sa, validateAssetBucketName as si, s3BucketRegionalDomainName as sn, DagBuilder as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, derivePartitionAndUrlSuffix as ta, assertAssetBucketRegion as ti, classifyReplaySecretRegion as tn, describeAwsFailure as to, safeRegion as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, ownValue as ua, buildDockerImage as ui, AWS_CREDENTIAL_ENV_KEYS as un, STATE_SOURCED_CROSS_GENERATION_RULES as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, resetAwsClients as va, getDockerCmd as vi, UNREADABLE_RESOURCES_MAP_ROW as vn, isSingleDynamicReferenceToken as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, CrossAccountSecretRefusalError as wa, stripControlChars as wi, malformedExportNamesWarning as wn, redactSecretsForState as wr, UNSPECIFIED_SKIP_REASON as wt, bindingSkippedOutputs as x, AssetError as xa, runDockerForeground as xi, hasReadableOutputs as xn, maskSecretsInText as xr, CloudControlProvider as xt, keptWholeReasonText as y, runWithStackAwsClients as ya, partitionSensitiveEnv as yi, displayLogicalId as yn, markSameGenerationBag as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, StackTerminationProtectionError as za, warnDeprecatedNoPrefixCliFlag as zi, refuseMalformedOrphans as zn, hasUnverifiableParameterRefusal as zr, refStateLookupFromResource as zt };
|
|
51284
|
+
//# sourceMappingURL=deploy-engine-TkL9nyq8.js.map
|