diff --git a/app-catalog/src/api/charts.tsx b/app-catalog/src/api/charts.tsx index 94f5cef2d..81203efe7 100644 --- a/app-catalog/src/api/charts.tsx +++ b/app-catalog/src/api/charts.tsx @@ -44,3 +44,32 @@ export function fetchChartValues(packageID: string, packageVersion: string) { }, }).then(response => response.text()); } + +export async function fetchLatestAppVersion(chartName: string): Promise { + if (!chartName) { + return '—'; + } + + try { + const url = new URL('https://artifacthub.io/api/v1/packages/search'); + url.searchParams.set('offset', '0'); + url.searchParams.set('limit', '5'); + url.searchParams.set('facets', 'false'); + url.searchParams.set('kind', '0'); + url.searchParams.set('ts_query_web', chartName); + + const response = await fetch(url.toString()); + const dataResponse = await response.json(); + const packages: any[] = dataResponse?.packages ?? []; + + const lowerChartName = chartName.toLowerCase(); + const selectedPackage = + packages.find( + p => p?.name?.toLowerCase() === lowerChartName || p?.normalized_name === lowerChartName + ) ?? packages[0]; + + return selectedPackage?.app_version ?? '—'; + } catch { + return '—'; + } +} diff --git a/app-catalog/src/components/releases/List.tsx b/app-catalog/src/components/releases/List.tsx index 11df6e435..eb322310c 100644 --- a/app-catalog/src/components/releases/List.tsx +++ b/app-catalog/src/components/releases/List.tsx @@ -8,10 +8,22 @@ import { } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import { Box } from '@mui/material'; import { useEffect, useState } from 'react'; +import { fetchLatestAppVersion } from '../../api/charts'; import { listReleases } from '../../api/releases'; +const formatVersion = (v?: string) => { + const s = (v ?? '').trim(); + + if (!s || s === '—') { + return '—'; + } + + return s.startsWith('v') ? s : `v${s}`; +}; + export default function ReleaseList({ fetchReleases = listReleases }) { const [releases, setReleases] = useState | null>(null); + const [latestMap, setLatestMap] = useState>({}); useEffect(() => { fetchReleases().then(response => { @@ -23,6 +35,21 @@ export default function ReleaseList({ fetchReleases = listReleases }) { }); }, []); + useEffect(() => { + if (!releases?.length) { + setLatestMap({}); + return; + } + + Promise.all( + releases.map(async r => { + const chartName = r?.chart?.metadata?.name; + const v = chartName ? await fetchLatestAppVersion(chartName).catch(() => '—') : '—'; + return [r.name, v] as const; + }) + ).then(entries => setLatestMap(Object.fromEntries(entries))); + }, [releases]); + return ( release.namespace, }, { - label: 'App Version', - getter: release => release.chart.metadata.appVersion, + label: 'Current Version', + getter: release => formatVersion(release.chart.metadata.appVersion), + }, + { + label: 'Latest Version', + getter: release => formatVersion(latestMap[release?.name]), }, { label: 'Version',