@expressots/cli 3.0.0 → 4.0.0-preview.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.
- package/README.md +41 -95
- package/bin/cicd/cli.d.ts +6 -0
- package/bin/cicd/cli.js +126 -0
- package/bin/cicd/form.d.ts +29 -0
- package/bin/cicd/form.js +345 -0
- package/bin/cicd/generators/azure-devops.d.ts +2 -0
- package/bin/cicd/generators/azure-devops.js +370 -0
- package/bin/cicd/generators/bitbucket.d.ts +2 -0
- package/bin/cicd/generators/bitbucket.js +217 -0
- package/bin/cicd/generators/circleci.d.ts +2 -0
- package/bin/cicd/generators/circleci.js +274 -0
- package/bin/cicd/generators/github-actions.d.ts +14 -0
- package/bin/cicd/generators/github-actions.js +426 -0
- package/bin/cicd/generators/gitlab-ci.d.ts +2 -0
- package/bin/cicd/generators/gitlab-ci.js +237 -0
- package/bin/cicd/generators/index.d.ts +6 -0
- package/bin/cicd/generators/index.js +15 -0
- package/bin/cicd/generators/jenkins.d.ts +2 -0
- package/bin/cicd/generators/jenkins.js +248 -0
- package/bin/cicd/generators/template-loader.d.ts +17 -0
- package/bin/cicd/generators/template-loader.js +128 -0
- package/bin/cicd/index.d.ts +1 -0
- package/bin/cicd/index.js +5 -0
- package/bin/cli.d.ts +1 -1
- package/bin/cli.js +18 -3
- package/bin/commands/project.commands.d.ts +19 -6
- package/bin/commands/project.commands.js +390 -61
- package/bin/config/index.d.ts +5 -0
- package/bin/config/index.js +10 -0
- package/bin/config/manager.d.ts +98 -0
- package/bin/config/manager.js +222 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.d.ts +46 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.js +187 -0
- package/bin/containerize/analyzers/project-analyzer.d.ts +20 -0
- package/bin/containerize/analyzers/project-analyzer.js +150 -0
- package/bin/containerize/cli.d.ts +4 -0
- package/bin/containerize/cli.js +113 -0
- package/bin/containerize/form.d.ts +15 -0
- package/bin/containerize/form.js +154 -0
- package/bin/containerize/generators/ci-generator.d.ts +31 -0
- package/bin/containerize/generators/ci-generator.js +936 -0
- package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
- package/bin/containerize/generators/docker-compose-generator.js +186 -0
- package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
- package/bin/containerize/generators/dockerfile-generator.js +635 -0
- package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
- package/bin/containerize/generators/kubernetes-generator.js +133 -0
- package/bin/containerize/generators/template-loader.d.ts +36 -0
- package/bin/containerize/generators/template-loader.js +129 -0
- package/bin/containerize/index.d.ts +4 -0
- package/bin/containerize/index.js +13 -0
- package/bin/containerize/presets/preset-registry.d.ts +20 -0
- package/bin/containerize/presets/preset-registry.js +102 -0
- package/bin/costs/cli.d.ts +5 -0
- package/bin/costs/cli.js +183 -0
- package/bin/costs/form.d.ts +44 -0
- package/bin/costs/form.js +412 -0
- package/bin/costs/index.d.ts +4 -0
- package/bin/costs/index.js +25 -0
- package/bin/costs/pricing-manager.d.ts +84 -0
- package/bin/costs/pricing-manager.js +342 -0
- package/bin/costs/providers/index.d.ts +32 -0
- package/bin/costs/providers/index.js +153 -0
- package/bin/costs/sources/api-source.d.ts +10 -0
- package/bin/costs/sources/api-source.js +32 -0
- package/bin/costs/sources/index.d.ts +6 -0
- package/bin/costs/sources/index.js +15 -0
- package/bin/costs/sources/local-json-source.d.ts +23 -0
- package/bin/costs/sources/local-json-source.js +59 -0
- package/bin/costs/sources/remote-json-source.d.ts +11 -0
- package/bin/costs/sources/remote-json-source.js +53 -0
- package/bin/costs/types.d.ts +53 -0
- package/bin/costs/types.js +5 -0
- package/bin/dev/cli.d.ts +4 -0
- package/bin/dev/cli.js +134 -0
- package/bin/dev/form.d.ts +36 -0
- package/bin/dev/form.js +254 -0
- package/bin/dev/index.d.ts +1 -0
- package/bin/dev/index.js +5 -0
- package/bin/generate/cli.js +29 -2
- package/bin/generate/form.d.ts +5 -1
- package/bin/generate/form.js +3 -3
- package/bin/generate/templates/nonopinionated/config.tpl +12 -0
- package/bin/generate/templates/nonopinionated/event.tpl +10 -0
- package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
- package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
- package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
- package/bin/generate/templates/opinionated/config.tpl +47 -0
- package/bin/generate/templates/opinionated/entity.tpl +1 -8
- package/bin/generate/templates/opinionated/event.tpl +15 -0
- package/bin/generate/templates/opinionated/guard.tpl +41 -0
- package/bin/generate/templates/opinionated/handler.tpl +23 -0
- package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
- package/bin/generate/utils/command-utils.d.ts +7 -3
- package/bin/generate/utils/command-utils.js +95 -31
- package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
- package/bin/generate/utils/nonopininated-cmd.js +100 -1
- package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
- package/bin/generate/utils/opinionated-cmd.js +112 -7
- package/bin/generate/utils/string-utils.d.ts +6 -0
- package/bin/generate/utils/string-utils.js +13 -1
- package/bin/help/form.js +11 -3
- package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
- package/bin/migrate/analyzers/platform-detector.js +116 -0
- package/bin/migrate/cli.d.ts +6 -0
- package/bin/migrate/cli.js +96 -0
- package/bin/migrate/form.d.ts +25 -0
- package/bin/migrate/form.js +347 -0
- package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
- package/bin/migrate/generators/compose-to-k8s.js +324 -0
- package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
- package/bin/migrate/generators/compose-to-railway.js +138 -0
- package/bin/migrate/generators/compose-to-render.d.ts +2 -0
- package/bin/migrate/generators/compose-to-render.js +148 -0
- package/bin/migrate/generators/generic-migration.d.ts +9 -0
- package/bin/migrate/generators/generic-migration.js +221 -0
- package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-fly.js +291 -0
- package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-railway.js +283 -0
- package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-render.js +148 -0
- package/bin/migrate/generators/index.d.ts +7 -0
- package/bin/migrate/generators/index.js +17 -0
- package/bin/migrate/generators/template-loader.d.ts +21 -0
- package/bin/migrate/generators/template-loader.js +59 -0
- package/bin/migrate/index.d.ts +1 -0
- package/bin/migrate/index.js +5 -0
- package/bin/new/cli.js +21 -6
- package/bin/new/form.d.ts +25 -4
- package/bin/new/form.js +285 -70
- package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
- package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
- package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
- package/bin/profile/analyzers/image-analyzer.js +85 -0
- package/bin/profile/cli.d.ts +4 -0
- package/bin/profile/cli.js +92 -0
- package/bin/profile/form.d.ts +56 -0
- package/bin/profile/form.js +400 -0
- package/bin/profile/index.d.ts +1 -0
- package/bin/profile/index.js +5 -0
- package/bin/profile/optimizers/index.d.ts +19 -0
- package/bin/profile/optimizers/index.js +137 -0
- package/bin/providers/add/form.d.ts +1 -1
- package/bin/providers/add/form.js +27 -6
- package/bin/providers/create/form.js +2 -1
- package/bin/scripts/form.js +27 -5
- package/bin/studio/cli.d.ts +15 -0
- package/bin/studio/cli.js +166 -0
- package/bin/studio/index.d.ts +5 -0
- package/bin/studio/index.js +9 -0
- package/bin/templates/cache.d.ts +54 -0
- package/bin/templates/cache.js +180 -0
- package/bin/templates/cli.d.ts +8 -0
- package/bin/templates/cli.js +292 -0
- package/bin/templates/fetcher.d.ts +49 -0
- package/bin/templates/fetcher.js +208 -0
- package/bin/templates/index.d.ts +11 -0
- package/bin/templates/index.js +37 -0
- package/bin/templates/manager.d.ts +116 -0
- package/bin/templates/manager.js +323 -0
- package/bin/templates/renderer.d.ts +49 -0
- package/bin/templates/renderer.js +204 -0
- package/bin/templates/types.d.ts +51 -0
- package/bin/templates/types.js +5 -0
- package/bin/utils/add-module-to-container.d.ts +2 -2
- package/bin/utils/add-module-to-container.js +15 -5
- package/bin/utils/cli-ui.d.ts +30 -3
- package/bin/utils/cli-ui.js +95 -13
- package/bin/utils/index.d.ts +4 -0
- package/bin/utils/index.js +4 -0
- package/bin/utils/input-validation.d.ts +50 -0
- package/bin/utils/input-validation.js +143 -0
- package/bin/utils/package-manager-commands.d.ts +24 -0
- package/bin/utils/package-manager-commands.js +50 -0
- package/bin/utils/safe-spawn.d.ts +35 -0
- package/bin/utils/safe-spawn.js +51 -0
- package/bin/utils/update-tsconfig-paths.d.ts +35 -0
- package/bin/utils/update-tsconfig-paths.js +286 -0
- package/package.json +154 -154
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.analyzeMigration = exports.listMigrations = exports.generateMigration = exports.initMigration = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const platform_detector_1 = require("./analyzers/platform-detector");
|
|
12
|
+
const generators_1 = require("./generators");
|
|
13
|
+
const SUPPORTED_MIGRATIONS = [
|
|
14
|
+
{
|
|
15
|
+
from: "heroku",
|
|
16
|
+
to: "railway",
|
|
17
|
+
description: "Heroku to Railway",
|
|
18
|
+
complexity: "low",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
from: "heroku",
|
|
22
|
+
to: "render",
|
|
23
|
+
description: "Heroku to Render",
|
|
24
|
+
complexity: "low",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
from: "heroku",
|
|
28
|
+
to: "fly",
|
|
29
|
+
description: "Heroku to Fly.io",
|
|
30
|
+
complexity: "medium",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
from: "heroku",
|
|
34
|
+
to: "kubernetes",
|
|
35
|
+
description: "Heroku to Kubernetes",
|
|
36
|
+
complexity: "high",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
from: "docker-compose",
|
|
40
|
+
to: "kubernetes",
|
|
41
|
+
description: "Docker Compose to Kubernetes",
|
|
42
|
+
complexity: "medium",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
from: "docker-compose",
|
|
46
|
+
to: "railway",
|
|
47
|
+
description: "Docker Compose to Railway",
|
|
48
|
+
complexity: "low",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
from: "docker-compose",
|
|
52
|
+
to: "render",
|
|
53
|
+
description: "Docker Compose to Render",
|
|
54
|
+
complexity: "low",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
from: "docker-compose",
|
|
58
|
+
to: "fly",
|
|
59
|
+
description: "Docker Compose to Fly.io",
|
|
60
|
+
complexity: "medium",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
from: "vercel",
|
|
64
|
+
to: "railway",
|
|
65
|
+
description: "Vercel to Railway",
|
|
66
|
+
complexity: "low",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
from: "vercel",
|
|
70
|
+
to: "render",
|
|
71
|
+
description: "Vercel to Render",
|
|
72
|
+
complexity: "low",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
from: "aws-ecs",
|
|
76
|
+
to: "gcp-cloudrun",
|
|
77
|
+
description: "AWS ECS to GCP Cloud Run",
|
|
78
|
+
complexity: "high",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
from: "aws-ecs",
|
|
82
|
+
to: "kubernetes",
|
|
83
|
+
description: "AWS ECS to Kubernetes",
|
|
84
|
+
complexity: "medium",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
from: "gcp-cloudrun",
|
|
88
|
+
to: "aws-ecs",
|
|
89
|
+
description: "GCP Cloud Run to AWS ECS",
|
|
90
|
+
complexity: "high",
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
/**
|
|
94
|
+
* Interactive migration setup wizard
|
|
95
|
+
*/
|
|
96
|
+
async function initMigration(options) {
|
|
97
|
+
console.log(chalk_1.default.cyan("\n🚀 ExpressoTS Migration Wizard\n"));
|
|
98
|
+
// Detect current platform
|
|
99
|
+
const detected = await (0, platform_detector_1.detectCurrentPlatform)();
|
|
100
|
+
if (detected) {
|
|
101
|
+
console.log(chalk_1.default.green(`✓ Detected current platform: ${detected}`));
|
|
102
|
+
}
|
|
103
|
+
// Interactive prompts
|
|
104
|
+
const answers = await inquirer_1.default.prompt([
|
|
105
|
+
{
|
|
106
|
+
type: "list",
|
|
107
|
+
name: "from",
|
|
108
|
+
message: "Select source platform:",
|
|
109
|
+
choices: [
|
|
110
|
+
{ name: "Heroku", value: "heroku" },
|
|
111
|
+
{ name: "Docker Compose", value: "docker-compose" },
|
|
112
|
+
{ name: "Vercel", value: "vercel" },
|
|
113
|
+
{ name: "AWS ECS", value: "aws-ecs" },
|
|
114
|
+
{ name: "GCP Cloud Run", value: "gcp-cloudrun" },
|
|
115
|
+
{ name: "Azure Container Apps", value: "azure-container" },
|
|
116
|
+
],
|
|
117
|
+
default: detected || "heroku",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: "list",
|
|
121
|
+
name: "to",
|
|
122
|
+
message: "Select target platform:",
|
|
123
|
+
choices: (prev) => {
|
|
124
|
+
const migrations = SUPPORTED_MIGRATIONS.filter((m) => m.from === prev.from);
|
|
125
|
+
if (migrations.length === 0) {
|
|
126
|
+
// Show all targets if no specific migrations defined
|
|
127
|
+
return [
|
|
128
|
+
{ name: "Railway", value: "railway" },
|
|
129
|
+
{ name: "Render", value: "render" },
|
|
130
|
+
{ name: "Fly.io", value: "fly" },
|
|
131
|
+
{ name: "Kubernetes", value: "kubernetes" },
|
|
132
|
+
{ name: "AWS ECS", value: "aws-ecs" },
|
|
133
|
+
{ name: "GCP Cloud Run", value: "gcp-cloudrun" },
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
return migrations.map((m) => ({
|
|
137
|
+
name: `${m.to.charAt(0).toUpperCase() + m.to.slice(1)} (${m.complexity} complexity)`,
|
|
138
|
+
value: m.to,
|
|
139
|
+
}));
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
type: "confirm",
|
|
144
|
+
name: "includeSecrets",
|
|
145
|
+
message: "Include environment variable migration?",
|
|
146
|
+
default: true,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
type: "confirm",
|
|
150
|
+
name: "includeData",
|
|
151
|
+
message: "Include data migration scripts?",
|
|
152
|
+
default: false,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: "input",
|
|
156
|
+
name: "outputDir",
|
|
157
|
+
message: "Output directory for migration files:",
|
|
158
|
+
default: "./migration",
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
const migrationOptions = {
|
|
162
|
+
...options,
|
|
163
|
+
from: answers.from,
|
|
164
|
+
to: answers.to,
|
|
165
|
+
includeSecrets: answers.includeSecrets,
|
|
166
|
+
includeData: answers.includeData,
|
|
167
|
+
outputDir: answers.outputDir,
|
|
168
|
+
};
|
|
169
|
+
await generateMigration(migrationOptions);
|
|
170
|
+
}
|
|
171
|
+
exports.initMigration = initMigration;
|
|
172
|
+
/**
|
|
173
|
+
* Generate migration scripts
|
|
174
|
+
*/
|
|
175
|
+
async function generateMigration(options) {
|
|
176
|
+
if (!options.from || !options.to) {
|
|
177
|
+
console.log(chalk_1.default.red("Error: Please specify both --from and --to platforms."));
|
|
178
|
+
console.log(chalk_1.default.gray("Use 'expressots migrate list' to see available migrations."));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
console.log(chalk_1.default.cyan(`\n📦 Generating migration: ${options.from} → ${options.to}\n`));
|
|
182
|
+
// Create output directory
|
|
183
|
+
const outputDir = path_1.default.resolve(options.outputDir);
|
|
184
|
+
if (!options.dryRun) {
|
|
185
|
+
fs_1.default.mkdirSync(outputDir, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
// Find migration path
|
|
188
|
+
const migration = SUPPORTED_MIGRATIONS.find((m) => m.from === options.from && m.to === options.to);
|
|
189
|
+
if (options.dryRun) {
|
|
190
|
+
console.log(chalk_1.default.yellow("🔍 Dry run mode - showing migration steps:\n"));
|
|
191
|
+
printMigrationSteps(options, migration);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Generate migration files based on source/target
|
|
195
|
+
try {
|
|
196
|
+
await generateMigrationFiles(options, outputDir, migration);
|
|
197
|
+
console.log(chalk_1.default.green(`\n✅ Migration files generated in ${outputDir}\n`));
|
|
198
|
+
printNextSteps(options);
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
console.log(chalk_1.default.red(`Error generating migration: ${error}`));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports.generateMigration = generateMigration;
|
|
205
|
+
/**
|
|
206
|
+
* List available migrations
|
|
207
|
+
*/
|
|
208
|
+
async function listMigrations() {
|
|
209
|
+
console.log(chalk_1.default.cyan("\n📋 Available Migration Paths\n"));
|
|
210
|
+
// Group by source
|
|
211
|
+
const bySource = SUPPORTED_MIGRATIONS.reduce((acc, m) => {
|
|
212
|
+
if (!acc[m.from])
|
|
213
|
+
acc[m.from] = [];
|
|
214
|
+
acc[m.from].push(m);
|
|
215
|
+
return acc;
|
|
216
|
+
}, {});
|
|
217
|
+
for (const [source, migrations] of Object.entries(bySource)) {
|
|
218
|
+
console.log(chalk_1.default.bold(`\nFrom ${source}:`));
|
|
219
|
+
for (const m of migrations) {
|
|
220
|
+
const complexityColor = m.complexity === "low"
|
|
221
|
+
? chalk_1.default.green
|
|
222
|
+
: m.complexity === "medium"
|
|
223
|
+
? chalk_1.default.yellow
|
|
224
|
+
: chalk_1.default.red;
|
|
225
|
+
console.log(` → ${m.to.padEnd(20)} ${complexityColor(`[${m.complexity}]`)}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
console.log(chalk_1.default.gray("\n\nUsage: expressots migrate generate --from <source> --to <target>"));
|
|
229
|
+
console.log(chalk_1.default.gray(" expressots migrate init (interactive wizard)\n"));
|
|
230
|
+
}
|
|
231
|
+
exports.listMigrations = listMigrations;
|
|
232
|
+
/**
|
|
233
|
+
* Analyze migration complexity
|
|
234
|
+
*/
|
|
235
|
+
async function analyzeMigration(options) {
|
|
236
|
+
console.log(chalk_1.default.cyan("\n🔍 Migration Analysis\n"));
|
|
237
|
+
const detected = await (0, platform_detector_1.detectCurrentPlatform)();
|
|
238
|
+
const cwd = process.cwd();
|
|
239
|
+
console.log(chalk_1.default.bold("Current Setup:"));
|
|
240
|
+
console.log(` Platform: ${detected || "Unknown"}`);
|
|
241
|
+
// Analyze project files
|
|
242
|
+
const hasDockerfile = fs_1.default.existsSync(path_1.default.join(cwd, "Dockerfile"));
|
|
243
|
+
const hasDockerCompose = fs_1.default.existsSync(path_1.default.join(cwd, "docker-compose.yml"));
|
|
244
|
+
const hasK8s = fs_1.default.existsSync(path_1.default.join(cwd, "k8s"));
|
|
245
|
+
const hasProcfile = fs_1.default.existsSync(path_1.default.join(cwd, "Procfile"));
|
|
246
|
+
const hasVercelConfig = fs_1.default.existsSync(path_1.default.join(cwd, "vercel.json"));
|
|
247
|
+
const hasRailwayConfig = fs_1.default.existsSync(path_1.default.join(cwd, "railway.json"));
|
|
248
|
+
console.log(` Dockerfile: ${hasDockerfile ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
249
|
+
console.log(` Docker Compose: ${hasDockerCompose ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
250
|
+
console.log(` Kubernetes: ${hasK8s ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
251
|
+
console.log(` Procfile (Heroku): ${hasProcfile ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
252
|
+
console.log(` Vercel Config: ${hasVercelConfig ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
253
|
+
console.log(` Railway Config: ${hasRailwayConfig ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
254
|
+
// Check for environment variables
|
|
255
|
+
const hasEnvFile = fs_1.default.existsSync(path_1.default.join(cwd, ".env"));
|
|
256
|
+
const hasEnvExample = fs_1.default.existsSync(path_1.default.join(cwd, ".env.example"));
|
|
257
|
+
console.log(chalk_1.default.bold("\nEnvironment:"));
|
|
258
|
+
console.log(` .env file: ${hasEnvFile ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
259
|
+
console.log(` .env.example: ${hasEnvExample ? chalk_1.default.green("✓") : chalk_1.default.gray("✗")}`);
|
|
260
|
+
// Analyze package.json for dependencies
|
|
261
|
+
const packageJsonPath = path_1.default.join(cwd, "package.json");
|
|
262
|
+
if (fs_1.default.existsSync(packageJsonPath)) {
|
|
263
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf-8"));
|
|
264
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
265
|
+
console.log(chalk_1.default.bold("\nDependencies:"));
|
|
266
|
+
if (deps.pg || deps.postgres)
|
|
267
|
+
console.log(" Database: PostgreSQL");
|
|
268
|
+
if (deps.mysql || deps.mysql2)
|
|
269
|
+
console.log(" Database: MySQL");
|
|
270
|
+
if (deps.mongodb)
|
|
271
|
+
console.log(" Database: MongoDB");
|
|
272
|
+
if (deps.redis || deps.ioredis)
|
|
273
|
+
console.log(" Cache: Redis");
|
|
274
|
+
}
|
|
275
|
+
if (options.to) {
|
|
276
|
+
const migration = SUPPORTED_MIGRATIONS.find((m) => m.from === (detected || options.from) && m.to === options.to);
|
|
277
|
+
if (migration) {
|
|
278
|
+
console.log(chalk_1.default.bold(`\nMigration to ${options.to}:`));
|
|
279
|
+
console.log(` Complexity: ${migration.complexity}`);
|
|
280
|
+
console.log(` Description: ${migration.description}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
console.log();
|
|
284
|
+
}
|
|
285
|
+
exports.analyzeMigration = analyzeMigration;
|
|
286
|
+
/**
|
|
287
|
+
* Generate migration files based on source/target
|
|
288
|
+
*/
|
|
289
|
+
async function generateMigrationFiles(options, outputDir, migration) {
|
|
290
|
+
const { from, to } = options;
|
|
291
|
+
// Generate based on migration path
|
|
292
|
+
if (from === "heroku" && to === "railway") {
|
|
293
|
+
await (0, generators_1.generateHerokuToRailway)(outputDir, options);
|
|
294
|
+
}
|
|
295
|
+
else if (from === "heroku" && to === "render") {
|
|
296
|
+
await (0, generators_1.generateHerokuToRender)(outputDir, options);
|
|
297
|
+
}
|
|
298
|
+
else if (from === "heroku" && to === "fly") {
|
|
299
|
+
await (0, generators_1.generateHerokuToFly)(outputDir, options);
|
|
300
|
+
}
|
|
301
|
+
else if (from === "docker-compose" && to === "kubernetes") {
|
|
302
|
+
await (0, generators_1.generateComposeToK8s)(outputDir, options);
|
|
303
|
+
}
|
|
304
|
+
else if (from === "docker-compose" && to === "railway") {
|
|
305
|
+
await (0, generators_1.generateComposeToRailway)(outputDir, options);
|
|
306
|
+
}
|
|
307
|
+
else if (from === "docker-compose" && to === "render") {
|
|
308
|
+
await (0, generators_1.generateComposeToRender)(outputDir, options);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// Generic migration
|
|
312
|
+
await (0, generators_1.generateGenericMigration)(outputDir, options, migration);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Print migration steps for dry run
|
|
317
|
+
*/
|
|
318
|
+
function printMigrationSteps(options, migration) {
|
|
319
|
+
console.log(chalk_1.default.bold(`Migration: ${options.from} → ${options.to}`));
|
|
320
|
+
console.log(`Complexity: ${migration?.complexity || "unknown"}`);
|
|
321
|
+
console.log();
|
|
322
|
+
console.log(chalk_1.default.bold("Steps:"));
|
|
323
|
+
console.log(" 1. Generate target platform configuration");
|
|
324
|
+
console.log(" 2. Create environment variable mapping");
|
|
325
|
+
if (options.includeSecrets) {
|
|
326
|
+
console.log(" 3. Generate secrets migration script");
|
|
327
|
+
}
|
|
328
|
+
if (options.includeData) {
|
|
329
|
+
console.log(" 4. Generate data migration scripts");
|
|
330
|
+
}
|
|
331
|
+
console.log(" 5. Create migration checklist (README.md)");
|
|
332
|
+
console.log();
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Print next steps after migration
|
|
336
|
+
*/
|
|
337
|
+
function printNextSteps(options) {
|
|
338
|
+
console.log(chalk_1.default.bold("📖 Next Steps:"));
|
|
339
|
+
console.log(` 1. Review the migration files in ${options.outputDir}`);
|
|
340
|
+
console.log(" 2. Check the MIGRATION_CHECKLIST.md for step-by-step instructions");
|
|
341
|
+
console.log(" 3. Set up environment variables on the target platform");
|
|
342
|
+
if (options.includeData) {
|
|
343
|
+
console.log(" 4. Review and run data migration scripts carefully");
|
|
344
|
+
}
|
|
345
|
+
console.log(" 5. Test the deployment in a staging environment first");
|
|
346
|
+
console.log();
|
|
347
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateComposeToK8s = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const template_loader_1 = require("./template-loader");
|
|
11
|
+
async function generateComposeToK8s(outputDir, options) {
|
|
12
|
+
console.log(chalk_1.default.yellow(" Generating Docker Compose → Kubernetes migration..."));
|
|
13
|
+
const vars = (0, template_loader_1.buildMigrationVars)(options);
|
|
14
|
+
const k8sDir = path_1.default.join(outputDir, "k8s");
|
|
15
|
+
fs_1.default.mkdirSync(k8sDir, { recursive: true });
|
|
16
|
+
// Generate deployment.yaml
|
|
17
|
+
const deployment = generateDeployment();
|
|
18
|
+
fs_1.default.writeFileSync(path_1.default.join(k8sDir, "deployment.yaml"), deployment, "utf-8");
|
|
19
|
+
console.log(chalk_1.default.green(" ✓ Created k8s/deployment.yaml"));
|
|
20
|
+
// Generate service.yaml
|
|
21
|
+
const service = generateService();
|
|
22
|
+
fs_1.default.writeFileSync(path_1.default.join(k8sDir, "service.yaml"), service, "utf-8");
|
|
23
|
+
console.log(chalk_1.default.green(" ✓ Created k8s/service.yaml"));
|
|
24
|
+
// Generate configmap.yaml
|
|
25
|
+
const configMap = generateConfigMap();
|
|
26
|
+
fs_1.default.writeFileSync(path_1.default.join(k8sDir, "configmap.yaml"), configMap, "utf-8");
|
|
27
|
+
console.log(chalk_1.default.green(" ✓ Created k8s/configmap.yaml"));
|
|
28
|
+
// Generate secrets.yaml template
|
|
29
|
+
const secrets = generateSecrets();
|
|
30
|
+
fs_1.default.writeFileSync(path_1.default.join(k8sDir, "secrets.yaml"), secrets, "utf-8");
|
|
31
|
+
console.log(chalk_1.default.green(" ✓ Created k8s/secrets.yaml"));
|
|
32
|
+
// Generate ingress.yaml
|
|
33
|
+
const ingress = generateIngress();
|
|
34
|
+
fs_1.default.writeFileSync(path_1.default.join(k8sDir, "ingress.yaml"), ingress, "utf-8");
|
|
35
|
+
console.log(chalk_1.default.green(" ✓ Created k8s/ingress.yaml"));
|
|
36
|
+
// Generate kustomization.yaml
|
|
37
|
+
const kustomization = generateKustomization();
|
|
38
|
+
fs_1.default.writeFileSync(path_1.default.join(k8sDir, "kustomization.yaml"), kustomization, "utf-8");
|
|
39
|
+
console.log(chalk_1.default.green(" ✓ Created k8s/kustomization.yaml"));
|
|
40
|
+
// Generate migration checklist
|
|
41
|
+
const checklist = generateK8sChecklist();
|
|
42
|
+
fs_1.default.writeFileSync(path_1.default.join(outputDir, "MIGRATION_CHECKLIST.md"), checklist, "utf-8");
|
|
43
|
+
console.log(chalk_1.default.green(" ✓ Created MIGRATION_CHECKLIST.md"));
|
|
44
|
+
// Generate kompose command reference
|
|
45
|
+
const komposeRef = generateKomposeReference();
|
|
46
|
+
fs_1.default.writeFileSync(path_1.default.join(outputDir, "kompose-reference.md"), komposeRef, "utf-8");
|
|
47
|
+
console.log(chalk_1.default.green(" ✓ Created kompose-reference.md"));
|
|
48
|
+
}
|
|
49
|
+
exports.generateComposeToK8s = generateComposeToK8s;
|
|
50
|
+
function generateDeployment() {
|
|
51
|
+
return `# Kubernetes Deployment
|
|
52
|
+
# Generated by ExpressoTS CLI
|
|
53
|
+
# Migrated from Docker Compose
|
|
54
|
+
|
|
55
|
+
apiVersion: apps/v1
|
|
56
|
+
kind: Deployment
|
|
57
|
+
metadata:
|
|
58
|
+
name: expressots-app
|
|
59
|
+
labels:
|
|
60
|
+
app: expressots-app
|
|
61
|
+
spec:
|
|
62
|
+
replicas: 2
|
|
63
|
+
selector:
|
|
64
|
+
matchLabels:
|
|
65
|
+
app: expressots-app
|
|
66
|
+
template:
|
|
67
|
+
metadata:
|
|
68
|
+
labels:
|
|
69
|
+
app: expressots-app
|
|
70
|
+
spec:
|
|
71
|
+
containers:
|
|
72
|
+
- name: app
|
|
73
|
+
image: your-registry/expressots-app:latest
|
|
74
|
+
ports:
|
|
75
|
+
- containerPort: 3000
|
|
76
|
+
envFrom:
|
|
77
|
+
- configMapRef:
|
|
78
|
+
name: expressots-config
|
|
79
|
+
- secretRef:
|
|
80
|
+
name: expressots-secrets
|
|
81
|
+
resources:
|
|
82
|
+
requests:
|
|
83
|
+
memory: "128Mi"
|
|
84
|
+
cpu: "100m"
|
|
85
|
+
limits:
|
|
86
|
+
memory: "512Mi"
|
|
87
|
+
cpu: "500m"
|
|
88
|
+
livenessProbe:
|
|
89
|
+
httpGet:
|
|
90
|
+
path: /health
|
|
91
|
+
port: 3000
|
|
92
|
+
initialDelaySeconds: 30
|
|
93
|
+
periodSeconds: 10
|
|
94
|
+
readinessProbe:
|
|
95
|
+
httpGet:
|
|
96
|
+
path: /health
|
|
97
|
+
port: 3000
|
|
98
|
+
initialDelaySeconds: 5
|
|
99
|
+
periodSeconds: 5
|
|
100
|
+
imagePullSecrets:
|
|
101
|
+
- name: registry-credentials
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
function generateService() {
|
|
105
|
+
return `# Kubernetes Service
|
|
106
|
+
# Generated by ExpressoTS CLI
|
|
107
|
+
|
|
108
|
+
apiVersion: v1
|
|
109
|
+
kind: Service
|
|
110
|
+
metadata:
|
|
111
|
+
name: expressots-app-service
|
|
112
|
+
labels:
|
|
113
|
+
app: expressots-app
|
|
114
|
+
spec:
|
|
115
|
+
type: ClusterIP
|
|
116
|
+
ports:
|
|
117
|
+
- port: 80
|
|
118
|
+
targetPort: 3000
|
|
119
|
+
protocol: TCP
|
|
120
|
+
name: http
|
|
121
|
+
selector:
|
|
122
|
+
app: expressots-app
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
function generateConfigMap() {
|
|
126
|
+
return `# Kubernetes ConfigMap
|
|
127
|
+
# Generated by ExpressoTS CLI
|
|
128
|
+
# Non-sensitive configuration values
|
|
129
|
+
|
|
130
|
+
apiVersion: v1
|
|
131
|
+
kind: ConfigMap
|
|
132
|
+
metadata:
|
|
133
|
+
name: expressots-config
|
|
134
|
+
data:
|
|
135
|
+
NODE_ENV: "production"
|
|
136
|
+
PORT: "3000"
|
|
137
|
+
# Add your non-sensitive config values here
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
function generateSecrets() {
|
|
141
|
+
return `# Kubernetes Secrets
|
|
142
|
+
# Generated by ExpressoTS CLI
|
|
143
|
+
# ⚠️ DO NOT commit this file with real values!
|
|
144
|
+
# Use: kubectl create secret generic expressots-secrets --from-env-file=.env
|
|
145
|
+
|
|
146
|
+
apiVersion: v1
|
|
147
|
+
kind: Secret
|
|
148
|
+
metadata:
|
|
149
|
+
name: expressots-secrets
|
|
150
|
+
type: Opaque
|
|
151
|
+
stringData:
|
|
152
|
+
DATABASE_URL: "postgresql://user:pass@host:5432/db"
|
|
153
|
+
API_KEY: "your-api-key"
|
|
154
|
+
# Add your sensitive values here
|
|
155
|
+
# In production, use sealed-secrets or external-secrets
|
|
156
|
+
`;
|
|
157
|
+
}
|
|
158
|
+
function generateIngress() {
|
|
159
|
+
return `# Kubernetes Ingress
|
|
160
|
+
# Generated by ExpressoTS CLI
|
|
161
|
+
|
|
162
|
+
apiVersion: networking.k8s.io/v1
|
|
163
|
+
kind: Ingress
|
|
164
|
+
metadata:
|
|
165
|
+
name: expressots-ingress
|
|
166
|
+
annotations:
|
|
167
|
+
kubernetes.io/ingress.class: nginx
|
|
168
|
+
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
169
|
+
spec:
|
|
170
|
+
tls:
|
|
171
|
+
- hosts:
|
|
172
|
+
- your-domain.com
|
|
173
|
+
secretName: expressots-tls
|
|
174
|
+
rules:
|
|
175
|
+
- host: your-domain.com
|
|
176
|
+
http:
|
|
177
|
+
paths:
|
|
178
|
+
- path: /
|
|
179
|
+
pathType: Prefix
|
|
180
|
+
backend:
|
|
181
|
+
service:
|
|
182
|
+
name: expressots-app-service
|
|
183
|
+
port:
|
|
184
|
+
number: 80
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
function generateKustomization() {
|
|
188
|
+
return `# Kustomization
|
|
189
|
+
# Generated by ExpressoTS CLI
|
|
190
|
+
|
|
191
|
+
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
192
|
+
kind: Kustomization
|
|
193
|
+
|
|
194
|
+
resources:
|
|
195
|
+
- deployment.yaml
|
|
196
|
+
- service.yaml
|
|
197
|
+
- configmap.yaml
|
|
198
|
+
- secrets.yaml
|
|
199
|
+
- ingress.yaml
|
|
200
|
+
|
|
201
|
+
commonLabels:
|
|
202
|
+
app.kubernetes.io/name: expressots-app
|
|
203
|
+
app.kubernetes.io/managed-by: kustomize
|
|
204
|
+
`;
|
|
205
|
+
}
|
|
206
|
+
function generateK8sChecklist() {
|
|
207
|
+
return `# Migration Checklist: Docker Compose → Kubernetes
|
|
208
|
+
|
|
209
|
+
## Pre-Migration
|
|
210
|
+
|
|
211
|
+
- [ ] Ensure kubectl is installed and configured
|
|
212
|
+
- [ ] Have access to a Kubernetes cluster
|
|
213
|
+
- [ ] Push Docker image to container registry
|
|
214
|
+
|
|
215
|
+
## Automatic Conversion (Optional)
|
|
216
|
+
|
|
217
|
+
Use Kompose for automatic conversion:
|
|
218
|
+
\`\`\`bash
|
|
219
|
+
kompose convert -f docker-compose.yml -o k8s/
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
## Manual Migration Steps
|
|
223
|
+
|
|
224
|
+
1. **Deployment**
|
|
225
|
+
- [ ] Update image name in deployment.yaml
|
|
226
|
+
- [ ] Adjust replica count
|
|
227
|
+
- [ ] Configure resource limits
|
|
228
|
+
|
|
229
|
+
2. **Configuration**
|
|
230
|
+
- [ ] Add non-sensitive values to configmap.yaml
|
|
231
|
+
- [ ] Add sensitive values to secrets.yaml
|
|
232
|
+
- [ ] Never commit secrets with real values!
|
|
233
|
+
|
|
234
|
+
3. **Networking**
|
|
235
|
+
- [ ] Configure Service (ClusterIP, LoadBalancer, NodePort)
|
|
236
|
+
- [ ] Set up Ingress for external access
|
|
237
|
+
- [ ] Configure TLS/SSL certificates
|
|
238
|
+
|
|
239
|
+
4. **Storage (if needed)**
|
|
240
|
+
- [ ] Create PersistentVolumeClaims for data
|
|
241
|
+
- [ ] Mount volumes in deployment
|
|
242
|
+
|
|
243
|
+
## Deployment
|
|
244
|
+
|
|
245
|
+
\`\`\`bash
|
|
246
|
+
# Apply all resources
|
|
247
|
+
kubectl apply -k k8s/
|
|
248
|
+
|
|
249
|
+
# Or apply individually
|
|
250
|
+
kubectl apply -f k8s/configmap.yaml
|
|
251
|
+
kubectl apply -f k8s/secrets.yaml
|
|
252
|
+
kubectl apply -f k8s/deployment.yaml
|
|
253
|
+
kubectl apply -f k8s/service.yaml
|
|
254
|
+
kubectl apply -f k8s/ingress.yaml
|
|
255
|
+
\`\`\`
|
|
256
|
+
|
|
257
|
+
## Verification
|
|
258
|
+
|
|
259
|
+
\`\`\`bash
|
|
260
|
+
kubectl get pods
|
|
261
|
+
kubectl get services
|
|
262
|
+
kubectl get ingress
|
|
263
|
+
kubectl logs -f deployment/expressots-app
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
## Database Migration
|
|
267
|
+
|
|
268
|
+
If migrating database:
|
|
269
|
+
- [ ] Create PersistentVolumeClaim for database
|
|
270
|
+
- [ ] Deploy database as StatefulSet
|
|
271
|
+
- [ ] Or use managed database service (RDS, Cloud SQL)
|
|
272
|
+
`;
|
|
273
|
+
}
|
|
274
|
+
function generateKomposeReference() {
|
|
275
|
+
return `# Kompose Reference
|
|
276
|
+
|
|
277
|
+
Kompose is a tool to convert Docker Compose files to Kubernetes resources.
|
|
278
|
+
|
|
279
|
+
## Installation
|
|
280
|
+
|
|
281
|
+
\`\`\`bash
|
|
282
|
+
# macOS
|
|
283
|
+
brew install kompose
|
|
284
|
+
|
|
285
|
+
# Linux
|
|
286
|
+
curl -L https://github.com/kubernetes/kompose/releases/download/v1.31.2/kompose-linux-amd64 -o kompose
|
|
287
|
+
chmod +x kompose
|
|
288
|
+
sudo mv kompose /usr/local/bin/
|
|
289
|
+
|
|
290
|
+
# Windows
|
|
291
|
+
choco install kubernetes-kompose
|
|
292
|
+
\`\`\`
|
|
293
|
+
|
|
294
|
+
## Basic Conversion
|
|
295
|
+
|
|
296
|
+
\`\`\`bash
|
|
297
|
+
# Convert docker-compose.yml
|
|
298
|
+
kompose convert
|
|
299
|
+
|
|
300
|
+
# Output to specific directory
|
|
301
|
+
kompose convert -o k8s/
|
|
302
|
+
|
|
303
|
+
# Generate Helm chart
|
|
304
|
+
kompose convert -c
|
|
305
|
+
\`\`\`
|
|
306
|
+
|
|
307
|
+
## Supported Features
|
|
308
|
+
|
|
309
|
+
| Docker Compose | Kubernetes |
|
|
310
|
+
|---------------|------------|
|
|
311
|
+
| services | Deployment + Service |
|
|
312
|
+
| volumes | PersistentVolumeClaim |
|
|
313
|
+
| environment | ConfigMap/Secret |
|
|
314
|
+
| ports | Service ports |
|
|
315
|
+
| depends_on | (manual ordering) |
|
|
316
|
+
| networks | (default pod networking) |
|
|
317
|
+
|
|
318
|
+
## Limitations
|
|
319
|
+
|
|
320
|
+
- \`build\` context is not supported (pre-build images)
|
|
321
|
+
- \`links\` deprecated, use service DNS
|
|
322
|
+
- Some volume types not supported
|
|
323
|
+
`;
|
|
324
|
+
}
|