@cruxgarden/cli 0.0.5 → 0.0.7
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/bin/crux.js +4 -4
- package/lib/commands.js +119 -29
- package/package.json +5 -2
package/bin/crux.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
logsNursery,
|
|
13
13
|
cleanNursery,
|
|
14
14
|
purgeNursery,
|
|
15
|
-
|
|
15
|
+
updateNursery,
|
|
16
16
|
resetNursery,
|
|
17
17
|
connectNurseryDb,
|
|
18
18
|
connectNurseryRedis,
|
|
@@ -80,9 +80,9 @@ nursery
|
|
|
80
80
|
.action(purgeNursery);
|
|
81
81
|
|
|
82
82
|
nursery
|
|
83
|
-
.command("
|
|
84
|
-
.description("
|
|
85
|
-
.action(
|
|
83
|
+
.command("update")
|
|
84
|
+
.description("Download the latest API image from ghcr.io")
|
|
85
|
+
.action(updateNursery);
|
|
86
86
|
|
|
87
87
|
nursery
|
|
88
88
|
.command("reset")
|
package/lib/commands.js
CHANGED
|
@@ -16,23 +16,40 @@ export function showBanner() {
|
|
|
16
16
|
// Check if banner was already shown in this shell session
|
|
17
17
|
if (process.env.CRUX_BANNER_SHOWN) return;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
██║
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
██║
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
const bannerLines = [
|
|
20
|
+
" ██████╗██████╗ ██╗ ██╗██╗ ██╗",
|
|
21
|
+
"██╔════╝██╔══██╗██║ ██║╚██╗██╔╝",
|
|
22
|
+
"██║ ██████╔╝██║ ██║ ╚███╔╝",
|
|
23
|
+
"██║ ██╔══██╗██║ ██║ ██╔██╗",
|
|
24
|
+
"╚██████╗██║ ██║╚██████╔╝██╔╝ ██╗",
|
|
25
|
+
" ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝",
|
|
26
|
+
"",
|
|
27
|
+
" ██████╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ██╗",
|
|
28
|
+
"██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██╔════╝████╗ ██║",
|
|
29
|
+
"██║ ███╗███████║██████╔╝██║ ██║█████╗ ██╔██╗ ██║",
|
|
30
|
+
"██║ ██║██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╗██║",
|
|
31
|
+
"╚██████╔╝██║ ██║██║ ██║██████╔╝███████╗██║ ╚████║",
|
|
32
|
+
" ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═══╝"
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
// Randomly pick a color from the CLI color palette
|
|
36
|
+
const colors = [
|
|
37
|
+
chalk.hex(SUCCESS_GREEN), // Green
|
|
38
|
+
chalk.cyan, // Cyan
|
|
39
|
+
chalk.yellow, // Yellow
|
|
40
|
+
chalk.magenta, // Magenta
|
|
41
|
+
chalk.blue, // Blue
|
|
42
|
+
chalk.red, // Red
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const randomColor = colors[Math.floor(Math.random() * colors.length)];
|
|
46
|
+
|
|
47
|
+
// Print each line with the same random color
|
|
48
|
+
console.log();
|
|
49
|
+
bannerLines.forEach((line) => {
|
|
50
|
+
console.log(randomColor(line));
|
|
51
|
+
});
|
|
52
|
+
|
|
36
53
|
console.log(chalk.gray(" Nursery Environment - Demo & Trial\n"));
|
|
37
54
|
|
|
38
55
|
// Set environment variable so it persists for this shell session
|
|
@@ -89,7 +106,8 @@ function runCommandAsync(command, options = {}) {
|
|
|
89
106
|
);
|
|
90
107
|
reject(error);
|
|
91
108
|
} else {
|
|
92
|
-
|
|
109
|
+
// Return both stdout and stderr since Docker commands often write to stderr
|
|
110
|
+
resolve({ stdout, stderr, combined: stdout + stderr });
|
|
93
111
|
}
|
|
94
112
|
});
|
|
95
113
|
|
|
@@ -117,11 +135,36 @@ function askConfirmation(question) {
|
|
|
117
135
|
});
|
|
118
136
|
}
|
|
119
137
|
|
|
138
|
+
function checkDockerAvailable() {
|
|
139
|
+
try {
|
|
140
|
+
execSync("docker info", { stdio: "pipe" });
|
|
141
|
+
return true;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function ensureDockerRunning() {
|
|
148
|
+
if (!checkDockerAvailable()) {
|
|
149
|
+
console.error(
|
|
150
|
+
chalk.red(
|
|
151
|
+
"\n✗ Docker is not running or not installed.\n",
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
console.log(chalk.gray("Please ensure Docker is installed and running:"));
|
|
155
|
+
console.log(chalk.gray(" • Start Docker Desktop, or"));
|
|
156
|
+
console.log(chalk.gray(" • Start the Docker daemon\n"));
|
|
157
|
+
console.log(chalk.gray("Then try your command again.\n"));
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
120
162
|
// ============================================================================
|
|
121
163
|
// Nursery Environment Commands
|
|
122
164
|
// ============================================================================
|
|
123
165
|
|
|
124
166
|
export async function startNursery(options) {
|
|
167
|
+
ensureDockerRunning();
|
|
125
168
|
const spinner = ora("Starting Crux Garden Nursery environment...").start();
|
|
126
169
|
|
|
127
170
|
try {
|
|
@@ -198,6 +241,7 @@ export async function startNursery(options) {
|
|
|
198
241
|
}
|
|
199
242
|
|
|
200
243
|
export async function stopNursery() {
|
|
244
|
+
ensureDockerRunning();
|
|
201
245
|
const spinner = ora("Stopping Crux Garden Nursery environment...").start();
|
|
202
246
|
await runCommandAsync("docker-compose -f docker-compose.nursery.yml down", {
|
|
203
247
|
silent: true,
|
|
@@ -212,6 +256,7 @@ export async function stopNursery() {
|
|
|
212
256
|
}
|
|
213
257
|
|
|
214
258
|
export async function logsNursery(options) {
|
|
259
|
+
ensureDockerRunning();
|
|
215
260
|
if (options.follow) {
|
|
216
261
|
console.log(
|
|
217
262
|
chalk.gray("Following nursery logs (press Ctrl+C to exit)...\n"),
|
|
@@ -236,6 +281,7 @@ export async function logsNursery(options) {
|
|
|
236
281
|
}
|
|
237
282
|
|
|
238
283
|
export async function cleanNursery() {
|
|
284
|
+
ensureDockerRunning();
|
|
239
285
|
console.log(
|
|
240
286
|
chalk.yellow(
|
|
241
287
|
"\n⚠️ This will delete all nursery containers and volumes (including data)!\n",
|
|
@@ -273,6 +319,7 @@ export async function cleanNursery() {
|
|
|
273
319
|
}
|
|
274
320
|
|
|
275
321
|
export async function purgeNursery() {
|
|
322
|
+
ensureDockerRunning();
|
|
276
323
|
console.log(
|
|
277
324
|
chalk.yellow(
|
|
278
325
|
"\n⚠️ This will delete ALL nursery resources (containers, volumes, AND images)!\n",
|
|
@@ -315,25 +362,63 @@ export async function purgeNursery() {
|
|
|
315
362
|
console.log();
|
|
316
363
|
}
|
|
317
364
|
|
|
318
|
-
export async function
|
|
365
|
+
export async function updateNursery() {
|
|
366
|
+
ensureDockerRunning();
|
|
319
367
|
const spinner = ora(
|
|
320
|
-
"
|
|
368
|
+
"Checking for latest Crux Garden API image from ghcr.io...",
|
|
321
369
|
).start();
|
|
322
370
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
371
|
+
try {
|
|
372
|
+
// Get the current local image digest before pulling
|
|
373
|
+
const beforeResult = await runCommandAsync(
|
|
374
|
+
"docker images --digests --format '{{.Digest}}' ghcr.io/cruxgarden/api:latest",
|
|
375
|
+
{ silent: true, ignoreError: true }
|
|
376
|
+
);
|
|
377
|
+
const digestBefore = beforeResult?.combined?.trim() || beforeResult?.stdout?.trim() || "";
|
|
326
378
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
379
|
+
// Pull latest images
|
|
380
|
+
spinner.text = "Pulling latest images...";
|
|
381
|
+
await runCommandAsync("docker-compose -f docker-compose.nursery.yml pull", {
|
|
382
|
+
silent: true,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Get the image digest after pulling
|
|
386
|
+
const afterResult = await runCommandAsync(
|
|
387
|
+
"docker images --digests --format '{{.Digest}}' ghcr.io/cruxgarden/api:latest",
|
|
388
|
+
{ silent: true, ignoreError: true }
|
|
389
|
+
);
|
|
390
|
+
const digestAfter = afterResult?.combined?.trim() || afterResult?.stdout?.trim() || "";
|
|
391
|
+
|
|
392
|
+
// Compare digests to see if image was updated
|
|
393
|
+
if (digestBefore && digestAfter && digestBefore === digestAfter) {
|
|
394
|
+
spinner.succeed("Nursery image is already up-to-date!");
|
|
395
|
+
console.log(
|
|
396
|
+
chalk.gray("\nYou're running the latest version."),
|
|
397
|
+
);
|
|
398
|
+
} else if (digestAfter) {
|
|
399
|
+
spinner.succeed("Latest nursery image downloaded!");
|
|
400
|
+
console.log(
|
|
401
|
+
chalk.gray("\nRun"),
|
|
402
|
+
chalk.cyan("crux nursery restart"),
|
|
403
|
+
chalk.gray("to use the new image."),
|
|
404
|
+
);
|
|
405
|
+
} else {
|
|
406
|
+
spinner.succeed("Images pulled successfully!");
|
|
407
|
+
console.log(
|
|
408
|
+
chalk.gray("\nRun"),
|
|
409
|
+
chalk.cyan("crux nursery restart"),
|
|
410
|
+
chalk.gray("to use the latest images."),
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
} catch (error) {
|
|
414
|
+
spinner.fail("Failed to update images");
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
333
417
|
console.log();
|
|
334
418
|
}
|
|
335
419
|
|
|
336
420
|
export async function resetNursery() {
|
|
421
|
+
ensureDockerRunning();
|
|
337
422
|
console.log(
|
|
338
423
|
chalk.yellow(
|
|
339
424
|
"\n⚠️ This will delete all nursery data and start fresh with the latest image!\n",
|
|
@@ -406,12 +491,14 @@ export async function restartNursery() {
|
|
|
406
491
|
}
|
|
407
492
|
|
|
408
493
|
export async function statusNursery() {
|
|
494
|
+
ensureDockerRunning();
|
|
409
495
|
console.log(chalk.bold("\nCrux Garden Nursery Environment Status:\n"));
|
|
410
496
|
runCommand("docker-compose -f docker-compose.nursery.yml ps");
|
|
411
497
|
console.log();
|
|
412
498
|
}
|
|
413
499
|
|
|
414
500
|
export async function connectNurseryDb() {
|
|
501
|
+
ensureDockerRunning();
|
|
415
502
|
console.log(chalk.gray("Connecting to Nursery PostgreSQL...\n"));
|
|
416
503
|
runCommand(
|
|
417
504
|
"docker exec -it cruxgarden-nursery-postgres psql -U cruxgarden -d cruxgarden",
|
|
@@ -419,16 +506,19 @@ export async function connectNurseryDb() {
|
|
|
419
506
|
}
|
|
420
507
|
|
|
421
508
|
export async function connectNurseryRedis() {
|
|
509
|
+
ensureDockerRunning();
|
|
422
510
|
console.log(chalk.gray("Connecting to Nursery Redis...\n"));
|
|
423
511
|
runCommand("docker exec -it cruxgarden-nursery-redis redis-cli");
|
|
424
512
|
}
|
|
425
513
|
|
|
426
514
|
export async function connectNurseryApi() {
|
|
515
|
+
ensureDockerRunning();
|
|
427
516
|
console.log(chalk.gray("Opening shell in Nursery API container...\n"));
|
|
428
517
|
runCommand("docker exec -it cruxgarden-nursery-api sh");
|
|
429
518
|
}
|
|
430
519
|
|
|
431
520
|
export async function stopNurseryDb() {
|
|
521
|
+
ensureDockerRunning();
|
|
432
522
|
const spinner = ora("Stopping nursery database services...").start();
|
|
433
523
|
await runCommandAsync(
|
|
434
524
|
"docker-compose -f docker-compose.nursery.yml stop postgres redis",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cruxgarden/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Crux Garden CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
"docker:nursery:db:stop": "cd docker && docker-compose -f docker-compose.nursery.yml stop postgres redis",
|
|
20
20
|
"docker:nursery:redis:connect": "docker exec -it cruxgarden-nursery-redis redis-cli",
|
|
21
21
|
"docker:nursery:db:connect": "docker exec -it cruxgarden-nursery-postgres psql -U cruxgarden -d cruxgarden",
|
|
22
|
-
"docker:nursery:api:connect": "docker exec -it cruxgarden-nursery-api sh"
|
|
22
|
+
"docker:nursery:api:connect": "docker exec -it cruxgarden-nursery-api sh",
|
|
23
|
+
"publish:patch": "npm version patch && npm publish && git push && git push --tags",
|
|
24
|
+
"publish:minor": "npm version minor && npm publish && git push && git push --tags",
|
|
25
|
+
"publish:major": "npm version major && npm publish && git push && git push --tags"
|
|
23
26
|
},
|
|
24
27
|
"engines": {
|
|
25
28
|
"node": ">=18.0.0"
|