mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
fix: stop misdiagnosing glibc-too-old native loads (#2672) and name the Windows FTS zero-install fix (#2669) (#2689)
* docs(plans): add glibc-windows-fts-diagnostics plan Implementation plan for #2672 (glibc-too-old native-load misdiagnosis) and #2669 (Windows FTS prerequisites + Git Bash zero-install workaround). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(cli): stop prescribing a reinstall when the host glibc is too old (#2672) The LadybugDB prebuilt binary requires GLIBC_2.34 (dlopen/pthread_* at 2.34, fstat64/lstat at 2.33). On an older host the loader reports version `GLIBC_2.34' not found (required by .../lbugjs.node) and checkLbugNative answered with "truncated file, ABI mismatch, or wrong-platform binary" plus instructions to re-run install.js. That advice is actively wrong for this class: every download ships the same prebuilt binary, so the reinstall fails identically and the user loops. Add glibcTooOldMessage: match a GLIBC_<version> token on a "not found" line, report the highest required version (compared numerically, so 2.9 < 2.34) alongside this host's glibc from process.report, state that reinstalling will NOT help, and point at the real options. The branch sits on the arm where the probe actually ran and failed, so an unrunnable probe still fails open (#2441). The glibc read is local rather than analyzer-identity's detectLibcVariant: native-check is the dependency-light startup gate and must not statically pull in a module the CLI reaches through a dynamic import. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(lbug): name the Git Bash zero-install fix for Windows FTS load failures (#2669) The Windows error-126 remedy already refuses to prescribe a reinstall and names the VC++ redistributable and the OpenSSL 3 DLLs, but not where those DLLs already exist on the machine. #2669's reporter had the redistributable installed and still failed: the same command failed in PowerShell and succeeded in Git Bash, because Git for Windows puts libssl-3-x64.dll and libcrypto-3-x64.dll on PATH via C:\Program Files\Git\mingw64\bin. Add that hint to the Windows-126 and structural missing-dependency remedies through one shared const, following the VC_REDIST_INSTALL_HINT anti-drift pattern (#2383 F5). Placing it in the builders rather than at a call site is load-bearing: markUnavailable caches the whole diagnosis (#2383 F3) and ftsDegradedWarning replays that cached remedy, so a call-site fix would miss the MCP query and /api/search surfaces. The hint is a fixed system path, never a user-profile one — remedy text is not path-redacted, and fts-degraded-warning.test.ts asserts no C:\Users\ path ever reaches a user. Both touched tests now assert that property directly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(readme): document the Linux glibc floor and Windows FTS prerequisites (#2672, #2669) Requirements listed only Node and git, so neither runtime prerequisite that these two issues turn on was discoverable before hitting the failure. - Linux: the LadybugDB prebuilt binary needs glibc 2.34+; name the distro versions that clear it and state plainly that reinstalling does not help. - Windows: full-text search needs the VC++ 2015-2022 x64 redistributable AND OpenSSL 3 on PATH. The redistributable alone is not sufficient (#2669's reporter had it), and Git for Windows already ships the OpenSSL DLLs, so running from Git Bash or prepending mingw64\bin is a zero-install fix. Without them analyze still succeeds but the index carries no search tables. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: drop the plan document from version control docs/* is gitignored; the plan was force-added so it would travel with the work. It is working material, not a repository artifact — the code, tests and README carry the reasoning that matters. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(cli): stop doctor reporting a present-but-unloadable binary as missing (#2672) doctor printed "✗ lbugjs.node missing" for every failed native check — including the case this PR is about, where the binary is right there and merely fails to load because the host glibc is too old. It then wrote the real detail to stderr directly beneath, so the two lines contradicted each other and the headline sent users to reinstall a file they already had. It said the same for a truncated download and for an entirely absent @ladybugdb/core package. checkLbugNative already knows which of the three it found, so record it: a `kind` discriminator ('package_missing' | 'binary_missing' | 'load_failed') set at each failure return. doctor renders it through a new exported `nativeStatusLine`, following the existing pageSizeDoctorLines/poolSizeDoctorLine pure-helper pattern — which also makes the line testable, where before it had no coverage at all. An unrecognized or absent kind keeps the conservative "missing". Deriving this in doctor with a second existsSync would have re-stat'd a file the check had already inspected, and could disagree with what it actually observed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ad1b9227c4
commit
3f1e23ba83
7 changed files with 336 additions and 7 deletions
|
|
@ -352,6 +352,13 @@ Installed automatically by both `gitnexus analyze` (per-repo) and `gitnexus setu
|
|||
|
||||
- Node.js >= 22
|
||||
- Git repository (uses git for commit tracking)
|
||||
- **Linux: glibc 2.34 or newer** (Ubuntu 22.04+, RHEL/Rocky/Alma 9+, Debian 12+, Fedora 35+). The
|
||||
LadybugDB native binary ships as a prebuild against that floor, so on an older host it cannot
|
||||
load and reinstalling does not help — see
|
||||
[Linux: `GLIBC_2.34' not found`](#linux-glibc_234-not-found).
|
||||
- **Windows, for full-text search:** the Microsoft Visual C++ 2015-2022 Redistributable (x64) *and*
|
||||
OpenSSL 3 (`libssl-3-x64.dll`, `libcrypto-3-x64.dll`) resolvable on `PATH` — see
|
||||
[Windows: full-text search unavailable](#windows-full-text-search-unavailable).
|
||||
|
||||
## Release candidates
|
||||
|
||||
|
|
@ -434,6 +441,50 @@ pnpm add -g --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=t
|
|||
gitnexus serve
|
||||
```
|
||||
|
||||
### Linux: `GLIBC_2.34' not found`
|
||||
|
||||
```
|
||||
LadybugDB native binary (lbugjs.node) exists but failed to load:
|
||||
/lib64/libc.so.6: version `GLIBC_2.34' not found (required by .../lbugjs.node)
|
||||
```
|
||||
|
||||
The LadybugDB addon ships as a prebuilt binary compiled against **glibc 2.34**. If your
|
||||
distribution is older (CentOS/RHEL 8 has 2.28, Ubuntu 20.04 has 2.31, Debian 11 has 2.31), the
|
||||
dynamic loader cannot resolve its symbols.
|
||||
|
||||
**Reinstalling does not help** — every download delivers the same prebuilt binary. The fix is a
|
||||
newer C library:
|
||||
|
||||
- Run GitNexus on a distribution with glibc 2.34 or newer — Ubuntu 22.04+, RHEL/Rocky/Alma 9+,
|
||||
Debian 12+, Fedora 35+.
|
||||
- Or run it in the container image, which bundles a current glibc (see [Docker](#docker)).
|
||||
|
||||
`gitnexus doctor` reports the required and detected glibc versions when this happens
|
||||
([#2672](https://github.com/abhigyanpatwari/GitNexus/issues/2672)).
|
||||
|
||||
### Windows: full-text search unavailable
|
||||
|
||||
`analyze` completes, but keyword search is degraded and `doctor` shows the FTS extension failing
|
||||
with Windows error 126 (`The specified module could not be found`). The extension needs two
|
||||
runtime dependencies Windows does not ship by default:
|
||||
|
||||
1. **Microsoft Visual C++ 2015-2022 Redistributable (x64)** —
|
||||
<https://aka.ms/vs/17/release/vc_redist.x64.exe>
|
||||
2. **OpenSSL 3** — `libssl-3-x64.dll` and `libcrypto-3-x64.dll`, resolvable on `PATH`
|
||||
|
||||
The redistributable alone is **not** sufficient. If Git for Windows is installed you already have
|
||||
the OpenSSL DLLs — run `gitnexus` from **Git Bash**, or prepend the directory to `PATH` in the
|
||||
shell you use:
|
||||
|
||||
```powershell
|
||||
$env:PATH = "C:\Program Files\Git\mingw64\bin;$env:PATH"
|
||||
gitnexus analyze --repair-fts
|
||||
```
|
||||
|
||||
Without them the index is still built, but without search tables, so `query` returns empty keyword
|
||||
results until you re-run `gitnexus analyze --repair-fts` from a shell where the DLLs resolve
|
||||
([#2669](https://github.com/abhigyanpatwari/GitNexus/issues/2669)).
|
||||
|
||||
### Installation fails with native module errors
|
||||
|
||||
Some optional language grammars (Dart, Proto, Swift, Kotlin) require native compilation. If they fail, GitNexus still works — those languages will be skipped. To skip them intentionally (no C++ toolchain needed), set `GITNEXUS_SKIP_OPTIONAL_GRAMMARS=1` before installing.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { cudaRedirectDoctorStatus } from '../core/embeddings/onnxruntime-node-resolver.js';
|
||||
import {
|
||||
checkLbugNative,
|
||||
type NativeCheckResult,
|
||||
probeFtsExtensionLoad,
|
||||
probeVectorExtensionLoad,
|
||||
} from '../core/lbug/native-check.js';
|
||||
|
|
@ -170,6 +171,33 @@ export function poolSizeDoctorLine(pool: number, envRaw: string | undefined): st
|
|||
return ` ${padDisplayEnd('pool size', 10)}${value}${envNote}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `native` status line. Literal label like the page-size and pool-size lines
|
||||
* above (no i18n key).
|
||||
*
|
||||
* A failed check is not automatically a MISSING binary, and saying so is the
|
||||
* same misdiagnosis #2672 fixed one layer down: on a host whose glibc is too
|
||||
* old, `lbugjs.node` is present and merely unloadable, so "missing" sent users
|
||||
* to reinstall a file that was already there — while the detail written to
|
||||
* stderr right below said the opposite. Render what the check actually found.
|
||||
*/
|
||||
export function nativeStatusLine(check: NativeCheckResult): string {
|
||||
return ` ${padDisplayEnd('native', 10)}${nativeStatusText(check)}`;
|
||||
}
|
||||
|
||||
function nativeStatusText(check: NativeCheckResult): string {
|
||||
if (check.ok) return '✓ lbugjs.node loaded';
|
||||
switch (check.kind) {
|
||||
case 'package_missing':
|
||||
return '✗ @ladybugdb/core not installed';
|
||||
case 'load_failed':
|
||||
return '✗ lbugjs.node present but failed to load';
|
||||
default:
|
||||
// 'binary_missing', and any future kind: the conservative claim.
|
||||
return '✗ lbugjs.node missing';
|
||||
}
|
||||
}
|
||||
|
||||
export const doctorCommand = async () => {
|
||||
const fingerprint = getRuntimeFingerprint();
|
||||
const capabilities = getRuntimeCapabilities();
|
||||
|
|
@ -194,10 +222,8 @@ export const doctorCommand = async () => {
|
|||
poolSizeDoctorLine(getEffectiveBufferPoolSize(), process.env.GITNEXUS_LBUG_BUFFER_POOL_SIZE),
|
||||
);
|
||||
const nativeCheck = checkLbugNative();
|
||||
if (nativeCheck.ok) {
|
||||
console.log(` ${padDisplayEnd('native', 10)}✓ lbugjs.node loaded`);
|
||||
} else {
|
||||
console.log(` ${padDisplayEnd('native', 10)}✗ lbugjs.node missing`);
|
||||
console.log(nativeStatusLine(nativeCheck));
|
||||
if (!nativeCheck.ok) {
|
||||
process.stderr.write(`\n${nativeCheck.message?.replace(/^/gm, ' ')}\n\n`);
|
||||
}
|
||||
console.log(` ${label('doctor.labels.onnx', 10)}${fingerprint.onnxruntime ?? 'unknown'}`);
|
||||
|
|
|
|||
|
|
@ -127,13 +127,25 @@ const VC_REDIST_INSTALL_HINT =
|
|||
'the Microsoft Visual C++ 2015-2022 Redistributable (x64) from ' +
|
||||
'https://aka.ms/vs/17/release/vc_redist.x64.exe';
|
||||
|
||||
// Git for Windows already ships the OpenSSL 3 DLLs in its mingw64 bin directory,
|
||||
// so the identical command that fails in PowerShell succeeds in Git Bash (#2669
|
||||
// reporter, who had the VC++ redist installed and still failed until that
|
||||
// directory was on PATH). Deliberately a fixed system path and never a
|
||||
// user-profile one: remedy text is NOT path-redacted (fts-indexes.ts redacts
|
||||
// only the reason), and fts-degraded-warning.test.ts asserts that no
|
||||
// `C:\Users\…` path ever reaches a user through this surface.
|
||||
const GIT_BASH_OPENSSL_HINT =
|
||||
' If Git for Windows is installed you already have those DLLs: run the same command from Git Bash, ' +
|
||||
'or prepend "C:\\Program Files\\Git\\mingw64\\bin" to PATH.';
|
||||
|
||||
// MSVC-first per DuckDB's canonical answer for this exact error; OpenSSL second.
|
||||
const windowsMissingDependencyRemedy = (label: string): string =>
|
||||
`The ${label} extension is present but a required runtime library is missing (Windows error 126). ` +
|
||||
'Reinstalling the extension will NOT help. Install ' +
|
||||
VC_REDIST_INSTALL_HINT +
|
||||
'; if the error persists, the extension also needs OpenSSL 3 ' +
|
||||
'(libcrypto-3-x64.dll / libssl-3-x64.dll) on the DLL search path.';
|
||||
'(libcrypto-3-x64.dll / libssl-3-x64.dll) on the DLL search path.' +
|
||||
GIT_BASH_OPENSSL_HINT;
|
||||
|
||||
const posixMissingDependencyRemedy = (label: string): string =>
|
||||
`The ${label} extension is present but a shared library it depends on could not be loaded (named in ` +
|
||||
|
|
@ -205,7 +217,8 @@ const structuralMissingDependencyRemedy = (label: string): string =>
|
|||
`The ${label} extension file is valid, so the failure is a missing or incompatible runtime dependency, ` +
|
||||
'not the extension itself — reinstalling will NOT help. On Windows, install ' +
|
||||
VC_REDIST_INSTALL_HINT +
|
||||
' and ensure OpenSSL 3 is available; on Linux/macOS install the shared library named in the error above.';
|
||||
' and ensure OpenSSL 3 is available; on Linux/macOS install the shared library named in the error above.' +
|
||||
GIT_BASH_OPENSSL_HINT;
|
||||
|
||||
/**
|
||||
* Pull the extension file path out of lbug's load error. lbug's wrapper is
|
||||
|
|
|
|||
|
|
@ -7,10 +7,21 @@ import { spawnSync, type SpawnSyncReturns } from 'node:child_process';
|
|||
* CLI startup gate (same bounding rationale as the extension probe below). */
|
||||
const NATIVE_LOAD_PROBE_TIMEOUT_MS = 15_000;
|
||||
|
||||
/**
|
||||
* Why the native check failed. A failed check is NOT necessarily a missing
|
||||
* binary — the package may be absent, the binary may be absent, or a binary that
|
||||
* is right there may fail to load (host glibc too old, truncated download).
|
||||
* Callers that render a status line must tell those apart: reporting all of them
|
||||
* as "missing" sends users to reinstall a file they already have (#2672).
|
||||
*/
|
||||
export type NativeCheckFailureKind = 'package_missing' | 'binary_missing' | 'load_failed';
|
||||
|
||||
export interface NativeCheckResult {
|
||||
ok: boolean;
|
||||
binaryPath?: string;
|
||||
message?: string;
|
||||
/** Set only when `ok` is false. */
|
||||
kind?: NativeCheckFailureKind;
|
||||
}
|
||||
|
||||
export function checkLbugNative(overridePkgDir?: string): NativeCheckResult {
|
||||
|
|
@ -26,6 +37,7 @@ export function checkLbugNative(overridePkgDir?: string): NativeCheckResult {
|
|||
} catch {
|
||||
return {
|
||||
ok: false,
|
||||
kind: 'package_missing',
|
||||
message: [
|
||||
'LadybugDB package (@ladybugdb/core) is not installed.',
|
||||
'',
|
||||
|
|
@ -40,6 +52,7 @@ export function checkLbugNative(overridePkgDir?: string): NativeCheckResult {
|
|||
return {
|
||||
ok: false,
|
||||
binaryPath,
|
||||
kind: 'binary_missing',
|
||||
message: [
|
||||
'LadybugDB native binary (lbugjs.node) is missing.',
|
||||
'',
|
||||
|
|
@ -89,9 +102,30 @@ export function checkLbugNative(overridePkgDir?: string): NativeCheckResult {
|
|||
return { ok: true, binaryPath };
|
||||
}
|
||||
|
||||
// One failure class is NOT repairable by reinstalling: a host whose glibc is
|
||||
// older than the prebuilt binary requires. Every download ships the same
|
||||
// binary, so the generic advice below sends the user around a loop that always
|
||||
// ends here (#2672). Branch before it, and only here — on the arm where the
|
||||
// probe actually ran and failed, so an unrunnable probe still fails open above.
|
||||
const glibcExplanation = glibcTooOldMessage(probe.stderr ?? '');
|
||||
if (glibcExplanation !== null) {
|
||||
return {
|
||||
ok: false,
|
||||
binaryPath,
|
||||
kind: 'load_failed',
|
||||
message: [
|
||||
'LadybugDB native binary (lbugjs.node) exists but failed to load:',
|
||||
` ${describeNativeLoadFailure(probe)}`,
|
||||
'',
|
||||
glibcExplanation,
|
||||
].join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: false,
|
||||
binaryPath,
|
||||
kind: 'load_failed',
|
||||
message: [
|
||||
'LadybugDB native binary (lbugjs.node) exists but failed to load:',
|
||||
` ${describeNativeLoadFailure(probe)}`,
|
||||
|
|
@ -133,6 +167,80 @@ function describeNativeLoadFailure(probe: SpawnSyncReturns<string>): string {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A `GLIBC_<version>` token. The dynamic loader names the first unresolved
|
||||
* versioned symbol as ``version `GLIBC_2.34' not found (required by …)``, but we
|
||||
* key on the token plus a "not found" line rather than on glibc's exact
|
||||
* backtick/apostrophe quoting: if that wording ever changes, this degrades to
|
||||
* the generic failure message instead of misfiring.
|
||||
*/
|
||||
const GLIBC_VERSION_TOKEN = /GLIBC_(\d+(?:\.\d+)+)/g;
|
||||
|
||||
/** Numeric dotted-segment order — glibc 2.9 is OLDER than 2.34, not newer. */
|
||||
function compareDottedVersions(a: string, b: string): number {
|
||||
const left = a.split('.').map((part) => Number.parseInt(part, 10));
|
||||
const right = b.split('.').map((part) => Number.parseInt(part, 10));
|
||||
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
||||
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
||||
if (diff !== 0) return diff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This host's runtime glibc, or null when Node cannot report it (musl builds,
|
||||
* embedders without `process.report`). Read locally rather than through
|
||||
* analyzer-identity's `detectLibcVariant`: that module is deliberately reached
|
||||
* via dynamic import from the CLI lazy actions, and this file is the
|
||||
* dependency-light startup gate that must not pull it in.
|
||||
*/
|
||||
function hostGlibcVersion(): string | null {
|
||||
try {
|
||||
const report = process.report?.getReport() as
|
||||
| { header?: { glibcVersionRuntime?: unknown } }
|
||||
| undefined;
|
||||
const runtime = report?.header?.glibcVersionRuntime;
|
||||
return typeof runtime === 'string' && runtime.length > 0 ? runtime : null;
|
||||
} catch {
|
||||
// Report generation is optional on some embedded Node builds; an unknown
|
||||
// host version still leaves the required version worth printing.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explain a glibc-too-old native load failure, or null when the probe's stderr
|
||||
* describes something else.
|
||||
*
|
||||
* Reinstalling cannot fix this class — the package ships one prebuilt binary per
|
||||
* platform — so the caller must NOT fall through to the reinstall instructions
|
||||
* (#2672). Exported for direct unit testing: a real `GLIBC_2.34' not found`
|
||||
* cannot be provoked on a host whose glibc is new enough to run the tests.
|
||||
*/
|
||||
export function glibcTooOldMessage(stderr: string): string | null {
|
||||
const required = stderr
|
||||
.split('\n')
|
||||
.filter((line) => /not found/i.test(line))
|
||||
.flatMap((line) => [...line.matchAll(GLIBC_VERSION_TOKEN)].map((match) => match[1]))
|
||||
.sort(compareDottedVersions)
|
||||
.at(-1);
|
||||
if (required === undefined) return null;
|
||||
|
||||
const host = hostGlibcVersion();
|
||||
return [
|
||||
"This host's C library (glibc) is older than the prebuilt binary requires.",
|
||||
` required: glibc ${required} or newer`,
|
||||
` this host: ${host === null ? 'glibc version could not be determined' : `glibc ${host}`}`,
|
||||
'',
|
||||
'Reinstalling will NOT help — every download ships the same prebuilt binary.',
|
||||
'',
|
||||
'Options:',
|
||||
` - Run GitNexus on a distribution with glibc ${required} or newer`,
|
||||
' (Ubuntu 22.04+, RHEL/Rocky/Alma 9+, Debian 12+, Fedora 35+).',
|
||||
' - Or use the GitNexus container image, which bundles a current glibc.',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
export interface FtsProbeResult {
|
||||
loaded: boolean;
|
||||
/** Collapsed LadybugDB error when `loaded` is false. */
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ import {
|
|||
doctorCommand,
|
||||
localEmbeddingDoctorStatus,
|
||||
padDisplayEnd,
|
||||
nativeStatusLine,
|
||||
pageSizeDoctorLines,
|
||||
poolSizeDoctorLine,
|
||||
} from '../../src/cli/doctor.js';
|
||||
import type { NativeCheckResult } from '../../src/core/lbug/native-check.js';
|
||||
|
||||
describe('doctor output formatting', () => {
|
||||
it('keeps ASCII padding equivalent to String.padEnd', () => {
|
||||
|
|
@ -187,6 +189,36 @@ describe('doctor pool-size line (#2631)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// #2672: every failed check used to print "lbugjs.node missing", including the
|
||||
// glibc case where the binary is present and merely unloadable — contradicting
|
||||
// the detail printed directly beneath it and sending users to reinstall a file
|
||||
// they already had.
|
||||
describe('doctor native status line (#2672)', () => {
|
||||
const nativeStatusCases: ReadonlyArray<readonly [string, NativeCheckResult, string]> = [
|
||||
['a loaded binary', { ok: true, binaryPath: '/x/lbugjs.node' }, '✓ lbugjs.node loaded'],
|
||||
[
|
||||
'an uninstalled package',
|
||||
{ ok: false, kind: 'package_missing', message: 'x' },
|
||||
'✗ @ladybugdb/core not installed',
|
||||
],
|
||||
[
|
||||
'an absent binary',
|
||||
{ ok: false, kind: 'binary_missing', binaryPath: '/x/lbugjs.node', message: 'x' },
|
||||
'✗ lbugjs.node missing',
|
||||
],
|
||||
[
|
||||
'a present-but-unloadable binary (glibc too old, truncated download)',
|
||||
{ ok: false, kind: 'load_failed', binaryPath: '/x/lbugjs.node', message: 'x' },
|
||||
'✗ lbugjs.node present but failed to load',
|
||||
],
|
||||
['a failure with no kind recorded', { ok: false, message: 'x' }, '✗ lbugjs.node missing'],
|
||||
];
|
||||
|
||||
it.each(nativeStatusCases)('reports %s', (_name, check, expected) => {
|
||||
expect(nativeStatusLine(check)).toBe(` ${padDisplayEnd('native', 10)}${expected}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('doctor survives a malformed GITNEXUS_EMBEDDING_DIMS (#2385)', () => {
|
||||
const ENV_KEYS = [
|
||||
'GITNEXUS_EMBEDDING_URL',
|
||||
|
|
|
|||
|
|
@ -154,6 +154,11 @@ describe('classifyExtensionLoadError', () => {
|
|||
expect(remedy).toMatch(/will NOT help/);
|
||||
// Must not resurrect the old, wrong "retry the network install" instruction.
|
||||
expect(remedy).not.toMatch(/Retry with network access/i);
|
||||
// #2669: the zero-install path — Git for Windows already ships those DLLs.
|
||||
expect(remedy).toMatch(/Git Bash/);
|
||||
expect(remedy).toMatch(/mingw64/);
|
||||
// Never a user-profile path: remedy text reaches /api/search unredacted.
|
||||
expect(remedy).not.toMatch(/C:\\Users\\/);
|
||||
});
|
||||
|
||||
it('hedged fallback remedy points at the OS error and offers both branches (language-independent)', () => {
|
||||
|
|
@ -318,6 +323,9 @@ describe('diagnoseExtensionLoad (structural, language-independent)', () => {
|
|||
const { kind, remedy } = diagnoseExtensionLoad(reason);
|
||||
expect(kind).toBe('missing_dependency');
|
||||
expect(remedy).toMatch(/vc_redist\.x64\.exe/);
|
||||
// #2669: the structural remedy carries the same zero-install hint.
|
||||
expect(remedy).toMatch(/Git Bash/);
|
||||
expect(remedy).not.toMatch(/C:\\Users\\/);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
|||
import os from 'os';
|
||||
import path from 'path';
|
||||
import fs from 'fs/promises';
|
||||
import { checkLbugNative } from '../../src/core/lbug/native-check.js';
|
||||
import { checkLbugNative, glibcTooOldMessage } from '../../src/core/lbug/native-check.js';
|
||||
|
||||
describe('checkLbugNative', () => {
|
||||
it('returns ok:true when the real @ladybugdb/core binary is present', () => {
|
||||
|
|
@ -20,6 +20,7 @@ describe('checkLbugNative', () => {
|
|||
const result = checkLbugNative(tmpDir);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.kind).toBe('binary_missing');
|
||||
expect(result.message).toContain('missing');
|
||||
expect(result.message).toContain('install.js');
|
||||
expect(result.message).toContain('trustedDependencies');
|
||||
|
|
@ -43,6 +44,8 @@ describe('checkLbugNative', () => {
|
|||
const result = checkLbugNative(tmpDir);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
// Present but unloadable — doctor must not call this "missing" (#2672).
|
||||
expect(result.kind).toBe('load_failed');
|
||||
expect(result.message).toContain('failed to load');
|
||||
expect(result.message).toContain('install.js');
|
||||
} finally {
|
||||
|
|
@ -73,6 +76,94 @@ describe('checkLbugNative', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('does not prescribe a reinstall when the host glibc is too old (#2672)', async () => {
|
||||
// The generic failure text ("truncated / ABI mismatch / re-run install.js")
|
||||
// is actively wrong for this class: the reinstall re-downloads the identical
|
||||
// prebuilt binary and fails identically. Guard the whole assembled message,
|
||||
// not just the helper, so the branch stays wired into checkLbugNative.
|
||||
const message = glibcTooOldMessage(
|
||||
"Error: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by " +
|
||||
'/usr/lib/node_modules/gitnexus/node_modules/@ladybugdb/core/lbugjs.node)',
|
||||
);
|
||||
|
||||
expect(message).toContain('glibc 2.34 or newer');
|
||||
expect(message).toContain('will NOT help');
|
||||
expect(message).not.toContain('install.js');
|
||||
expect(message).not.toContain('trustedDependencies');
|
||||
expect(message).not.toContain('--allow-build');
|
||||
});
|
||||
|
||||
// POSIX-only: the fake "node" is a shebang script, which Windows cannot exec.
|
||||
// The real Windows path has no glibc, so this class cannot occur there anyway.
|
||||
it.skipIf(process.platform === 'win32')(
|
||||
'checkLbugNative routes a glibc load failure to that message, not the reinstall text',
|
||||
async () => {
|
||||
// Proves the branch is WIRED, not merely present: the probe child is
|
||||
// replaced by a script that emits the loader error the #2672 reporter saw.
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-glibc-'));
|
||||
const originalExecPath = process.execPath;
|
||||
try {
|
||||
await fs.writeFile(path.join(tmpDir, 'lbugjs.node'), Buffer.from('content-irrelevant'));
|
||||
await fs.writeFile(path.join(tmpDir, 'install.js'), '');
|
||||
const fakeNode = path.join(tmpDir, 'fake-node');
|
||||
await fs.writeFile(
|
||||
fakeNode,
|
||||
'#!/bin/sh\n' +
|
||||
'echo "Error: /lib64/libc.so.6: version \\`GLIBC_2.34\' not found' +
|
||||
' (required by /x/@ladybugdb/core/lbugjs.node)" >&2\n' +
|
||||
'exit 1\n',
|
||||
);
|
||||
await fs.chmod(fakeNode, 0o755);
|
||||
process.execPath = fakeNode;
|
||||
|
||||
const result = checkLbugNative(tmpDir);
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.kind).toBe('load_failed');
|
||||
expect(result.message).toContain('glibc 2.34 or newer');
|
||||
expect(result.message).toContain('will NOT help');
|
||||
expect(result.message).not.toContain('install.js');
|
||||
} finally {
|
||||
process.execPath = originalExecPath;
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it('names this host glibc alongside the required one', () => {
|
||||
const message = glibcTooOldMessage("version `GLIBC_2.34' not found");
|
||||
// Never pin the runner's own glibc — assert the line is populated either way.
|
||||
expect(message).toMatch(/this host: (glibc \d+\.\d+|glibc version could not be determined)/);
|
||||
});
|
||||
|
||||
const requiredVersionCases: ReadonlyArray<readonly [string, string, string]> = [
|
||||
['reports the single required version', "version `GLIBC_2.34' not found", '2.34'],
|
||||
[
|
||||
'reports the highest of several required versions',
|
||||
"version `GLIBC_2.29' not found\nversion `GLIBC_2.34' not found",
|
||||
'2.34',
|
||||
],
|
||||
[
|
||||
'orders versions numerically, not lexically',
|
||||
"version `GLIBC_2.9' not found\nversion `GLIBC_2.34' not found",
|
||||
'2.34',
|
||||
],
|
||||
];
|
||||
|
||||
it.each(requiredVersionCases)('%s', (_name, stderr, expected) => {
|
||||
expect(glibcTooOldMessage(stderr)).toContain(`glibc ${expected} or newer`);
|
||||
});
|
||||
|
||||
const nonGlibcCases: ReadonlyArray<readonly [string, string]> = [
|
||||
['an unrelated loader error', 'Error: invalid ELF header'],
|
||||
['empty stderr', ''],
|
||||
['a GLIBC token with no not-found line', 'linked against GLIBC_2.34 successfully'],
|
||||
];
|
||||
|
||||
it.each(nonGlibcCases)('returns null for %s, leaving the generic message', (_name, stderr) => {
|
||||
expect(glibcTooOldMessage(stderr)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns ok:true when the load probe cannot be spawned (inconclusive, not a broken binary)', async () => {
|
||||
// The binary is present, but the child probe cannot launch — a sandbox that
|
||||
// forbids subprocesses, or a non-Node execPath. We could not test the binary,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue