Login
4 branches 0 tags
Ben (Desktop/Arch) CI e8a9e18 15 days ago 237 Commits
rubhub / frontend / ci.ts
interface CiJobResultResponse {
	name: string;
	status: string;
	exit_code: number | null;
	stdout: string;
	stderr: string;
}

interface CiRunStatusResponse {
	id: string;
	status: string;
	jobs: CiJobResultResponse[];
}

export function initCiLogPolling() {
	const container = document.getElementById("ci-jobs-container");
	if (!container) return;

	const statusUrl = container.dataset.statusUrl;
	const initialStatus = container.dataset.initialStatus;
	if (!statusUrl) return;

	// Don't poll if already finished - but still do one fetch to load logs
	const isFinished = initialStatus === "success" || initialStatus === "failed";

	async function fetchAndUpdate(): Promise<boolean> {
		try {
			const res = await fetch(statusUrl);
			if (!res.ok) return true; // Stop polling on error

			const data: CiRunStatusResponse = await res.json();

			// Update overall status badge
			const statusBadge = document.getElementById("run-status");
			if (statusBadge) {
				statusBadge.textContent = data.status;
				statusBadge.className = `issue-status status-${data.status}`;
			}

			// Update each job's logs and status
			for (const job of data.jobs) {
				const jobEl = container?.querySelector(
					`[data-job-name="${job.name}"]`,
				);
				if (!jobEl) continue;

				// Update job status
				const statusEl = jobEl.querySelector(".ci-job-status");
				if (statusEl) {
					statusEl.textContent = job.status;
					statusEl.className = `ci-job-status status-${job.status}`;
				}

				// Update logs
				const stdoutEl =
					jobEl.querySelector<HTMLElement>(".ci-log-stdout");
				const stderrEl =
					jobEl.querySelector<HTMLElement>(".ci-log-stderr");

				if (stdoutEl) {
					stdoutEl.textContent = job.stdout || "(no output)";
				}
				if (stderrEl) {
					if (job.stderr) {
						stderrEl.textContent = job.stderr;
						stderrEl.style.display = "block";
					} else {
						stderrEl.style.display = "none";
					}
				}
			}

			// Return true if we should stop polling (finished)
			return data.status === "success" || data.status === "failed";
		} catch {
			// Stop polling on exception - user can refresh if needed
			return true;
		}
	}

	async function poll() {
		const shouldStop = await fetchAndUpdate();
		if (!shouldStop) {
			// Schedule next poll after 1 second
			// Using setTimeout instead of setInterval ensures we don't pile up
			// requests on slow connections
			setTimeout(poll, 1000);
		}
	}

	if (isFinished) {
		// Just load logs once, don't poll
		fetchAndUpdate();
	} else {
		// Start polling
		poll();
	}
}