fix(fleet): resolve getSenchoVersion crash in Docker containers (#396)

* fix(fleet): resolve getSenchoVersion crash in Docker containers

Replace the runtime __dirname walk (which fails in Docker because the
root package.json is absent from the final image) with a build-time
version injection. A prebuild script reads the root package.json and
generates src/generated/version.ts with the version baked in as a
constant. The compiled output carries the correct version regardless
of the container's filesystem layout.

Retains a filesystem walk fallback for dev environments where the
prebuild hook may not have run.

* fix(fleet): skip postinstall version generation when script is absent

The Docker prod-deps stage copies only package.json (no source files)
before running npm ci --omit=dev. The postinstall hook now checks for
the script file before executing, so it no-ops in stages where
scripts/generate-version.js has not been copied.
This commit is contained in:
Anso
2026-04-06 00:44:36 -04:00
committed by GitHub
parent 89da454db4
commit 670a429168
5 changed files with 47 additions and 10 deletions
+15 -9
View File
@@ -1,6 +1,7 @@
import axios from 'axios';
import path from 'path';
import fs from 'fs';
import { SENCHO_VERSION } from '../generated/version';
/**
* Static registry of capabilities supported by THIS Sencho instance.
@@ -34,24 +35,29 @@ export const CAPABILITIES = [
export type Capability = (typeof CAPABILITIES)[number];
export function getSenchoVersion(): string {
// Walk up from __dirname to find the root package.json (name === 'sencho').
// In dev this is 3 levels up (src/services/ -> root), in Docker it's 2
// (dist/services/ -> /app/). Skips intermediate package.json files like
// backend/package.json which has a different name and version.
// Resolved once per process at import time, then cached.
function resolveVersion(): string {
if (SENCHO_VERSION !== '0.0.0-dev') return SENCHO_VERSION;
// Fallback for manual ts-node runs without the predev hook.
let dir = __dirname;
for (let i = 0; i < 5; i++) {
const candidate = path.join(dir, 'package.json');
if (fs.existsSync(candidate)) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const pkg = require(candidate);
try {
const pkg = JSON.parse(fs.readFileSync(candidate, 'utf8'));
if (pkg.name === 'sencho') return pkg.version;
}
} catch { /* not found, keep walking */ }
dir = path.dirname(dir);
}
return 'unknown';
}
const cachedVersion = resolveVersion();
export function getSenchoVersion(): string {
return cachedVersion;
}
export interface RemoteMeta {
version: string | null;
capabilities: string[];