fix(fleet): resolve version detection pipeline for Docker builds (#402)

* fix(fleet): resolve version detection pipeline for Docker builds

The Dockerfile backend-builder stage was missing a COPY of the root
package.json, causing generate-version.js to fall back to "0.0.0-dev"
at build time. At runtime, the filesystem walk also failed (root
package.json not in the final image), producing the string "unknown"
which the frontend rendered as "vunknown".

Changes:
- Dockerfile: copy root package.json into backend-builder stage
- CapabilityRegistry: return null (not "unknown") for unresolvable
  versions; add isValidVersion() type guard; normalize remote meta
  responses to strip "unknown"/"0.0.0-dev" sentinel values
- Fleet endpoints: hoist gateway version validation outside per-node
  loops; treat unresolvable remote versions as "potentially outdated"
  instead of silently marking them up to date
- FleetView: guard all version display points (card badge, update
  button, gateway label, modal columns) via shared formatVersion()
- EditorLayout, CapabilityGate: use shared isValidVersion utility
- New frontend/src/lib/version.ts shared utility
- Docs: add troubleshooting section for version display edge cases
- Screenshots: updated Fleet Overview and Node Updates modal

* docs: update fleet node updates screenshot with live remote node
This commit is contained in:
Anso
2026-04-06 03:03:56 -04:00
committed by GitHub
parent 9002ff95e1
commit a55d1245f8
11 changed files with 82 additions and 23 deletions
+12 -4
View File
@@ -1,6 +1,7 @@
import axios from 'axios';
import path from 'path';
import fs from 'fs';
import semver from 'semver';
import { SENCHO_VERSION } from '../generated/version';
/**
@@ -35,8 +36,13 @@ export const CAPABILITIES = [
export type Capability = (typeof CAPABILITIES)[number];
/** Returns true when the string is a usable semver version. */
export function isValidVersion(v: string | null | undefined): v is string {
return !!v && v !== 'unknown' && v !== '0.0.0-dev' && !!semver.valid(v);
}
// Resolved once per process at import time, then cached.
function resolveVersion(): string {
function resolveVersion(): string | null {
if (SENCHO_VERSION !== '0.0.0-dev') return SENCHO_VERSION;
// Fallback for manual ts-node runs without the predev hook.
@@ -49,12 +55,13 @@ function resolveVersion(): string {
} catch { /* not found, keep walking */ }
dir = path.dirname(dir);
}
return 'unknown';
console.warn('[CapabilityRegistry] Could not resolve Sencho version from any source');
return null;
}
const cachedVersion = resolveVersion();
export function getSenchoVersion(): string {
export function getSenchoVersion(): string | null {
return cachedVersion;
}
@@ -83,8 +90,9 @@ export async function fetchRemoteMeta(baseUrl: string, apiToken: string): Promis
headers: { Authorization: `Bearer ${apiToken}` },
timeout: 5000,
});
const rawVersion: string | undefined = res.data.version;
return {
version: res.data.version ?? null,
version: isValidVersion(rawVersion) ? rawVersion : null,
capabilities: Array.isArray(res.data.capabilities) ? res.data.capabilities : [],
};
} catch (err) {