@pagopa/dx-cli 0.8.2 → 0.10.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.
Files changed (3) hide show
  1. package/README.md +58 -0
  2. package/bin/index.js +35 -17
  3. package/package.json +6 -5
package/README.md CHANGED
@@ -71,6 +71,64 @@ Checking monorepo scripts...
71
71
  ✅ Monorepo scripts are correctly set up
72
72
  ```
73
73
 
74
+ #### `savemoney`
75
+
76
+ Analyze Azure subscriptions to identify unused or underutilized resources that could be costing you money.
77
+
78
+ ```bash
79
+ dx savemoney [options]
80
+ ```
81
+
82
+ **Options:**
83
+
84
+ | Option | Alias | Description | Default |
85
+ | :----------- | :---- | :-------------------------------------------------------------------- | :----------- |
86
+ | `--config` | `-c` | Path to a JSON configuration file. | N/A |
87
+ | `--format` | `-f` | Report format (`table`, `json`, `detailed-json`). | `table` |
88
+ | `--days` | `-d` | Metric analysis period in days. | `30` |
89
+ | `--location` | `-l` | Preferred Azure location for resources. | `italynorth` |
90
+ | `--verbose` | `-v` | Enable verbose mode with detailed logging for each resource analyzed. | `false` |
91
+
92
+ **Example usage:**
93
+
94
+ ```bash
95
+ # Analyze with default settings (interactive prompts)
96
+ dx savemoney
97
+
98
+ # Use a configuration file
99
+ dx savemoney --config config.json
100
+
101
+ # Output as JSON with verbose logging
102
+ dx savemoney --format json --verbose
103
+
104
+ # Analyze with specific timespan
105
+ dx savemoney --days 60 --location italynorth
106
+ ```
107
+
108
+ **Configuration file example (`config.json`):**
109
+
110
+ ```json
111
+ {
112
+ "tenantId": "your-tenant-id",
113
+ "subscriptionIds": ["subscription-1", "subscription-2"],
114
+ "preferredLocation": "italynorth",
115
+ "timespanDays": 30
116
+ }
117
+ ```
118
+
119
+ **Analyzed Azure resources:**
120
+
121
+ - **Virtual Machines**: Deallocated or stopped VMs, low CPU usage
122
+ - **Managed Disks**: Unattached disks
123
+ - **Network Interfaces**: Unattached NICs
124
+ - **Public IP Addresses**: Unassociated static IPs
125
+ - **Storage Accounts**: Low transaction counts
126
+ - **App Service Plans**: Empty plans or oversized tiers
127
+ - **Private Endpoints**: Unused or misconfigured endpoints
128
+
129
+ > [!NOTE]
130
+ > Currently only Azure is supported. Support for additional cloud providers (AWS) is planned for future releases.
131
+
74
132
  ### Global Options
75
133
 
76
134
  - `--version, -V`: Display version number
package/bin/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import "core-js/actual/set/index.js";
5
- import { configure, getConsoleSink, getLogger as getLogger9 } from "@logtape/logtape";
5
+ import { configure, getConsoleSink, getLogger as getLogger8 } from "@logtape/logtape";
6
6
 
7
7
  // src/adapters/codemods/registry.ts
8
8
  import { okAsync } from "neverthrow";
@@ -756,37 +756,54 @@ var makeInitCommand = () => new Command4().name("init").description(
756
756
  })
757
757
  );
758
758
 
759
- // src/adapters/commander/commands/version.ts
759
+ // src/adapters/commander/commands/savemoney.ts
760
+ import { azure, loadConfig } from "@pagopa/dx-savemoney";
760
761
  import { Command as Command5 } from "commander";
761
-
762
- // src/domain/version.ts
763
- import { getLogger as getLogger7 } from "@logtape/logtape";
764
- function printVersion() {
765
- const logger2 = getLogger7(["dx-cli", "version"]);
766
- logger2.info(`dx CLI version: ${"0.8.2"}`);
767
- }
768
-
769
- // src/adapters/commander/commands/version.ts
770
- var makeVersionCommand = () => new Command5().name("version").alias("v").action(() => printVersion());
762
+ var makeSavemoneyCommand = () => new Command5("savemoney").description(
763
+ "Analyze Azure subscriptions and report unused or inefficient resources"
764
+ ).option("-c, --config <path>", "Path to configuration file (JSON)").option(
765
+ "-f, --format <format>",
766
+ "Report format: json, table, or detailed-json (default: table)",
767
+ "table"
768
+ ).option(
769
+ "-l, --location <string>",
770
+ "Preferred Azure location for resources",
771
+ "italynorth"
772
+ ).option("-d, --days <number>", "Number of days for metrics analysis", "30").option("-v, --verbose", "Enable verbose logging").action(async function(options) {
773
+ try {
774
+ const config2 = await loadConfig(options.config);
775
+ const finalConfig = {
776
+ ...config2,
777
+ preferredLocation: options.location || config2.preferredLocation,
778
+ timespanDays: Number.parseInt(options.days, 10) || config2.timespanDays,
779
+ verbose: options.verbose || false
780
+ };
781
+ await azure.analyzeAzureResources(finalConfig, options.format);
782
+ } catch (error) {
783
+ this.error(
784
+ `Analysis failed: ${error instanceof Error ? error.message : error}`
785
+ );
786
+ }
787
+ });
771
788
 
772
789
  // src/adapters/commander/index.ts
773
790
  var makeCli = (deps2, config2, cliDeps) => {
774
791
  const program2 = new Command6();
775
- program2.name("dx").description("The CLI for DX-Platform").version("0.8.2");
792
+ program2.name("dx").description("The CLI for DX-Platform").version("0.10.0");
776
793
  program2.addCommand(makeDoctorCommand(deps2, config2));
777
794
  program2.addCommand(makeCodemodCommand(cliDeps));
795
+ program2.addCommand(makeSavemoneyCommand());
778
796
  if (process.env.ENABLE_INIT_COMMAND) {
779
797
  program2.addCommand(makeInitCommand());
780
798
  }
781
- program2.addCommand(makeVersionCommand());
782
799
  program2.addCommand(makeInfoCommand(deps2, config2));
783
800
  return program2;
784
801
  };
785
802
 
786
803
  // src/adapters/logtape/validation-reporter.ts
787
- import { getLogger as getLogger8 } from "@logtape/logtape";
804
+ import { getLogger as getLogger7 } from "@logtape/logtape";
788
805
  var makeValidationReporter = () => {
789
- const logger2 = getLogger8(["dx-cli", "validation"]);
806
+ const logger2 = getLogger7(["dx-cli", "validation"]);
790
807
  return {
791
808
  reportCheckResult(result) {
792
809
  if (result.isValid) {
@@ -962,6 +979,7 @@ var listCodemods = (registry2) => () => registry2.getAll();
962
979
  await configure({
963
980
  loggers: [
964
981
  { category: ["dx-cli"], lowestLevel: "info", sinks: ["console"] },
982
+ { category: ["savemoney"], lowestLevel: "debug", sinks: ["console"] },
965
983
  { category: ["json"], lowestLevel: "info", sinks: ["rawJson"] },
966
984
  {
967
985
  category: ["logtape", "meta"],
@@ -976,7 +994,7 @@ await configure({
976
994
  }
977
995
  }
978
996
  });
979
- var logger = getLogger9(["dx-cli"]);
997
+ var logger = getLogger8(["dx-cli"]);
980
998
  var repositoryReader = makeRepositoryReader();
981
999
  var packageJsonReader = makePackageJsonReader();
982
1000
  var validationReporter = makeValidationReporter();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-cli",
3
- "version": "0.8.2",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "A CLI useful to manage DX tools.",
6
6
  "repository": {
@@ -31,21 +31,22 @@
31
31
  "semver": "^7.7.2",
32
32
  "yaml": "^2.8.0",
33
33
  "zod": "^3.25.28",
34
- "@pagopa/monorepo-generator": "^0.8.0"
34
+ "@pagopa/monorepo-generator": "^0.8.3",
35
+ "@pagopa/dx-savemoney": "^0.1.1"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@tsconfig/node22": "22.0.2",
38
- "@types/node": "^22.16.2",
39
+ "@types/node": "^22.19.1",
39
40
  "@types/semver": "^7.7.1",
40
41
  "@vitest/coverage-v8": "^3.2.4",
41
- "eslint": "^9.30.0",
42
+ "eslint": "^9.39.1",
42
43
  "memfs": "^4.23.0",
43
44
  "prettier": "3.6.2",
44
45
  "tsup": "^8.5.0",
45
46
  "typescript": "~5.8.3",
46
47
  "vitest": "^3.2.4",
47
48
  "vitest-mock-extended": "^3.1.0",
48
- "@pagopa/eslint-config": "^5.1.0"
49
+ "@pagopa/eslint-config": "^5.1.1"
49
50
  },
50
51
  "engines": {
51
52
  "node": ">=22.0.0"