From dc8cbb48ca35753cca4529d0bfd228bdae883427 Mon Sep 17 00:00:00 2001 From: Ben Allfree Date: Fri, 10 Apr 2026 00:51:57 -0700 Subject: [PATCH] Refactor CI failure handling in RepoPage and update error messages in formatBuildErrorSummary. Improved user feedback for build retries and adjusted UI logic to show CI failure state more accurately. --- src/lib/formatBuildErrorSummary.ts | 6 ++-- src/pages/RepoPage.tsx | 47 ++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/lib/formatBuildErrorSummary.ts b/src/lib/formatBuildErrorSummary.ts index 71ae39e..35aabfb 100644 --- a/src/lib/formatBuildErrorSummary.ts +++ b/src/lib/formatBuildErrorSummary.ts @@ -32,7 +32,7 @@ export function buildFailurePresentation(summary: string | undefined): { if (/GitHub API failed:\s*5\d\d/.test(summary) || /GitHub API failed:\s*429/.test(summary)) { return { headline: 'GitHub was temporarily unavailable', - body: 'Starting the build failed because GitHub returned an error or rate limit. Use Retry build — it often works on a second try.', + body: 'Starting the build failed because GitHub returned an error or rate limit. Flash again — it often works on a second try.', } } if (/GitHub API failed:\s*4\d\d/.test(summary) && !summary.includes('422')) { @@ -50,7 +50,7 @@ export function buildFailurePresentation(summary: string | undefined): { return { headline: 'Build failed in CI', body: - 'Often a compile error, missing PlatformIO dependency, or bad env config in the repo. Fix the project if you can, then use Retry build. ' + - 'Transient CI issues also happen — retry is safe.', + 'Often a compile error, missing PlatformIO dependency, or bad env config in the repo. Fix the project if you can, then use Flash again. ' + + 'Transient CI issues also happen — trying again is safe.', } } diff --git a/src/pages/RepoPage.tsx b/src/pages/RepoPage.tsx index 5ec87a8..3a41535 100644 --- a/src/pages/RepoPage.tsx +++ b/src/pages/RepoPage.tsx @@ -3,7 +3,7 @@ import { api } from "@/convex/_generated/api" import { sortTagNames } from "@/convex/lib/tagSemver" import { useAction, useMutation, useQuery } from "convex/react" import { Github, Link2, RefreshCw } from "lucide-react" -import { useEffect, useLayoutEffect, useMemo, useState } from "react" +import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react" import ReactMarkdown from "react-markdown" import { Link, useNavigate, useParams } from "react-router-dom" import rehypeRaw from "rehype-raw" @@ -158,6 +158,39 @@ export default function RepoPage() { const buildKey = resolvedSha && resolvedTargetEnv ? normalizeBuildKey(resolvedSha, resolvedTargetEnv) : null const build = useQuery(api.repoBuilds.getByBuildKey, buildKey ? { buildKey } : "skip") + /** Only show CI failure UI if this tab saw the build move into `failed` (not for stale failures on load). */ + const [witnessedCiFailure, setWitnessedCiFailure] = useState(false) + const trackedBuildKeyRef = useRef(null) + const prevBuildStatusRef = useRef(undefined) + + useEffect(() => { + if (!buildKey) { + trackedBuildKeyRef.current = null + setWitnessedCiFailure(false) + prevBuildStatusRef.current = undefined + return + } + + if (trackedBuildKeyRef.current !== buildKey) { + trackedBuildKeyRef.current = buildKey + setWitnessedCiFailure(false) + prevBuildStatusRef.current = undefined + } + + if (build === undefined) { + return + } + + const status = build === null ? "absent" : build.status + const prev = prevBuildStatusRef.current + + if (status === "failed" && prev !== undefined && prev !== "failed") { + setWitnessedCiFailure(true) + } + + prevBuildStatusRef.current = status + }, [buildKey, build]) + const [flashUrl, setFlashUrl] = useState(null) const [flashPrep, setFlashPrep] = useState<"idle" | "loading" | "ready" | "error">("idle") @@ -190,7 +223,7 @@ export default function RepoPage() { if (!effectiveRef || !resolvedSha || !resolvedTargetEnv) return if (build?.status === "failed" && build._id) { void retryBuild({ buildId: build._id }) - .then(() => toast.message("Re-queued build")) + .then(() => toast.message("Starting a new build…")) .catch(e => toast.error(String(e))) return } @@ -263,7 +296,11 @@ export default function RepoPage() { !scanReady || buildInProgress - const flashButtonLabel = build?.status === "failed" ? "Retry build" : buildInProgress ? "Building…" : "Flash" + const flashButtonLabel = buildInProgress ? "Building…" : "Flash" + + const showCiCard = + build && + (build.status !== "failed" || witnessedCiFailure) const targetPlaceholder = !hasRef ? "--target--" @@ -368,7 +405,7 @@ export default function RepoPage() {
- {build ? ( + {showCiCard ? (
CI @@ -408,7 +445,7 @@ export default function RepoPage() {
                                 {build.errorSummary.length > 2500
-                                  ? `${build.errorSummary.slice(0, 2500)}…`
+                                  ? `…${build.errorSummary.slice(-2500)}`
                                   : build.errorSummary}