feat(deploy): stream live build logs in build progress modal
Show real-time kaniko/init-container build output inside the build progress modal during the building/deploying phases, alongside the existing percentage. The build-logs endpoint now returns live pod logs while a build is in progress (falling back to the persisted log once finished), and the modal polls it every 2s with auto-scroll. Minimize behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import api from '@/lib/api';
|
||||
import { Loader2, Upload, Hammer, Rocket, CheckCircle, XCircle, X, Minimize2 } from 'lucide-react';
|
||||
import { notify } from '@/lib/notify';
|
||||
@@ -69,9 +70,29 @@ export function BuildProgressModal({
|
||||
const showBytes = progress.phase === 'uploading' && progress.totalBytes;
|
||||
const isCancelling = cancelMutation.isPending;
|
||||
|
||||
// Live build logs — only meaningful once the build pod exists (building/deploying).
|
||||
const showBuildLog = progress.phase === 'building' || progress.phase === 'deploying';
|
||||
const { data: buildLogsData } = useQuery<{ buildLog: string | null }>({
|
||||
queryKey: ['build-logs', appId],
|
||||
queryFn: () => api.get(`/deployments/applications/${appId}/build-logs`).then((r) => r.data),
|
||||
enabled: showBuildLog,
|
||||
refetchInterval: showBuildLog ? 2000 : false,
|
||||
});
|
||||
|
||||
const logRef = useRef<HTMLPreElement>(null);
|
||||
const buildLog = buildLogsData?.buildLog;
|
||||
useEffect(() => {
|
||||
const el = logRef.current;
|
||||
if (el) el.scrollTop = el.scrollHeight;
|
||||
}, [buildLog]);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||
<div className="relative bg-white rounded-2xl shadow-2xl p-8 max-w-md w-full mx-4 space-y-5">
|
||||
<div
|
||||
className={`relative bg-white rounded-2xl shadow-2xl p-8 w-full mx-4 space-y-5 ${
|
||||
showBuildLog ? 'max-w-2xl' : 'max-w-md'
|
||||
}`}
|
||||
>
|
||||
<div className="absolute top-4 right-4 rtl:right-auto rtl:left-4 flex items-center gap-1">
|
||||
{isActive && (
|
||||
<button
|
||||
@@ -129,6 +150,27 @@ export function BuildProgressModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showBuildLog && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-700 flex items-center gap-1.5">
|
||||
<Hammer className="w-4 h-4" /> {c.buildLogTitle}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400 flex items-center gap-1.5">
|
||||
<span className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
|
||||
{c.buildLogLive}
|
||||
</span>
|
||||
</div>
|
||||
<pre
|
||||
ref={logRef}
|
||||
className="bg-gray-900 text-blue-300 p-4 rounded-xl text-xs font-mono overflow-x-auto max-h-64 overflow-y-auto whitespace-pre-wrap break-words text-left rtl:text-left"
|
||||
dir="ltr"
|
||||
>
|
||||
{buildLog || c.buildLogWaiting}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{progress.phase === 'failed' && progress.message && (
|
||||
<p className="text-sm text-red-600 text-center">{progress.message}</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user