@turboops/cli 1.0.0-dev.723 → 1.0.0-dev.725
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 +25 -25
- package/dist/index.js +252 -77
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -97,12 +97,12 @@ turbo deploy production --timeout 900000
|
|
|
97
97
|
|
|
98
98
|
**Options:**
|
|
99
99
|
|
|
100
|
-
| Option
|
|
101
|
-
|
|
102
|
-
| `-i, --image <tag>` | Docker image tag to deploy
|
|
103
|
-
| `-w, --wait`
|
|
104
|
-
| `--no-wait`
|
|
105
|
-
| `--timeout <ms>`
|
|
100
|
+
| Option | Description |
|
|
101
|
+
| ------------------- | ------------------------------------------------------ |
|
|
102
|
+
| `-i, --image <tag>` | Docker image tag to deploy |
|
|
103
|
+
| `-w, --wait` | Wait for deployment to complete (default) |
|
|
104
|
+
| `--no-wait` | Do not wait for deployment to complete |
|
|
105
|
+
| `--timeout <ms>` | Timeout in milliseconds when waiting (default: 600000) |
|
|
106
106
|
|
|
107
107
|
**Note:** This command requires a Project Token with `deploy` permission. Set the token via `TURBOOPS_TOKEN` environment variable or `--token` flag.
|
|
108
108
|
|
|
@@ -135,11 +135,11 @@ turbo pipeline secrets --type github
|
|
|
135
135
|
|
|
136
136
|
**Options for `pipeline generate`:**
|
|
137
137
|
|
|
138
|
-
| Option
|
|
139
|
-
|
|
140
|
-
| `-t, --type <type>`
|
|
141
|
-
| `-f, --force`
|
|
142
|
-
| `-o, --output <path>` | Custom output path
|
|
138
|
+
| Option | Description |
|
|
139
|
+
| --------------------- | ----------------------------------- |
|
|
140
|
+
| `-t, --type <type>` | Pipeline type: `gitlab` or `github` |
|
|
141
|
+
| `-f, --force` | Overwrite existing pipeline file |
|
|
142
|
+
| `-o, --output <path>` | Custom output path |
|
|
143
143
|
|
|
144
144
|
### Self-Update
|
|
145
145
|
|
|
@@ -150,10 +150,10 @@ turbo self-update
|
|
|
150
150
|
|
|
151
151
|
## Environment Variables
|
|
152
152
|
|
|
153
|
-
| Variable
|
|
154
|
-
|
|
155
|
-
| `TURBOOPS_TOKEN`
|
|
156
|
-
| `TURBOOPS_PROJECT` | Project slug
|
|
153
|
+
| Variable | Description |
|
|
154
|
+
| ------------------ | ------------------------------------------ |
|
|
155
|
+
| `TURBOOPS_TOKEN` | API token for authentication |
|
|
156
|
+
| `TURBOOPS_PROJECT` | Project slug |
|
|
157
157
|
| `TURBOOPS_API_URL` | API URL (default: https://api.turboops.io) |
|
|
158
158
|
|
|
159
159
|
## Global Options
|
|
@@ -169,16 +169,16 @@ turbo self-update
|
|
|
169
169
|
|
|
170
170
|
## Exit Codes
|
|
171
171
|
|
|
172
|
-
| Code | Description
|
|
173
|
-
|
|
174
|
-
| 0
|
|
175
|
-
| 1
|
|
176
|
-
| 2
|
|
177
|
-
| 3
|
|
178
|
-
| 10
|
|
179
|
-
| 11
|
|
180
|
-
| 12
|
|
181
|
-
| 20
|
|
172
|
+
| Code | Description |
|
|
173
|
+
| ---- | -------------------- |
|
|
174
|
+
| 0 | Success |
|
|
175
|
+
| 1 | General error |
|
|
176
|
+
| 2 | Timeout |
|
|
177
|
+
| 3 | Network error |
|
|
178
|
+
| 10 | Authentication error |
|
|
179
|
+
| 11 | Not found |
|
|
180
|
+
| 12 | API error |
|
|
181
|
+
| 20 | Validation error |
|
|
182
182
|
|
|
183
183
|
## License
|
|
184
184
|
|
package/dist/index.js
CHANGED
|
@@ -259,7 +259,11 @@ function writeLocalConfig(config) {
|
|
|
259
259
|
const projectRoot = findProjectRoot() || process.cwd();
|
|
260
260
|
const configPath = path.join(projectRoot, LOCAL_CONFIG_FILE);
|
|
261
261
|
try {
|
|
262
|
-
fs.writeFileSync(
|
|
262
|
+
fs.writeFileSync(
|
|
263
|
+
configPath,
|
|
264
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
265
|
+
"utf-8"
|
|
266
|
+
);
|
|
263
267
|
return true;
|
|
264
268
|
} catch (error) {
|
|
265
269
|
logger.error(`Failed to write ${LOCAL_CONFIG_FILE}: ${error}`);
|
|
@@ -509,7 +513,10 @@ var apiClient = {
|
|
|
509
513
|
};
|
|
510
514
|
}
|
|
511
515
|
if (process.env.DEBUG) {
|
|
512
|
-
console.debug(
|
|
516
|
+
console.debug(
|
|
517
|
+
"[API] Response:",
|
|
518
|
+
JSON.stringify(data, null, 2).substring(0, 500)
|
|
519
|
+
);
|
|
513
520
|
}
|
|
514
521
|
return { data, status: response.status };
|
|
515
522
|
} catch (error) {
|
|
@@ -695,31 +702,47 @@ var apiClient = {
|
|
|
695
702
|
* Update project's detected configuration (partial update) (CLI endpoint)
|
|
696
703
|
*/
|
|
697
704
|
async updateProjectConfig(projectId, config) {
|
|
698
|
-
return this.request(
|
|
705
|
+
return this.request(
|
|
706
|
+
"PATCH",
|
|
707
|
+
`/cli/deployment/projects/${projectId}/config`,
|
|
708
|
+
config
|
|
709
|
+
);
|
|
699
710
|
},
|
|
700
711
|
/**
|
|
701
712
|
* Upload compose file content to project (CLI endpoint)
|
|
702
713
|
*/
|
|
703
714
|
async uploadCompose(projectId, content, message) {
|
|
704
|
-
return this.request(
|
|
715
|
+
return this.request(
|
|
716
|
+
"POST",
|
|
717
|
+
`/cli/deployment/projects/${projectId}/compose`,
|
|
718
|
+
{ content, message }
|
|
719
|
+
);
|
|
705
720
|
},
|
|
706
721
|
/**
|
|
707
722
|
* Generate CI/CD pipeline configuration (CLI endpoint)
|
|
708
723
|
*/
|
|
709
724
|
async generatePipeline(projectId, type) {
|
|
710
|
-
return this.request(
|
|
725
|
+
return this.request(
|
|
726
|
+
"GET",
|
|
727
|
+
`/cli/deployment/projects/${projectId}/pipeline/${type}`
|
|
728
|
+
);
|
|
711
729
|
},
|
|
712
730
|
/**
|
|
713
731
|
* Get required secrets for pipeline (CLI endpoint)
|
|
714
732
|
*/
|
|
715
733
|
async getPipelineSecrets(projectId, type) {
|
|
716
|
-
return this.request(
|
|
734
|
+
return this.request(
|
|
735
|
+
"GET",
|
|
736
|
+
`/cli/deployment/projects/${projectId}/pipeline/${type}/secrets`
|
|
737
|
+
);
|
|
717
738
|
},
|
|
718
739
|
/**
|
|
719
740
|
* Trigger a deployment (Project Token endpoint for CI/CD)
|
|
720
741
|
*/
|
|
721
742
|
async deploy(stageId, imageTag, composeFile) {
|
|
722
|
-
const body = {
|
|
743
|
+
const body = {
|
|
744
|
+
stage: stageId
|
|
745
|
+
};
|
|
723
746
|
if (imageTag) body.imageTag = imageTag;
|
|
724
747
|
if (composeFile) body.composeFile = composeFile;
|
|
725
748
|
return this.request("POST", "/project/deployment/deploy", body);
|
|
@@ -838,7 +861,10 @@ var authService = {
|
|
|
838
861
|
logger.info("Initialisiere Anmeldung...");
|
|
839
862
|
const authInit = await this.initAuth();
|
|
840
863
|
if (!authInit) {
|
|
841
|
-
return {
|
|
864
|
+
return {
|
|
865
|
+
error: "Konnte Authentifizierung nicht initialisieren",
|
|
866
|
+
success: false
|
|
867
|
+
};
|
|
842
868
|
}
|
|
843
869
|
logger.newline();
|
|
844
870
|
logger.info("Bitte autorisieren Sie die CLI in Ihrem Browser.");
|
|
@@ -869,7 +895,10 @@ var authService = {
|
|
|
869
895
|
};
|
|
870
896
|
}
|
|
871
897
|
if (result.status === "expired" || result.status === "consumed") {
|
|
872
|
-
return {
|
|
898
|
+
return {
|
|
899
|
+
error: "Autorisierungscode abgelaufen oder bereits verwendet",
|
|
900
|
+
success: false
|
|
901
|
+
};
|
|
873
902
|
}
|
|
874
903
|
await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
|
|
875
904
|
}
|
|
@@ -917,13 +946,16 @@ var loginCommand = new Command("login").description("Authenticate with TurboOps
|
|
|
917
946
|
if (options.token) {
|
|
918
947
|
configService.setToken(options.token);
|
|
919
948
|
if (options.token.startsWith("turbo_cli_")) {
|
|
920
|
-
const { data: data2, error: error2 } = await withSpinner(
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
949
|
+
const { data: data2, error: error2 } = await withSpinner(
|
|
950
|
+
"Verifying token...",
|
|
951
|
+
async () => {
|
|
952
|
+
const result2 = await authService.validateToken(options.token);
|
|
953
|
+
if (result2.valid && result2.user) {
|
|
954
|
+
return { data: result2.user, error: void 0 };
|
|
955
|
+
}
|
|
956
|
+
return { data: void 0, error: "Invalid token" };
|
|
924
957
|
}
|
|
925
|
-
|
|
926
|
-
});
|
|
958
|
+
);
|
|
927
959
|
if (error2 || !data2) {
|
|
928
960
|
configService.clearToken();
|
|
929
961
|
logger.error(`Authentication failed: ${error2 || "Invalid token"}`);
|
|
@@ -957,18 +989,27 @@ var loginCommand = new Command("login").description("Authenticate with TurboOps
|
|
|
957
989
|
const result = await authService.browserLogin();
|
|
958
990
|
if (!result.success) {
|
|
959
991
|
logger.error(`Login failed: ${result.error || "Unknown error"}`);
|
|
960
|
-
addJsonData({
|
|
992
|
+
addJsonData({
|
|
993
|
+
authenticated: false,
|
|
994
|
+
error: result.error || "Unknown error"
|
|
995
|
+
});
|
|
961
996
|
process.exit(10 /* AUTH_ERROR */);
|
|
962
997
|
}
|
|
963
998
|
addJsonData({
|
|
964
999
|
authenticated: true,
|
|
965
|
-
user: result.user ? {
|
|
1000
|
+
user: result.user ? {
|
|
1001
|
+
email: result.user.email,
|
|
1002
|
+
fullName: result.user.fullName,
|
|
1003
|
+
id: result.user.id
|
|
1004
|
+
} : void 0
|
|
966
1005
|
});
|
|
967
1006
|
logger.newline();
|
|
968
1007
|
logger.success(`Angemeldet als ${result.user?.email || "Unknown"}`);
|
|
969
1008
|
logger.newline();
|
|
970
1009
|
logger.info("Ihre Anmeldedaten wurden gespeichert.");
|
|
971
|
-
logger.info(
|
|
1010
|
+
logger.info(
|
|
1011
|
+
"F\xFChren Sie `turbo whoami` aus, um Ihre Authentifizierung zu \xFCberpr\xFCfen."
|
|
1012
|
+
);
|
|
972
1013
|
});
|
|
973
1014
|
var logoutCommand = new Command("logout").description("Remove stored authentication").action(() => {
|
|
974
1015
|
configService.clearToken();
|
|
@@ -983,13 +1024,16 @@ var whoamiCommand = new Command("whoami").description("Show current authenticate
|
|
|
983
1024
|
}
|
|
984
1025
|
const token = configService.getToken();
|
|
985
1026
|
if (token?.startsWith("turbo_cli_")) {
|
|
986
|
-
const { data: data2, error: error2 } = await withSpinner(
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1027
|
+
const { data: data2, error: error2 } = await withSpinner(
|
|
1028
|
+
"Fetching user info...",
|
|
1029
|
+
async () => {
|
|
1030
|
+
const result = await authService.validateToken(token);
|
|
1031
|
+
if (result.valid && result.user) {
|
|
1032
|
+
return { data: result.user, error: void 0 };
|
|
1033
|
+
}
|
|
1034
|
+
return { data: void 0, error: "Invalid token" };
|
|
990
1035
|
}
|
|
991
|
-
|
|
992
|
-
});
|
|
1036
|
+
);
|
|
993
1037
|
if (error2 || !data2) {
|
|
994
1038
|
logger.error(`Failed to fetch user info: ${error2 || "Unknown error"}`);
|
|
995
1039
|
addJsonData({ authenticated: false, error: error2 || "Unknown error" });
|
|
@@ -1058,7 +1102,9 @@ async function requireAuth() {
|
|
|
1058
1102
|
if (token?.startsWith("turbo_cli_")) {
|
|
1059
1103
|
const validation = await authService.checkCliToken();
|
|
1060
1104
|
if (!validation.valid) {
|
|
1061
|
-
logger.error(
|
|
1105
|
+
logger.error(
|
|
1106
|
+
"Session expired or revoked. Please run `turbo login` to re-authenticate."
|
|
1107
|
+
);
|
|
1062
1108
|
configService.clearToken();
|
|
1063
1109
|
process.exit(10 /* AUTH_ERROR */);
|
|
1064
1110
|
}
|
|
@@ -1179,7 +1225,9 @@ var statusCommand = new Command2("status").description("Show deployment status")
|
|
|
1179
1225
|
});
|
|
1180
1226
|
}
|
|
1181
1227
|
});
|
|
1182
|
-
var configCommand = new Command2("config").description(
|
|
1228
|
+
var configCommand = new Command2("config").description(
|
|
1229
|
+
"Manage configuration"
|
|
1230
|
+
);
|
|
1183
1231
|
configCommand.command("show", { isDefault: true }).description("Show current configuration").action(() => {
|
|
1184
1232
|
const config = configService.getAll();
|
|
1185
1233
|
addJsonData({ config });
|
|
@@ -1281,7 +1329,13 @@ var AI_TOOLS = {
|
|
|
1281
1329
|
claude: {
|
|
1282
1330
|
name: "Claude Code",
|
|
1283
1331
|
command: "claude",
|
|
1284
|
-
args: [
|
|
1332
|
+
args: [
|
|
1333
|
+
"-p",
|
|
1334
|
+
"--dangerously-skip-permissions",
|
|
1335
|
+
"--verbose",
|
|
1336
|
+
"--output-format",
|
|
1337
|
+
"stream-json"
|
|
1338
|
+
]
|
|
1285
1339
|
},
|
|
1286
1340
|
codex: {
|
|
1287
1341
|
name: "OpenAI Codex",
|
|
@@ -1312,7 +1366,9 @@ var aiToolsService = {
|
|
|
1312
1366
|
async selectTool() {
|
|
1313
1367
|
const available = this.getAvailableTools();
|
|
1314
1368
|
if (available.length === 0) {
|
|
1315
|
-
logger.error(
|
|
1369
|
+
logger.error(
|
|
1370
|
+
"Kein AI-Tool gefunden. Bitte installieren Sie Claude Code oder OpenAI Codex."
|
|
1371
|
+
);
|
|
1316
1372
|
logger.newline();
|
|
1317
1373
|
logger.info(" Claude Code: https://claude.ai/code");
|
|
1318
1374
|
logger.info(" OpenAI Codex: https://openai.com/codex");
|
|
@@ -1345,9 +1401,15 @@ var aiToolsService = {
|
|
|
1345
1401
|
console.log(chalk4.cyan("[DEBUG] === AI Tool Debug Mode ==="));
|
|
1346
1402
|
console.log(chalk4.dim(`[DEBUG] Tool: ${config.name}`));
|
|
1347
1403
|
console.log(chalk4.dim(`[DEBUG] Command: ${config.command}`));
|
|
1348
|
-
console.log(
|
|
1404
|
+
console.log(
|
|
1405
|
+
chalk4.dim(
|
|
1406
|
+
`[DEBUG] Args: ${config.args.join(" ")} + prompt (${prompt.length} chars)`
|
|
1407
|
+
)
|
|
1408
|
+
);
|
|
1349
1409
|
console.log(chalk4.dim(`[DEBUG] CWD: ${process.cwd()}`));
|
|
1350
|
-
console.log(
|
|
1410
|
+
console.log(
|
|
1411
|
+
chalk4.dim(`[DEBUG] Prompt preview: ${prompt.substring(0, 100)}...`)
|
|
1412
|
+
);
|
|
1351
1413
|
console.log("");
|
|
1352
1414
|
}
|
|
1353
1415
|
const spinner = ora2({
|
|
@@ -1373,7 +1435,9 @@ var aiToolsService = {
|
|
|
1373
1435
|
child.stdin?.end();
|
|
1374
1436
|
if (verbose) {
|
|
1375
1437
|
spinner.stop();
|
|
1376
|
-
console.log(
|
|
1438
|
+
console.log(
|
|
1439
|
+
chalk4.dim(`[DEBUG] Child process spawned, PID: ${child.pid}`)
|
|
1440
|
+
);
|
|
1377
1441
|
console.log(chalk4.dim(`[DEBUG] Waiting for output...`));
|
|
1378
1442
|
spinner.start();
|
|
1379
1443
|
}
|
|
@@ -1408,7 +1472,11 @@ var aiToolsService = {
|
|
|
1408
1472
|
spinner.text = action;
|
|
1409
1473
|
if (verbose) {
|
|
1410
1474
|
spinner.stop();
|
|
1411
|
-
console.log(
|
|
1475
|
+
console.log(
|
|
1476
|
+
chalk4.dim(
|
|
1477
|
+
`[TOOL] ${block.name}: ${JSON.stringify(block.input).substring(0, 200)}`
|
|
1478
|
+
)
|
|
1479
|
+
);
|
|
1412
1480
|
spinner.start();
|
|
1413
1481
|
}
|
|
1414
1482
|
}
|
|
@@ -1447,9 +1515,13 @@ var aiToolsService = {
|
|
|
1447
1515
|
if (verbose) {
|
|
1448
1516
|
spinner.stop();
|
|
1449
1517
|
console.log(chalk4.dim(`[DEBUG] Process exited with code: ${code}`));
|
|
1450
|
-
console.log(
|
|
1518
|
+
console.log(
|
|
1519
|
+
chalk4.dim(`[DEBUG] Total output length: ${rawOutput.length} chars`)
|
|
1520
|
+
);
|
|
1451
1521
|
if (rawOutput.length === 0) {
|
|
1452
|
-
console.log(
|
|
1522
|
+
console.log(
|
|
1523
|
+
chalk4.yellow(`[DEBUG] No output received from ${config.name}!`)
|
|
1524
|
+
);
|
|
1453
1525
|
}
|
|
1454
1526
|
}
|
|
1455
1527
|
if (code === 0) {
|
|
@@ -1471,7 +1543,9 @@ var aiToolsService = {
|
|
|
1471
1543
|
if (verbose) {
|
|
1472
1544
|
console.log(chalk4.red(`[ERROR] Spawn error: ${err.message}`));
|
|
1473
1545
|
}
|
|
1474
|
-
spinner.fail(
|
|
1546
|
+
spinner.fail(
|
|
1547
|
+
`Fehler beim Ausf\xFChren von ${config.name}: ${err.message}`
|
|
1548
|
+
);
|
|
1475
1549
|
resolve4(false);
|
|
1476
1550
|
});
|
|
1477
1551
|
});
|
|
@@ -1512,7 +1586,12 @@ async function detectProjectConfig() {
|
|
|
1512
1586
|
hasGitHubPipeline: false,
|
|
1513
1587
|
hasTurboOpsInPipeline: false
|
|
1514
1588
|
};
|
|
1515
|
-
const composePaths = [
|
|
1589
|
+
const composePaths = [
|
|
1590
|
+
"docker-compose.yml",
|
|
1591
|
+
"docker-compose.yaml",
|
|
1592
|
+
"compose.yml",
|
|
1593
|
+
"compose.yaml"
|
|
1594
|
+
];
|
|
1516
1595
|
for (const composePath of composePaths) {
|
|
1517
1596
|
try {
|
|
1518
1597
|
await fs2.access(path2.join(cwd, composePath));
|
|
@@ -1533,7 +1612,9 @@ async function detectProjectConfig() {
|
|
|
1533
1612
|
const githubPath = path2.join(cwd, ".github", "workflows");
|
|
1534
1613
|
try {
|
|
1535
1614
|
const files = await fs2.readdir(githubPath);
|
|
1536
|
-
const ymlFiles = files.filter(
|
|
1615
|
+
const ymlFiles = files.filter(
|
|
1616
|
+
(f) => f.endsWith(".yml") || f.endsWith(".yaml")
|
|
1617
|
+
);
|
|
1537
1618
|
if (ymlFiles.length > 0) {
|
|
1538
1619
|
result.hasGitHubPipeline = true;
|
|
1539
1620
|
result.pipelinePath = `.github/workflows/${ymlFiles[0]}`;
|
|
@@ -1573,7 +1654,9 @@ var initCommand = new Command3("init").description("Initialize TurboOps project
|
|
|
1573
1654
|
}
|
|
1574
1655
|
const result = await authService.browserLogin();
|
|
1575
1656
|
if (!result.success) {
|
|
1576
|
-
logger.error(
|
|
1657
|
+
logger.error(
|
|
1658
|
+
`Anmeldung fehlgeschlagen: ${result.error || "Unbekannter Fehler"}`
|
|
1659
|
+
);
|
|
1577
1660
|
addJsonData({
|
|
1578
1661
|
error: result.error || "Unknown error",
|
|
1579
1662
|
initialized: false,
|
|
@@ -1601,7 +1684,9 @@ var initCommand = new Command3("init").description("Initialize TurboOps project
|
|
|
1601
1684
|
}
|
|
1602
1685
|
const result = await authService.browserLogin();
|
|
1603
1686
|
if (!result.success) {
|
|
1604
|
-
logger.error(
|
|
1687
|
+
logger.error(
|
|
1688
|
+
`Anmeldung fehlgeschlagen: ${result.error || "Unbekannter Fehler"}`
|
|
1689
|
+
);
|
|
1605
1690
|
addJsonData({
|
|
1606
1691
|
error: result.error || "Unknown error",
|
|
1607
1692
|
initialized: false,
|
|
@@ -1740,13 +1825,23 @@ async function createNewProject(slug, verbose = false) {
|
|
|
1740
1825
|
if (newProject.projectToken) {
|
|
1741
1826
|
logger.newline();
|
|
1742
1827
|
logger.header("CI/CD Projekt-Token");
|
|
1743
|
-
console.log(
|
|
1828
|
+
console.log(
|
|
1829
|
+
chalk5.yellow.bold(
|
|
1830
|
+
" WICHTIG: Diesen Token jetzt kopieren - er wird nur einmal angezeigt!"
|
|
1831
|
+
)
|
|
1832
|
+
);
|
|
1744
1833
|
logger.newline();
|
|
1745
|
-
console.log(
|
|
1834
|
+
console.log(
|
|
1835
|
+
chalk5.cyan(" Token: ") + chalk5.green.bold(newProject.projectToken)
|
|
1836
|
+
);
|
|
1746
1837
|
logger.newline();
|
|
1747
1838
|
console.log(chalk5.dim(" Diesen Token als CI/CD Secret hinterlegen:"));
|
|
1748
|
-
console.log(
|
|
1749
|
-
|
|
1839
|
+
console.log(
|
|
1840
|
+
chalk5.dim(" \u2192 GitLab: Settings \u2192 CI/CD \u2192 Variables \u2192 TURBOOPS_TOKEN")
|
|
1841
|
+
);
|
|
1842
|
+
console.log(
|
|
1843
|
+
chalk5.dim(" \u2192 GitHub: Settings \u2192 Secrets \u2192 Actions \u2192 TURBOOPS_TOKEN")
|
|
1844
|
+
);
|
|
1750
1845
|
logger.newline();
|
|
1751
1846
|
}
|
|
1752
1847
|
configService.setProject(newProject.slug);
|
|
@@ -1850,7 +1945,9 @@ async function createPipeline(projectId) {
|
|
|
1850
1945
|
() => apiClient.generatePipeline(projectId, pipelineDetails.pipelineType)
|
|
1851
1946
|
);
|
|
1852
1947
|
if (error || !pipeline) {
|
|
1853
|
-
logger.error(
|
|
1948
|
+
logger.error(
|
|
1949
|
+
`Pipeline konnte nicht generiert werden: ${error || "Unbekannter Fehler"}`
|
|
1950
|
+
);
|
|
1854
1951
|
return;
|
|
1855
1952
|
}
|
|
1856
1953
|
const fs4 = await import("fs/promises");
|
|
@@ -1873,7 +1970,10 @@ async function createPipeline(projectId) {
|
|
|
1873
1970
|
}
|
|
1874
1971
|
});
|
|
1875
1972
|
logger.newline();
|
|
1876
|
-
const { data: secrets } = await apiClient.getPipelineSecrets(
|
|
1973
|
+
const { data: secrets } = await apiClient.getPipelineSecrets(
|
|
1974
|
+
projectId,
|
|
1975
|
+
pipelineDetails.pipelineType
|
|
1976
|
+
);
|
|
1877
1977
|
if (secrets && secrets.length > 0) {
|
|
1878
1978
|
logger.header("Erforderliche CI/CD Secrets");
|
|
1879
1979
|
for (const secret of secrets) {
|
|
@@ -1940,12 +2040,18 @@ async function showFinalSummary(project) {
|
|
|
1940
2040
|
console.log(chalk5.cyan(" 2. Token als CI/CD Secret hinterlegen:"));
|
|
1941
2041
|
console.log(chalk5.dim(" GitLab:"));
|
|
1942
2042
|
console.log(` ${chalk5.dim("\u2192")} Settings \u2192 CI/CD \u2192 Variables`);
|
|
1943
|
-
console.log(
|
|
2043
|
+
console.log(
|
|
2044
|
+
` ${chalk5.dim("\u2192")} Variable: ${chalk5.bold("TURBOOPS_TOKEN")} = <dein-token>`
|
|
2045
|
+
);
|
|
1944
2046
|
console.log(` ${chalk5.dim("\u2192")} Flags: Protected, Masked`);
|
|
1945
2047
|
logger.newline();
|
|
1946
2048
|
console.log(chalk5.dim(" GitHub:"));
|
|
1947
|
-
console.log(
|
|
1948
|
-
|
|
2049
|
+
console.log(
|
|
2050
|
+
` ${chalk5.dim("\u2192")} Settings \u2192 Secrets and variables \u2192 Actions`
|
|
2051
|
+
);
|
|
2052
|
+
console.log(
|
|
2053
|
+
` ${chalk5.dim("\u2192")} New repository secret: ${chalk5.bold("TURBOOPS_TOKEN")}`
|
|
2054
|
+
);
|
|
1949
2055
|
logger.newline();
|
|
1950
2056
|
logger.header("N\xE4chste Schritte");
|
|
1951
2057
|
logger.list([
|
|
@@ -1977,7 +2083,10 @@ async function offerAiAssistance(projectSlug, projectId, verbose = false) {
|
|
|
1977
2083
|
message: "Docker-Setup:",
|
|
1978
2084
|
choices: [
|
|
1979
2085
|
{ title: "Bestehende docker-compose.yml verwenden", value: "keep" },
|
|
1980
|
-
{
|
|
2086
|
+
{
|
|
2087
|
+
title: "Neu erstellen mit AI (\xFCberschreibt bestehende)",
|
|
2088
|
+
value: "create"
|
|
2089
|
+
}
|
|
1981
2090
|
],
|
|
1982
2091
|
initial: 0
|
|
1983
2092
|
});
|
|
@@ -1986,7 +2095,9 @@ async function offerAiAssistance(projectSlug, projectId, verbose = false) {
|
|
|
1986
2095
|
}
|
|
1987
2096
|
} else {
|
|
1988
2097
|
logger.warning("Keine docker-compose.yml gefunden.");
|
|
1989
|
-
logger.info(
|
|
2098
|
+
logger.info(
|
|
2099
|
+
"TurboOps Deployment ben\xF6tigt eine docker-compose.yml auf Root-Ebene."
|
|
2100
|
+
);
|
|
1990
2101
|
const { shouldCreateDocker } = await prompts2({
|
|
1991
2102
|
type: "confirm",
|
|
1992
2103
|
name: "shouldCreateDocker",
|
|
@@ -2019,12 +2130,20 @@ async function offerAiAssistance(projectSlug, projectId, verbose = false) {
|
|
|
2019
2130
|
message: "Pipeline-Setup:",
|
|
2020
2131
|
choices: [
|
|
2021
2132
|
{ title: "Bestehende Pipeline behalten", value: "keep" },
|
|
2022
|
-
{
|
|
2133
|
+
{
|
|
2134
|
+
title: "Mit AI neu integrieren (\xFCberschreibt TurboOps-Teil)",
|
|
2135
|
+
value: "integrate"
|
|
2136
|
+
}
|
|
2023
2137
|
],
|
|
2024
2138
|
initial: 0
|
|
2025
2139
|
});
|
|
2026
2140
|
if (pipelineAction === "integrate") {
|
|
2027
|
-
await integratePipelineWithAI(
|
|
2141
|
+
await integratePipelineWithAI(
|
|
2142
|
+
detection,
|
|
2143
|
+
projectSlug,
|
|
2144
|
+
verbose,
|
|
2145
|
+
projectId
|
|
2146
|
+
);
|
|
2028
2147
|
}
|
|
2029
2148
|
} else {
|
|
2030
2149
|
logger.info(`${pipelineType} Pipeline gefunden (ohne TurboOps).`);
|
|
@@ -2035,7 +2154,12 @@ async function offerAiAssistance(projectSlug, projectId, verbose = false) {
|
|
|
2035
2154
|
initial: true
|
|
2036
2155
|
});
|
|
2037
2156
|
if (shouldIntegrate) {
|
|
2038
|
-
await integratePipelineWithAI(
|
|
2157
|
+
await integratePipelineWithAI(
|
|
2158
|
+
detection,
|
|
2159
|
+
projectSlug,
|
|
2160
|
+
verbose,
|
|
2161
|
+
projectId
|
|
2162
|
+
);
|
|
2039
2163
|
}
|
|
2040
2164
|
}
|
|
2041
2165
|
} else {
|
|
@@ -2221,7 +2345,11 @@ async function detectAndUploadCompose(projectSlug) {
|
|
|
2221
2345
|
}
|
|
2222
2346
|
try {
|
|
2223
2347
|
const content = readFileSync2(fullPath, "utf-8");
|
|
2224
|
-
const { error } = await apiClient.uploadCompose(
|
|
2348
|
+
const { error } = await apiClient.uploadCompose(
|
|
2349
|
+
project.id,
|
|
2350
|
+
content,
|
|
2351
|
+
"Uploaded via turbo init"
|
|
2352
|
+
);
|
|
2225
2353
|
if (error) {
|
|
2226
2354
|
logger.warning(`Compose-Upload fehlgeschlagen: ${error}`);
|
|
2227
2355
|
} else {
|
|
@@ -2243,7 +2371,9 @@ async function integratePipelineWithAI(detection, projectSlug, verbose = false,
|
|
|
2243
2371
|
() => apiClient.generatePipeline(projectId, pipelineType)
|
|
2244
2372
|
);
|
|
2245
2373
|
if (error || !pipelineTemplate) {
|
|
2246
|
-
logger.warning(
|
|
2374
|
+
logger.warning(
|
|
2375
|
+
`Pipeline-Template konnte nicht geladen werden: ${error || "Unbekannter Fehler"}`
|
|
2376
|
+
);
|
|
2247
2377
|
logger.info("Claude Code wird ein generisches Template verwenden.");
|
|
2248
2378
|
} else {
|
|
2249
2379
|
templateContent = pipelineTemplate.content;
|
|
@@ -2362,7 +2492,9 @@ Erstelle die Datei "${pipelineFile}".`;
|
|
|
2362
2492
|
});
|
|
2363
2493
|
}
|
|
2364
2494
|
logger.newline();
|
|
2365
|
-
logger.info(
|
|
2495
|
+
logger.info(
|
|
2496
|
+
"Vergessen Sie nicht, das CI/CD Secret TURBOOPS_TOKEN zu konfigurieren."
|
|
2497
|
+
);
|
|
2366
2498
|
}
|
|
2367
2499
|
}
|
|
2368
2500
|
|
|
@@ -2401,11 +2533,13 @@ function detectCiEnvironment() {
|
|
|
2401
2533
|
if (process.env.JENKINS_URL) return "Jenkins";
|
|
2402
2534
|
return null;
|
|
2403
2535
|
}
|
|
2404
|
-
var deployCommand = new Command4("deploy").description("Trigger a deployment (for CI/CD pipelines)").argument("<environment>", "Environment slug (e.g., production, staging)").option(
|
|
2405
|
-
"--
|
|
2406
|
-
"
|
|
2407
|
-
|
|
2408
|
-
|
|
2536
|
+
var deployCommand = new Command4("deploy").description("Trigger a deployment (for CI/CD pipelines)").argument("<environment>", "Environment slug (e.g., production, staging)").option(
|
|
2537
|
+
"-i, --image <tag>",
|
|
2538
|
+
"Docker image tag to deploy (auto-detected in CI/CD)"
|
|
2539
|
+
).option(
|
|
2540
|
+
"-c, --compose <path>",
|
|
2541
|
+
"Path to docker-compose file (auto-detected by default)"
|
|
2542
|
+
).option("-w, --wait", "Wait for deployment to complete", true).option("--no-wait", "Do not wait for deployment to complete").option("--timeout <ms>", "Timeout in milliseconds when waiting", "600000").action(async (environment, options) => {
|
|
2409
2543
|
const { project, environment: env } = await getCommandContextWithEnvironment(environment);
|
|
2410
2544
|
logger.header(`Deploying: ${project.name} \u2192 ${env.name}`);
|
|
2411
2545
|
let imageTag = options.image;
|
|
@@ -2428,10 +2562,15 @@ var deployCommand = new Command4("deploy").description("Trigger a deployment (fo
|
|
|
2428
2562
|
composeFileContent = readFileSync3(composePath, "utf-8");
|
|
2429
2563
|
logger.info(`Using compose file: ${composePath}`);
|
|
2430
2564
|
} else if (project.detectedConfig?.composePath) {
|
|
2431
|
-
const composePath = resolve2(
|
|
2565
|
+
const composePath = resolve2(
|
|
2566
|
+
process.cwd(),
|
|
2567
|
+
project.detectedConfig.composePath
|
|
2568
|
+
);
|
|
2432
2569
|
if (existsSync4(composePath)) {
|
|
2433
2570
|
composeFileContent = readFileSync3(composePath, "utf-8");
|
|
2434
|
-
logger.info(
|
|
2571
|
+
logger.info(
|
|
2572
|
+
`Using project compose file: ${project.detectedConfig.composePath}`
|
|
2573
|
+
);
|
|
2435
2574
|
}
|
|
2436
2575
|
}
|
|
2437
2576
|
logger.info("Triggering deployment...");
|
|
@@ -2537,7 +2676,9 @@ import prompts3 from "prompts";
|
|
|
2537
2676
|
import chalk7 from "chalk";
|
|
2538
2677
|
import * as fs3 from "fs/promises";
|
|
2539
2678
|
import * as path3 from "path";
|
|
2540
|
-
var pipelineCommand = new Command5("pipeline").description(
|
|
2679
|
+
var pipelineCommand = new Command5("pipeline").description(
|
|
2680
|
+
"Manage CI/CD pipeline configuration"
|
|
2681
|
+
);
|
|
2541
2682
|
pipelineCommand.command("generate").description("Generate CI/CD pipeline configuration").option("-t, --type <type>", "Pipeline type (gitlab, github)").option("-f, --force", "Overwrite existing pipeline file").option("-o, --output <path>", "Custom output path").action(async (options) => {
|
|
2542
2683
|
const { project } = await getCommandContext();
|
|
2543
2684
|
logger.header("CI/CD Pipeline generieren");
|
|
@@ -2577,14 +2718,21 @@ pipelineCommand.command("generate").description("Generate CI/CD pipeline configu
|
|
|
2577
2718
|
pipelineType = selectedType;
|
|
2578
2719
|
}
|
|
2579
2720
|
if (!["gitlab", "github"].includes(pipelineType)) {
|
|
2580
|
-
logger.error(
|
|
2721
|
+
logger.error(
|
|
2722
|
+
`Ung\xFCltiger Pipeline-Typ: ${pipelineType}. Erlaubt: gitlab, github`
|
|
2723
|
+
);
|
|
2581
2724
|
process.exit(14 /* VALIDATION_ERROR */);
|
|
2582
2725
|
}
|
|
2583
2726
|
let outputPath;
|
|
2584
2727
|
if (options.output) {
|
|
2585
2728
|
outputPath = path3.resolve(options.output);
|
|
2586
2729
|
} else if (pipelineType === "github") {
|
|
2587
|
-
outputPath = path3.join(
|
|
2730
|
+
outputPath = path3.join(
|
|
2731
|
+
process.cwd(),
|
|
2732
|
+
".github",
|
|
2733
|
+
"workflows",
|
|
2734
|
+
"deploy.yml"
|
|
2735
|
+
);
|
|
2588
2736
|
} else {
|
|
2589
2737
|
outputPath = path3.join(process.cwd(), ".gitlab-ci.yml");
|
|
2590
2738
|
}
|
|
@@ -2599,7 +2747,10 @@ pipelineCommand.command("generate").description("Generate CI/CD pipeline configu
|
|
|
2599
2747
|
name: "action",
|
|
2600
2748
|
message: `${path3.basename(outputPath)} existiert bereits ohne TurboOps.`,
|
|
2601
2749
|
choices: [
|
|
2602
|
-
{
|
|
2750
|
+
{
|
|
2751
|
+
title: "Mit AI integrieren (empfohlen)",
|
|
2752
|
+
value: "ai-integrate"
|
|
2753
|
+
},
|
|
2603
2754
|
{ title: "\xDCberschreiben", value: "overwrite" },
|
|
2604
2755
|
{ title: "Abbrechen", value: "cancel" }
|
|
2605
2756
|
]
|
|
@@ -2610,7 +2761,11 @@ pipelineCommand.command("generate").description("Generate CI/CD pipeline configu
|
|
|
2610
2761
|
return;
|
|
2611
2762
|
}
|
|
2612
2763
|
if (action === "ai-integrate") {
|
|
2613
|
-
await integratePipelineWithAI2(
|
|
2764
|
+
await integratePipelineWithAI2(
|
|
2765
|
+
pipelineType,
|
|
2766
|
+
project.slug,
|
|
2767
|
+
outputPath
|
|
2768
|
+
);
|
|
2614
2769
|
return;
|
|
2615
2770
|
}
|
|
2616
2771
|
} else {
|
|
@@ -2634,7 +2789,9 @@ pipelineCommand.command("generate").description("Generate CI/CD pipeline configu
|
|
|
2634
2789
|
() => apiClient.generatePipeline(project.id, pipelineType)
|
|
2635
2790
|
);
|
|
2636
2791
|
if (error || !pipeline) {
|
|
2637
|
-
logger.error(
|
|
2792
|
+
logger.error(
|
|
2793
|
+
`Pipeline konnte nicht generiert werden: ${error || "Unbekannter Fehler"}`
|
|
2794
|
+
);
|
|
2638
2795
|
addJsonData({ error: error || "Unknown error", generated: false });
|
|
2639
2796
|
process.exit(13 /* API_ERROR */);
|
|
2640
2797
|
}
|
|
@@ -2642,7 +2799,9 @@ pipelineCommand.command("generate").description("Generate CI/CD pipeline configu
|
|
|
2642
2799
|
await fs3.mkdir(outputDir, { recursive: true });
|
|
2643
2800
|
try {
|
|
2644
2801
|
await fs3.writeFile(outputPath, pipeline.content, "utf-8");
|
|
2645
|
-
logger.success(
|
|
2802
|
+
logger.success(
|
|
2803
|
+
`${path3.relative(process.cwd(), outputPath)} wurde erstellt!`
|
|
2804
|
+
);
|
|
2646
2805
|
addJsonData({
|
|
2647
2806
|
filename: path3.relative(process.cwd(), outputPath),
|
|
2648
2807
|
generated: true,
|
|
@@ -2662,14 +2821,21 @@ pipelineCommand.command("secrets").description("Show required CI/CD secrets").op
|
|
|
2662
2821
|
const { project } = await getCommandContext();
|
|
2663
2822
|
const pipelineType = options.type;
|
|
2664
2823
|
if (!["gitlab", "github"].includes(pipelineType)) {
|
|
2665
|
-
logger.error(
|
|
2824
|
+
logger.error(
|
|
2825
|
+
`Ung\xFCltiger Pipeline-Typ: ${pipelineType}. Erlaubt: gitlab, github`
|
|
2826
|
+
);
|
|
2666
2827
|
process.exit(14 /* VALIDATION_ERROR */);
|
|
2667
2828
|
}
|
|
2668
|
-
logger.header(
|
|
2829
|
+
logger.header(
|
|
2830
|
+
`CI/CD Secrets f\xFCr ${pipelineType === "gitlab" ? "GitLab" : "GitHub"}`
|
|
2831
|
+
);
|
|
2669
2832
|
await showSecrets(project.id, pipelineType);
|
|
2670
2833
|
});
|
|
2671
2834
|
async function showSecrets(projectId, pipelineType) {
|
|
2672
|
-
const { data: secrets, error } = await apiClient.getPipelineSecrets(
|
|
2835
|
+
const { data: secrets, error } = await apiClient.getPipelineSecrets(
|
|
2836
|
+
projectId,
|
|
2837
|
+
pipelineType
|
|
2838
|
+
);
|
|
2673
2839
|
if (error) {
|
|
2674
2840
|
logger.error(`Fehler beim Laden der Secrets: ${error}`);
|
|
2675
2841
|
return;
|
|
@@ -2889,7 +3055,9 @@ Modifiziere die Datei "${pipelinePath}" entsprechend.`;
|
|
|
2889
3055
|
if (success) {
|
|
2890
3056
|
logger.success("Pipeline wurde mit AI aktualisiert!");
|
|
2891
3057
|
logger.newline();
|
|
2892
|
-
logger.info(
|
|
3058
|
+
logger.info(
|
|
3059
|
+
"Vergessen Sie nicht, das CI/CD Secret TURBOOPS_TOKEN zu konfigurieren."
|
|
3060
|
+
);
|
|
2893
3061
|
addJsonData({ generated: true, method: "ai-integration" });
|
|
2894
3062
|
} else {
|
|
2895
3063
|
addJsonData({ generated: false, reason: "ai_failed" });
|
|
@@ -3035,7 +3203,9 @@ WICHTIG - TURBOOPS DEPLOYMENT:
|
|
|
3035
3203
|
- Verhindert Port-Konflikte bei mehreren Projekten auf einem Server
|
|
3036
3204
|
|
|
3037
3205
|
Erstelle alle notwendigen Dateien.`;
|
|
3038
|
-
var dockerCommand = new Command6("docker").description(
|
|
3206
|
+
var dockerCommand = new Command6("docker").description(
|
|
3207
|
+
"Manage Docker configuration"
|
|
3208
|
+
);
|
|
3039
3209
|
dockerCommand.command("generate").description("Docker-Setup (docker-compose + Dockerfiles) mit AI erstellen").option("-f, --force", "Bestehende Dateien \xFCberschreiben").action(async (options) => {
|
|
3040
3210
|
logger.header("Docker-Setup generieren");
|
|
3041
3211
|
const detection = await detectProjectConfig();
|
|
@@ -3058,7 +3228,10 @@ dockerCommand.command("generate").description("Docker-Setup (docker-compose + Do
|
|
|
3058
3228
|
addJsonData({ generated: false, reason: "no_ai_tool" });
|
|
3059
3229
|
return;
|
|
3060
3230
|
}
|
|
3061
|
-
const success = await aiToolsService.runWithPrompt(
|
|
3231
|
+
const success = await aiToolsService.runWithPrompt(
|
|
3232
|
+
tool,
|
|
3233
|
+
DOCKER_SETUP_PROMPT
|
|
3234
|
+
);
|
|
3062
3235
|
if (success) {
|
|
3063
3236
|
logger.success("Docker-Setup wurde erstellt!");
|
|
3064
3237
|
logger.newline();
|
|
@@ -3070,7 +3243,9 @@ dockerCommand.command("generate").description("Docker-Setup (docker-compose + Do
|
|
|
3070
3243
|
]);
|
|
3071
3244
|
addJsonData({ generated: true });
|
|
3072
3245
|
} else {
|
|
3073
|
-
logger.warning(
|
|
3246
|
+
logger.warning(
|
|
3247
|
+
"Docker-Setup Generierung wurde abgebrochen oder ist fehlgeschlagen."
|
|
3248
|
+
);
|
|
3074
3249
|
addJsonData({ generated: false, reason: "ai_failed" });
|
|
3075
3250
|
}
|
|
3076
3251
|
});
|
package/package.json
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turboops/cli",
|
|
3
|
-
"version": "1.0.0-dev.
|
|
3
|
+
"version": "1.0.0-dev.725",
|
|
4
4
|
"description": "TurboCLI - Command line interface for TurboOps deployments",
|
|
5
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"deployment",
|
|
8
|
+
"docker",
|
|
9
|
+
"swarm",
|
|
10
|
+
"turboops"
|
|
11
|
+
],
|
|
6
12
|
"license": "MIT",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
13
|
+
"author": "lenne.tech GmbH",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/lenne-tech/turboops.git",
|
|
17
|
+
"directory": "projects/cli"
|
|
18
|
+
},
|
|
9
19
|
"bin": {
|
|
10
20
|
"turbo": "./dist/index.js"
|
|
11
21
|
},
|
|
12
22
|
"files": [
|
|
13
23
|
"dist"
|
|
14
24
|
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
15
27
|
"dependencies": {
|
|
16
28
|
"chalk": "^5.3.0",
|
|
17
29
|
"commander": "^12.1.0",
|
|
@@ -32,18 +44,6 @@
|
|
|
32
44
|
"engines": {
|
|
33
45
|
"node": ">=18.0.0"
|
|
34
46
|
},
|
|
35
|
-
"keywords": [
|
|
36
|
-
"turboops",
|
|
37
|
-
"cli",
|
|
38
|
-
"deployment",
|
|
39
|
-
"docker",
|
|
40
|
-
"swarm"
|
|
41
|
-
],
|
|
42
|
-
"repository": {
|
|
43
|
-
"type": "git",
|
|
44
|
-
"url": "https://github.com/lenne-tech/turboops.git",
|
|
45
|
-
"directory": "projects/cli"
|
|
46
|
-
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
49
49
|
"build:local": "tsup src/index.ts --format esm --dts --clean --define.__TURBOOPS_ENV__=\"'local'\"",
|