@vaharoni/devops 1.1.0 → 1.1.2

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 (116) hide show
  1. package/dist/cli/init.d.ts.map +1 -1
  2. package/dist/cli/init.js +196 -46
  3. package/dist/libs/init-generator.d.ts +41 -0
  4. package/dist/libs/init-generator.d.ts.map +1 -0
  5. package/dist/libs/init-generator.js +123 -0
  6. package/package.json +3 -1
  7. package/src/cli/init.ts +221 -48
  8. package/src/libs/init-generator.ts +165 -0
  9. package/src/target-templates/README.md +1 -0
  10. package/src/target-templates/cluster-resource-options/README.md +2 -0
  11. package/src/target-templates/{.devops → cluster-resource-options}/postgres/production/configurations/07-SGObjectStorage.yaml +1 -1
  12. package/src/target-templates/{.devops → cluster-resource-options}/postgres/production/configurations/08-SGScript.yaml +1 -1
  13. package/src/target-templates/{.devops → cluster-resource-options}/postgres/staging/configurations/07-SGObjectStorage.yaml +1 -1
  14. package/src/target-templates/{.devops → cluster-resource-options}/postgres/staging/configurations/08-SGScript.yaml +1 -1
  15. package/src/target-templates/infra-variants/README.md +2 -0
  16. package/src/target-templates/infra-variants/digitalocean/.devops/config/constants.yaml +18 -0
  17. package/src/target-templates/infra-variants/digitalocean/.github/workflows/k8s-build.yaml +73 -0
  18. package/src/target-templates/infra-variants/gcloud/.devops/config/constants.yaml +15 -0
  19. package/src/target-templates/infra-variants/gcloud/.devops/manifests/ingress.yaml.hb +22 -0
  20. package/src/target-templates/infra-variants/gcloud/.github/workflows/k8s-build.yaml +77 -0
  21. package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/config/constants.yaml +4 -4
  22. package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/abandoned/harbor-values.yaml +2 -2
  23. package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/abandoned/hcloud-config.yaml +1 -1
  24. package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/harbor-cert.yaml +2 -2
  25. package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/harbor-values.yaml +2 -2
  26. package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/hcloud-config.yaml +1 -1
  27. package/src/target-templates/{.github → infra-variants/hetzner/.github}/workflows/k8s-build.yaml +0 -4
  28. package/src/target-templates/lang-variants-common/README.md +4 -0
  29. package/src/target-templates/{.devops → lang-variants-common/python/.devops}/config/images.yaml +4 -4
  30. package/src/target-templates/{.devops → lang-variants-common/python/.devops}/manifests/prefect.yaml.hb +1 -0
  31. package/src/target-templates/{pyproject.toml → lang-variants-common/python/pyproject.toml} +1 -1
  32. package/src/target-templates/lang-variants-common/typescript/.devops/config/images.yaml +69 -0
  33. package/src/target-templates/lang-variants-common/typescript/.devops/manifests/_index.yaml +19 -0
  34. package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/cron-jobs.yaml.hb +1 -0
  35. package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/db-migrate-job.yaml.hb +1 -0
  36. package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/deployment-debug.yaml.hb +1 -0
  37. package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/deployment-process.yaml.hb +1 -0
  38. package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/deployment-web.yaml.hb +1 -0
  39. package/src/target-templates/lang-variants-common/typescript/.github/actions/connect-to-gke@v1/action.yaml +43 -0
  40. package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/connect-to-infra@v1/action.yaml +22 -1
  41. package/src/target-templates/lang-variants-prisma/README.md +3 -0
  42. package/src/target-templates/config/.env.development +0 -1
  43. package/src/target-templates/config/.env.global +0 -4
  44. package/src/target-templates/config/.env.test +0 -1
  45. package/src/target-templates/libs/example-node-lib/bun.lock +0 -27
  46. /package/src/target-templates/{.devops/infra/test.yaml → cluster-resource-options/dns-test/dns-test.yaml} +0 -0
  47. /package/src/target-templates/{.devops → cluster-resource-options}/milvus/production/milvus-values.yaml +0 -0
  48. /package/src/target-templates/{.devops → cluster-resource-options}/milvus/staging/milvus-values.yaml +0 -0
  49. /package/src/target-templates/{.devops/infra → cluster-resource-options/monitoring-ingress}/monitoring-ingress.yaml +0 -0
  50. /package/src/target-templates/{.devops/postgres/DailyOperatorRestart.yaml → cluster-resource-options/postgres/daily-operator-restart.yaml} +0 -0
  51. /package/src/target-templates/{.devops → cluster-resource-options}/postgres/production/cluster/PodDisruptionBudget.yaml +0 -0
  52. /package/src/target-templates/{.devops → cluster-resource-options}/postgres/production/cluster/SGCluster.yaml +0 -0
  53. /package/src/target-templates/{.devops → cluster-resource-options}/postgres/production/cluster/StackGres-alerts.yaml +0 -0
  54. /package/src/target-templates/{.devops → cluster-resource-options}/postgres/production/configurations/06-SGDistributedLogs.yaml +0 -0
  55. /package/src/target-templates/{.devops/infra → cluster-resource-options/postgres}/stackgres-ui-ingress.yaml +0 -0
  56. /package/src/target-templates/{.devops → cluster-resource-options}/postgres/staging/cluster/SGCluster.yaml +0 -0
  57. /package/src/target-templates/{.devops → cluster-resource-options}/prefect/production/prefect-values.yaml +0 -0
  58. /package/src/target-templates/{.devops → cluster-resource-options}/prefect/staging/prefect-values.yaml +0 -0
  59. /package/src/target-templates/{.devops → cluster-resource-options}/redis/production/redis-values.yaml +0 -0
  60. /package/src/target-templates/{.devops → cluster-resource-options}/redis/staging/redis-values.yaml +0 -0
  61. /package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/cert-manager.yaml +0 -0
  62. /package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/ingress-nginx-annotations.yaml +0 -0
  63. /package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/ingress-nginx-configmap.yaml +0 -0
  64. /package/src/target-templates/{.devops → infra-variants/hetzner/.devops}/infra/hetzner/retain-storage-class.yaml +0 -0
  65. /package/src/target-templates/{.devops → lang-variants-common/python/.devops}/docker-images/python-services/python-exec.sh +0 -0
  66. /package/src/target-templates/{.devops → lang-variants-common/python/.devops}/docker-images/python-services/python-run.sh +0 -0
  67. /package/src/target-templates/{.devops → lang-variants-common/python/.devops}/docker-images/python-services.Dockerfile +0 -0
  68. /package/src/target-templates/{.devops → lang-variants-common/python/.devops}/manifests/_index.yaml +0 -0
  69. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-data-pipeline/pyproject.toml +0 -0
  70. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-data-pipeline/src/example_data_pipeline/main.py +0 -0
  71. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-python/pyproject.toml +0 -0
  72. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-python/src/example_python/__init__.py +0 -0
  73. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-python/src/example_python/main.py +0 -0
  74. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-python/src/example_python/scripts.py +0 -0
  75. /package/src/target-templates/{applications → lang-variants-common/python/applications}/example-python/tests/__init__.py +0 -0
  76. /package/src/target-templates/{devopspy → lang-variants-common/python/devopspy} +0 -0
  77. /package/src/target-templates/{libs → lang-variants-common/python/libs}/example-python-lib/pyproject.toml +0 -0
  78. /package/src/target-templates/{libs → lang-variants-common/python/libs}/example-python-lib/src/example_python_lib/__init__.py +0 -0
  79. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/docker-images/common/docker-common.sh +0 -0
  80. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/docker-images/node-services/node-exec.sh +0 -0
  81. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/docker-images/node-services/node-run.sh +0 -0
  82. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/docker-images/node-services.Dockerfile +0 -0
  83. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/env.example.yaml +0 -0
  84. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/ingress.yaml.hb +0 -0
  85. /package/src/target-templates/{.devops → lang-variants-common/typescript/.devops}/manifests/service.yaml.hb +0 -0
  86. /package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/build-image@v1/action.yaml +0 -0
  87. /package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/connect-to-digital-ocean@v1/action.yaml +0 -0
  88. /package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/connect-to-hetzner@v1/action.yaml +0 -0
  89. /package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/db-migrate@v1/action.yaml +0 -0
  90. /package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/deploy-image@v1/action.yaml +0 -0
  91. /package/src/target-templates/{.github → lang-variants-common/typescript/.github}/actions/setup-prereq@v1/action.yaml +0 -0
  92. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/example-node/index.ts +0 -0
  93. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/example-node/package.json +0 -0
  94. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/example-node/tsconfig.json +0 -0
  95. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/jobs/README.md +0 -0
  96. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/jobs/index.ts +0 -0
  97. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/jobs/package.json +0 -0
  98. /package/src/target-templates/{applications → lang-variants-common/typescript/applications}/jobs/tsconfig.json +0 -0
  99. /package/src/target-templates/{devops → lang-variants-common/typescript/devops} +0 -0
  100. /package/src/target-templates/{libs → lang-variants-common/typescript/libs}/example-node-lib/index.ts +0 -0
  101. /package/src/target-templates/{libs → lang-variants-common/typescript/libs}/example-node-lib/package.json +0 -0
  102. /package/src/target-templates/{libs → lang-variants-common/typescript/libs}/example-node-lib/tsconfig.json +0 -0
  103. /package/src/target-templates/{tmp → lang-variants-common/typescript/tmp}/.gitkeep +0 -0
  104. /package/src/target-templates/{tsconfig.json → lang-variants-common/typescript/tsconfig.json} +0 -0
  105. /package/src/target-templates/{db → lang-variants-prisma/python/db}/db/__init__.py +0 -0
  106. /package/src/target-templates/{db → lang-variants-prisma/python/db}/db/db_client_test.py +0 -0
  107. /package/src/target-templates/{db → lang-variants-prisma/python/db}/pyproject.toml +0 -0
  108. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/db-client-test.ts +0 -0
  109. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/db-client.ts +0 -0
  110. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/env.yaml +0 -0
  111. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/package.json +0 -0
  112. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/prisma/schema.prisma +0 -0
  113. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/prisma-setup-vitest.ts +0 -0
  114. /package/src/target-templates/{db → lang-variants-prisma/typescript/db}/tsconfig.json +0 -0
  115. /package/src/target-templates/{dml → lang-variants-prisma/typescript/dml}/package.json +0 -0
  116. /package/src/target-templates/{dml → lang-variants-prisma/typescript/dml}/tsconfig.json +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,gBAAgB,EAAqB,MAAM,UAAU,CAAC;AAgB/D,iBAAe,GAAG,CAAC,MAAM,EAAE,gBAAgB,iBAG1C;;;;;;;;AAED,wBAEE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAqB,MAAM,UAAU,CAAC;AAmB/D,iBAAe,GAAG,CAAC,MAAM,EAAE,gBAAgB,iBAG1C;;;;;;;;AAED,wBAEE"}
package/dist/cli/init.js CHANGED
@@ -1,11 +1,8 @@
1
- import url from "url";
2
- import path from "path";
3
- import fs from "fs-extra";
4
- const __file__ = url.fileURLToPath(import.meta.url);
5
- const __root__ = path.join(path.dirname(__file__), "../..");
6
- const templatesDir = path.join(__root__, "src/target-templates");
7
- const targetDir = process.cwd(); // User's current working directory
1
+ import inquirer from "inquirer";
2
+ import { InitGenerator } from "../libs/init-generator";
8
3
  import { CLICommandParser, printUsageAndExit } from "./common";
4
+ import chalk from "chalk";
5
+ import fs from 'fs-extra';
9
6
  const oneLiner = "Initializes the devops utility by copying template files to the current folder";
10
7
  const keyExamples = `$ devops init`;
11
8
  const usage = `
@@ -20,52 +17,205 @@ EXAMPLES
20
17
  async function run(cmdObj) {
21
18
  if (cmdObj.help)
22
19
  printUsageAndExit(usage);
23
- copyTemplates();
20
+ createFiles();
24
21
  }
25
22
  export default {
26
23
  init: { oneLiner, keyExamples, run },
27
24
  };
28
- async function copyTemplates() {
29
- console.log(`Initializing devops files from ${templatesDir} to ${targetDir}`);
30
- try {
31
- // Copy files without overriding existing ones
32
- await fs.copy(templatesDir, targetDir, {
33
- overwrite: false,
34
- errorOnExist: false,
35
- dereference: false,
36
- });
37
- console.log(successMessage);
25
+ async function createFiles() {
26
+ const tc = new InitGenerator();
27
+ const userChoices = await getUserChoices(tc.projectName);
28
+ // Language variants
29
+ tc.addCopiedFolder("lang-variants-common/typescript", ".");
30
+ if (userChoices.usePython) {
31
+ tc.addCopiedFolder("lang-variants-common/python", ".");
32
+ tc.enableSubtitution("pyproject.toml");
38
33
  }
39
- catch (error) {
40
- console.error("❌ Failed to initialize devops files:", error);
41
- process.exit(1);
34
+ tc.enableSubtitution(".devops/config/images.yaml");
35
+ tc.setMessageGenerator(".envrc", envrcMessage);
36
+ // gitignore
37
+ const gitIgnore = gitIgnoreContent(userChoices.infraVariant, userChoices.usePython);
38
+ tc.addGeneratedFile(".gitignore", gitIgnore);
39
+ tc.setMessageGenerator(".gitignore", gitignoreMessageGen(gitIgnore));
40
+ // Infra variants
41
+ tc.addCopiedFolder(`infra-variants/${userChoices.infraVariant}`, ".");
42
+ tc.enableSubtitution(".devops/config/constants.yaml");
43
+ if (userChoices.infraVariant === "hetzner") {
44
+ tc.enableSubtitution(".devops/infra/hetzner/harbor-cert.yaml");
45
+ tc.enableSubtitution(".devops/infra/hetzner/harbor-values.yaml");
46
+ tc.enableSubtitution(".devops/infra/hetzner/hcloud-config.yaml");
42
47
  }
48
+ // Prisma
49
+ if (userChoices.usePrisma) {
50
+ tc.addCopiedFolder("lang-variants-prisma/typescript", ".");
51
+ if (userChoices.usePython) {
52
+ tc.addCopiedFolder("lang-variants-prisma/python", ".");
53
+ }
54
+ }
55
+ // Cluster resources
56
+ const clusterResources = new Set(userChoices.clusterResources);
57
+ if (clusterResources.has("dns-test")) {
58
+ tc.addCopiedFolder("cluster-resource-options/dns-test", ".devops/infra/dns-test");
59
+ }
60
+ if (clusterResources.has("monitoring-ingress")) {
61
+ tc.addCopiedFolder("cluster-resource-options/monitoring-ingress", ".devops/infra/monitoring-ingress");
62
+ }
63
+ if (clusterResources.has("postgres")) {
64
+ tc.addCopiedFolder("cluster-resource-options/postgres", ".devops/infra/postgres");
65
+ // prettier-ignore
66
+ tc.enableSubtitution(".devops/infra/postgres/staging/configurations/07-SGObjectStorage.yaml");
67
+ // prettier-ignore
68
+ tc.enableSubtitution(".devops/infra/postgres/staging/configurations/08-SGScript.yaml");
69
+ // prettier-ignore
70
+ tc.enableSubtitution(".devops/infra/postgres/production/configurations/07-SGObjectStorage.yaml");
71
+ // prettier-ignore
72
+ tc.enableSubtitution(".devops/infra/postgres/production/configurations/08-SGScript.yaml");
73
+ }
74
+ if (clusterResources.has("redis")) {
75
+ tc.addCopiedFolder("cluster-resource-options/redis", ".devops/infra/redis");
76
+ }
77
+ if (clusterResources.has("milvus")) {
78
+ tc.addCopiedFolder("cluster-resource-options/milvus", ".devops/infra/milvus");
79
+ }
80
+ if (clusterResources.has("prefect") && userChoices.usePython) {
81
+ tc.addCopiedFolder("cluster-resource-options/prefect", ".devops/infra/prefect");
82
+ }
83
+ tc.run({
84
+ substitution: {
85
+ 'PROJECT_NAME': userChoices.projectName,
86
+ 'STAGING_DOMAIN': userChoices.stagingDomain,
87
+ 'PRODUCTION_DOMAIN': userChoices.productionDomain,
88
+ 'GCLOUD_PROJECT_ID': userChoices.gcloudProjectId,
89
+ 'REGISTRY_IMAGE_PATH_PREFIX': userChoices.registryImagePathPrefix,
90
+ 'REGISTRY_BASE_URL': userChoices.registryBaseUrl,
91
+ },
92
+ messages: [
93
+ packageJsonMessage(userChoices.usePrisma)
94
+ ]
95
+ });
43
96
  }
44
- const successMessage = `
45
- Devops files initialized successfully!
46
-
47
- To finish the setup:
48
-
49
- 1. add the following entry to the main package.json:
50
- "workspaces": [
51
- "libs/**",
52
- "applications/**",
97
+ function packageJsonMessage(usePrisma) {
98
+ const prismaMessage = usePrisma
99
+ ? `,
53
100
  "db/**",
54
- "dml/**"
55
- ],
56
-
57
- 2. add the following to your .gitignore:
58
- **/.env*
101
+ "dml/**"`
102
+ : "";
103
+ return `add the following entry to the main ${chalk.blue("package.json")}:
104
+ ${chalk.yellow(`"workspaces": [
105
+ "libs/**",
106
+ "applications/**"${prismaMessage}
107
+ ],`)}`;
108
+ }
109
+ function gitIgnoreContent(infraVariant, usePython) {
110
+ const common = `**/.env*
59
111
  config/kubeconfig
60
112
  tmp/**
61
- !tmp/**/.gitkeep
62
- venv/
63
- **/__pycache__
64
-
65
- 3. optionally create an .envrc file with the following content and run direnv allow:
66
- if [ -f "$PWD/config/kubeconfig" ]; then
67
- export KUBECONFIG="$PWD/config/kubeconfig"
68
- else
69
- export KUBECONFIG="$HOME/.kube/config"
70
- fi
71
- `;
113
+ !tmp/**/.gitkeep`;
114
+ const gcloud = infraVariant === 'gcloud'
115
+ ? 'config/gke_gcloud_auth_plugin_cache'
116
+ : null;
117
+ const python = usePython
118
+ ? `venv/
119
+ **/__pycache__`
120
+ : null;
121
+ return [common, gcloud, python].filter(Boolean).join('\n');
122
+ }
123
+ function gitignoreMessageGen(content) {
124
+ return (exists) => {
125
+ if (!exists)
126
+ return;
127
+ return `add the following to your ${chalk.blue(".gitignore")}:
128
+ ${chalk.yellow(content)}`;
129
+ };
130
+ }
131
+ function envrcMessage(targetExists, fileInfo) {
132
+ if (fileInfo.type !== 'copied')
133
+ throw new Error(`envrcMessage() expects a copied file, got ${fileInfo.type}`);
134
+ if (targetExists) {
135
+ const content = fs.readFileSync(fileInfo.sourceAbs, 'utf-8');
136
+ return `add the following to your ${chalk.blue(".envrc")} and run ${chalk.yellow("direnv allow")}:
137
+ ${chalk.yellow(content)}`;
138
+ }
139
+ else {
140
+ return `Enable ${chalk.blue(".envrc")} by installing ${chalk.blue('direnv')} and running ${chalk.yellow("direnv allow")}`;
141
+ }
142
+ }
143
+ function getUserChoices(projectName) {
144
+ const defaultProjectName = projectName || "changeme";
145
+ return inquirer.prompt([
146
+ {
147
+ type: "input",
148
+ name: "projectName",
149
+ message: `Enter the project name (default: '${defaultProjectName}')`,
150
+ default: defaultProjectName,
151
+ },
152
+ {
153
+ type: "input",
154
+ name: "stagingDomain",
155
+ message: "Enter the staging domain (default: 'staging.com')",
156
+ default: "staging.com",
157
+ },
158
+ {
159
+ type: "input",
160
+ name: "productionDomain",
161
+ message: "Enter the production domain (default: 'production.com')",
162
+ default: "production.com",
163
+ },
164
+ {
165
+ type: "list",
166
+ name: "infraVariant",
167
+ message: "Where does your cluster run?",
168
+ choices: [
169
+ { name: "Google Cloud", value: "gcloud" },
170
+ { name: "Digital Ocean", value: "digitalocean" },
171
+ { name: "Hetzner", value: "hetzner" },
172
+ ],
173
+ },
174
+ {
175
+ type: "input",
176
+ name: "gcloudProjectId",
177
+ message: "Enter the GCP project ID (default: 'changeme')",
178
+ default: "changeme",
179
+ when: (answers) => answers.infraVariant === "gcloud",
180
+ },
181
+ {
182
+ type: "input",
183
+ name: "registryImagePathPrefix",
184
+ message: (answers) => `Enter your Digital Ocean container registry name (default: '${answers.projectName}')`,
185
+ default: (answers) => answers.projectName,
186
+ when: (answers) => answers.infraVariant === "digitalocean",
187
+ },
188
+ {
189
+ type: "input",
190
+ name: "registryBaseUrl",
191
+ message: (answers) => `Enter your registry base URL (default: 'registry.${answers.stagingDomain}')`,
192
+ default: (answers) => `registry.${answers.stagingDomain}`,
193
+ when: (answers) => answers.infraVariant === "hetzner",
194
+ },
195
+ {
196
+ type: "confirm",
197
+ name: "usePython",
198
+ message: "Add support for Python?",
199
+ default: true,
200
+ },
201
+ {
202
+ type: "confirm",
203
+ name: "usePrisma",
204
+ message: "Add support for Prisma?",
205
+ default: true,
206
+ },
207
+ {
208
+ type: "checkbox",
209
+ name: "clusterResources",
210
+ message: "Optional manifests and helm charts to add",
211
+ choices: (answers) => [
212
+ { name: "Manifest to test DNS setup", value: "dns-test" },
213
+ { name: "Manifest to setup ingress for graphana and prometheus", value: "monitoring-ingress" },
214
+ { name: "Stackgres CRDs and manifests for Postgres", value: "postgres" },
215
+ { name: "Redis Helm chart values", value: "redis" },
216
+ { name: "Milvus helm chart values", value: "milvus" },
217
+ ...(answers.usePython ? [{ name: "Prefect Helm chart values", value: "prefect" }] : [])
218
+ ]
219
+ }
220
+ ]);
221
+ }
@@ -0,0 +1,41 @@
1
+ type MessageGeneratorFn = (targetExists: boolean, fileInfo: InitGeneratorFileInfo) => string | null | undefined;
2
+ type CommonFileInfo = {
3
+ targetRel: string;
4
+ targetAbs: string;
5
+ targetFolderAbs: string;
6
+ targetExists: boolean;
7
+ messageGenerator?: MessageGeneratorFn;
8
+ };
9
+ export type InitGeneratorCopiedFileInfo = CommonFileInfo & {
10
+ type: "copied";
11
+ sourceRel: string;
12
+ sourceAbs: string;
13
+ enableSubstitution?: boolean;
14
+ };
15
+ type InitGeneratorGeneratedFileInfo = CommonFileInfo & {
16
+ type: "generated";
17
+ content: string;
18
+ };
19
+ export type InitGeneratorFileInfo = InitGeneratorCopiedFileInfo | InitGeneratorGeneratedFileInfo;
20
+ export declare class InitGenerator {
21
+ projectName?: string;
22
+ /** The key is targetRel */
23
+ files: Record<string, InitGeneratorFileInfo>;
24
+ constructor();
25
+ _ensureFileExists(targetRel: string): void;
26
+ enableSubtitution(targetRel: string): void;
27
+ setMessageGenerator(targetRel: string, messageGen: MessageGeneratorFn): void;
28
+ addGeneratedFile(targetRel: string, content: string): void;
29
+ /**
30
+ * @param source relative path under the templates folder. All files and folders under `source` are copied directly under `target`.
31
+ * @param target relative path under the project root folder.
32
+ * If the target file exists already in `files`, it will be overridden.
33
+ */
34
+ addCopiedFolder(source: string, target: string): void;
35
+ run({ substitution, messages, }: {
36
+ substitution?: Record<string, string | undefined>;
37
+ messages?: string[];
38
+ }): void;
39
+ }
40
+ export {};
41
+ //# sourceMappingURL=init-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-generator.d.ts","sourceRoot":"","sources":["../../src/libs/init-generator.ts"],"names":[],"mappings":"AAWA,KAAK,kBAAkB,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAEhH,KAAK,cAAc,GAAG;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;CACvC,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG,cAAc,GAAE;IACxD,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAA;AAED,KAAK,8BAA8B,GAAG,cAAc,GAAG;IACrD,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,GAAG,8BAA8B,CAAC;AAEjG,qBAAa,aAAa;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAM;;IASlD,iBAAiB,CAAC,SAAS,EAAE,MAAM;IAMnC,iBAAiB,CAAC,SAAS,EAAE,MAAM;IAQnC,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB;IAKrE,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAcnD;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAsB9C,GAAG,CAAC,EACF,YAAiB,EACjB,QAAa,GACd,EAAG;QACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;QAClD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB;CAkDF"}
@@ -0,0 +1,123 @@
1
+ import url from "url";
2
+ import path from "path";
3
+ import fs from "fs-extra";
4
+ import chalk from "chalk";
5
+ import fg from 'fast-glob';
6
+ const __file__ = url.fileURLToPath(import.meta.url);
7
+ const __root__ = path.join(path.dirname(__file__), "../..");
8
+ const templatesDir = path.join(__root__, "src/target-templates");
9
+ const targetDir = process.cwd(); // User's current working directory
10
+ export class InitGenerator {
11
+ projectName;
12
+ /** The key is targetRel */
13
+ files = {};
14
+ constructor() {
15
+ if (fs.existsSync("package.json")) {
16
+ const packageJson = fs.readJSONSync("package.json");
17
+ this.projectName = packageJson.name;
18
+ }
19
+ }
20
+ _ensureFileExists(targetRel) {
21
+ if (!this.files[targetRel]) {
22
+ throw new Error(`File for target "${targetRel}" not found.`);
23
+ }
24
+ }
25
+ enableSubtitution(targetRel) {
26
+ this._ensureFileExists(targetRel);
27
+ if (this.files[targetRel].type !== "copied") {
28
+ throw new Error(`File for target "${targetRel}" is not a copied file.`);
29
+ }
30
+ this.files[targetRel].enableSubstitution = true;
31
+ }
32
+ setMessageGenerator(targetRel, messageGen) {
33
+ this._ensureFileExists(targetRel);
34
+ this.files[targetRel].messageGenerator = messageGen;
35
+ }
36
+ addGeneratedFile(targetRel, content) {
37
+ const targetAbs = path.join(targetDir, targetRel);
38
+ const targetFolderAbs = path.dirname(targetAbs);
39
+ const exists = fs.existsSync(targetAbs);
40
+ this.files[targetRel] = {
41
+ type: "generated",
42
+ targetRel,
43
+ targetAbs,
44
+ targetFolderAbs,
45
+ targetExists: exists,
46
+ content,
47
+ };
48
+ }
49
+ /**
50
+ * @param source relative path under the templates folder. All files and folders under `source` are copied directly under `target`.
51
+ * @param target relative path under the project root folder.
52
+ * If the target file exists already in `files`, it will be overridden.
53
+ */
54
+ addCopiedFolder(source, target) {
55
+ const pathPrefix = path.join(templatesDir, source);
56
+ const glob = path.join(pathPrefix, '**/*');
57
+ fg.globSync(glob, { dot: true }).forEach((sourceAbs) => {
58
+ const sourceRel = path.relative(templatesDir, sourceAbs);
59
+ const pathUnderSource = path.relative(pathPrefix, sourceAbs);
60
+ const targetRel = path.join(target, pathUnderSource);
61
+ const targetAbs = path.join(targetDir, targetRel);
62
+ const targetFolderAbs = path.dirname(targetAbs);
63
+ const exists = fs.existsSync(targetAbs);
64
+ this.files[targetRel] = {
65
+ type: "copied",
66
+ sourceRel,
67
+ targetRel,
68
+ sourceAbs,
69
+ targetAbs,
70
+ targetFolderAbs,
71
+ targetExists: exists,
72
+ };
73
+ });
74
+ }
75
+ run({ substitution = {}, messages = [], }) {
76
+ const fileMessages = [];
77
+ Object.values(this.files).forEach((fileInfo) => {
78
+ if (fileInfo.messageGenerator) {
79
+ const message = fileInfo.messageGenerator(fileInfo.targetExists, fileInfo);
80
+ if (message) {
81
+ fileMessages.push(message);
82
+ }
83
+ }
84
+ if (fileInfo.targetExists) {
85
+ console.log(`Skipped ${chalk.yellow(fileInfo.targetRel)} (exists)`);
86
+ return;
87
+ }
88
+ // Create or copy
89
+ if (!fs.existsSync(fileInfo.targetFolderAbs)) {
90
+ fs.mkdirSync(fileInfo.targetFolderAbs, { recursive: true });
91
+ }
92
+ if (fileInfo.type === 'generated') {
93
+ fs.writeFileSync(fileInfo.targetAbs, fileInfo.content, 'utf8');
94
+ }
95
+ else if (fileInfo.enableSubstitution) {
96
+ const content = fs.readFileSync(fileInfo.sourceAbs, 'utf8');
97
+ const substitutedContent = content.replace(/\$([A-Z_]+)/g, (_, varName) => {
98
+ const value = substitution[varName];
99
+ if (!value) {
100
+ throw new Error(`${chalk.blue("TemplateCopier.run()")}: Variable ${chalk.yellow(varName)} is needed by ${chalk.yellow(fileInfo.targetRel)} but is undefined.`);
101
+ }
102
+ return value;
103
+ });
104
+ fs.writeFileSync(fileInfo.targetAbs, substitutedContent);
105
+ }
106
+ else {
107
+ fs.copySync(fileInfo.sourceAbs, fileInfo.targetAbs, {
108
+ overwrite: false,
109
+ errorOnExist: false,
110
+ dereference: false,
111
+ });
112
+ }
113
+ console.log(`Created ${chalk.green(fileInfo.targetRel)}`);
114
+ });
115
+ const allMessages = [...messages, ...fileMessages];
116
+ if (!allMessages.length)
117
+ return;
118
+ console.log(chalk.blue("\nNext steps:"));
119
+ allMessages.forEach((msg, i) => {
120
+ console.log(`${i + 1}. ${msg}\n`);
121
+ });
122
+ }
123
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vaharoni/devops",
3
3
  "type": "module",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "description": "Devops utility",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -34,9 +34,11 @@
34
34
  "@iarna/toml": "^2.2.5",
35
35
  "chalk": "^5.3.0",
36
36
  "concurrently": "^9.1.2",
37
+ "fast-glob": "^3.3.3",
37
38
  "fs-extra": "11.2.0",
38
39
  "glob": "^11.0.1",
39
40
  "handlebars": "^4.7.8",
41
+ "inquirer": "^12.7.0",
40
42
  "lodash": "^4.17.21",
41
43
  "tsx": "^4.19.3",
42
44
  "yaml": "^2.4.2",