@pagopa/dx-cli 0.6.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.
Files changed (2) hide show
  1. package/bin/index.js +392 -56
  2. package/package.json +5 -2
package/bin/index.js CHANGED
@@ -2,22 +2,10 @@
2
2
 
3
3
  // src/index.ts
4
4
  import "core-js/actual/set/index.js";
5
- import { configure, getConsoleSink, getLogger as getLogger4 } from "@logtape/logtape";
6
-
7
- // src/adapters/codemods/example.ts
8
- import { okAsync } from "neverthrow";
9
- var apply = () => {
10
- console.log("Hello from example codemod!");
11
- return okAsync(void 0);
12
- };
13
- var example_default = {
14
- apply,
15
- description: "An example codemod that does nothing",
16
- id: "example"
17
- };
5
+ import { configure, getConsoleSink, getLogger as getLogger9 } from "@logtape/logtape";
18
6
 
19
7
  // src/adapters/codemods/registry.ts
20
- import { okAsync as okAsync2 } from "neverthrow";
8
+ import { okAsync } from "neverthrow";
21
9
  var LocalCodemodRegistry = class {
22
10
  #m;
23
11
  constructor() {
@@ -27,22 +15,359 @@ var LocalCodemodRegistry = class {
27
15
  this.#m.set(codemod.id, codemod);
28
16
  }
29
17
  getAll() {
30
- return okAsync2(Array.from(this.#m.values()));
18
+ return okAsync(Array.from(this.#m.values()));
31
19
  }
32
20
  getById(id) {
33
- return okAsync2(this.#m.get(id));
21
+ return okAsync(this.#m.get(id));
34
22
  }
35
23
  };
36
24
 
25
+ // src/adapters/codemods/update-code-review.ts
26
+ import { getLogger as getLogger2 } from "@logtape/logtape";
27
+ import { replaceInFile } from "replace-in-file";
28
+
29
+ // src/adapters/codemods/git.ts
30
+ import { getLogger } from "@logtape/logtape";
31
+ import { Octokit } from "octokit";
32
+ var getLatestCommitSha = async (owner, repo, ref = "main") => {
33
+ const octokit = new Octokit();
34
+ const response = await octokit.rest.repos.getCommit({
35
+ owner,
36
+ ref,
37
+ repo
38
+ });
39
+ return response.data.sha;
40
+ };
41
+ var getLatestCommitShaOrRef = async (owner, repo, ref = "main") => {
42
+ const logger2 = getLogger(["dx-cli", "codemod"]);
43
+ return getLatestCommitSha(owner, repo, ref).catch(() => {
44
+ logger2.warn(
45
+ "Failed to fetch the latest commit from {owner}/{repo}, fallback to {fallback}",
46
+ { fallback: ref, owner, repo }
47
+ );
48
+ return ref;
49
+ });
50
+ };
51
+
52
+ // src/adapters/codemods/update-code-review.ts
53
+ var updateJSCodeReview = async (sha) => {
54
+ const logger2 = getLogger2(["dx-cli", "codemod"]);
55
+ const results = await replaceInFile({
56
+ allowEmptyPaths: true,
57
+ files: [".github/workflows/*.yaml"],
58
+ from: [/pagopa\/dx\/.github\/workflows\/js_code_review.yaml@(\S+)/g],
59
+ to: [`pagopa/dx/.github/workflows/js_code_review.yaml@${sha}`]
60
+ });
61
+ const updated = results.filter((r) => r.hasChanged).map((r) => r.file);
62
+ updated.forEach((filename) => {
63
+ logger2.info("Workflow {filename} updated", { filename });
64
+ });
65
+ return updated;
66
+ };
67
+ var updateCodeReview = {
68
+ apply: async () => {
69
+ const logger2 = getLogger2(["dx-cli", "codemod"]);
70
+ const owner = "pagopa";
71
+ const repo = "dx";
72
+ return getLatestCommitSha(owner, repo).then(async (sha) => {
73
+ await updateJSCodeReview(sha);
74
+ }).catch(() => {
75
+ logger2.error(
76
+ "Failed to fetch the latest commit sha from {owner}/{repo}",
77
+ {
78
+ owner,
79
+ repo
80
+ }
81
+ );
82
+ });
83
+ },
84
+ description: "Update js_code_review workflow to its latest version",
85
+ id: "update-code-review"
86
+ };
87
+
88
+ // src/adapters/codemods/use-azure-appsvc.ts
89
+ import { getLogger as getLogger3 } from "@logtape/logtape";
90
+ import { replaceInFile as replaceInFile2 } from "replace-in-file";
91
+ import YAML from "yaml";
92
+ var isChildOf = (path2, key) => {
93
+ const ancestor = path2.at(-1);
94
+ return YAML.isPair(ancestor) && YAML.isScalar(ancestor.key) && typeof ancestor.key.value === "string" && ancestor.key.value === key;
95
+ };
96
+ var migrateWorkflow = (sha) => (workflow, filename) => {
97
+ const logger2 = getLogger3(["dx-cli", "codemod"]);
98
+ logger2.debug("Processing {filename} file", { filename });
99
+ const document = YAML.parseDocument(workflow);
100
+ let updated = false;
101
+ YAML.visit(document, {
102
+ Map(_, map, path2) {
103
+ if (isChildOf(path2, "jobs") || isChildOf(path2, "with")) {
104
+ return void 0;
105
+ }
106
+ if (map.has("jobs")) {
107
+ return void 0;
108
+ }
109
+ if (map.has("uses")) {
110
+ const uses = map.get("uses");
111
+ if (typeof uses === "string" && uses.match(
112
+ /^pagopa\/dx\/.github\/workflows\/(web|function)_app_deploy/
113
+ )) {
114
+ logger2.debug("Adding disable_auto_staging_deploy");
115
+ map.addIn(["with", "disable_auto_staging_deploy"], true);
116
+ map.set("permissions", {
117
+ attestations: "write",
118
+ contents: "read",
119
+ "id-token": "write"
120
+ });
121
+ updated = true;
122
+ return void 0;
123
+ }
124
+ }
125
+ return YAML.visit.SKIP;
126
+ },
127
+ Pair(_, pair) {
128
+ if (YAML.isScalar(pair.key)) {
129
+ if (pair.key.value === "function_app_name") {
130
+ updated = true;
131
+ logger2.debug("Updating function_app_name to web_app_name");
132
+ return new YAML.Pair("web_app_name", pair.value);
133
+ }
134
+ if (pair.key.value === "use_staging_slot") {
135
+ updated = true;
136
+ logger2.debug("Removing use_staging_slot");
137
+ return YAML.visit.REMOVE;
138
+ }
139
+ if (pair.key.value === "uses") {
140
+ updated = true;
141
+ logger2.debug("Updating uses value");
142
+ return new YAML.Pair(
143
+ "uses",
144
+ `pagopa/dx/.github/workflows/release-azure-appsvc.yaml@${sha}`
145
+ );
146
+ }
147
+ }
148
+ }
149
+ });
150
+ if (updated) {
151
+ logger2.info("Workflow {filename} updated", {
152
+ filename
153
+ });
154
+ return YAML.stringify(document);
155
+ }
156
+ logger2.debug("No changes applied to {filename}", { filename });
157
+ return workflow;
158
+ };
159
+ var useAzureAppsvc = {
160
+ apply: async () => {
161
+ const sha = await getLatestCommitShaOrRef("pagopa", "dx");
162
+ await replaceInFile2({
163
+ allowEmptyPaths: true,
164
+ files: [".github/workflows/*.yaml"],
165
+ processor: migrateWorkflow(sha)
166
+ });
167
+ },
168
+ description: "Refactor legacy deploy workflows to use release-azure-appsvc",
169
+ id: "use-azure-appsvc"
170
+ };
171
+
172
+ // src/adapters/codemods/use-pnpm.ts
173
+ import { getLogger as getLogger4 } from "@logtape/logtape";
174
+ import { $ } from "execa";
175
+ import * as fs from "fs/promises";
176
+ import { replaceInFile as replaceInFile3 } from "replace-in-file";
177
+ import YAML2 from "yaml";
178
+ var NPM = class {
179
+ lockFileName = "package-lock.json";
180
+ async listWorkspaces() {
181
+ const { stdout } = await $`npm query .workspace`;
182
+ const workspaces = JSON.parse(stdout);
183
+ const workspaceNames = [];
184
+ if (Array.isArray(workspaces)) {
185
+ for (const ws of workspaces) {
186
+ if (Object.hasOwn(ws, "name")) {
187
+ workspaceNames.push(ws.name);
188
+ }
189
+ }
190
+ }
191
+ return workspaceNames;
192
+ }
193
+ };
194
+ var Yarn = class {
195
+ lockFileName = "yarn.lock";
196
+ async listWorkspaces() {
197
+ const { stdout } = await $({ lines: true })`yarn workspaces list --json`;
198
+ const workspaceNames = [];
199
+ for (const line of stdout) {
200
+ const ws = JSON.parse(line);
201
+ if (Object.hasOwn(ws, "name")) {
202
+ workspaceNames.push(ws.name);
203
+ }
204
+ }
205
+ return workspaceNames;
206
+ }
207
+ };
208
+ async function extractPackageExtensions() {
209
+ try {
210
+ const yarnrc = await fs.readFile(".yarnrc.yml", "utf-8");
211
+ const parsed = YAML2.parse(yarnrc);
212
+ if (parsed.packageExtensions) {
213
+ return parsed.packageExtensions;
214
+ }
215
+ } catch {
216
+ }
217
+ return void 0;
218
+ }
219
+ async function preparePackageJsonForPnpm() {
220
+ const packageJson2 = await fs.readFile("package.json", "utf-8");
221
+ const manifest = JSON.parse(packageJson2);
222
+ let workspaces = [];
223
+ if (Object.hasOwn(manifest, "packageManager")) {
224
+ delete manifest.packageManager;
225
+ }
226
+ if (Object.hasOwn(manifest, "workspaces")) {
227
+ if (Array.isArray(manifest.workspaces)) {
228
+ workspaces = manifest.workspaces;
229
+ }
230
+ delete manifest.workspaces;
231
+ }
232
+ await fs.writeFile("package.json", JSON.stringify(manifest, null, 2));
233
+ return workspaces;
234
+ }
235
+ async function removeFiles(...files) {
236
+ await Promise.all(
237
+ files.map(
238
+ (file) => (
239
+ // Remove the file if it exists, fail silently if it doesn't.
240
+ fs.rm(file, { force: true, recursive: true }).catch(() => void 0)
241
+ )
242
+ )
243
+ );
244
+ }
245
+ async function replacePMOccurrences() {
246
+ const logger2 = getLogger4(["dx-cli", "codemod"]);
247
+ logger2.info("Replacing yarn and npm occurrences in files...");
248
+ const results = await replaceInFile3({
249
+ allowEmptyPaths: true,
250
+ files: ["**/*.json", "**/*.md", "**/Dockerfile", "**/docker-compose.yml"],
251
+ from: [
252
+ "https://yarnpkg.com/",
253
+ "https://classic.yarnpkg.com/",
254
+ /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) (\S+)\b/g,
255
+ /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) /g,
256
+ /\b(yarn install --immutable|npm ci)\b/g,
257
+ /\b(yarn -q dlx|npx)\b/g,
258
+ /\b(Yarn|npm)\b/gi
259
+ ],
260
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
261
+ to: [
262
+ "https://pnpm.io/",
263
+ "https://pnpm.io/",
264
+ "pnpm --filter $1",
265
+ "pnpm --filter <package-selector>",
266
+ "pnpm install --frozen-lockfile",
267
+ "pnpm dlx",
268
+ "pnpm"
269
+ ]
270
+ });
271
+ const count = results.reduce(
272
+ (acc, file) => file.hasChanged ? acc + 1 : acc,
273
+ 0
274
+ );
275
+ logger2.info("Replaced yarn occurrences in {count} files", { count });
276
+ }
277
+ async function updateDXWorkflows() {
278
+ const logger2 = getLogger4(["dx-cli", "codemod"]);
279
+ logger2.info("Updating Github Workflows workflows...");
280
+ const sha = await getLatestCommitShaOrRef("pagopa", "dx");
281
+ const ignore = await updateJSCodeReview(sha);
282
+ await replaceInFile3({
283
+ allowEmptyPaths: true,
284
+ files: [".github/workflows/*.yaml"],
285
+ ignore,
286
+ processor: migrateWorkflow(sha)
287
+ });
288
+ }
289
+ async function writePnpmWorkspaceFile(workspaces, packageExtensions) {
290
+ const pnpmWorkspace = {
291
+ packageExtensions,
292
+ packages: workspaces.length > 0 ? workspaces : ["apps/*", "packages/*"]
293
+ };
294
+ const yamlContent = YAML2.stringify(pnpmWorkspace);
295
+ await fs.writeFile("pnpm-workspace.yaml", yamlContent, "utf-8");
296
+ }
297
+ var apply = async (info) => {
298
+ if (info.packageManager === "pnpm") {
299
+ throw new Error("Project is already using pnpm");
300
+ }
301
+ const pm = info.packageManager === "yarn" ? new Yarn() : new NPM();
302
+ const logger2 = getLogger4(["dx-cli", "codemod"]);
303
+ const localWorkspaces = await pm.listWorkspaces();
304
+ logger2.info("Using the {protocol} protocol for local dependencies", {
305
+ protocol: "workspace:"
306
+ });
307
+ if (localWorkspaces.length > 0) {
308
+ await replaceInFile3({
309
+ allowEmptyPaths: true,
310
+ files: ["**/package.json"],
311
+ from: localWorkspaces.map((ws) => new RegExp(`"${ws}": ".*?"`, "g")),
312
+ to: localWorkspaces.map((ws) => `"${ws}": "workspace:^"`)
313
+ });
314
+ }
315
+ logger2.info("Remove unused fields from {file}", {
316
+ file: "package.json"
317
+ });
318
+ const workspaces = await preparePackageJsonForPnpm();
319
+ const packageExtensions = info.packageManager === "yarn" ? await extractPackageExtensions() : void 0;
320
+ logger2.info("Create {file}", {
321
+ file: "pnpm-workspace.yaml"
322
+ });
323
+ await writePnpmWorkspaceFile(workspaces, packageExtensions);
324
+ await $`corepack pnpm@latest add --config pnpm-plugin-pagopa`;
325
+ logger2.info("Remove node_modules and yarn files");
326
+ await removeFiles(
327
+ ".yarnrc",
328
+ ".yarnrc.yml",
329
+ "yarn.config.cjs",
330
+ ".yarn",
331
+ ".pnp.cjs",
332
+ ".pnp.loader.cjs",
333
+ "node_modules"
334
+ );
335
+ const stat2 = await fs.stat(pm.lockFileName);
336
+ if (stat2.isFile()) {
337
+ logger2.info("Importing {source} to {target}", {
338
+ source: pm.lockFileName,
339
+ target: "pnpm-lock.yaml"
340
+ });
341
+ await $`corepack pnpm@latest import ${pm.lockFileName}`;
342
+ await removeFiles(pm.lockFileName);
343
+ } else {
344
+ logger2.info("No {source} file found, skipping import.", {
345
+ source: pm.lockFileName
346
+ });
347
+ }
348
+ await replacePMOccurrences();
349
+ await updateDXWorkflows();
350
+ logger2.info("Setting pnpm as the package manager...");
351
+ await $`corepack use pnpm@latest`;
352
+ };
353
+ var use_pnpm_default = {
354
+ apply,
355
+ description: "Migrate the project to use pnpm as the package manager",
356
+ id: "use-pnpm"
357
+ };
358
+
37
359
  // src/adapters/codemods/index.ts
38
360
  var registry = new LocalCodemodRegistry();
39
- registry.add(example_default);
361
+ registry.add(use_pnpm_default);
362
+ registry.add(useAzureAppsvc);
363
+ registry.add(updateCodeReview);
40
364
  var codemods_default = registry;
41
365
 
42
366
  // src/adapters/commander/index.ts
43
367
  import { Command as Command6 } from "commander";
44
368
 
45
369
  // src/adapters/commander/commands/codemod.ts
370
+ import { getLogger as getLogger5 } from "@logtape/logtape";
46
371
  import { Command } from "commander";
47
372
  var makeCodemodCommand = ({
48
373
  applyCodemodById: applyCodemodById2,
@@ -50,16 +375,14 @@ var makeCodemodCommand = ({
50
375
  }) => new Command("codemod").description("Manage and apply migration scripts to the repository").addCommand(
51
376
  new Command("list").description("List available migration scripts").action(async function() {
52
377
  await listCodemods2().andTee(
53
- (codemods) => (
54
- // eslint-disable-next-line no-console
55
- console.table(codemods, ["id", "description"])
56
- )
378
+ (codemods) => console.table(codemods, ["id", "description"])
57
379
  ).orTee((error) => this.error(error.message));
58
380
  })
59
381
  ).addCommand(
60
382
  new Command("apply").argument("<id>", "The id of the codemod to apply").description("Apply migration scripts to the repository").action(async function(id) {
383
+ const logger2 = getLogger5(["dx-cli", "codemod"]);
61
384
  await applyCodemodById2(id).andTee(() => {
62
- console.log("Codemod applied successfully \u2705");
385
+ logger2.info("Codemod applied \u2705");
63
386
  }).orTee((error) => this.error(error.message));
64
387
  })
65
388
  );
@@ -140,7 +463,7 @@ var checkMonorepoScripts = async (dependencies, config2) => {
140
463
 
141
464
  // src/domain/repository.ts
142
465
  import { ok as ok2 } from "neverthrow";
143
- import fs from "path";
466
+ import fs2 from "path";
144
467
  import coerce from "semver/functions/coerce.js";
145
468
  import semverGte from "semver/functions/gte.js";
146
469
  var isVersionValid = (version, minVersion) => {
@@ -155,7 +478,7 @@ var checkPreCommitConfig = async (dependencies, config2) => {
155
478
  const { repositoryReader: repositoryReader2 } = dependencies;
156
479
  const checkName = "Pre-commit Configuration";
157
480
  const preCommitResult = await repositoryReader2.fileExists(
158
- fs.join(config2.repository.root, ".pre-commit-config.yaml")
481
+ fs2.join(config2.repository.root, ".pre-commit-config.yaml")
159
482
  );
160
483
  if (preCommitResult.isOk() && preCommitResult.value) {
161
484
  return ok2({
@@ -176,7 +499,7 @@ var checkTurboConfig = async (dependencies, config2) => {
176
499
  const checkName = "Turbo Configuration";
177
500
  const repoRoot2 = config2.repository.root;
178
501
  const turboResult = await repositoryReader2.fileExists(
179
- fs.join(repoRoot2, "turbo.json")
502
+ fs2.join(repoRoot2, "turbo.json")
180
503
  );
181
504
  if (turboResult.isErr()) {
182
505
  return ok2({
@@ -315,7 +638,7 @@ var makeDoctorCommand = (dependencies, config2) => new Command2().name("doctor")
315
638
  import { Command as Command3 } from "commander";
316
639
 
317
640
  // src/domain/info.ts
318
- import { getLogger } from "@logtape/logtape";
641
+ import { getLogger as getLogger6 } from "@logtape/logtape";
319
642
  import { join } from "path";
320
643
  var detectFromLockFile = async (dependencies, config2) => {
321
644
  const { repositoryReader: repositoryReader2 } = dependencies;
@@ -341,7 +664,7 @@ var detectPackageManager = async (dependencies, config2) => {
341
664
  var detectNodeVersion = async ({ repositoryReader: repositoryReader2 }, nodeVersionFilePath) => await repositoryReader2.readFile(nodeVersionFilePath).map((nodeVersion) => nodeVersion.trim()).unwrapOr(void 0);
342
665
  var detectTerraformVersion = async ({ repositoryReader: repositoryReader2 }, terraformVersionFilePath) => await repositoryReader2.readFile(terraformVersionFilePath).map((tfVersion) => tfVersion.trim()).unwrapOr(void 0);
343
666
  var detectTurboVersion = ({ devDependencies }) => devDependencies.get("turbo")?.trim();
344
- var getInfo = async (dependencies, config2) => ({
667
+ var getInfo = (dependencies, config2) => async () => ({
345
668
  node: await detectNodeVersion(
346
669
  { repositoryReader: dependencies.repositoryReader },
347
670
  `${config2.repository.root}/.node-version`
@@ -354,13 +677,13 @@ var getInfo = async (dependencies, config2) => ({
354
677
  turbo: detectTurboVersion(dependencies.packageJson)
355
678
  });
356
679
  var printInfo = (result) => {
357
- const logger2 = getLogger("json");
680
+ const logger2 = getLogger6("json");
358
681
  logger2.info(JSON.stringify(result));
359
682
  };
360
683
 
361
684
  // src/adapters/commander/commands/info.ts
362
685
  var makeInfoCommand = (dependencies, config2) => new Command3().name("info").description("Display information about the project").action(async () => {
363
- const result = await getInfo(dependencies, config2);
686
+ const result = await getInfo(dependencies, config2)();
364
687
  printInfo(result);
365
688
  });
366
689
 
@@ -402,23 +725,21 @@ var makeInitCommand = () => new Command4().name("init").description(
402
725
  import { Command as Command5 } from "commander";
403
726
 
404
727
  // src/domain/version.ts
405
- import { getLogger as getLogger2 } from "@logtape/logtape";
728
+ import { getLogger as getLogger7 } from "@logtape/logtape";
406
729
  function printVersion() {
407
- const logger2 = getLogger2(["dx-cli", "version"]);
408
- logger2.info(`dx CLI version: ${"0.6.0"}`);
730
+ const logger2 = getLogger7(["dx-cli", "version"]);
731
+ logger2.info(`dx CLI version: ${"0.8.0"}`);
409
732
  }
410
733
 
411
734
  // src/adapters/commander/commands/version.ts
412
735
  var makeVersionCommand = () => new Command5().name("version").alias("v").action(() => printVersion());
413
736
 
414
737
  // src/adapters/commander/index.ts
415
- var makeCli = (deps2, config2) => {
738
+ var makeCli = (deps2, config2, cliDeps) => {
416
739
  const program2 = new Command6();
417
- program2.name("dx").description("The CLI for DX-Platform").version("0.6.0");
740
+ program2.name("dx").description("The CLI for DX-Platform").version("0.8.0");
418
741
  program2.addCommand(makeDoctorCommand(deps2, config2));
419
- if (process.env.ENABLE_CODEMODS) {
420
- program2.addCommand(makeCodemodCommand(deps2));
421
- }
742
+ program2.addCommand(makeCodemodCommand(cliDeps));
422
743
  if (process.env.ENABLE_INIT_COMMAND) {
423
744
  program2.addCommand(makeInitCommand());
424
745
  }
@@ -428,9 +749,9 @@ var makeCli = (deps2, config2) => {
428
749
  };
429
750
 
430
751
  // src/adapters/logtape/validation-reporter.ts
431
- import { getLogger as getLogger3 } from "@logtape/logtape";
752
+ import { getLogger as getLogger8 } from "@logtape/logtape";
432
753
  var makeValidationReporter = () => {
433
- const logger2 = getLogger3(["dx-cli", "validation"]);
754
+ const logger2 = getLogger8(["dx-cli", "validation"]);
434
755
  return {
435
756
  reportCheckResult(result) {
436
757
  if (result.isValid) {
@@ -448,7 +769,7 @@ import * as process3 from "process";
448
769
 
449
770
  // src/adapters/node/fs/file-reader.ts
450
771
  import { ResultAsync as ResultAsync6 } from "neverthrow";
451
- import fs2 from "fs/promises";
772
+ import fs3 from "fs/promises";
452
773
 
453
774
  // src/adapters/zod/index.ts
454
775
  import { ResultAsync as ResultAsync5 } from "neverthrow";
@@ -465,13 +786,13 @@ var parseJson = Result2.fromThrowable(
465
786
  );
466
787
 
467
788
  // src/adapters/node/fs/file-reader.ts
468
- var readFile = (filePath) => ResultAsync6.fromPromise(
469
- fs2.readFile(filePath, "utf-8"),
789
+ var readFile2 = (filePath) => ResultAsync6.fromPromise(
790
+ fs3.readFile(filePath, "utf-8"),
470
791
  (cause) => new Error(`Failed to read file: ${filePath}`, { cause })
471
792
  );
472
- var readFileAndDecode = (filePath, schema) => readFile(filePath).andThen(parseJson).andThen(decode(schema));
793
+ var readFileAndDecode = (filePath, schema) => readFile2(filePath).andThen(parseJson).andThen(decode(schema));
473
794
  var fileExists = (path2) => ResultAsync6.fromPromise(
474
- fs2.stat(path2),
795
+ fs3.stat(path2),
475
796
  () => new Error(`${path2} not found.`)
476
797
  ).map(() => true);
477
798
 
@@ -501,7 +822,7 @@ var makePackageJsonReader = () => ({
501
822
 
502
823
  // src/adapters/node/repository.ts
503
824
  import * as glob from "glob";
504
- import { okAsync as okAsync3, ResultAsync as ResultAsync7 } from "neverthrow";
825
+ import { okAsync as okAsync2, ResultAsync as ResultAsync7 } from "neverthrow";
505
826
  import * as path from "path";
506
827
  import { z as z3 } from "zod/v4";
507
828
 
@@ -535,11 +856,11 @@ var resolveWorkspacePattern = (repoRoot2, pattern) => ResultAsync7.fromPromise(
535
856
  subDirectories.map((directory) => path.join(repoRoot2, directory))
536
857
  )
537
858
  );
538
- var getWorkspaces = (repoRoot2) => readFile(path.join(repoRoot2, "pnpm-workspace.yaml")).andThen(parseYaml).andThen(
859
+ var getWorkspaces = (repoRoot2) => readFile2(path.join(repoRoot2, "pnpm-workspace.yaml")).andThen(parseYaml).andThen(
539
860
  (obj) => (
540
861
  // If no packages are defined, go on with an empty array
541
862
  decode(z3.object({ packages: z3.array(z3.string()) }))(obj).orElse(
542
- () => okAsync3({ packages: [] })
863
+ () => okAsync2({ packages: [] })
543
864
  )
544
865
  )
545
866
  ).andThen(
@@ -567,7 +888,7 @@ var makeRepositoryReader = () => ({
567
888
  fileExists,
568
889
  findRepositoryRoot,
569
890
  getWorkspaces,
570
- readFile
891
+ readFile: readFile2
571
892
  });
572
893
 
573
894
  // src/config.ts
@@ -581,10 +902,23 @@ var getConfig = (repositoryRoot2) => ({
581
902
  });
582
903
 
583
904
  // src/use-cases/apply-codemod.ts
584
- import { errAsync, okAsync as okAsync4 } from "neverthrow";
585
- var applyCodemodById = (registry2) => (id) => registry2.getById(id).andThen(
586
- (codemod) => codemod ? okAsync4(codemod) : errAsync(new Error(`Codemod with id ${id} not found`))
587
- ).andThen((codemod) => codemod.apply());
905
+ import { errAsync, okAsync as okAsync3, ResultAsync as ResultAsync8 } from "neverthrow";
906
+ var getCodemodById = (registry2, id) => registry2.getById(id).andThen(
907
+ (codemod) => codemod ? okAsync3(codemod) : errAsync(new Error(`Codemod with id ${id} not found`))
908
+ );
909
+ var safeGetInfo = (getInfo2) => ResultAsync8.fromPromise(
910
+ getInfo2(),
911
+ (error) => new Error("Failed to get info", { cause: error })
912
+ );
913
+ var applyCodemodById = (registry2, getInfo2) => (id) => ResultAsync8.combine([
914
+ safeGetInfo(getInfo2),
915
+ getCodemodById(registry2, id)
916
+ ]).andThen(
917
+ ([info, codemod]) => ResultAsync8.fromPromise(codemod.apply(info), (error) => {
918
+ const message = error instanceof Error ? `: ${error.message}` : "";
919
+ return new Error("Failed to apply codemod" + message, { cause: error });
920
+ })
921
+ );
588
922
 
589
923
  // src/use-cases/list-codemods.ts
590
924
  var listCodemods = (registry2) => () => registry2.getAll();
@@ -607,7 +941,7 @@ await configure({
607
941
  }
608
942
  }
609
943
  });
610
- var logger = getLogger4(["dx-cli"]);
944
+ var logger = getLogger9(["dx-cli"]);
611
945
  var repositoryReader = makeRepositoryReader();
612
946
  var packageJsonReader = makePackageJsonReader();
613
947
  var validationReporter = makeValidationReporter();
@@ -626,13 +960,15 @@ if (repoPackageJson.isErr()) {
626
960
  }
627
961
  var packageJson = repoPackageJson.value;
628
962
  var deps = {
629
- applyCodemodById: applyCodemodById(codemods_default),
630
- listCodemods: listCodemods(codemods_default),
631
963
  packageJson,
632
964
  packageJsonReader,
633
965
  repositoryReader,
634
966
  validationReporter
635
967
  };
636
968
  var config = getConfig(repositoryRoot);
637
- var program = makeCli(deps, config);
969
+ var useCases = {
970
+ applyCodemodById: applyCodemodById(codemods_default, getInfo(deps, config)),
971
+ listCodemods: listCodemods(codemods_default)
972
+ };
973
+ var program = makeCli(deps, config, useCases);
638
974
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-cli",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "description": "A CLI useful to manage DX tools.",
6
6
  "repository": {
@@ -22,13 +22,16 @@
22
22
  "@logtape/logtape": "^1.0.0",
23
23
  "commander": "^14.0.0",
24
24
  "core-js": "^3.44.0",
25
+ "execa": "^9.6.0",
25
26
  "glob": "^11.0.3",
26
27
  "neverthrow": "^8.2.0",
27
28
  "node-plop": "^0.32.1",
29
+ "octokit": "^5.0.3",
30
+ "replace-in-file": "^8.3.0",
28
31
  "semver": "^7.7.2",
29
32
  "yaml": "^2.8.0",
30
33
  "zod": "^3.25.28",
31
- "@pagopa/monorepo-generator": "^0.6.0"
34
+ "@pagopa/monorepo-generator": "^0.8.0"
32
35
  },
33
36
  "devDependencies": {
34
37
  "@tsconfig/node22": "22.0.2",