From 266ff5a79f032a1acabec46aece2f6b35f08b320 Mon Sep 17 00:00:00 2001 From: auto-bot Date: Mon, 20 Oct 2025 15:59:45 +0200 Subject: [PATCH] fix: normalize script matching to handle underscore vs hyphen differences - Add normalizeId helper to compare local filenames with script slugs/names - Include install_basenames from install_methods for robust matching - Fix false 'Not Downloaded' status for PVE Host scripts like 'PVE LXC Execute Command' - Update DownloadedScriptsTab and ScriptsGrid to use normalized comparisons - Resolves issue where scripts with underscores in filenames (e.g., pbs_microcode.sh) weren't matching JSON slugs with hyphens (e.g., pbs-microcode) --- src/app/_components/DownloadedScriptsTab.tsx | 17 ++++++++++++++--- src/app/_components/ScriptsGrid.tsx | 17 ++++++++++++++--- src/server/api/routers/scripts.ts | 10 ++++++++++ src/types/script.ts | 2 ++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/app/_components/DownloadedScriptsTab.tsx b/src/app/_components/DownloadedScriptsTab.tsx index fcaca50..24069f9 100644 --- a/src/app/_components/DownloadedScriptsTab.tsx +++ b/src/app/_components/DownloadedScriptsTab.tsx @@ -169,6 +169,13 @@ export function DownloadedScriptsTab({ onInstallScript }: DownloadedScriptsTabPr // Update scripts with download status and filter to only downloaded scripts const downloadedScripts = React.useMemo((): ScriptCardType[] => { + // Helper to normalize identifiers so underscores vs hyphens don't break matches + const normalizeId = (s?: string): string => (s ?? '') + .toLowerCase() + .replace(/\.(sh|bash|py|js|ts)$/g, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + return combinedScripts .map(script => { if (!script?.name) { @@ -178,9 +185,13 @@ export function DownloadedScriptsTab({ onInstallScript }: DownloadedScriptsTabPr // Check if there's a corresponding local script const hasLocalVersion = localScriptsData?.scripts?.some(local => { if (!local?.name) return false; - const localName = local.name.replace(/\.sh$/, ''); - return localName.toLowerCase() === script.name.toLowerCase() || - localName.toLowerCase() === (script.slug ?? '').toLowerCase(); + const normalizedLocal = normalizeId(local.name); + const matchesNameOrSlug = ( + normalizedLocal === normalizeId(script.name) || + normalizedLocal === normalizeId(script.slug) + ); + const matchesInstallBasename = (script as any)?.install_basenames?.some((base: string) => normalizeId(base) === normalizedLocal) ?? false; + return matchesNameOrSlug || matchesInstallBasename; }) ?? false; return { diff --git a/src/app/_components/ScriptsGrid.tsx b/src/app/_components/ScriptsGrid.tsx index e113f40..bfeb3ef 100644 --- a/src/app/_components/ScriptsGrid.tsx +++ b/src/app/_components/ScriptsGrid.tsx @@ -200,6 +200,13 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) { // Update scripts with download status const scriptsWithStatus = React.useMemo((): ScriptCardType[] => { + // Helper to normalize identifiers for robust matching + const normalizeId = (s?: string): string => (s ?? '') + .toLowerCase() + .replace(/\.(sh|bash|py|js|ts)$/g, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + return combinedScripts.map(script => { if (!script?.name) { return script; // Return as-is if invalid @@ -208,9 +215,13 @@ export function ScriptsGrid({ onInstallScript }: ScriptsGridProps) { // Check if there's a corresponding local script const hasLocalVersion = localScriptsData?.scripts?.some(local => { if (!local?.name) return false; - const localName = local.name.replace(/\.sh$/, ''); - return localName.toLowerCase() === script.name.toLowerCase() || - localName.toLowerCase() === (script.slug ?? '').toLowerCase(); + const normalizedLocal = normalizeId(local.name); + const matchesNameOrSlug = ( + normalizedLocal === normalizeId(script.name) || + normalizedLocal === normalizeId(script.slug) + ); + const matchesInstallBasename = (script as any)?.install_basenames?.some((base: string) => normalizeId(base) === normalizedLocal) ?? false; + return matchesNameOrSlug || matchesInstallBasename; }) ?? false; return { diff --git a/src/server/api/routers/scripts.ts b/src/server/api/routers/scripts.ts index 03185a8..95326ad 100644 --- a/src/server/api/routers/scripts.ts +++ b/src/server/api/routers/scripts.ts @@ -177,6 +177,15 @@ export const scriptsRouter = createTRPCRouter({ const firstInstallMethod = script?.install_methods?.[0]; const os = firstInstallMethod?.resources?.os; const version = firstInstallMethod?.resources?.version; + // Extract install basenames for robust local matching (e.g., execute.sh -> execute) + const install_basenames = (script?.install_methods ?? []) + .map(m => m?.script) + .filter((p): p is string => typeof p === 'string') + .map(p => { + const parts = p.split('/'); + const file = parts[parts.length - 1] ?? ''; + return file.replace(/\.(sh|bash|py|js|ts)$/i, ''); + }); return { ...card, @@ -189,6 +198,7 @@ export const scriptsRouter = createTRPCRouter({ version: version, // Add interface port interface_port: script?.interface_port, + install_basenames, } as ScriptCard; }); diff --git a/src/types/script.ts b/src/types/script.ts index 6c353f5..07ef022 100644 --- a/src/types/script.ts +++ b/src/types/script.ts @@ -60,6 +60,8 @@ export interface ScriptCard { os?: string; version?: string; interface_port?: number | null; + // Optional: basenames of install scripts (without extension) + install_basenames?: string[]; } export interface GitHubFile {