@archznn/xavva 2.1.0 → 2.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/README.md +3 -204
- package/package.json +1 -1
- package/src/commands/DeployCommand.ts +33 -28
- package/src/commands/DepsCommand.ts +60 -0
- package/src/commands/HelpCommand.ts +3 -0
- package/src/services/BuildService.ts +12 -4
- package/src/services/DashboardService.ts +178 -133
- package/src/services/DependencyAnalyzerService.ts +119 -11
- package/src/services/TomcatService.ts +33 -12
- package/src/utils/ui.ts +641 -312
|
@@ -1,142 +1,187 @@
|
|
|
1
1
|
import { Logger } from "../utils/ui";
|
|
2
2
|
import { ProcessManager } from "../utils/processManager";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
MAX_LOG_SCROLLBUFFER,
|
|
5
|
+
DASHBOARD_REFRESH_INTERVAL_MS,
|
|
6
|
+
DASHBOARD_LOG_SLICE_LINES
|
|
7
7
|
} from "../utils/constants";
|
|
8
8
|
import type { AppConfig } from "../types/config";
|
|
9
9
|
import os from "os";
|
|
10
10
|
|
|
11
|
+
const C = Logger.C;
|
|
12
|
+
|
|
11
13
|
export class DashboardService {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
14
|
+
private isTui: boolean;
|
|
15
|
+
private logLines: string[] = [];
|
|
16
|
+
private maxLogLines: number = 0;
|
|
17
|
+
private status: string = "idle";
|
|
18
|
+
private statusColor: string = C.gray;
|
|
19
|
+
private gitContext: { branch: string; hash: string } | null = null;
|
|
20
|
+
private actions: Map<string, () => void> = new Map();
|
|
21
|
+
private startTime = Date.now();
|
|
22
|
+
|
|
23
|
+
constructor(private config: AppConfig) {
|
|
24
|
+
this.isTui = config.project.tui;
|
|
25
|
+
if (this.isTui) {
|
|
26
|
+
this.gitContext = Logger.getGitContext();
|
|
27
|
+
this.maxLogLines = process.stdout.rows - 8;
|
|
28
|
+
this.setupTui();
|
|
29
|
+
this.registerShutdownHandlers();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private registerShutdownHandlers() {
|
|
34
|
+
const processManager = ProcessManager.getInstance();
|
|
35
|
+
processManager.onShutdown(() => {
|
|
36
|
+
this.restoreTerminal();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private restoreTerminal() {
|
|
41
|
+
if (this.isTui) {
|
|
42
|
+
process.stdout.write("\x1B[?1049l");
|
|
43
|
+
process.stdout.write("\x1B[?25h");
|
|
44
|
+
process.stdin.setRawMode(false);
|
|
45
|
+
process.stdin.pause();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public isTuiActive(): boolean {
|
|
50
|
+
return this.isTui;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public onAction(key: string, callback: () => void) {
|
|
54
|
+
this.actions.set(key.toLowerCase(), callback);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private setupTui() {
|
|
58
|
+
process.stdout.write("\x1B[?1049h");
|
|
59
|
+
process.stdout.write("\x1B[2J");
|
|
60
|
+
process.stdout.write("\x1B[?25l");
|
|
61
|
+
|
|
62
|
+
process.stdin.setRawMode(true);
|
|
63
|
+
process.stdin.resume();
|
|
64
|
+
process.stdin.setEncoding("utf8");
|
|
65
|
+
|
|
66
|
+
process.stdin.on("data", (key: string) => {
|
|
67
|
+
const input = key.toLowerCase();
|
|
68
|
+
if (key === "\u0003" || input === "q") {
|
|
69
|
+
this.exit();
|
|
70
|
+
}
|
|
71
|
+
if (input === "l") {
|
|
72
|
+
this.logLines = [];
|
|
73
|
+
this.render();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const action = this.actions.get(input);
|
|
77
|
+
if (action) action();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
process.on("SIGINT", () => this.exit());
|
|
81
|
+
process.on("exit", () => this.exit());
|
|
82
|
+
|
|
83
|
+
setInterval(() => this.render(), DASHBOARD_REFRESH_INTERVAL_MS);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public setStatus(status: string, type: 'idle' | 'building' | 'ready' | 'error' = 'idle') {
|
|
87
|
+
this.status = status;
|
|
88
|
+
switch (type) {
|
|
89
|
+
case 'building':
|
|
90
|
+
this.statusColor = C.primary;
|
|
91
|
+
break;
|
|
92
|
+
case 'ready':
|
|
93
|
+
this.statusColor = C.success;
|
|
94
|
+
break;
|
|
95
|
+
case 'error':
|
|
96
|
+
this.statusColor = C.error;
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
this.statusColor = C.gray;
|
|
100
|
+
}
|
|
101
|
+
this.render();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public spinner(msg: string) {
|
|
105
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
106
|
+
let i = 0;
|
|
107
|
+
|
|
108
|
+
this.setStatus(msg, 'building');
|
|
109
|
+
|
|
110
|
+
const timer = setInterval(() => {
|
|
111
|
+
this.status = `${frames[i]} ${msg}`;
|
|
112
|
+
i = (i + 1) % frames.length;
|
|
113
|
+
this.render();
|
|
114
|
+
}, 80);
|
|
115
|
+
|
|
116
|
+
return (success = true) => {
|
|
117
|
+
clearInterval(timer);
|
|
118
|
+
if (success) {
|
|
119
|
+
this.setStatus('ready', 'ready');
|
|
120
|
+
} else {
|
|
121
|
+
this.setStatus('error', 'error');
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public log(message: string) {
|
|
127
|
+
if (!message) return;
|
|
128
|
+
|
|
129
|
+
if (this.isTui) {
|
|
130
|
+
const lines = message.split("\n");
|
|
131
|
+
this.logLines.push(...lines);
|
|
132
|
+
if (this.logLines.length > MAX_LOG_SCROLLBUFFER) {
|
|
133
|
+
this.logLines = this.logLines.slice(-DASHBOARD_LOG_SLICE_LINES);
|
|
134
|
+
}
|
|
135
|
+
this.render();
|
|
136
|
+
} else {
|
|
137
|
+
console.log(message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private render() {
|
|
142
|
+
if (!this.isTui) return;
|
|
143
|
+
|
|
144
|
+
this.maxLogLines = process.stdout.rows - 8;
|
|
145
|
+
|
|
146
|
+
const projectName = (process.cwd().split(/[/\\]/).pop() || "project").toLowerCase();
|
|
147
|
+
const uptime = Math.floor((Date.now() - this.startTime) / 1000);
|
|
148
|
+
const uptimeStr = uptime < 60 ? `${uptime}s` : `${Math.floor(uptime / 60)}m ${uptime % 60}s`;
|
|
149
|
+
|
|
150
|
+
const mem = Math.round((os.totalmem() - os.freemem()) / 1024 / 1024 / 1024 * 10) / 10;
|
|
151
|
+
const totalMem = Math.round(os.totalmem() / 1024 / 1024 / 1024);
|
|
152
|
+
|
|
153
|
+
// Header minimalista
|
|
154
|
+
let output = "\x1B[H";
|
|
155
|
+
output += `${C.gray}┌─ ${C.primary}${C.bold}XAVVA${C.reset}${C.gray}.${C.reset}${projectName}${C.reset}`;
|
|
156
|
+
output += ` ${C.gray}│${C.reset} ${this.statusColor}${this.status}${C.reset}\x1B[K\n`;
|
|
157
|
+
|
|
158
|
+
// Info bar
|
|
159
|
+
const infos: string[] = [];
|
|
160
|
+
if (this.config.project.profile) infos.push(`${C.warning}${this.config.project.profile}${C.reset}`);
|
|
161
|
+
if (this.gitContext?.branch) infos.push(`${C.secondary}git:${this.gitContext.branch}${C.reset}`);
|
|
162
|
+
infos.push(`${C.gray}mem:${mem}/${totalMem}GB${C.reset}`);
|
|
163
|
+
infos.push(`${C.gray}up:${uptimeStr}${C.reset}`);
|
|
164
|
+
|
|
165
|
+
output += `${C.gray}│${C.reset} ${infos.join(` ${C.gray}•${C.reset} `)}\x1B[K\n`;
|
|
166
|
+
output += `${C.gray}├────────────────────────────────────────────────────────┤${C.reset}\x1B[K\n`;
|
|
167
|
+
|
|
168
|
+
// Logs
|
|
169
|
+
const visibleLogs = this.logLines.slice(-this.maxLogLines);
|
|
170
|
+
for (let i = 0; i < this.maxLogLines; i++) {
|
|
171
|
+
const line = visibleLogs[i] || "";
|
|
172
|
+
output += `${C.gray}│${C.reset} ${line.substring(0, process.stdout.columns - 3)}\x1B[K\n`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Footer minimalista
|
|
176
|
+
output += `${C.gray}├────────────────────────────────────────────────────────┤${C.reset}\x1B[K\n`;
|
|
177
|
+
output += `${C.gray}│${C.reset} ${C.white}[r]${C.reset}${C.gray}estart ${C.white}[l]${C.reset}${C.gray}og clear ${C.white}[q]${C.reset}${C.gray}uit${C.reset}\x1B[K\n`;
|
|
178
|
+
output += `${C.gray}└────────────────────────────────────────────────────────┘${C.reset}\x1B[K`;
|
|
179
|
+
|
|
180
|
+
process.stdout.write(output);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private async exit() {
|
|
184
|
+
this.restoreTerminal();
|
|
185
|
+
await ProcessManager.getInstance().shutdown(0);
|
|
186
|
+
}
|
|
142
187
|
}
|
|
@@ -471,12 +471,120 @@ export class DependencyAnalyzerService {
|
|
|
471
471
|
return 0;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
+
async updateSafe(updates: DependencyUpdate[]): Promise<{ updated: number; skipped: number; errors: string[] }> {
|
|
475
|
+
const safeUpdates = updates.filter(u => !u.isMajor);
|
|
476
|
+
const result = { updated: 0, skipped: 0, errors: [] as string[] };
|
|
477
|
+
|
|
478
|
+
if (safeUpdates.length === 0) {
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (this.projectConfig.buildTool === "maven") {
|
|
483
|
+
return this.updateMavenSafe(safeUpdates);
|
|
484
|
+
} else {
|
|
485
|
+
return this.updateGradleSafe(safeUpdates);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
private async updateMavenSafe(updates: DependencyUpdate[]): Promise<{ updated: number; skipped: number; errors: string[] }> {
|
|
490
|
+
const result = { updated: 0, skipped: 0, errors: [] as string[] };
|
|
491
|
+
const pomPath = path.join(process.cwd(), "pom.xml");
|
|
492
|
+
|
|
493
|
+
if (!fs.existsSync(pomPath)) {
|
|
494
|
+
result.errors.push("pom.xml não encontrado");
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
let content = fs.readFileSync(pomPath, "utf-8");
|
|
499
|
+
let modified = false;
|
|
500
|
+
|
|
501
|
+
for (const update of updates) {
|
|
502
|
+
// Pattern para encontrar a versão específica desta dependência
|
|
503
|
+
const groupId = update.groupId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
504
|
+
const artifactId = update.artifactId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
505
|
+
const currentVersion = update.currentVersion.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
506
|
+
|
|
507
|
+
// Regex para encontrar <version> dentro do bloco da dependência
|
|
508
|
+
const depPattern = new RegExp(
|
|
509
|
+
`(<dependency>\\s*<groupId>${groupId}</groupId>\\s*<artifactId>${artifactId}</artifactId>(?:\\s*<version>)${currentVersion}(</version>))`,
|
|
510
|
+
'g'
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
if (depPattern.test(content)) {
|
|
514
|
+
content = content.replace(depPattern, `$1${update.latestVersion}$2`);
|
|
515
|
+
result.updated++;
|
|
516
|
+
modified = true;
|
|
517
|
+
} else {
|
|
518
|
+
// Pode ser definida via property
|
|
519
|
+
result.skipped++;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (modified) {
|
|
524
|
+
// Backup do pom.xml
|
|
525
|
+
fs.writeFileSync(`${pomPath}.backup`, fs.readFileSync(pomPath));
|
|
526
|
+
fs.writeFileSync(pomPath, content);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
private async updateGradleSafe(updates: DependencyUpdate[]): Promise<{ updated: number; skipped: number; errors: string[] }> {
|
|
533
|
+
const result = { updated: 0, skipped: 0, errors: [] as string[] };
|
|
534
|
+
const gradlePath = path.join(process.cwd(), "build.gradle");
|
|
535
|
+
|
|
536
|
+
if (!fs.existsSync(gradlePath)) {
|
|
537
|
+
result.errors.push("build.gradle não encontrado");
|
|
538
|
+
return result;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
let content = fs.readFileSync(gradlePath, "utf-8");
|
|
542
|
+
let modified = false;
|
|
543
|
+
|
|
544
|
+
for (const update of updates) {
|
|
545
|
+
const groupId = update.groupId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
546
|
+
const artifactId = update.artifactId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
547
|
+
const currentVersion = update.currentVersion.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
548
|
+
|
|
549
|
+
// Pattern: implementation("group:artifact:version") ou implementation 'group:artifact:version'
|
|
550
|
+
const patterns = [
|
|
551
|
+
new RegExp(`(implementation\\s*\\(\\s*["']${groupId}:${artifactId}:)${currentVersion}(["']\\s*\\))`, 'g'),
|
|
552
|
+
new RegExp(`(implementation\\s+["']${groupId}:${artifactId}:)${currentVersion}(["'])`, 'g'),
|
|
553
|
+
new RegExp(`(compile\\s*\\(\\s*["']${groupId}:${artifactId}:)${currentVersion}(["']\\s*\\))`, 'g'),
|
|
554
|
+
new RegExp(`(compile\\s+["']${groupId}:${artifactId}:)${currentVersion}(["'])`, 'g'),
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
let updated = false;
|
|
558
|
+
for (const pattern of patterns) {
|
|
559
|
+
if (pattern.test(content)) {
|
|
560
|
+
content = content.replace(pattern, `$1${update.latestVersion}$2`);
|
|
561
|
+
updated = true;
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (updated) {
|
|
567
|
+
result.updated++;
|
|
568
|
+
modified = true;
|
|
569
|
+
} else {
|
|
570
|
+
result.skipped++;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (modified) {
|
|
575
|
+
fs.writeFileSync(`${gradlePath}.backup`, fs.readFileSync(gradlePath));
|
|
576
|
+
fs.writeFileSync(gradlePath, content);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return result;
|
|
580
|
+
}
|
|
581
|
+
|
|
474
582
|
generateReport(result: DependencyAnalysisResult): string {
|
|
475
583
|
const lines: string[] = [];
|
|
476
584
|
lines.push("");
|
|
477
|
-
lines.push(`${Logger.C.
|
|
585
|
+
lines.push(`${Logger.C.primary}══════════════════════════════════════════════════════════${Logger.C.reset}`);
|
|
478
586
|
lines.push(`${Logger.C.bold}📊 ANÁLISE DE DEPENDÊNCIAS${Logger.C.reset}`);
|
|
479
|
-
lines.push(`${Logger.C.
|
|
587
|
+
lines.push(`${Logger.C.primary}══════════════════════════════════════════════════════════${Logger.C.reset}`);
|
|
480
588
|
lines.push("");
|
|
481
589
|
|
|
482
590
|
// Estatísticas
|
|
@@ -487,10 +595,10 @@ export class DependencyAnalyzerService {
|
|
|
487
595
|
|
|
488
596
|
// Conflitos
|
|
489
597
|
if (result.conflicts.length > 0) {
|
|
490
|
-
lines.push(`${Logger.C.
|
|
598
|
+
lines.push(`${Logger.C.warning}⚠️ CONFLITOS DE VERSÃO (${result.conflicts.length})${Logger.C.reset}`);
|
|
491
599
|
for (const conflict of result.conflicts) {
|
|
492
600
|
const icon = conflict.severity === "error" ? "✖" : "▲";
|
|
493
|
-
const color = conflict.severity === "error" ? Logger.C.
|
|
601
|
+
const color = conflict.severity === "error" ? Logger.C.error : Logger.C.warning;
|
|
494
602
|
lines.push(` ${color}${icon}${Logger.C.reset} ${conflict.groupId}:${conflict.artifactId}`);
|
|
495
603
|
lines.push(` Versões: ${conflict.versions.join(", ")}`);
|
|
496
604
|
}
|
|
@@ -503,10 +611,10 @@ export class DependencyAnalyzerService {
|
|
|
503
611
|
const minorUpdates = result.updates.filter(u => !u.isMajor);
|
|
504
612
|
|
|
505
613
|
if (minorUpdates.length > 0) {
|
|
506
|
-
lines.push(`${Logger.C.
|
|
614
|
+
lines.push(`${Logger.C.success}⬆️ ATUALIZAÇÕES DISPONÍVEIS (${minorUpdates.length})${Logger.C.reset}`);
|
|
507
615
|
for (const update of minorUpdates.slice(0, 5)) {
|
|
508
|
-
lines.push(` ${Logger.C.
|
|
509
|
-
lines.push(` ${update.currentVersion} → ${Logger.C.
|
|
616
|
+
lines.push(` ${Logger.C.success}↑${Logger.C.reset} ${update.groupId}:${update.artifactId}`);
|
|
617
|
+
lines.push(` ${update.currentVersion} → ${Logger.C.success}${update.latestVersion}${Logger.C.reset}`);
|
|
510
618
|
}
|
|
511
619
|
if (minorUpdates.length > 5) {
|
|
512
620
|
lines.push(` ${Logger.C.dim}... e mais ${minorUpdates.length - 5}${Logger.C.reset}`);
|
|
@@ -515,11 +623,11 @@ export class DependencyAnalyzerService {
|
|
|
515
623
|
}
|
|
516
624
|
|
|
517
625
|
if (majorUpdates.length > 0) {
|
|
518
|
-
lines.push(`${Logger.C.
|
|
626
|
+
lines.push(`${Logger.C.warning}⚠️ ATUALIZAÇÕES MAJOR (${majorUpdates.length})${Logger.C.reset}`);
|
|
519
627
|
lines.push(` ${Logger.C.dim}Podem conter breaking changes${Logger.C.reset}`);
|
|
520
628
|
for (const update of majorUpdates.slice(0, 3)) {
|
|
521
|
-
lines.push(` ${Logger.C.
|
|
522
|
-
lines.push(` ${update.currentVersion} → ${Logger.C.
|
|
629
|
+
lines.push(` ${Logger.C.warning}!${Logger.C.reset} ${update.groupId}:${update.artifactId}`);
|
|
630
|
+
lines.push(` ${update.currentVersion} → ${Logger.C.warning}${update.latestVersion}${Logger.C.reset}`);
|
|
523
631
|
}
|
|
524
632
|
lines.push("");
|
|
525
633
|
}
|
|
@@ -527,7 +635,7 @@ export class DependencyAnalyzerService {
|
|
|
527
635
|
|
|
528
636
|
// Resumo
|
|
529
637
|
if (result.conflicts.length === 0 && result.updates.length === 0) {
|
|
530
|
-
lines.push(`${Logger.C.
|
|
638
|
+
lines.push(`${Logger.C.success}✔ Todas as dependências estão atualizadas!${Logger.C.reset}`);
|
|
531
639
|
}
|
|
532
640
|
|
|
533
641
|
lines.push("");
|
|
@@ -2,7 +2,7 @@ import type { TomcatConfig, AppConfig } from "../types/config";
|
|
|
2
2
|
import { Logger } from "../utils/ui";
|
|
3
3
|
import type { Subprocess } from "bun";
|
|
4
4
|
import { ProjectService } from "./ProjectService";
|
|
5
|
-
import { existsSync, mkdirSync, writeFileSync, promises as fs } from "fs";
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync, statSync, promises as fs } from "fs";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import os from "os";
|
|
8
8
|
|
|
@@ -98,10 +98,10 @@ export class TomcatService {
|
|
|
98
98
|
const agentDir = path.join(os.homedir(), ".xavva", "agents");
|
|
99
99
|
const agentPath = path.join(agentDir, "hotswap-agent-2.0.3.jar");
|
|
100
100
|
|
|
101
|
-
if (
|
|
101
|
+
if (existsSync(agentPath) && statSync(agentPath).size > 1000) return agentPath;
|
|
102
102
|
|
|
103
103
|
try {
|
|
104
|
-
if (!
|
|
104
|
+
if (!existsSync(agentDir)) mkdirSync(agentDir, { recursive: true });
|
|
105
105
|
|
|
106
106
|
Logger.step("Downloading HotswapAgent v2.0.3 (Global)...");
|
|
107
107
|
const url = "https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-2.0.3/hotswap-agent-2.0.3.jar";
|
|
@@ -109,7 +109,7 @@ export class TomcatService {
|
|
|
109
109
|
if (!response.ok) throw new Error(`Status: ${response.status}`);
|
|
110
110
|
|
|
111
111
|
const buffer = await response.arrayBuffer();
|
|
112
|
-
|
|
112
|
+
writeFileSync(agentPath, Buffer.from(buffer));
|
|
113
113
|
Logger.success("HotswapAgent v2.0.3 installed globally!");
|
|
114
114
|
return agentPath;
|
|
115
115
|
} catch (e) {
|
|
@@ -237,18 +237,21 @@ export class TomcatService {
|
|
|
237
237
|
private async processLogStream(stream: ReadableStream, clean: boolean, quiet: boolean, verbose: boolean, grep: string) {
|
|
238
238
|
const reader = stream.getReader();
|
|
239
239
|
const decoder = new TextDecoder();
|
|
240
|
+
let buffer = "";
|
|
240
241
|
|
|
241
242
|
while (true) {
|
|
242
243
|
const { done, value } = await reader.read();
|
|
243
244
|
if (done) break;
|
|
244
245
|
|
|
245
|
-
|
|
246
|
-
const lines =
|
|
246
|
+
buffer += decoder.decode(value, { stream: true });
|
|
247
|
+
const lines = buffer.split(/\r?\n/);
|
|
248
|
+
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
|
247
249
|
|
|
248
250
|
for (const line of lines) {
|
|
249
251
|
const cleanLine = line.trim();
|
|
250
252
|
if (!cleanLine || cleanLine.startsWith("Listening for transport")) continue;
|
|
251
253
|
|
|
254
|
+
// Detect startup completion
|
|
252
255
|
if (cleanLine.includes("Server startup in") || cleanLine.includes("SEVERE") || cleanLine.includes("Exception")) {
|
|
253
256
|
const isSuccess = cleanLine.includes("Server startup in");
|
|
254
257
|
if (this.stopStartupSpinner) {
|
|
@@ -260,26 +263,44 @@ export class TomcatService {
|
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
|
|
266
|
+
// Verbose: formata logs do Tomcat
|
|
263
267
|
if (verbose) {
|
|
264
|
-
Logger.
|
|
268
|
+
if (Logger.isTomcatNoise(cleanLine)) {
|
|
269
|
+
continue; // Silencia noise completamente
|
|
270
|
+
}
|
|
271
|
+
const formatted = Logger.formatTomcatLog(cleanLine);
|
|
272
|
+
if (formatted) {
|
|
273
|
+
console.log(formatted);
|
|
274
|
+
}
|
|
265
275
|
continue;
|
|
266
276
|
}
|
|
267
277
|
|
|
278
|
+
// Clean mode: filtra noise
|
|
268
279
|
if (clean) {
|
|
280
|
+
// Sempre filtra noise do sistema
|
|
281
|
+
if (Logger.isSystemNoise(cleanLine)) continue;
|
|
282
|
+
|
|
283
|
+
// Quiet mode: só mostra essencial
|
|
269
284
|
if (quiet && !Logger.isEssential(cleanLine)) {
|
|
270
|
-
if (
|
|
271
|
-
if (cleanLine.includes("INFO")) continue;
|
|
272
|
-
} else if (Logger.isSystemNoise(cleanLine)) {
|
|
273
|
-
continue;
|
|
285
|
+
if (cleanLine.includes("INFO") && !cleanLine.includes("ERROR")) continue;
|
|
274
286
|
}
|
|
275
287
|
|
|
288
|
+
// Grep filter
|
|
276
289
|
if (grep && !cleanLine.toLowerCase().includes(grep.toLowerCase())) {
|
|
277
290
|
if (!Logger.isEssential(cleanLine)) continue;
|
|
278
291
|
}
|
|
279
292
|
|
|
280
293
|
const summarized = Logger.summarize(cleanLine);
|
|
281
|
-
if (summarized)
|
|
294
|
+
if (summarized) {
|
|
295
|
+
// Só mostra se não for vazio (rate limiting)
|
|
296
|
+
if (summarized.trim()) Logger.log(summarized);
|
|
297
|
+
}
|
|
282
298
|
} else {
|
|
299
|
+
// Non-clean: mostra tudo mas formata
|
|
300
|
+
if (Logger.isSystemNoise(cleanLine)) {
|
|
301
|
+
// Silencia completamente noise em non-clean também
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
283
304
|
Logger.log(cleanLine);
|
|
284
305
|
}
|
|
285
306
|
}
|