@pagopa/dx-cli 0.15.2 → 0.15.3

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 (195) hide show
  1. package/bin/index.js +8 -1280
  2. package/dist/adapters/azure/__tests__/cloud-account-repository.test.d.ts +1 -0
  3. package/dist/adapters/azure/__tests__/cloud-account-repository.test.js +95 -0
  4. package/dist/adapters/azure/__tests__/cloud-account-service.test.d.ts +1 -0
  5. package/dist/adapters/azure/__tests__/cloud-account-service.test.js +95 -0
  6. package/dist/adapters/azure/cloud-account-repository.d.ts +12 -0
  7. package/dist/adapters/azure/cloud-account-repository.js +23 -0
  8. package/dist/adapters/azure/cloud-account-service.d.ts +22 -0
  9. package/dist/adapters/azure/cloud-account-service.js +255 -0
  10. package/dist/adapters/azure/locations.d.ts +7 -0
  11. package/dist/adapters/azure/locations.js +21 -0
  12. package/dist/adapters/codemods/__tests__/registry.test.d.ts +1 -0
  13. package/dist/adapters/codemods/__tests__/registry.test.js +59 -0
  14. package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.d.ts +1 -0
  15. package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.js +77 -0
  16. package/dist/adapters/codemods/__tests__/use-pnpm.test.d.ts +1 -0
  17. package/dist/adapters/codemods/__tests__/use-pnpm.test.js +148 -0
  18. package/dist/adapters/codemods/git.d.ts +2 -0
  19. package/dist/adapters/codemods/git.js +18 -0
  20. package/dist/adapters/codemods/index.d.ts +3 -0
  21. package/dist/adapters/codemods/index.js +9 -0
  22. package/dist/adapters/codemods/registry.d.ts +8 -0
  23. package/dist/adapters/codemods/registry.js +16 -0
  24. package/dist/adapters/codemods/update-code-review.d.ts +3 -0
  25. package/dist/adapters/codemods/update-code-review.js +60 -0
  26. package/dist/adapters/codemods/use-azure-appsvc.d.ts +3 -0
  27. package/dist/adapters/codemods/use-azure-appsvc.js +84 -0
  28. package/dist/adapters/codemods/use-pnpm.d.ts +22 -0
  29. package/dist/adapters/codemods/use-pnpm.js +214 -0
  30. package/dist/adapters/codemods/yaml.d.ts +2 -0
  31. package/dist/adapters/codemods/yaml.js +8 -0
  32. package/dist/adapters/commander/commands/codemod.d.ts +8 -0
  33. package/dist/adapters/commander/commands/codemod.js +22 -0
  34. package/dist/adapters/commander/commands/doctor.d.ts +4 -0
  35. package/dist/adapters/commander/commands/doctor.js +12 -0
  36. package/dist/adapters/commander/commands/info.d.ts +3 -0
  37. package/dist/adapters/commander/commands/info.js +9 -0
  38. package/dist/adapters/commander/commands/init.d.ts +7 -0
  39. package/dist/adapters/commander/commands/init.js +126 -0
  40. package/dist/adapters/commander/commands/savemoney.d.ts +2 -0
  41. package/dist/adapters/commander/commands/savemoney.js +26 -0
  42. package/dist/adapters/commander/index.d.ts +7 -0
  43. package/dist/adapters/commander/index.js +19 -0
  44. package/dist/adapters/execa/terraform.d.ts +27 -0
  45. package/dist/adapters/execa/terraform.js +28 -0
  46. package/dist/adapters/github/__tests__/github-repo.spec.d.ts +1 -0
  47. package/dist/adapters/github/__tests__/github-repo.spec.js +67 -0
  48. package/dist/adapters/github/github-repo.d.ts +2 -0
  49. package/dist/adapters/github/github-repo.js +31 -0
  50. package/dist/adapters/logtape/validation-reporter.d.ts +2 -0
  51. package/dist/adapters/logtape/validation-reporter.js +14 -0
  52. package/dist/adapters/node/__tests__/data.d.ts +18 -0
  53. package/dist/adapters/node/__tests__/data.js +22 -0
  54. package/dist/adapters/node/__tests__/package-json.test.d.ts +1 -0
  55. package/dist/adapters/node/__tests__/package-json.test.js +86 -0
  56. package/dist/adapters/node/__tests__/repository.test.d.ts +1 -0
  57. package/dist/adapters/node/__tests__/repository.test.js +77 -0
  58. package/dist/adapters/node/fs/__tests__/file-reader.test.d.ts +1 -0
  59. package/dist/adapters/node/fs/__tests__/file-reader.test.js +80 -0
  60. package/dist/adapters/node/fs/file-reader.d.ts +24 -0
  61. package/dist/adapters/node/fs/file-reader.js +26 -0
  62. package/dist/adapters/node/json/__tests__/index.test.d.ts +1 -0
  63. package/dist/adapters/node/json/__tests__/index.test.js +14 -0
  64. package/dist/adapters/node/json/index.d.ts +2 -0
  65. package/dist/adapters/node/json/index.js +2 -0
  66. package/dist/adapters/node/package-json.d.ts +2 -0
  67. package/dist/adapters/node/package-json.js +22 -0
  68. package/dist/adapters/node/release.d.ts +2 -0
  69. package/dist/adapters/node/release.js +33 -0
  70. package/dist/adapters/node/repository.d.ts +2 -0
  71. package/dist/adapters/node/repository.js +47 -0
  72. package/dist/adapters/octokit/__tests__/index.test.d.ts +1 -0
  73. package/dist/adapters/octokit/__tests__/index.test.js +197 -0
  74. package/dist/adapters/octokit/index.d.ts +24 -0
  75. package/dist/adapters/octokit/index.js +65 -0
  76. package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.d.ts +1 -0
  77. package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.js +115 -0
  78. package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.d.ts +1 -0
  79. package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.js +116 -0
  80. package/dist/adapters/plop/actions/fetch-github-release.d.ts +12 -0
  81. package/dist/adapters/plop/actions/fetch-github-release.js +20 -0
  82. package/dist/adapters/plop/actions/get-node-version.d.ts +2 -0
  83. package/dist/adapters/plop/actions/get-node-version.js +9 -0
  84. package/dist/adapters/plop/actions/get-terraform-backend.d.ts +3 -0
  85. package/dist/adapters/plop/actions/get-terraform-backend.js +17 -0
  86. package/dist/adapters/plop/actions/init-cloud-accounts.d.ts +5 -0
  87. package/dist/adapters/plop/actions/init-cloud-accounts.js +13 -0
  88. package/dist/adapters/plop/actions/provision-terraform-backend.d.ts +10 -0
  89. package/dist/adapters/plop/actions/provision-terraform-backend.js +16 -0
  90. package/dist/adapters/plop/actions/semver.d.ts +19 -0
  91. package/dist/adapters/plop/actions/semver.js +27 -0
  92. package/dist/adapters/plop/actions/setup-pnpm.d.ts +2 -0
  93. package/dist/adapters/plop/actions/setup-pnpm.js +26 -0
  94. package/dist/adapters/plop/generators/environment/__tests__/actions.test.d.ts +2 -0
  95. package/dist/adapters/plop/generators/environment/__tests__/actions.test.js +55 -0
  96. package/dist/adapters/plop/generators/environment/actions.d.ts +2 -0
  97. package/dist/adapters/plop/generators/environment/actions.js +54 -0
  98. package/dist/adapters/plop/generators/environment/index.d.ts +3 -0
  99. package/dist/adapters/plop/generators/environment/index.js +19 -0
  100. package/dist/adapters/plop/generators/environment/prompts.d.ts +66 -0
  101. package/dist/adapters/plop/generators/environment/prompts.js +166 -0
  102. package/dist/adapters/plop/generators/monorepo/actions.d.ts +51 -0
  103. package/dist/adapters/plop/generators/monorepo/actions.js +35 -0
  104. package/dist/adapters/plop/generators/monorepo/index.d.ts +6 -0
  105. package/dist/adapters/plop/generators/monorepo/index.js +17 -0
  106. package/dist/adapters/plop/generators/monorepo/prompts.d.ts +10 -0
  107. package/dist/adapters/plop/generators/monorepo/prompts.js +31 -0
  108. package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.d.ts +1 -0
  109. package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.js +113 -0
  110. package/dist/adapters/plop/helpers/env-short.d.ts +3 -0
  111. package/dist/adapters/plop/helpers/env-short.js +9 -0
  112. package/dist/adapters/plop/helpers/resource-prefix.d.ts +5 -0
  113. package/dist/adapters/plop/helpers/resource-prefix.js +18 -0
  114. package/dist/adapters/plop/index.d.ts +8 -0
  115. package/dist/adapters/plop/index.js +24 -0
  116. package/dist/adapters/terraform/fmt.d.ts +1 -0
  117. package/dist/adapters/terraform/fmt.js +17 -0
  118. package/dist/adapters/yaml/__tests__/index.test.d.ts +1 -0
  119. package/dist/adapters/yaml/__tests__/index.test.js +53 -0
  120. package/dist/adapters/yaml/index.d.ts +8 -0
  121. package/dist/adapters/yaml/index.js +9 -0
  122. package/dist/adapters/zod/index.d.ts +23 -0
  123. package/dist/adapters/zod/index.js +22 -0
  124. package/dist/config.d.ts +6 -0
  125. package/dist/config.js +5 -0
  126. package/dist/domain/__tests__/data.d.ts +17 -0
  127. package/dist/domain/__tests__/data.js +27 -0
  128. package/dist/domain/__tests__/environment.test.d.ts +1 -0
  129. package/dist/domain/__tests__/environment.test.js +282 -0
  130. package/dist/domain/__tests__/info.test.d.ts +1 -0
  131. package/dist/domain/__tests__/info.test.js +77 -0
  132. package/dist/domain/__tests__/package-json.test.d.ts +1 -0
  133. package/dist/domain/__tests__/package-json.test.js +39 -0
  134. package/dist/domain/__tests__/repository.test.d.ts +1 -0
  135. package/dist/domain/__tests__/repository.test.js +101 -0
  136. package/dist/domain/__tests__/workspace.test.d.ts +1 -0
  137. package/dist/domain/__tests__/workspace.test.js +57 -0
  138. package/dist/domain/cloud-account.d.ts +28 -0
  139. package/dist/domain/cloud-account.js +12 -0
  140. package/dist/domain/codemod.d.ts +11 -0
  141. package/dist/domain/codemod.js +1 -0
  142. package/dist/domain/dependencies.d.ts +10 -0
  143. package/dist/domain/dependencies.js +1 -0
  144. package/dist/domain/doctor.d.ts +10 -0
  145. package/dist/domain/doctor.js +50 -0
  146. package/dist/domain/environment.d.ts +40 -0
  147. package/dist/domain/environment.js +57 -0
  148. package/dist/domain/github-repo.d.ts +6 -0
  149. package/dist/domain/github-repo.js +8 -0
  150. package/dist/domain/github.d.ts +37 -0
  151. package/dist/domain/github.js +29 -0
  152. package/dist/domain/info.d.ts +11 -0
  153. package/dist/domain/info.js +52 -0
  154. package/dist/domain/package-json.d.ts +42 -0
  155. package/dist/domain/package-json.js +69 -0
  156. package/dist/domain/remote-backend.d.ts +8 -0
  157. package/dist/domain/remote-backend.js +9 -0
  158. package/dist/domain/repository.d.ts +13 -0
  159. package/dist/domain/repository.js +72 -0
  160. package/dist/domain/validation.d.ts +16 -0
  161. package/dist/domain/validation.js +1 -0
  162. package/dist/domain/workspace.d.ts +9 -0
  163. package/dist/domain/workspace.js +32 -0
  164. package/dist/index.d.ts +2 -0
  165. package/dist/index.js +35 -0
  166. package/dist/use-cases/__tests__/apply-codemod.test.d.ts +1 -0
  167. package/dist/use-cases/__tests__/apply-codemod.test.js +73 -0
  168. package/dist/use-cases/__tests__/list-codemods.test.d.ts +1 -0
  169. package/dist/use-cases/__tests__/list-codemods.test.js +38 -0
  170. package/dist/use-cases/apply-codemod.d.ts +5 -0
  171. package/dist/use-cases/apply-codemod.js +14 -0
  172. package/dist/use-cases/list-codemods.d.ts +4 -0
  173. package/dist/use-cases/list-codemods.js +1 -0
  174. package/package.json +19 -8
  175. package/templates/environment/bootstrapper/{{env.name}}/data.tf.hbs +13 -0
  176. package/templates/environment/bootstrapper/{{env.name}}/main.tf.hbs +70 -0
  177. package/templates/environment/bootstrapper/{{env.name}}/providers.tf.hbs +26 -0
  178. package/templates/environment/core/{{env.name}}/main.tf.hbs +21 -0
  179. package/templates/environment/core/{{env.name}}/outputs.tf.hbs +8 -0
  180. package/templates/environment/core/{{env.name}}/providers.tf.hbs +21 -0
  181. package/templates/environment/shared/backend.tf.hbs +14 -0
  182. package/templates/environment/shared/locals.tf.hbs +26 -0
  183. package/templates/monorepo/.editorconfig +8 -0
  184. package/templates/monorepo/.node-version.hbs +1 -0
  185. package/templates/monorepo/.pre-commit-config.yaml.hbs +34 -0
  186. package/templates/monorepo/.prettierignore +5 -0
  187. package/templates/monorepo/.terraform-version.hbs +1 -0
  188. package/templates/monorepo/.trivyignore +16 -0
  189. package/templates/monorepo/README.md.hbs +163 -0
  190. package/templates/monorepo/infra/repository/main.tf.hbs +11 -0
  191. package/templates/monorepo/infra/repository/outputs.tf.hbs +11 -0
  192. package/templates/monorepo/infra/repository/providers.tf.hbs +13 -0
  193. package/templates/monorepo/package.json.hbs +7 -0
  194. package/templates/monorepo/pnpm-workspace.yaml +7 -0
  195. package/templates/monorepo/turbo.json +29 -0
package/bin/index.js CHANGED
@@ -1,1263 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/index.ts
4
- import "core-js/actual/set/index.js";
5
3
  import { configure, getConsoleSink } from "@logtape/logtape";
6
- import { Octokit as Octokit3 } from "octokit";
4
+ import { runCli } from "../dist/index.js";
5
+ import packageJson from "../package.json" with { type: "json" };
7
6
 
8
- // src/adapters/codemods/registry.ts
9
- import { okAsync } from "neverthrow";
10
- var LocalCodemodRegistry = class {
11
- #m;
12
- constructor() {
13
- this.#m = /* @__PURE__ */ new Map();
14
- }
15
- add(codemod) {
16
- this.#m.set(codemod.id, codemod);
17
- }
18
- getAll() {
19
- return okAsync(Array.from(this.#m.values()));
20
- }
21
- getById(id) {
22
- return okAsync(this.#m.get(id));
23
- }
24
- };
25
-
26
- // src/adapters/codemods/update-code-review.ts
27
- import { getLogger as getLogger2 } from "@logtape/logtape";
28
- import { replaceInFile } from "replace-in-file";
29
- import * as YAML2 from "yaml";
30
-
31
- // src/adapters/codemods/git.ts
32
- import { getLogger } from "@logtape/logtape";
33
- import { Octokit } from "octokit";
34
- var getLatestCommitSha = async (owner, repo, ref = "main") => {
35
- const octokit2 = new Octokit();
36
- const response = await octokit2.rest.repos.getCommit({
37
- owner,
38
- ref,
39
- repo
40
- });
41
- return response.data.sha;
42
- };
43
- var getLatestCommitShaOrRef = async (owner, repo, ref = "main") => {
44
- const logger = getLogger(["dx-cli", "codemod"]);
45
- return getLatestCommitSha(owner, repo, ref).catch(() => {
46
- logger.warn(
47
- "Failed to fetch the latest commit from {owner}/{repo}, fallback to {fallback}",
48
- { fallback: ref, owner, repo }
49
- );
50
- return ref;
51
- });
52
- };
53
-
54
- // src/adapters/codemods/yaml.ts
55
- import * as YAML from "yaml";
56
- var isChildOf = (path3, key) => {
57
- const ancestor = path3.at(-1);
58
- return YAML.isPair(ancestor) && YAML.isScalar(ancestor.key) && typeof ancestor.key.value === "string" && ancestor.key.value === key;
59
- };
60
-
61
- // src/adapters/codemods/update-code-review.ts
62
- var updateJSCodeReviewJob = (sha) => (workflow, filename) => {
63
- const logger = getLogger2(["dx-cli", "codemod"]);
64
- const document = YAML2.parseDocument(workflow);
65
- let updated = false;
66
- YAML2.visit(document, {
67
- Map(_, map, path3) {
68
- if (map.has("jobs") || isChildOf(path3, "jobs")) {
69
- return void 0;
70
- }
71
- if (map.has("uses")) {
72
- const uses = map.get("uses");
73
- if (typeof uses === "string" && uses.startsWith("pagopa/dx/.github/workflows/js_code_review.yaml@")) {
74
- map.set("secrets", "inherit");
75
- map.set("permissions", {
76
- contents: "read",
77
- "pull-requests": "write"
78
- });
79
- map.set(
80
- "uses",
81
- `pagopa/dx/.github/workflows/js_code_review.yaml@${sha}`
82
- );
83
- updated = true;
84
- }
85
- }
86
- return YAML2.visit.SKIP;
87
- }
88
- });
89
- if (updated) {
90
- logger.info("Workflow {filename} updated", {
91
- filename
92
- });
93
- return YAML2.stringify(document);
94
- }
95
- return workflow;
96
- };
97
- var updateCodeReview = {
98
- apply: async () => {
99
- const logger = getLogger2(["dx-cli", "codemod"]);
100
- const owner = "pagopa";
101
- const repo = "dx";
102
- return getLatestCommitSha(owner, repo).then(async (sha) => {
103
- await replaceInFile({
104
- allowEmptyPaths: true,
105
- files: [".github/workflows/*.yaml"],
106
- processor: updateJSCodeReviewJob(sha)
107
- });
108
- }).catch(() => {
109
- logger.error(
110
- "Failed to fetch the latest commit sha from {repository}",
111
- {
112
- repository: `${owner}/${repo}`
113
- }
114
- );
115
- });
116
- },
117
- description: "Update js_code_review workflow to its latest version",
118
- id: "update-code-review"
119
- };
120
-
121
- // src/adapters/codemods/use-azure-appsvc.ts
122
- import { getLogger as getLogger3 } from "@logtape/logtape";
123
- import { replaceInFile as replaceInFile2 } from "replace-in-file";
124
- import YAML3 from "yaml";
125
- var migrateWorkflow = (sha) => (workflow, filename) => {
126
- const logger = getLogger3(["dx-cli", "codemod"]);
127
- logger.debug("Processing {filename} file", { filename });
128
- const document = YAML3.parseDocument(workflow);
129
- let updated = false;
130
- YAML3.visit(document, {
131
- Map(_, map, path3) {
132
- if (isChildOf(path3, "jobs") || isChildOf(path3, "with")) {
133
- return void 0;
134
- }
135
- if (map.has("jobs")) {
136
- return void 0;
137
- }
138
- if (map.has("uses")) {
139
- const uses = map.get("uses");
140
- if (typeof uses === "string" && uses.match(
141
- /^pagopa\/dx\/.github\/workflows\/(web|function)_app_deploy/
142
- )) {
143
- logger.debug("Adding disable_auto_staging_deploy");
144
- map.addIn(["with", "disable_auto_staging_deploy"], true);
145
- map.set("permissions", {
146
- attestations: "write",
147
- contents: "read",
148
- "id-token": "write"
149
- });
150
- updated = true;
151
- return void 0;
152
- }
153
- }
154
- return YAML3.visit.SKIP;
155
- },
156
- Pair(_, pair) {
157
- if (YAML3.isScalar(pair.key)) {
158
- if (pair.key.value === "function_app_name") {
159
- updated = true;
160
- logger.debug("Updating function_app_name to web_app_name");
161
- return new YAML3.Pair("web_app_name", pair.value);
162
- }
163
- if (pair.key.value === "use_staging_slot") {
164
- updated = true;
165
- logger.debug("Removing use_staging_slot");
166
- return YAML3.visit.REMOVE;
167
- }
168
- if (pair.key.value === "uses") {
169
- updated = true;
170
- logger.debug("Updating uses value");
171
- return new YAML3.Pair(
172
- "uses",
173
- `pagopa/dx/.github/workflows/release-azure-appsvc-v1.yaml@${sha}`
174
- );
175
- }
176
- }
177
- }
178
- });
179
- if (updated) {
180
- logger.info("Workflow {filename} updated", {
181
- filename
182
- });
183
- return YAML3.stringify(document);
184
- }
185
- logger.debug("No changes applied to {filename}", { filename });
186
- return workflow;
187
- };
188
- var useAzureAppsvc = {
189
- apply: async () => {
190
- const sha = await getLatestCommitShaOrRef("pagopa", "dx");
191
- await replaceInFile2({
192
- allowEmptyPaths: true,
193
- files: [".github/workflows/*.yaml"],
194
- processor: migrateWorkflow(sha)
195
- });
196
- },
197
- description: "Refactor legacy deploy workflows to use release-azure-appsvc",
198
- id: "use-azure-appsvc"
199
- };
200
-
201
- // src/adapters/codemods/use-pnpm.ts
202
- import { getLogger as getLogger4 } from "@logtape/logtape";
203
- import { $ } from "execa";
204
- import assert from "assert/strict";
205
- import fs from "fs/promises";
206
- import { replaceInFile as replaceInFile3 } from "replace-in-file";
207
- import semver from "semver";
208
- import YAML4 from "yaml";
209
- var NPM = class {
210
- lockFileName = "package-lock.json";
211
- async listWorkspaces() {
212
- const { stdout } = await $`npm query .workspace`;
213
- const workspaces = JSON.parse(stdout);
214
- const workspaceNames = [];
215
- if (Array.isArray(workspaces)) {
216
- for (const ws of workspaces) {
217
- if (Object.hasOwn(ws, "name")) {
218
- workspaceNames.push(ws.name);
219
- }
220
- }
221
- }
222
- return workspaceNames;
223
- }
224
- };
225
- var Yarn = class {
226
- lockFileName = "yarn.lock";
227
- async listWorkspaces() {
228
- const { stdout } = await $({ lines: true })`yarn workspaces list --json`;
229
- const workspaceNames = [];
230
- for (const line of stdout) {
231
- const ws = JSON.parse(line);
232
- if (Object.hasOwn(ws, "name")) {
233
- workspaceNames.push(ws.name);
234
- }
235
- }
236
- return workspaceNames;
237
- }
238
- };
239
- async function extractPackageExtensions() {
240
- try {
241
- const yarnrc = await fs.readFile(".yarnrc.yml", "utf-8");
242
- const parsed = YAML4.parse(yarnrc);
243
- if (parsed.packageExtensions) {
244
- return parsed.packageExtensions;
245
- }
246
- } catch {
247
- }
248
- return void 0;
249
- }
250
- async function preparePackageJsonForPnpm() {
251
- const packageJson = await fs.readFile("package.json", "utf-8");
252
- const manifest = JSON.parse(packageJson);
253
- let workspaces = [];
254
- if (Object.hasOwn(manifest, "packageManager")) {
255
- delete manifest.packageManager;
256
- }
257
- if (Object.hasOwn(manifest, "workspaces")) {
258
- if (Array.isArray(manifest.workspaces)) {
259
- workspaces = manifest.workspaces;
260
- }
261
- delete manifest.workspaces;
262
- }
263
- await fs.writeFile("package.json", JSON.stringify(manifest, null, 2));
264
- return workspaces;
265
- }
266
- async function writePnpmWorkspaceFile(workspaces, packageExtensions) {
267
- const pnpmWorkspace = {
268
- cleanupUnusedCatalogs: true,
269
- linkWorkspacePackages: true,
270
- packageExtensions,
271
- packageImportMethod: "clone-or-copy",
272
- packages: workspaces.length > 0 ? workspaces : ["apps/*", "packages/*"]
273
- };
274
- const yamlContent = YAML4.stringify(pnpmWorkspace);
275
- await fs.writeFile("pnpm-workspace.yaml", yamlContent, "utf-8");
276
- }
277
- async function removeFiles(...files) {
278
- await Promise.all(
279
- files.map(
280
- (file) => (
281
- // Remove the file if it exists, fail silently if it doesn't.
282
- fs.rm(file, { force: true, recursive: true }).catch(() => void 0)
283
- )
284
- )
285
- );
286
- }
287
- async function replacePMOccurrences() {
288
- const logger = getLogger4(["dx-cli", "codemod"]);
289
- logger.info("Replacing yarn and npm occurrences in files...");
290
- const results = await replaceInFile3({
291
- allowEmptyPaths: true,
292
- files: ["**/*.json", "**/*.md", "**/Dockerfile", "**/docker-compose.yml"],
293
- from: [
294
- "https://yarnpkg.com/",
295
- "https://classic.yarnpkg.com/",
296
- /\b(yarn workspace|npm -(\b-workspace\b|\bw\b)) (\S+)\b/g,
297
- /\b(yarn workspace|npm -(\b-workspace\b|\bw\b))\b/g,
298
- /\b(yarn install --immutable|npm ci)\b/g,
299
- /\b(yarn -q dlx|npx)\b/g,
300
- /\b((yarn|npm) run)\b/g,
301
- /(^|\s|")(yarn|npm)(?!\S)/gi
302
- ],
303
- ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"],
304
- to: [
305
- "https://pnpm.io/",
306
- "https://pnpm.io/",
307
- "pnpm --filter $3",
308
- "pnpm --filter <package-selector>",
309
- "pnpm install --frozen-lockfile",
310
- "pnpm dlx",
311
- "pnpm run",
312
- "$1pnpm"
313
- ]
314
- });
315
- const count = results.reduce(
316
- (acc, file) => file.hasChanged ? acc + 1 : acc,
317
- 0
318
- );
319
- logger.info("Replaced yarn occurrences in {count} files", { count });
320
- }
321
- async function updateDXWorkflows() {
322
- const logger = getLogger4(["dx-cli", "codemod"]);
323
- logger.info("Updating Github Workflows workflows...");
324
- const sha = await getLatestCommitShaOrRef("pagopa", "dx");
325
- const results = await replaceInFile3({
326
- allowEmptyPaths: true,
327
- files: [".github/workflows/*.yaml"],
328
- processor: updateJSCodeReviewJob(sha)
329
- });
330
- const ignore = results.filter((r) => r.hasChanged).map((r) => r.file);
331
- await replaceInFile3({
332
- allowEmptyPaths: true,
333
- files: [".github/workflows/*.yaml"],
334
- ignore,
335
- processor: migrateWorkflow(sha)
336
- });
337
- }
338
- var usePnpm = async (packageManager, currentNodeVersion) => {
339
- const minNodeVersion = "20.19.5";
340
- assert.notEqual(packageManager, "pnpm", "Project is already using pnpm");
341
- assert.ok(
342
- semver.gte(currentNodeVersion, minNodeVersion),
343
- `his codemod requires Node.js >= ${minNodeVersion}. Current version: ${currentNodeVersion}`
344
- );
345
- const logger = getLogger4(["dx-cli", "codemod"]);
346
- const pm = packageManager === "yarn" ? new Yarn() : new NPM();
347
- const localWorkspaces = await pm.listWorkspaces();
348
- if (localWorkspaces.length > 0) {
349
- logger.info("Using the {protocol} protocol for local dependencies", {
350
- protocol: "workspace:"
351
- });
352
- await replaceInFile3({
353
- allowEmptyPaths: true,
354
- files: ["**/package.json"],
355
- from: localWorkspaces.map((ws) => new RegExp(`"${ws}": ".*?"`, "g")),
356
- to: localWorkspaces.map((ws) => `"${ws}": "workspace:^"`)
357
- });
358
- }
359
- logger.info("Remove unused fields from {file}", {
360
- file: "package.json"
361
- });
362
- const workspaces = await preparePackageJsonForPnpm();
363
- const packageExtensions = packageManager === "yarn" ? await extractPackageExtensions() : void 0;
364
- logger.info("Create {file}", {
365
- file: "pnpm-workspace.yaml"
366
- });
367
- await writePnpmWorkspaceFile(workspaces, packageExtensions);
368
- logger.info("Remove node_modules and yarn files");
369
- await removeFiles(
370
- ".yarnrc",
371
- ".yarnrc.yml",
372
- "yarn.config.cjs",
373
- ".yarn",
374
- ".pnp.cjs",
375
- ".pnp.loader.cjs",
376
- "node_modules"
377
- );
378
- const stat = await fs.stat(pm.lockFileName);
379
- if (stat.isFile()) {
380
- logger.info("Importing {source} to {target}", {
381
- source: pm.lockFileName,
382
- target: "pnpm-lock.yaml"
383
- });
384
- await $`corepack pnpm@latest import ${pm.lockFileName}`;
385
- await removeFiles(pm.lockFileName);
386
- } else {
387
- logger.info("No {source} file found, skipping import.", {
388
- source: pm.lockFileName
389
- });
390
- }
391
- await replacePMOccurrences();
392
- await updateDXWorkflows();
393
- logger.info("Adding pnpm store to .gitignore...");
394
- await fs.appendFile(".gitignore", "\n\n# PNPM\n.pnpm-store");
395
- logger.info("Setting pnpm as the package manager...");
396
- await $`corepack use pnpm@latest`;
397
- };
398
- var apply = async (info) => {
399
- const logger = getLogger4(["dx-cli", "codemod"]);
400
- try {
401
- await usePnpm(info.packageManager, process.versions.node);
402
- } catch (error) {
403
- if (error instanceof Error) {
404
- logger.error(error.message);
405
- }
406
- }
407
- };
408
- var use_pnpm_default = {
409
- apply,
410
- description: "Migrate the project to use pnpm as the package manager",
411
- id: "use-pnpm"
412
- };
413
-
414
- // src/adapters/codemods/index.ts
415
- var registry = new LocalCodemodRegistry();
416
- registry.add(use_pnpm_default);
417
- registry.add(useAzureAppsvc);
418
- registry.add(updateCodeReview);
419
- var codemods_default = registry;
420
-
421
- // src/adapters/commander/index.ts
422
- import { Command as Command6 } from "commander";
423
-
424
- // src/adapters/commander/commands/codemod.ts
425
- import { getLogger as getLogger5 } from "@logtape/logtape";
426
- import { Command } from "commander";
427
- var makeCodemodCommand = ({
428
- applyCodemodById: applyCodemodById2,
429
- listCodemods: listCodemods2
430
- }) => new Command("codemod").description("Manage and apply migration scripts to the repository").addCommand(
431
- new Command("list").description("List available migration scripts").action(async function() {
432
- await listCodemods2().andTee(
433
- (codemods) => console.table(codemods, ["id", "description"])
434
- ).orTee((error) => this.error(error.message));
435
- })
436
- ).addCommand(
437
- new Command("apply").argument("<id>", "The id of the codemod to apply").description("Apply migration scripts to the repository").action(async function(id) {
438
- const logger = getLogger5(["dx-cli", "codemod"]);
439
- await applyCodemodById2(id).andTee(() => {
440
- logger.info("Codemod applied \u2705");
441
- }).orTee((error) => this.error(error.message));
442
- })
443
- );
444
-
445
- // src/adapters/commander/commands/doctor.ts
446
- import { Command as Command2 } from "commander";
447
- import * as process2 from "process";
448
-
449
- // src/domain/doctor.ts
450
- import { ResultAsync as ResultAsync3 } from "neverthrow";
451
-
452
- // src/domain/package-json.ts
453
- import { err, ok } from "neverthrow";
454
- import { z } from "zod/v4";
455
- var ScriptName = z.string().brand();
456
- var scriptSchema = z.object({
457
- name: ScriptName,
458
- script: z.string()
459
- });
460
- var dependencySchema = z.object({
461
- name: z.string(),
462
- version: z.string()
463
- });
464
- var PackageName = z.string().min(1).brand();
465
- var scriptsSchema = z.record(ScriptName, z.string()).optional().transform(
466
- (obj) => new Map(
467
- obj ? Object.entries(obj).map(([name, script]) => [
468
- ScriptName.parse(name),
469
- script
470
- ]) : []
471
- )
472
- );
473
- var dependenciesSchema = z.record(z.string(), z.string()).optional().transform(
474
- (obj) => new Map(
475
- obj ? Object.entries(obj).map(([name, version]) => [name, version]) : []
476
- )
477
- );
478
- var packageManagerSchema = z.enum(["npm", "pnpm", "yarn"]);
479
- var packageJsonSchema = z.object({
480
- dependencies: dependenciesSchema,
481
- devDependencies: dependenciesSchema,
482
- name: PackageName,
483
- packageManager: z.string().transform((str) => str.split("@")[0]).pipe(packageManagerSchema).optional(),
484
- scripts: scriptsSchema
485
- });
486
- var findMissingScripts = (availableScripts, requiredScripts) => {
487
- const availableScriptNames = new Set(availableScripts.keys());
488
- const requiredScriptNames = new Set(requiredScripts.keys());
489
- return requiredScriptNames.difference(availableScriptNames);
490
- };
491
- var checkMonorepoScripts = async (dependencies, repositoryRoot) => {
492
- const { packageJsonReader: packageJsonReader2 } = dependencies;
493
- const checkName = "Monorepo Scripts";
494
- const scriptsResult = await packageJsonReader2.getScripts(repositoryRoot);
495
- if (scriptsResult.isErr()) {
496
- return err(scriptsResult.error);
497
- }
498
- const requiredScriptsMap = packageJsonReader2.getRootRequiredScripts();
499
- const missingScripts = findMissingScripts(
500
- scriptsResult.value,
501
- requiredScriptsMap
502
- );
503
- if (missingScripts.size === 0) {
504
- return ok({
505
- checkName,
506
- isValid: true,
507
- successMessage: "Monorepo scripts are correctly set up"
508
- });
509
- }
510
- return ok({
511
- checkName,
512
- errorMessage: `Missing required scripts: ${Array.from(missingScripts).join(", ")}`,
513
- isValid: false
514
- });
515
- };
516
-
517
- // src/domain/repository.ts
518
- import { ok as ok2 } from "neverthrow";
519
- import fs2 from "path";
520
- import coerce from "semver/functions/coerce.js";
521
- import semverGte from "semver/functions/gte.js";
522
- var isVersionValid = (version, minVersion) => {
523
- const minAcceptedSemVer = coerce(minVersion);
524
- const dependencySemVer = coerce(version);
525
- if (!minAcceptedSemVer || !dependencySemVer) {
526
- return false;
527
- }
528
- return semverGte(dependencySemVer, minAcceptedSemVer);
529
- };
530
- var checkPreCommitConfig = async (dependencies, repositoryRoot) => {
531
- const { repositoryReader: repositoryReader2 } = dependencies;
532
- const checkName = "Pre-commit Configuration";
533
- const preCommitResult = await repositoryReader2.fileExists(
534
- fs2.join(repositoryRoot, ".pre-commit-config.yaml")
535
- );
536
- if (preCommitResult.isOk() && preCommitResult.value) {
537
- return ok2({
538
- checkName,
539
- isValid: true,
540
- successMessage: "Pre-commit configuration is present in the repository root"
541
- });
542
- }
543
- const errorMessage = preCommitResult.isErr() ? preCommitResult.error.message : `Pre-commit configuration is not present in the repository root. Please add a .pre-commit-config.yaml file to the repository root.`;
544
- return ok2({
545
- checkName,
546
- errorMessage,
547
- isValid: false
548
- });
549
- };
550
- var checkTurboConfig = async (dependencies, repositoryRoot, config2) => {
551
- const { packageJsonReader: packageJsonReader2, repositoryReader: repositoryReader2 } = dependencies;
552
- const checkName = "Turbo Configuration";
553
- const turboResult = await repositoryReader2.fileExists(
554
- fs2.join(repositoryRoot, "turbo.json")
555
- );
556
- if (turboResult.isErr()) {
557
- return ok2({
558
- checkName,
559
- errorMessage: turboResult.error.message,
560
- isValid: false
561
- });
562
- }
563
- const dependenciesResult = await packageJsonReader2.getDependencies(
564
- repositoryRoot,
565
- "dev"
566
- );
567
- if (dependenciesResult.isErr()) {
568
- return ok2({
569
- checkName,
570
- errorMessage: dependenciesResult.error.message,
571
- isValid: false
572
- });
573
- }
574
- const turboVersion = dependenciesResult.value.get(
575
- "turbo"
576
- );
577
- if (!turboVersion) {
578
- return ok2({
579
- checkName,
580
- errorMessage: "Turbo dependency not found in devDependencies. Please add 'turbo' to your devDependencies.",
581
- isValid: false
582
- });
583
- }
584
- if (!isVersionValid(turboVersion, config2.minVersions.turbo)) {
585
- return ok2({
586
- checkName,
587
- errorMessage: `Turbo version (${turboVersion}) is too low. Minimum required version is ${config2.minVersions.turbo}.`,
588
- isValid: false
589
- });
590
- }
591
- return ok2({
592
- checkName,
593
- isValid: true,
594
- successMessage: "Turbo configuration is present in the monorepo root and turbo dependency is installed"
595
- });
596
- };
597
-
598
- // src/domain/workspace.ts
599
- import { ok as ok3 } from "neverthrow";
600
- import { z as z2 } from "zod/v4";
601
- var WorkspaceName = z2.string().min(1).brand();
602
- var workspaceSchema = z2.object({
603
- name: WorkspaceName,
604
- path: z2.string()
605
- });
606
- var checkWorkspaces = async (dependencies, monorepoDir) => {
607
- const { repositoryReader: repositoryReader2 } = dependencies;
608
- const checkName = "Workspaces";
609
- const workspacesResult = await repositoryReader2.getWorkspaces(monorepoDir);
610
- if (workspacesResult.isErr()) {
611
- return ok3({
612
- checkName,
613
- errorMessage: "Something is wrong with the workspaces configuration. If you need help, please contact the DevEx team.",
614
- isValid: false
615
- });
616
- }
617
- const { length: workspaceNumber } = workspacesResult.value;
618
- if (workspaceNumber === 0) {
619
- return ok3({
620
- checkName,
621
- errorMessage: "No workspace configuration found. Make sure to configure workspaces in pnpm-workspace.yaml.",
622
- isValid: false
623
- });
624
- }
625
- return ok3({
626
- checkName,
627
- isValid: true,
628
- successMessage: `Found ${workspaceNumber} workspace${workspaceNumber === 1 ? "" : "s"}`
629
- });
630
- };
631
-
632
- // src/domain/doctor.ts
633
- var runDoctor = async (dependencies, config2) => {
634
- const repoRootResult = await dependencies.repositoryReader.findRepositoryRoot();
635
- if (repoRootResult.isErr()) {
636
- return {
637
- checks: [
638
- {
639
- checkName: "Repository Detection",
640
- errorMessage: "Could not find repository root. Make sure to run this command inside a Git repository.",
641
- isValid: false
642
- }
643
- ],
644
- hasErrors: true
645
- };
646
- }
647
- const repositoryRoot = repoRootResult.value;
648
- const doctorChecks = [
649
- ResultAsync3.fromPromise(
650
- checkPreCommitConfig(dependencies, repositoryRoot),
651
- () => new Error("Error checking pre-commit configuration")
652
- ),
653
- ResultAsync3.fromPromise(
654
- checkTurboConfig(dependencies, repositoryRoot, config2),
655
- () => new Error("Error checking Turbo configuration")
656
- ),
657
- ResultAsync3.fromPromise(
658
- checkMonorepoScripts(dependencies, repositoryRoot),
659
- () => new Error("Error checking monorepo scripts")
660
- ),
661
- ResultAsync3.fromPromise(
662
- checkWorkspaces(dependencies, repositoryRoot),
663
- () => new Error("Error checking monorepo scripts")
664
- )
665
- ];
666
- return ResultAsync3.combine(doctorChecks).match(
667
- toDoctorResult,
668
- () => ({
669
- checks: [],
670
- hasErrors: true
671
- })
672
- );
673
- };
674
- var toDoctorResult = (validationCheckResults) => {
675
- const checks = validationCheckResults.map((result) => {
676
- if (result.isOk()) {
677
- return result.value;
678
- }
679
- return {
680
- checkName: "Unknown",
681
- errorMessage: result.error.message,
682
- isValid: false
683
- };
684
- });
685
- const hasErrors = checks.some((check) => !check.isValid);
686
- return {
687
- checks,
688
- hasErrors
689
- };
690
- };
691
- var printDoctorResult = ({ validationReporter: validationReporter2 }, result) => result.checks.map(validationReporter2.reportCheckResult);
692
-
693
- // src/adapters/commander/commands/doctor.ts
694
- var makeDoctorCommand = (dependencies, config2) => new Command2().name("doctor").description(
695
- "Verify the repository setup according to the DevEx guidelines"
696
- ).action(async () => {
697
- const result = await runDoctor(dependencies, config2);
698
- printDoctorResult(dependencies, result);
699
- const exitCode = result.hasErrors ? 1 : 0;
700
- process2.exit(exitCode);
701
- });
702
-
703
- // src/adapters/commander/commands/info.ts
704
- import { Command as Command3 } from "commander";
705
-
706
- // src/domain/info.ts
707
- import { getLogger as getLogger6 } from "@logtape/logtape";
708
- import { join } from "path";
709
- var detectFromLockFile = async (dependencies, repositoryRoot) => {
710
- const { repositoryReader: repositoryReader2 } = dependencies;
711
- const pnpmResult = await repositoryReader2.fileExists(
712
- join(repositoryRoot, "pnpm-lock.yaml")
713
- );
714
- if (pnpmResult.isOk() && pnpmResult.value) return "pnpm";
715
- const yarnResult = await repositoryReader2.fileExists(
716
- join(repositoryRoot, "yarn.lock")
717
- );
718
- if (yarnResult.isOk() && yarnResult.value) return "yarn";
719
- const npmResult = await repositoryReader2.fileExists(
720
- join(repositoryRoot, "package-lock.json")
721
- );
722
- if (npmResult.isOk() && npmResult.value) return "npm";
723
- return void 0;
724
- };
725
- var detectPackageManager = async (dependencies, repositoryRoot) => {
726
- const packageJsonResult = await dependencies.packageJsonReader.readPackageJson(repositoryRoot);
727
- const packageManager = packageJsonResult.map((pkg) => pkg.packageManager).unwrapOr(void 0) ?? await detectFromLockFile(dependencies, repositoryRoot);
728
- return packageManager ?? "npm";
729
- };
730
- var detectNodeVersion = async ({ repositoryReader: repositoryReader2 }, nodeVersionFilePath) => await repositoryReader2.readFile(nodeVersionFilePath).map((nodeVersion) => nodeVersion.trim()).unwrapOr(void 0);
731
- var detectTerraformVersion = async ({ repositoryReader: repositoryReader2 }, terraformVersionFilePath) => await repositoryReader2.readFile(terraformVersionFilePath).map((tfVersion) => tfVersion.trim()).unwrapOr(void 0);
732
- var detectTurboVersion = async (dependencies, repositoryRoot) => {
733
- const packageJsonResult = await dependencies.packageJsonReader.readPackageJson(repositoryRoot);
734
- return packageJsonResult.map((pkg) => pkg.devDependencies.get("turbo")?.trim()).unwrapOr(void 0);
735
- };
736
- var getInfo = (dependencies) => async () => {
737
- const repositoryRoot = await dependencies.repositoryReader.findRepositoryRoot().unwrapOr(process.cwd());
738
- return {
739
- node: await detectNodeVersion(
740
- { repositoryReader: dependencies.repositoryReader },
741
- `${repositoryRoot}/.node-version`
742
- ),
743
- packageManager: await detectPackageManager(dependencies, repositoryRoot),
744
- terraform: await detectTerraformVersion(
745
- { repositoryReader: dependencies.repositoryReader },
746
- `${repositoryRoot}/.terraform-version`
747
- ),
748
- turbo: await detectTurboVersion(dependencies, repositoryRoot)
749
- };
750
- };
751
- var printInfo = (result) => {
752
- const logger = getLogger6("json");
753
- logger.info(JSON.stringify(result));
754
- };
755
-
756
- // src/adapters/commander/commands/info.ts
757
- var makeInfoCommand = (dependencies) => new Command3().name("info").description("Display information about the project").action(async () => {
758
- const result = await getInfo(dependencies)();
759
- printInfo(result);
760
- });
761
-
762
- // src/adapters/commander/commands/init.ts
763
- import loadMonorepoScaffolder, {
764
- answersSchema,
765
- PLOP_MONOREPO_GENERATOR_NAME
766
- } from "@pagopa/monorepo-generator";
767
- import chalk from "chalk";
768
- import { Command as Command4 } from "commander";
769
- import { $ as $3 } from "execa";
770
- import { errAsync, okAsync as okAsync2, ResultAsync as ResultAsync6 } from "neverthrow";
771
- import * as path from "path";
772
- import { oraPromise } from "ora";
773
-
774
- // src/domain/github.ts
775
- var PullRequest = class {
776
- constructor(url) {
777
- this.url = url;
778
- }
779
- };
780
- var Repository = class {
781
- constructor(name, owner) {
782
- this.name = name;
783
- this.owner = owner;
784
- }
785
- get fullName() {
786
- return `${this.owner}/${this.name}`;
787
- }
788
- get ssh() {
789
- return `git@github.com:${this.owner}/${this.name}.git`;
790
- }
791
- get url() {
792
- return `https://github.com/${this.owner}/${this.name}`;
793
- }
794
- };
795
- var RepositoryNotFoundError = class extends Error {
796
- constructor(owner, name) {
797
- super(`Repository ${owner}/${name} not found`);
798
- this.name = "RepositoryNotFoundError";
799
- }
800
- };
801
-
802
- // src/adapters/execa/terraform.ts
803
- import { $ as $2 } from "execa";
804
- var tf$ = $2({
805
- environment: {
806
- NO_COLOR: "1",
807
- TF_IN_AUTOMATION: "1",
808
- TF_INPUT: "0"
809
- },
810
- shell: true
811
- });
812
-
813
- // src/adapters/plop/index.ts
814
- import { Result, ResultAsync as ResultAsync4 } from "neverthrow";
815
- import nodePlop from "node-plop";
816
- var initPlop = () => ResultAsync4.fromPromise(
817
- nodePlop(),
818
- () => new Error("Failed to initialize plop")
819
- );
820
- var getGenerator = (plopAPI) => Result.fromThrowable(
821
- plopAPI.getGenerator,
822
- () => new Error("Generator not found")
823
- );
824
- var getPrompts = (generator) => ResultAsync4.fromPromise(
825
- generator.runPrompts(),
826
- (cause) => new Error("Failed to run the generator prompts", { cause })
827
- );
828
-
829
- // src/adapters/zod/index.ts
830
- import { ResultAsync as ResultAsync5 } from "neverthrow";
831
- var decode = (schema) => (data) => ResultAsync5.fromPromise(
832
- schema.parseAsync(data),
833
- (cause) => new Error("Input is not valid for the given schema", { cause })
834
- );
835
-
836
- // src/adapters/commander/commands/init.ts
837
- var withSpinner = (text, successText, failText, promise) => ResultAsync6.fromPromise(
838
- oraPromise(promise, {
839
- failText,
840
- successText,
841
- text
842
- }),
843
- (cause) => {
844
- console.error(`Something went wrong: ${JSON.stringify(cause, null, 2)}`);
845
- return new Error(failText, { cause });
846
- }
847
- );
848
- var validateAnswers = (githubService) => (answers) => ResultAsync6.fromPromise(
849
- githubService.getRepository(answers.repoOwner, answers.repoName),
850
- (error) => error
851
- ).andThen(
852
- ({ fullName }) => errAsync(new Error(`Repository ${fullName} already exists.`))
853
- ).orElse(
854
- (error) => error instanceof RepositoryNotFoundError ? (
855
- // If repository is not found, it's safe to proceed
856
- okAsync2(answers)
857
- ) : (
858
- // Otherwise, propagate the error
859
- errAsync(error)
860
- )
861
- ).map(() => answers);
862
- var runGeneratorActions = (generator) => (answers) => withSpinner(
863
- "Creating workspace files...",
864
- "Workspace files created successfully!",
865
- "Failed to create workspace files.",
866
- generator.runActions(answers)
867
- ).map(() => answers);
868
- var displaySummary = (initResult) => {
869
- const { csp, pr, repository } = initResult;
870
- console.log(chalk.green.bold("\nWorkspace created successfully!"));
871
- console.log(`- Cloud Service Provider: ${chalk.cyan(csp.name)}`);
872
- console.log(`- CSP location: ${chalk.cyan(csp.location)}`);
873
- if (repository) {
874
- console.log(`- Name: ${chalk.cyan(repository.name)}`);
875
- console.log(`- GitHub Repository: ${chalk.cyan(repository.url)}
876
- `);
877
- } else {
878
- console.log(
879
- chalk.yellow(
880
- `
881
- \u26A0\uFE0F GitHub repository may not have been created automatically.`
882
- )
883
- );
884
- }
885
- if (pr) {
886
- console.log(chalk.green.bold("\nNext Steps:"));
887
- console.log(
888
- `1. Review the Pull Request in the GitHub repository: ${chalk.underline(pr.url)}`
889
- );
890
- console.log(
891
- `2. Visit ${chalk.underline("https://dx.pagopa.it/getting-started")} to deploy your first project
892
- `
893
- );
894
- } else {
895
- console.log(
896
- chalk.yellow(`
897
- \u26A0\uFE0F There was an error during Pull Request creation.`)
898
- );
899
- console.log(
900
- `Please, manually create a Pull Request in the GitHub repository to review the scaffolded code.
901
- `
902
- );
903
- }
904
- };
905
- var checkTerraformCliIsInstalled = (text, successText, failText) => withSpinner(text, successText, failText, tf$`terraform -version`);
906
- var checkPreconditions = () => checkTerraformCliIsInstalled(
907
- "Checking Terraform CLI is installed...",
908
- "Terraform CLI is installed!",
909
- "Terraform CLI is not installed."
910
- );
911
- var createRemoteRepository = ({
912
- repoName,
913
- repoOwner
914
- }) => {
915
- const cwd2 = path.resolve(repoName, "infra", "repository");
916
- const applyTerraform = async () => {
917
- await tf$({ cwd: cwd2 })`terraform init`;
918
- await tf$({ cwd: cwd2 })`terraform apply -auto-approve`;
919
- };
920
- return withSpinner(
921
- "Creating GitHub repository...",
922
- "GitHub repository created successfully!",
923
- "Failed to create GitHub repository.",
924
- applyTerraform()
925
- ).map(() => new Repository(repoName, repoOwner));
926
- };
927
- var initializeGitRepository = (repository) => {
928
- const cwd2 = path.resolve(repository.name);
929
- const branchName = "features/scaffold-workspace";
930
- const git$ = $3({
931
- cwd: cwd2,
932
- shell: true
933
- });
934
- const pushToOrigin = async () => {
935
- await git$`git init`;
936
- await git$`git add README.md`;
937
- await git$`git commit --no-gpg-sign -m "Create README.md"`;
938
- await git$`git branch -M main`;
939
- await git$`git remote add origin ${repository.ssh}`;
940
- await git$`git push -u origin main`;
941
- await git$`git switch -c ${branchName}`;
942
- await git$`git add .`;
943
- await git$`git commit --no-gpg-sign -m "Scaffold workspace"`;
944
- await git$`git push -u origin ${branchName}`;
945
- };
946
- return withSpinner(
947
- "Pushing code to GitHub...",
948
- "Code pushed to GitHub successfully!",
949
- "Failed to push code to GitHub.",
950
- pushToOrigin()
951
- ).map(() => ({ branchName, repository }));
952
- };
953
- var handleNewGitHubRepository = (githubService) => (answers) => createRemoteRepository(answers).andThen(initializeGitRepository).andThen(
954
- (localWorkspace) => createPullRequest(githubService)(localWorkspace).map((pr) => ({
955
- pr,
956
- repository: localWorkspace.repository
957
- }))
958
- );
959
- var makeInitResult = (answers, { pr, repository }) => {
960
- const csp = {
961
- location: answers.csp === "azure" ? answers.azureLocation : answers.awsRegion,
962
- name: answers.csp
963
- };
964
- return {
965
- csp,
966
- pr,
967
- repository
968
- };
969
- };
970
- var createPullRequest = (githubService) => ({
971
- branchName,
972
- repository
973
- }) => withSpinner(
974
- "Creating Pull Request...",
975
- "Pull Request created successfully!",
976
- "Failed to create Pull Request.",
977
- githubService.createPullRequest({
978
- base: "main",
979
- body: "This PR contains the scaffolded monorepo structure.",
980
- head: branchName,
981
- owner: repository.owner,
982
- repo: repository.name,
983
- title: "Scaffold repository"
984
- })
985
- ).orElse(() => okAsync2(void 0));
986
- var makeInitCommand = ({
987
- gitHubService: gitHubService2
988
- }) => new Command4().name("init").description(
989
- "Command to initialize resources (like projects, subscriptions, ...)"
990
- ).addCommand(
991
- new Command4("project").description("Initialize a new monorepo project").action(async function() {
992
- await checkPreconditions().andThen(initPlop).andTee(loadMonorepoScaffolder).andThen((plop) => getGenerator(plop)(PLOP_MONOREPO_GENERATOR_NAME)).andThen(
993
- (generator) => (
994
- // Ask the user the questions defined in the plop generator
995
- getPrompts(generator).andThen(decode(answersSchema)).andThen(validateAnswers(gitHubService2)).andThen(runGeneratorActions(generator))
996
- )
997
- ).andThen(
998
- (answers) => handleNewGitHubRepository(gitHubService2)(answers).map(
999
- (repoPr) => makeInitResult(answers, repoPr)
1000
- )
1001
- ).match(displaySummary, exitWithError(this));
1002
- })
1003
- );
1004
-
1005
- // src/adapters/commander/commands/savemoney.ts
1006
- import { azure, loadConfig } from "@pagopa/dx-savemoney";
1007
- import { Command as Command5 } from "commander";
1008
- var makeSavemoneyCommand = () => new Command5("savemoney").description(
1009
- "Analyze Azure subscriptions and report unused or inefficient resources"
1010
- ).option("-c, --config <path>", "Path to configuration file (JSON)").option(
1011
- "-f, --format <format>",
1012
- "Report format: json, table, or detailed-json (default: table)",
1013
- "table"
1014
- ).option(
1015
- "-l, --location <string>",
1016
- "Preferred Azure location for resources",
1017
- "italynorth"
1018
- ).option("-d, --days <number>", "Number of days for metrics analysis", "30").option("-v, --verbose", "Enable verbose logging").action(async function(options) {
1019
- try {
1020
- const config2 = await loadConfig(options.config);
1021
- const finalConfig = {
1022
- ...config2,
1023
- preferredLocation: options.location || config2.preferredLocation,
1024
- timespanDays: Number.parseInt(options.days, 10) || config2.timespanDays,
1025
- verbose: options.verbose || false
1026
- };
1027
- await azure.analyzeAzureResources(finalConfig, options.format);
1028
- } catch (error) {
1029
- this.error(
1030
- `Analysis failed: ${error instanceof Error ? error.message : error}`
1031
- );
1032
- }
1033
- });
1034
-
1035
- // src/adapters/commander/index.ts
1036
- var makeCli = (deps2, config2, cliDeps) => {
1037
- const program2 = new Command6();
1038
- program2.name("dx").description("The CLI for DX-Platform").version("0.15.2");
1039
- program2.addCommand(makeDoctorCommand(deps2, config2));
1040
- program2.addCommand(makeCodemodCommand(cliDeps));
1041
- program2.addCommand(makeInitCommand(deps2));
1042
- program2.addCommand(makeSavemoneyCommand());
1043
- program2.addCommand(makeInfoCommand(deps2));
1044
- return program2;
1045
- };
1046
- var exitWithError = (command) => (error) => {
1047
- command.error(error.message);
1048
- };
1049
-
1050
- // src/adapters/logtape/validation-reporter.ts
1051
- import { getLogger as getLogger7 } from "@logtape/logtape";
1052
- var makeValidationReporter = () => {
1053
- const logger = getLogger7(["dx-cli", "validation"]);
1054
- return {
1055
- reportCheckResult(result) {
1056
- if (result.isValid) {
1057
- logger.info(`\u2705 ${result.successMessage}`);
1058
- } else {
1059
- logger.error(`\u274C ${result.errorMessage}`);
1060
- }
1061
- }
1062
- };
1063
- };
1064
-
1065
- // src/adapters/node/package-json.ts
1066
- import { join as join2 } from "path";
1067
- import * as process3 from "process";
1068
-
1069
- // src/adapters/node/fs/file-reader.ts
1070
- import { ResultAsync as ResultAsync7 } from "neverthrow";
1071
- import fs3 from "fs/promises";
1072
-
1073
- // src/adapters/node/json/index.ts
1074
- import { Result as Result2 } from "neverthrow";
1075
- var parseJson = Result2.fromThrowable(
1076
- JSON.parse,
1077
- (cause) => new Error("Failed to parse JSON", { cause })
1078
- );
1079
-
1080
- // src/adapters/node/fs/file-reader.ts
1081
- var readFile = (filePath) => ResultAsync7.fromPromise(
1082
- fs3.readFile(filePath, "utf-8"),
1083
- (cause) => new Error(`Failed to read file: ${filePath}`, { cause })
1084
- );
1085
- var readFileAndDecode = (filePath, schema) => readFile(filePath).andThen(parseJson).andThen(decode(schema));
1086
- var fileExists = (path3) => ResultAsync7.fromPromise(
1087
- fs3.stat(path3),
1088
- () => new Error(`${path3} not found.`)
1089
- ).map(() => true);
1090
-
1091
- // src/adapters/node/package-json.ts
1092
- var makePackageJsonReader = () => ({
1093
- getDependencies: (cwd2 = process3.cwd(), type) => {
1094
- const packageJsonPath = join2(cwd2, "package.json");
1095
- return readFileAndDecode(packageJsonPath, packageJsonSchema).map(
1096
- (packageJson) => {
1097
- const key = type === "dev" ? "devDependencies" : "dependencies";
1098
- return packageJson[key];
1099
- }
1100
- );
1101
- },
1102
- getRootRequiredScripts: () => (/* @__PURE__ */ new Map()).set("code-review", "eslint ."),
1103
- getScripts: (cwd2 = process3.cwd()) => {
1104
- const packageJsonPath = join2(cwd2, "package.json");
1105
- return readFileAndDecode(packageJsonPath, packageJsonSchema).map(
1106
- ({ scripts }) => scripts
1107
- );
1108
- },
1109
- readPackageJson: (cwd2 = process3.cwd()) => {
1110
- const packageJsonPath = join2(cwd2, "package.json");
1111
- return readFileAndDecode(packageJsonPath, packageJsonSchema);
1112
- }
1113
- });
1114
-
1115
- // src/adapters/node/repository.ts
1116
- import * as glob from "glob";
1117
- import { okAsync as okAsync3, ResultAsync as ResultAsync8 } from "neverthrow";
1118
- import * as path2 from "path";
1119
- import { z as z3 } from "zod/v4";
1120
-
1121
- // src/adapters/yaml/index.ts
1122
- import { Result as Result3 } from "neverthrow";
1123
- import yaml from "yaml";
1124
- var parseYaml = Result3.fromThrowable(
1125
- (content) => yaml.parse(content),
1126
- () => new Error("Failed to parse YAML")
1127
- );
1128
-
1129
- // src/adapters/node/repository.ts
1130
- var findRepositoryRoot = (dir = process.cwd()) => {
1131
- const gitPath = path2.join(dir, ".git");
1132
- return fileExists(gitPath).mapErr(
1133
- () => new Error(
1134
- "Could not find repository root. Make sure to have the repo initialized."
1135
- )
1136
- ).map(() => dir);
1137
- };
1138
- var resolveWorkspacePattern = (repoRoot, pattern) => ResultAsync8.fromPromise(
1139
- // For now it is not possible to use the fs.glob function (from node:fs/promises)
1140
- // because it is not possible to run it on Node 20.x
1141
- glob.glob(pattern, { cwd: repoRoot }),
1142
- (cause) => new Error(`Failed to resolve workspace glob: ${pattern}`, {
1143
- cause
1144
- })
1145
- ).map(
1146
- (subDirectories) => (
1147
- // Create the absolute path to the subdirectory
1148
- subDirectories.map((directory) => path2.join(repoRoot, directory))
1149
- )
1150
- );
1151
- var getWorkspaces = (repoRoot) => readFile(path2.join(repoRoot, "pnpm-workspace.yaml")).andThen(parseYaml).andThen(
1152
- (obj) => (
1153
- // If no packages are defined, go on with an empty array
1154
- decode(z3.object({ packages: z3.array(z3.string()) }))(obj).orElse(
1155
- () => okAsync3({ packages: [] })
1156
- )
1157
- )
1158
- ).andThen(
1159
- ({ packages }) => (
1160
- // For every package pattern in the pnpm-workspace.yaml file, get the list of subdirectories
1161
- ResultAsync8.combine(
1162
- packages.map((pattern) => resolveWorkspacePattern(repoRoot, pattern))
1163
- ).map((workspacesList) => workspacesList.flat()).andThen((workspaceFolders) => {
1164
- const workspaceResults = workspaceFolders.map(
1165
- (nodeWorkspaceDirectory) => readFileAndDecode(
1166
- path2.join(nodeWorkspaceDirectory, "package.json"),
1167
- packageJsonSchema
1168
- ).map(
1169
- ({ name }) => (
1170
- // Create the workspace object using the package.json name and the nodeWorkspaceDirectory
1171
- workspaceSchema.parse({ name, path: nodeWorkspaceDirectory })
1172
- )
1173
- )
1174
- );
1175
- return ResultAsync8.combine(workspaceResults);
1176
- })
1177
- )
1178
- );
1179
- var makeRepositoryReader = () => ({
1180
- fileExists,
1181
- findRepositoryRoot,
1182
- getWorkspaces,
1183
- readFile
1184
- });
1185
-
1186
- // src/adapters/octokit/index.ts
1187
- import { RequestError } from "octokit";
1188
- var OctokitGitHubService = class {
1189
- #octokit;
1190
- constructor(octokit2) {
1191
- this.#octokit = octokit2;
1192
- }
1193
- async createPullRequest(params) {
1194
- try {
1195
- const { data } = await this.#octokit.rest.pulls.create({
1196
- base: params.base,
1197
- body: params.body,
1198
- head: params.head,
1199
- owner: params.owner,
1200
- repo: params.repo,
1201
- title: params.title
1202
- });
1203
- return new PullRequest(data.html_url);
1204
- } catch (error) {
1205
- throw new Error(
1206
- `Failed to create pull request in ${params.owner}/${params.repo}`,
1207
- {
1208
- cause: error
1209
- }
1210
- );
1211
- }
1212
- }
1213
- async getRepository(owner, name) {
1214
- try {
1215
- const { data } = await this.#octokit.rest.repos.get({
1216
- owner,
1217
- repo: name
1218
- });
1219
- return new Repository(data.name, data.owner.login);
1220
- } catch (error) {
1221
- if (error instanceof RequestError && error.status === 404) {
1222
- throw new RepositoryNotFoundError(owner, name);
1223
- }
1224
- throw new Error(`Failed to fetch repository ${owner}/${name}`, {
1225
- cause: error
1226
- });
1227
- }
1228
- }
1229
- };
1230
-
1231
- // src/config.ts
1232
- var getConfig = () => ({
1233
- minVersions: {
1234
- turbo: "2"
1235
- }
1236
- });
1237
-
1238
- // src/use-cases/apply-codemod.ts
1239
- import { errAsync as errAsync2, okAsync as okAsync4, ResultAsync as ResultAsync9 } from "neverthrow";
1240
- var getCodemodById = (registry2, id) => registry2.getById(id).andThen(
1241
- (codemod) => codemod ? okAsync4(codemod) : errAsync2(new Error(`Codemod with id ${id} not found`))
1242
- );
1243
- var safeGetInfo = (getInfo2) => ResultAsync9.fromPromise(
1244
- getInfo2(),
1245
- (error) => new Error("Failed to get info", { cause: error })
1246
- );
1247
- var applyCodemodById = (registry2, getInfo2) => (id) => ResultAsync9.combine([
1248
- safeGetInfo(getInfo2),
1249
- getCodemodById(registry2, id)
1250
- ]).andThen(
1251
- ([info, codemod]) => ResultAsync9.fromPromise(codemod.apply(info), (error) => {
1252
- const message = error instanceof Error ? `: ${error.message}` : "";
1253
- return new Error("Failed to apply codemod" + message, { cause: error });
1254
- })
1255
- );
1256
-
1257
- // src/use-cases/list-codemods.ts
1258
- var listCodemods = (registry2) => () => registry2.getAll();
1259
-
1260
- // src/index.ts
1261
7
  await configure({
1262
8
  loggers: [
1263
9
  { category: ["dx-cli"], lowestLevel: "info", sinks: ["console"] },
@@ -1266,33 +12,15 @@ await configure({
1266
12
  {
1267
13
  category: ["logtape", "meta"],
1268
14
  lowestLevel: "warning",
1269
- sinks: ["console"]
1270
- }
15
+ sinks: ["console"],
16
+ },
1271
17
  ],
1272
18
  sinks: {
1273
19
  console: getConsoleSink(),
1274
20
  rawJson(record) {
1275
21
  console.log(record.rawMessage);
1276
- }
1277
- }
1278
- });
1279
- var repositoryReader = makeRepositoryReader();
1280
- var packageJsonReader = makePackageJsonReader();
1281
- var validationReporter = makeValidationReporter();
1282
- var octokit = new Octokit3({
1283
- auth: process.env.GITHUB_TOKEN
22
+ },
23
+ },
1284
24
  });
1285
- var gitHubService = new OctokitGitHubService(octokit);
1286
- var deps = {
1287
- gitHubService,
1288
- packageJsonReader,
1289
- repositoryReader,
1290
- validationReporter
1291
- };
1292
- var config = getConfig();
1293
- var useCases = {
1294
- applyCodemodById: applyCodemodById(codemods_default, getInfo(deps)),
1295
- listCodemods: listCodemods(codemods_default)
1296
- };
1297
- var program = makeCli(deps, config, useCases);
1298
- program.parse();
25
+
26
+ runCli(packageJson.version);