From b5e2a8cc246d8b1d62f8f78ac6f19db38bbbf241 Mon Sep 17 00:00:00 2001 From: gbudjeakp Date: Fri, 26 Jun 2026 00:06:13 -0600 Subject: [PATCH] fix: self-hosted metadata fallback and jobs refresh control --- internal/agent/collector.go | 28 ++++++++++++++++++++++++++++ web/src/pages/JobsPage.tsx | 28 ++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/internal/agent/collector.go b/internal/agent/collector.go index 5e53e65..8d31eb3 100644 --- a/internal/agent/collector.go +++ b/internal/agent/collector.go @@ -491,7 +491,35 @@ func applyDetectedMachineMetadata(summary *types.MetricsSummary) { summary.DetectedMachineMatchReason = reason summary.DetectedMachineConfidenceLevel = confidenceLevel(confidence) if machine == nil { + summary.DetectedMachine = fallbackDetectedMachine(summary.CIPlatform, v, m) + summary.DetectedMachineConfidence = 0 summary.DetectedMachineConfidenceLevel = "unknown" + summary.DetectedMachineMatchReason = "no catalog match found; using runtime self-hosted machine metadata" + } +} + +func fallbackDetectedMachine(ciPlatform string, vcpus int, memGiB float64) *types.MachineType { + id := "self-hosted" + if runner := strings.TrimSpace(os.Getenv("RUNNER_NAME")); runner != "" { + id = runner + } + + provider := types.Provider("") + if ciPlatform == "github" { + provider = types.ProviderGitHub + } + + return &types.MachineType{ + ID: id, + Provider: provider, + Family: "self-hosted", + Series: "self-hosted", + VCPUs: vcpus, + MemoryGiB: math.Round(memGiB*100) / 100, + StorageType: "unknown", + Architecture: runtime.GOARCH, + OnDemandPricePerHour: 0, + Tags: []string{"self-hosted", "runtime-detected"}, } } diff --git a/web/src/pages/JobsPage.tsx b/web/src/pages/JobsPage.tsx index e52794f..4dffa42 100644 --- a/web/src/pages/JobsPage.tsx +++ b/web/src/pages/JobsPage.tsx @@ -16,6 +16,7 @@ export default function JobsPage() { const { currency } = useCurrencyPreference() const [jobs, setJobs] = useState([]) const [loading, setLoading] = useState(true) + const [refreshing, setRefreshing] = useState(false) const [error, setError] = useState(null) const [search, setSearch] = useState('') @@ -31,11 +32,23 @@ export default function JobsPage() { const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(10) + async function loadJobs(showInitialLoader = false) { + if (showInitialLoader) setLoading(true) + else setRefreshing(true) + setError(null) + try { + const data = await fetchJobs() + setJobs(data) + } catch (e) { + setError((e as Error).message) + } finally { + if (showInitialLoader) setLoading(false) + else setRefreshing(false) + } + } + useEffect(() => { - fetchJobs() - .then(setJobs) - .catch((e) => setError(e.message)) - .finally(() => setLoading(false)) + void loadJobs(true) }, []) // Reset to page 1 on filter/page-size change @@ -235,6 +248,13 @@ export default function JobsPage() { Clear )} + {filtered.length} {filtered.length === 1 ? 'job' : 'jobs'}