@go-to-k/cdkd 0.87.0 → 0.89.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 +183 -5
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.89.0.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.87.0.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -79381,6 +79381,7 @@ async function exportCommand(stackArg, options) {
|
|
|
79381
79381
|
let template;
|
|
79382
79382
|
let resolvedStackName;
|
|
79383
79383
|
let synthedRegion;
|
|
79384
|
+
let allSynthStacks = [];
|
|
79384
79385
|
if (options.template) {
|
|
79385
79386
|
if (!stackArg) {
|
|
79386
79387
|
throw new Error(
|
|
@@ -79424,6 +79425,10 @@ async function exportCommand(stackArg, options) {
|
|
|
79424
79425
|
template = stackInfo.template;
|
|
79425
79426
|
resolvedStackName = stackInfo.stackName;
|
|
79426
79427
|
synthedRegion = stackInfo.region;
|
|
79428
|
+
allSynthStacks = result.stacks.map((s) => ({
|
|
79429
|
+
stackName: s.stackName,
|
|
79430
|
+
template: s.template
|
|
79431
|
+
}));
|
|
79427
79432
|
}
|
|
79428
79433
|
const cfnStackName = options.cfnStackName ?? resolvedStackName;
|
|
79429
79434
|
const targetRegion = await pickStackRegion2(
|
|
@@ -79512,19 +79517,55 @@ async function exportCommand(stackArg, options) {
|
|
|
79512
79517
|
return;
|
|
79513
79518
|
}
|
|
79514
79519
|
}
|
|
79520
|
+
reportDriftBaselineGaps(state, logger);
|
|
79521
|
+
if (allSynthStacks.length > 0) {
|
|
79522
|
+
const crossRefs = scanCrossStackReferences(allSynthStacks, resolvedStackName);
|
|
79523
|
+
if (crossRefs.length > 0) {
|
|
79524
|
+
const lines = crossRefs.map(
|
|
79525
|
+
(r) => ` ${r.consumerStackName} \u2192 ${resolvedStackName}.${r.outputName} at ${r.location}`
|
|
79526
|
+
);
|
|
79527
|
+
if (options.strictCrossStack) {
|
|
79528
|
+
throw new Error(
|
|
79529
|
+
`Refusing to export: ${crossRefs.length} cross-stack reference(s) to ${resolvedStackName} found in sibling stacks. After migration, those references will break (cdkd's Fn::GetStackOutput reads cdkd state; the migrated stack's outputs live in CFn). Migrate consumers first, or remove the references, or drop --strict-cross-stack to proceed with a warning:
|
|
79530
|
+
` + lines.join("\n")
|
|
79531
|
+
);
|
|
79532
|
+
}
|
|
79533
|
+
logger.warn(
|
|
79534
|
+
`${crossRefs.length} cross-stack reference(s) to '${resolvedStackName}' from sibling stacks. These will break the next time those stacks deploy via cdkd (cdkd's Fn::GetStackOutput resolver reads cdkd state; the migrated stack's outputs are now in CFn). Plan multi-stack migrations from the leaves up.`
|
|
79535
|
+
);
|
|
79536
|
+
for (const line of lines)
|
|
79537
|
+
logger.warn(line);
|
|
79538
|
+
}
|
|
79539
|
+
}
|
|
79540
|
+
const userParameters = parseParameterOverrides(options.parameter);
|
|
79541
|
+
const { parameters: cfnParameters, missing } = resolveTemplateParameters(
|
|
79542
|
+
template,
|
|
79543
|
+
userParameters
|
|
79544
|
+
);
|
|
79545
|
+
if (missing.length > 0) {
|
|
79546
|
+
throw new Error(
|
|
79547
|
+
`Template requires parameter(s) without defaults: ${missing.join(", ")}. Pass each one as --parameter Key=Value (or set a Default in the CDK code).`
|
|
79548
|
+
);
|
|
79549
|
+
}
|
|
79515
79550
|
const phase1Template = filterTemplateForImport(template, phase1Imports);
|
|
79516
79551
|
await executeImportChangeSet(
|
|
79517
79552
|
awsClients.cloudFormation,
|
|
79518
79553
|
cfnStackName,
|
|
79519
79554
|
phase1Template,
|
|
79520
|
-
phase1Imports
|
|
79555
|
+
phase1Imports,
|
|
79556
|
+
cfnParameters
|
|
79521
79557
|
);
|
|
79522
79558
|
logger.info(
|
|
79523
79559
|
`\u2713 Phase 1: CloudFormation stack '${cfnStackName}' created via IMPORT. ${phase1Imports.length} resource(s) imported.`
|
|
79524
79560
|
);
|
|
79525
79561
|
if (phase2Creates.length > 0) {
|
|
79526
79562
|
try {
|
|
79527
|
-
await executeUpdateChangeSet(
|
|
79563
|
+
await executeUpdateChangeSet(
|
|
79564
|
+
awsClients.cloudFormation,
|
|
79565
|
+
cfnStackName,
|
|
79566
|
+
template,
|
|
79567
|
+
cfnParameters
|
|
79568
|
+
);
|
|
79528
79569
|
logger.info(`\u2713 Phase 2: ${phase2Creates.length} non-importable resource(s) CREATEd.`);
|
|
79529
79570
|
} catch (err) {
|
|
79530
79571
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -79762,6 +79803,64 @@ async function fetchPrimaryIdentifier(resourceType, cfnClient) {
|
|
|
79762
79803
|
`primary identifier unknown (DescribeType returned no usable schema and no fallback is registered). Add ${resourceType} to PRIMARY_IDENTIFIER_FALLBACK in export.ts, or open an issue.`
|
|
79763
79804
|
);
|
|
79764
79805
|
}
|
|
79806
|
+
function parseParameterOverrides(tokens) {
|
|
79807
|
+
const map = {};
|
|
79808
|
+
if (!tokens)
|
|
79809
|
+
return map;
|
|
79810
|
+
for (const t of tokens) {
|
|
79811
|
+
const eq = t.indexOf("=");
|
|
79812
|
+
if (eq < 1) {
|
|
79813
|
+
throw new Error(
|
|
79814
|
+
`Invalid --parameter '${t}': expected 'Key=Value' (e.g. --parameter Env=prod)`
|
|
79815
|
+
);
|
|
79816
|
+
}
|
|
79817
|
+
const key = t.slice(0, eq).trim();
|
|
79818
|
+
const value = t.slice(eq + 1);
|
|
79819
|
+
if (!key) {
|
|
79820
|
+
throw new Error(`Invalid --parameter '${t}': key is empty`);
|
|
79821
|
+
}
|
|
79822
|
+
map[key] = value;
|
|
79823
|
+
}
|
|
79824
|
+
return map;
|
|
79825
|
+
}
|
|
79826
|
+
function resolveTemplateParameters(template, userOverrides) {
|
|
79827
|
+
const tplParams = template["Parameters"];
|
|
79828
|
+
if (!tplParams || typeof tplParams !== "object" || Array.isArray(tplParams)) {
|
|
79829
|
+
const stray = Object.keys(userOverrides);
|
|
79830
|
+
if (stray.length > 0) {
|
|
79831
|
+
throw new Error(
|
|
79832
|
+
`--parameter override(s) supplied (${stray.join(", ")}) but template has no Parameters section.`
|
|
79833
|
+
);
|
|
79834
|
+
}
|
|
79835
|
+
return { parameters: [], missing: [] };
|
|
79836
|
+
}
|
|
79837
|
+
const parameters = [];
|
|
79838
|
+
const missing = [];
|
|
79839
|
+
const known = /* @__PURE__ */ new Set();
|
|
79840
|
+
for (const [name, raw] of Object.entries(tplParams)) {
|
|
79841
|
+
known.add(name);
|
|
79842
|
+
const def = raw ?? {};
|
|
79843
|
+
const override = userOverrides[name];
|
|
79844
|
+
if (override !== void 0) {
|
|
79845
|
+
parameters.push({ ParameterKey: name, ParameterValue: override });
|
|
79846
|
+
continue;
|
|
79847
|
+
}
|
|
79848
|
+
if ("Default" in def) {
|
|
79849
|
+
const value = typeof def.Default === "string" ? def.Default : String(def.Default);
|
|
79850
|
+
parameters.push({ ParameterKey: name, ParameterValue: value });
|
|
79851
|
+
continue;
|
|
79852
|
+
}
|
|
79853
|
+
missing.push(name);
|
|
79854
|
+
}
|
|
79855
|
+
for (const name of Object.keys(userOverrides)) {
|
|
79856
|
+
if (!known.has(name)) {
|
|
79857
|
+
throw new Error(
|
|
79858
|
+
`--parameter override '${name}' does not match any parameter in the synthesized template (template declares: ${[...known].join(", ") || "(none)"})`
|
|
79859
|
+
);
|
|
79860
|
+
}
|
|
79861
|
+
}
|
|
79862
|
+
return { parameters, missing };
|
|
79863
|
+
}
|
|
79765
79864
|
function filterTemplateForImport(template, plan) {
|
|
79766
79865
|
const allow = new Set(plan.map((p) => p.logicalId));
|
|
79767
79866
|
const original = template["Resources"];
|
|
@@ -79788,6 +79887,76 @@ function filterTemplateForImport(template, plan) {
|
|
|
79788
79887
|
}
|
|
79789
79888
|
return result;
|
|
79790
79889
|
}
|
|
79890
|
+
function reportDriftBaselineGaps(state, logger) {
|
|
79891
|
+
const entries = Object.entries(state.resources ?? {});
|
|
79892
|
+
if (entries.length === 0)
|
|
79893
|
+
return;
|
|
79894
|
+
const missing = entries.filter(([, r]) => r.observedProperties === void 0);
|
|
79895
|
+
if (missing.length === 0)
|
|
79896
|
+
return;
|
|
79897
|
+
if (state.version !== void 0 && state.version < 3) {
|
|
79898
|
+
logger.warn(
|
|
79899
|
+
`cdkd state schema is v${state.version} (pre-observedProperties). cdkd drift cannot reliably compare against AWS for this stack; the next \`cdk deploy\` after migration may surface spurious changes if AWS has drifted from the template. Run \`cdkd state refresh-observed ${state.stackName}\` (or any redeploy) before export to capture an AWS-current baseline.`
|
|
79900
|
+
);
|
|
79901
|
+
return;
|
|
79902
|
+
}
|
|
79903
|
+
logger.warn(
|
|
79904
|
+
`${missing.length} of ${entries.length} resource(s) in cdkd state lack an AWS-current baseline (observedProperties). cdkd drift may produce false positives for them; the next \`cdk deploy\` after migration may surface unexpected changes. Run \`cdkd state refresh-observed ${state.stackName}\` to capture a baseline before export, then \`cdkd drift\` to verify the stack matches AWS.`
|
|
79905
|
+
);
|
|
79906
|
+
for (const [logicalId] of missing.slice(0, 10)) {
|
|
79907
|
+
logger.warn(` ${logicalId}`);
|
|
79908
|
+
}
|
|
79909
|
+
if (missing.length > 10) {
|
|
79910
|
+
logger.warn(` ... and ${missing.length - 10} more`);
|
|
79911
|
+
}
|
|
79912
|
+
}
|
|
79913
|
+
function scanCrossStackReferences(stacks, exportingStackName) {
|
|
79914
|
+
const refs = [];
|
|
79915
|
+
for (const stack of stacks) {
|
|
79916
|
+
if (stack.stackName === exportingStackName)
|
|
79917
|
+
continue;
|
|
79918
|
+
walkForGetStackOutput(stack.template, "", (ref) => {
|
|
79919
|
+
if (ref.stackName === exportingStackName) {
|
|
79920
|
+
refs.push({
|
|
79921
|
+
consumerStackName: stack.stackName,
|
|
79922
|
+
outputName: ref.outputName,
|
|
79923
|
+
location: ref.location
|
|
79924
|
+
});
|
|
79925
|
+
}
|
|
79926
|
+
});
|
|
79927
|
+
}
|
|
79928
|
+
return refs;
|
|
79929
|
+
}
|
|
79930
|
+
function walkForGetStackOutput(node, path3, emit) {
|
|
79931
|
+
if (!node || typeof node !== "object")
|
|
79932
|
+
return;
|
|
79933
|
+
if (Array.isArray(node)) {
|
|
79934
|
+
node.forEach((item, i) => walkForGetStackOutput(item, `${path3}[${i}]`, emit));
|
|
79935
|
+
return;
|
|
79936
|
+
}
|
|
79937
|
+
const obj = node;
|
|
79938
|
+
const intrinsic = obj["Fn::GetStackOutput"];
|
|
79939
|
+
if (intrinsic !== void 0) {
|
|
79940
|
+
if (intrinsic && typeof intrinsic === "object" && !Array.isArray(intrinsic)) {
|
|
79941
|
+
const i = intrinsic;
|
|
79942
|
+
const stackName = typeof i["StackName"] === "string" ? i["StackName"] : void 0;
|
|
79943
|
+
const outputName = typeof i["OutputName"] === "string" ? i["OutputName"] : void 0;
|
|
79944
|
+
if (stackName && outputName) {
|
|
79945
|
+
emit({ stackName, outputName, location: path3 });
|
|
79946
|
+
}
|
|
79947
|
+
} else if (Array.isArray(intrinsic) && intrinsic.length === 2) {
|
|
79948
|
+
const arr = intrinsic;
|
|
79949
|
+
const stackName = arr[0];
|
|
79950
|
+
const outputName = arr[1];
|
|
79951
|
+
if (typeof stackName === "string" && typeof outputName === "string") {
|
|
79952
|
+
emit({ stackName, outputName, location: path3 });
|
|
79953
|
+
}
|
|
79954
|
+
}
|
|
79955
|
+
}
|
|
79956
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
79957
|
+
walkForGetStackOutput(value, path3 ? `${path3}.${key}` : key, emit);
|
|
79958
|
+
}
|
|
79959
|
+
}
|
|
79791
79960
|
function referencesOnly(node, allow) {
|
|
79792
79961
|
if (!node || typeof node !== "object")
|
|
79793
79962
|
return true;
|
|
@@ -79821,7 +79990,7 @@ function printPlan(plan, cfnStackName) {
|
|
|
79821
79990
|
}
|
|
79822
79991
|
logger.info("");
|
|
79823
79992
|
}
|
|
79824
|
-
async function executeImportChangeSet(cfnClient, stackName, template, plan) {
|
|
79993
|
+
async function executeImportChangeSet(cfnClient, stackName, template, plan, parameters) {
|
|
79825
79994
|
const logger = getLogger();
|
|
79826
79995
|
const changeSetName = `cdkd-migrate-${Date.now()}`;
|
|
79827
79996
|
const templateBody = JSON.stringify(template, null, 2);
|
|
@@ -79846,6 +80015,7 @@ async function executeImportChangeSet(cfnClient, stackName, template, plan) {
|
|
|
79846
80015
|
ChangeSetType: "IMPORT",
|
|
79847
80016
|
TemplateBody: templateBody,
|
|
79848
80017
|
ResourcesToImport: resourcesToImport,
|
|
80018
|
+
...parameters.length > 0 && { Parameters: parameters },
|
|
79849
80019
|
// CDK templates routinely require CAPABILITY_IAM /
|
|
79850
80020
|
// CAPABILITY_NAMED_IAM. Forward both so the user does not have to
|
|
79851
80021
|
// re-discover and re-pass them.
|
|
@@ -79892,7 +80062,7 @@ async function executeImportChangeSet(cfnClient, stackName, template, plan) {
|
|
|
79892
80062
|
throw err;
|
|
79893
80063
|
}
|
|
79894
80064
|
}
|
|
79895
|
-
async function executeUpdateChangeSet(cfnClient, stackName, template) {
|
|
80065
|
+
async function executeUpdateChangeSet(cfnClient, stackName, template, parameters) {
|
|
79896
80066
|
const logger = getLogger();
|
|
79897
80067
|
const changeSetName = `cdkd-phase2-${Date.now()}`;
|
|
79898
80068
|
const templateBody = JSON.stringify(template, null, 2);
|
|
@@ -79911,6 +80081,7 @@ async function executeUpdateChangeSet(cfnClient, stackName, template) {
|
|
|
79911
80081
|
ChangeSetName: changeSetName,
|
|
79912
80082
|
ChangeSetType: "UPDATE",
|
|
79913
80083
|
TemplateBody: templateBody,
|
|
80084
|
+
...parameters.length > 0 && { Parameters: parameters },
|
|
79914
80085
|
Capabilities: ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
|
|
79915
80086
|
})
|
|
79916
80087
|
);
|
|
@@ -80027,6 +80198,13 @@ function createExportCommand() {
|
|
|
80027
80198
|
"--include-non-importable",
|
|
80028
80199
|
"Run a 2-phase migration when the stack contains non-importable resources (Custom::*). Phase 1 imports the importable resources; phase 2 CFn-CREATEs the non-importable ones, which re-invokes each Custom Resource's onCreate handler. Make sure onCreate is idempotent before enabling.",
|
|
80029
80200
|
false
|
|
80201
|
+
).option(
|
|
80202
|
+
"--parameter <key=value...>",
|
|
80203
|
+
"CFn template Parameter override, repeatable. Required when the synthesized template has Parameters without Default values; otherwise overrides the template's default value. Format: --parameter Key=Value."
|
|
80204
|
+
).option(
|
|
80205
|
+
"--strict-cross-stack",
|
|
80206
|
+
"Refuse to export when sibling cdkd stacks in the same CDK app reference the exporting stack via Fn::GetStackOutput. Without the flag, cdkd warns but proceeds \u2014 the user is expected to migrate the consumer stacks in a follow-up.",
|
|
80207
|
+
false
|
|
80030
80208
|
).action(withErrorHandling(exportCommand));
|
|
80031
80209
|
[...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
|
|
80032
80210
|
(opt) => cmd.addOption(opt)
|
|
@@ -80065,7 +80243,7 @@ function reorderArgs(argv) {
|
|
|
80065
80243
|
}
|
|
80066
80244
|
async function main() {
|
|
80067
80245
|
const program = new Command18();
|
|
80068
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
80246
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.89.0");
|
|
80069
80247
|
program.addCommand(createBootstrapCommand());
|
|
80070
80248
|
program.addCommand(createSynthCommand());
|
|
80071
80249
|
program.addCommand(createListCommand());
|