cloudburn 0.7.0 → 0.8.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 +150 -25
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -459,7 +459,7 @@ var registerCompletionCommand = (program) => {
|
|
|
459
459
|
|
|
460
460
|
// src/commands/discover.ts
|
|
461
461
|
import { assertValidAwsRegion, CloudBurnClient } from "@cloudburn/sdk";
|
|
462
|
-
import { InvalidArgumentError as
|
|
462
|
+
import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
|
|
463
463
|
|
|
464
464
|
// src/exit-codes.ts
|
|
465
465
|
var EXIT_CODE_OK = 0;
|
|
@@ -707,12 +707,22 @@ var renderAsciiTable = (rows, columns) => {
|
|
|
707
707
|
return [border, header, border, ...body, border].join("\n");
|
|
708
708
|
};
|
|
709
709
|
|
|
710
|
+
// src/commands/config-options.ts
|
|
711
|
+
import { InvalidArgumentError as InvalidArgumentError2 } from "commander";
|
|
712
|
+
var parseRuleIdList = (value) => {
|
|
713
|
+
const ruleIds = value.split(",").map((ruleId) => ruleId.trim()).filter((ruleId) => ruleId.length > 0);
|
|
714
|
+
if (ruleIds.length === 0) {
|
|
715
|
+
throw new InvalidArgumentError2("Provide at least one rule ID.");
|
|
716
|
+
}
|
|
717
|
+
return ruleIds;
|
|
718
|
+
};
|
|
719
|
+
|
|
710
720
|
// src/commands/discover.ts
|
|
711
721
|
var parseAwsRegion = (value) => {
|
|
712
722
|
try {
|
|
713
723
|
return assertValidAwsRegion(value);
|
|
714
724
|
} catch (err) {
|
|
715
|
-
throw new
|
|
725
|
+
throw new InvalidArgumentError3(err instanceof Error ? err.message : "Invalid AWS region.");
|
|
716
726
|
}
|
|
717
727
|
};
|
|
718
728
|
var parseDiscoverRegion = (value) => {
|
|
@@ -722,6 +732,17 @@ var parseDiscoverRegion = (value) => {
|
|
|
722
732
|
return parseAwsRegion(value);
|
|
723
733
|
};
|
|
724
734
|
var resolveDiscoveryTarget = (region) => region === void 0 ? { mode: "current" } : region === "all" ? { mode: "all" } : { mode: "region", region };
|
|
735
|
+
var toDiscoveryConfigOverride = (options) => {
|
|
736
|
+
if (options.enabledRules === void 0 && options.disabledRules === void 0) {
|
|
737
|
+
return void 0;
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
discovery: {
|
|
741
|
+
disabledRules: options.disabledRules,
|
|
742
|
+
enabledRules: options.enabledRules
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
};
|
|
725
746
|
var runCommand = async (action) => {
|
|
726
747
|
try {
|
|
727
748
|
process.exitCode = await action() ?? EXIT_CODE_OK;
|
|
@@ -737,11 +758,22 @@ var registerDiscoverCommand = (program) => {
|
|
|
737
758
|
"--region <region>",
|
|
738
759
|
'Discovery region to use. Pass "all" to require an aggregator index.',
|
|
739
760
|
parseDiscoverRegion
|
|
740
|
-
).option("--exit-code", "Exit with code 1 when findings exist").action(async (options, command) => {
|
|
761
|
+
).option("--config <path>", "Explicit CloudBurn config file to load").option("--enabled-rules <ruleIds>", "Comma-separated rule IDs to enable", parseRuleIdList).option("--disabled-rules <ruleIds>", "Comma-separated rule IDs to disable", parseRuleIdList).option("--exit-code", "Exit with code 1 when findings exist").action(async (options, command) => {
|
|
741
762
|
await runCommand(async () => {
|
|
742
763
|
const scanner = new CloudBurnClient();
|
|
743
|
-
const
|
|
744
|
-
const
|
|
764
|
+
const loadedConfig = await scanner.loadConfig(options.config);
|
|
765
|
+
const discoveryOptions = {
|
|
766
|
+
target: resolveDiscoveryTarget(options.region)
|
|
767
|
+
};
|
|
768
|
+
const configOverride = toDiscoveryConfigOverride(options);
|
|
769
|
+
if (configOverride !== void 0) {
|
|
770
|
+
discoveryOptions.config = configOverride;
|
|
771
|
+
}
|
|
772
|
+
if (options.config !== void 0) {
|
|
773
|
+
discoveryOptions.configPath = options.config;
|
|
774
|
+
}
|
|
775
|
+
const result = await scanner.discover(discoveryOptions);
|
|
776
|
+
const format = resolveOutputFormat(command, void 0, loadedConfig.discovery.format ?? "table");
|
|
745
777
|
const output = renderResponse({ kind: "scan-result", result }, format);
|
|
746
778
|
process.stdout.write(`${output}
|
|
747
779
|
`);
|
|
@@ -874,26 +906,105 @@ var registerEstimateCommand = (program) => {
|
|
|
874
906
|
};
|
|
875
907
|
|
|
876
908
|
// src/commands/init.ts
|
|
877
|
-
|
|
878
|
-
|
|
909
|
+
import { access, writeFile } from "fs/promises";
|
|
910
|
+
import { dirname, join, resolve } from "path";
|
|
911
|
+
var CONFIG_FILENAMES = [".cloudburn.yml", ".cloudburn.yaml"];
|
|
912
|
+
var starterConfig = `# Static IaC scan configuration.
|
|
913
|
+
# enabled-rules restricts scans to only the listed rule IDs.
|
|
914
|
+
# disabled-rules removes specific rule IDs from the active set.
|
|
915
|
+
# format sets the default output format when --format is not passed.
|
|
916
|
+
iac:
|
|
917
|
+
enabled-rules:
|
|
918
|
+
- CLDBRN-AWS-EBS-1
|
|
919
|
+
disabled-rules:
|
|
920
|
+
- CLDBRN-AWS-EC2-2
|
|
921
|
+
format: table
|
|
879
922
|
|
|
880
|
-
#
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
923
|
+
# Live AWS discovery configuration.
|
|
924
|
+
# Use the same rule controls here to tune discover runs separately from IaC scans.
|
|
925
|
+
discovery:
|
|
926
|
+
enabled-rules:
|
|
927
|
+
- CLDBRN-AWS-EBS-1
|
|
928
|
+
disabled-rules:
|
|
929
|
+
- CLDBRN-AWS-S3-1
|
|
930
|
+
format: json
|
|
884
931
|
`;
|
|
932
|
+
var renderStarterConfig = (command) => renderResponse(
|
|
933
|
+
{
|
|
934
|
+
kind: "document",
|
|
935
|
+
content: starterConfig,
|
|
936
|
+
contentType: "application/yaml"
|
|
937
|
+
},
|
|
938
|
+
resolveOutputFormat(command, void 0, "text")
|
|
939
|
+
);
|
|
940
|
+
var fileExists = async (path) => {
|
|
941
|
+
try {
|
|
942
|
+
await access(path);
|
|
943
|
+
return true;
|
|
944
|
+
} catch {
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
var findProjectRoot = async (startDirectory) => {
|
|
949
|
+
let currentDirectory = resolve(startDirectory);
|
|
950
|
+
while (true) {
|
|
951
|
+
if (await fileExists(join(currentDirectory, ".git"))) {
|
|
952
|
+
return currentDirectory;
|
|
953
|
+
}
|
|
954
|
+
const parentDirectory = dirname(currentDirectory);
|
|
955
|
+
if (parentDirectory === currentDirectory) {
|
|
956
|
+
return resolve(startDirectory);
|
|
957
|
+
}
|
|
958
|
+
currentDirectory = parentDirectory;
|
|
959
|
+
}
|
|
960
|
+
};
|
|
885
961
|
var registerInitCommand = (program) => {
|
|
886
|
-
program.command("init").description("
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
962
|
+
const initCommand = program.command("init").description("Initialize CloudBurn scaffolding").usage("[command]").action(function() {
|
|
963
|
+
process.stdout.write(`${renderStarterConfig(this)}
|
|
964
|
+
`);
|
|
965
|
+
process.exitCode = EXIT_CODE_OK;
|
|
966
|
+
});
|
|
967
|
+
initCommand.command("config").description("Create a starter .cloudburn.yml configuration").option("--print", "Print the starter config instead of writing the file").action(async function(options) {
|
|
968
|
+
try {
|
|
969
|
+
if (options.print) {
|
|
970
|
+
process.stdout.write(`${renderStarterConfig(this)}
|
|
971
|
+
`);
|
|
972
|
+
process.exitCode = EXIT_CODE_OK;
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
const rootDirectory = await findProjectRoot(process.cwd());
|
|
976
|
+
const existingConfigPath = (await Promise.all(
|
|
977
|
+
CONFIG_FILENAMES.map(async (filename) => {
|
|
978
|
+
const path = join(rootDirectory, filename);
|
|
979
|
+
return await fileExists(path) ? path : void 0;
|
|
980
|
+
})
|
|
981
|
+
)).find((path) => path !== void 0);
|
|
982
|
+
if (existingConfigPath) {
|
|
983
|
+
throw new Error(
|
|
984
|
+
`CloudBurn config already exists at ${existingConfigPath}. Use --print to inspect the template.`
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
const configPath = join(rootDirectory, ".cloudburn.yml");
|
|
988
|
+
await writeFile(configPath, starterConfig, { encoding: "utf8", flag: "wx" });
|
|
989
|
+
const output = renderResponse(
|
|
990
|
+
{
|
|
991
|
+
kind: "status",
|
|
992
|
+
data: {
|
|
993
|
+
message: "Created CloudBurn config.",
|
|
994
|
+
path: configPath
|
|
995
|
+
},
|
|
996
|
+
text: `Created ${configPath}.`
|
|
997
|
+
},
|
|
998
|
+
resolveOutputFormat(this)
|
|
999
|
+
);
|
|
1000
|
+
process.stdout.write(`${output}
|
|
1001
|
+
`);
|
|
1002
|
+
process.exitCode = EXIT_CODE_OK;
|
|
1003
|
+
} catch (err) {
|
|
1004
|
+
process.stderr.write(`${formatError(err)}
|
|
896
1005
|
`);
|
|
1006
|
+
process.exitCode = EXIT_CODE_RUNTIME_ERROR;
|
|
1007
|
+
}
|
|
897
1008
|
});
|
|
898
1009
|
};
|
|
899
1010
|
|
|
@@ -917,13 +1028,27 @@ var registerRulesListCommand = (program) => {
|
|
|
917
1028
|
|
|
918
1029
|
// src/commands/scan.ts
|
|
919
1030
|
import { CloudBurnClient as CloudBurnClient2 } from "@cloudburn/sdk";
|
|
1031
|
+
var toScanConfigOverride = (options) => {
|
|
1032
|
+
if (options.enabledRules === void 0 && options.disabledRules === void 0) {
|
|
1033
|
+
return void 0;
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
iac: {
|
|
1037
|
+
disabledRules: options.disabledRules,
|
|
1038
|
+
enabledRules: options.enabledRules
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
};
|
|
920
1042
|
var registerScanCommand = (program) => {
|
|
921
1043
|
setCommandExamples(
|
|
922
|
-
program.command("scan").description("Run an autodetected static IaC scan").argument("[path]", "Terraform file, CloudFormation template, or directory to scan").option("--exit-code", "Exit with code 1 when findings exist").action(async (path, options, command) => {
|
|
1044
|
+
program.command("scan").description("Run an autodetected static IaC scan").argument("[path]", "Terraform file, CloudFormation template, or directory to scan").option("--config <path>", "Explicit CloudBurn config file to load").option("--enabled-rules <ruleIds>", "Comma-separated rule IDs to enable", parseRuleIdList).option("--disabled-rules <ruleIds>", "Comma-separated rule IDs to disable", parseRuleIdList).option("--exit-code", "Exit with code 1 when findings exist").action(async (path, options, command) => {
|
|
923
1045
|
try {
|
|
924
1046
|
const scanner = new CloudBurnClient2();
|
|
925
|
-
const
|
|
926
|
-
const
|
|
1047
|
+
const loadedConfig = await scanner.loadConfig(options.config);
|
|
1048
|
+
const configOverride = toScanConfigOverride(options);
|
|
1049
|
+
const scanPath = path ?? process.cwd();
|
|
1050
|
+
const result = configOverride === void 0 && options.config === void 0 ? await scanner.scanStatic(scanPath) : options.config === void 0 ? await scanner.scanStatic(scanPath, configOverride) : await scanner.scanStatic(scanPath, configOverride, { configPath: options.config });
|
|
1051
|
+
const format = resolveOutputFormat(command, void 0, loadedConfig.iac.format ?? "table");
|
|
927
1052
|
const output = renderResponse({ kind: "scan-result", result }, format);
|
|
928
1053
|
process.stdout.write(`${output}
|
|
929
1054
|
`);
|
|
@@ -945,7 +1070,7 @@ var registerScanCommand = (program) => {
|
|
|
945
1070
|
// src/cli.ts
|
|
946
1071
|
var createProgram = () => {
|
|
947
1072
|
const program = createCliCommand();
|
|
948
|
-
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.
|
|
1073
|
+
program.name("cloudburn").usage("[command]").description("Know what you spend. Fix what you waste.").version("0.8.0").option("--format <format>", OUTPUT_FORMAT_OPTION_DESCRIPTION, parseOutputFormat);
|
|
949
1074
|
configureCliHelp(program);
|
|
950
1075
|
registerCompletionCommand(program);
|
|
951
1076
|
registerDiscoverCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudburn",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Cloudburn CLI for cloud cost optimization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"commander": "^13.1.0",
|
|
14
|
-
"@cloudburn/sdk": "0.
|
|
14
|
+
"@cloudburn/sdk": "0.12.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@biomejs/biome": "^2.4.6",
|