@pagopa/dx-cli 0.15.1 → 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
@@ -0,0 +1,50 @@
1
+ import { ResultAsync } from "neverthrow";
2
+ import { checkMonorepoScripts } from "./package-json.js";
3
+ import { checkPreCommitConfig } from "./repository.js";
4
+ import { checkTurboConfig } from "./repository.js";
5
+ import { checkWorkspaces } from "./workspace.js";
6
+ export const runDoctor = async (dependencies, config) => {
7
+ // Get repository root - doctor command requires being in a repository
8
+ const repoRootResult = await dependencies.repositoryReader.findRepositoryRoot();
9
+ if (repoRootResult.isErr()) {
10
+ return {
11
+ checks: [
12
+ {
13
+ checkName: "Repository Detection",
14
+ errorMessage: "Could not find repository root. Make sure to run this command inside a Git repository.",
15
+ isValid: false,
16
+ },
17
+ ],
18
+ hasErrors: true,
19
+ };
20
+ }
21
+ const repositoryRoot = repoRootResult.value;
22
+ const doctorChecks = [
23
+ ResultAsync.fromPromise(checkPreCommitConfig(dependencies, repositoryRoot), () => new Error("Error checking pre-commit configuration")),
24
+ ResultAsync.fromPromise(checkTurboConfig(dependencies, repositoryRoot, config), () => new Error("Error checking Turbo configuration")),
25
+ ResultAsync.fromPromise(checkMonorepoScripts(dependencies, repositoryRoot), () => new Error("Error checking monorepo scripts")),
26
+ ResultAsync.fromPromise(checkWorkspaces(dependencies, repositoryRoot), () => new Error("Error checking monorepo scripts")),
27
+ ];
28
+ return ResultAsync.combine(doctorChecks).match(toDoctorResult, () => ({
29
+ checks: [],
30
+ hasErrors: true,
31
+ }));
32
+ };
33
+ const toDoctorResult = (validationCheckResults) => {
34
+ const checks = validationCheckResults.map((result) => {
35
+ if (result.isOk()) {
36
+ return result.value;
37
+ }
38
+ return {
39
+ checkName: "Unknown",
40
+ errorMessage: result.error.message,
41
+ isValid: false,
42
+ };
43
+ });
44
+ const hasErrors = checks.some((check) => !check.isValid);
45
+ return {
46
+ checks,
47
+ hasErrors,
48
+ };
49
+ };
50
+ export const printDoctorResult = ({ validationReporter }, result) => result.checks.map(validationReporter.reportCheckResult);
@@ -0,0 +1,40 @@
1
+ import { z } from "zod/v4";
2
+ import { CloudAccount, CloudAccountService } from "./cloud-account.js";
3
+ import { TerraformBackend } from "./remote-backend.js";
4
+ export declare const environmentShort: Record<Environment["name"], string>;
5
+ export declare const environmentSchema: z.ZodObject<{
6
+ cloudAccounts: z.ZodArray<z.ZodObject<{
7
+ csp: z.ZodDefault<z.ZodEnum<{
8
+ azure: "azure";
9
+ }>>;
10
+ defaultLocation: z.ZodString;
11
+ displayName: z.ZodString;
12
+ id: z.ZodString;
13
+ }, z.core.$strip>>;
14
+ name: z.ZodEnum<{
15
+ dev: "dev";
16
+ prod: "prod";
17
+ uat: "uat";
18
+ }>;
19
+ prefix: z.ZodString;
20
+ }, z.core.$strip>;
21
+ export type Environment = z.infer<typeof environmentSchema>;
22
+ export type EnvironmentId = Pick<Environment, "name" | "prefix">;
23
+ export type EnvironmentInitStatus = {
24
+ initialized: false;
25
+ issues: EnvironmentInitIssue[];
26
+ } | {
27
+ initialized: true;
28
+ };
29
+ type CloudAccountNotInitializedIssue = {
30
+ cloudAccount: CloudAccount;
31
+ type: "CLOUD_ACCOUNT_NOT_INITIALIZED";
32
+ };
33
+ type EnvironmentInitIssue = CloudAccountNotInitializedIssue | MissingRemoteBackendIssue;
34
+ type MissingRemoteBackendIssue = {
35
+ type: "MISSING_REMOTE_BACKEND";
36
+ };
37
+ export declare function getInitializationStatus(cloudAccountService: CloudAccountService, environment: Environment): Promise<EnvironmentInitStatus>;
38
+ export declare function getTerraformBackend(cloudAccountService: CloudAccountService, environment: Environment): Promise<TerraformBackend | undefined>;
39
+ export declare function hasUserPermissionToInitialize(cloudAccountService: CloudAccountService, environment: Environment): Promise<boolean>;
40
+ export {};
@@ -0,0 +1,57 @@
1
+ import { z } from "zod/v4";
2
+ import { cloudAccountSchema, } from "./cloud-account.js";
3
+ export const environmentShort = {
4
+ dev: "d",
5
+ prod: "p",
6
+ uat: "u",
7
+ };
8
+ export const environmentSchema = z.object({
9
+ cloudAccounts: z.array(cloudAccountSchema).min(1),
10
+ name: z.enum(["dev", "prod", "uat"]),
11
+ prefix: z.string().min(2).max(4),
12
+ });
13
+ export async function getInitializationStatus(cloudAccountService, environment) {
14
+ const issues = [];
15
+ for (const cloudAccount of environment.cloudAccounts) {
16
+ const initialized = await cloudAccountService.isInitialized(cloudAccount.id, environment);
17
+ if (!initialized) {
18
+ issues.push({
19
+ cloudAccount,
20
+ type: "CLOUD_ACCOUNT_NOT_INITIALIZED",
21
+ });
22
+ }
23
+ }
24
+ const terraformBackend = await getTerraformBackend(cloudAccountService, environment);
25
+ if (!terraformBackend) {
26
+ issues.push({
27
+ type: "MISSING_REMOTE_BACKEND",
28
+ });
29
+ }
30
+ if (issues.length > 0) {
31
+ return {
32
+ initialized: false,
33
+ issues,
34
+ };
35
+ }
36
+ return {
37
+ initialized: true,
38
+ };
39
+ }
40
+ export async function getTerraformBackend(cloudAccountService, environment) {
41
+ for (const cloudAccount of environment.cloudAccounts) {
42
+ const backend = await cloudAccountService.getTerraformBackend(cloudAccount.id, environment);
43
+ if (backend) {
44
+ return backend;
45
+ }
46
+ }
47
+ return undefined;
48
+ }
49
+ export async function hasUserPermissionToInitialize(cloudAccountService, environment) {
50
+ for (const cloudAccount of environment.cloudAccounts) {
51
+ const result = await cloudAccountService.hasUserPermissionToInitialize(cloudAccount.id);
52
+ if (!result) {
53
+ return false;
54
+ }
55
+ }
56
+ return true;
57
+ }
@@ -0,0 +1,6 @@
1
+ import { z } from "zod/v4";
2
+ export declare const githubRepoSchema: z.ZodObject<{
3
+ owner: z.ZodString;
4
+ repo: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
5
+ }, z.core.$strip>;
6
+ export type GitHubRepo = z.infer<typeof githubRepoSchema>;
@@ -0,0 +1,8 @@
1
+ import { z } from "zod/v4";
2
+ export const githubRepoSchema = z.object({
3
+ owner: z.string().min(1),
4
+ repo: z
5
+ .string()
6
+ .min(1)
7
+ .transform((repo) => repo.replace(/\.git$/, "")),
8
+ });
@@ -0,0 +1,37 @@
1
+ export interface GitHubService {
2
+ /**
3
+ * Creates a pull request in a GitHub repository.
4
+ * @throws Error if pull request creation fails
5
+ */
6
+ createPullRequest(params: PullRequestBody): Promise<PullRequest>;
7
+ /**
8
+ * Gets a GitHub repository by owner and name.
9
+ * @throws RepositoryNotFoundError if repository doesn't exist (404)
10
+ * @throws Error for other failures
11
+ */
12
+ getRepository(owner: string, name: string): Promise<Repository>;
13
+ }
14
+ type PullRequestBody = {
15
+ base: string;
16
+ body: string;
17
+ head: string;
18
+ owner: string;
19
+ repo: string;
20
+ title: string;
21
+ };
22
+ export declare class PullRequest {
23
+ readonly url: string;
24
+ constructor(url: string);
25
+ }
26
+ export declare class Repository {
27
+ readonly name: string;
28
+ readonly owner: string;
29
+ get fullName(): string;
30
+ get ssh(): string;
31
+ get url(): string;
32
+ constructor(name: string, owner: string);
33
+ }
34
+ export declare class RepositoryNotFoundError extends Error {
35
+ constructor(owner: string, name: string);
36
+ }
37
+ export {};
@@ -0,0 +1,29 @@
1
+ export class PullRequest {
2
+ url;
3
+ constructor(url) {
4
+ this.url = url;
5
+ }
6
+ }
7
+ export class Repository {
8
+ name;
9
+ owner;
10
+ get fullName() {
11
+ return `${this.owner}/${this.name}`;
12
+ }
13
+ get ssh() {
14
+ return `git@github.com:${this.owner}/${this.name}.git`;
15
+ }
16
+ get url() {
17
+ return `https://github.com/${this.owner}/${this.name}`;
18
+ }
19
+ constructor(name, owner) {
20
+ this.name = name;
21
+ this.owner = owner;
22
+ }
23
+ }
24
+ export class RepositoryNotFoundError extends Error {
25
+ constructor(owner, name) {
26
+ super(`Repository ${owner}/${name} not found`);
27
+ this.name = "RepositoryNotFoundError";
28
+ }
29
+ }
@@ -0,0 +1,11 @@
1
+ import { Dependencies } from "./dependencies.js";
2
+ import { PackageManager } from "./package-json.js";
3
+ export type InfoResult = {
4
+ node?: string;
5
+ packageManager: PackageManager;
6
+ terraform?: string;
7
+ turbo?: string;
8
+ };
9
+ export type GetInfo = () => Promise<InfoResult>;
10
+ export declare const getInfo: (dependencies: Dependencies) => GetInfo;
11
+ export declare const printInfo: (result: InfoResult) => void;
@@ -0,0 +1,52 @@
1
+ import { getLogger } from "@logtape/logtape";
2
+ import { join } from "node:path";
3
+ const detectFromLockFile = async (dependencies, repositoryRoot) => {
4
+ const { repositoryReader } = dependencies;
5
+ const pnpmResult = await repositoryReader.fileExists(join(repositoryRoot, "pnpm-lock.yaml"));
6
+ if (pnpmResult.isOk() && pnpmResult.value)
7
+ return "pnpm";
8
+ const yarnResult = await repositoryReader.fileExists(join(repositoryRoot, "yarn.lock"));
9
+ if (yarnResult.isOk() && yarnResult.value)
10
+ return "yarn";
11
+ const npmResult = await repositoryReader.fileExists(join(repositoryRoot, "package-lock.json"));
12
+ if (npmResult.isOk() && npmResult.value)
13
+ return "npm";
14
+ return undefined;
15
+ };
16
+ const detectPackageManager = async (dependencies, repositoryRoot) => {
17
+ // Try to read package.json to get packageManager field
18
+ const packageJsonResult = await dependencies.packageJsonReader.readPackageJson(repositoryRoot);
19
+ const packageManager = packageJsonResult.map((pkg) => pkg.packageManager).unwrapOr(undefined) ??
20
+ (await detectFromLockFile(dependencies, repositoryRoot));
21
+ return packageManager ?? "npm";
22
+ };
23
+ const detectNodeVersion = async ({ repositoryReader }, nodeVersionFilePath) => await repositoryReader
24
+ .readFile(nodeVersionFilePath)
25
+ .map((nodeVersion) => nodeVersion.trim())
26
+ .unwrapOr(undefined);
27
+ const detectTerraformVersion = async ({ repositoryReader }, terraformVersionFilePath) => await repositoryReader
28
+ .readFile(terraformVersionFilePath)
29
+ .map((tfVersion) => tfVersion.trim())
30
+ .unwrapOr(undefined);
31
+ const detectTurboVersion = async (dependencies, repositoryRoot) => {
32
+ const packageJsonResult = await dependencies.packageJsonReader.readPackageJson(repositoryRoot);
33
+ return packageJsonResult
34
+ .map((pkg) => pkg.devDependencies.get("turbo")?.trim())
35
+ .unwrapOr(undefined);
36
+ };
37
+ export const getInfo = (dependencies) => async () => {
38
+ // Get repository root, fallback to current working directory if not in a repository
39
+ const repositoryRoot = await dependencies.repositoryReader
40
+ .findRepositoryRoot()
41
+ .unwrapOr(process.cwd());
42
+ return {
43
+ node: await detectNodeVersion({ repositoryReader: dependencies.repositoryReader }, `${repositoryRoot}/.node-version`),
44
+ packageManager: await detectPackageManager(dependencies, repositoryRoot),
45
+ terraform: await detectTerraformVersion({ repositoryReader: dependencies.repositoryReader }, `${repositoryRoot}/.terraform-version`),
46
+ turbo: await detectTurboVersion(dependencies, repositoryRoot),
47
+ };
48
+ };
49
+ export const printInfo = (result) => {
50
+ const logger = getLogger("json");
51
+ logger.info(JSON.stringify(result));
52
+ };
@@ -0,0 +1,42 @@
1
+ import { ResultAsync } from "neverthrow";
2
+ import { z } from "zod/v4";
3
+ import { Dependencies } from "./dependencies.js";
4
+ import { ValidationCheckResult } from "./validation.js";
5
+ export declare const scriptSchema: z.ZodObject<{
6
+ name: z.core.$ZodBranded<z.ZodString, "ScriptName">;
7
+ script: z.ZodString;
8
+ }, z.core.$strip>;
9
+ export declare const dependencySchema: z.ZodObject<{
10
+ name: z.ZodString;
11
+ version: z.ZodString;
12
+ }, z.core.$strip>;
13
+ declare const PackageName: z.core.$ZodBranded<z.ZodString, "PackageName">;
14
+ export type PackageName = z.infer<typeof PackageName>;
15
+ declare const packageManagerSchema: z.ZodEnum<{
16
+ npm: "npm";
17
+ pnpm: "pnpm";
18
+ yarn: "yarn";
19
+ }>;
20
+ export declare const packageJsonSchema: z.ZodObject<{
21
+ dependencies: z.ZodPipe<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<Map<string, string>, Record<string, string> | undefined>>;
22
+ devDependencies: z.ZodPipe<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>, z.ZodTransform<Map<string, string>, Record<string, string> | undefined>>;
23
+ name: z.core.$ZodBranded<z.ZodString, "PackageName">;
24
+ packageManager: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodEnum<{
25
+ npm: "npm";
26
+ pnpm: "pnpm";
27
+ yarn: "yarn";
28
+ }>>>;
29
+ scripts: z.ZodPipe<z.ZodOptional<z.ZodRecord<z.core.$ZodBranded<z.ZodString, "ScriptName">, z.ZodString>>, z.ZodTransform<Map<string & z.core.$brand<"ScriptName">, string>, Record<string & z.core.$brand<"ScriptName">, string> | undefined>>;
30
+ }, z.core.$strip>;
31
+ export type Dependency = z.infer<typeof dependencySchema>;
32
+ export type PackageJson = z.infer<typeof packageJsonSchema>;
33
+ export type PackageJsonReader = {
34
+ getDependencies(cwd: string, type: "dev" | "prod"): ResultAsync<Map<Dependency["name"], Dependency["version"]>, Error>;
35
+ getRootRequiredScripts(): Map<Script["name"], Script["script"]>;
36
+ getScripts(cwd: string): ResultAsync<Map<Script["name"], Script["script"]>, Error>;
37
+ readPackageJson(cwd: string): ResultAsync<PackageJson, Error>;
38
+ };
39
+ export type PackageManager = z.infer<typeof packageManagerSchema>;
40
+ export type Script = z.infer<typeof scriptSchema>;
41
+ export declare const checkMonorepoScripts: (dependencies: Pick<Dependencies, "packageJsonReader">, repositoryRoot: string) => Promise<ValidationCheckResult>;
42
+ export {};
@@ -0,0 +1,69 @@
1
+ import { err, ok } from "neverthrow";
2
+ import { z } from "zod/v4";
3
+ const ScriptName = z.string().brand();
4
+ export const scriptSchema = z.object({
5
+ name: ScriptName,
6
+ script: z.string(),
7
+ });
8
+ export const dependencySchema = z.object({
9
+ name: z.string(),
10
+ version: z.string(),
11
+ });
12
+ const PackageName = z.string().min(1).brand();
13
+ const scriptsSchema = z
14
+ .record(ScriptName, z.string())
15
+ .optional()
16
+ .transform((obj) => new Map(obj
17
+ ? Object.entries(obj).map(([name, script]) => [
18
+ ScriptName.parse(name),
19
+ script,
20
+ ])
21
+ : []));
22
+ const dependenciesSchema = z
23
+ // An object where keys are Dependency["name"] and values are their versions (string for now, but we could type them as well)
24
+ .record(z.string(), z.string())
25
+ .optional()
26
+ // Transform the record into a Map<Dependency["name"], Dependency["version"]>
27
+ .transform((obj) => new Map(obj
28
+ ? Object.entries(obj).map(([name, version]) => [name, version])
29
+ : []));
30
+ const packageManagerSchema = z.enum(["npm", "pnpm", "yarn"]);
31
+ export const packageJsonSchema = z.object({
32
+ dependencies: dependenciesSchema,
33
+ devDependencies: dependenciesSchema,
34
+ name: PackageName,
35
+ packageManager: z
36
+ .string()
37
+ .transform((str) => str.split("@")[0])
38
+ .pipe(packageManagerSchema)
39
+ .optional(),
40
+ scripts: scriptsSchema,
41
+ });
42
+ const findMissingScripts = (availableScripts, requiredScripts) => {
43
+ const availableScriptNames = new Set(availableScripts.keys());
44
+ const requiredScriptNames = new Set(requiredScripts.keys());
45
+ // Returns a set of scripts that are required, but not listed in the package.json
46
+ return requiredScriptNames.difference(availableScriptNames);
47
+ };
48
+ export const checkMonorepoScripts = async (dependencies, repositoryRoot) => {
49
+ const { packageJsonReader } = dependencies;
50
+ const checkName = "Monorepo Scripts";
51
+ const scriptsResult = await packageJsonReader.getScripts(repositoryRoot);
52
+ if (scriptsResult.isErr()) {
53
+ return err(scriptsResult.error);
54
+ }
55
+ const requiredScriptsMap = packageJsonReader.getRootRequiredScripts();
56
+ const missingScripts = findMissingScripts(scriptsResult.value, requiredScriptsMap);
57
+ if (missingScripts.size === 0) {
58
+ return ok({
59
+ checkName,
60
+ isValid: true,
61
+ successMessage: "Monorepo scripts are correctly set up",
62
+ });
63
+ }
64
+ return ok({
65
+ checkName,
66
+ errorMessage: `Missing required scripts: ${Array.from(missingScripts).join(", ")}`,
67
+ isValid: false,
68
+ });
69
+ };
@@ -0,0 +1,8 @@
1
+ import { z } from "zod/v4";
2
+ export declare const terraformBackendSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3
+ resourceGroupName: z.ZodString;
4
+ storageAccountName: z.ZodString;
5
+ subscriptionId: z.ZodString;
6
+ type: z.ZodLiteral<"azurerm">;
7
+ }, z.core.$strip>], "type">;
8
+ export type TerraformBackend = z.infer<typeof terraformBackendSchema>;
@@ -0,0 +1,9 @@
1
+ import { z } from "zod/v4";
2
+ export const terraformBackendSchema = z.discriminatedUnion("type", [
3
+ z.object({
4
+ resourceGroupName: z.string().min(1),
5
+ storageAccountName: z.string().min(1),
6
+ subscriptionId: z.string().min(1),
7
+ type: z.literal("azurerm"),
8
+ }),
9
+ ]);
@@ -0,0 +1,13 @@
1
+ import { ResultAsync } from "neverthrow";
2
+ import { Config } from "../config.js";
3
+ import { Dependencies } from "./dependencies.js";
4
+ import { ValidationCheckResult } from "./validation.js";
5
+ import { Workspace } from "./workspace.js";
6
+ export type RepositoryReader = {
7
+ fileExists(path: string): ResultAsync<boolean, Error>;
8
+ findRepositoryRoot(cwd?: string): ResultAsync<string, Error>;
9
+ getWorkspaces(repoRoot: string): ResultAsync<Workspace[], Error>;
10
+ readFile(path: string): ResultAsync<string, Error>;
11
+ };
12
+ export declare const checkPreCommitConfig: (dependencies: Pick<Dependencies, "repositoryReader">, repositoryRoot: string) => Promise<ValidationCheckResult>;
13
+ export declare const checkTurboConfig: (dependencies: Pick<Dependencies, "packageJsonReader" | "repositoryReader">, repositoryRoot: string, config: Config) => Promise<ValidationCheckResult>;
@@ -0,0 +1,72 @@
1
+ import { ok } from "neverthrow";
2
+ import fs from "node:path";
3
+ import coerce from "semver/functions/coerce.js";
4
+ import semverGte from "semver/functions/gte.js";
5
+ const isVersionValid = (version, minVersion) => {
6
+ const minAcceptedSemVer = coerce(minVersion);
7
+ const dependencySemVer = coerce(version);
8
+ if (!minAcceptedSemVer || !dependencySemVer) {
9
+ return false;
10
+ }
11
+ return semverGte(dependencySemVer, minAcceptedSemVer);
12
+ };
13
+ export const checkPreCommitConfig = async (dependencies, repositoryRoot) => {
14
+ const { repositoryReader } = dependencies;
15
+ const checkName = "Pre-commit Configuration";
16
+ const preCommitResult = await repositoryReader.fileExists(fs.join(repositoryRoot, ".pre-commit-config.yaml"));
17
+ if (preCommitResult.isOk() && preCommitResult.value) {
18
+ return ok({
19
+ checkName,
20
+ isValid: true,
21
+ successMessage: "Pre-commit configuration is present in the repository root",
22
+ });
23
+ }
24
+ const errorMessage = preCommitResult.isErr()
25
+ ? preCommitResult.error.message
26
+ : `Pre-commit configuration is not present in the repository root. Please add a .pre-commit-config.yaml file to the repository root.`;
27
+ return ok({
28
+ checkName,
29
+ errorMessage,
30
+ isValid: false,
31
+ });
32
+ };
33
+ export const checkTurboConfig = async (dependencies, repositoryRoot, config) => {
34
+ const { packageJsonReader, repositoryReader } = dependencies;
35
+ const checkName = "Turbo Configuration";
36
+ const turboResult = await repositoryReader.fileExists(fs.join(repositoryRoot, "turbo.json"));
37
+ if (turboResult.isErr()) {
38
+ return ok({
39
+ checkName,
40
+ errorMessage: turboResult.error.message,
41
+ isValid: false,
42
+ });
43
+ }
44
+ const dependenciesResult = await packageJsonReader.getDependencies(repositoryRoot, "dev");
45
+ if (dependenciesResult.isErr()) {
46
+ return ok({
47
+ checkName,
48
+ errorMessage: dependenciesResult.error.message,
49
+ isValid: false,
50
+ });
51
+ }
52
+ const turboVersion = dependenciesResult.value.get("turbo");
53
+ if (!turboVersion) {
54
+ return ok({
55
+ checkName,
56
+ errorMessage: "Turbo dependency not found in devDependencies. Please add 'turbo' to your devDependencies.",
57
+ isValid: false,
58
+ });
59
+ }
60
+ if (!isVersionValid(turboVersion, config.minVersions.turbo)) {
61
+ return ok({
62
+ checkName,
63
+ errorMessage: `Turbo version (${turboVersion}) is too low. Minimum required version is ${config.minVersions.turbo}.`,
64
+ isValid: false,
65
+ });
66
+ }
67
+ return ok({
68
+ checkName,
69
+ isValid: true,
70
+ successMessage: "Turbo configuration is present in the monorepo root and turbo dependency is installed",
71
+ });
72
+ };
@@ -0,0 +1,16 @@
1
+ import { Result } from "neverthrow";
2
+ export type ValidationCheck = FailedCheck | SuccessfulCheck;
3
+ export type ValidationCheckResult = Result<ValidationCheck, Error>;
4
+ export type ValidationReporter = {
5
+ reportCheckResult(result: ValidationCheck): void;
6
+ };
7
+ type FailedCheck = Pick<SuccessfulCheck, "checkName"> & {
8
+ errorMessage: string;
9
+ isValid: false;
10
+ };
11
+ type SuccessfulCheck = {
12
+ checkName: string;
13
+ isValid: true;
14
+ successMessage: string;
15
+ };
16
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { z } from "zod/v4";
2
+ import { Dependencies } from "./dependencies.js";
3
+ import { ValidationCheckResult } from "./validation.js";
4
+ export declare const workspaceSchema: z.ZodObject<{
5
+ name: z.core.$ZodBranded<z.ZodString, "WorkspaceName">;
6
+ path: z.ZodString;
7
+ }, z.core.$strip>;
8
+ export type Workspace = z.infer<typeof workspaceSchema>;
9
+ export declare const checkWorkspaces: (dependencies: Pick<Dependencies, "repositoryReader">, monorepoDir: string) => Promise<ValidationCheckResult>;
@@ -0,0 +1,32 @@
1
+ import { ok } from "neverthrow";
2
+ import { z } from "zod/v4";
3
+ const WorkspaceName = z.string().min(1).brand();
4
+ export const workspaceSchema = z.object({
5
+ name: WorkspaceName,
6
+ path: z.string(),
7
+ });
8
+ export const checkWorkspaces = async (dependencies, monorepoDir) => {
9
+ const { repositoryReader } = dependencies;
10
+ const checkName = "Workspaces";
11
+ const workspacesResult = await repositoryReader.getWorkspaces(monorepoDir);
12
+ if (workspacesResult.isErr()) {
13
+ return ok({
14
+ checkName,
15
+ errorMessage: "Something is wrong with the workspaces configuration. If you need help, please contact the DevEx team.",
16
+ isValid: false,
17
+ });
18
+ }
19
+ const { length: workspaceNumber } = workspacesResult.value;
20
+ if (workspaceNumber === 0) {
21
+ return ok({
22
+ checkName,
23
+ errorMessage: "No workspace configuration found. Make sure to configure workspaces in pnpm-workspace.yaml.",
24
+ isValid: false,
25
+ });
26
+ }
27
+ return ok({
28
+ checkName,
29
+ isValid: true,
30
+ successMessage: `Found ${workspaceNumber} workspace${workspaceNumber === 1 ? "" : "s"}`,
31
+ });
32
+ };
@@ -0,0 +1,2 @@
1
+ import "core-js/actual/set/index.js";
2
+ export declare const runCli: (version: string) => void;
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ import "core-js/actual/set/index.js";
2
+ import { Octokit } from "octokit";
3
+ import codemodRegistry from "./adapters/codemods/index.js";
4
+ import { makeCli } from "./adapters/commander/index.js";
5
+ import { makeValidationReporter } from "./adapters/logtape/validation-reporter.js";
6
+ import { makePackageJsonReader } from "./adapters/node/package-json.js";
7
+ import { makeRepositoryReader } from "./adapters/node/repository.js";
8
+ import { OctokitGitHubService } from "./adapters/octokit/index.js";
9
+ import { getConfig } from "./config.js";
10
+ import { getInfo } from "./domain/info.js";
11
+ import { applyCodemodById } from "./use-cases/apply-codemod.js";
12
+ import { listCodemods } from "./use-cases/list-codemods.js";
13
+ export const runCli = (version) => {
14
+ // Creating the adapters
15
+ const repositoryReader = makeRepositoryReader();
16
+ const packageJsonReader = makePackageJsonReader();
17
+ const validationReporter = makeValidationReporter();
18
+ const octokit = new Octokit({
19
+ auth: process.env.GITHUB_TOKEN,
20
+ });
21
+ const gitHubService = new OctokitGitHubService(octokit);
22
+ const deps = {
23
+ gitHubService,
24
+ packageJsonReader,
25
+ repositoryReader,
26
+ validationReporter,
27
+ };
28
+ const config = getConfig();
29
+ const useCases = {
30
+ applyCodemodById: applyCodemodById(codemodRegistry, getInfo(deps)),
31
+ listCodemods: listCodemods(codemodRegistry),
32
+ };
33
+ const program = makeCli(deps, config, useCases, version);
34
+ program.parse();
35
+ };
@@ -0,0 +1 @@
1
+ export {};