@pagopa/dx-cli 0.10.3 → 0.11.1
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/README.md +76 -6
- package/bin/index.js +104 -106
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -18,14 +18,27 @@ The DX CLI is a command-line tool designed to help developers manage and validat
|
|
|
18
18
|
|
|
19
19
|
## ✨ Features
|
|
20
20
|
|
|
21
|
-
- **Repository Validation**: Verify repository setup against DevEx guidelines
|
|
22
|
-
- **
|
|
21
|
+
- **Repository Validation**: Verify repository setup against DevEx guidelines with the `doctor` command
|
|
22
|
+
- **Code Migrations**: Apply automated migration scripts (codemods) to update code and configurations
|
|
23
|
+
- **Project Initialization**: Bootstrap new monorepo projects with standardized structure
|
|
24
|
+
- **Cost Optimization**: Analyze Azure subscriptions to identify unused or underutilized resources
|
|
25
|
+
- **Project Information**: Display comprehensive information about your project setup and tools
|
|
23
26
|
- **Developer Experience Optimization**: Ensure consistent development practices across projects
|
|
24
27
|
|
|
25
28
|
## 🚀 Installation
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
Install the CLI globally using your preferred package manager:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Using npm
|
|
34
|
+
npm install -g @pagopa/dx-cli
|
|
35
|
+
|
|
36
|
+
# Using yarn
|
|
37
|
+
yarn global add @pagopa/dx-cli
|
|
38
|
+
|
|
39
|
+
# Using pnpm
|
|
40
|
+
pnpm add -g @pagopa/dx-cli
|
|
41
|
+
```
|
|
29
42
|
|
|
30
43
|
### From Source (Development)
|
|
31
44
|
|
|
@@ -34,11 +47,19 @@ The DX CLI is a command-line tool designed to help developers manage and validat
|
|
|
34
47
|
git clone https://github.com/pagopa/dx.git
|
|
35
48
|
cd dx
|
|
36
49
|
|
|
37
|
-
# Install dependencies
|
|
50
|
+
# Install dependencies (using npm)
|
|
51
|
+
npm install
|
|
52
|
+
|
|
53
|
+
# Or using yarn
|
|
38
54
|
yarn install
|
|
39
55
|
|
|
56
|
+
# Or using pnpm
|
|
57
|
+
pnpm install
|
|
58
|
+
|
|
40
59
|
# Build the CLI
|
|
41
|
-
|
|
60
|
+
npm run build
|
|
61
|
+
# Or: yarn build
|
|
62
|
+
# Or: pnpm build
|
|
42
63
|
|
|
43
64
|
# Run the CLI
|
|
44
65
|
node ./apps/cli/bin/index.js --help
|
|
@@ -71,6 +92,55 @@ Checking monorepo scripts...
|
|
|
71
92
|
✅ Monorepo scripts are correctly set up
|
|
72
93
|
```
|
|
73
94
|
|
|
95
|
+
#### `codemod`
|
|
96
|
+
|
|
97
|
+
Manage and apply migration scripts (codemods) to the repository.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# List available codemods
|
|
101
|
+
dx codemod list
|
|
102
|
+
|
|
103
|
+
# Apply a specific codemod by ID
|
|
104
|
+
dx codemod apply <id>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This command helps you:
|
|
108
|
+
|
|
109
|
+
- View all available migration scripts for your repository
|
|
110
|
+
- Apply automated code transformations to keep your codebase up to date
|
|
111
|
+
- Migrate configurations and code patterns to newer standards
|
|
112
|
+
|
|
113
|
+
#### `init`
|
|
114
|
+
|
|
115
|
+
Initialize resources such as new projects with standardized structure.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Initialize a new monorepo project
|
|
119
|
+
dx init project
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This command will:
|
|
123
|
+
|
|
124
|
+
- Guide you through creating a new monorepo project
|
|
125
|
+
- Set up the project structure according to PagoPA's DevEx guidelines
|
|
126
|
+
- Configure necessary tools and dependencies
|
|
127
|
+
- Bootstrap the project with best practices
|
|
128
|
+
|
|
129
|
+
#### `info`
|
|
130
|
+
|
|
131
|
+
Display comprehensive information about your project setup and tools.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
dx info
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This command provides:
|
|
138
|
+
|
|
139
|
+
- Current project configuration details
|
|
140
|
+
- Installed tool versions
|
|
141
|
+
- Repository metadata
|
|
142
|
+
- Development environment information
|
|
143
|
+
|
|
74
144
|
#### `savemoney`
|
|
75
145
|
|
|
76
146
|
Analyze Azure subscriptions to identify unused or underutilized resources that could be costing you money.
|
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
|
|
5
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
6
6
|
|
|
7
7
|
// src/adapters/codemods/registry.ts
|
|
8
8
|
import { okAsync } from "neverthrow";
|
|
@@ -40,9 +40,9 @@ var getLatestCommitSha = async (owner, repo, ref = "main") => {
|
|
|
40
40
|
return response.data.sha;
|
|
41
41
|
};
|
|
42
42
|
var getLatestCommitShaOrRef = async (owner, repo, ref = "main") => {
|
|
43
|
-
const
|
|
43
|
+
const logger = getLogger(["dx-cli", "codemod"]);
|
|
44
44
|
return getLatestCommitSha(owner, repo, ref).catch(() => {
|
|
45
|
-
|
|
45
|
+
logger.warn(
|
|
46
46
|
"Failed to fetch the latest commit from {owner}/{repo}, fallback to {fallback}",
|
|
47
47
|
{ fallback: ref, owner, repo }
|
|
48
48
|
);
|
|
@@ -59,7 +59,7 @@ var isChildOf = (path2, key) => {
|
|
|
59
59
|
|
|
60
60
|
// src/adapters/codemods/update-code-review.ts
|
|
61
61
|
var updateJSCodeReviewJob = (sha) => (workflow, filename) => {
|
|
62
|
-
const
|
|
62
|
+
const logger = getLogger2(["dx-cli", "codemod"]);
|
|
63
63
|
const document = YAML2.parseDocument(workflow);
|
|
64
64
|
let updated = false;
|
|
65
65
|
YAML2.visit(document, {
|
|
@@ -86,7 +86,7 @@ var updateJSCodeReviewJob = (sha) => (workflow, filename) => {
|
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
if (updated) {
|
|
89
|
-
|
|
89
|
+
logger.info("Workflow {filename} updated", {
|
|
90
90
|
filename
|
|
91
91
|
});
|
|
92
92
|
return YAML2.stringify(document);
|
|
@@ -95,7 +95,7 @@ var updateJSCodeReviewJob = (sha) => (workflow, filename) => {
|
|
|
95
95
|
};
|
|
96
96
|
var updateCodeReview = {
|
|
97
97
|
apply: async () => {
|
|
98
|
-
const
|
|
98
|
+
const logger = getLogger2(["dx-cli", "codemod"]);
|
|
99
99
|
const owner = "pagopa";
|
|
100
100
|
const repo = "dx";
|
|
101
101
|
return getLatestCommitSha(owner, repo).then(async (sha) => {
|
|
@@ -105,7 +105,7 @@ var updateCodeReview = {
|
|
|
105
105
|
processor: updateJSCodeReviewJob(sha)
|
|
106
106
|
});
|
|
107
107
|
}).catch(() => {
|
|
108
|
-
|
|
108
|
+
logger.error(
|
|
109
109
|
"Failed to fetch the latest commit sha from {repository}",
|
|
110
110
|
{
|
|
111
111
|
repository: `${owner}/${repo}`
|
|
@@ -122,8 +122,8 @@ import { getLogger as getLogger3 } from "@logtape/logtape";
|
|
|
122
122
|
import { replaceInFile as replaceInFile2 } from "replace-in-file";
|
|
123
123
|
import YAML3 from "yaml";
|
|
124
124
|
var migrateWorkflow = (sha) => (workflow, filename) => {
|
|
125
|
-
const
|
|
126
|
-
|
|
125
|
+
const logger = getLogger3(["dx-cli", "codemod"]);
|
|
126
|
+
logger.debug("Processing {filename} file", { filename });
|
|
127
127
|
const document = YAML3.parseDocument(workflow);
|
|
128
128
|
let updated = false;
|
|
129
129
|
YAML3.visit(document, {
|
|
@@ -139,7 +139,7 @@ var migrateWorkflow = (sha) => (workflow, filename) => {
|
|
|
139
139
|
if (typeof uses === "string" && uses.match(
|
|
140
140
|
/^pagopa\/dx\/.github\/workflows\/(web|function)_app_deploy/
|
|
141
141
|
)) {
|
|
142
|
-
|
|
142
|
+
logger.debug("Adding disable_auto_staging_deploy");
|
|
143
143
|
map.addIn(["with", "disable_auto_staging_deploy"], true);
|
|
144
144
|
map.set("permissions", {
|
|
145
145
|
attestations: "write",
|
|
@@ -156,17 +156,17 @@ var migrateWorkflow = (sha) => (workflow, filename) => {
|
|
|
156
156
|
if (YAML3.isScalar(pair.key)) {
|
|
157
157
|
if (pair.key.value === "function_app_name") {
|
|
158
158
|
updated = true;
|
|
159
|
-
|
|
159
|
+
logger.debug("Updating function_app_name to web_app_name");
|
|
160
160
|
return new YAML3.Pair("web_app_name", pair.value);
|
|
161
161
|
}
|
|
162
162
|
if (pair.key.value === "use_staging_slot") {
|
|
163
163
|
updated = true;
|
|
164
|
-
|
|
164
|
+
logger.debug("Removing use_staging_slot");
|
|
165
165
|
return YAML3.visit.REMOVE;
|
|
166
166
|
}
|
|
167
167
|
if (pair.key.value === "uses") {
|
|
168
168
|
updated = true;
|
|
169
|
-
|
|
169
|
+
logger.debug("Updating uses value");
|
|
170
170
|
return new YAML3.Pair(
|
|
171
171
|
"uses",
|
|
172
172
|
`pagopa/dx/.github/workflows/release-azure-appsvc.yaml@${sha}`
|
|
@@ -176,12 +176,12 @@ var migrateWorkflow = (sha) => (workflow, filename) => {
|
|
|
176
176
|
}
|
|
177
177
|
});
|
|
178
178
|
if (updated) {
|
|
179
|
-
|
|
179
|
+
logger.info("Workflow {filename} updated", {
|
|
180
180
|
filename
|
|
181
181
|
});
|
|
182
182
|
return YAML3.stringify(document);
|
|
183
183
|
}
|
|
184
|
-
|
|
184
|
+
logger.debug("No changes applied to {filename}", { filename });
|
|
185
185
|
return workflow;
|
|
186
186
|
};
|
|
187
187
|
var useAzureAppsvc = {
|
|
@@ -245,8 +245,8 @@ async function extractPackageExtensions() {
|
|
|
245
245
|
return void 0;
|
|
246
246
|
}
|
|
247
247
|
async function preparePackageJsonForPnpm() {
|
|
248
|
-
const
|
|
249
|
-
const manifest = JSON.parse(
|
|
248
|
+
const packageJson = await fs.readFile("package.json", "utf-8");
|
|
249
|
+
const manifest = JSON.parse(packageJson);
|
|
250
250
|
let workspaces = [];
|
|
251
251
|
if (Object.hasOwn(manifest, "packageManager")) {
|
|
252
252
|
delete manifest.packageManager;
|
|
@@ -271,8 +271,8 @@ async function removeFiles(...files) {
|
|
|
271
271
|
);
|
|
272
272
|
}
|
|
273
273
|
async function replacePMOccurrences() {
|
|
274
|
-
const
|
|
275
|
-
|
|
274
|
+
const logger = getLogger4(["dx-cli", "codemod"]);
|
|
275
|
+
logger.info("Replacing yarn and npm occurrences in files...");
|
|
276
276
|
const results = await replaceInFile3({
|
|
277
277
|
allowEmptyPaths: true,
|
|
278
278
|
files: ["**/*.json", "**/*.md", "**/Dockerfile", "**/docker-compose.yml"],
|
|
@@ -300,11 +300,11 @@ async function replacePMOccurrences() {
|
|
|
300
300
|
(acc, file) => file.hasChanged ? acc + 1 : acc,
|
|
301
301
|
0
|
|
302
302
|
);
|
|
303
|
-
|
|
303
|
+
logger.info("Replaced yarn occurrences in {count} files", { count });
|
|
304
304
|
}
|
|
305
305
|
async function updateDXWorkflows() {
|
|
306
|
-
const
|
|
307
|
-
|
|
306
|
+
const logger = getLogger4(["dx-cli", "codemod"]);
|
|
307
|
+
logger.info("Updating Github Workflows workflows...");
|
|
308
308
|
const sha = await getLatestCommitShaOrRef("pagopa", "dx");
|
|
309
309
|
const results = await replaceInFile3({
|
|
310
310
|
allowEmptyPaths: true,
|
|
@@ -332,9 +332,9 @@ var apply = async (info) => {
|
|
|
332
332
|
throw new Error("Project is already using pnpm");
|
|
333
333
|
}
|
|
334
334
|
const pm = info.packageManager === "yarn" ? new Yarn() : new NPM();
|
|
335
|
-
const
|
|
335
|
+
const logger = getLogger4(["dx-cli", "codemod"]);
|
|
336
336
|
const localWorkspaces = await pm.listWorkspaces();
|
|
337
|
-
|
|
337
|
+
logger.info("Using the {protocol} protocol for local dependencies", {
|
|
338
338
|
protocol: "workspace:"
|
|
339
339
|
});
|
|
340
340
|
if (localWorkspaces.length > 0) {
|
|
@@ -345,17 +345,17 @@ var apply = async (info) => {
|
|
|
345
345
|
to: localWorkspaces.map((ws) => `"${ws}": "workspace:^"`)
|
|
346
346
|
});
|
|
347
347
|
}
|
|
348
|
-
|
|
348
|
+
logger.info("Remove unused fields from {file}", {
|
|
349
349
|
file: "package.json"
|
|
350
350
|
});
|
|
351
351
|
const workspaces = await preparePackageJsonForPnpm();
|
|
352
352
|
const packageExtensions = info.packageManager === "yarn" ? await extractPackageExtensions() : void 0;
|
|
353
|
-
|
|
353
|
+
logger.info("Create {file}", {
|
|
354
354
|
file: "pnpm-workspace.yaml"
|
|
355
355
|
});
|
|
356
356
|
await writePnpmWorkspaceFile(workspaces, packageExtensions);
|
|
357
357
|
await $`corepack pnpm@latest add --config pnpm-plugin-pagopa`;
|
|
358
|
-
|
|
358
|
+
logger.info("Remove node_modules and yarn files");
|
|
359
359
|
await removeFiles(
|
|
360
360
|
".yarnrc",
|
|
361
361
|
".yarnrc.yml",
|
|
@@ -367,22 +367,22 @@ var apply = async (info) => {
|
|
|
367
367
|
);
|
|
368
368
|
const stat2 = await fs.stat(pm.lockFileName);
|
|
369
369
|
if (stat2.isFile()) {
|
|
370
|
-
|
|
370
|
+
logger.info("Importing {source} to {target}", {
|
|
371
371
|
source: pm.lockFileName,
|
|
372
372
|
target: "pnpm-lock.yaml"
|
|
373
373
|
});
|
|
374
374
|
await $`corepack pnpm@latest import ${pm.lockFileName}`;
|
|
375
375
|
await removeFiles(pm.lockFileName);
|
|
376
376
|
} else {
|
|
377
|
-
|
|
377
|
+
logger.info("No {source} file found, skipping import.", {
|
|
378
378
|
source: pm.lockFileName
|
|
379
379
|
});
|
|
380
380
|
}
|
|
381
381
|
await replacePMOccurrences();
|
|
382
382
|
await updateDXWorkflows();
|
|
383
|
-
|
|
383
|
+
logger.info("Adding pnpm store to .gitignore...");
|
|
384
384
|
await fs.appendFile(".gitignore", "\n\n# PNPM\n.pnpm-store");
|
|
385
|
-
|
|
385
|
+
logger.info("Setting pnpm as the package manager...");
|
|
386
386
|
await $`corepack use pnpm@latest`;
|
|
387
387
|
};
|
|
388
388
|
var use_pnpm_default = {
|
|
@@ -415,9 +415,9 @@ var makeCodemodCommand = ({
|
|
|
415
415
|
})
|
|
416
416
|
).addCommand(
|
|
417
417
|
new Command("apply").argument("<id>", "The id of the codemod to apply").description("Apply migration scripts to the repository").action(async function(id) {
|
|
418
|
-
const
|
|
418
|
+
const logger = getLogger5(["dx-cli", "codemod"]);
|
|
419
419
|
await applyCodemodById2(id).andTee(() => {
|
|
420
|
-
|
|
420
|
+
logger.info("Codemod applied \u2705");
|
|
421
421
|
}).orTee((error) => this.error(error.message));
|
|
422
422
|
})
|
|
423
423
|
);
|
|
@@ -468,12 +468,10 @@ var findMissingScripts = (availableScripts, requiredScripts) => {
|
|
|
468
468
|
const requiredScriptNames = new Set(requiredScripts.keys());
|
|
469
469
|
return requiredScriptNames.difference(availableScriptNames);
|
|
470
470
|
};
|
|
471
|
-
var checkMonorepoScripts = async (dependencies,
|
|
471
|
+
var checkMonorepoScripts = async (dependencies, repositoryRoot) => {
|
|
472
472
|
const { packageJsonReader: packageJsonReader2 } = dependencies;
|
|
473
473
|
const checkName = "Monorepo Scripts";
|
|
474
|
-
const scriptsResult = await packageJsonReader2.getScripts(
|
|
475
|
-
config2.repository.root
|
|
476
|
-
);
|
|
474
|
+
const scriptsResult = await packageJsonReader2.getScripts(repositoryRoot);
|
|
477
475
|
if (scriptsResult.isErr()) {
|
|
478
476
|
return err(scriptsResult.error);
|
|
479
477
|
}
|
|
@@ -509,11 +507,11 @@ var isVersionValid = (version, minVersion) => {
|
|
|
509
507
|
}
|
|
510
508
|
return semverGte(dependencySemVer, minAcceptedSemVer);
|
|
511
509
|
};
|
|
512
|
-
var checkPreCommitConfig = async (dependencies,
|
|
510
|
+
var checkPreCommitConfig = async (dependencies, repositoryRoot) => {
|
|
513
511
|
const { repositoryReader: repositoryReader2 } = dependencies;
|
|
514
512
|
const checkName = "Pre-commit Configuration";
|
|
515
513
|
const preCommitResult = await repositoryReader2.fileExists(
|
|
516
|
-
fs2.join(
|
|
514
|
+
fs2.join(repositoryRoot, ".pre-commit-config.yaml")
|
|
517
515
|
);
|
|
518
516
|
if (preCommitResult.isOk() && preCommitResult.value) {
|
|
519
517
|
return ok2({
|
|
@@ -529,12 +527,11 @@ var checkPreCommitConfig = async (dependencies, config2) => {
|
|
|
529
527
|
isValid: false
|
|
530
528
|
});
|
|
531
529
|
};
|
|
532
|
-
var checkTurboConfig = async (dependencies, config2) => {
|
|
530
|
+
var checkTurboConfig = async (dependencies, repositoryRoot, config2) => {
|
|
533
531
|
const { packageJsonReader: packageJsonReader2, repositoryReader: repositoryReader2 } = dependencies;
|
|
534
532
|
const checkName = "Turbo Configuration";
|
|
535
|
-
const repoRoot2 = config2.repository.root;
|
|
536
533
|
const turboResult = await repositoryReader2.fileExists(
|
|
537
|
-
fs2.join(
|
|
534
|
+
fs2.join(repositoryRoot, "turbo.json")
|
|
538
535
|
);
|
|
539
536
|
if (turboResult.isErr()) {
|
|
540
537
|
return ok2({
|
|
@@ -544,7 +541,7 @@ var checkTurboConfig = async (dependencies, config2) => {
|
|
|
544
541
|
});
|
|
545
542
|
}
|
|
546
543
|
const dependenciesResult = await packageJsonReader2.getDependencies(
|
|
547
|
-
|
|
544
|
+
repositoryRoot,
|
|
548
545
|
"dev"
|
|
549
546
|
);
|
|
550
547
|
if (dependenciesResult.isErr()) {
|
|
@@ -613,22 +610,36 @@ var checkWorkspaces = async (dependencies, monorepoDir) => {
|
|
|
613
610
|
};
|
|
614
611
|
|
|
615
612
|
// src/domain/doctor.ts
|
|
616
|
-
var runDoctor = (dependencies, config2) => {
|
|
613
|
+
var runDoctor = async (dependencies, config2) => {
|
|
614
|
+
const repoRootResult = await dependencies.repositoryReader.findRepositoryRoot();
|
|
615
|
+
if (repoRootResult.isErr()) {
|
|
616
|
+
return {
|
|
617
|
+
checks: [
|
|
618
|
+
{
|
|
619
|
+
checkName: "Repository Detection",
|
|
620
|
+
errorMessage: "Could not find repository root. Make sure to run this command inside a Git repository.",
|
|
621
|
+
isValid: false
|
|
622
|
+
}
|
|
623
|
+
],
|
|
624
|
+
hasErrors: true
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
const repositoryRoot = repoRootResult.value;
|
|
617
628
|
const doctorChecks = [
|
|
618
629
|
ResultAsync3.fromPromise(
|
|
619
|
-
checkPreCommitConfig(dependencies,
|
|
630
|
+
checkPreCommitConfig(dependencies, repositoryRoot),
|
|
620
631
|
() => new Error("Error checking pre-commit configuration")
|
|
621
632
|
),
|
|
622
633
|
ResultAsync3.fromPromise(
|
|
623
|
-
checkTurboConfig(dependencies, config2),
|
|
634
|
+
checkTurboConfig(dependencies, repositoryRoot, config2),
|
|
624
635
|
() => new Error("Error checking Turbo configuration")
|
|
625
636
|
),
|
|
626
637
|
ResultAsync3.fromPromise(
|
|
627
|
-
checkMonorepoScripts(dependencies,
|
|
638
|
+
checkMonorepoScripts(dependencies, repositoryRoot),
|
|
628
639
|
() => new Error("Error checking monorepo scripts")
|
|
629
640
|
),
|
|
630
641
|
ResultAsync3.fromPromise(
|
|
631
|
-
checkWorkspaces(dependencies,
|
|
642
|
+
checkWorkspaces(dependencies, repositoryRoot),
|
|
632
643
|
() => new Error("Error checking monorepo scripts")
|
|
633
644
|
)
|
|
634
645
|
];
|
|
@@ -675,50 +686,56 @@ import { Command as Command3 } from "commander";
|
|
|
675
686
|
// src/domain/info.ts
|
|
676
687
|
import { getLogger as getLogger6 } from "@logtape/logtape";
|
|
677
688
|
import { join } from "path";
|
|
678
|
-
var detectFromLockFile = async (dependencies,
|
|
689
|
+
var detectFromLockFile = async (dependencies, repositoryRoot) => {
|
|
679
690
|
const { repositoryReader: repositoryReader2 } = dependencies;
|
|
680
|
-
const repoRoot2 = config2.repository.root;
|
|
681
691
|
const pnpmResult = await repositoryReader2.fileExists(
|
|
682
|
-
join(
|
|
692
|
+
join(repositoryRoot, "pnpm-lock.yaml")
|
|
683
693
|
);
|
|
684
694
|
if (pnpmResult.isOk() && pnpmResult.value) return "pnpm";
|
|
685
695
|
const yarnResult = await repositoryReader2.fileExists(
|
|
686
|
-
join(
|
|
696
|
+
join(repositoryRoot, "yarn.lock")
|
|
687
697
|
);
|
|
688
698
|
if (yarnResult.isOk() && yarnResult.value) return "yarn";
|
|
689
699
|
const npmResult = await repositoryReader2.fileExists(
|
|
690
|
-
join(
|
|
700
|
+
join(repositoryRoot, "package-lock.json")
|
|
691
701
|
);
|
|
692
702
|
if (npmResult.isOk() && npmResult.value) return "npm";
|
|
693
703
|
return void 0;
|
|
694
704
|
};
|
|
695
|
-
var detectPackageManager = async (dependencies,
|
|
696
|
-
const
|
|
705
|
+
var detectPackageManager = async (dependencies, repositoryRoot) => {
|
|
706
|
+
const packageJsonResult = await dependencies.packageJsonReader.readPackageJson(repositoryRoot);
|
|
707
|
+
const packageManager = packageJsonResult.map((pkg) => pkg.packageManager).unwrapOr(void 0) ?? await detectFromLockFile(dependencies, repositoryRoot);
|
|
697
708
|
return packageManager ?? "npm";
|
|
698
709
|
};
|
|
699
710
|
var detectNodeVersion = async ({ repositoryReader: repositoryReader2 }, nodeVersionFilePath) => await repositoryReader2.readFile(nodeVersionFilePath).map((nodeVersion) => nodeVersion.trim()).unwrapOr(void 0);
|
|
700
711
|
var detectTerraformVersion = async ({ repositoryReader: repositoryReader2 }, terraformVersionFilePath) => await repositoryReader2.readFile(terraformVersionFilePath).map((tfVersion) => tfVersion.trim()).unwrapOr(void 0);
|
|
701
|
-
var detectTurboVersion = (
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
)
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
712
|
+
var detectTurboVersion = async (dependencies, repositoryRoot) => {
|
|
713
|
+
const packageJsonResult = await dependencies.packageJsonReader.readPackageJson(repositoryRoot);
|
|
714
|
+
return packageJsonResult.map((pkg) => pkg.devDependencies.get("turbo")?.trim()).unwrapOr(void 0);
|
|
715
|
+
};
|
|
716
|
+
var getInfo = (dependencies) => async () => {
|
|
717
|
+
const repositoryRoot = await dependencies.repositoryReader.findRepositoryRoot().unwrapOr(process.cwd());
|
|
718
|
+
return {
|
|
719
|
+
node: await detectNodeVersion(
|
|
720
|
+
{ repositoryReader: dependencies.repositoryReader },
|
|
721
|
+
`${repositoryRoot}/.node-version`
|
|
722
|
+
),
|
|
723
|
+
packageManager: await detectPackageManager(dependencies, repositoryRoot),
|
|
724
|
+
terraform: await detectTerraformVersion(
|
|
725
|
+
{ repositoryReader: dependencies.repositoryReader },
|
|
726
|
+
`${repositoryRoot}/.terraform-version`
|
|
727
|
+
),
|
|
728
|
+
turbo: await detectTurboVersion(dependencies, repositoryRoot)
|
|
729
|
+
};
|
|
730
|
+
};
|
|
714
731
|
var printInfo = (result) => {
|
|
715
|
-
const
|
|
716
|
-
|
|
732
|
+
const logger = getLogger6("json");
|
|
733
|
+
logger.info(JSON.stringify(result));
|
|
717
734
|
};
|
|
718
735
|
|
|
719
736
|
// src/adapters/commander/commands/info.ts
|
|
720
|
-
var makeInfoCommand = (dependencies
|
|
721
|
-
const result = await getInfo(dependencies
|
|
737
|
+
var makeInfoCommand = (dependencies) => new Command3().name("info").description("Display information about the project").action(async () => {
|
|
738
|
+
const result = await getInfo(dependencies)();
|
|
722
739
|
printInfo(result);
|
|
723
740
|
});
|
|
724
741
|
|
|
@@ -789,25 +806,25 @@ var makeSavemoneyCommand = () => new Command5("savemoney").description(
|
|
|
789
806
|
// src/adapters/commander/index.ts
|
|
790
807
|
var makeCli = (deps2, config2, cliDeps) => {
|
|
791
808
|
const program2 = new Command6();
|
|
792
|
-
program2.name("dx").description("The CLI for DX-Platform").version("0.
|
|
809
|
+
program2.name("dx").description("The CLI for DX-Platform").version("0.11.1");
|
|
793
810
|
program2.addCommand(makeDoctorCommand(deps2, config2));
|
|
794
811
|
program2.addCommand(makeCodemodCommand(cliDeps));
|
|
795
812
|
program2.addCommand(makeInitCommand());
|
|
796
813
|
program2.addCommand(makeSavemoneyCommand());
|
|
797
|
-
program2.addCommand(makeInfoCommand(deps2
|
|
814
|
+
program2.addCommand(makeInfoCommand(deps2));
|
|
798
815
|
return program2;
|
|
799
816
|
};
|
|
800
817
|
|
|
801
818
|
// src/adapters/logtape/validation-reporter.ts
|
|
802
819
|
import { getLogger as getLogger7 } from "@logtape/logtape";
|
|
803
820
|
var makeValidationReporter = () => {
|
|
804
|
-
const
|
|
821
|
+
const logger = getLogger7(["dx-cli", "validation"]);
|
|
805
822
|
return {
|
|
806
823
|
reportCheckResult(result) {
|
|
807
824
|
if (result.isValid) {
|
|
808
|
-
|
|
825
|
+
logger.info(`\u2705 ${result.successMessage}`);
|
|
809
826
|
} else {
|
|
810
|
-
|
|
827
|
+
logger.error(`\u274C ${result.errorMessage}`);
|
|
811
828
|
}
|
|
812
829
|
}
|
|
813
830
|
};
|
|
@@ -851,9 +868,9 @@ var makePackageJsonReader = () => ({
|
|
|
851
868
|
getDependencies: (cwd2 = process3.cwd(), type) => {
|
|
852
869
|
const packageJsonPath = join2(cwd2, "package.json");
|
|
853
870
|
return readFileAndDecode(packageJsonPath, packageJsonSchema).map(
|
|
854
|
-
(
|
|
871
|
+
(packageJson) => {
|
|
855
872
|
const key = type === "dev" ? "devDependencies" : "dependencies";
|
|
856
|
-
return
|
|
873
|
+
return packageJson[key];
|
|
857
874
|
}
|
|
858
875
|
);
|
|
859
876
|
},
|
|
@@ -893,20 +910,20 @@ var findRepositoryRoot = (dir = process.cwd()) => {
|
|
|
893
910
|
)
|
|
894
911
|
).map(() => dir);
|
|
895
912
|
};
|
|
896
|
-
var resolveWorkspacePattern = (
|
|
913
|
+
var resolveWorkspacePattern = (repoRoot, pattern) => ResultAsync7.fromPromise(
|
|
897
914
|
// For now it is not possible to use the fs.glob function (from node:fs/promises)
|
|
898
915
|
// because it is not possible to run it on Node 20.x
|
|
899
|
-
glob.glob(pattern, { cwd:
|
|
916
|
+
glob.glob(pattern, { cwd: repoRoot }),
|
|
900
917
|
(cause) => new Error(`Failed to resolve workspace glob: ${pattern}`, {
|
|
901
918
|
cause
|
|
902
919
|
})
|
|
903
920
|
).map(
|
|
904
921
|
(subDirectories) => (
|
|
905
922
|
// Create the absolute path to the subdirectory
|
|
906
|
-
subDirectories.map((directory) => path.join(
|
|
923
|
+
subDirectories.map((directory) => path.join(repoRoot, directory))
|
|
907
924
|
)
|
|
908
925
|
);
|
|
909
|
-
var getWorkspaces = (
|
|
926
|
+
var getWorkspaces = (repoRoot) => readFile2(path.join(repoRoot, "pnpm-workspace.yaml")).andThen(parseYaml).andThen(
|
|
910
927
|
(obj) => (
|
|
911
928
|
// If no packages are defined, go on with an empty array
|
|
912
929
|
decode(z3.object({ packages: z3.array(z3.string()) }))(obj).orElse(
|
|
@@ -917,7 +934,7 @@ var getWorkspaces = (repoRoot2) => readFile2(path.join(repoRoot2, "pnpm-workspac
|
|
|
917
934
|
({ packages }) => (
|
|
918
935
|
// For every package pattern in the pnpm-workspace.yaml file, get the list of subdirectories
|
|
919
936
|
ResultAsync7.combine(
|
|
920
|
-
packages.map((pattern) => resolveWorkspacePattern(
|
|
937
|
+
packages.map((pattern) => resolveWorkspacePattern(repoRoot, pattern))
|
|
921
938
|
).map((workspacesList) => workspacesList.flat()).andThen((workspaceFolders) => {
|
|
922
939
|
const workspaceResults = workspaceFolders.map(
|
|
923
940
|
(nodeWorkspaceDirectory) => readFileAndDecode(
|
|
@@ -942,12 +959,9 @@ var makeRepositoryReader = () => ({
|
|
|
942
959
|
});
|
|
943
960
|
|
|
944
961
|
// src/config.ts
|
|
945
|
-
var getConfig = (
|
|
962
|
+
var getConfig = () => ({
|
|
946
963
|
minVersions: {
|
|
947
964
|
turbo: "2"
|
|
948
|
-
},
|
|
949
|
-
repository: {
|
|
950
|
-
root: repositoryRoot2
|
|
951
965
|
}
|
|
952
966
|
});
|
|
953
967
|
|
|
@@ -992,33 +1006,17 @@ await configure({
|
|
|
992
1006
|
}
|
|
993
1007
|
}
|
|
994
1008
|
});
|
|
995
|
-
var logger = getLogger8(["dx-cli"]);
|
|
996
1009
|
var repositoryReader = makeRepositoryReader();
|
|
997
1010
|
var packageJsonReader = makePackageJsonReader();
|
|
998
1011
|
var validationReporter = makeValidationReporter();
|
|
999
|
-
var repoRoot = await repositoryReader.findRepositoryRoot(process.cwd());
|
|
1000
|
-
if (repoRoot.isErr()) {
|
|
1001
|
-
logger.error(
|
|
1002
|
-
"Could not find repository root. Make sure to have the repo initialized."
|
|
1003
|
-
);
|
|
1004
|
-
process.exit(1);
|
|
1005
|
-
}
|
|
1006
|
-
var repositoryRoot = repoRoot.value;
|
|
1007
|
-
var repoPackageJson = await packageJsonReader.readPackageJson(repositoryRoot);
|
|
1008
|
-
if (repoPackageJson.isErr()) {
|
|
1009
|
-
logger.error("Repository does not contain a package.json file");
|
|
1010
|
-
process.exit(1);
|
|
1011
|
-
}
|
|
1012
|
-
var packageJson = repoPackageJson.value;
|
|
1013
1012
|
var deps = {
|
|
1014
|
-
packageJson,
|
|
1015
1013
|
packageJsonReader,
|
|
1016
1014
|
repositoryReader,
|
|
1017
1015
|
validationReporter
|
|
1018
1016
|
};
|
|
1019
|
-
var config = getConfig(
|
|
1017
|
+
var config = getConfig();
|
|
1020
1018
|
var useCases = {
|
|
1021
|
-
applyCodemodById: applyCodemodById(codemods_default, getInfo(deps
|
|
1019
|
+
applyCodemodById: applyCodemodById(codemods_default, getInfo(deps)),
|
|
1022
1020
|
listCodemods: listCodemods(codemods_default)
|
|
1023
1021
|
};
|
|
1024
1022
|
var program = makeCli(deps, config, useCases);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagopa/dx-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A CLI useful to manage DX tools.",
|
|
6
6
|
"repository": {
|
|
@@ -19,32 +19,32 @@
|
|
|
19
19
|
"dx": "./bin/index.js"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@logtape/logtape": "^1.
|
|
23
|
-
"commander": "^14.0.
|
|
24
|
-
"core-js": "^3.
|
|
25
|
-
"execa": "^9.6.
|
|
26
|
-
"glob": "^11.0
|
|
22
|
+
"@logtape/logtape": "^1.2.2",
|
|
23
|
+
"commander": "^14.0.2",
|
|
24
|
+
"core-js": "^3.47.0",
|
|
25
|
+
"execa": "^9.6.1",
|
|
26
|
+
"glob": "^11.1.0",
|
|
27
27
|
"neverthrow": "^8.2.0",
|
|
28
|
-
"node-plop": "^0.32.
|
|
29
|
-
"octokit": "^5.0.
|
|
28
|
+
"node-plop": "^0.32.3",
|
|
29
|
+
"octokit": "^5.0.5",
|
|
30
30
|
"replace-in-file": "^8.3.0",
|
|
31
31
|
"semver": "^7.7.2",
|
|
32
|
-
"yaml": "^2.8.
|
|
33
|
-
"zod": "^3.25.
|
|
34
|
-
"@pagopa/dx-savemoney": "^0.1.
|
|
35
|
-
"@pagopa/monorepo-generator": "^0.
|
|
32
|
+
"yaml": "^2.8.2",
|
|
33
|
+
"zod": "^3.25.76",
|
|
34
|
+
"@pagopa/dx-savemoney": "^0.1.4",
|
|
35
|
+
"@pagopa/monorepo-generator": "^0.11.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@tsconfig/node22": "22.0.2",
|
|
39
39
|
"@types/node": "^22.19.1",
|
|
40
40
|
"@types/semver": "^7.7.1",
|
|
41
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
41
42
|
"eslint": "^9.39.1",
|
|
42
|
-
"memfs": "^4.
|
|
43
|
+
"memfs": "^4.51.1",
|
|
43
44
|
"prettier": "3.6.2",
|
|
44
|
-
"tsup": "^8.5.
|
|
45
|
+
"tsup": "^8.5.1",
|
|
45
46
|
"typescript": "~5.8.3",
|
|
46
47
|
"vitest": "^3.2.4",
|
|
47
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
48
48
|
"vitest-mock-extended": "^3.1.0",
|
|
49
49
|
"@pagopa/eslint-config": "^5.1.1"
|
|
50
50
|
},
|