@pagopa/dx-cli 0.8.1 → 0.9.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 +40 -8
  3. package/package.json +3 -2
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
@@ -280,7 +280,7 @@ async function replacePMOccurrences() {
280
280
  "https://yarnpkg.com/",
281
281
  "https://classic.yarnpkg.com/",
282
282
  /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) (\S+)\b/g,
283
- /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) /g,
283
+ /\b(yarn workspace|npm -(\b-workspace\b|\bw\b))\b/g,
284
284
  /\b(yarn install --immutable|npm ci)\b/g,
285
285
  /\b(yarn -q dlx|npx)\b/g,
286
286
  /\b(Yarn|npm)\b/gi
@@ -289,7 +289,7 @@ async function replacePMOccurrences() {
289
289
  to: [
290
290
  "https://pnpm.io/",
291
291
  "https://pnpm.io/",
292
- "pnpm --filter $1",
292
+ "pnpm --filter $3",
293
293
  "pnpm --filter <package-selector>",
294
294
  "pnpm install --frozen-lockfile",
295
295
  "pnpm dlx",
@@ -399,7 +399,7 @@ registry.add(updateCodeReview);
399
399
  var codemods_default = registry;
400
400
 
401
401
  // src/adapters/commander/index.ts
402
- import { Command as Command6 } from "commander";
402
+ import { Command as Command7 } from "commander";
403
403
 
404
404
  // src/adapters/commander/commands/codemod.ts
405
405
  import { getLogger as getLogger5 } from "@logtape/logtape";
@@ -756,25 +756,56 @@ 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";
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
+ });
788
+
789
+ // src/adapters/commander/commands/version.ts
790
+ import { Command as Command6 } from "commander";
761
791
 
762
792
  // src/domain/version.ts
763
793
  import { getLogger as getLogger7 } from "@logtape/logtape";
764
794
  function printVersion() {
765
795
  const logger2 = getLogger7(["dx-cli", "version"]);
766
- logger2.info(`dx CLI version: ${"0.8.1"}`);
796
+ logger2.info(`dx CLI version: ${"0.9.0"}`);
767
797
  }
768
798
 
769
799
  // src/adapters/commander/commands/version.ts
770
- var makeVersionCommand = () => new Command5().name("version").alias("v").action(() => printVersion());
800
+ var makeVersionCommand = () => new Command6().name("version").alias("v").action(() => printVersion());
771
801
 
772
802
  // src/adapters/commander/index.ts
773
803
  var makeCli = (deps2, config2, cliDeps) => {
774
- const program2 = new Command6();
775
- program2.name("dx").description("The CLI for DX-Platform").version("0.8.1");
804
+ const program2 = new Command7();
805
+ program2.name("dx").description("The CLI for DX-Platform").version("0.9.0");
776
806
  program2.addCommand(makeDoctorCommand(deps2, config2));
777
807
  program2.addCommand(makeCodemodCommand(cliDeps));
808
+ program2.addCommand(makeSavemoneyCommand());
778
809
  if (process.env.ENABLE_INIT_COMMAND) {
779
810
  program2.addCommand(makeInitCommand());
780
811
  }
@@ -962,6 +993,7 @@ var listCodemods = (registry2) => () => registry2.getAll();
962
993
  await configure({
963
994
  loggers: [
964
995
  { category: ["dx-cli"], lowestLevel: "info", sinks: ["console"] },
996
+ { category: ["savemoney"], lowestLevel: "debug", sinks: ["console"] },
965
997
  { category: ["json"], lowestLevel: "info", sinks: ["rawJson"] },
966
998
  {
967
999
  category: ["logtape", "meta"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "A CLI useful to manage DX tools.",
6
6
  "repository": {
@@ -31,7 +31,8 @@
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/dx-savemoney": "^0.1.0",
35
+ "@pagopa/monorepo-generator": "^0.8.3"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@tsconfig/node22": "22.0.2",