onveloz 0.0.0-beta.4 → 0.0.0-beta.40
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/deploy-tui-6DaoY351.mjs +1097 -0
- package/dist/index.mjs +35582 -2853
- package/dist/login-BXY4HXAt.mjs +3 -0
- package/package.json +25 -17
|
@@ -0,0 +1,1097 @@
|
|
|
1
|
+
import { a as statusLabels, i as TERMINAL_STATUSES, m as success, n as isHiddenMessage, o as getClient, p as info, r as parseBuildLine, t as cleanDisplayLine } from "./index.mjs";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { Box, Static, Text, render, useApp } from "ink";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region ../../packages/api/src/lib/build-steps.ts
|
|
8
|
+
const TIMESTAMP_RE = /^\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]\s*/;
|
|
9
|
+
const STEP_PREFIX_RE = /^#(\d+)\s+(.*)/;
|
|
10
|
+
const TIMING_PREFIX_RE = /^(\d+\.\d+)\s*(.*)/;
|
|
11
|
+
const DONE_RE = /^DONE\s+([\d.]+s?)$/;
|
|
12
|
+
const STAGE_RE = /^\[\s*(?:([\w-]+)\s+)?(\d+)\/\d+\]/;
|
|
13
|
+
function parseStageInfo(title) {
|
|
14
|
+
const match = title.match(STAGE_RE);
|
|
15
|
+
if (!match) return null;
|
|
16
|
+
return {
|
|
17
|
+
stage: (match[1] ?? "").toLowerCase(),
|
|
18
|
+
substep: parseInt(match[2], 10)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Derive stage order from first occurrence in the step list (preserves Dockerfile order). */
|
|
22
|
+
function buildStageOrder(steps) {
|
|
23
|
+
const order = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const step of steps) {
|
|
25
|
+
const info$1 = parseStageInfo(step.title);
|
|
26
|
+
if (info$1 && !order.has(info$1.stage)) order.set(info$1.stage, order.size);
|
|
27
|
+
}
|
|
28
|
+
return order;
|
|
29
|
+
}
|
|
30
|
+
/** Matches the pre-start log section header written by the reconciler. */
|
|
31
|
+
const PRE_START_HEADER_RE = /^──\s*Logs do comando pre-start/;
|
|
32
|
+
/** Matches the pre-start unavailable message. */
|
|
33
|
+
const PRE_START_UNAVAILABLE_RE = /^──\s*Logs do comando pre-start indisponíveis/;
|
|
34
|
+
/** Matches the crash log section header written by the reconciler. */
|
|
35
|
+
const CRASH_LOG_HEADER_RE = /^──\s*Logs do (?:serviço \(falha\)|container \(crash\))/;
|
|
36
|
+
/** Matches the crash logs unavailable message. */
|
|
37
|
+
const CRASH_LOG_UNAVAILABLE_RE = /^──\s*Logs do (?:serviço|container) indisponíveis/;
|
|
38
|
+
/** Lines that mark the beginning of the deploy/rollout phase. */
|
|
39
|
+
const DEPLOY_PHASE_MARKERS = [
|
|
40
|
+
/^Realizando deploy/,
|
|
41
|
+
/^Atualizando serviço/,
|
|
42
|
+
/^Criando serviço/,
|
|
43
|
+
/^Waiting for rollout/,
|
|
44
|
+
/^Updating deployment/,
|
|
45
|
+
/^Creating K8s deployment/
|
|
46
|
+
];
|
|
47
|
+
/** Lines that indicate the pre-start command is starting. */
|
|
48
|
+
const PRE_START_MARKER_RE = /^Executando comando pre-start:/;
|
|
49
|
+
const RUNTIME_LINE_PATTERNS = [
|
|
50
|
+
/^[\u26A0\u{1F680}]/u,
|
|
51
|
+
/^\{.*"(?:level|msg|pid)"/,
|
|
52
|
+
/^Error:\s/,
|
|
53
|
+
/^\s+at\s+/,
|
|
54
|
+
/^errorno:/i,
|
|
55
|
+
/^code:\s+'/,
|
|
56
|
+
/^syscall:\s+'/,
|
|
57
|
+
/Server (?:running|started)/i
|
|
58
|
+
];
|
|
59
|
+
function isRuntimeLine(content) {
|
|
60
|
+
return RUNTIME_LINE_PATTERNS.some((p) => p.test(content.trim()));
|
|
61
|
+
}
|
|
62
|
+
function isPreStartLine(content) {
|
|
63
|
+
const trimmed = content.trim();
|
|
64
|
+
return PRE_START_MARKER_RE.test(trimmed) || PRE_START_HEADER_RE.test(trimmed) || PRE_START_UNAVAILABLE_RE.test(trimmed);
|
|
65
|
+
}
|
|
66
|
+
function isDeployPhaseLine(content) {
|
|
67
|
+
const trimmed = content.trim();
|
|
68
|
+
return DEPLOY_PHASE_MARKERS.some((p) => p.test(trimmed)) || CRASH_LOG_HEADER_RE.test(trimmed) || CRASH_LOG_UNAVAILABLE_RE.test(trimmed);
|
|
69
|
+
}
|
|
70
|
+
function parseBuildSteps(rawText) {
|
|
71
|
+
const rawLines = rawText.split("\n");
|
|
72
|
+
let currentPhase = 0;
|
|
73
|
+
let maxStepInPhase = 0;
|
|
74
|
+
const phaseStepMap = /* @__PURE__ */ new Map();
|
|
75
|
+
const allSteps = [];
|
|
76
|
+
let currentGeneralStep = null;
|
|
77
|
+
for (const raw of rawLines) {
|
|
78
|
+
const trimmed = raw.trim();
|
|
79
|
+
if (!trimmed) continue;
|
|
80
|
+
const tsMatch = trimmed.match(TIMESTAMP_RE);
|
|
81
|
+
const timestamp = tsMatch?.[1] ?? null;
|
|
82
|
+
const line = tsMatch ? trimmed.slice(tsMatch[0].length) : trimmed;
|
|
83
|
+
if (!line.trim()) continue;
|
|
84
|
+
const stepMatch = line.match(STEP_PREFIX_RE);
|
|
85
|
+
if (!stepMatch) {
|
|
86
|
+
if (!currentGeneralStep) {
|
|
87
|
+
currentGeneralStep = {
|
|
88
|
+
stepNumber: null,
|
|
89
|
+
title: "Configuração",
|
|
90
|
+
duration: null,
|
|
91
|
+
status: "done",
|
|
92
|
+
lines: [],
|
|
93
|
+
phase: currentPhase,
|
|
94
|
+
startedAt: timestamp
|
|
95
|
+
};
|
|
96
|
+
allSteps.push(currentGeneralStep);
|
|
97
|
+
}
|
|
98
|
+
currentGeneralStep.lines.push({
|
|
99
|
+
content: line,
|
|
100
|
+
elapsed: null,
|
|
101
|
+
timestamp
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
currentGeneralStep = null;
|
|
106
|
+
const stepNum = parseInt(stepMatch[1], 10);
|
|
107
|
+
let content = stepMatch[2];
|
|
108
|
+
if (content.trim() === "...") continue;
|
|
109
|
+
const existingKey = `${currentPhase}-${stepNum}`;
|
|
110
|
+
const existing = phaseStepMap.get(existingKey);
|
|
111
|
+
if (existing && (existing.status === "done" || existing.status === "cached") && stepNum < maxStepInPhase) {
|
|
112
|
+
currentPhase++;
|
|
113
|
+
maxStepInPhase = 0;
|
|
114
|
+
}
|
|
115
|
+
if (stepNum > maxStepInPhase) maxStepInPhase = stepNum;
|
|
116
|
+
const key = `${currentPhase}-${stepNum}`;
|
|
117
|
+
let step = phaseStepMap.get(key);
|
|
118
|
+
if (!step) {
|
|
119
|
+
step = {
|
|
120
|
+
stepNumber: stepNum,
|
|
121
|
+
title: "",
|
|
122
|
+
duration: null,
|
|
123
|
+
status: "running",
|
|
124
|
+
lines: [],
|
|
125
|
+
phase: currentPhase,
|
|
126
|
+
startedAt: timestamp
|
|
127
|
+
};
|
|
128
|
+
phaseStepMap.set(key, step);
|
|
129
|
+
allSteps.push(step);
|
|
130
|
+
}
|
|
131
|
+
const doneMatch = content.match(DONE_RE);
|
|
132
|
+
if (doneMatch) {
|
|
133
|
+
step.duration = doneMatch[1];
|
|
134
|
+
step.status = "done";
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (content.trim() === "CACHED") {
|
|
138
|
+
step.status = "cached";
|
|
139
|
+
step.duration = "cached";
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
let elapsed = null;
|
|
143
|
+
const timingMatch = content.match(TIMING_PREFIX_RE);
|
|
144
|
+
if (timingMatch) {
|
|
145
|
+
elapsed = parseFloat(timingMatch[1]);
|
|
146
|
+
content = timingMatch[2];
|
|
147
|
+
}
|
|
148
|
+
if (!content.trim()) continue;
|
|
149
|
+
if (!step.title) step.title = content.trim();
|
|
150
|
+
if (step.lines.length > 0 || content.trim() !== step.title) step.lines.push({
|
|
151
|
+
content: content.trim(),
|
|
152
|
+
elapsed,
|
|
153
|
+
timestamp
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const numberedWithIndex = [];
|
|
157
|
+
const structure = [];
|
|
158
|
+
for (let i = 0; i < allSteps.length; i++) {
|
|
159
|
+
const step = allSteps[i];
|
|
160
|
+
if (step.stepNumber === null) structure.push({
|
|
161
|
+
type: "general",
|
|
162
|
+
origIdx: i
|
|
163
|
+
});
|
|
164
|
+
else if (step.title.trim() !== "") {
|
|
165
|
+
structure.push({
|
|
166
|
+
type: "numbered",
|
|
167
|
+
origIdx: i
|
|
168
|
+
});
|
|
169
|
+
numberedWithIndex.push({
|
|
170
|
+
step,
|
|
171
|
+
origIdx: i
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const stageOrder = buildStageOrder(numberedWithIndex.map((n) => n.step));
|
|
176
|
+
numberedWithIndex.sort((a, b) => {
|
|
177
|
+
if (a.step.phase !== b.step.phase) return a.step.phase - b.step.phase;
|
|
178
|
+
const aInfo = parseStageInfo(a.step.title);
|
|
179
|
+
const bInfo = parseStageInfo(b.step.title);
|
|
180
|
+
const aOrder = aInfo ? stageOrder.get(aInfo.stage) ?? 99 : 99;
|
|
181
|
+
const bOrder = bInfo ? stageOrder.get(bInfo.stage) ?? 99 : 99;
|
|
182
|
+
if (aOrder !== bOrder) return aOrder - bOrder;
|
|
183
|
+
const aSubstep = aInfo?.substep ?? a.step.stepNumber ?? 0;
|
|
184
|
+
const bSubstep = bInfo?.substep ?? b.step.stepNumber ?? 0;
|
|
185
|
+
if (aSubstep !== bSubstep) return aSubstep - bSubstep;
|
|
186
|
+
if (a.step.startedAt && b.step.startedAt) return new Date(a.step.startedAt).getTime() - new Date(b.step.startedAt).getTime();
|
|
187
|
+
return 0;
|
|
188
|
+
});
|
|
189
|
+
const result = [];
|
|
190
|
+
let nIdx = 0;
|
|
191
|
+
let generalCount = 0;
|
|
192
|
+
const totalGenerals = structure.filter((s) => s.type === "general").length;
|
|
193
|
+
for (const entry of structure) {
|
|
194
|
+
if (entry.type === "numbered") {
|
|
195
|
+
result.push(numberedWithIndex[nIdx].step);
|
|
196
|
+
nIdx++;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
generalCount++;
|
|
200
|
+
const generalStep = allSteps[entry.origIdx];
|
|
201
|
+
if (generalCount === totalGenerals && totalGenerals > 1) {
|
|
202
|
+
const finalizationLines = [];
|
|
203
|
+
const preStartLines = [];
|
|
204
|
+
const deployLines = [];
|
|
205
|
+
const healthLines = [];
|
|
206
|
+
let phase = "finalization";
|
|
207
|
+
let inCrashLogBlock = false;
|
|
208
|
+
for (const line of generalStep.lines) {
|
|
209
|
+
const content = line.content.trim();
|
|
210
|
+
if (CRASH_LOG_HEADER_RE.test(content) || PRE_START_HEADER_RE.test(content)) inCrashLogBlock = true;
|
|
211
|
+
if (phase === "finalization" && isPreStartLine(content)) phase = "prestart";
|
|
212
|
+
else if ((phase === "finalization" || phase === "prestart") && isDeployPhaseLine(content)) phase = "deploy";
|
|
213
|
+
else if ((phase === "finalization" || phase === "deploy") && !inCrashLogBlock && isRuntimeLine(content)) phase = "health";
|
|
214
|
+
switch (phase) {
|
|
215
|
+
case "finalization":
|
|
216
|
+
finalizationLines.push(line);
|
|
217
|
+
break;
|
|
218
|
+
case "prestart":
|
|
219
|
+
preStartLines.push(line);
|
|
220
|
+
break;
|
|
221
|
+
case "deploy":
|
|
222
|
+
deployLines.push(line);
|
|
223
|
+
break;
|
|
224
|
+
case "health":
|
|
225
|
+
healthLines.push(line);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const hasPreStartError = preStartLines.some((l) => /pre-start falhou|FAILED/i.test(l.content));
|
|
230
|
+
const hasDeployError = deployLines.some((l) => /DEPLOY FAILED/i.test(l.content));
|
|
231
|
+
if (finalizationLines.length > 0) result.push({
|
|
232
|
+
stepNumber: null,
|
|
233
|
+
title: "Finalização",
|
|
234
|
+
duration: null,
|
|
235
|
+
status: "done",
|
|
236
|
+
lines: finalizationLines,
|
|
237
|
+
phase: generalStep.phase,
|
|
238
|
+
startedAt: finalizationLines[0]?.timestamp ?? generalStep.startedAt
|
|
239
|
+
});
|
|
240
|
+
if (preStartLines.length > 0) result.push({
|
|
241
|
+
stepNumber: null,
|
|
242
|
+
title: "Pre-start",
|
|
243
|
+
duration: null,
|
|
244
|
+
status: hasPreStartError ? "error" : "done",
|
|
245
|
+
lines: preStartLines,
|
|
246
|
+
phase: generalStep.phase,
|
|
247
|
+
startedAt: preStartLines[0]?.timestamp ?? null
|
|
248
|
+
});
|
|
249
|
+
if (deployLines.length > 0) result.push({
|
|
250
|
+
stepNumber: null,
|
|
251
|
+
title: "Deploy",
|
|
252
|
+
duration: null,
|
|
253
|
+
status: hasDeployError ? "error" : "done",
|
|
254
|
+
lines: deployLines,
|
|
255
|
+
phase: generalStep.phase,
|
|
256
|
+
startedAt: deployLines[0]?.timestamp ?? null
|
|
257
|
+
});
|
|
258
|
+
if (healthLines.length > 0) result.push({
|
|
259
|
+
stepNumber: null,
|
|
260
|
+
title: "Verificação de saúde",
|
|
261
|
+
duration: null,
|
|
262
|
+
status: "done",
|
|
263
|
+
lines: healthLines,
|
|
264
|
+
phase: generalStep.phase,
|
|
265
|
+
startedAt: healthLines[0]?.timestamp ?? null
|
|
266
|
+
});
|
|
267
|
+
} else result.push(generalStep);
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/components/deploy-tui.tsx
|
|
274
|
+
const BRAND_COLOR = "#FF4D00";
|
|
275
|
+
const BAR_WIDTH = 20;
|
|
276
|
+
const SPINNER_FRAMES = [
|
|
277
|
+
"⠋",
|
|
278
|
+
"⠙",
|
|
279
|
+
"⠹",
|
|
280
|
+
"⠸",
|
|
281
|
+
"⠼",
|
|
282
|
+
"⠴",
|
|
283
|
+
"⠦",
|
|
284
|
+
"⠧",
|
|
285
|
+
"⠇",
|
|
286
|
+
"⠏"
|
|
287
|
+
];
|
|
288
|
+
const MAX_OUTPUT_LINES = 5;
|
|
289
|
+
/** Regex to extract stage info from BuildStep titles like `[stage-0 3/11] COPY ...` */
|
|
290
|
+
const STAGE_TITLE_RE = /^\[([\w-]+)\s+(\d+)\/(\d+)\]\s+(.+)$/;
|
|
291
|
+
function buildStepsToStages(steps) {
|
|
292
|
+
const stages = /* @__PURE__ */ new Map();
|
|
293
|
+
const platformMessages = [];
|
|
294
|
+
const stepDetails = /* @__PURE__ */ new Map();
|
|
295
|
+
const displayOrder = [];
|
|
296
|
+
const seenStages = /* @__PURE__ */ new Set();
|
|
297
|
+
for (const step of steps) {
|
|
298
|
+
if (step.stepNumber === null) {
|
|
299
|
+
for (const line of step.lines) {
|
|
300
|
+
const cleaned = cleanDisplayLine(line.content);
|
|
301
|
+
if (cleaned) platformMessages.push(cleaned);
|
|
302
|
+
}
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const match = step.title.match(STAGE_TITLE_RE);
|
|
306
|
+
if (!match) {
|
|
307
|
+
if (step.title && !/^\[internal\]/i.test(step.title)) displayOrder.push({
|
|
308
|
+
kind: "extra",
|
|
309
|
+
step
|
|
310
|
+
});
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const stageName = match[1];
|
|
314
|
+
const substep = parseInt(match[2], 10);
|
|
315
|
+
const total = parseInt(match[3], 10);
|
|
316
|
+
const command = match[4];
|
|
317
|
+
let stage = stages.get(stageName);
|
|
318
|
+
if (!stage) {
|
|
319
|
+
stage = {
|
|
320
|
+
name: stageName,
|
|
321
|
+
total,
|
|
322
|
+
steps: /* @__PURE__ */ new Map(),
|
|
323
|
+
stepNumMap: /* @__PURE__ */ new Map(),
|
|
324
|
+
cachedStepNums: /* @__PURE__ */ new Set(),
|
|
325
|
+
doneStepNums: /* @__PURE__ */ new Set()
|
|
326
|
+
};
|
|
327
|
+
stages.set(stageName, stage);
|
|
328
|
+
}
|
|
329
|
+
if (!seenStages.has(stageName)) {
|
|
330
|
+
seenStages.add(stageName);
|
|
331
|
+
displayOrder.push({
|
|
332
|
+
kind: "stage",
|
|
333
|
+
name: stageName
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
stage.steps.set(substep, command);
|
|
337
|
+
stage.stepNumMap.set(step.stepNumber, substep);
|
|
338
|
+
stage.total = Math.max(stage.total, total);
|
|
339
|
+
if (step.status === "cached") stage.cachedStepNums.add(step.stepNumber);
|
|
340
|
+
else if (step.status === "done") stage.doneStepNums.add(step.stepNumber);
|
|
341
|
+
stepDetails.set(`${stageName}-${substep}`, {
|
|
342
|
+
duration: step.duration !== "cached" ? step.duration : null,
|
|
343
|
+
status: step.status,
|
|
344
|
+
lastLines: step.lines.slice(-MAX_OUTPUT_LINES).map((l) => l.content),
|
|
345
|
+
lineCount: step.lines.length
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
stages,
|
|
350
|
+
platformMessages,
|
|
351
|
+
displayOrder,
|
|
352
|
+
stepDetails
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function AnimatedSpinner({ color = "cyan" }) {
|
|
356
|
+
const [frame, setFrame] = useState(0);
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
const timer = setInterval(() => {
|
|
359
|
+
setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
|
|
360
|
+
}, 80);
|
|
361
|
+
return () => clearInterval(timer);
|
|
362
|
+
}, []);
|
|
363
|
+
return /* @__PURE__ */ jsx(Text, {
|
|
364
|
+
color,
|
|
365
|
+
children: SPINNER_FRAMES[frame]
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function ProgressBar({ filled, total, allCached, allDone }) {
|
|
369
|
+
const ratio = total > 0 ? filled / total : 0;
|
|
370
|
+
const filledChars = Math.round(ratio * BAR_WIDTH);
|
|
371
|
+
const emptyChars = BAR_WIDTH - filledChars;
|
|
372
|
+
const counter = `${filled}/${total}`;
|
|
373
|
+
if (allCached) return /* @__PURE__ */ jsxs(Text, {
|
|
374
|
+
color: BRAND_COLOR,
|
|
375
|
+
children: [
|
|
376
|
+
"━".repeat(BAR_WIDTH),
|
|
377
|
+
" ",
|
|
378
|
+
counter,
|
|
379
|
+
" ✓ veloz cache"
|
|
380
|
+
]
|
|
381
|
+
});
|
|
382
|
+
if (allDone) return /* @__PURE__ */ jsxs(Text, {
|
|
383
|
+
color: "green",
|
|
384
|
+
children: [
|
|
385
|
+
"━".repeat(BAR_WIDTH),
|
|
386
|
+
" ",
|
|
387
|
+
counter,
|
|
388
|
+
" ✓"
|
|
389
|
+
]
|
|
390
|
+
});
|
|
391
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
392
|
+
/* @__PURE__ */ jsx(Text, {
|
|
393
|
+
color: "cyan",
|
|
394
|
+
children: "━".repeat(filledChars)
|
|
395
|
+
}),
|
|
396
|
+
/* @__PURE__ */ jsx(Text, {
|
|
397
|
+
dimColor: true,
|
|
398
|
+
children: "─".repeat(emptyChars)
|
|
399
|
+
}),
|
|
400
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
401
|
+
dimColor: true,
|
|
402
|
+
children: [" ", counter]
|
|
403
|
+
})
|
|
404
|
+
] });
|
|
405
|
+
}
|
|
406
|
+
function getStageStats(stage) {
|
|
407
|
+
let finishedCount = 0;
|
|
408
|
+
let cachedCount = 0;
|
|
409
|
+
for (const bkNum of stage.stepNumMap.keys()) if (stage.cachedStepNums.has(bkNum)) {
|
|
410
|
+
finishedCount++;
|
|
411
|
+
cachedCount++;
|
|
412
|
+
} else if (stage.doneStepNums.has(bkNum)) finishedCount++;
|
|
413
|
+
const allFinished = stage.total > 0 && finishedCount >= stage.total;
|
|
414
|
+
return {
|
|
415
|
+
finishedCount,
|
|
416
|
+
cachedCount,
|
|
417
|
+
allFinished,
|
|
418
|
+
allCached: allFinished && cachedCount >= stage.total,
|
|
419
|
+
allDone: allFinished && cachedCount < stage.total
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function getStepStatus(stepNum, stage) {
|
|
423
|
+
for (const [bkNum, dockerStep] of stage.stepNumMap.entries()) if (dockerStep === stepNum) {
|
|
424
|
+
if (stage.cachedStepNums.has(bkNum)) return "cached";
|
|
425
|
+
if (stage.doneStepNums.has(bkNum)) return "done";
|
|
426
|
+
return "running";
|
|
427
|
+
}
|
|
428
|
+
return "pending";
|
|
429
|
+
}
|
|
430
|
+
function StepStatusIcon({ status, spinnerFrame }) {
|
|
431
|
+
switch (status) {
|
|
432
|
+
case "cached": return /* @__PURE__ */ jsx(Text, {
|
|
433
|
+
color: BRAND_COLOR,
|
|
434
|
+
children: "✓"
|
|
435
|
+
});
|
|
436
|
+
case "done": return /* @__PURE__ */ jsx(Text, {
|
|
437
|
+
color: "green",
|
|
438
|
+
children: "✓"
|
|
439
|
+
});
|
|
440
|
+
case "running": return /* @__PURE__ */ jsx(Text, {
|
|
441
|
+
color: "cyan",
|
|
442
|
+
children: SPINNER_FRAMES[spinnerFrame]
|
|
443
|
+
});
|
|
444
|
+
case "pending": return /* @__PURE__ */ jsx(Text, {
|
|
445
|
+
dimColor: true,
|
|
446
|
+
children: "·"
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function friendlyExtraTitle(title) {
|
|
451
|
+
if (/^exporting to image/i.test(title)) return "Código compilado, distribuindo no sistema da Veloz";
|
|
452
|
+
if (/^exporting to client/i.test(title)) return "Exportando resultado";
|
|
453
|
+
return title;
|
|
454
|
+
}
|
|
455
|
+
function BuildDashboard({ serviceName, stages, displayOrder, committedCount, platformMessages, stepDetails, spinnerText }) {
|
|
456
|
+
const [spinnerFrame, setSpinnerFrame] = useState(0);
|
|
457
|
+
useEffect(() => {
|
|
458
|
+
const timer = setInterval(() => {
|
|
459
|
+
setSpinnerFrame((f) => (f + 1) % SPINNER_FRAMES.length);
|
|
460
|
+
}, 80);
|
|
461
|
+
return () => clearInterval(timer);
|
|
462
|
+
}, []);
|
|
463
|
+
const visibleItems = displayOrder.slice(committedCount);
|
|
464
|
+
const showHeader = committedCount === 0;
|
|
465
|
+
const stageNames = displayOrder.filter((d) => d.kind === "stage").map((d) => d.name);
|
|
466
|
+
const maxNameLen = Math.max(...stageNames.map((n) => n.length), 4);
|
|
467
|
+
const allStagesComplete = stageNames.length > 0 && stageNames.every((name) => {
|
|
468
|
+
const stage = stages.get(name);
|
|
469
|
+
return stage ? getStageStats(stage).allFinished : false;
|
|
470
|
+
});
|
|
471
|
+
const hasRunningExtra = displayOrder.some((d) => d.kind === "extra" && d.step.status === "running");
|
|
472
|
+
const allComplete = displayOrder.length > 0 && displayOrder.every((item) => {
|
|
473
|
+
if (item.kind === "stage") {
|
|
474
|
+
const stage = stages.get(item.name);
|
|
475
|
+
return stage ? getStageStats(stage).allFinished : false;
|
|
476
|
+
}
|
|
477
|
+
return item.step.status === "done" || item.step.status === "cached";
|
|
478
|
+
});
|
|
479
|
+
let bottomSpinner = spinnerText;
|
|
480
|
+
if (allComplete) bottomSpinner = "Finalizando build...";
|
|
481
|
+
else if (allStagesComplete && hasRunningExtra) bottomSpinner = "Distribuindo...";
|
|
482
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
483
|
+
flexDirection: "column",
|
|
484
|
+
paddingLeft: 2,
|
|
485
|
+
children: [
|
|
486
|
+
showHeader && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
487
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
488
|
+
bold: true,
|
|
489
|
+
color: "cyan",
|
|
490
|
+
children: ["BUILD ", serviceName ? /* @__PURE__ */ jsxs(Text, {
|
|
491
|
+
dimColor: true,
|
|
492
|
+
children: [
|
|
493
|
+
"(",
|
|
494
|
+
serviceName,
|
|
495
|
+
")"
|
|
496
|
+
]
|
|
497
|
+
}) : null]
|
|
498
|
+
}),
|
|
499
|
+
platformMessages.map((msg, i) => /* @__PURE__ */ jsxs(Text, { children: [" ", msg] }, `pm-${i}`)),
|
|
500
|
+
displayOrder.length > 0 && /* @__PURE__ */ jsx(Text, { children: "" })
|
|
501
|
+
] }),
|
|
502
|
+
visibleItems.map((item, idx) => {
|
|
503
|
+
if (item.kind === "stage") {
|
|
504
|
+
const stage = stages.get(item.name);
|
|
505
|
+
const stats = getStageStats(stage);
|
|
506
|
+
const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
|
|
507
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
508
|
+
flexDirection: "column",
|
|
509
|
+
children: [
|
|
510
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
511
|
+
/* @__PURE__ */ jsx(Text, {
|
|
512
|
+
bold: true,
|
|
513
|
+
children: item.name.padEnd(maxNameLen)
|
|
514
|
+
}),
|
|
515
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
516
|
+
/* @__PURE__ */ jsx(ProgressBar, {
|
|
517
|
+
filled: stats.finishedCount,
|
|
518
|
+
total: stage.total,
|
|
519
|
+
allCached: stats.allCached,
|
|
520
|
+
allDone: stats.allDone
|
|
521
|
+
})
|
|
522
|
+
] }),
|
|
523
|
+
sortedSteps.map(([stepNum, command]) => {
|
|
524
|
+
const status$1 = getStepStatus(stepNum, stage);
|
|
525
|
+
const detail = stepDetails.get(`${item.name}-${stepNum}`);
|
|
526
|
+
const isDone$1 = status$1 === "done" || status$1 === "cached";
|
|
527
|
+
const isRunning$1 = status$1 === "running";
|
|
528
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
529
|
+
flexDirection: "column",
|
|
530
|
+
children: [/* @__PURE__ */ jsxs(Box, { children: [
|
|
531
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
532
|
+
/* @__PURE__ */ jsx(StepStatusIcon, {
|
|
533
|
+
status: status$1,
|
|
534
|
+
spinnerFrame
|
|
535
|
+
}),
|
|
536
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
537
|
+
dimColor: isDone$1,
|
|
538
|
+
children: [
|
|
539
|
+
" ",
|
|
540
|
+
command,
|
|
541
|
+
status$1 === "cached" ? /* @__PURE__ */ jsx(Text, {
|
|
542
|
+
color: BRAND_COLOR,
|
|
543
|
+
children: " (veloz cache)"
|
|
544
|
+
}) : isDone$1 && detail?.duration ? /* @__PURE__ */ jsxs(Text, {
|
|
545
|
+
dimColor: true,
|
|
546
|
+
children: [" ", detail.duration]
|
|
547
|
+
}) : null
|
|
548
|
+
]
|
|
549
|
+
})
|
|
550
|
+
] }), isRunning$1 && detail && detail.lastLines.length > 0 && /* @__PURE__ */ jsx(Box, {
|
|
551
|
+
flexDirection: "column",
|
|
552
|
+
children: detail.lastLines.map((line, li) => /* @__PURE__ */ jsxs(Text, {
|
|
553
|
+
dimColor: true,
|
|
554
|
+
children: [" ", line]
|
|
555
|
+
}, li))
|
|
556
|
+
})]
|
|
557
|
+
}, stepNum);
|
|
558
|
+
}),
|
|
559
|
+
idx < visibleItems.length - 1 && /* @__PURE__ */ jsx(Text, { children: "" })
|
|
560
|
+
]
|
|
561
|
+
}, item.name);
|
|
562
|
+
}
|
|
563
|
+
const step = item.step;
|
|
564
|
+
const isDone = step.status === "done" || step.status === "cached";
|
|
565
|
+
const isRunning = step.status === "running";
|
|
566
|
+
const status = step.status === "error" ? "running" : step.status;
|
|
567
|
+
const title = friendlyExtraTitle(step.title);
|
|
568
|
+
const lastLines = step.lines.slice(-MAX_OUTPUT_LINES).map((l) => l.content);
|
|
569
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
570
|
+
flexDirection: "column",
|
|
571
|
+
children: [/* @__PURE__ */ jsxs(Box, { children: [
|
|
572
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
573
|
+
/* @__PURE__ */ jsx(StepStatusIcon, {
|
|
574
|
+
status,
|
|
575
|
+
spinnerFrame
|
|
576
|
+
}),
|
|
577
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
578
|
+
dimColor: isDone,
|
|
579
|
+
children: [
|
|
580
|
+
" ",
|
|
581
|
+
title,
|
|
582
|
+
isDone && step.duration && step.duration !== "cached" ? /* @__PURE__ */ jsxs(Text, {
|
|
583
|
+
dimColor: true,
|
|
584
|
+
children: [" ", step.duration]
|
|
585
|
+
}) : null
|
|
586
|
+
]
|
|
587
|
+
})
|
|
588
|
+
] }), isRunning && lastLines.length > 0 && /* @__PURE__ */ jsx(Box, {
|
|
589
|
+
flexDirection: "column",
|
|
590
|
+
children: lastLines.map((line, li) => /* @__PURE__ */ jsxs(Text, {
|
|
591
|
+
dimColor: true,
|
|
592
|
+
children: [" ", line]
|
|
593
|
+
}, li))
|
|
594
|
+
})]
|
|
595
|
+
}, `extra-${idx}`);
|
|
596
|
+
}),
|
|
597
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
598
|
+
marginTop: 1,
|
|
599
|
+
children: [/* @__PURE__ */ jsx(AnimatedSpinner, {}), /* @__PURE__ */ jsxs(Text, { children: [" ", bottomSpinner] })]
|
|
600
|
+
})
|
|
601
|
+
]
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
function DeployTUIApp({ stream, serviceName, resultRef }) {
|
|
605
|
+
const { exit } = useApp();
|
|
606
|
+
const [, forceUpdate] = useState(0);
|
|
607
|
+
const [staticLines, setStaticLines] = useState([]);
|
|
608
|
+
const [shouldExit, setShouldExit] = useState(false);
|
|
609
|
+
const stateRef = useRef({
|
|
610
|
+
stages: /* @__PURE__ */ new Map(),
|
|
611
|
+
displayOrder: [],
|
|
612
|
+
platformMessages: [],
|
|
613
|
+
stepDetails: /* @__PURE__ */ new Map(),
|
|
614
|
+
phase: "waiting",
|
|
615
|
+
finalStatus: "",
|
|
616
|
+
committedCount: 0,
|
|
617
|
+
headerCommitted: false
|
|
618
|
+
});
|
|
619
|
+
useEffect(() => {
|
|
620
|
+
if (shouldExit) exit();
|
|
621
|
+
}, [shouldExit, exit]);
|
|
622
|
+
useEffect(() => {
|
|
623
|
+
const state$1 = stateRef.current;
|
|
624
|
+
const allLogs = [];
|
|
625
|
+
let accumulatedBuildLogs = "";
|
|
626
|
+
let runtimeLogId = 0;
|
|
627
|
+
function applyBuildParse() {
|
|
628
|
+
const result = buildStepsToStages(parseBuildSteps(accumulatedBuildLogs));
|
|
629
|
+
state$1.stages = result.stages;
|
|
630
|
+
state$1.displayOrder = result.displayOrder;
|
|
631
|
+
state$1.platformMessages = result.platformMessages;
|
|
632
|
+
state$1.stepDetails = result.stepDetails;
|
|
633
|
+
}
|
|
634
|
+
/** Check if a display item is fully complete */
|
|
635
|
+
function isItemComplete(item) {
|
|
636
|
+
if (item.kind === "stage") {
|
|
637
|
+
const stage = state$1.stages.get(item.name);
|
|
638
|
+
return stage ? getStageStats(stage).allFinished : false;
|
|
639
|
+
}
|
|
640
|
+
return item.step.status === "done" || item.step.status === "cached";
|
|
641
|
+
}
|
|
642
|
+
/** Progressively commit completed leading items to Static for scrolling */
|
|
643
|
+
function commitCompleted() {
|
|
644
|
+
const newItems = [];
|
|
645
|
+
if (!state$1.headerCommitted && state$1.displayOrder.length > 0) {
|
|
646
|
+
state$1.headerCommitted = true;
|
|
647
|
+
newItems.push({
|
|
648
|
+
id: "build-header",
|
|
649
|
+
node: /* @__PURE__ */ jsxs(Text, {
|
|
650
|
+
bold: true,
|
|
651
|
+
color: "cyan",
|
|
652
|
+
children: [" BUILD ", serviceName ? /* @__PURE__ */ jsxs(Text, {
|
|
653
|
+
dimColor: true,
|
|
654
|
+
children: [
|
|
655
|
+
"(",
|
|
656
|
+
serviceName,
|
|
657
|
+
")"
|
|
658
|
+
]
|
|
659
|
+
}) : null]
|
|
660
|
+
})
|
|
661
|
+
});
|
|
662
|
+
for (const [i, msg] of state$1.platformMessages.entries()) newItems.push({
|
|
663
|
+
id: `platform-${i}`,
|
|
664
|
+
node: /* @__PURE__ */ jsxs(Text, { children: [" ", msg] })
|
|
665
|
+
});
|
|
666
|
+
newItems.push({
|
|
667
|
+
id: "header-gap",
|
|
668
|
+
node: /* @__PURE__ */ jsx(Text, { children: "" })
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
const stageNames = state$1.displayOrder.filter((d) => d.kind === "stage").map((d) => d.name);
|
|
672
|
+
const maxNameLen = Math.max(...stageNames.map((n) => n.length), 4);
|
|
673
|
+
for (let i = state$1.committedCount; i < state$1.displayOrder.length; i++) {
|
|
674
|
+
const item = state$1.displayOrder[i];
|
|
675
|
+
if (!isItemComplete(item)) break;
|
|
676
|
+
if (item.kind === "stage") {
|
|
677
|
+
const stage = state$1.stages.get(item.name);
|
|
678
|
+
const stats = getStageStats(stage);
|
|
679
|
+
const counter = `${stats.finishedCount}/${stage.total}`;
|
|
680
|
+
const barNode = stats.allCached ? /* @__PURE__ */ jsxs(Text, {
|
|
681
|
+
color: BRAND_COLOR,
|
|
682
|
+
children: [
|
|
683
|
+
"━".repeat(BAR_WIDTH),
|
|
684
|
+
" ",
|
|
685
|
+
counter,
|
|
686
|
+
" ✓ veloz cache"
|
|
687
|
+
]
|
|
688
|
+
}) : /* @__PURE__ */ jsxs(Text, {
|
|
689
|
+
color: "green",
|
|
690
|
+
children: [
|
|
691
|
+
"━".repeat(BAR_WIDTH),
|
|
692
|
+
" ",
|
|
693
|
+
counter,
|
|
694
|
+
" ✓"
|
|
695
|
+
]
|
|
696
|
+
});
|
|
697
|
+
newItems.push({
|
|
698
|
+
id: `stage-${item.name}`,
|
|
699
|
+
node: /* @__PURE__ */ jsxs(Box, { children: [
|
|
700
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
701
|
+
bold: true,
|
|
702
|
+
children: [" ", item.name.padEnd(maxNameLen)]
|
|
703
|
+
}),
|
|
704
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
705
|
+
barNode
|
|
706
|
+
] })
|
|
707
|
+
});
|
|
708
|
+
const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
|
|
709
|
+
for (const [stepNum, command] of sortedSteps) {
|
|
710
|
+
const stepStatus = getStepStatus(stepNum, stage);
|
|
711
|
+
const detail = state$1.stepDetails.get(`${item.name}-${stepNum}`);
|
|
712
|
+
const icon = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
|
|
713
|
+
color: BRAND_COLOR,
|
|
714
|
+
children: "✓"
|
|
715
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
716
|
+
color: "green",
|
|
717
|
+
children: "✓"
|
|
718
|
+
});
|
|
719
|
+
const suffix = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
|
|
720
|
+
color: BRAND_COLOR,
|
|
721
|
+
children: " (veloz cache)"
|
|
722
|
+
}) : detail?.duration ? /* @__PURE__ */ jsxs(Text, {
|
|
723
|
+
dimColor: true,
|
|
724
|
+
children: [" ", detail.duration]
|
|
725
|
+
}) : null;
|
|
726
|
+
newItems.push({
|
|
727
|
+
id: `step-${item.name}-${stepNum}`,
|
|
728
|
+
node: /* @__PURE__ */ jsxs(Text, { children: [
|
|
729
|
+
" ",
|
|
730
|
+
icon,
|
|
731
|
+
" ",
|
|
732
|
+
/* @__PURE__ */ jsx(Text, {
|
|
733
|
+
dimColor: true,
|
|
734
|
+
children: command
|
|
735
|
+
}),
|
|
736
|
+
suffix
|
|
737
|
+
] })
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
} else {
|
|
741
|
+
const step = item.step;
|
|
742
|
+
const title = friendlyExtraTitle(step.title);
|
|
743
|
+
const duration = step.duration && step.duration !== "cached" ? ` ${step.duration}` : "";
|
|
744
|
+
newItems.push({
|
|
745
|
+
id: `extra-${i}`,
|
|
746
|
+
node: /* @__PURE__ */ jsxs(Text, { children: [
|
|
747
|
+
" ",
|
|
748
|
+
/* @__PURE__ */ jsx(Text, {
|
|
749
|
+
color: "green",
|
|
750
|
+
children: "✓"
|
|
751
|
+
}),
|
|
752
|
+
" ",
|
|
753
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
754
|
+
dimColor: true,
|
|
755
|
+
children: [title, duration]
|
|
756
|
+
})
|
|
757
|
+
] })
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
newItems.push({
|
|
761
|
+
id: `gap-${i}`,
|
|
762
|
+
node: /* @__PURE__ */ jsx(Text, { children: "" })
|
|
763
|
+
});
|
|
764
|
+
state$1.committedCount = i + 1;
|
|
765
|
+
}
|
|
766
|
+
if (newItems.length > 0) setStaticLines((prev) => [...prev, ...newItems]);
|
|
767
|
+
}
|
|
768
|
+
/** Force-commit all remaining items (even if not complete) — used at phase transitions */
|
|
769
|
+
function commitAllRemaining() {
|
|
770
|
+
const newItems = [];
|
|
771
|
+
const stageNames = state$1.displayOrder.filter((d) => d.kind === "stage").map((d) => d.name);
|
|
772
|
+
const maxNameLen = Math.max(...stageNames.map((n) => n.length), 4);
|
|
773
|
+
for (let i = state$1.committedCount; i < state$1.displayOrder.length; i++) {
|
|
774
|
+
const item = state$1.displayOrder[i];
|
|
775
|
+
if (item.kind === "stage") {
|
|
776
|
+
const stage = state$1.stages.get(item.name);
|
|
777
|
+
const stats = getStageStats(stage);
|
|
778
|
+
const counter = `${stats.finishedCount}/${stage.total}`;
|
|
779
|
+
const barNode = stats.allCached ? /* @__PURE__ */ jsxs(Text, {
|
|
780
|
+
color: BRAND_COLOR,
|
|
781
|
+
children: [
|
|
782
|
+
"━".repeat(BAR_WIDTH),
|
|
783
|
+
" ",
|
|
784
|
+
counter,
|
|
785
|
+
" ✓ veloz cache"
|
|
786
|
+
]
|
|
787
|
+
}) : stats.allDone ? /* @__PURE__ */ jsxs(Text, {
|
|
788
|
+
color: "green",
|
|
789
|
+
children: [
|
|
790
|
+
"━".repeat(BAR_WIDTH),
|
|
791
|
+
" ",
|
|
792
|
+
counter,
|
|
793
|
+
" ✓"
|
|
794
|
+
]
|
|
795
|
+
}) : /* @__PURE__ */ jsxs(Text, { children: [
|
|
796
|
+
/* @__PURE__ */ jsx(Text, {
|
|
797
|
+
color: "cyan",
|
|
798
|
+
children: "━".repeat(Math.round(stats.finishedCount / Math.max(stage.total, 1) * BAR_WIDTH))
|
|
799
|
+
}),
|
|
800
|
+
/* @__PURE__ */ jsx(Text, {
|
|
801
|
+
dimColor: true,
|
|
802
|
+
children: "─".repeat(BAR_WIDTH - Math.round(stats.finishedCount / Math.max(stage.total, 1) * BAR_WIDTH))
|
|
803
|
+
}),
|
|
804
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
805
|
+
dimColor: true,
|
|
806
|
+
children: [" ", counter]
|
|
807
|
+
})
|
|
808
|
+
] });
|
|
809
|
+
newItems.push({
|
|
810
|
+
id: `stage-${item.name}`,
|
|
811
|
+
node: /* @__PURE__ */ jsxs(Box, { children: [
|
|
812
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
813
|
+
bold: true,
|
|
814
|
+
children: [" ", item.name.padEnd(maxNameLen)]
|
|
815
|
+
}),
|
|
816
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
817
|
+
barNode
|
|
818
|
+
] })
|
|
819
|
+
});
|
|
820
|
+
const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
|
|
821
|
+
for (const [stepNum, command] of sortedSteps) {
|
|
822
|
+
const stepStatus = getStepStatus(stepNum, stage);
|
|
823
|
+
const detail = state$1.stepDetails.get(`${item.name}-${stepNum}`);
|
|
824
|
+
const icon = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
|
|
825
|
+
color: BRAND_COLOR,
|
|
826
|
+
children: "✓"
|
|
827
|
+
}) : stepStatus === "done" || stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
|
|
828
|
+
color: "green",
|
|
829
|
+
children: "✓"
|
|
830
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
831
|
+
dimColor: true,
|
|
832
|
+
children: "·"
|
|
833
|
+
});
|
|
834
|
+
const suffix = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
|
|
835
|
+
color: BRAND_COLOR,
|
|
836
|
+
children: " (veloz cache)"
|
|
837
|
+
}) : detail?.duration ? /* @__PURE__ */ jsxs(Text, {
|
|
838
|
+
dimColor: true,
|
|
839
|
+
children: [" ", detail.duration]
|
|
840
|
+
}) : null;
|
|
841
|
+
newItems.push({
|
|
842
|
+
id: `step-${item.name}-${stepNum}`,
|
|
843
|
+
node: /* @__PURE__ */ jsxs(Text, { children: [
|
|
844
|
+
" ",
|
|
845
|
+
icon,
|
|
846
|
+
" ",
|
|
847
|
+
/* @__PURE__ */ jsx(Text, {
|
|
848
|
+
dimColor: true,
|
|
849
|
+
children: command
|
|
850
|
+
}),
|
|
851
|
+
suffix
|
|
852
|
+
] })
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
} else {
|
|
856
|
+
const step = item.step;
|
|
857
|
+
const isDone = step.status === "done" || step.status === "cached";
|
|
858
|
+
const title = friendlyExtraTitle(step.title);
|
|
859
|
+
const duration = isDone && step.duration && step.duration !== "cached" ? ` ${step.duration}` : "";
|
|
860
|
+
const icon = isDone ? /* @__PURE__ */ jsx(Text, {
|
|
861
|
+
color: "green",
|
|
862
|
+
children: "✓"
|
|
863
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
864
|
+
dimColor: true,
|
|
865
|
+
children: "·"
|
|
866
|
+
});
|
|
867
|
+
newItems.push({
|
|
868
|
+
id: `extra-${i}`,
|
|
869
|
+
node: /* @__PURE__ */ jsxs(Text, { children: [
|
|
870
|
+
" ",
|
|
871
|
+
icon,
|
|
872
|
+
" ",
|
|
873
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
874
|
+
dimColor: true,
|
|
875
|
+
children: [title, duration]
|
|
876
|
+
})
|
|
877
|
+
] })
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
newItems.push({
|
|
881
|
+
id: `gap-${i}`,
|
|
882
|
+
node: /* @__PURE__ */ jsx(Text, { children: "" })
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
state$1.committedCount = state$1.displayOrder.length;
|
|
886
|
+
return newItems;
|
|
887
|
+
}
|
|
888
|
+
const consume = async () => {
|
|
889
|
+
try {
|
|
890
|
+
for await (const event of stream) if (event.type === "status") {
|
|
891
|
+
state$1.finalStatus = event.content;
|
|
892
|
+
switch (event.content) {
|
|
893
|
+
case "BUILDING":
|
|
894
|
+
state$1.phase = "building";
|
|
895
|
+
forceUpdate((n) => n + 1);
|
|
896
|
+
break;
|
|
897
|
+
case "DEPLOYING":
|
|
898
|
+
if (accumulatedBuildLogs) applyBuildParse();
|
|
899
|
+
for (const stage of state$1.stages.values()) if (stage.steps.size > 0 && stage.steps.size < stage.total) stage.total = stage.steps.size;
|
|
900
|
+
commitCompleted();
|
|
901
|
+
if (state$1.committedCount < state$1.displayOrder.length) {
|
|
902
|
+
const remaining = commitAllRemaining();
|
|
903
|
+
if (remaining.length > 0) setStaticLines((prev) => [...prev, ...remaining]);
|
|
904
|
+
}
|
|
905
|
+
state$1.phase = "deploying";
|
|
906
|
+
forceUpdate((n) => n + 1);
|
|
907
|
+
break;
|
|
908
|
+
case "LIVE":
|
|
909
|
+
setStaticLines((prev) => [...prev, {
|
|
910
|
+
id: "live-status",
|
|
911
|
+
node: /* @__PURE__ */ jsxs(Text, {
|
|
912
|
+
color: "green",
|
|
913
|
+
children: [" ", "✓ Publicado"]
|
|
914
|
+
})
|
|
915
|
+
}]);
|
|
916
|
+
state$1.phase = "live";
|
|
917
|
+
forceUpdate((n) => n + 1);
|
|
918
|
+
break;
|
|
919
|
+
default: if (TERMINAL_STATUSES.has(event.content)) {
|
|
920
|
+
if (accumulatedBuildLogs) applyBuildParse();
|
|
921
|
+
if (state$1.phase === "building" || state$1.phase === "waiting") {
|
|
922
|
+
commitCompleted();
|
|
923
|
+
if (state$1.committedCount < state$1.displayOrder.length) {
|
|
924
|
+
const remaining = commitAllRemaining();
|
|
925
|
+
if (remaining.length > 0) setStaticLines((prev) => [...prev, ...remaining]);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
state$1.phase = "failed";
|
|
929
|
+
forceUpdate((n) => n + 1);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
} else if (event.type === "log") {
|
|
933
|
+
const lines = event.content.split("\n");
|
|
934
|
+
allLogs.push(...lines);
|
|
935
|
+
if (state$1.phase === "deploying" || state$1.phase === "live") {
|
|
936
|
+
for (const line of lines) {
|
|
937
|
+
const trimmed = line.trim();
|
|
938
|
+
if (!trimmed) continue;
|
|
939
|
+
const parsed = parseBuildLine(trimmed);
|
|
940
|
+
let text = null;
|
|
941
|
+
if (parsed.kind === "platform") text = parsed.message;
|
|
942
|
+
else if (parsed.kind === "other" && parsed.text) text = parsed.text;
|
|
943
|
+
else if (parsed.kind === "output") text = parsed.text;
|
|
944
|
+
if (text && !isHiddenMessage(text)) {
|
|
945
|
+
const logId = runtimeLogId++;
|
|
946
|
+
const newItems = [];
|
|
947
|
+
if (logId === 0) newItems.push({
|
|
948
|
+
id: "runtime-header",
|
|
949
|
+
node: /* @__PURE__ */ jsx(Text, {
|
|
950
|
+
bold: true,
|
|
951
|
+
color: "cyan",
|
|
952
|
+
children: "\n RUNTIME"
|
|
953
|
+
})
|
|
954
|
+
});
|
|
955
|
+
newItems.push({
|
|
956
|
+
id: `log-${logId}`,
|
|
957
|
+
node: /* @__PURE__ */ jsxs(Text, { children: [" ", text] })
|
|
958
|
+
});
|
|
959
|
+
setStaticLines((prev) => [...prev, ...newItems]);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
if (state$1.phase === "waiting") state$1.phase = "building";
|
|
965
|
+
if (accumulatedBuildLogs && !accumulatedBuildLogs.endsWith("\n")) accumulatedBuildLogs += "\n";
|
|
966
|
+
accumulatedBuildLogs += event.content;
|
|
967
|
+
applyBuildParse();
|
|
968
|
+
commitCompleted();
|
|
969
|
+
forceUpdate((n) => n + 1);
|
|
970
|
+
}
|
|
971
|
+
} catch {}
|
|
972
|
+
resultRef.current = {
|
|
973
|
+
status: state$1.finalStatus,
|
|
974
|
+
logs: allLogs.filter((l) => l.trim())
|
|
975
|
+
};
|
|
976
|
+
setShouldExit(true);
|
|
977
|
+
};
|
|
978
|
+
consume();
|
|
979
|
+
}, [
|
|
980
|
+
stream,
|
|
981
|
+
serviceName,
|
|
982
|
+
resultRef,
|
|
983
|
+
exit,
|
|
984
|
+
setShouldExit
|
|
985
|
+
]);
|
|
986
|
+
const state = stateRef.current;
|
|
987
|
+
const showBuild = state.phase === "waiting" || state.phase === "building";
|
|
988
|
+
const showDeploySpinner = state.phase === "deploying";
|
|
989
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
990
|
+
flexDirection: "column",
|
|
991
|
+
children: [
|
|
992
|
+
/* @__PURE__ */ jsx(Static, {
|
|
993
|
+
items: staticLines,
|
|
994
|
+
children: (line) => /* @__PURE__ */ jsx(Box, { children: line.node }, line.id)
|
|
995
|
+
}),
|
|
996
|
+
showBuild && /* @__PURE__ */ jsx(BuildDashboard, {
|
|
997
|
+
serviceName,
|
|
998
|
+
stages: state.stages,
|
|
999
|
+
displayOrder: state.displayOrder,
|
|
1000
|
+
committedCount: state.committedCount,
|
|
1001
|
+
platformMessages: state.platformMessages,
|
|
1002
|
+
stepDetails: state.stepDetails,
|
|
1003
|
+
spinnerText: state.phase === "waiting" ? "Aguardando início do build..." : "Compilando..."
|
|
1004
|
+
}),
|
|
1005
|
+
showDeploySpinner && /* @__PURE__ */ jsxs(Box, {
|
|
1006
|
+
paddingLeft: 2,
|
|
1007
|
+
children: [/* @__PURE__ */ jsx(AnimatedSpinner, {}), /* @__PURE__ */ jsx(Text, { children: " Publicando..." })]
|
|
1008
|
+
})
|
|
1009
|
+
]
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
async function fetchDeployUrls(client, serviceId) {
|
|
1013
|
+
try {
|
|
1014
|
+
return (await client.domains.list({ serviceId })).map((d) => `https://${d.domain}`);
|
|
1015
|
+
} catch {
|
|
1016
|
+
return [];
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function getFailureHints(status) {
|
|
1020
|
+
switch (status) {
|
|
1021
|
+
case "BUILD_FAILED": return [
|
|
1022
|
+
"Verifique os logs de build acima para erros de compilação",
|
|
1023
|
+
"Teste o build localmente: rode o comando de build do seu projeto",
|
|
1024
|
+
"Use 'veloz builds list' e 'veloz builds logs <id>' para revisar o build",
|
|
1025
|
+
"Use 'veloz config show' para verificar as configurações"
|
|
1026
|
+
];
|
|
1027
|
+
case "DEPLOY_FAILED": return [
|
|
1028
|
+
"O build passou mas o serviço falhou ao iniciar",
|
|
1029
|
+
"Verifique se a porta configurada está correta: 'veloz config show'",
|
|
1030
|
+
"Veja os logs de runtime: 'veloz logs -f'"
|
|
1031
|
+
];
|
|
1032
|
+
case "CANCELLED": return ["Deploy cancelado. Execute 'veloz deploy' para tentar novamente."];
|
|
1033
|
+
default: return ["Execute 'veloz logs -f' para mais detalhes."];
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
async function renderDeployTUI(deploymentId, serviceId, serviceName) {
|
|
1037
|
+
const client = await getClient();
|
|
1038
|
+
const stream = await client.logs.streamBuildLogs({ deploymentId });
|
|
1039
|
+
const resultRef = { current: {
|
|
1040
|
+
status: "",
|
|
1041
|
+
logs: []
|
|
1042
|
+
} };
|
|
1043
|
+
const instance = render(/* @__PURE__ */ jsx(DeployTUIApp, {
|
|
1044
|
+
stream,
|
|
1045
|
+
serviceName,
|
|
1046
|
+
resultRef
|
|
1047
|
+
}), {
|
|
1048
|
+
exitOnCtrlC: false,
|
|
1049
|
+
patchConsole: false
|
|
1050
|
+
});
|
|
1051
|
+
try {
|
|
1052
|
+
await instance.waitUntilExit();
|
|
1053
|
+
} catch {
|
|
1054
|
+
instance.unmount();
|
|
1055
|
+
}
|
|
1056
|
+
if (!resultRef.current.status) try {
|
|
1057
|
+
const d = await client.deployments.get({ deploymentId });
|
|
1058
|
+
resultRef.current.status = d.status;
|
|
1059
|
+
try {
|
|
1060
|
+
const logs = await client.logs.getBuildLogs({ deploymentId });
|
|
1061
|
+
if (logs.buildLogs) resultRef.current.logs.push(...logs.buildLogs.split("\n"));
|
|
1062
|
+
} catch {}
|
|
1063
|
+
} catch {}
|
|
1064
|
+
const urls = resultRef.current.status === "LIVE" ? await fetchDeployUrls(client, serviceId) : [];
|
|
1065
|
+
const finalStatus = resultRef.current.status;
|
|
1066
|
+
if (finalStatus === "LIVE") {
|
|
1067
|
+
success(serviceName ? `Deploy de ${chalk.bold(serviceName)} concluído! Serviço ativo.` : "Deploy concluído! Serviço ativo.");
|
|
1068
|
+
for (const url of urls) info(chalk.bold(url));
|
|
1069
|
+
} else if (TERMINAL_STATUSES.has(finalStatus)) {
|
|
1070
|
+
const label = statusLabels[finalStatus] ?? finalStatus;
|
|
1071
|
+
const hints = getFailureHints(finalStatus);
|
|
1072
|
+
const allLogs = resultRef.current.logs;
|
|
1073
|
+
if (allLogs.length > 0) {
|
|
1074
|
+
process.stderr.write("\n");
|
|
1075
|
+
process.stderr.write(chalk.red(` ${"─".repeat(50)}`) + "\n");
|
|
1076
|
+
process.stderr.write(chalk.red.bold(" Logs de build:") + "\n");
|
|
1077
|
+
process.stderr.write(chalk.red(` ${"─".repeat(50)}`) + "\n");
|
|
1078
|
+
for (const line of allLogs) if (line.trim()) process.stderr.write(chalk.dim(` ${line}`) + "\n");
|
|
1079
|
+
process.stderr.write(chalk.red(` ${"─".repeat(50)}`) + "\n");
|
|
1080
|
+
}
|
|
1081
|
+
const errorMsg = serviceName ? `Deploy de ${chalk.bold(serviceName)} finalizou: ${label}` : `Deploy finalizou: ${label}`;
|
|
1082
|
+
process.stderr.write(chalk.red(`\n✗ ${errorMsg}`) + "\n");
|
|
1083
|
+
for (const hint of hints) process.stderr.write(chalk.yellow(` → ${hint}`) + "\n");
|
|
1084
|
+
process.stderr.write(chalk.yellow(` → Use 'veloz builds logs ${deploymentId}' para ver os logs completos`) + "\n");
|
|
1085
|
+
process.exit(1);
|
|
1086
|
+
}
|
|
1087
|
+
return {
|
|
1088
|
+
deploymentId,
|
|
1089
|
+
status: finalStatus,
|
|
1090
|
+
logs: resultRef.current.logs.filter((l) => l.trim()),
|
|
1091
|
+
urls,
|
|
1092
|
+
serviceName
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
//#endregion
|
|
1097
|
+
export { renderDeployTUI };
|