Skip to content

Commit f22dfea

Browse files
committed
add log date
1 parent 5a3e620 commit f22dfea

File tree

4 files changed

+96
-23
lines changed

4 files changed

+96
-23
lines changed

src/Terrabuild.UI/src/App.tsx

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const App = () => {
103103
Record<string, { x: number; y: number }>
104104
>({});
105105
const [draggedNodeId, setDraggedNodeId] = useState<string | null>(null);
106+
const [buildStartedAt, setBuildStartedAt] = useState<Date | null>(null);
106107
const [nodes, setNodes] = useNodesState([]);
107108
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
108109
const flowInstance = useRef<ReactFlowInstance | null>(null);
@@ -121,6 +122,7 @@ const App = () => {
121122
null
122123
);
123124
const pendingBuildLogRef = useRef(false);
125+
const lastApiNoticeRef = useRef(0);
124126
const applyTerminalTheme = () => {
125127
if (!terminal.current || !terminalReady.current) {
126128
return;
@@ -138,6 +140,19 @@ const App = () => {
138140
effectiveColorScheme === "dark" ? "#3b3f45" : "#c9d0d8",
139141
};
140142
};
143+
144+
const notifyApiUnavailable = () => {
145+
const now = Date.now();
146+
if (now - lastApiNoticeRef.current < 15000) {
147+
return;
148+
}
149+
lastApiNoticeRef.current = now;
150+
notifications.show({
151+
color: "red",
152+
title: "API unavailable",
153+
message: "Unable to reach the Terrabuild API.",
154+
});
155+
};
141156
const flushPendingTerminalActions = () => {
142157
if (!terminalReady.current) {
143158
return;
@@ -278,15 +293,23 @@ const App = () => {
278293

279294
useEffect(() => {
280295
const load = async () => {
281-
const [targetsRes, projectsRes] = await Promise.all([
282-
fetch("/api/targets"),
283-
fetch("/api/projects"),
284-
]);
285-
if (targetsRes.ok) {
286-
setTargets(await targetsRes.json());
287-
}
288-
if (projectsRes.ok) {
289-
setProjects(await projectsRes.json());
296+
try {
297+
const [targetsRes, projectsRes] = await Promise.all([
298+
fetch("/api/targets"),
299+
fetch("/api/projects"),
300+
]);
301+
if (targetsRes.ok) {
302+
setTargets(await targetsRes.json());
303+
} else {
304+
notifyApiUnavailable();
305+
}
306+
if (projectsRes.ok) {
307+
setProjects(await projectsRes.json());
308+
} else {
309+
notifyApiUnavailable();
310+
}
311+
} catch {
312+
notifyApiUnavailable();
290313
}
291314
};
292315
load().catch(() => null);
@@ -345,6 +368,7 @@ const App = () => {
345368
fetchGraph().catch(() => {
346369
setGraphError("Failed to load graph.");
347370
setGraph(null);
371+
notifyApiUnavailable();
348372
});
349373
}, [selectedTargets, selectedProjects, configuration, environment, engine]);
350374

@@ -510,7 +534,14 @@ const App = () => {
510534
const controller = new AbortController();
511535
logAbort.current = controller;
512536

513-
const response = await fetch("/api/build/log", { signal: controller.signal });
537+
let response: Response;
538+
try {
539+
response = await fetch("/api/build/log", { signal: controller.signal });
540+
} catch {
541+
notifyApiUnavailable();
542+
setBuildRunning(false);
543+
return;
544+
}
514545
if (!response.body) {
515546
return;
516547
}
@@ -632,6 +663,7 @@ const App = () => {
632663
message: "Build command copied to clipboard.",
633664
});
634665
} catch {
666+
notifyApiUnavailable();
635667
notifications.show({
636668
color: "red",
637669
title: "Copy failed",
@@ -647,13 +679,22 @@ const App = () => {
647679
setBuildRunning(true);
648680
setBuildError(null);
649681
const payload = buildPayload();
650-
const response = await fetch("/api/build", {
651-
method: "POST",
652-
headers: { "Content-Type": "application/json" },
653-
body: JSON.stringify(payload),
654-
});
682+
let response: Response;
683+
try {
684+
response = await fetch("/api/build", {
685+
method: "POST",
686+
headers: { "Content-Type": "application/json" },
687+
body: JSON.stringify(payload),
688+
});
689+
} catch {
690+
setBuildRunning(false);
691+
setBuildStartedAt(null);
692+
notifyApiUnavailable();
693+
return;
694+
}
655695
if (!response.ok) {
656696
setBuildRunning(false);
697+
setBuildStartedAt(null);
657698
const message = await response.text();
658699
setBuildError(message);
659700
notifications.show({
@@ -663,13 +704,15 @@ const App = () => {
663704
});
664705
return;
665706
}
707+
setBuildStartedAt(null);
666708
notifications.show({
667709
color: "blue",
668710
title: "Build started",
669711
message: "Build is running.",
670712
});
671713
startLogStream().catch(() => {
672714
setBuildRunning(false);
715+
notifyApiUnavailable();
673716
notifications.show({
674717
color: "red",
675718
title: "Build failed",
@@ -708,7 +751,9 @@ const App = () => {
708751
setProjectStatus(statusMap);
709752
}
710753
};
711-
refresh().catch(() => null);
754+
refresh().catch(() => {
755+
notifyApiUnavailable();
756+
});
712757
}, [
713758
buildRunning,
714759
selectedTargets,
@@ -749,6 +794,12 @@ const App = () => {
749794
if (!terminal.current) {
750795
return;
751796
}
797+
const summary = nodeResults[key];
798+
if (summary?.startedAt) {
799+
setBuildStartedAt(new Date(summary.startedAt));
800+
} else {
801+
setBuildStartedAt(null);
802+
}
752803
setSelectedTargetKey(key);
753804
terminal.current.reset();
754805
setShowTerminal(true);
@@ -779,6 +830,16 @@ const App = () => {
779830
await loadTargetLog(key, target);
780831
};
781832

833+
useEffect(() => {
834+
if (!selectedTargetKey) {
835+
return;
836+
}
837+
const summary = nodeResults[selectedTargetKey];
838+
if (summary?.startedAt) {
839+
setBuildStartedAt(new Date(summary.startedAt));
840+
}
841+
}, [selectedTargetKey, nodeResults]);
842+
782843
const handleNodesChange: OnNodesChange = (changes) => {
783844
setNodes((current) => {
784845
const updated = applyNodeChanges(changes, current);
@@ -796,6 +857,13 @@ const App = () => {
796857

797858
const terminalBackground =
798859
effectiveColorScheme === "dark" ? theme.colors.dark[7] : theme.white;
860+
const buildLogTitle = buildStartedAt
861+
? `Build Log ${buildStartedAt
862+
.toISOString()
863+
.replace("T", " ")
864+
.replace("Z", "")
865+
.slice(0, 19)}`
866+
: "Build Log";
799867

800868
return (
801869
<AppShell
@@ -917,6 +985,7 @@ const App = () => {
917985
<BuildLogPanel
918986
showTerminal={showTerminal}
919987
buildRunning={buildRunning}
988+
title={buildLogTitle}
920989
onHide={() => setShowTerminal(false)}
921990
terminalRef={terminalRef}
922991
background={terminalBackground}

src/Terrabuild.UI/src/components/BuildLogPanel.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { RefObject } from "react";
55
type BuildLogPanelProps = {
66
showTerminal: boolean;
77
buildRunning: boolean;
8+
title: string;
89
onHide: () => void;
910
terminalRef: RefObject<HTMLDivElement | null>;
1011
background: string;
@@ -13,6 +14,7 @@ type BuildLogPanelProps = {
1314
const BuildLogPanel = ({
1415
showTerminal,
1516
buildRunning,
17+
title,
1618
onHide,
1719
terminalRef,
1820
background,
@@ -35,7 +37,7 @@ const BuildLogPanel = ({
3537
>
3638
{showTerminal && (
3739
<Group justify="space-between" align="center" mb="sm">
38-
<Title order={4}>Build Log</Title>
40+
<Title order={4}>{title}</Title>
3941
<Group spacing="xs" align="center" justify="flex-end">
4042
<Badge color={buildRunning ? "orange" : "gray"}>
4143
{buildRunning ? "Live" : "Idle"}

src/Terrabuild/Helpers/Exec.fs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,18 @@ let execCaptureTimestampedOutput (workingDir: string) (command: string) (args: s
168168
try
169169
use logWriter = new StreamWriter(logFile)
170170
let writeLock = Lock()
171-
172-
let inline lockWrite (from: string) (msg: string | null) =
171+
let inline lockWrite (msg: string | null) =
173172
match msg with
174-
| NonNull msg -> lock writeLock (fun () -> logWriter.WriteLine($"{DateTime.UtcNow} {from} {msg}"))
173+
| NonNull msg ->
174+
lock writeLock (fun () ->
175+
logWriter.WriteLine(msg)
176+
)
175177
| _ -> ()
176178

177179
Log.Debug("Running and capturing timestamped output of '{Command}' with arguments '{Args}' in working dir '{WorkingDir}'", command, args, workingDir)
178180
use proc = createProcess workingDir command args envs true
179-
proc.OutputDataReceived.Add(fun e -> lockWrite "OUT" e.Data)
180-
proc.ErrorDataReceived.Add(fun e -> lockWrite "ERR" e.Data)
181+
proc.OutputDataReceived.Add(fun e -> lockWrite e.Data)
182+
proc.ErrorDataReceived.Add(fun e -> lockWrite e.Data)
181183
proc.BeginOutputReadLine()
182184
proc.BeginErrorReadLine()
183185
proc.WaitForExit()

src/Terrabuild/Web/GraphServer.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ let start (graphArgs: ParseResults<GraphArgs>) (logEnabled: bool) (debugEnabled:
582582
let content = IO.readTextFile step.Log
583583
if String.IsNullOrWhiteSpace(content) |> not then
584584
if builder.Length > 0 then builder.AppendLine().AppendLine() |> ignore
585-
builder.AppendLine(step.MetaCommand).AppendLine(content) |> ignore
585+
builder.AppendLine(content) |> ignore
586586
)
587587
let logText = builder.ToString()
588588
Results.Text(logText, "text/plain")

0 commit comments

Comments
 (0)