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