@kodus/cli 0.0.2 ā 0.0.6
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/index.js +4 -2
- package/package.json +6 -1
- package/src/commands/install.js +310 -0
- package/src/config/default.js +66 -0
- package/src/utils/helpers.js +94 -0
package/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from "commander";
|
|
4
|
-
import
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
|
+
import { setupEnvironment } from './src/commands/install.js';
|
|
5
7
|
|
|
6
8
|
// CLI version
|
|
7
|
-
program.version("
|
|
9
|
+
program.version("0.0.5");
|
|
8
10
|
|
|
9
11
|
// Install command
|
|
10
12
|
program
|
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kodus/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "CLI tool for Kodus installation and management",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./src/*": "./src/*.js"
|
|
10
|
+
},
|
|
7
11
|
"preferGlobal": true,
|
|
8
12
|
"bin": {
|
|
9
13
|
"kodus": "./index.js"
|
|
10
14
|
},
|
|
11
15
|
"files": [
|
|
12
16
|
"index.js",
|
|
17
|
+
"src/**/*.js",
|
|
13
18
|
"templates/**/*"
|
|
14
19
|
],
|
|
15
20
|
"scripts": {
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import fs from "fs-extra";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import { DEFAULT_CONFIG, DOCKER_NETWORKS, CRITICAL_ERRORS } from "../config/default.js";
|
|
8
|
+
import {
|
|
9
|
+
generateSecretKey,
|
|
10
|
+
generateDbPassword,
|
|
11
|
+
copyTemplates,
|
|
12
|
+
createDockerNetworks,
|
|
13
|
+
} from "../utils/helpers.js";
|
|
14
|
+
|
|
15
|
+
const maxAttempts = 30; // 5 minutes with 10 second intervals
|
|
16
|
+
|
|
17
|
+
const waitForService = async (serviceName, successMessage, errorMessage) => {
|
|
18
|
+
const spinner = ora(`Waiting for ${serviceName} to be ready`).start();
|
|
19
|
+
let isReady = false;
|
|
20
|
+
let attempts = 0;
|
|
21
|
+
|
|
22
|
+
while (!isReady && attempts < maxAttempts) {
|
|
23
|
+
try {
|
|
24
|
+
const logs = execSync(`docker-compose logs ${serviceName}`, { stdio: "pipe" }).toString();
|
|
25
|
+
|
|
26
|
+
if (logs.includes(successMessage)) {
|
|
27
|
+
isReady = true;
|
|
28
|
+
spinner.succeed(`${serviceName} is ready`);
|
|
29
|
+
} else {
|
|
30
|
+
attempts++;
|
|
31
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
attempts++;
|
|
35
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!isReady) {
|
|
40
|
+
spinner.fail(errorMessage);
|
|
41
|
+
console.error(chalk.yellow("\nTroubleshooting steps:"));
|
|
42
|
+
console.error(chalk.white(`1. Check ${serviceName} logs: docker-compose logs ${serviceName}`));
|
|
43
|
+
console.error(chalk.white(`2. Verify ${serviceName} container is running: docker-compose ps ${serviceName}`));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const setupEnvironment = async () => {
|
|
49
|
+
try {
|
|
50
|
+
// Check prerequisites
|
|
51
|
+
console.log(chalk.blue("\nš Checking prerequisites..."));
|
|
52
|
+
const spinner = ora("Checking Docker installation").start();
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
execSync("docker --version", { stdio: "ignore" });
|
|
56
|
+
spinner.succeed("Docker is installed");
|
|
57
|
+
} catch (error) {
|
|
58
|
+
spinner.fail("Docker is not installed");
|
|
59
|
+
console.error(
|
|
60
|
+
chalk.red(
|
|
61
|
+
"\nPlease install Docker first: https://docs.docker.com/get-docker/"
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Copy template files
|
|
68
|
+
const templateSpinner = ora("Copying template files").start();
|
|
69
|
+
try {
|
|
70
|
+
copyTemplates(process.cwd());
|
|
71
|
+
templateSpinner.succeed("Template files copied");
|
|
72
|
+
} catch (error) {
|
|
73
|
+
templateSpinner.fail("Failed to copy template files");
|
|
74
|
+
console.error(chalk.red("\nError details:"));
|
|
75
|
+
console.error(chalk.white(error.message));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Environment type selection
|
|
80
|
+
const { envType } = await inquirer.prompt([
|
|
81
|
+
{
|
|
82
|
+
type: "list",
|
|
83
|
+
name: "envType",
|
|
84
|
+
message: "What type of environment are you setting up?",
|
|
85
|
+
choices: [
|
|
86
|
+
{ name: "Local (localhost)", value: "local" },
|
|
87
|
+
{ name: "External (with public URL)", value: "external" },
|
|
88
|
+
],
|
|
89
|
+
default: "local",
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
let baseUrl = "http://localhost:3000";
|
|
94
|
+
if (envType === "external") {
|
|
95
|
+
const { url } = await inquirer.prompt([
|
|
96
|
+
{
|
|
97
|
+
type: "input",
|
|
98
|
+
name: "url",
|
|
99
|
+
message: "Enter your public URL (e.g., https://kodus.yourdomain.com):",
|
|
100
|
+
validate: (input) => {
|
|
101
|
+
try {
|
|
102
|
+
const url = new URL(input);
|
|
103
|
+
if (!url.protocol.startsWith('http')) {
|
|
104
|
+
return "URL must start with http:// or https://";
|
|
105
|
+
}
|
|
106
|
+
if (!url.hostname.includes('.')) {
|
|
107
|
+
return "URL must include a valid domain name";
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
return "Please enter a valid URL (e.g., https://kodus.yourdomain.com)";
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
116
|
+
baseUrl = url;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Git service selection
|
|
120
|
+
const { gitService } = await inquirer.prompt([
|
|
121
|
+
{
|
|
122
|
+
type: "list",
|
|
123
|
+
name: "gitService",
|
|
124
|
+
message: "Which Git service will you use?",
|
|
125
|
+
choices: [
|
|
126
|
+
{ name: "GitHub", value: "github" },
|
|
127
|
+
{ name: "GitLab", value: "gitlab" },
|
|
128
|
+
{ name: "Bitbucket", value: "bitbucket" },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
// Git service configuration
|
|
134
|
+
let gitConfig = {};
|
|
135
|
+
|
|
136
|
+
if (envType === "local") {
|
|
137
|
+
console.log(chalk.yellow("\nā ļø IMPORTANT: If you're using a cloud Git service (GitHub, GitLab, Bitbucket), you'll need to configure the webhook manually."));
|
|
138
|
+
console.log(chalk.yellow("For local or self-hosted Git instances, no additional configuration is needed."));
|
|
139
|
+
console.log(chalk.yellow("The webhook URL will be:"));
|
|
140
|
+
console.log(chalk.white(`${baseUrl}/api/webhook/${gitService}`));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Configure webhook URL based on the selected Git service
|
|
144
|
+
if (gitService === "github") {
|
|
145
|
+
gitConfig = {
|
|
146
|
+
API_GITHUB_CODE_MANAGEMENT_WEBHOOK: `${baseUrl}/api/webhook/${gitService}`,
|
|
147
|
+
};
|
|
148
|
+
} else if (gitService === "gitlab") {
|
|
149
|
+
gitConfig = {
|
|
150
|
+
API_GITLAB_CODE_MANAGEMENT_WEBHOOK: `${baseUrl}/api/webhook/${gitService}`,
|
|
151
|
+
};
|
|
152
|
+
} else if (gitService === "bitbucket") {
|
|
153
|
+
gitConfig = {
|
|
154
|
+
GLOBAL_BITBUCKET_CODE_MANAGEMENT_WEBHOOK: `${baseUrl}/api/webhook/${gitService}`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Database configuration
|
|
159
|
+
const { useDefaultDb } = await inquirer.prompt([
|
|
160
|
+
{
|
|
161
|
+
type: "confirm",
|
|
162
|
+
name: "useDefaultDb",
|
|
163
|
+
message: "Would you like to use default database configurations?",
|
|
164
|
+
default: true,
|
|
165
|
+
},
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
// LLM API Keys configuration
|
|
169
|
+
console.log(chalk.blue("\nš Configuring LLM API Keys..."));
|
|
170
|
+
const llmKeys = await inquirer.prompt([
|
|
171
|
+
{
|
|
172
|
+
type: "input",
|
|
173
|
+
name: "openAiKey",
|
|
174
|
+
message: "Enter your OpenAI API key (optional):",
|
|
175
|
+
default: "",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: "input",
|
|
179
|
+
name: "googleAiKey",
|
|
180
|
+
message: "Enter your Google AI API key (optional):",
|
|
181
|
+
default: "",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: "input",
|
|
185
|
+
name: "anthropicKey",
|
|
186
|
+
message: "Enter your Anthropic API key (optional):",
|
|
187
|
+
default: "",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: "input",
|
|
191
|
+
name: "novitaKey",
|
|
192
|
+
message: "Enter your Novita AI API key (optional):",
|
|
193
|
+
default: "",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: "input",
|
|
197
|
+
name: "vertexKey",
|
|
198
|
+
message: "Enter your Vertex AI API key (optional):",
|
|
199
|
+
default: "",
|
|
200
|
+
},
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
// Generate .env content
|
|
204
|
+
const envContent = {
|
|
205
|
+
...DEFAULT_CONFIG,
|
|
206
|
+
WEB_NEXTAUTH_SECRET: generateSecretKey(),
|
|
207
|
+
WEB_JWT_SECRET_KEY: generateSecretKey(),
|
|
208
|
+
API_JWT_SECRET: generateSecretKey(),
|
|
209
|
+
API_JWT_REFRESHSECRET: generateSecretKey(),
|
|
210
|
+
API_PG_DB_PASSWORD: generateDbPassword(),
|
|
211
|
+
API_MG_DB_PASSWORD: generateDbPassword(),
|
|
212
|
+
RABBITMQ_DEFAULT_PASS: generateDbPassword(),
|
|
213
|
+
GRAFANA_ADMIN_PASSWORD: generateDbPassword(),
|
|
214
|
+
API_OPEN_AI_APIKEY: llmKeys.openAiKey,
|
|
215
|
+
API_GOOGLE_AI_API_KEY: llmKeys.googleAiKey,
|
|
216
|
+
API_ANTHROPIC_API_KEY: llmKeys.anthropicKey,
|
|
217
|
+
API_NOVITA_AI_API_KEY: llmKeys.novitaKey,
|
|
218
|
+
API_VERTEX_AI_API_KEY: llmKeys.vertexKey,
|
|
219
|
+
...gitConfig,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Create .env file
|
|
223
|
+
const envSpinner = ora("Creating .env file").start();
|
|
224
|
+
const envContentString = Object.entries(envContent)
|
|
225
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
226
|
+
.join("\n");
|
|
227
|
+
|
|
228
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
229
|
+
fs.writeFileSync(envPath, envContentString);
|
|
230
|
+
envSpinner.succeed("Created .env file");
|
|
231
|
+
|
|
232
|
+
// Create Docker networks
|
|
233
|
+
createDockerNetworks(DOCKER_NETWORKS);
|
|
234
|
+
|
|
235
|
+
// Start containers
|
|
236
|
+
const dockerSpinner = ora("Starting containers").start();
|
|
237
|
+
try {
|
|
238
|
+
execSync("docker-compose up -d", { stdio: "pipe" });
|
|
239
|
+
dockerSpinner.succeed("Containers started");
|
|
240
|
+
} catch (error) {
|
|
241
|
+
dockerSpinner.fail("Failed to start containers");
|
|
242
|
+
console.error(chalk.red("\nError details:"));
|
|
243
|
+
console.error(chalk.white(error.stdout?.toString() || error.message));
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Wait for services to be ready
|
|
248
|
+
await waitForService(
|
|
249
|
+
"db_kodus_postgres",
|
|
250
|
+
"database system is ready to accept connections",
|
|
251
|
+
"PostgreSQL failed to start"
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
await waitForService(
|
|
255
|
+
"db_kodus_mongodb",
|
|
256
|
+
"Waiting for connections",
|
|
257
|
+
"MongoDB failed to start"
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Setup database
|
|
261
|
+
const dbSpinner = ora("Setting up database").start();
|
|
262
|
+
try {
|
|
263
|
+
execSync("./scripts/setup-db.sh", { stdio: "pipe" });
|
|
264
|
+
dbSpinner.succeed("Database setup completed");
|
|
265
|
+
} catch (error) {
|
|
266
|
+
dbSpinner.fail("Failed to setup database");
|
|
267
|
+
console.error(chalk.red("\nError details:"));
|
|
268
|
+
console.error(chalk.white(error.stdout?.toString() || error.message));
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Installation complete
|
|
273
|
+
console.log(chalk.green("\n⨠Installation completed successfully!"));
|
|
274
|
+
|
|
275
|
+
console.log(chalk.blue("\nš Installation Summary:"));
|
|
276
|
+
console.log(chalk.white(` - Environment: ${envType === "local" ? "Local" : "External"}`));
|
|
277
|
+
if (envType === "external") {
|
|
278
|
+
console.log(chalk.white(` - Base URL: ${baseUrl}`));
|
|
279
|
+
}
|
|
280
|
+
console.log(chalk.white(` - Git Service: ${gitService.charAt(0).toUpperCase() + gitService.slice(1)}`));
|
|
281
|
+
console.log(chalk.white(` - Database: ${useDefaultDb ? "Default configuration" : "Custom configuration"}`));
|
|
282
|
+
|
|
283
|
+
console.log(chalk.blue("\nš Access URLs:"));
|
|
284
|
+
console.log(chalk.white(` - Web Interface: ${baseUrl}`));
|
|
285
|
+
console.log(chalk.white(" - Grafana Dashboard: http://localhost:3001"));
|
|
286
|
+
console.log(chalk.white(" - RabbitMQ Management: http://localhost:15672"));
|
|
287
|
+
|
|
288
|
+
console.log(chalk.blue("\nš Next Steps:"));
|
|
289
|
+
console.log(chalk.white(" 1. Access the web interface"));
|
|
290
|
+
console.log(chalk.white(" 2. Set up your first user"));
|
|
291
|
+
console.log(chalk.white(" 3. Start using Kodus!"));
|
|
292
|
+
|
|
293
|
+
const { startServices } = await inquirer.prompt([
|
|
294
|
+
{
|
|
295
|
+
type: "confirm",
|
|
296
|
+
name: "startServices",
|
|
297
|
+
message: "Would you like to start the services now?",
|
|
298
|
+
default: true,
|
|
299
|
+
},
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
if (startServices) {
|
|
303
|
+
execSync("docker-compose up -d", { stdio: "ignore" });
|
|
304
|
+
console.log(chalk.green("\nServices started successfully!"));
|
|
305
|
+
}
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error(chalk.red("\nā Installation failed:"), error.message);
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export const DEFAULT_CONFIG = {
|
|
2
|
+
// Base configuration
|
|
3
|
+
WEB_NODE_ENV: "development",
|
|
4
|
+
WEB_HOSTNAME_API: "localhost",
|
|
5
|
+
WEB_PORT_API: "3001",
|
|
6
|
+
WEB_PORT: "3000",
|
|
7
|
+
WEB_NEXTAUTH_URL: "http://localhost:3000",
|
|
8
|
+
|
|
9
|
+
API_NODE_ENV: "development",
|
|
10
|
+
API_LOG_LEVEL: "error",
|
|
11
|
+
API_LOG_PRETTY: "true",
|
|
12
|
+
API_HOST: "0.0.0.0",
|
|
13
|
+
API_PORT: "3001",
|
|
14
|
+
API_RATE_MAX_REQUEST: "100",
|
|
15
|
+
API_RATE_INTERVAL: "1000",
|
|
16
|
+
API_JWT_EXPIRES_IN: "365d",
|
|
17
|
+
API_JWT_REFRESH_EXPIRES_IN: "7d",
|
|
18
|
+
|
|
19
|
+
GLOBAL_API_CONTAINER_NAME: "kodus-orchestrator-prod",
|
|
20
|
+
|
|
21
|
+
// Database
|
|
22
|
+
API_DATABASE_ENV: "development",
|
|
23
|
+
API_PG_DB_USERNAME: "kodusdev",
|
|
24
|
+
API_PG_DB_DATABASE: "kodus_db",
|
|
25
|
+
API_PG_DB_HOST: "db_kodus_postgres",
|
|
26
|
+
API_PG_DB_PORT: "5432",
|
|
27
|
+
|
|
28
|
+
API_MG_DB_USERNAME: "kodusdev",
|
|
29
|
+
API_MG_DB_DATABASE: "kodus_db",
|
|
30
|
+
API_MG_DB_HOST: "db_kodus_mongodb",
|
|
31
|
+
API_MG_DB_PORT: "27017",
|
|
32
|
+
API_MG_DB_PRODUCTION_CONFIG: "",
|
|
33
|
+
|
|
34
|
+
// LLM Models
|
|
35
|
+
API_LLM_MODEL_CHATGPT_3_5_TURBO: "gpt-4o-mini",
|
|
36
|
+
API_LLM_MODEL_CHATGPT_3_5_TURBO_16K: "gpt-4o-mini",
|
|
37
|
+
API_LLM_MODEL_CHATGPT_4: "gpt-4o-mini",
|
|
38
|
+
API_LLM_MODEL_CHATGPT_4_TURBO: "gpt-4o-mini",
|
|
39
|
+
API_LLM_MODEL_CHATGPT_4_ALL: "gpt-4o",
|
|
40
|
+
API_LLM_MODEL_CHATGPT_4_ALL_MINI: "gpt-4o-mini",
|
|
41
|
+
API_LLM_MODEL_CLAUDE_3_5_SONNET: "claude-3-5-sonnet-20241022",
|
|
42
|
+
API_LLM_MODEL_CLAUDE_3_5_SONNET_20241022: "claude-3-5-sonnet-20241022",
|
|
43
|
+
API_LLM_MODEL_GEMINI_1_5_PRO: "gpt-4o-mini",
|
|
44
|
+
API_LLM_MODEL_GEMINI_1_5_PRO_EXP: "gpt-4o-mini",
|
|
45
|
+
|
|
46
|
+
// Ports
|
|
47
|
+
WEB_PORT: "3000",
|
|
48
|
+
API_PORT: "3001",
|
|
49
|
+
|
|
50
|
+
// RabbitMQ
|
|
51
|
+
RABBITMQ_DEFAULT_USER: "kodus",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const DOCKER_NETWORKS = [
|
|
55
|
+
'shared-network',
|
|
56
|
+
'monitoring-network',
|
|
57
|
+
'kodus-backend-services'
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
export const CRITICAL_ERRORS = [
|
|
61
|
+
"password authentication failed",
|
|
62
|
+
"Unable to connect to the database",
|
|
63
|
+
"FATAL:",
|
|
64
|
+
"MongoServerError:",
|
|
65
|
+
"connection refused"
|
|
66
|
+
];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname } from 'path';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
export const generateSecretKey = () => crypto.randomBytes(32).toString("base64");
|
|
14
|
+
|
|
15
|
+
export const generateDbPassword = () =>
|
|
16
|
+
crypto
|
|
17
|
+
.randomBytes(16)
|
|
18
|
+
.toString("base64")
|
|
19
|
+
.replace(/[^a-zA-Z0-9]/g, "")
|
|
20
|
+
.slice(0, 16);
|
|
21
|
+
|
|
22
|
+
export const copyTemplates = (targetDir) => {
|
|
23
|
+
const templatesDir = path.join(__dirname, '../../templates');
|
|
24
|
+
fs.copySync(path.join(templatesDir, 'docker-compose.yml'), path.join(targetDir, 'docker-compose.yml'));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const backupEnv = () => {
|
|
28
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
29
|
+
if (fs.existsSync(envPath)) {
|
|
30
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
31
|
+
const backupPath = path.join(process.cwd(), `.env.backup.${timestamp}`);
|
|
32
|
+
fs.copySync(envPath, backupPath);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const restoreEnv = (backupFile) => {
|
|
39
|
+
const backupPath = path.join(process.cwd(), backupFile);
|
|
40
|
+
if (fs.existsSync(backupPath)) {
|
|
41
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
42
|
+
fs.copySync(backupPath, envPath);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const getLatestVersion = () => {
|
|
49
|
+
try {
|
|
50
|
+
const result = execSync("git describe --tags --abbrev=0", { stdio: "pipe" })
|
|
51
|
+
.toString()
|
|
52
|
+
.trim();
|
|
53
|
+
return result;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const getCurrentVersion = () => {
|
|
60
|
+
try {
|
|
61
|
+
const result = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
62
|
+
stdio: "pipe",
|
|
63
|
+
})
|
|
64
|
+
.toString()
|
|
65
|
+
.trim();
|
|
66
|
+
return result;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const createDockerNetworks = (networks) => {
|
|
73
|
+
const networkSpinner = ora("Creating Docker networks").start();
|
|
74
|
+
|
|
75
|
+
for (const network of networks) {
|
|
76
|
+
try {
|
|
77
|
+
// Check if network exists
|
|
78
|
+
execSync(`docker network inspect ${network}`, { stdio: 'ignore' });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// Network doesn't exist, create it
|
|
81
|
+
try {
|
|
82
|
+
execSync(`docker network create ${network}`, { stdio: 'pipe' });
|
|
83
|
+
console.log(chalk.green(`\nCreated network: ${network}`));
|
|
84
|
+
} catch (createError) {
|
|
85
|
+
networkSpinner.fail(`Failed to create network: ${network}`);
|
|
86
|
+
console.error(chalk.red("\nError details:"));
|
|
87
|
+
console.error(chalk.white(createError.stdout?.toString() || createError.message));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
networkSpinner.succeed("Docker networks created");
|
|
94
|
+
};
|