diff --git a/backend/src/build/build.service.ts b/backend/src/build/build.service.ts index a04f459..99555d7 100644 --- a/backend/src/build/build.service.ts +++ b/backend/src/build/build.service.ts @@ -1727,6 +1727,24 @@ CMD ["sh", "-c", "DLL=$(find . -maxdepth 1 -name '*.dll' ! -name '*.deps.dll' ! throw new Error(`Build job ${jobName} timed out after ${timeoutSeconds}s\nLogs:\n${logs}`); } + /** + * Live build logs for an in-progress build, read straight from the running + * build pod (init + kaniko containers). Returns null when there is no active + * build session for this deployment (e.g. build already finished/cleaned up), + * so callers can fall back to the persisted build log. + */ + async getLiveBuildLog(deploymentId: string): Promise { + const session = this.activeBuilds.get(deploymentId); + if (!session?.coreApi || !session.namespace || !session.buildPodName) { + return null; + } + try { + return await this.getBuildLogs(session.coreApi, session.buildPodName, session.namespace); + } catch { + return null; + } + } + private async getBuildLogs(coreApi: k8s.CoreV1Api, jobName: string, namespace: string): Promise { try { const pods = await coreApi.listNamespacedPod({ diff --git a/backend/src/deployments/deployments.service.ts b/backend/src/deployments/deployments.service.ts index 40e4070..8d6784c 100644 --- a/backend/src/deployments/deployments.service.ts +++ b/backend/src/deployments/deployments.service.ts @@ -507,8 +507,19 @@ export class DeploymentsService { }; } + // While the build is in progress the persisted buildLog isn't written yet — + // stream the live build pod logs so the deploy modal can show them in real time. + let buildLog = latest.buildLog || null; + if ( + latest.status === DeploymentStatus.BUILDING || + latest.status === DeploymentStatus.DEPLOYING + ) { + const live = await this.buildService.getLiveBuildLog(latest.id); + if (live) buildLog = live; + } + return { - buildLog: latest.buildLog || null, + buildLog, status: latest.status, version: latest.version, createdAt: latest.createdAt, diff --git a/frontend/src/components/build-progress-modal.tsx b/frontend/src/components/build-progress-modal.tsx index b013600..e501300 100644 --- a/frontend/src/components/build-progress-modal.tsx +++ b/frontend/src/components/build-progress-modal.tsx @@ -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(null); + const buildLog = buildLogsData?.buildLog; + useEffect(() => { + const el = logRef.current; + if (el) el.scrollTop = el.scrollHeight; + }, [buildLog]); + return (
-
+
{isActive && (