rushdeploy 1.2.0 → 1.2.1
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/index.js +77 -35
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* RushDeploy CLI
|
|
4
4
|
* A zero-dependency Node.js CLI utility to manage your RushDeploy account.
|
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const os = require('os');
|
|
10
|
+
let crypto;
|
|
11
|
+
try {
|
|
12
|
+
crypto = require('node:crypto');
|
|
13
|
+
} catch {
|
|
14
|
+
try {
|
|
15
|
+
crypto = require('crypto');
|
|
16
|
+
} catch {}
|
|
17
|
+
}
|
|
10
18
|
const http = require('http');
|
|
11
19
|
const https = require('https');
|
|
12
20
|
const { exec, spawn } = require('child_process');
|
|
@@ -14,6 +22,11 @@ const { exec, spawn } = require('child_process');
|
|
|
14
22
|
const CONFIG_DIR = path.join(os.homedir(), '.rushdeploy');
|
|
15
23
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
16
24
|
|
|
25
|
+
let CLI_VERSION = '1.2.1';
|
|
26
|
+
try {
|
|
27
|
+
CLI_VERSION = require('../package.json').version;
|
|
28
|
+
} catch {}
|
|
29
|
+
|
|
17
30
|
// Domain Hardcoding: Always default to rushdeploy.com
|
|
18
31
|
const DEFAULT_SERVER = 'https://rushdeploy.com';
|
|
19
32
|
|
|
@@ -92,8 +105,8 @@ const ui = {
|
|
|
92
105
|
},
|
|
93
106
|
|
|
94
107
|
box(fields, options = {}) {
|
|
95
|
-
const fieldPairs = Array.isArray(fields)
|
|
96
|
-
? fields
|
|
108
|
+
const fieldPairs = Array.isArray(fields)
|
|
109
|
+
? fields
|
|
97
110
|
: Object.entries(fields).map(([label, value]) => ({ label, value }));
|
|
98
111
|
|
|
99
112
|
const labelWidth = 14;
|
|
@@ -110,7 +123,7 @@ const ui = {
|
|
|
110
123
|
const innerWidth = Math.max(options.minWidth || 66, leftIndent + labelWidth + gap + maxValLen + rightMargin);
|
|
111
124
|
|
|
112
125
|
const topBorder = `┌${'─'.repeat(innerWidth)}┐`;
|
|
113
|
-
const emptyRow
|
|
126
|
+
const emptyRow = `│${' '.repeat(innerWidth)}│`;
|
|
114
127
|
const bottomBorder = `└${'─'.repeat(innerWidth)}┘`;
|
|
115
128
|
|
|
116
129
|
console.log(`\n${colors.gray}${topBorder}${colors.reset}`);
|
|
@@ -192,10 +205,18 @@ function loadConfig() {
|
|
|
192
205
|
function saveConfig(server, token) {
|
|
193
206
|
try {
|
|
194
207
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
195
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
208
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
196
209
|
}
|
|
197
210
|
const cleanServer = (server || DEFAULT_SERVER).replace(/\/$/, '');
|
|
198
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify({ server: cleanServer, token }, null, 2), 'utf8');
|
|
211
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify({ server: cleanServer, token }, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
212
|
+
try {
|
|
213
|
+
fs.chmodSync(CONFIG_FILE, 0o600);
|
|
214
|
+
} catch (chmodErr) {
|
|
215
|
+
try {
|
|
216
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
217
|
+
} catch {}
|
|
218
|
+
throw chmodErr;
|
|
219
|
+
}
|
|
199
220
|
return true;
|
|
200
221
|
} catch (err) {
|
|
201
222
|
ui.stepError(`Error saving configuration: ${err.message}`);
|
|
@@ -352,9 +373,30 @@ function extractRepoName(url) {
|
|
|
352
373
|
}
|
|
353
374
|
}
|
|
354
375
|
|
|
376
|
+
function generateRandomState() {
|
|
377
|
+
if (crypto && typeof crypto.randomBytes === 'function') {
|
|
378
|
+
try {
|
|
379
|
+
return crypto.randomBytes(16).toString('hex');
|
|
380
|
+
} catch {}
|
|
381
|
+
}
|
|
382
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto?.getRandomValues) {
|
|
383
|
+
try {
|
|
384
|
+
const array = new Uint8Array(16);
|
|
385
|
+
globalThis.crypto.getRandomValues(array);
|
|
386
|
+
return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
387
|
+
} catch {}
|
|
388
|
+
}
|
|
389
|
+
if (typeof globalThis !== 'undefined' && typeof globalThis.crypto?.randomUUID === 'function') {
|
|
390
|
+
try {
|
|
391
|
+
return globalThis.crypto.randomUUID().replace(/-/g, '');
|
|
392
|
+
} catch {}
|
|
393
|
+
}
|
|
394
|
+
return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
395
|
+
}
|
|
396
|
+
|
|
355
397
|
function loginViaBrowser(server) {
|
|
356
398
|
return new Promise((resolve, reject) => {
|
|
357
|
-
const state =
|
|
399
|
+
const state = generateRandomState();
|
|
358
400
|
|
|
359
401
|
const localServer = http.createServer((req, res) => {
|
|
360
402
|
try {
|
|
@@ -425,7 +467,7 @@ function loginViaBrowser(server) {
|
|
|
425
467
|
timer = setTimeout(() => {
|
|
426
468
|
try {
|
|
427
469
|
localServer.close();
|
|
428
|
-
} catch {}
|
|
470
|
+
} catch { }
|
|
429
471
|
reject(new Error('Authentication timed out after 2 minutes'));
|
|
430
472
|
}, 120000);
|
|
431
473
|
if (timer.unref) {
|
|
@@ -450,16 +492,16 @@ async function cmdLogin(tokenArg, serverArg) {
|
|
|
450
492
|
} else {
|
|
451
493
|
ui.stepInfo(`Connecting to ${colors.brightWhite}${server}${colors.reset}...`);
|
|
452
494
|
}
|
|
453
|
-
|
|
495
|
+
|
|
454
496
|
saveConfig(server, token);
|
|
455
|
-
|
|
497
|
+
|
|
456
498
|
try {
|
|
457
499
|
ui.stepInfo(`Validating authentication token...`);
|
|
458
500
|
const res = await makeRequest('/auth/me');
|
|
459
501
|
if (res.success) {
|
|
460
502
|
const user = res.user;
|
|
461
503
|
ui.step(`Authentication successful!`);
|
|
462
|
-
|
|
504
|
+
|
|
463
505
|
ui.box([
|
|
464
506
|
{ label: 'Account', value: `${colors.bold}${user.name}${colors.reset} (${colors.gray}${user.email}${colors.reset})` },
|
|
465
507
|
{ label: 'Role', value: user.role.toUpperCase() },
|
|
@@ -526,7 +568,7 @@ async function cmdPlans() {
|
|
|
526
568
|
function drawCards(cards, cardsPerRow = 2) {
|
|
527
569
|
const cardWidth = 34;
|
|
528
570
|
const rows = [];
|
|
529
|
-
|
|
571
|
+
|
|
530
572
|
for (let i = 0; i < cards.length; i += cardsPerRow) {
|
|
531
573
|
rows.push(cards.slice(i, i + cardsPerRow));
|
|
532
574
|
}
|
|
@@ -545,19 +587,19 @@ function drawCards(cards, cardsPerRow = 2) {
|
|
|
545
587
|
|
|
546
588
|
const label = card.label || '';
|
|
547
589
|
let value = card.value !== undefined ? String(card.value) : '';
|
|
548
|
-
|
|
590
|
+
|
|
549
591
|
let valColor = colors.bold;
|
|
550
592
|
if (card.status === 'online') valColor = colors.green + colors.bold;
|
|
551
593
|
else if (card.status === 'offline') valColor = colors.red + colors.bold;
|
|
552
594
|
else if (card.status === 'warn') valColor = colors.yellow + colors.bold;
|
|
553
|
-
|
|
595
|
+
|
|
554
596
|
const valStr = `${valColor}${value}${colors.reset}`;
|
|
555
597
|
const rawVal = stripAnsi(value);
|
|
556
|
-
|
|
598
|
+
|
|
557
599
|
const textLen = label.length + rawVal.length;
|
|
558
600
|
const padLen = cardWidth - 6 - textLen;
|
|
559
601
|
const pad = ' '.repeat(Math.max(1, padLen));
|
|
560
|
-
|
|
602
|
+
|
|
561
603
|
labelValLine += space + `${colors.gray}│${colors.reset} ${colors.brightCyan}${label}${colors.reset}${pad}${valStr} ${colors.gray}│${colors.reset}`;
|
|
562
604
|
sepLine += space + colors.gray + '├' + '─'.repeat(cardWidth - 2) + '┤' + colors.reset;
|
|
563
605
|
|
|
@@ -565,7 +607,7 @@ function drawCards(cards, cardsPerRow = 2) {
|
|
|
565
607
|
const rawHint = stripAnsi(hint);
|
|
566
608
|
const hintPadLen = cardWidth - 6 - rawHint.length;
|
|
567
609
|
const hintPad = ' '.repeat(Math.max(0, hintPadLen));
|
|
568
|
-
|
|
610
|
+
|
|
569
611
|
hintLine += space + `${colors.gray}│${colors.reset} ${colors.gray}${hint}${colors.reset}${hintPad} ${colors.gray}│${colors.reset}`;
|
|
570
612
|
bottomBorder += space + colors.gray + '└' + '─'.repeat(cardWidth - 2) + '┘' + colors.reset;
|
|
571
613
|
});
|
|
@@ -604,7 +646,7 @@ async function cmdStatus() {
|
|
|
604
646
|
const byStatus = stats.projects_by_status || {};
|
|
605
647
|
const celery = stats.celery || {};
|
|
606
648
|
const workerOnline = (celery.workers ?? 0) > 0;
|
|
607
|
-
|
|
649
|
+
|
|
608
650
|
const activeBuildingProjects = (byStatus.building || byStatus.BUILDING || 0) + (byStatus.pending || byStatus.PENDING || 0);
|
|
609
651
|
const activeTasksCount = (celery.active && celery.active > 0) ? celery.active : activeBuildingProjects;
|
|
610
652
|
const pendingInQueue = stats.redis_queue_len ?? celery.queued ?? 0;
|
|
@@ -622,23 +664,23 @@ async function cmdStatus() {
|
|
|
622
664
|
const adminCards = [
|
|
623
665
|
{ label: 'Users', value: stats.users ?? 0, hint: 'Total registered users' },
|
|
624
666
|
{ label: 'Databases', value: stats.databases ?? 0, hint: 'Total workspace databases' },
|
|
625
|
-
{
|
|
626
|
-
label: 'Projects',
|
|
627
|
-
value: stats.projects ?? 0,
|
|
628
|
-
hint: `${colors.green}${runCount} run${colors.reset} · ${colors.yellow}${buildCount} build${colors.reset} · ${colors.red}${failCount} fail${colors.reset}`
|
|
667
|
+
{
|
|
668
|
+
label: 'Projects',
|
|
669
|
+
value: stats.projects ?? 0,
|
|
670
|
+
hint: `${colors.green}${runCount} run${colors.reset} · ${colors.yellow}${buildCount} build${colors.reset} · ${colors.red}${failCount} fail${colors.reset}`
|
|
629
671
|
},
|
|
630
|
-
{
|
|
631
|
-
label: 'App Containers',
|
|
632
|
-
value: stats.running_containers ?? 0,
|
|
672
|
+
{
|
|
673
|
+
label: 'App Containers',
|
|
674
|
+
value: stats.running_containers ?? 0,
|
|
633
675
|
status: stats.docker_available ? 'online' : 'offline',
|
|
634
676
|
hint: stats.docker_available ? 'Active running containers' : 'Engine offline'
|
|
635
677
|
},
|
|
636
678
|
{ label: 'CPU Usage', value: cpuVal, hint: 'Host CPU load' },
|
|
637
679
|
{ label: 'RAM Usage', value: ramVal, hint: 'Host memory allocation' },
|
|
638
680
|
{ label: 'Disk Usage', value: diskVal, hint: 'Host storage capacity' },
|
|
639
|
-
{
|
|
640
|
-
label: 'Task Queue',
|
|
641
|
-
value: workerOnline ? 'Online' : 'Offline',
|
|
681
|
+
{
|
|
682
|
+
label: 'Task Queue',
|
|
683
|
+
value: workerOnline ? 'Online' : 'Offline',
|
|
642
684
|
status: workerOnline ? 'online' : 'offline',
|
|
643
685
|
hint: `${activeTasksCount} active · ${pendingInQueue} pending`
|
|
644
686
|
}
|
|
@@ -940,14 +982,14 @@ async function resolveProjectId(idOrSlug) {
|
|
|
940
982
|
if (/^\d+$/.test(idOrSlug)) {
|
|
941
983
|
return parseInt(idOrSlug, 10);
|
|
942
984
|
}
|
|
943
|
-
|
|
985
|
+
|
|
944
986
|
const res = await makeRequest('/projects/');
|
|
945
987
|
const projects = res.projects || [];
|
|
946
988
|
const matched = projects.find(p => p.slug === idOrSlug || p.name.toLowerCase() === idOrSlug.toLowerCase());
|
|
947
989
|
if (matched) {
|
|
948
990
|
return matched.id;
|
|
949
991
|
}
|
|
950
|
-
|
|
992
|
+
|
|
951
993
|
ui.stepError(`No project found matching name or slug "${idOrSlug}".`);
|
|
952
994
|
process.exit(1);
|
|
953
995
|
}
|
|
@@ -1017,7 +1059,7 @@ async function cmdProject(idOrSlug, subcommand, subArgs) {
|
|
|
1017
1059
|
const stats = statsRes.stats;
|
|
1018
1060
|
|
|
1019
1061
|
ui.stepInfo(`Resource metrics for Project ${id}`);
|
|
1020
|
-
|
|
1062
|
+
|
|
1021
1063
|
const fields = [
|
|
1022
1064
|
{ label: 'Current Status', value: ui.badge(info.status) }
|
|
1023
1065
|
];
|
|
@@ -1298,7 +1340,7 @@ async function cmdWorkspace() {
|
|
|
1298
1340
|
|
|
1299
1341
|
function printHelp() {
|
|
1300
1342
|
console.log(`
|
|
1301
|
-
${colors.bold}${colors.brightWhite}RushDeploy CLI${colors.reset} ${colors.gray}
|
|
1343
|
+
${colors.bold}${colors.brightWhite}RushDeploy CLI${colors.reset} ${colors.gray}v${CLI_VERSION}${colors.reset} - Modern PaaS Command Line Interface
|
|
1302
1344
|
|
|
1303
1345
|
${colors.bold}USAGE:${colors.reset}
|
|
1304
1346
|
${colors.brightCyan}rushdeploy${colors.reset} <command> [arguments] [options]
|
|
@@ -1355,7 +1397,7 @@ async function main() {
|
|
|
1355
1397
|
}
|
|
1356
1398
|
|
|
1357
1399
|
if (command === '--version' || command === '-v' || command === 'version') {
|
|
1358
|
-
console.log(
|
|
1400
|
+
console.log(CLI_VERSION);
|
|
1359
1401
|
process.exit(0);
|
|
1360
1402
|
}
|
|
1361
1403
|
|
|
@@ -1363,7 +1405,7 @@ async function main() {
|
|
|
1363
1405
|
case 'login': {
|
|
1364
1406
|
let token = null;
|
|
1365
1407
|
let server = null;
|
|
1366
|
-
|
|
1408
|
+
|
|
1367
1409
|
const serverIndex = args.indexOf('--server');
|
|
1368
1410
|
if (serverIndex !== -1 && args[serverIndex + 1]) {
|
|
1369
1411
|
server = args[serverIndex + 1];
|
|
@@ -1373,7 +1415,7 @@ async function main() {
|
|
|
1373
1415
|
if (args[1] && !args[1].startsWith('--')) {
|
|
1374
1416
|
token = args[1];
|
|
1375
1417
|
}
|
|
1376
|
-
|
|
1418
|
+
|
|
1377
1419
|
await cmdLogin(token, server);
|
|
1378
1420
|
break;
|
|
1379
1421
|
}
|