@go-to-k/cdkd 0.82.0 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +470 -153
- package/dist/cli.js.map +4 -4
- package/dist/go-to-k-cdkd-0.83.0.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.82.0.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -70040,6 +70040,76 @@ import { dirname as dirname7 } from "node:path";
|
|
|
70040
70040
|
import * as path2 from "node:path";
|
|
70041
70041
|
import { Command as Command16, Option as Option9 } from "commander";
|
|
70042
70042
|
|
|
70043
|
+
// src/cli/commands/local-state-loader.ts
|
|
70044
|
+
init_aws_clients();
|
|
70045
|
+
async function loadStateForStack(stackName, synthRegion, opts) {
|
|
70046
|
+
const logger = getLogger();
|
|
70047
|
+
const prefix = opts.logPrefix ?? "--from-state";
|
|
70048
|
+
const region = opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1";
|
|
70049
|
+
let stateBucket;
|
|
70050
|
+
try {
|
|
70051
|
+
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
70052
|
+
} catch (err) {
|
|
70053
|
+
logger.warn(
|
|
70054
|
+
`${prefix}: could not resolve state bucket: ${err instanceof Error ? err.message : String(err)}. Falling back.`
|
|
70055
|
+
);
|
|
70056
|
+
return void 0;
|
|
70057
|
+
}
|
|
70058
|
+
const awsClients = new AwsClients({
|
|
70059
|
+
...opts.region !== void 0 && { region: opts.region },
|
|
70060
|
+
...opts.profile !== void 0 && { profile: opts.profile }
|
|
70061
|
+
});
|
|
70062
|
+
setAwsClients(awsClients);
|
|
70063
|
+
try {
|
|
70064
|
+
const stateConfig = { bucket: stateBucket, prefix: opts.statePrefix };
|
|
70065
|
+
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
70066
|
+
...opts.region !== void 0 && { region: opts.region },
|
|
70067
|
+
...opts.profile !== void 0 && { profile: opts.profile }
|
|
70068
|
+
});
|
|
70069
|
+
await stateBackend.verifyBucketExists();
|
|
70070
|
+
const refs = (await stateBackend.listStacks()).filter((r) => r.stackName === stackName);
|
|
70071
|
+
if (refs.length === 0) {
|
|
70072
|
+
logger.warn(
|
|
70073
|
+
`${prefix}: no cdkd state found for stack '${stackName}' in bucket '${stateBucket}'. Was it deployed via 'cdkd deploy'? Falling back.`
|
|
70074
|
+
);
|
|
70075
|
+
return void 0;
|
|
70076
|
+
}
|
|
70077
|
+
let targetRegion;
|
|
70078
|
+
if (opts.stackRegion) {
|
|
70079
|
+
const found = refs.find((r) => r.region === opts.stackRegion);
|
|
70080
|
+
if (!found) {
|
|
70081
|
+
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
70082
|
+
logger.warn(
|
|
70083
|
+
`${prefix}: stack '${stackName}' has no state in region '${opts.stackRegion}' (available: ${seen}). Falling back.`
|
|
70084
|
+
);
|
|
70085
|
+
return void 0;
|
|
70086
|
+
}
|
|
70087
|
+
targetRegion = opts.stackRegion;
|
|
70088
|
+
} else if (synthRegion && refs.some((r) => r.region === synthRegion)) {
|
|
70089
|
+
targetRegion = synthRegion;
|
|
70090
|
+
} else if (refs.length === 1) {
|
|
70091
|
+
targetRegion = refs[0].region ?? synthRegion ?? region;
|
|
70092
|
+
} else {
|
|
70093
|
+
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
70094
|
+
logger.warn(
|
|
70095
|
+
`${prefix}: stack '${stackName}' has state in multiple regions (${seen}). Re-run with --stack-region <region>. Falling back.`
|
|
70096
|
+
);
|
|
70097
|
+
return void 0;
|
|
70098
|
+
}
|
|
70099
|
+
const stateData = await stateBackend.getState(stackName, targetRegion);
|
|
70100
|
+
if (!stateData) {
|
|
70101
|
+
logger.warn(
|
|
70102
|
+
`${prefix}: state record for '${stackName}' (${targetRegion}) returned empty. Falling back.`
|
|
70103
|
+
);
|
|
70104
|
+
return void 0;
|
|
70105
|
+
}
|
|
70106
|
+
logger.debug(`${prefix}: loaded state for ${stackName} (${targetRegion})`);
|
|
70107
|
+
return { state: stateData.state, region: targetRegion };
|
|
70108
|
+
} finally {
|
|
70109
|
+
awsClients.destroy();
|
|
70110
|
+
}
|
|
70111
|
+
}
|
|
70112
|
+
|
|
70043
70113
|
// src/local/lambda-resolver.ts
|
|
70044
70114
|
import { existsSync as existsSync4, statSync as statSync3 } from "node:fs";
|
|
70045
70115
|
import { dirname, isAbsolute, resolve as resolve4 } from "node:path";
|
|
@@ -71217,8 +71287,23 @@ function extractHashFromImageUri(imageUri) {
|
|
|
71217
71287
|
return match?.[1];
|
|
71218
71288
|
}
|
|
71219
71289
|
|
|
71220
|
-
// src/
|
|
71221
|
-
|
|
71290
|
+
// src/utils/single-flight.ts
|
|
71291
|
+
function singleFlight(fn, onError) {
|
|
71292
|
+
let promise;
|
|
71293
|
+
return async () => {
|
|
71294
|
+
if (!promise) {
|
|
71295
|
+
promise = (async () => {
|
|
71296
|
+
try {
|
|
71297
|
+
await fn();
|
|
71298
|
+
} catch (err) {
|
|
71299
|
+
if (onError)
|
|
71300
|
+
onError(err);
|
|
71301
|
+
}
|
|
71302
|
+
})();
|
|
71303
|
+
}
|
|
71304
|
+
await promise;
|
|
71305
|
+
};
|
|
71306
|
+
}
|
|
71222
71307
|
|
|
71223
71308
|
// src/cli/commands/local-start-api.ts
|
|
71224
71309
|
import { cpSync, mkdirSync as mkdirSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
@@ -76047,21 +76132,12 @@ async function localStartApiCommand(options) {
|
|
|
76047
76132
|
});
|
|
76048
76133
|
logger.info(`Watching ${options.output} (and ${lastAssetPaths.value.length} asset dir(s))`);
|
|
76049
76134
|
}
|
|
76050
|
-
let
|
|
76135
|
+
let shutdownStarted = false;
|
|
76136
|
+
let firstSignal;
|
|
76137
|
+
let firstExitCode = 0;
|
|
76051
76138
|
let forceExitArmed = false;
|
|
76052
|
-
const
|
|
76053
|
-
|
|
76054
|
-
if (!forceExitArmed) {
|
|
76055
|
-
forceExitArmed = true;
|
|
76056
|
-
logger.warn(
|
|
76057
|
-
`Received second ${signal}; force-exiting. Orphan containers may remain \u2014 run 'docker ps --filter name=cdkd-local-' and 'docker rm -f' to clean up.`
|
|
76058
|
-
);
|
|
76059
|
-
process.exit(130);
|
|
76060
|
-
}
|
|
76061
|
-
return;
|
|
76062
|
-
}
|
|
76063
|
-
shuttingDown = true;
|
|
76064
|
-
logger.info(`Received ${signal}, shutting down...`);
|
|
76139
|
+
const runCleanup = singleFlight(async () => {
|
|
76140
|
+
logger.info(`Received ${firstSignal}, shutting down...`);
|
|
76065
76141
|
if (watcher) {
|
|
76066
76142
|
try {
|
|
76067
76143
|
await watcher.close();
|
|
@@ -76109,7 +76185,23 @@ async function localStartApiCommand(options) {
|
|
|
76109
76185
|
);
|
|
76110
76186
|
}
|
|
76111
76187
|
}
|
|
76112
|
-
|
|
76188
|
+
});
|
|
76189
|
+
const shutdown = async (signal, exitCode) => {
|
|
76190
|
+
if (shutdownStarted) {
|
|
76191
|
+
if (!forceExitArmed) {
|
|
76192
|
+
forceExitArmed = true;
|
|
76193
|
+
logger.warn(
|
|
76194
|
+
`Received second ${signal}; force-exiting. Orphan containers may remain \u2014 run 'docker ps --filter name=cdkd-local-' and 'docker rm -f' to clean up.`
|
|
76195
|
+
);
|
|
76196
|
+
process.exit(130);
|
|
76197
|
+
}
|
|
76198
|
+
return;
|
|
76199
|
+
}
|
|
76200
|
+
shutdownStarted = true;
|
|
76201
|
+
firstSignal = signal;
|
|
76202
|
+
firstExitCode = exitCode;
|
|
76203
|
+
await runCleanup();
|
|
76204
|
+
process.exit(firstExitCode);
|
|
76113
76205
|
};
|
|
76114
76206
|
process.on("SIGINT", () => {
|
|
76115
76207
|
void shutdown("SIGINT", 130);
|
|
@@ -76621,6 +76713,83 @@ var EcsTaskResolutionError = class _EcsTaskResolutionError extends Error {
|
|
|
76621
76713
|
Object.setPrototypeOf(this, _EcsTaskResolutionError.prototype);
|
|
76622
76714
|
}
|
|
76623
76715
|
};
|
|
76716
|
+
function derivePartitionAndUrlSuffix(region) {
|
|
76717
|
+
if (region.startsWith("cn-"))
|
|
76718
|
+
return { partition: "aws-cn", urlSuffix: "amazonaws.com.cn" };
|
|
76719
|
+
if (region.startsWith("us-gov-"))
|
|
76720
|
+
return { partition: "aws-us-gov", urlSuffix: "amazonaws.com" };
|
|
76721
|
+
if (region.startsWith("us-iso-"))
|
|
76722
|
+
return { partition: "aws-iso", urlSuffix: "c2s.ic.gov" };
|
|
76723
|
+
if (region.startsWith("us-isob-"))
|
|
76724
|
+
return { partition: "aws-iso-b", urlSuffix: "sc2s.sgov.gov" };
|
|
76725
|
+
return { partition: "aws", urlSuffix: "amazonaws.com" };
|
|
76726
|
+
}
|
|
76727
|
+
function detectEcsImageResolutionNeeds(stack) {
|
|
76728
|
+
const resources = stack.template.Resources ?? {};
|
|
76729
|
+
let needsPseudoParameters = false;
|
|
76730
|
+
let needsStateResources = false;
|
|
76731
|
+
for (const res of Object.values(resources)) {
|
|
76732
|
+
if (res.Type !== "AWS::ECS::TaskDefinition")
|
|
76733
|
+
continue;
|
|
76734
|
+
const props = res.Properties ?? {};
|
|
76735
|
+
const containers = Array.isArray(props["ContainerDefinitions"]) ? props["ContainerDefinitions"] : [];
|
|
76736
|
+
for (const c of containers) {
|
|
76737
|
+
if (!c || typeof c !== "object")
|
|
76738
|
+
continue;
|
|
76739
|
+
const image = c["Image"];
|
|
76740
|
+
const need = inspectImageForSubstitutions(image, resources);
|
|
76741
|
+
if (need.pseudo)
|
|
76742
|
+
needsPseudoParameters = true;
|
|
76743
|
+
if (need.state)
|
|
76744
|
+
needsStateResources = true;
|
|
76745
|
+
if (needsPseudoParameters && needsStateResources)
|
|
76746
|
+
break;
|
|
76747
|
+
}
|
|
76748
|
+
if (needsPseudoParameters && needsStateResources)
|
|
76749
|
+
break;
|
|
76750
|
+
}
|
|
76751
|
+
return { needsPseudoParameters, needsStateResources };
|
|
76752
|
+
}
|
|
76753
|
+
function inspectImageForSubstitutions(image, resources) {
|
|
76754
|
+
if (!image || typeof image !== "object")
|
|
76755
|
+
return { pseudo: false, state: false };
|
|
76756
|
+
const obj = image;
|
|
76757
|
+
const getAtt = obj["Fn::GetAtt"];
|
|
76758
|
+
if (getAtt !== void 0) {
|
|
76759
|
+
let lid;
|
|
76760
|
+
if (Array.isArray(getAtt) && typeof getAtt[0] === "string")
|
|
76761
|
+
lid = getAtt[0];
|
|
76762
|
+
else if (typeof getAtt === "string")
|
|
76763
|
+
lid = getAtt.split(".")[0];
|
|
76764
|
+
if (lid && resources[lid]?.Type === "AWS::ECR::Repository") {
|
|
76765
|
+
return { pseudo: false, state: true };
|
|
76766
|
+
}
|
|
76767
|
+
}
|
|
76768
|
+
let sub;
|
|
76769
|
+
const subRaw = obj["Fn::Sub"];
|
|
76770
|
+
if (typeof subRaw === "string")
|
|
76771
|
+
sub = subRaw;
|
|
76772
|
+
else if (Array.isArray(subRaw) && typeof subRaw[0] === "string")
|
|
76773
|
+
sub = subRaw[0];
|
|
76774
|
+
if (!sub)
|
|
76775
|
+
return { pseudo: false, state: false };
|
|
76776
|
+
let pseudo = false;
|
|
76777
|
+
let state = false;
|
|
76778
|
+
const placeholderRegex = /\$\{([^}]+)\}/g;
|
|
76779
|
+
let m;
|
|
76780
|
+
while ((m = placeholderRegex.exec(sub)) !== null) {
|
|
76781
|
+
const key = m[1];
|
|
76782
|
+
if (key.startsWith("AWS::")) {
|
|
76783
|
+
pseudo = true;
|
|
76784
|
+
continue;
|
|
76785
|
+
}
|
|
76786
|
+
const dot = key.indexOf(".");
|
|
76787
|
+
const lid = dot === -1 ? key : key.slice(0, dot);
|
|
76788
|
+
if (resources[lid]?.Type === "AWS::ECR::Repository")
|
|
76789
|
+
state = true;
|
|
76790
|
+
}
|
|
76791
|
+
return { pseudo, state };
|
|
76792
|
+
}
|
|
76624
76793
|
function parseEcsTarget(target) {
|
|
76625
76794
|
if (typeof target !== "string" || target.length === 0) {
|
|
76626
76795
|
throw new EcsTaskResolutionError(
|
|
@@ -76642,7 +76811,7 @@ function parseEcsTarget(target) {
|
|
|
76642
76811
|
}
|
|
76643
76812
|
return { stackPattern: null, pathOrId: target, isPath: false };
|
|
76644
76813
|
}
|
|
76645
|
-
function resolveEcsTaskTarget(target, stacks) {
|
|
76814
|
+
function resolveEcsTaskTarget(target, stacks, context) {
|
|
76646
76815
|
if (stacks.length === 0) {
|
|
76647
76816
|
throw new EcsTaskResolutionError("No stacks found in the synthesized assembly.");
|
|
76648
76817
|
}
|
|
@@ -76685,7 +76854,7 @@ function resolveEcsTaskTarget(target, stacks) {
|
|
|
76685
76854
|
`Resource '${logicalId}' in ${stack.stackName} is ${resource.Type}, not an AWS::ECS::TaskDefinition.`
|
|
76686
76855
|
);
|
|
76687
76856
|
}
|
|
76688
|
-
return extractTaskDefinitionProperties(stack, logicalId, resource);
|
|
76857
|
+
return extractTaskDefinitionProperties(stack, logicalId, resource, context);
|
|
76689
76858
|
}
|
|
76690
76859
|
function pickStack2(parsed, stacks) {
|
|
76691
76860
|
if (parsed.stackPattern === null) {
|
|
@@ -76708,7 +76877,7 @@ function pickStack2(parsed, stacks) {
|
|
|
76708
76877
|
}
|
|
76709
76878
|
return matched[0];
|
|
76710
76879
|
}
|
|
76711
|
-
function extractTaskDefinitionProperties(stack, logicalId, resource) {
|
|
76880
|
+
function extractTaskDefinitionProperties(stack, logicalId, resource, context) {
|
|
76712
76881
|
const props = resource.Properties ?? {};
|
|
76713
76882
|
const warnings = [];
|
|
76714
76883
|
const family = pickString(props["Family"]) ?? logicalId;
|
|
@@ -76735,7 +76904,7 @@ function extractTaskDefinitionProperties(stack, logicalId, resource) {
|
|
|
76735
76904
|
throw new EcsTaskResolutionError(`Task definition '${logicalId}' has no ContainerDefinitions.`);
|
|
76736
76905
|
}
|
|
76737
76906
|
const containers = rawContainers.map(
|
|
76738
|
-
(c, idx) => parseContainerDefinition(c, idx, logicalId, resources, stack)
|
|
76907
|
+
(c, idx) => parseContainerDefinition(c, idx, logicalId, resources, stack, context)
|
|
76739
76908
|
);
|
|
76740
76909
|
const rawVolumes = props["Volumes"];
|
|
76741
76910
|
const volumes = Array.isArray(rawVolumes) ? rawVolumes.map((v, idx) => parseVolume(v, idx, logicalId)) : [];
|
|
@@ -76781,7 +76950,7 @@ function parseRuntimePlatform(value) {
|
|
|
76781
76950
|
return void 0;
|
|
76782
76951
|
return { cpuArchitecture: cpu, operatingSystemFamily: os };
|
|
76783
76952
|
}
|
|
76784
|
-
function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack) {
|
|
76953
|
+
function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack, context) {
|
|
76785
76954
|
if (!raw || typeof raw !== "object") {
|
|
76786
76955
|
throw new EcsTaskResolutionError(
|
|
76787
76956
|
`Task '${taskLogicalId}' ContainerDefinitions[${idx}] is not an object.`
|
|
@@ -76794,7 +76963,7 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack) {
|
|
|
76794
76963
|
`Task '${taskLogicalId}' ContainerDefinitions[${idx}] has no Name.`
|
|
76795
76964
|
);
|
|
76796
76965
|
}
|
|
76797
|
-
const image = parseContainerImage(c["Image"], name, taskLogicalId, resources, stack);
|
|
76966
|
+
const image = parseContainerImage(c["Image"], name, taskLogicalId, resources, stack, context);
|
|
76798
76967
|
const command = pickStringArray2(c["Command"]);
|
|
76799
76968
|
const entryPoint = pickStringArray2(c["EntryPoint"]);
|
|
76800
76969
|
const workingDirectory = pickString(c["WorkingDirectory"]);
|
|
@@ -76940,7 +77109,11 @@ function parseContainerDefinition(raw, idx, taskLogicalId, resources, stack) {
|
|
|
76940
77109
|
out.readonlyRootFilesystem = readonlyRootFilesystem;
|
|
76941
77110
|
return out;
|
|
76942
77111
|
}
|
|
76943
|
-
function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack) {
|
|
77112
|
+
function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack, context) {
|
|
77113
|
+
const getAttImage = tryResolveImageGetAtt(raw, resources, context);
|
|
77114
|
+
if (getAttImage) {
|
|
77115
|
+
return classifyResolvedImage(getAttImage);
|
|
77116
|
+
}
|
|
76944
77117
|
const flat = extractImageString(raw);
|
|
76945
77118
|
if (!flat) {
|
|
76946
77119
|
throw new EcsTaskResolutionError(
|
|
@@ -76954,23 +77127,123 @@ function parseContainerImage(raw, containerName, taskLogicalId, resources, _stac
|
|
|
76954
77127
|
out.assetHash = hashMatch[1];
|
|
76955
77128
|
return out;
|
|
76956
77129
|
}
|
|
76957
|
-
const
|
|
76958
|
-
if (
|
|
76959
|
-
|
|
76960
|
-
|
|
76961
|
-
|
|
77130
|
+
const substituted = substituteImagePlaceholders(flat, resources, context);
|
|
77131
|
+
if (substituted.includes("${")) {
|
|
77132
|
+
const unresolvedRepoRef = findUnresolvedEcrRepositoryRef(substituted, resources);
|
|
77133
|
+
if (unresolvedRepoRef) {
|
|
77134
|
+
throw new EcsTaskResolutionError(
|
|
77135
|
+
`Container '${containerName}' in task '${taskLogicalId}' references same-stack ECR repository '${unresolvedRepoRef}'. cdkd local run-task v1 cannot resolve the repository URI without state \u2014 pass --from-state (the stack must have been deployed via cdkd deploy), build via ContainerImage.fromAsset, or pin a public image.`
|
|
77136
|
+
);
|
|
77137
|
+
}
|
|
77138
|
+
if (substituted.includes("AWS::")) {
|
|
77139
|
+
throw new EcsTaskResolutionError(
|
|
77140
|
+
`Container '${containerName}' in task '${taskLogicalId}' has an Image that references AWS pseudo parameters (${substituted}). cdkd could not resolve them: confirm AWS credentials are configured so STS GetCallerIdentity succeeds, and that --region / AWS_REGION names the target region. Workaround: build the image locally (ContainerImage.fromAsset) or pin a public image.`
|
|
77141
|
+
);
|
|
77142
|
+
}
|
|
76962
77143
|
throw new EcsTaskResolutionError(
|
|
76963
|
-
`Container '${containerName}' in task '${taskLogicalId}' has an Image
|
|
77144
|
+
`Container '${containerName}' in task '${taskLogicalId}' has an Image with unresolved \${...} placeholders (${substituted}). cdkd local run-task v1 only resolves AWS pseudo parameters and same-stack AWS::ECR::Repository refs.`
|
|
76964
77145
|
);
|
|
76965
77146
|
}
|
|
76966
|
-
|
|
76967
|
-
|
|
76968
|
-
|
|
76969
|
-
|
|
76970
|
-
|
|
77147
|
+
const ecrMatch = /^(\d{12})\.dkr\.ecr\.([^.]+)\.amazonaws\.com(?:\.cn)?\//.exec(substituted);
|
|
77148
|
+
if (ecrMatch) {
|
|
77149
|
+
return { kind: "ecr", uri: substituted, account: ecrMatch[1], region: ecrMatch[2] };
|
|
77150
|
+
}
|
|
77151
|
+
return { kind: "public", uri: substituted };
|
|
77152
|
+
}
|
|
77153
|
+
function findUnresolvedEcrRepositoryRef(substituted, resources) {
|
|
77154
|
+
const placeholderRegex = /\$\{([^}]+)\}/g;
|
|
77155
|
+
let m;
|
|
77156
|
+
while ((m = placeholderRegex.exec(substituted)) !== null) {
|
|
77157
|
+
const key = m[1];
|
|
77158
|
+
if (key.startsWith("AWS::"))
|
|
77159
|
+
continue;
|
|
77160
|
+
const dot = key.indexOf(".");
|
|
77161
|
+
const lid = dot === -1 ? key : key.slice(0, dot);
|
|
77162
|
+
if (resources[lid]?.Type === "AWS::ECR::Repository")
|
|
77163
|
+
return lid;
|
|
77164
|
+
}
|
|
77165
|
+
return void 0;
|
|
77166
|
+
}
|
|
77167
|
+
function classifyResolvedImage(uri) {
|
|
77168
|
+
if (uri.includes("cdk-hnb659fds-container-assets-")) {
|
|
77169
|
+
const hashMatch = /:([a-f0-9]{8,})$/.exec(uri);
|
|
77170
|
+
const out = { kind: "cdk-asset" };
|
|
77171
|
+
if (hashMatch)
|
|
77172
|
+
out.assetHash = hashMatch[1];
|
|
77173
|
+
return out;
|
|
77174
|
+
}
|
|
77175
|
+
const ecrMatch = /^(\d{12})\.dkr\.ecr\.([^.]+)\.amazonaws\.com(?:\.cn)?\//.exec(uri);
|
|
77176
|
+
if (ecrMatch) {
|
|
77177
|
+
return { kind: "ecr", uri, account: ecrMatch[1], region: ecrMatch[2] };
|
|
77178
|
+
}
|
|
77179
|
+
return { kind: "public", uri };
|
|
77180
|
+
}
|
|
77181
|
+
function substituteImagePlaceholders(flat, resources, context) {
|
|
77182
|
+
if (!flat.includes("${"))
|
|
77183
|
+
return flat;
|
|
77184
|
+
return flat.replace(/\$\{([^}]+)\}/g, (full, key) => {
|
|
77185
|
+
if (context?.pseudoParameters) {
|
|
77186
|
+
if (key === "AWS::AccountId" && context.pseudoParameters.accountId) {
|
|
77187
|
+
return context.pseudoParameters.accountId;
|
|
77188
|
+
}
|
|
77189
|
+
if (key === "AWS::Region" && context.pseudoParameters.region) {
|
|
77190
|
+
return context.pseudoParameters.region;
|
|
77191
|
+
}
|
|
77192
|
+
if (key === "AWS::Partition" && context.pseudoParameters.partition) {
|
|
77193
|
+
return context.pseudoParameters.partition;
|
|
77194
|
+
}
|
|
77195
|
+
if (key === "AWS::URLSuffix" && context.pseudoParameters.urlSuffix) {
|
|
77196
|
+
return context.pseudoParameters.urlSuffix;
|
|
77197
|
+
}
|
|
77198
|
+
}
|
|
77199
|
+
if (context?.stateResources) {
|
|
77200
|
+
const dot = key.indexOf(".");
|
|
77201
|
+
const logicalId = dot === -1 ? key : key.slice(0, dot);
|
|
77202
|
+
const refResource = resources[logicalId];
|
|
77203
|
+
const stateEntry = context.stateResources[logicalId];
|
|
77204
|
+
if (refResource?.Type === "AWS::ECR::Repository" && stateEntry) {
|
|
77205
|
+
if (dot === -1) {
|
|
77206
|
+
return stateEntry.physicalId;
|
|
77207
|
+
}
|
|
77208
|
+
const attr = key.slice(dot + 1);
|
|
77209
|
+
const cached = stateEntry.attributes?.[attr];
|
|
77210
|
+
if (typeof cached === "string")
|
|
77211
|
+
return cached;
|
|
77212
|
+
}
|
|
77213
|
+
}
|
|
77214
|
+
return full;
|
|
77215
|
+
});
|
|
77216
|
+
}
|
|
77217
|
+
function tryResolveImageGetAtt(raw, resources, context) {
|
|
77218
|
+
if (!raw || typeof raw !== "object")
|
|
77219
|
+
return void 0;
|
|
77220
|
+
const obj = raw;
|
|
77221
|
+
const arg = obj["Fn::GetAtt"];
|
|
77222
|
+
if (arg === void 0)
|
|
77223
|
+
return void 0;
|
|
77224
|
+
let logicalId;
|
|
77225
|
+
let attr;
|
|
77226
|
+
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && typeof arg[1] === "string") {
|
|
77227
|
+
logicalId = arg[0];
|
|
77228
|
+
attr = arg[1];
|
|
77229
|
+
} else if (typeof arg === "string") {
|
|
77230
|
+
const dot = arg.indexOf(".");
|
|
77231
|
+
if (dot > 0 && dot < arg.length - 1) {
|
|
77232
|
+
logicalId = arg.slice(0, dot);
|
|
77233
|
+
attr = arg.slice(dot + 1);
|
|
76971
77234
|
}
|
|
76972
77235
|
}
|
|
76973
|
-
|
|
77236
|
+
if (!logicalId || !attr)
|
|
77237
|
+
return void 0;
|
|
77238
|
+
const refResource = resources[logicalId];
|
|
77239
|
+
if (refResource?.Type !== "AWS::ECR::Repository")
|
|
77240
|
+
return void 0;
|
|
77241
|
+
if (attr !== "RepositoryUri" && attr !== "Arn")
|
|
77242
|
+
return void 0;
|
|
77243
|
+
const cached = context?.stateResources?.[logicalId]?.attributes?.[attr];
|
|
77244
|
+
if (typeof cached === "string" && cached.length > 0)
|
|
77245
|
+
return cached;
|
|
77246
|
+
return void 0;
|
|
76974
77247
|
}
|
|
76975
77248
|
function extractImageString(value) {
|
|
76976
77249
|
if (typeof value === "string" && value.length > 0)
|
|
@@ -77049,6 +77322,7 @@ function parseVolume(raw, idx, taskLogicalId) {
|
|
|
77049
77322
|
}
|
|
77050
77323
|
return { name, kind: "host" };
|
|
77051
77324
|
}
|
|
77325
|
+
var TASK_ROLE_ACCOUNT_PLACEHOLDER = "${AWS::AccountId}";
|
|
77052
77326
|
function resolveRoleArn(value, resources) {
|
|
77053
77327
|
if (value === void 0 || value === null)
|
|
77054
77328
|
return void 0;
|
|
@@ -77057,24 +77331,23 @@ function resolveRoleArn(value, resources) {
|
|
|
77057
77331
|
if (typeof value !== "object")
|
|
77058
77332
|
return void 0;
|
|
77059
77333
|
const obj = value;
|
|
77334
|
+
let refLogicalId;
|
|
77060
77335
|
if ("Ref" in obj && typeof obj["Ref"] === "string") {
|
|
77061
|
-
|
|
77062
|
-
|
|
77063
|
-
if (role?.Type === "AWS::IAM::Role") {
|
|
77064
|
-
return void 0;
|
|
77065
|
-
}
|
|
77066
|
-
}
|
|
77067
|
-
if ("Fn::GetAtt" in obj) {
|
|
77336
|
+
refLogicalId = obj["Ref"];
|
|
77337
|
+
} else if ("Fn::GetAtt" in obj) {
|
|
77068
77338
|
const arg = obj["Fn::GetAtt"];
|
|
77069
77339
|
if (Array.isArray(arg) && typeof arg[0] === "string") {
|
|
77070
|
-
const
|
|
77071
|
-
|
|
77072
|
-
|
|
77073
|
-
return void 0;
|
|
77074
|
-
}
|
|
77340
|
+
const attr = typeof arg[1] === "string" ? arg[1] : "";
|
|
77341
|
+
if (attr === "" || attr === "Arn")
|
|
77342
|
+
refLogicalId = arg[0];
|
|
77075
77343
|
}
|
|
77076
77344
|
}
|
|
77077
|
-
|
|
77345
|
+
if (refLogicalId === void 0)
|
|
77346
|
+
return void 0;
|
|
77347
|
+
const role = resources[refLogicalId];
|
|
77348
|
+
if (role?.Type !== "AWS::IAM::Role")
|
|
77349
|
+
return void 0;
|
|
77350
|
+
return `arn:aws:iam::${TASK_ROLE_ACCOUNT_PLACEHOLDER}:role/${refLogicalId}`;
|
|
77078
77351
|
}
|
|
77079
77352
|
function pickString(value) {
|
|
77080
77353
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -77911,7 +78184,8 @@ async function localRunTaskCommand(target, options) {
|
|
|
77911
78184
|
...Object.keys(context).length > 0 && { context }
|
|
77912
78185
|
};
|
|
77913
78186
|
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
77914
|
-
const
|
|
78187
|
+
const imageContext = await buildEcsImageResolutionContext(target, stacks, options);
|
|
78188
|
+
const task = resolveEcsTaskTarget(target, stacks, imageContext);
|
|
77915
78189
|
logger.info(
|
|
77916
78190
|
`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`
|
|
77917
78191
|
);
|
|
@@ -77930,10 +78204,10 @@ async function localRunTaskCommand(target, options) {
|
|
|
77930
78204
|
if (options.assumeTaskRole === true) {
|
|
77931
78205
|
if (!task.taskRoleArn) {
|
|
77932
78206
|
throw new Error(
|
|
77933
|
-
`--assume-task-role passed without an ARN but the task definition
|
|
78207
|
+
`--assume-task-role passed without an ARN but the task definition has no resolvable TaskRoleArn. Either the task definition does not set TaskRoleArn, or it points at a resource cdkd cannot resolve to an IAM Role at synth time. Pass the ARN explicitly: --assume-task-role <arn>`
|
|
77934
78208
|
);
|
|
77935
78209
|
}
|
|
77936
|
-
resolvedRoleArn = task.taskRoleArn;
|
|
78210
|
+
resolvedRoleArn = await resolvePlaceholderAccount(task.taskRoleArn, options.region);
|
|
77937
78211
|
assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);
|
|
77938
78212
|
} else if (typeof options.assumeTaskRole === "string") {
|
|
77939
78213
|
resolvedRoleArn = options.assumeTaskRole;
|
|
@@ -77981,6 +78255,24 @@ async function localRunTaskCommand(target, options) {
|
|
|
77981
78255
|
await cleanup();
|
|
77982
78256
|
}
|
|
77983
78257
|
}
|
|
78258
|
+
async function resolvePlaceholderAccount(arn, region) {
|
|
78259
|
+
if (!arn.includes(TASK_ROLE_ACCOUNT_PLACEHOLDER))
|
|
78260
|
+
return arn;
|
|
78261
|
+
const { STSClient: STSClient11, GetCallerIdentityCommand: GetCallerIdentityCommand12 } = await import("@aws-sdk/client-sts");
|
|
78262
|
+
const sts = new STSClient11({ ...region && { region } });
|
|
78263
|
+
try {
|
|
78264
|
+
const identity = await sts.send(new GetCallerIdentityCommand12({}));
|
|
78265
|
+
const account = identity.Account;
|
|
78266
|
+
if (!account) {
|
|
78267
|
+
throw new Error(
|
|
78268
|
+
`--assume-task-role: GetCallerIdentity returned no Account; cannot resolve placeholder ARN '${arn}'. Pass the ARN explicitly: --assume-task-role <arn>`
|
|
78269
|
+
);
|
|
78270
|
+
}
|
|
78271
|
+
return arn.split(TASK_ROLE_ACCOUNT_PLACEHOLDER).join(account);
|
|
78272
|
+
} finally {
|
|
78273
|
+
sts.destroy();
|
|
78274
|
+
}
|
|
78275
|
+
}
|
|
77984
78276
|
async function assumeTaskRole(roleArn, region) {
|
|
77985
78277
|
const { STSClient: STSClient11, AssumeRoleCommand: AssumeRoleCommand2 } = await import("@aws-sdk/client-sts");
|
|
77986
78278
|
const sts = new STSClient11({ ...region && { region } });
|
|
@@ -78005,6 +78297,80 @@ async function assumeTaskRole(roleArn, region) {
|
|
|
78005
78297
|
sts.destroy();
|
|
78006
78298
|
}
|
|
78007
78299
|
}
|
|
78300
|
+
async function buildEcsImageResolutionContext(target, stacks, options) {
|
|
78301
|
+
const logger = getLogger();
|
|
78302
|
+
const parsed = parseEcsTarget(target);
|
|
78303
|
+
const candidate = pickCandidateStack(parsed.stackPattern, stacks);
|
|
78304
|
+
if (!candidate)
|
|
78305
|
+
return void 0;
|
|
78306
|
+
const needs = detectEcsImageResolutionNeeds(candidate);
|
|
78307
|
+
if (!needs.needsPseudoParameters && !needs.needsStateResources)
|
|
78308
|
+
return void 0;
|
|
78309
|
+
const ctx = {};
|
|
78310
|
+
if (needs.needsPseudoParameters) {
|
|
78311
|
+
const region = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region;
|
|
78312
|
+
if (!region) {
|
|
78313
|
+
logger.warn(
|
|
78314
|
+
"Container Image references ${AWS::Region} but cdkd could not determine the target region. Pass --region, set AWS_REGION, or declare env.region on the CDK stack."
|
|
78315
|
+
);
|
|
78316
|
+
}
|
|
78317
|
+
let accountId;
|
|
78318
|
+
try {
|
|
78319
|
+
accountId = await resolveCallerAccountId(region);
|
|
78320
|
+
} catch (err) {
|
|
78321
|
+
logger.warn(
|
|
78322
|
+
`Container Image references \${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. Substitution will be skipped; the resolver will surface its existing error.`
|
|
78323
|
+
);
|
|
78324
|
+
}
|
|
78325
|
+
const partitionAndSuffix = region ? derivePartitionAndUrlSuffix(region) : void 0;
|
|
78326
|
+
ctx.pseudoParameters = {
|
|
78327
|
+
...accountId !== void 0 && { accountId },
|
|
78328
|
+
...region !== void 0 && { region },
|
|
78329
|
+
...partitionAndSuffix && {
|
|
78330
|
+
partition: partitionAndSuffix.partition,
|
|
78331
|
+
urlSuffix: partitionAndSuffix.urlSuffix
|
|
78332
|
+
}
|
|
78333
|
+
};
|
|
78334
|
+
}
|
|
78335
|
+
if (options.fromState && needs.needsStateResources) {
|
|
78336
|
+
const loaded = await loadStateForStack(candidate.stackName, candidate.region, {
|
|
78337
|
+
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
|
|
78338
|
+
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
78339
|
+
statePrefix: options.statePrefix,
|
|
78340
|
+
...options.region !== void 0 && { region: options.region },
|
|
78341
|
+
...options.profile !== void 0 && { profile: options.profile }
|
|
78342
|
+
});
|
|
78343
|
+
if (loaded) {
|
|
78344
|
+
ctx.stateResources = loaded.state.resources;
|
|
78345
|
+
}
|
|
78346
|
+
} else if (!options.fromState && needs.needsStateResources) {
|
|
78347
|
+
logger.warn(
|
|
78348
|
+
"Container Image references a same-stack AWS::ECR::Repository. Pass --from-state to substitute the deployed repository URI (requires the stack to have been deployed via cdkd deploy). Otherwise the resolver will surface its existing error."
|
|
78349
|
+
);
|
|
78350
|
+
}
|
|
78351
|
+
return ctx;
|
|
78352
|
+
}
|
|
78353
|
+
function pickCandidateStack(stackPattern, stacks) {
|
|
78354
|
+
if (stackPattern === null) {
|
|
78355
|
+
if (stacks.length === 1)
|
|
78356
|
+
return stacks[0];
|
|
78357
|
+
return void 0;
|
|
78358
|
+
}
|
|
78359
|
+
const matched = matchStacks(stacks, [stackPattern]);
|
|
78360
|
+
if (matched.length === 1)
|
|
78361
|
+
return matched[0];
|
|
78362
|
+
return void 0;
|
|
78363
|
+
}
|
|
78364
|
+
async function resolveCallerAccountId(region) {
|
|
78365
|
+
const { STSClient: STSClient11, GetCallerIdentityCommand: GetCallerIdentityCommand12 } = await import("@aws-sdk/client-sts");
|
|
78366
|
+
const sts = new STSClient11({ ...region && { region } });
|
|
78367
|
+
try {
|
|
78368
|
+
const identity = await sts.send(new GetCallerIdentityCommand12({}));
|
|
78369
|
+
return identity.Account;
|
|
78370
|
+
} finally {
|
|
78371
|
+
sts.destroy();
|
|
78372
|
+
}
|
|
78373
|
+
}
|
|
78008
78374
|
function readEnvOverridesFile2(filePath) {
|
|
78009
78375
|
if (!filePath)
|
|
78010
78376
|
return void 0;
|
|
@@ -78072,8 +78438,20 @@ function createLocalRunTaskCommand() {
|
|
|
78072
78438
|
"--detach",
|
|
78073
78439
|
"Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle."
|
|
78074
78440
|
).default(false)
|
|
78441
|
+
).addOption(
|
|
78442
|
+
new Option8(
|
|
78443
|
+
"--from-state",
|
|
78444
|
+
"Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default \u2014 only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag."
|
|
78445
|
+
).default(false)
|
|
78446
|
+
).addOption(
|
|
78447
|
+
new Option8(
|
|
78448
|
+
"--stack-region <region>",
|
|
78449
|
+
"Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions)."
|
|
78450
|
+
)
|
|
78075
78451
|
).action(withErrorHandling(localRunTaskCommand));
|
|
78076
|
-
[...commonOptions, ...appOptions, ...contextOptions].forEach(
|
|
78452
|
+
[...commonOptions, ...appOptions, ...contextOptions, ...stateOptions].forEach(
|
|
78453
|
+
(opt) => cmd.addOption(opt)
|
|
78454
|
+
);
|
|
78077
78455
|
cmd.addOption(deprecatedRegionOption);
|
|
78078
78456
|
return cmd;
|
|
78079
78457
|
}
|
|
@@ -78089,44 +78467,49 @@ async function localInvokeCommand(target, options) {
|
|
|
78089
78467
|
let containerId;
|
|
78090
78468
|
let stopLogs;
|
|
78091
78469
|
let sigintHandler;
|
|
78092
|
-
const cleanup =
|
|
78093
|
-
|
|
78094
|
-
|
|
78095
|
-
|
|
78096
|
-
|
|
78097
|
-
|
|
78098
|
-
|
|
78099
|
-
|
|
78470
|
+
const cleanup = singleFlight(
|
|
78471
|
+
async () => {
|
|
78472
|
+
if (stopLogs) {
|
|
78473
|
+
try {
|
|
78474
|
+
stopLogs();
|
|
78475
|
+
} catch (err) {
|
|
78476
|
+
getLogger().debug(
|
|
78477
|
+
`streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`
|
|
78478
|
+
);
|
|
78479
|
+
}
|
|
78100
78480
|
}
|
|
78101
|
-
|
|
78102
|
-
|
|
78103
|
-
|
|
78104
|
-
|
|
78105
|
-
|
|
78106
|
-
|
|
78107
|
-
|
|
78108
|
-
|
|
78481
|
+
if (containerId) {
|
|
78482
|
+
try {
|
|
78483
|
+
await removeContainer(containerId);
|
|
78484
|
+
} catch (err) {
|
|
78485
|
+
getLogger().debug(
|
|
78486
|
+
`removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
78487
|
+
);
|
|
78488
|
+
}
|
|
78109
78489
|
}
|
|
78110
|
-
|
|
78111
|
-
|
|
78112
|
-
|
|
78113
|
-
|
|
78114
|
-
|
|
78115
|
-
|
|
78116
|
-
|
|
78117
|
-
|
|
78490
|
+
if (imagePlan?.inlineTmpDir) {
|
|
78491
|
+
try {
|
|
78492
|
+
rmSync3(imagePlan.inlineTmpDir, { recursive: true, force: true });
|
|
78493
|
+
} catch (err) {
|
|
78494
|
+
getLogger().debug(
|
|
78495
|
+
`Failed to remove inline-code tmpdir ${imagePlan.inlineTmpDir}: ${err instanceof Error ? err.message : String(err)}`
|
|
78496
|
+
);
|
|
78497
|
+
}
|
|
78118
78498
|
}
|
|
78119
|
-
|
|
78120
|
-
|
|
78121
|
-
|
|
78122
|
-
|
|
78123
|
-
|
|
78124
|
-
|
|
78125
|
-
|
|
78126
|
-
|
|
78499
|
+
if (imagePlan?.layersTmpDir) {
|
|
78500
|
+
try {
|
|
78501
|
+
rmSync3(imagePlan.layersTmpDir, { recursive: true, force: true });
|
|
78502
|
+
} catch (err) {
|
|
78503
|
+
getLogger().debug(
|
|
78504
|
+
`Failed to remove merged-layers tmpdir ${imagePlan.layersTmpDir}: ${err instanceof Error ? err.message : String(err)}`
|
|
78505
|
+
);
|
|
78506
|
+
}
|
|
78127
78507
|
}
|
|
78508
|
+
},
|
|
78509
|
+
(err) => {
|
|
78510
|
+
getLogger().debug(`cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
78128
78511
|
}
|
|
78129
|
-
|
|
78512
|
+
);
|
|
78130
78513
|
try {
|
|
78131
78514
|
await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });
|
|
78132
78515
|
await ensureDockerAvailable();
|
|
@@ -78477,72 +78860,6 @@ function materializeInlineCode2(handler, source, fileExtension) {
|
|
|
78477
78860
|
writeFileSync6(filePath, source, "utf-8");
|
|
78478
78861
|
return dir;
|
|
78479
78862
|
}
|
|
78480
|
-
async function loadStateForStack(stackName, synthRegion, opts) {
|
|
78481
|
-
const logger = getLogger();
|
|
78482
|
-
const region = opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1";
|
|
78483
|
-
let stateBucket;
|
|
78484
|
-
try {
|
|
78485
|
-
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
78486
|
-
} catch (err) {
|
|
78487
|
-
logger.warn(
|
|
78488
|
-
`--from-state: could not resolve state bucket: ${err instanceof Error ? err.message : String(err)}. Falling back to PR 1 warn-and-drop semantics.`
|
|
78489
|
-
);
|
|
78490
|
-
return void 0;
|
|
78491
|
-
}
|
|
78492
|
-
const awsClients = new AwsClients({
|
|
78493
|
-
...opts.region !== void 0 && { region: opts.region },
|
|
78494
|
-
...opts.profile !== void 0 && { profile: opts.profile }
|
|
78495
|
-
});
|
|
78496
|
-
setAwsClients(awsClients);
|
|
78497
|
-
try {
|
|
78498
|
-
const stateConfig = { bucket: stateBucket, prefix: opts.statePrefix };
|
|
78499
|
-
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
78500
|
-
...opts.region !== void 0 && { region: opts.region },
|
|
78501
|
-
...opts.profile !== void 0 && { profile: opts.profile }
|
|
78502
|
-
});
|
|
78503
|
-
await stateBackend.verifyBucketExists();
|
|
78504
|
-
const refs = (await stateBackend.listStacks()).filter((r) => r.stackName === stackName);
|
|
78505
|
-
if (refs.length === 0) {
|
|
78506
|
-
logger.warn(
|
|
78507
|
-
`--from-state: no cdkd state found for stack '${stackName}' in bucket '${stateBucket}'. Was it deployed via 'cdkd deploy'? Falling back to PR 1 warn-and-drop semantics.`
|
|
78508
|
-
);
|
|
78509
|
-
return void 0;
|
|
78510
|
-
}
|
|
78511
|
-
let targetRegion;
|
|
78512
|
-
if (opts.stackRegion) {
|
|
78513
|
-
const found = refs.find((r) => r.region === opts.stackRegion);
|
|
78514
|
-
if (!found) {
|
|
78515
|
-
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
78516
|
-
logger.warn(
|
|
78517
|
-
`--from-state: stack '${stackName}' has no state in region '${opts.stackRegion}' (available: ${seen}). Falling back.`
|
|
78518
|
-
);
|
|
78519
|
-
return void 0;
|
|
78520
|
-
}
|
|
78521
|
-
targetRegion = opts.stackRegion;
|
|
78522
|
-
} else if (synthRegion && refs.some((r) => r.region === synthRegion)) {
|
|
78523
|
-
targetRegion = synthRegion;
|
|
78524
|
-
} else if (refs.length === 1) {
|
|
78525
|
-
targetRegion = refs[0].region ?? synthRegion ?? region;
|
|
78526
|
-
} else {
|
|
78527
|
-
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
78528
|
-
logger.warn(
|
|
78529
|
-
`--from-state: stack '${stackName}' has state in multiple regions (${seen}). Re-run with --stack-region <region>. Falling back.`
|
|
78530
|
-
);
|
|
78531
|
-
return void 0;
|
|
78532
|
-
}
|
|
78533
|
-
const stateData = await stateBackend.getState(stackName, targetRegion);
|
|
78534
|
-
if (!stateData) {
|
|
78535
|
-
logger.warn(
|
|
78536
|
-
`--from-state: state record for '${stackName}' (${targetRegion}) returned empty. Falling back.`
|
|
78537
|
-
);
|
|
78538
|
-
return void 0;
|
|
78539
|
-
}
|
|
78540
|
-
logger.debug(`--from-state: loaded state for ${stackName} (${targetRegion})`);
|
|
78541
|
-
return { state: stateData.state, region: targetRegion };
|
|
78542
|
-
} finally {
|
|
78543
|
-
awsClients.destroy();
|
|
78544
|
-
}
|
|
78545
|
-
}
|
|
78546
78863
|
function suggestAssumeRoleFromState(state, logicalId) {
|
|
78547
78864
|
const logger = getLogger();
|
|
78548
78865
|
const lambda = state.resources[logicalId];
|
|
@@ -78658,7 +78975,7 @@ function reorderArgs(argv) {
|
|
|
78658
78975
|
}
|
|
78659
78976
|
async function main() {
|
|
78660
78977
|
const program = new Command17();
|
|
78661
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
78978
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.83.0");
|
|
78662
78979
|
program.addCommand(createBootstrapCommand());
|
|
78663
78980
|
program.addCommand(createSynthCommand());
|
|
78664
78981
|
program.addCommand(createListCommand());
|