@turboops/cli 1.0.0-dev.580 → 1.0.0-dev.582
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/dist/index.js +531 -82
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import chalk8 from "chalk";
|
|
|
6
6
|
|
|
7
7
|
// src/services/config.ts
|
|
8
8
|
import Conf from "conf";
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import * as path from "path";
|
|
9
11
|
|
|
10
12
|
// src/utils/logger.ts
|
|
11
13
|
import chalk from "chalk";
|
|
@@ -133,9 +135,9 @@ function loadPackageJson() {
|
|
|
133
135
|
join(__dirname, "../package.json")
|
|
134
136
|
// From dist/
|
|
135
137
|
];
|
|
136
|
-
for (const
|
|
137
|
-
if (existsSync(
|
|
138
|
-
return require2(
|
|
138
|
+
for (const path2 of paths) {
|
|
139
|
+
if (existsSync(path2)) {
|
|
140
|
+
return require2(path2);
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
143
|
return { version: "0.0.0", name: "@turboops/cli" };
|
|
@@ -200,41 +202,76 @@ var APP_URLS = {
|
|
|
200
202
|
production: "https://turbo-ops.de",
|
|
201
203
|
dev: "https://dev.turbo-ops.de"
|
|
202
204
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const isDevVersion = version.includes("-dev");
|
|
206
|
-
return isDevVersion ? API_URLS.dev : API_URLS.production;
|
|
207
|
-
}
|
|
208
|
-
var defaults = {
|
|
209
|
-
apiUrl: API_URLS.production,
|
|
205
|
+
var LOCAL_CONFIG_FILE = ".turboops.json";
|
|
206
|
+
var globalDefaults = {
|
|
210
207
|
token: null,
|
|
211
|
-
project: null,
|
|
212
208
|
userId: null
|
|
213
209
|
};
|
|
214
|
-
var
|
|
210
|
+
var globalConfig = new Conf({
|
|
215
211
|
projectName: "turboops-cli",
|
|
216
|
-
defaults
|
|
212
|
+
defaults: globalDefaults
|
|
217
213
|
});
|
|
214
|
+
function findProjectRoot() {
|
|
215
|
+
let currentDir = process.cwd();
|
|
216
|
+
const root = path.parse(currentDir).root;
|
|
217
|
+
while (currentDir !== root) {
|
|
218
|
+
if (fs.existsSync(path.join(currentDir, LOCAL_CONFIG_FILE))) {
|
|
219
|
+
return currentDir;
|
|
220
|
+
}
|
|
221
|
+
if (fs.existsSync(path.join(currentDir, ".git"))) {
|
|
222
|
+
return currentDir;
|
|
223
|
+
}
|
|
224
|
+
if (fs.existsSync(path.join(currentDir, "package.json"))) {
|
|
225
|
+
return currentDir;
|
|
226
|
+
}
|
|
227
|
+
currentDir = path.dirname(currentDir);
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
function getLocalConfigPath() {
|
|
232
|
+
const projectRoot = findProjectRoot();
|
|
233
|
+
if (!projectRoot) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
return path.join(projectRoot, LOCAL_CONFIG_FILE);
|
|
237
|
+
}
|
|
238
|
+
function readLocalConfig() {
|
|
239
|
+
const configPath = getLocalConfigPath();
|
|
240
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
245
|
+
return JSON.parse(content);
|
|
246
|
+
} catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function writeLocalConfig(config) {
|
|
251
|
+
const projectRoot = findProjectRoot() || process.cwd();
|
|
252
|
+
const configPath = path.join(projectRoot, LOCAL_CONFIG_FILE);
|
|
253
|
+
try {
|
|
254
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
255
|
+
return true;
|
|
256
|
+
} catch (error) {
|
|
257
|
+
logger.error(`Failed to write ${LOCAL_CONFIG_FILE}: ${error}`);
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function getApiUrl() {
|
|
262
|
+
const version = getCurrentVersion();
|
|
263
|
+
const isDevVersion = version.includes("-dev");
|
|
264
|
+
return isDevVersion ? API_URLS.dev : API_URLS.production;
|
|
265
|
+
}
|
|
218
266
|
var configService = {
|
|
219
267
|
/**
|
|
220
|
-
* Get API URL
|
|
221
|
-
* Priority: ENV variable > stored config > version-based default
|
|
268
|
+
* Get API URL (fixed based on CLI version)
|
|
222
269
|
*/
|
|
223
270
|
getApiUrl() {
|
|
224
271
|
if (process.env.TURBOOPS_API_URL) {
|
|
225
272
|
return process.env.TURBOOPS_API_URL;
|
|
226
273
|
}
|
|
227
|
-
|
|
228
|
-
if (storedUrl === "https://api.turboops.io" || storedUrl === API_URLS.production) {
|
|
229
|
-
return getDefaultApiUrl();
|
|
230
|
-
}
|
|
231
|
-
return storedUrl;
|
|
232
|
-
},
|
|
233
|
-
/**
|
|
234
|
-
* Set API URL
|
|
235
|
-
*/
|
|
236
|
-
setApiUrl(url) {
|
|
237
|
-
config.set("apiUrl", url);
|
|
274
|
+
return getApiUrl();
|
|
238
275
|
},
|
|
239
276
|
/**
|
|
240
277
|
* Get App URL (frontend)
|
|
@@ -246,53 +283,65 @@ var configService = {
|
|
|
246
283
|
return isDevVersion ? APP_URLS.dev : APP_URLS.production;
|
|
247
284
|
},
|
|
248
285
|
/**
|
|
249
|
-
* Get authentication token
|
|
286
|
+
* Get authentication token (global - user session)
|
|
250
287
|
*/
|
|
251
288
|
getToken() {
|
|
252
|
-
return process.env.TURBOOPS_TOKEN ||
|
|
289
|
+
return process.env.TURBOOPS_TOKEN || globalConfig.get("token");
|
|
253
290
|
},
|
|
254
291
|
/**
|
|
255
|
-
* Set authentication token
|
|
292
|
+
* Set authentication token (global - user session)
|
|
256
293
|
*/
|
|
257
294
|
setToken(token) {
|
|
258
|
-
|
|
295
|
+
globalConfig.set("token", token);
|
|
259
296
|
},
|
|
260
297
|
/**
|
|
261
298
|
* Clear authentication token
|
|
262
299
|
*/
|
|
263
300
|
clearToken() {
|
|
264
|
-
|
|
265
|
-
|
|
301
|
+
globalConfig.set("token", null);
|
|
302
|
+
globalConfig.set("userId", null);
|
|
266
303
|
},
|
|
267
304
|
/**
|
|
268
|
-
* Get current project
|
|
305
|
+
* Get current project (local - project specific)
|
|
269
306
|
*/
|
|
270
307
|
getProject() {
|
|
271
|
-
|
|
308
|
+
if (process.env.TURBOOPS_PROJECT) {
|
|
309
|
+
return process.env.TURBOOPS_PROJECT;
|
|
310
|
+
}
|
|
311
|
+
const localConfig = readLocalConfig();
|
|
312
|
+
return localConfig?.project || null;
|
|
272
313
|
},
|
|
273
314
|
/**
|
|
274
|
-
* Set current project
|
|
315
|
+
* Set current project (local - writes to .turboops.json)
|
|
275
316
|
*/
|
|
276
317
|
setProject(project) {
|
|
277
|
-
|
|
318
|
+
const localConfig = readLocalConfig() || { project: "" };
|
|
319
|
+
localConfig.project = project;
|
|
320
|
+
writeLocalConfig(localConfig);
|
|
278
321
|
},
|
|
279
322
|
/**
|
|
280
323
|
* Clear current project
|
|
281
324
|
*/
|
|
282
325
|
clearProject() {
|
|
283
|
-
|
|
326
|
+
const configPath = getLocalConfigPath();
|
|
327
|
+
if (configPath && fs.existsSync(configPath)) {
|
|
328
|
+
try {
|
|
329
|
+
fs.unlinkSync(configPath);
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
}
|
|
284
333
|
},
|
|
285
334
|
/**
|
|
286
|
-
* Get user ID
|
|
335
|
+
* Get user ID (global - user session)
|
|
287
336
|
*/
|
|
288
337
|
getUserId() {
|
|
289
|
-
return
|
|
338
|
+
return globalConfig.get("userId");
|
|
290
339
|
},
|
|
291
340
|
/**
|
|
292
|
-
* Set user ID
|
|
341
|
+
* Set user ID (global - user session)
|
|
293
342
|
*/
|
|
294
343
|
setUserId(userId) {
|
|
295
|
-
|
|
344
|
+
globalConfig.set("userId", userId);
|
|
296
345
|
},
|
|
297
346
|
/**
|
|
298
347
|
* Check if user is authenticated
|
|
@@ -306,6 +355,19 @@ var configService = {
|
|
|
306
355
|
hasProject() {
|
|
307
356
|
return !!this.getProject();
|
|
308
357
|
},
|
|
358
|
+
/**
|
|
359
|
+
* Check if local config exists
|
|
360
|
+
*/
|
|
361
|
+
hasLocalConfig() {
|
|
362
|
+
const configPath = getLocalConfigPath();
|
|
363
|
+
return !!configPath && fs.existsSync(configPath);
|
|
364
|
+
},
|
|
365
|
+
/**
|
|
366
|
+
* Get local config file path (for display purposes)
|
|
367
|
+
*/
|
|
368
|
+
getLocalConfigPath() {
|
|
369
|
+
return getLocalConfigPath();
|
|
370
|
+
},
|
|
309
371
|
/**
|
|
310
372
|
* Get all configuration
|
|
311
373
|
*/
|
|
@@ -318,17 +380,25 @@ var configService = {
|
|
|
318
380
|
};
|
|
319
381
|
},
|
|
320
382
|
/**
|
|
321
|
-
* Clear all configuration
|
|
383
|
+
* Clear all configuration (both global and local)
|
|
322
384
|
*/
|
|
323
385
|
clearAll() {
|
|
324
|
-
|
|
386
|
+
globalConfig.clear();
|
|
387
|
+
this.clearProject();
|
|
325
388
|
},
|
|
326
389
|
/**
|
|
327
|
-
* Check if using a project token (turbo_xxx format)
|
|
390
|
+
* Check if using a project token (turbo_xxx format, but not turbo_cli_xxx)
|
|
328
391
|
*/
|
|
329
392
|
isProjectToken() {
|
|
330
393
|
const token = this.getToken();
|
|
331
|
-
return !!token && token.startsWith("turbo_");
|
|
394
|
+
return !!token && token.startsWith("turbo_") && !token.startsWith("turbo_cli_");
|
|
395
|
+
},
|
|
396
|
+
/**
|
|
397
|
+
* Check if using a CLI session token (turbo_cli_xxx format)
|
|
398
|
+
*/
|
|
399
|
+
isCliSessionToken() {
|
|
400
|
+
const token = this.getToken();
|
|
401
|
+
return !!token && token.startsWith("turbo_cli_");
|
|
332
402
|
},
|
|
333
403
|
/**
|
|
334
404
|
* Show current configuration
|
|
@@ -336,12 +406,15 @@ var configService = {
|
|
|
336
406
|
show() {
|
|
337
407
|
const data = this.getAll();
|
|
338
408
|
const isProjectToken2 = this.isProjectToken();
|
|
409
|
+
const isCliToken = this.isCliSessionToken();
|
|
410
|
+
const localConfigPath = this.getLocalConfigPath();
|
|
339
411
|
logger.header("Configuration");
|
|
340
412
|
logger.table({
|
|
341
413
|
"API URL": data.apiUrl,
|
|
342
414
|
Token: data.token || "Not set",
|
|
343
|
-
"Token Type": isProjectToken2 ? "Project Token" : data.token ? "User Token" : "N/A",
|
|
415
|
+
"Token Type": isProjectToken2 ? "Project Token (CI/CD)" : isCliToken ? "CLI Session Token" : data.token ? "User Token" : "N/A",
|
|
344
416
|
Project: data.project || (isProjectToken2 ? "(from token)" : "Not set"),
|
|
417
|
+
"Project Config": localConfigPath || "(not found)",
|
|
345
418
|
"User ID": data.userId || (isProjectToken2 ? "(N/A for project token)" : "Not set")
|
|
346
419
|
});
|
|
347
420
|
}
|
|
@@ -359,7 +432,7 @@ var apiClient = {
|
|
|
359
432
|
/**
|
|
360
433
|
* Make an API request
|
|
361
434
|
*/
|
|
362
|
-
async request(method,
|
|
435
|
+
async request(method, path2, body) {
|
|
363
436
|
const apiUrl = configService.getApiUrl();
|
|
364
437
|
const token = configService.getToken();
|
|
365
438
|
if (!token) {
|
|
@@ -368,7 +441,7 @@ var apiClient = {
|
|
|
368
441
|
status: 401
|
|
369
442
|
};
|
|
370
443
|
}
|
|
371
|
-
const url = `${apiUrl}${
|
|
444
|
+
const url = `${apiUrl}${path2}`;
|
|
372
445
|
const headers = {
|
|
373
446
|
"Content-Type": "application/json",
|
|
374
447
|
Authorization: `Bearer ${token}`
|
|
@@ -513,23 +586,66 @@ var apiClient = {
|
|
|
513
586
|
return this.request("GET", `/deployment/projects/by-slug/${slug}`);
|
|
514
587
|
},
|
|
515
588
|
/**
|
|
516
|
-
*
|
|
589
|
+
* Create a new project
|
|
590
|
+
*/
|
|
591
|
+
async createProject(data) {
|
|
592
|
+
const payload = {
|
|
593
|
+
name: data.name,
|
|
594
|
+
slug: data.slug
|
|
595
|
+
};
|
|
596
|
+
if (data.customer) payload.customer = data.customer;
|
|
597
|
+
if (data.description) payload.description = data.description;
|
|
598
|
+
if (data.repositoryUrl) payload.repositoryUrl = data.repositoryUrl;
|
|
599
|
+
return this.request("POST", "/deployment/projects/simple", payload);
|
|
600
|
+
},
|
|
601
|
+
/**
|
|
602
|
+
* Get all customers
|
|
603
|
+
*/
|
|
604
|
+
async getCustomers() {
|
|
605
|
+
return this.request("GET", "/customer");
|
|
606
|
+
},
|
|
607
|
+
/**
|
|
608
|
+
* Get environments (stages) for project
|
|
517
609
|
*/
|
|
518
610
|
async getEnvironments(projectId) {
|
|
519
611
|
return this.request(
|
|
520
612
|
"GET",
|
|
521
|
-
`/deployment/
|
|
613
|
+
`/deployment/stages?projectId=${projectId}`
|
|
522
614
|
);
|
|
523
615
|
},
|
|
524
616
|
/**
|
|
525
|
-
* Get environment by slug
|
|
617
|
+
* Get environment (stage) by slug
|
|
526
618
|
*/
|
|
527
619
|
async getEnvironmentBySlug(projectId, slug) {
|
|
528
620
|
return this.request(
|
|
529
621
|
"GET",
|
|
530
|
-
`/deployment/
|
|
622
|
+
`/deployment/stages/by-slug/${projectId}/${slug}`
|
|
531
623
|
);
|
|
532
624
|
},
|
|
625
|
+
/**
|
|
626
|
+
* Create a new stage
|
|
627
|
+
*/
|
|
628
|
+
async createStage(data) {
|
|
629
|
+
return this.request("POST", "/deployment/stages/simple", data);
|
|
630
|
+
},
|
|
631
|
+
/**
|
|
632
|
+
* Get deployment-ready servers
|
|
633
|
+
*/
|
|
634
|
+
async getDeploymentServers() {
|
|
635
|
+
return this.request("GET", "/server?deploymentReady=true");
|
|
636
|
+
},
|
|
637
|
+
/**
|
|
638
|
+
* Generate CI/CD pipeline configuration
|
|
639
|
+
*/
|
|
640
|
+
async generatePipeline(projectId, type) {
|
|
641
|
+
return this.request("GET", `/deployment/projects/${projectId}/pipeline/${type}`);
|
|
642
|
+
},
|
|
643
|
+
/**
|
|
644
|
+
* Get required secrets for pipeline
|
|
645
|
+
*/
|
|
646
|
+
async getPipelineSecrets(projectId, type) {
|
|
647
|
+
return this.request("GET", `/deployment/projects/${projectId}/pipeline/${type}/secrets`);
|
|
648
|
+
},
|
|
533
649
|
/**
|
|
534
650
|
* Trigger deployment
|
|
535
651
|
*/
|
|
@@ -560,7 +676,7 @@ var apiClient = {
|
|
|
560
676
|
async restart(environmentId) {
|
|
561
677
|
return this.request(
|
|
562
678
|
"POST",
|
|
563
|
-
`/deployment/
|
|
679
|
+
`/deployment/stages/${environmentId}/restart`
|
|
564
680
|
);
|
|
565
681
|
},
|
|
566
682
|
/**
|
|
@@ -569,7 +685,7 @@ var apiClient = {
|
|
|
569
685
|
async stop(environmentId) {
|
|
570
686
|
return this.request(
|
|
571
687
|
"POST",
|
|
572
|
-
`/deployment/
|
|
688
|
+
`/deployment/stages/${environmentId}/stop`
|
|
573
689
|
);
|
|
574
690
|
},
|
|
575
691
|
/**
|
|
@@ -578,7 +694,7 @@ var apiClient = {
|
|
|
578
694
|
async wake(environmentId) {
|
|
579
695
|
return this.request(
|
|
580
696
|
"POST",
|
|
581
|
-
`/deployment/
|
|
697
|
+
`/deployment/stages/${environmentId}/wake`
|
|
582
698
|
);
|
|
583
699
|
},
|
|
584
700
|
/**
|
|
@@ -593,7 +709,7 @@ var apiClient = {
|
|
|
593
709
|
async getEnvVars(environmentId) {
|
|
594
710
|
return this.request(
|
|
595
711
|
"GET",
|
|
596
|
-
`/deployment/
|
|
712
|
+
`/deployment/stages/${environmentId}/env-vars`
|
|
597
713
|
);
|
|
598
714
|
},
|
|
599
715
|
/**
|
|
@@ -602,7 +718,7 @@ var apiClient = {
|
|
|
602
718
|
async setEnvVar(environmentId, key, value, secret) {
|
|
603
719
|
return this.request(
|
|
604
720
|
"PUT",
|
|
605
|
-
`/deployment/
|
|
721
|
+
`/deployment/stages/${environmentId}/env-vars`,
|
|
606
722
|
{
|
|
607
723
|
key,
|
|
608
724
|
value,
|
|
@@ -616,7 +732,7 @@ var apiClient = {
|
|
|
616
732
|
async deleteEnvVar(environmentId, key) {
|
|
617
733
|
return this.request(
|
|
618
734
|
"DELETE",
|
|
619
|
-
`/deployment/
|
|
735
|
+
`/deployment/stages/${environmentId}/env-vars/${key}`
|
|
620
736
|
);
|
|
621
737
|
},
|
|
622
738
|
/**
|
|
@@ -1206,11 +1322,77 @@ var statusCommand = new Command3("status").description("Show deployment status")
|
|
|
1206
1322
|
});
|
|
1207
1323
|
}
|
|
1208
1324
|
});
|
|
1209
|
-
var configCommand = new Command3("config").description("
|
|
1210
|
-
|
|
1211
|
-
|
|
1325
|
+
var configCommand = new Command3("config").description("Manage configuration");
|
|
1326
|
+
configCommand.command("show", { isDefault: true }).description("Show current configuration").action(() => {
|
|
1327
|
+
const config = configService.getAll();
|
|
1328
|
+
addJsonData({ config });
|
|
1212
1329
|
configService.show();
|
|
1213
1330
|
});
|
|
1331
|
+
configCommand.command("set").description("Set a configuration value").argument("<key>", "Configuration key (token, project)").argument("<value>", "Value to set").action((key, value) => {
|
|
1332
|
+
switch (key.toLowerCase()) {
|
|
1333
|
+
case "token":
|
|
1334
|
+
configService.setToken(value);
|
|
1335
|
+
logger.success("Token saved (global)");
|
|
1336
|
+
addJsonData({ key: "token", saved: true, scope: "global" });
|
|
1337
|
+
break;
|
|
1338
|
+
case "project":
|
|
1339
|
+
configService.setProject(value);
|
|
1340
|
+
logger.success(`Project saved to .turboops.json`);
|
|
1341
|
+
addJsonData({ key: "project", value, saved: true, scope: "local" });
|
|
1342
|
+
break;
|
|
1343
|
+
default:
|
|
1344
|
+
logger.error(`Unknown config key: ${key}`);
|
|
1345
|
+
logger.info("Available keys: token, project");
|
|
1346
|
+
addJsonData({ error: `Unknown config key: ${key}` });
|
|
1347
|
+
process.exit(14 /* VALIDATION_ERROR */);
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
configCommand.command("get").description("Get a configuration value").argument("<key>", "Configuration key (token, project, api-url)").action((key) => {
|
|
1351
|
+
let value = null;
|
|
1352
|
+
switch (key.toLowerCase()) {
|
|
1353
|
+
case "token":
|
|
1354
|
+
value = configService.getToken();
|
|
1355
|
+
if (value) {
|
|
1356
|
+
console.log("***");
|
|
1357
|
+
addJsonData({ key: "token", set: true });
|
|
1358
|
+
} else {
|
|
1359
|
+
console.log("(not set)");
|
|
1360
|
+
addJsonData({ key: "token", set: false });
|
|
1361
|
+
}
|
|
1362
|
+
return;
|
|
1363
|
+
case "project":
|
|
1364
|
+
value = configService.getProject();
|
|
1365
|
+
break;
|
|
1366
|
+
case "api-url":
|
|
1367
|
+
value = configService.getApiUrl();
|
|
1368
|
+
break;
|
|
1369
|
+
default:
|
|
1370
|
+
logger.error(`Unknown config key: ${key}`);
|
|
1371
|
+
logger.info("Available keys: token, project, api-url");
|
|
1372
|
+
addJsonData({ error: `Unknown config key: ${key}` });
|
|
1373
|
+
process.exit(14 /* VALIDATION_ERROR */);
|
|
1374
|
+
}
|
|
1375
|
+
console.log(value || "(not set)");
|
|
1376
|
+
addJsonData({ key, value });
|
|
1377
|
+
});
|
|
1378
|
+
configCommand.command("clear").description("Clear configuration").option("--all", "Clear all configuration").option("--token", "Clear token only").option("--project", "Clear project only").action((opts) => {
|
|
1379
|
+
if (opts.all) {
|
|
1380
|
+
configService.clearAll();
|
|
1381
|
+
logger.success("All configuration cleared");
|
|
1382
|
+
addJsonData({ cleared: "all" });
|
|
1383
|
+
} else if (opts.token) {
|
|
1384
|
+
configService.clearToken();
|
|
1385
|
+
logger.success("Token cleared");
|
|
1386
|
+
addJsonData({ cleared: "token" });
|
|
1387
|
+
} else if (opts.project) {
|
|
1388
|
+
configService.clearProject();
|
|
1389
|
+
logger.success("Project configuration cleared");
|
|
1390
|
+
addJsonData({ cleared: "project" });
|
|
1391
|
+
} else {
|
|
1392
|
+
logger.error("Specify what to clear: --all, --token, or --project");
|
|
1393
|
+
process.exit(14 /* VALIDATION_ERROR */);
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1214
1396
|
function getStatusColor(status) {
|
|
1215
1397
|
switch (status) {
|
|
1216
1398
|
case "healthy":
|
|
@@ -1367,24 +1549,294 @@ var initCommand = new Command5("init").description("Initialize TurboOps project
|
|
|
1367
1549
|
return;
|
|
1368
1550
|
}
|
|
1369
1551
|
const { data: project, error: projectError } = await withSpinner(
|
|
1370
|
-
"
|
|
1552
|
+
"Suche Projekt...",
|
|
1371
1553
|
() => apiClient.getProject(projectSlug)
|
|
1372
1554
|
);
|
|
1373
|
-
if (
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1555
|
+
if (project) {
|
|
1556
|
+
await setupProject(project);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
logger.newline();
|
|
1560
|
+
logger.warning(`Projekt "${projectSlug}" nicht gefunden.`);
|
|
1561
|
+
const { shouldCreate } = await prompts({
|
|
1562
|
+
initial: true,
|
|
1563
|
+
message: "M\xF6chten Sie ein neues Projekt anlegen?",
|
|
1564
|
+
name: "shouldCreate",
|
|
1565
|
+
type: "confirm"
|
|
1566
|
+
});
|
|
1567
|
+
if (!shouldCreate) {
|
|
1568
|
+
logger.info("Initialisierung abgebrochen.");
|
|
1569
|
+
addJsonData({ initialized: false, reason: "project_not_found" });
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
await createNewProject(projectSlug);
|
|
1573
|
+
});
|
|
1574
|
+
async function setupProject(project) {
|
|
1575
|
+
configService.setProject(project.slug);
|
|
1576
|
+
const { data: environments } = await apiClient.getEnvironments(project.id);
|
|
1577
|
+
if (!environments || environments.length === 0) {
|
|
1578
|
+
logger.newline();
|
|
1579
|
+
const { shouldCreateStage } = await prompts({
|
|
1580
|
+
initial: true,
|
|
1581
|
+
message: "Das Projekt hat noch keine Stages. M\xF6chten Sie jetzt eine erstellen?",
|
|
1582
|
+
name: "shouldCreateStage",
|
|
1583
|
+
type: "confirm"
|
|
1584
|
+
});
|
|
1585
|
+
if (shouldCreateStage) {
|
|
1586
|
+
await createFirstStage(project.id, project.slug);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
const fs2 = await import("fs/promises");
|
|
1590
|
+
const path2 = await import("path");
|
|
1591
|
+
const gitlabCiPath = path2.join(process.cwd(), ".gitlab-ci.yml");
|
|
1592
|
+
let hasGitLabPipeline = false;
|
|
1593
|
+
try {
|
|
1594
|
+
await fs2.access(gitlabCiPath);
|
|
1595
|
+
hasGitLabPipeline = true;
|
|
1596
|
+
} catch {
|
|
1597
|
+
}
|
|
1598
|
+
if (!hasGitLabPipeline) {
|
|
1599
|
+
logger.newline();
|
|
1600
|
+
const { shouldCreatePipeline } = await prompts({
|
|
1601
|
+
initial: false,
|
|
1602
|
+
message: "M\xF6chten Sie eine CI/CD Pipeline anlegen?",
|
|
1603
|
+
name: "shouldCreatePipeline",
|
|
1604
|
+
type: "confirm"
|
|
1605
|
+
});
|
|
1606
|
+
if (shouldCreatePipeline) {
|
|
1607
|
+
await createPipeline(project.id);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
await showFinalSummary(project);
|
|
1611
|
+
}
|
|
1612
|
+
async function createNewProject(slug) {
|
|
1613
|
+
logger.newline();
|
|
1614
|
+
logger.header("Neues Projekt erstellen");
|
|
1615
|
+
const { data: customers } = await withSpinner(
|
|
1616
|
+
"Lade verf\xFCgbare Kunden...",
|
|
1617
|
+
() => apiClient.getCustomers()
|
|
1618
|
+
);
|
|
1619
|
+
const promptQuestions = [
|
|
1620
|
+
{
|
|
1621
|
+
initial: slug.charAt(0).toUpperCase() + slug.slice(1).replace(/-/g, " "),
|
|
1622
|
+
message: "Projektname:",
|
|
1623
|
+
name: "name",
|
|
1624
|
+
type: "text",
|
|
1625
|
+
validate: (value) => value.length > 0 || "Projektname ist erforderlich"
|
|
1626
|
+
}
|
|
1627
|
+
];
|
|
1628
|
+
if (customers && customers.length > 0) {
|
|
1629
|
+
promptQuestions.push({
|
|
1630
|
+
choices: [
|
|
1631
|
+
{ title: "(Kein Kunde)", value: "" },
|
|
1632
|
+
...customers.map((c) => ({ title: c.name, value: c.id }))
|
|
1633
|
+
],
|
|
1634
|
+
message: "Kunde (optional):",
|
|
1635
|
+
name: "customer",
|
|
1636
|
+
type: "select"
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
promptQuestions.push(
|
|
1640
|
+
{
|
|
1641
|
+
message: "Beschreibung (optional):",
|
|
1642
|
+
name: "description",
|
|
1643
|
+
type: "text"
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
message: "Repository URL (optional):",
|
|
1647
|
+
name: "repositoryUrl",
|
|
1648
|
+
type: "text"
|
|
1649
|
+
}
|
|
1650
|
+
);
|
|
1651
|
+
const projectDetails = await prompts(promptQuestions);
|
|
1652
|
+
if (!projectDetails.name) {
|
|
1653
|
+
logger.warning("Projekterstellung abgebrochen");
|
|
1654
|
+
addJsonData({ initialized: false, reason: "cancelled" });
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
const { data: newProject, error: createError } = await withSpinner(
|
|
1658
|
+
"Erstelle Projekt...",
|
|
1659
|
+
() => apiClient.createProject({
|
|
1660
|
+
customer: projectDetails.customer || void 0,
|
|
1661
|
+
description: projectDetails.description || void 0,
|
|
1662
|
+
name: projectDetails.name,
|
|
1663
|
+
repositoryUrl: projectDetails.repositoryUrl || void 0,
|
|
1664
|
+
slug
|
|
1665
|
+
})
|
|
1666
|
+
);
|
|
1667
|
+
if (createError || !newProject) {
|
|
1668
|
+
logger.error(`Projekt konnte nicht erstellt werden: ${createError || "Unbekannter Fehler"}`);
|
|
1380
1669
|
addJsonData({
|
|
1670
|
+
error: createError || "Unknown error",
|
|
1381
1671
|
initialized: false,
|
|
1382
|
-
|
|
1383
|
-
reason: "project_not_found"
|
|
1672
|
+
reason: "create_failed"
|
|
1384
1673
|
});
|
|
1385
|
-
process.exit(
|
|
1674
|
+
process.exit(13 /* API_ERROR */);
|
|
1386
1675
|
}
|
|
1387
|
-
|
|
1676
|
+
logger.success(`Projekt "${newProject.name}" wurde erstellt!`);
|
|
1677
|
+
configService.setProject(newProject.slug);
|
|
1678
|
+
logger.newline();
|
|
1679
|
+
const { shouldCreateStage } = await prompts({
|
|
1680
|
+
initial: true,
|
|
1681
|
+
message: "M\xF6chten Sie jetzt die erste Stage anlegen?",
|
|
1682
|
+
name: "shouldCreateStage",
|
|
1683
|
+
type: "confirm"
|
|
1684
|
+
});
|
|
1685
|
+
if (shouldCreateStage) {
|
|
1686
|
+
await createFirstStage(newProject.id, newProject.slug);
|
|
1687
|
+
}
|
|
1688
|
+
logger.newline();
|
|
1689
|
+
const { shouldCreatePipeline } = await prompts({
|
|
1690
|
+
initial: true,
|
|
1691
|
+
message: "M\xF6chten Sie eine CI/CD Pipeline anlegen?",
|
|
1692
|
+
name: "shouldCreatePipeline",
|
|
1693
|
+
type: "confirm"
|
|
1694
|
+
});
|
|
1695
|
+
if (shouldCreatePipeline) {
|
|
1696
|
+
await createPipeline(newProject.id);
|
|
1697
|
+
}
|
|
1698
|
+
await showFinalSummary(newProject);
|
|
1699
|
+
}
|
|
1700
|
+
async function createFirstStage(projectId, projectSlug) {
|
|
1701
|
+
logger.newline();
|
|
1702
|
+
logger.header("Erste Stage erstellen");
|
|
1703
|
+
const { data: servers } = await withSpinner(
|
|
1704
|
+
"Lade verf\xFCgbare Server...",
|
|
1705
|
+
() => apiClient.getDeploymentServers()
|
|
1706
|
+
);
|
|
1707
|
+
const stageTypes = [
|
|
1708
|
+
{ title: "Development", value: "development" },
|
|
1709
|
+
{ title: "Staging", value: "staging" },
|
|
1710
|
+
{ title: "Production", value: "production" }
|
|
1711
|
+
];
|
|
1712
|
+
const stageQuestions = [
|
|
1713
|
+
{
|
|
1714
|
+
choices: stageTypes,
|
|
1715
|
+
initial: 0,
|
|
1716
|
+
message: "Stage-Typ:",
|
|
1717
|
+
name: "type",
|
|
1718
|
+
type: "select"
|
|
1719
|
+
},
|
|
1720
|
+
{
|
|
1721
|
+
initial: (prev) => prev === "production" ? "Production" : prev === "staging" ? "Staging" : "Development",
|
|
1722
|
+
message: "Stage-Name:",
|
|
1723
|
+
name: "name",
|
|
1724
|
+
type: "text",
|
|
1725
|
+
validate: (value) => value.length > 0 || "Name ist erforderlich"
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
initial: (_prev, values) => values.type || "",
|
|
1729
|
+
message: "Stage-Slug:",
|
|
1730
|
+
name: "slug",
|
|
1731
|
+
type: "text",
|
|
1732
|
+
validate: (value) => value.length > 0 || "Slug ist erforderlich"
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
initial: (_prev, values) => `${values.slug || ""}.${projectSlug}.example.com`,
|
|
1736
|
+
message: "Domain:",
|
|
1737
|
+
name: "domain",
|
|
1738
|
+
type: "text"
|
|
1739
|
+
},
|
|
1740
|
+
{
|
|
1741
|
+
initial: (_prev, values) => values.type === "production" ? "main" : values.type === "staging" ? "staging" : "develop",
|
|
1742
|
+
message: "Branch:",
|
|
1743
|
+
name: "branch",
|
|
1744
|
+
type: "text"
|
|
1745
|
+
}
|
|
1746
|
+
];
|
|
1747
|
+
if (servers && servers.length > 0) {
|
|
1748
|
+
stageQuestions.push({
|
|
1749
|
+
choices: [
|
|
1750
|
+
{ title: "(Sp\xE4ter ausw\xE4hlen)", value: "" },
|
|
1751
|
+
...servers.map((s) => ({ title: `${s.name} (${s.host})`, value: s.id }))
|
|
1752
|
+
],
|
|
1753
|
+
message: "Server (optional):",
|
|
1754
|
+
name: "server",
|
|
1755
|
+
type: "select"
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
const stageDetails = await prompts(stageQuestions);
|
|
1759
|
+
if (!stageDetails.name || !stageDetails.slug) {
|
|
1760
|
+
logger.warning("Stage-Erstellung abgebrochen");
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
const { data: newStage, error: stageError } = await withSpinner(
|
|
1764
|
+
"Erstelle Stage...",
|
|
1765
|
+
() => apiClient.createStage({
|
|
1766
|
+
project: projectId,
|
|
1767
|
+
name: stageDetails.name,
|
|
1768
|
+
slug: stageDetails.slug,
|
|
1769
|
+
type: stageDetails.type,
|
|
1770
|
+
domain: stageDetails.domain || void 0,
|
|
1771
|
+
server: stageDetails.server || void 0,
|
|
1772
|
+
branch: stageDetails.branch || void 0
|
|
1773
|
+
})
|
|
1774
|
+
);
|
|
1775
|
+
if (stageError || !newStage) {
|
|
1776
|
+
logger.error(`Stage konnte nicht erstellt werden: ${stageError || "Unbekannter Fehler"}`);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
logger.success(`Stage "${newStage.name}" wurde erstellt!`);
|
|
1780
|
+
}
|
|
1781
|
+
async function createPipeline(projectId) {
|
|
1782
|
+
logger.newline();
|
|
1783
|
+
logger.header("CI/CD Pipeline erstellen");
|
|
1784
|
+
const pipelineQuestions = [
|
|
1785
|
+
{
|
|
1786
|
+
choices: [
|
|
1787
|
+
{ title: "GitLab CI/CD", value: "gitlab" },
|
|
1788
|
+
{ title: "GitHub Actions", value: "github" }
|
|
1789
|
+
],
|
|
1790
|
+
initial: 0,
|
|
1791
|
+
message: "Pipeline-Typ:",
|
|
1792
|
+
name: "pipelineType",
|
|
1793
|
+
type: "select"
|
|
1794
|
+
}
|
|
1795
|
+
];
|
|
1796
|
+
const pipelineDetails = await prompts(pipelineQuestions);
|
|
1797
|
+
if (!pipelineDetails.pipelineType) {
|
|
1798
|
+
logger.warning("Pipeline-Erstellung abgebrochen");
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
const { data: pipeline, error } = await withSpinner(
|
|
1802
|
+
"Generiere Pipeline...",
|
|
1803
|
+
() => apiClient.generatePipeline(projectId, pipelineDetails.pipelineType)
|
|
1804
|
+
);
|
|
1805
|
+
if (error || !pipeline) {
|
|
1806
|
+
logger.error(`Pipeline konnte nicht generiert werden: ${error || "Unbekannter Fehler"}`);
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
const fs2 = await import("fs/promises");
|
|
1810
|
+
const path2 = await import("path");
|
|
1811
|
+
let filePath;
|
|
1812
|
+
if (pipelineDetails.pipelineType === "github") {
|
|
1813
|
+
const workflowsDir = path2.join(process.cwd(), ".github", "workflows");
|
|
1814
|
+
await fs2.mkdir(workflowsDir, { recursive: true });
|
|
1815
|
+
filePath = path2.join(workflowsDir, "deploy.yml");
|
|
1816
|
+
} else {
|
|
1817
|
+
filePath = path2.join(process.cwd(), ".gitlab-ci.yml");
|
|
1818
|
+
}
|
|
1819
|
+
try {
|
|
1820
|
+
await fs2.writeFile(filePath, pipeline.content, "utf-8");
|
|
1821
|
+
logger.success(`${pipeline.filename} wurde erstellt!`);
|
|
1822
|
+
logger.newline();
|
|
1823
|
+
const { data: secrets } = await apiClient.getPipelineSecrets(projectId, pipelineDetails.pipelineType);
|
|
1824
|
+
if (secrets && secrets.length > 0) {
|
|
1825
|
+
logger.header("Erforderliche CI/CD Secrets");
|
|
1826
|
+
for (const secret of secrets) {
|
|
1827
|
+
const value = secret.isSecret ? chalk6.dim("(geheim)") : chalk6.cyan(secret.value || "-");
|
|
1828
|
+
console.log(` ${chalk6.bold(secret.name)}: ${value}`);
|
|
1829
|
+
console.log(` ${chalk6.dim(secret.description)}`);
|
|
1830
|
+
}
|
|
1831
|
+
logger.newline();
|
|
1832
|
+
logger.info("F\xFCgen Sie diese Werte als CI/CD Secrets/Variables hinzu.");
|
|
1833
|
+
logger.info("Projekt-Token k\xF6nnen Sie in der TurboOps Web-UI erstellen.");
|
|
1834
|
+
}
|
|
1835
|
+
} catch (error2) {
|
|
1836
|
+
logger.error(`Fehler beim Schreiben der Pipeline-Datei: ${error2}`);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
async function showFinalSummary(project) {
|
|
1388
1840
|
logger.newline();
|
|
1389
1841
|
logger.success("Projekt initialisiert!");
|
|
1390
1842
|
logger.newline();
|
|
@@ -1411,7 +1863,7 @@ var initCommand = new Command5("init").description("Initialize TurboOps project
|
|
|
1411
1863
|
});
|
|
1412
1864
|
if (environments && environments.length > 0) {
|
|
1413
1865
|
logger.newline();
|
|
1414
|
-
logger.header("Verf\xFCgbare
|
|
1866
|
+
logger.header("Verf\xFCgbare Stages");
|
|
1415
1867
|
for (const env of environments) {
|
|
1416
1868
|
console.log(` ${chalk6.bold(env.slug)} - ${env.name} (${env.type})`);
|
|
1417
1869
|
}
|
|
@@ -1419,11 +1871,11 @@ var initCommand = new Command5("init").description("Initialize TurboOps project
|
|
|
1419
1871
|
logger.newline();
|
|
1420
1872
|
logger.header("N\xE4chste Schritte");
|
|
1421
1873
|
logger.list([
|
|
1422
|
-
"F\xFChren Sie `turbo status` aus, um alle
|
|
1423
|
-
"F\xFChren Sie `turbo deploy <
|
|
1424
|
-
"F\xFChren Sie `turbo logs <
|
|
1874
|
+
"F\xFChren Sie `turbo status` aus, um alle Stages zu sehen",
|
|
1875
|
+
"F\xFChren Sie `turbo deploy <stage>` aus, um zu deployen",
|
|
1876
|
+
"F\xFChren Sie `turbo logs <stage>` aus, um Logs anzuzeigen"
|
|
1425
1877
|
]);
|
|
1426
|
-
}
|
|
1878
|
+
}
|
|
1427
1879
|
|
|
1428
1880
|
// src/commands/logs.ts
|
|
1429
1881
|
import { Command as Command6 } from "commander";
|
|
@@ -1588,7 +2040,7 @@ function getLevelColor(level) {
|
|
|
1588
2040
|
var VERSION = getCurrentVersion();
|
|
1589
2041
|
var shouldCheckUpdate = true;
|
|
1590
2042
|
var program = new Command7();
|
|
1591
|
-
program.name("turbo").description("TurboCLI - Command line interface for TurboOps deployments").version(VERSION, "-v, --version", "Show version number").option("--project <slug>", "Override project slug").option("--token <token>", "Override API token").option("--
|
|
2043
|
+
program.name("turbo").description("TurboCLI - Command line interface for TurboOps deployments").version(VERSION, "-v, --version", "Show version number").option("--project <slug>", "Override project slug").option("--token <token>", "Override API token").option("--json", "Output as JSON").option("--quiet", "Only show errors").option("--verbose", "Show debug output").option("--no-update-check", "Skip version check");
|
|
1592
2044
|
program.hook("preAction", (thisCommand) => {
|
|
1593
2045
|
const opts = thisCommand.opts();
|
|
1594
2046
|
clearJsonData();
|
|
@@ -1605,9 +2057,6 @@ program.hook("preAction", (thisCommand) => {
|
|
|
1605
2057
|
if (opts.token) {
|
|
1606
2058
|
configService.setToken(opts.token);
|
|
1607
2059
|
}
|
|
1608
|
-
if (opts.api) {
|
|
1609
|
-
configService.setApiUrl(opts.api);
|
|
1610
|
-
}
|
|
1611
2060
|
if (opts.verbose) {
|
|
1612
2061
|
process.env.DEBUG = "true";
|
|
1613
2062
|
}
|