@pagopa/dx-cli 0.7.0 → 0.8.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.
Files changed (2) hide show
  1. package/bin/index.js +154 -58
  2. package/package.json +2 -2
package/bin/index.js CHANGED
@@ -25,6 +25,7 @@ var LocalCodemodRegistry = class {
25
25
  // src/adapters/codemods/update-code-review.ts
26
26
  import { getLogger as getLogger2 } from "@logtape/logtape";
27
27
  import { replaceInFile } from "replace-in-file";
28
+ import * as YAML2 from "yaml";
28
29
 
29
30
  // src/adapters/codemods/git.ts
30
31
  import { getLogger } from "@logtape/logtape";
@@ -49,20 +50,48 @@ var getLatestCommitShaOrRef = async (owner, repo, ref = "main") => {
49
50
  });
50
51
  };
51
52
 
53
+ // src/adapters/codemods/yaml.ts
54
+ import * as YAML from "yaml";
55
+ var isChildOf = (path2, key) => {
56
+ const ancestor = path2.at(-1);
57
+ return YAML.isPair(ancestor) && YAML.isScalar(ancestor.key) && typeof ancestor.key.value === "string" && ancestor.key.value === key;
58
+ };
59
+
52
60
  // src/adapters/codemods/update-code-review.ts
53
- var updateJSCodeReview = async (sha) => {
61
+ var updateJSCodeReviewJob = (sha) => (workflow, filename) => {
54
62
  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 });
63
+ const document = YAML2.parseDocument(workflow);
64
+ let updated = false;
65
+ YAML2.visit(document, {
66
+ Map(_, map, path2) {
67
+ if (map.has("jobs") || isChildOf(path2, "jobs")) {
68
+ return void 0;
69
+ }
70
+ if (map.has("uses")) {
71
+ const uses = map.get("uses");
72
+ if (typeof uses === "string" && uses.startsWith("pagopa/dx/.github/workflows/js_code_review.yaml@")) {
73
+ map.set("secrets", "inherit");
74
+ map.set("permissions", {
75
+ contents: "read",
76
+ "pull-requests": "write"
77
+ });
78
+ map.set(
79
+ "uses",
80
+ `pagopa/dx/.github/workflows/js_code_review.yaml@${sha}`
81
+ );
82
+ updated = true;
83
+ }
84
+ }
85
+ return YAML2.visit.SKIP;
86
+ }
64
87
  });
65
- return updated;
88
+ if (updated) {
89
+ logger2.info("Workflow {filename} updated", {
90
+ filename
91
+ });
92
+ return YAML2.stringify(document);
93
+ }
94
+ return workflow;
66
95
  };
67
96
  var updateCodeReview = {
68
97
  apply: async () => {
@@ -70,13 +99,16 @@ var updateCodeReview = {
70
99
  const owner = "pagopa";
71
100
  const repo = "dx";
72
101
  return getLatestCommitSha(owner, repo).then(async (sha) => {
73
- await updateJSCodeReview(sha);
102
+ await replaceInFile({
103
+ allowEmptyPaths: true,
104
+ files: [".github/workflows/*.yaml"],
105
+ processor: updateJSCodeReviewJob(sha)
106
+ });
74
107
  }).catch(() => {
75
108
  logger2.error(
76
- "Failed to fetch the latest commit sha from {owner}/{repo}",
109
+ "Failed to fetch the latest commit sha from {repository}",
77
110
  {
78
- owner,
79
- repo
111
+ repository: `${owner}/${repo}`
80
112
  }
81
113
  );
82
114
  });
@@ -88,17 +120,13 @@ var updateCodeReview = {
88
120
  // src/adapters/codemods/use-azure-appsvc.ts
89
121
  import { getLogger as getLogger3 } from "@logtape/logtape";
90
122
  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
- };
123
+ import YAML3 from "yaml";
96
124
  var migrateWorkflow = (sha) => (workflow, filename) => {
97
125
  const logger2 = getLogger3(["dx-cli", "codemod"]);
98
126
  logger2.debug("Processing {filename} file", { filename });
99
- const document = YAML.parseDocument(workflow);
127
+ const document = YAML3.parseDocument(workflow);
100
128
  let updated = false;
101
- YAML.visit(document, {
129
+ YAML3.visit(document, {
102
130
  Map(_, map, path2) {
103
131
  if (isChildOf(path2, "jobs") || isChildOf(path2, "with")) {
104
132
  return void 0;
@@ -113,28 +141,33 @@ var migrateWorkflow = (sha) => (workflow, filename) => {
113
141
  )) {
114
142
  logger2.debug("Adding disable_auto_staging_deploy");
115
143
  map.addIn(["with", "disable_auto_staging_deploy"], true);
144
+ map.set("permissions", {
145
+ attestations: "write",
146
+ contents: "read",
147
+ "id-token": "write"
148
+ });
116
149
  updated = true;
117
150
  return void 0;
118
151
  }
119
152
  }
120
- return YAML.visit.SKIP;
153
+ return YAML3.visit.SKIP;
121
154
  },
122
155
  Pair(_, pair) {
123
- if (YAML.isScalar(pair.key)) {
156
+ if (YAML3.isScalar(pair.key)) {
124
157
  if (pair.key.value === "function_app_name") {
125
158
  updated = true;
126
159
  logger2.debug("Updating function_app_name to web_app_name");
127
- return new YAML.Pair("web_app_name", pair.value);
160
+ return new YAML3.Pair("web_app_name", pair.value);
128
161
  }
129
162
  if (pair.key.value === "use_staging_slot") {
130
163
  updated = true;
131
164
  logger2.debug("Removing use_staging_slot");
132
- return YAML.visit.REMOVE;
165
+ return YAML3.visit.REMOVE;
133
166
  }
134
167
  if (pair.key.value === "uses") {
135
168
  updated = true;
136
169
  logger2.debug("Updating uses value");
137
- return new YAML.Pair(
170
+ return new YAML3.Pair(
138
171
  "uses",
139
172
  `pagopa/dx/.github/workflows/release-azure-appsvc.yaml@${sha}`
140
173
  );
@@ -146,7 +179,7 @@ var migrateWorkflow = (sha) => (workflow, filename) => {
146
179
  logger2.info("Workflow {filename} updated", {
147
180
  filename
148
181
  });
149
- return YAML.stringify(document);
182
+ return YAML3.stringify(document);
150
183
  }
151
184
  logger2.debug("No changes applied to {filename}", { filename });
152
185
  return workflow;
@@ -169,7 +202,48 @@ import { getLogger as getLogger4 } from "@logtape/logtape";
169
202
  import { $ } from "execa";
170
203
  import * as fs from "fs/promises";
171
204
  import { replaceInFile as replaceInFile3 } from "replace-in-file";
172
- import YAML2 from "yaml";
205
+ import YAML4 from "yaml";
206
+ var NPM = class {
207
+ lockFileName = "package-lock.json";
208
+ async listWorkspaces() {
209
+ const { stdout } = await $`npm query .workspace`;
210
+ const workspaces = JSON.parse(stdout);
211
+ const workspaceNames = [];
212
+ if (Array.isArray(workspaces)) {
213
+ for (const ws of workspaces) {
214
+ if (Object.hasOwn(ws, "name")) {
215
+ workspaceNames.push(ws.name);
216
+ }
217
+ }
218
+ }
219
+ return workspaceNames;
220
+ }
221
+ };
222
+ var Yarn = class {
223
+ lockFileName = "yarn.lock";
224
+ async listWorkspaces() {
225
+ const { stdout } = await $({ lines: true })`yarn workspaces list --json`;
226
+ const workspaceNames = [];
227
+ for (const line of stdout) {
228
+ const ws = JSON.parse(line);
229
+ if (Object.hasOwn(ws, "name")) {
230
+ workspaceNames.push(ws.name);
231
+ }
232
+ }
233
+ return workspaceNames;
234
+ }
235
+ };
236
+ async function extractPackageExtensions() {
237
+ try {
238
+ const yarnrc = await fs.readFile(".yarnrc.yml", "utf-8");
239
+ const parsed = YAML4.parse(yarnrc);
240
+ if (parsed.packageExtensions) {
241
+ return parsed.packageExtensions;
242
+ }
243
+ } catch {
244
+ }
245
+ return void 0;
246
+ }
173
247
  async function preparePackageJsonForPnpm() {
174
248
  const packageJson2 = await fs.readFile("package.json", "utf-8");
175
249
  const manifest = JSON.parse(packageJson2);
@@ -196,20 +270,20 @@ async function removeFiles(...files) {
196
270
  )
197
271
  );
198
272
  }
199
- async function replaceYarnOccurrences() {
273
+ async function replacePMOccurrences() {
200
274
  const logger2 = getLogger4(["dx-cli", "codemod"]);
201
- logger2.info("Replacing yarn occurrences in files...");
275
+ logger2.info("Replacing yarn and npm occurrences in files...");
202
276
  const results = await replaceInFile3({
203
277
  allowEmptyPaths: true,
204
278
  files: ["**/*.json", "**/*.md", "**/Dockerfile", "**/docker-compose.yml"],
205
279
  from: [
206
280
  "https://yarnpkg.com/",
207
281
  "https://classic.yarnpkg.com/",
208
- /yarn workspace (\S+)/g,
209
- /yarn workspace/g,
210
- /yarn install --immutable/g,
211
- /yarn -q dlx/g,
212
- /Yarn/gi
282
+ /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) (\S+)\b/g,
283
+ /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) /g,
284
+ /\b(yarn install --immutable|npm ci)\b/g,
285
+ /\b(yarn -q dlx|npx)\b/g,
286
+ /\b(Yarn|npm)\b/gi
213
287
  ],
214
288
  ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
215
289
  to: [
@@ -232,7 +306,12 @@ async function updateDXWorkflows() {
232
306
  const logger2 = getLogger4(["dx-cli", "codemod"]);
233
307
  logger2.info("Updating Github Workflows workflows...");
234
308
  const sha = await getLatestCommitShaOrRef("pagopa", "dx");
235
- const ignore = await updateJSCodeReview(sha);
309
+ const results = await replaceInFile3({
310
+ allowEmptyPaths: true,
311
+ files: [".github/workflows/*.yaml"],
312
+ processor: updateJSCodeReviewJob(sha)
313
+ });
314
+ const ignore = results.filter((r) => !r.hasChanged).map((r) => r.file);
236
315
  await replaceInFile3({
237
316
  allowEmptyPaths: true,
238
317
  files: [".github/workflows/*.yaml"],
@@ -240,26 +319,42 @@ async function updateDXWorkflows() {
240
319
  processor: migrateWorkflow(sha)
241
320
  });
242
321
  }
243
- async function writePnpmWorkspaceFile(workspaces) {
322
+ async function writePnpmWorkspaceFile(workspaces, packageExtensions) {
244
323
  const pnpmWorkspace = {
324
+ packageExtensions,
245
325
  packages: workspaces.length > 0 ? workspaces : ["apps/*", "packages/*"]
246
326
  };
247
- const yamlContent = YAML2.stringify(pnpmWorkspace);
327
+ const yamlContent = YAML4.stringify(pnpmWorkspace);
248
328
  await fs.writeFile("pnpm-workspace.yaml", yamlContent, "utf-8");
249
329
  }
250
330
  var apply = async (info) => {
251
331
  if (info.packageManager === "pnpm") {
252
332
  throw new Error("Project is already using pnpm");
253
333
  }
334
+ const pm = info.packageManager === "yarn" ? new Yarn() : new NPM();
254
335
  const logger2 = getLogger4(["dx-cli", "codemod"]);
336
+ const localWorkspaces = await pm.listWorkspaces();
337
+ logger2.info("Using the {protocol} protocol for local dependencies", {
338
+ protocol: "workspace:"
339
+ });
340
+ if (localWorkspaces.length > 0) {
341
+ await replaceInFile3({
342
+ allowEmptyPaths: true,
343
+ files: ["**/package.json"],
344
+ from: localWorkspaces.map((ws) => new RegExp(`"${ws}": ".*?"`, "g")),
345
+ to: localWorkspaces.map((ws) => `"${ws}": "workspace:^"`)
346
+ });
347
+ }
255
348
  logger2.info("Remove unused fields from {file}", {
256
349
  file: "package.json"
257
350
  });
258
351
  const workspaces = await preparePackageJsonForPnpm();
352
+ const packageExtensions = info.packageManager === "yarn" ? await extractPackageExtensions() : void 0;
259
353
  logger2.info("Create {file}", {
260
354
  file: "pnpm-workspace.yaml"
261
355
  });
262
- await writePnpmWorkspaceFile(workspaces);
356
+ await writePnpmWorkspaceFile(workspaces, packageExtensions);
357
+ await $`corepack pnpm@latest add --config pnpm-plugin-pagopa`;
263
358
  logger2.info("Remove node_modules and yarn files");
264
359
  await removeFiles(
265
360
  ".yarnrc",
@@ -270,26 +365,29 @@ var apply = async (info) => {
270
365
  ".pnp.loader.cjs",
271
366
  "node_modules"
272
367
  );
273
- logger2.info("Importing {source} to {target}", {
274
- source: "yarn.lock",
275
- target: "pnpm-lock.yaml"
276
- });
277
- try {
278
- await fs.access("yarn.lock");
279
- await $`corepack pnpm@latest import yarn.lock`;
280
- await removeFiles("yarn.lock");
281
- } catch {
282
- logger2.info("No yarn.lock file found, skipping import.");
368
+ const stat2 = await fs.stat(pm.lockFileName);
369
+ if (stat2.isFile()) {
370
+ logger2.info("Importing {source} to {target}", {
371
+ source: pm.lockFileName,
372
+ target: "pnpm-lock.yaml"
373
+ });
374
+ await $`corepack pnpm@latest import ${pm.lockFileName}`;
375
+ await removeFiles(pm.lockFileName);
376
+ } else {
377
+ logger2.info("No {source} file found, skipping import.", {
378
+ source: pm.lockFileName
379
+ });
283
380
  }
284
- await $`corepack pnpm@latest add --config pnpm-plugin-pagopa`;
285
- await replaceYarnOccurrences();
381
+ await replacePMOccurrences();
286
382
  await updateDXWorkflows();
383
+ logger2.info("Adding pnpm store to .gitignore...");
384
+ await fs.appendFile(".gitignore", "\n\n# PNPM\n.pnpm-store");
287
385
  logger2.info("Setting pnpm as the package manager...");
288
386
  await $`corepack use pnpm@latest`;
289
387
  };
290
388
  var use_pnpm_default = {
291
389
  apply,
292
- description: "A codemod that switches the project to use pnpm",
390
+ description: "Migrate the project to use pnpm as the package manager",
293
391
  id: "use-pnpm"
294
392
  };
295
393
 
@@ -665,7 +763,7 @@ import { Command as Command5 } from "commander";
665
763
  import { getLogger as getLogger7 } from "@logtape/logtape";
666
764
  function printVersion() {
667
765
  const logger2 = getLogger7(["dx-cli", "version"]);
668
- logger2.info(`dx CLI version: ${"0.7.0"}`);
766
+ logger2.info(`dx CLI version: ${"0.8.1"}`);
669
767
  }
670
768
 
671
769
  // src/adapters/commander/commands/version.ts
@@ -674,11 +772,9 @@ var makeVersionCommand = () => new Command5().name("version").alias("v").action(
674
772
  // src/adapters/commander/index.ts
675
773
  var makeCli = (deps2, config2, cliDeps) => {
676
774
  const program2 = new Command6();
677
- program2.name("dx").description("The CLI for DX-Platform").version("0.7.0");
775
+ program2.name("dx").description("The CLI for DX-Platform").version("0.8.1");
678
776
  program2.addCommand(makeDoctorCommand(deps2, config2));
679
- if (process.env.ENABLE_CODEMODS) {
680
- program2.addCommand(makeCodemodCommand(cliDeps));
681
- }
777
+ program2.addCommand(makeCodemodCommand(cliDeps));
682
778
  if (process.env.ENABLE_INIT_COMMAND) {
683
779
  program2.addCommand(makeInitCommand());
684
780
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/dx-cli",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "description": "A CLI useful to manage DX tools.",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "semver": "^7.7.2",
32
32
  "yaml": "^2.8.0",
33
33
  "zod": "^3.25.28",
34
- "@pagopa/monorepo-generator": "^0.6.0"
34
+ "@pagopa/monorepo-generator": "^0.8.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@tsconfig/node22": "22.0.2",