@go-to-k/cdkd 0.86.0 → 0.88.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 +234 -28
- package/dist/cli.js.map +3 -3
- package/dist/go-to-k-cdkd-0.88.0.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.86.0.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -79270,7 +79270,8 @@ import {
|
|
|
79270
79270
|
DescribeTypeCommand,
|
|
79271
79271
|
DeleteChangeSetCommand,
|
|
79272
79272
|
waitUntilChangeSetCreateComplete,
|
|
79273
|
-
waitUntilStackImportComplete
|
|
79273
|
+
waitUntilStackImportComplete,
|
|
79274
|
+
waitUntilStackUpdateComplete as waitUntilStackUpdateComplete2
|
|
79274
79275
|
} from "@aws-sdk/client-cloudformation";
|
|
79275
79276
|
init_aws_clients();
|
|
79276
79277
|
var NEVER_IMPORTABLE_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -79457,39 +79458,106 @@ async function exportCommand(stackArg, options) {
|
|
|
79457
79458
|
}
|
|
79458
79459
|
}
|
|
79459
79460
|
try {
|
|
79460
|
-
const {
|
|
79461
|
-
|
|
79461
|
+
const { phase1Imports, phase2Creates, blocked } = await buildImportPlan(
|
|
79462
|
+
state,
|
|
79463
|
+
template,
|
|
79464
|
+
awsClients.cloudFormation
|
|
79465
|
+
);
|
|
79466
|
+
if (blocked.length > 0) {
|
|
79467
|
+
logger.error("The following resources block migration:");
|
|
79468
|
+
for (const b of blocked) {
|
|
79469
|
+
logger.error(` - ${b.logicalId} (${b.resourceType}): ${b.reason}`);
|
|
79470
|
+
}
|
|
79471
|
+
throw new Error(
|
|
79472
|
+
`${blocked.length} resource(s) block migration. Either destroy them first (cdkd destroy / cdkd state destroy cherry-picked), or remove them from the CDK app and re-synthesize.`
|
|
79473
|
+
);
|
|
79474
|
+
}
|
|
79475
|
+
if (phase2Creates.length > 0 && !options.includeNonImportable) {
|
|
79462
79476
|
logger.error("The following resources cannot be imported into CloudFormation:");
|
|
79463
|
-
for (const
|
|
79464
|
-
logger.error(` - ${
|
|
79477
|
+
for (const p of phase2Creates) {
|
|
79478
|
+
logger.error(` - ${p.logicalId} (${p.resourceType}): CFn cannot import this type`);
|
|
79465
79479
|
}
|
|
79466
79480
|
throw new Error(
|
|
79467
|
-
`${
|
|
79481
|
+
`${phase2Creates.length} non-importable resource(s) detected (Custom::*). Pass --include-non-importable to run a 2-phase migration: phase 1 imports the importable resources; phase 2 CFn-CREATEs the non-importable ones (re-invoking each Custom Resource's backing Lambda onCreate handler \u2014 make sure those are idempotent). Or destroy these resources first.`
|
|
79468
79482
|
);
|
|
79469
79483
|
}
|
|
79470
|
-
if (
|
|
79471
|
-
logger.warn("No resources to
|
|
79484
|
+
if (phase1Imports.length === 0 && phase2Creates.length === 0) {
|
|
79485
|
+
logger.warn("No resources to migrate \u2014 cdkd state is empty.");
|
|
79472
79486
|
return;
|
|
79473
79487
|
}
|
|
79474
|
-
|
|
79488
|
+
if (phase1Imports.length === 0) {
|
|
79489
|
+
throw new Error(
|
|
79490
|
+
"No importable resources in the template. CloudFormation IMPORT changeset requires at least one importable resource for phase 1."
|
|
79491
|
+
);
|
|
79492
|
+
}
|
|
79493
|
+
printPlan(phase1Imports, cfnStackName);
|
|
79494
|
+
if (phase2Creates.length > 0) {
|
|
79495
|
+
logger.info(`Phase 2 will CREATE ${phase2Creates.length} non-importable resource(s):`);
|
|
79496
|
+
for (const p of phase2Creates) {
|
|
79497
|
+
logger.info(` ${p.logicalId} (${p.resourceType})`);
|
|
79498
|
+
}
|
|
79499
|
+
logger.info("");
|
|
79500
|
+
}
|
|
79475
79501
|
if (options.dryRun) {
|
|
79476
79502
|
logger.info("--dry-run: no CloudFormation changeset will be created.");
|
|
79477
79503
|
return;
|
|
79478
79504
|
}
|
|
79479
79505
|
if (!options.yes) {
|
|
79506
|
+
const phase2Note = phase2Creates.length > 0 ? ` Phase 2 will then CREATE ${phase2Creates.length} non-importable resource(s) (invoking each Custom Resource's onCreate handler).` : "";
|
|
79480
79507
|
const ok = await confirmPrompt6(
|
|
79481
|
-
`Create CloudFormation stack '${cfnStackName}' by importing ${
|
|
79508
|
+
`Create CloudFormation stack '${cfnStackName}' by importing ${phase1Imports.length} resource(s) from cdkd state '${resolvedStackName}' (${targetRegion})?` + phase2Note + ` AWS resources are unchanged on import. cdkd state for '${resolvedStackName}' will be deleted on success.`
|
|
79482
79509
|
);
|
|
79483
79510
|
if (!ok) {
|
|
79484
79511
|
logger.info("Migration cancelled. cdkd state and CloudFormation are unchanged.");
|
|
79485
79512
|
return;
|
|
79486
79513
|
}
|
|
79487
79514
|
}
|
|
79488
|
-
const
|
|
79489
|
-
|
|
79515
|
+
const userParameters = parseParameterOverrides(options.parameter);
|
|
79516
|
+
const { parameters: cfnParameters, missing } = resolveTemplateParameters(
|
|
79517
|
+
template,
|
|
79518
|
+
userParameters
|
|
79519
|
+
);
|
|
79520
|
+
if (missing.length > 0) {
|
|
79521
|
+
throw new Error(
|
|
79522
|
+
`Template requires parameter(s) without defaults: ${missing.join(", ")}. Pass each one as --parameter Key=Value (or set a Default in the CDK code).`
|
|
79523
|
+
);
|
|
79524
|
+
}
|
|
79525
|
+
const phase1Template = filterTemplateForImport(template, phase1Imports);
|
|
79526
|
+
await executeImportChangeSet(
|
|
79527
|
+
awsClients.cloudFormation,
|
|
79528
|
+
cfnStackName,
|
|
79529
|
+
phase1Template,
|
|
79530
|
+
phase1Imports,
|
|
79531
|
+
cfnParameters
|
|
79532
|
+
);
|
|
79490
79533
|
logger.info(
|
|
79491
|
-
`\u2713 CloudFormation stack '${cfnStackName}' created via IMPORT. ${
|
|
79534
|
+
`\u2713 Phase 1: CloudFormation stack '${cfnStackName}' created via IMPORT. ${phase1Imports.length} resource(s) imported.`
|
|
79492
79535
|
);
|
|
79536
|
+
if (phase2Creates.length > 0) {
|
|
79537
|
+
try {
|
|
79538
|
+
await executeUpdateChangeSet(
|
|
79539
|
+
awsClients.cloudFormation,
|
|
79540
|
+
cfnStackName,
|
|
79541
|
+
template,
|
|
79542
|
+
cfnParameters
|
|
79543
|
+
);
|
|
79544
|
+
logger.info(`\u2713 Phase 2: ${phase2Creates.length} non-importable resource(s) CREATEd.`);
|
|
79545
|
+
} catch (err) {
|
|
79546
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
79547
|
+
throw new Error(
|
|
79548
|
+
`Phase 1 (IMPORT) succeeded; phase 2 (UPDATE) failed: ${msg}
|
|
79549
|
+
|
|
79550
|
+
The CloudFormation stack '${cfnStackName}' now contains the imported resources but is missing the ${phase2Creates.length} non-importable resource(s). cdkd state is UNCHANGED so you can inspect what's in it, but DO NOT run \`cdkd deploy\` against this stack (the imported resources are now CFn-managed). To recover:
|
|
79551
|
+
1. Fix the failure cause (typically an onCreate Lambda error).
|
|
79552
|
+
2. Re-run the phase 2 UPDATE manually with the full synth template:
|
|
79553
|
+
aws cloudformation create-change-set --stack-name ${cfnStackName} \\
|
|
79554
|
+
--change-set-name cdkd-phase2-retry --change-set-type UPDATE \\
|
|
79555
|
+
--template-body file://<full-template.json>
|
|
79556
|
+
3. Once phase 2 succeeds, run: cdkd state orphan ${resolvedStackName}
|
|
79557
|
+
to clean up cdkd's stale state record.`
|
|
79558
|
+
);
|
|
79559
|
+
}
|
|
79560
|
+
}
|
|
79493
79561
|
await stateBackend.deleteState(resolvedStackName, targetRegion);
|
|
79494
79562
|
logger.info(
|
|
79495
79563
|
`cdkd state for '${resolvedStackName}' (${targetRegion}) removed. Manage the stack with 'cdk deploy' or 'aws cloudformation' from here on.`
|
|
@@ -79585,13 +79653,17 @@ async function assertCfnStackAbsent(cfnClient, stackName) {
|
|
|
79585
79653
|
throw err;
|
|
79586
79654
|
}
|
|
79587
79655
|
}
|
|
79656
|
+
function isPhase2CreatableType(resourceType) {
|
|
79657
|
+
return resourceType.startsWith("Custom::");
|
|
79658
|
+
}
|
|
79588
79659
|
async function buildImportPlan(state, template, cfnClient) {
|
|
79589
79660
|
const templateResources = template["Resources"];
|
|
79590
79661
|
if (!templateResources || typeof templateResources !== "object" || Array.isArray(templateResources)) {
|
|
79591
79662
|
throw new Error("Template has no Resources section.");
|
|
79592
79663
|
}
|
|
79593
|
-
const
|
|
79594
|
-
const
|
|
79664
|
+
const phase1Imports = [];
|
|
79665
|
+
const phase2Creates = [];
|
|
79666
|
+
const blocked = [];
|
|
79595
79667
|
const identifierCache = /* @__PURE__ */ new Map();
|
|
79596
79668
|
for (const [logicalId, raw] of Object.entries(templateResources)) {
|
|
79597
79669
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
@@ -79600,19 +79672,24 @@ async function buildImportPlan(state, template, cfnClient) {
|
|
|
79600
79672
|
const resourceType = resource.Type ?? "";
|
|
79601
79673
|
if (!resourceType)
|
|
79602
79674
|
continue;
|
|
79675
|
+
if (resourceType === "AWS::CDK::Metadata") {
|
|
79676
|
+
continue;
|
|
79677
|
+
}
|
|
79603
79678
|
if (isNeverImportableType(resourceType)) {
|
|
79604
|
-
if (resourceType
|
|
79605
|
-
|
|
79606
|
-
|
|
79607
|
-
|
|
79608
|
-
|
|
79609
|
-
|
|
79610
|
-
|
|
79679
|
+
if (isPhase2CreatableType(resourceType)) {
|
|
79680
|
+
phase2Creates.push({ logicalId, resourceType });
|
|
79681
|
+
} else {
|
|
79682
|
+
blocked.push({
|
|
79683
|
+
logicalId,
|
|
79684
|
+
resourceType,
|
|
79685
|
+
reason: "CloudFormation cannot import or recreate this resource type"
|
|
79686
|
+
});
|
|
79687
|
+
}
|
|
79611
79688
|
continue;
|
|
79612
79689
|
}
|
|
79613
79690
|
const stateEntry = state.resources[logicalId];
|
|
79614
79691
|
if (!stateEntry || !stateEntry.physicalId) {
|
|
79615
|
-
|
|
79692
|
+
blocked.push({
|
|
79616
79693
|
logicalId,
|
|
79617
79694
|
resourceType,
|
|
79618
79695
|
reason: "no entry in cdkd state (resource is in template but was not deployed by cdkd)"
|
|
@@ -79628,21 +79705,21 @@ async function buildImportPlan(state, template, cfnClient) {
|
|
|
79628
79705
|
identifierCache
|
|
79629
79706
|
);
|
|
79630
79707
|
} catch (err) {
|
|
79631
|
-
|
|
79708
|
+
blocked.push({
|
|
79632
79709
|
logicalId,
|
|
79633
79710
|
resourceType,
|
|
79634
79711
|
reason: "could not resolve resource identifier: " + (err instanceof Error ? err.message : String(err))
|
|
79635
79712
|
});
|
|
79636
79713
|
continue;
|
|
79637
79714
|
}
|
|
79638
|
-
|
|
79715
|
+
phase1Imports.push({
|
|
79639
79716
|
logicalId,
|
|
79640
79717
|
resourceType,
|
|
79641
79718
|
physicalId: stateEntry.physicalId,
|
|
79642
79719
|
resourceIdentifier
|
|
79643
79720
|
});
|
|
79644
79721
|
}
|
|
79645
|
-
return {
|
|
79722
|
+
return { phase1Imports, phase2Creates, blocked };
|
|
79646
79723
|
}
|
|
79647
79724
|
async function resolveResourceIdentifier(resourceType, physicalId, cfnClient, cache2) {
|
|
79648
79725
|
let entry = cache2.get(resourceType);
|
|
@@ -79701,6 +79778,64 @@ async function fetchPrimaryIdentifier(resourceType, cfnClient) {
|
|
|
79701
79778
|
`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.`
|
|
79702
79779
|
);
|
|
79703
79780
|
}
|
|
79781
|
+
function parseParameterOverrides(tokens) {
|
|
79782
|
+
const map = {};
|
|
79783
|
+
if (!tokens)
|
|
79784
|
+
return map;
|
|
79785
|
+
for (const t of tokens) {
|
|
79786
|
+
const eq = t.indexOf("=");
|
|
79787
|
+
if (eq < 1) {
|
|
79788
|
+
throw new Error(
|
|
79789
|
+
`Invalid --parameter '${t}': expected 'Key=Value' (e.g. --parameter Env=prod)`
|
|
79790
|
+
);
|
|
79791
|
+
}
|
|
79792
|
+
const key = t.slice(0, eq).trim();
|
|
79793
|
+
const value = t.slice(eq + 1);
|
|
79794
|
+
if (!key) {
|
|
79795
|
+
throw new Error(`Invalid --parameter '${t}': key is empty`);
|
|
79796
|
+
}
|
|
79797
|
+
map[key] = value;
|
|
79798
|
+
}
|
|
79799
|
+
return map;
|
|
79800
|
+
}
|
|
79801
|
+
function resolveTemplateParameters(template, userOverrides) {
|
|
79802
|
+
const tplParams = template["Parameters"];
|
|
79803
|
+
if (!tplParams || typeof tplParams !== "object" || Array.isArray(tplParams)) {
|
|
79804
|
+
const stray = Object.keys(userOverrides);
|
|
79805
|
+
if (stray.length > 0) {
|
|
79806
|
+
throw new Error(
|
|
79807
|
+
`--parameter override(s) supplied (${stray.join(", ")}) but template has no Parameters section.`
|
|
79808
|
+
);
|
|
79809
|
+
}
|
|
79810
|
+
return { parameters: [], missing: [] };
|
|
79811
|
+
}
|
|
79812
|
+
const parameters = [];
|
|
79813
|
+
const missing = [];
|
|
79814
|
+
const known = /* @__PURE__ */ new Set();
|
|
79815
|
+
for (const [name, raw] of Object.entries(tplParams)) {
|
|
79816
|
+
known.add(name);
|
|
79817
|
+
const def = raw ?? {};
|
|
79818
|
+
const override = userOverrides[name];
|
|
79819
|
+
if (override !== void 0) {
|
|
79820
|
+
parameters.push({ ParameterKey: name, ParameterValue: override });
|
|
79821
|
+
continue;
|
|
79822
|
+
}
|
|
79823
|
+
if ("Default" in def) {
|
|
79824
|
+
const value = typeof def.Default === "string" ? def.Default : String(def.Default);
|
|
79825
|
+
parameters.push({ ParameterKey: name, ParameterValue: value });
|
|
79826
|
+
continue;
|
|
79827
|
+
}
|
|
79828
|
+
missing.push(name);
|
|
79829
|
+
}
|
|
79830
|
+
for (const name of Object.keys(userOverrides)) {
|
|
79831
|
+
if (!known.has(name)) {
|
|
79832
|
+
throw new Error(
|
|
79833
|
+
`--parameter override '${name}' does not match any parameter in the synthesized template (template declares: ${[...known].join(", ") || "(none)"})`
|
|
79834
|
+
);
|
|
79835
|
+
}
|
|
79836
|
+
}
|
|
79837
|
+
return { parameters, missing };
|
|
79838
|
+
}
|
|
79704
79839
|
function filterTemplateForImport(template, plan) {
|
|
79705
79840
|
const allow = new Set(plan.map((p) => p.logicalId));
|
|
79706
79841
|
const original = template["Resources"];
|
|
@@ -79760,7 +79895,7 @@ function printPlan(plan, cfnStackName) {
|
|
|
79760
79895
|
}
|
|
79761
79896
|
logger.info("");
|
|
79762
79897
|
}
|
|
79763
|
-
async function executeImportChangeSet(cfnClient, stackName, template, plan) {
|
|
79898
|
+
async function executeImportChangeSet(cfnClient, stackName, template, plan, parameters) {
|
|
79764
79899
|
const logger = getLogger();
|
|
79765
79900
|
const changeSetName = `cdkd-migrate-${Date.now()}`;
|
|
79766
79901
|
const templateBody = JSON.stringify(template, null, 2);
|
|
@@ -79785,6 +79920,7 @@ async function executeImportChangeSet(cfnClient, stackName, template, plan) {
|
|
|
79785
79920
|
ChangeSetType: "IMPORT",
|
|
79786
79921
|
TemplateBody: templateBody,
|
|
79787
79922
|
ResourcesToImport: resourcesToImport,
|
|
79923
|
+
...parameters.length > 0 && { Parameters: parameters },
|
|
79788
79924
|
// CDK templates routinely require CAPABILITY_IAM /
|
|
79789
79925
|
// CAPABILITY_NAMED_IAM. Forward both so the user does not have to
|
|
79790
79926
|
// re-discover and re-pass them.
|
|
@@ -79831,6 +79967,69 @@ async function executeImportChangeSet(cfnClient, stackName, template, plan) {
|
|
|
79831
79967
|
throw err;
|
|
79832
79968
|
}
|
|
79833
79969
|
}
|
|
79970
|
+
async function executeUpdateChangeSet(cfnClient, stackName, template, parameters) {
|
|
79971
|
+
const logger = getLogger();
|
|
79972
|
+
const changeSetName = `cdkd-phase2-${Date.now()}`;
|
|
79973
|
+
const templateBody = JSON.stringify(template, null, 2);
|
|
79974
|
+
if (templateBody.length > 51200) {
|
|
79975
|
+
throw new Error(
|
|
79976
|
+
`Full template is ${templateBody.length} bytes, over the 51,200-byte inline TemplateBody limit for phase-2 UPDATE. TemplateURL upload is not yet implemented.`
|
|
79977
|
+
);
|
|
79978
|
+
}
|
|
79979
|
+
logger.info(
|
|
79980
|
+
`Creating UPDATE changeset '${changeSetName}' for phase 2 (${templateBody.length} bytes)...`
|
|
79981
|
+
);
|
|
79982
|
+
try {
|
|
79983
|
+
await cfnClient.send(
|
|
79984
|
+
new CreateChangeSetCommand({
|
|
79985
|
+
StackName: stackName,
|
|
79986
|
+
ChangeSetName: changeSetName,
|
|
79987
|
+
ChangeSetType: "UPDATE",
|
|
79988
|
+
TemplateBody: templateBody,
|
|
79989
|
+
...parameters.length > 0 && { Parameters: parameters },
|
|
79990
|
+
Capabilities: ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
|
|
79991
|
+
})
|
|
79992
|
+
);
|
|
79993
|
+
} catch (err) {
|
|
79994
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
79995
|
+
throw new Error(`Failed to create UPDATE changeset: ${msg}`);
|
|
79996
|
+
}
|
|
79997
|
+
try {
|
|
79998
|
+
await waitUntilChangeSetCreateComplete(
|
|
79999
|
+
{ client: cfnClient, maxWaitTime: 600 },
|
|
80000
|
+
{ StackName: stackName, ChangeSetName: changeSetName }
|
|
80001
|
+
);
|
|
80002
|
+
} catch (err) {
|
|
80003
|
+
try {
|
|
80004
|
+
const desc = await cfnClient.send(
|
|
80005
|
+
new DescribeChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })
|
|
80006
|
+
);
|
|
80007
|
+
const reason = desc.StatusReason ?? "unknown";
|
|
80008
|
+
await cfnClient.send(new DeleteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })).catch(() => {
|
|
80009
|
+
});
|
|
80010
|
+
throw new Error(`UPDATE changeset FAILED: ${reason}`);
|
|
80011
|
+
} catch (innerErr) {
|
|
80012
|
+
if (innerErr instanceof Error && innerErr.message.startsWith("UPDATE changeset FAILED")) {
|
|
80013
|
+
throw innerErr;
|
|
80014
|
+
}
|
|
80015
|
+
throw err;
|
|
80016
|
+
}
|
|
80017
|
+
}
|
|
80018
|
+
logger.info(`Executing UPDATE changeset...`);
|
|
80019
|
+
try {
|
|
80020
|
+
await cfnClient.send(
|
|
80021
|
+
new ExecuteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })
|
|
80022
|
+
);
|
|
80023
|
+
await waitUntilStackUpdateComplete2(
|
|
80024
|
+
{ client: cfnClient, maxWaitTime: 3600 },
|
|
80025
|
+
{ StackName: stackName }
|
|
80026
|
+
);
|
|
80027
|
+
} catch (err) {
|
|
80028
|
+
await cfnClient.send(new DeleteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })).catch(() => {
|
|
80029
|
+
});
|
|
80030
|
+
throw err;
|
|
80031
|
+
}
|
|
80032
|
+
}
|
|
79834
80033
|
function refuseTransientContextIfUnsafe(options) {
|
|
79835
80034
|
const overrides = options.context ?? [];
|
|
79836
80035
|
if (overrides.length === 0)
|
|
@@ -79900,6 +80099,13 @@ function createExportCommand() {
|
|
|
79900
80099
|
"--accept-transient-context",
|
|
79901
80100
|
"Allow CLI -c key=value overrides at export time even though they are not persisted to cdk.json / cdk.context.json (default: refuse). When set, the user is responsible for passing the same -c flags to every future cdk deploy.",
|
|
79902
80101
|
false
|
|
80102
|
+
).option(
|
|
80103
|
+
"--include-non-importable",
|
|
80104
|
+
"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.",
|
|
80105
|
+
false
|
|
80106
|
+
).option(
|
|
80107
|
+
"--parameter <key=value...>",
|
|
80108
|
+
"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."
|
|
79903
80109
|
).action(withErrorHandling(exportCommand));
|
|
79904
80110
|
[...commonOptions, ...appOptions, ...stateOptions, ...contextOptions].forEach(
|
|
79905
80111
|
(opt) => cmd.addOption(opt)
|
|
@@ -79938,7 +80144,7 @@ function reorderArgs(argv) {
|
|
|
79938
80144
|
}
|
|
79939
80145
|
async function main() {
|
|
79940
80146
|
const program = new Command18();
|
|
79941
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
80147
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.88.0");
|
|
79942
80148
|
program.addCommand(createBootstrapCommand());
|
|
79943
80149
|
program.addCommand(createSynthCommand());
|
|
79944
80150
|
program.addCommand(createListCommand());
|