mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-08 22:22:52 +00:00
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Skill copy sync / shipped skills drift guard (push) Has been cancelled
* feat(cli): add a bunx lane to the runner ladder
The ladder assumed a Node toolchain: global gitnexus, then pnpm dlx or
npx in some order, with npx as the last resort. On a bun-only machine
npm, npx and pnpm are all absent, so every rung fell through to npx and
both the emitted hint and the generated .gitnexus/run.cjs produced a
command the machine could not run at all.
Add bun as a fourth mode, invoked as an install-free bunx one-shot, on
two rungs:
- npm 11+ with no pnpm to fall back on — bunx dodges the same arborist
install crash the pnpm rung exists for (#1939);
- npm and pnpm both absent — previously the dead end described above.
Every pre-existing outcome is preserved: pnpm still wins on npm 11+, npx
still wins on npm < 11, and pnpm still wins over bunx when npm is absent.
Regression tests pin each of those. The bun PATH probe is lazy, so a
machine with a Node toolchain pays no extra scan and the stale-index hook
budget is unchanged.
bunx takes no allow-build equivalent: bun's --trust is a bun add/install
flag that writes trustedDependencies into a project package.json, which a
one-shot has none of, so the argv stays flag-free.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016R9psS9gJ73MRyquoBoPKg
* fix(lbug): restore the prebuilt native binary when install scripts were skipped
Without this the new bunx lane resolves to a command that still fails:
bun skips lifecycle scripts for a bunx fetch, so @ladybugdb/core's
install script never copies lbugjs.node up from its per-platform
sub-package and every native command dead-ends on 'LadybugDB native
binary (lbugjs.node) is missing'.
The existing guidance cannot rescue that case. It offers pnpm
--allow-build, a global install, or adding trustedDependencies to a
project package.json — bunx has no project package.json to add to, no
per-invocation opt-in, and re-extracts the package on every run, so an
out-of-band repair is wiped before the next invocation. In-process
recovery is the only thing that can work.
Recovery is cheap because nothing is actually absent: the binary is
already on disk in @ladybugdb/core-<platform>-<arch>, and the skipped
script only copied it up. Redo that copy (prebuilt only — never a source
build, never a network fetch) before reporting failure. Best-effort by
construction: read-only node_modules, an absent sub-package or an
unsupported platform all fall through to the existing diagnostics
unchanged, which a test pins.
Also covers pnpm dlx without --allow-build and npm --ignore-scripts.
Declare trustedDependencies so a plain `bun install` in this repo
produces a working native binary too — the remedy the error message
already prescribes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016R9psS9gJ73MRyquoBoPKg
* fix(ai-context): name every install-free runner in the generated bootstrap note
The emitted gitnexus:start block told a reader with no runner yet to run
`npx gitnexus analyze`, falling back to a global npm install. Both name
binaries a bun-only machine does not have, so the generated AGENTS.md and
CLAUDE.md offered it no reachable bootstrap path.
List npx, bunx and pnpm dlx instead of resolving one. The block is
committed, so emitting the command this machine happens to resolve would
make two contributors on different package managers rewrite it at each
other on every analyze — the per-machine churn #1706 removed. Naming all
three keeps the note machine-independent and correct everywhere.
Regenerates this repo's own committed block to match.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016R9psS9gJ73MRyquoBoPKg
* fix(cli): address PR #2765 review — bunx liveness, restore diagnostics, docs
Addresses all five review comments on #2765.
P1 — `hasBun()` was a PATH-existence check only, so a present-but-broken
`bunx` shim (partial uninstall, failed `bun upgrade`) was selected with no
functional validation. Because selecting `bun` also suppresses the npm-11
npx-crash warning, the result was a silent dead end: no diagnostic, and a
`bunx gitnexus@latest analyze` command that only fails at execution time.
Add `probeRuns()` — a real `bunx --version` liveness probe, gated behind the
cheap spawn-free PATH scan so machines with npm/pnpm still pay nothing. It
ignores the output on purpose (a banner or unparseable version still counts
as alive); only a spawn failure, non-zero exit, or timeout rejects. Injectable
via a new `bunRuns` dep so the mode tests stay host-independent.
P2 — the `gitnexus-cli` skill (and both shipped mirrors) still described the
pre-bunx ladder, stranding exactly this PR's audience: a bun-only machine
whose agent bootstraps from that file was told to use npx/npm/pnpm, none of
which exist there. All three copies now name `bunx` in the ladder and the
bootstrap fallback, with a `shipped-skills-sync` fragment assertion so the
gap is CI-caught (these copies are not byte-compared, only the engineering
family is).
P2 — `restorePrebuiltNativeBinary` collapsed every failure into `false`, so an
EACCES/EROFS from `copyFileSync` was indistinguishable from "no prebuilt
sub-package exists". Users on a read-only `node_modules` layer (a baked
container image mounted read-only — a common CI pattern) got the generic
lifecycle-script advice, which cannot fix a non-writable filesystem. Return a
`RestoreOutcome` instead and route `copy-failed` to its own message.
P2 — document that `trustedDependencies` only takes effect for `bun install` /
`pnpm install` run inside this repo: it does nothing for a `bunx` one-shot or
for a consumer's `bun add gitnexus`. The note sits on
`restorePrebuiltNativeBinary` so a future maintainer cannot mistake that
function for redundant and delete the thing the bunx path actually relies on.
P3 — the `binary_missing` bun advice told `bunx` one-shot users to edit a
package.json they do not have, and listed 1 of the 3 packages this package
now trusts. Both repair messages now share one `BUN_REPAIR_LINES` const with
the full package list and a `bun install -g gitnexus` alternative.
Also: shortened the bootstrap note and raised the CLAUDE.md block budget
2900 -> 2950. The note has to name every install-free runner (that is the
point of the bun lane), and main's own growth since this PR's last green CI
had already pushed the generated block over the old ceiling.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015epfxkEMsmHFNVSFQqAkB4
* refactor(cli): simplify the #2765 review fixes
Cleanup pass over the previous commit — no intended behavior change except
the doctor status line noted below.
Reuse: `probeRuns()` duplicated `probeVersion()`'s entire spawn setup — same
argv, timeout, `windowsHide`, and the CVE-2024-27980 Windows-shim workaround —
in a file with two byte-identical committed copies, so the shim rule lived at
four sites. Its docstring's own objection was to the RETURN SHAPE, not to
reuse, so `probeVersion` now returns `{ ran, major, minor }` and `hasBun` reads
`.ran`. Existing callers only read `major`/`minor`, so nothing else changes.
Also dropped a pointless `const runs = () => …` thunk (`&&` already
short-circuits), and deleted a new test that was a character-for-character
duplicate of `falls back to npx when npm is null-absent and pnpm is also
absent` — its cheapest-first-gate rationale moved into that test's comment.
Correctness in the budget comment: the claim that the bun rung is free because
"pnpm is absent there, so its probe never ran" was wrong. `formatAnalyzeCommand`
spawns `pnpm --version` unconditionally when no global `gitnexus` is on PATH —
that spawn IS how pnpm presence is discovered. Real worst case is 5 subprocesses
/ ~8s, and the 8s needs Windows (`shell: true` spawns cmd.exe for an absent
pnpm); on POSIX an absent pnpm ENOENTs in ~1ms. Comment now says that. Likewise
"a machine with npm or pnpm never pays" was wrong for npm 11+ without pnpm —
that IS the rung that pays.
Altitude: `copy-failed` changed only the message text while still returning
`kind: 'binary_missing'`, so `doctor` would have printed "✗ lbugjs.node missing"
directly above a message saying the binary IS present — exactly the
contradiction #2672 removed. Added a `binary_unwritable` kind, a doctor case,
and a `nativeStatusCases` row. The binary-missing message construction moved
out of `checkLbugNative` into `unrestorableBinaryFailure`, typed
`Exclude<RestoreOutcome, 'restored'>` so a new outcome forces a decision
instead of silently inheriting the lifecycle-script advice.
Drift: the trusted-package list was hand-spelled in five places in
native-check.ts, with "matches gitnexus/package.json" asserted only in a
comment. All five now render from one `NATIVE_BUILD_PACKAGES` const (rendered
output is byte-identical), and the test reads the list out of package.json
instead of restating it, so a fourth native package fails the test rather than
silently shipping stale advice.
Finally, replaced the absolute CLAUDE.md block cap with the ratio the two prior
justifications actually appealed to (`< 5465 * 0.55`). Raising 2700 -> 2900 ->
2950 was a ratchet with no ratchet: an absolute cap can only fail on the PR
that adds the character, and the fix is always to nudge the number. Also fixed
a stale runner ladder in skills-steering.test.ts that still omitted bunx.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015epfxkEMsmHFNVSFQqAkB4
---------
Co-authored-by: drdave-flexnteos <revenaugh.david@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
307 lines
14 KiB
TypeScript
307 lines
14 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
import fs from 'fs/promises';
|
|
import { checkLbugNative, glibcTooOldMessage } from '../../src/core/lbug/native-check.js';
|
|
|
|
describe('checkLbugNative', () => {
|
|
it('returns ok:true when the real @ladybugdb/core binary is present', () => {
|
|
const result = checkLbugNative();
|
|
expect(result.ok).toBe(true);
|
|
expect(result.binaryPath).toBeDefined();
|
|
expect(result.message).toBeUndefined();
|
|
});
|
|
|
|
it('returns ok:false with repair instructions when lbugjs.node is missing', async () => {
|
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
|
|
try {
|
|
await fs.writeFile(path.join(tmpDir, 'install.js'), '');
|
|
|
|
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');
|
|
// Every package gitnexus/package.json actually trusts, not just the first —
|
|
// a partial list leaves one unbuilt and the "repair" only half works. Read
|
|
// from the manifest rather than restated, so adding a native package fails
|
|
// here instead of silently shipping stale advice.
|
|
const manifest = JSON.parse(
|
|
await fs.readFile(path.join(__dirname, '..', '..', 'package.json'), 'utf-8'),
|
|
) as { trustedDependencies: string[] };
|
|
expect(manifest.trustedDependencies.length).toBeGreaterThan(0);
|
|
expect(result.message).toContain(
|
|
`"trustedDependencies": [${manifest.trustedDependencies.map((p) => `"${p}"`).join(', ')}]`,
|
|
);
|
|
for (const pkg of manifest.trustedDependencies) {
|
|
expect(result.message).toContain(`--allow-build=${pkg}`);
|
|
}
|
|
// A `bunx gitnexus@latest …` one-shot has no package.json to edit, so the
|
|
// trustedDependencies advice alone is unactionable for this PR's audience.
|
|
expect(result.message).toContain('bun install -g gitnexus');
|
|
expect(result.message).toContain('ignore-scripts');
|
|
expect(result.message).toContain('--allow-build=@ladybugdb/core');
|
|
expect(result.message).toContain('pnpm add -g --allow-build=@ladybugdb/core');
|
|
const allowBuildIdx = result.message!.indexOf('--allow-build=@ladybugdb/core');
|
|
const dlxIdx = result.message!.indexOf('dlx gitnexus');
|
|
expect(allowBuildIdx).toBeGreaterThanOrEqual(0);
|
|
expect(dlxIdx).toBeGreaterThan(allowBuildIdx);
|
|
} finally {
|
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
// The `<name>-<platform>-<arch>` sub-package layout restorePrebuiltNativeBinary
|
|
// derives is encoded here once, so the two tests that depend on it cannot drift
|
|
// apart — and the negative test below stays honest about WHY it finds nothing.
|
|
async function prebuiltFixture(prefix: string) {
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
const pkgDir = path.join(root, 'node_modules', '@ladybugdb', 'core');
|
|
const subPkgDir = path.join(
|
|
root,
|
|
'node_modules',
|
|
'@ladybugdb',
|
|
`core-${process.platform}-${process.arch}`,
|
|
);
|
|
await fs.mkdir(pkgDir, { recursive: true });
|
|
await fs.mkdir(subPkgDir, { recursive: true });
|
|
await fs.writeFile(path.join(pkgDir, 'package.json'), '{"name":"@ladybugdb/core"}');
|
|
await fs.writeFile(path.join(subPkgDir, 'package.json'), '{"name":"sub"}');
|
|
return { root, pkgDir, subPkgDir };
|
|
}
|
|
|
|
it('restores a missing lbugjs.node from the prebuilt platform sub-package', async () => {
|
|
// The "install lifecycle script was skipped" case is recoverable without a
|
|
// network fetch: the binary is already on disk in the per-platform optional
|
|
// sub-package and the skipped script only copied it up. Load-bearing for
|
|
// `bunx gitnexus@latest …` — bun skips lifecycle scripts for a bunx fetch,
|
|
// offers no per-invocation opt-in, and re-extracts on every run, so an
|
|
// out-of-band repair cannot survive to the next invocation.
|
|
const { root, pkgDir, subPkgDir } = await prebuiltFixture('lbug-restore-');
|
|
try {
|
|
// A real, loadable binary so the check reaches ok:true rather than
|
|
// stopping at the load probe — this asserts the whole path, not just the copy.
|
|
const realPath = checkLbugNative().binaryPath;
|
|
expect(realPath).toBeDefined();
|
|
await fs.copyFile(realPath!, path.join(subPkgDir, 'lbugjs.node'));
|
|
|
|
const result = checkLbugNative(pkgDir);
|
|
|
|
expect(result.ok).toBe(true);
|
|
await expect(fs.access(path.join(pkgDir, 'lbugjs.node'))).resolves.toBeUndefined();
|
|
} finally {
|
|
await fs.rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('still reports binary_missing when no prebuilt sub-package exists to restore from', async () => {
|
|
// Recovery is best-effort: with nothing to copy, the existing diagnostics
|
|
// must survive verbatim rather than being masked by a failed restore.
|
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-restore-none-'));
|
|
try {
|
|
const pkgDir = path.join(root, 'node_modules', '@ladybugdb', 'core');
|
|
await fs.mkdir(pkgDir, { recursive: true });
|
|
await fs.writeFile(path.join(pkgDir, 'package.json'), '{"name":"@ladybugdb/core"}');
|
|
await fs.writeFile(path.join(pkgDir, 'install.js'), '');
|
|
|
|
const result = checkLbugNative(pkgDir);
|
|
|
|
expect(result.ok).toBe(false);
|
|
expect(result.kind).toBe('binary_missing');
|
|
expect(result.message).toContain('install.js');
|
|
} finally {
|
|
await fs.rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
// POSIX-and-non-root only: the trigger is a real EACCES out of copyFileSync.
|
|
// Windows chmod is a no-op on directories and root bypasses the mode bits, so
|
|
// on either the copy would succeed and there would be nothing to assert.
|
|
it.skipIf(process.platform === 'win32' || process.getuid?.() === 0)(
|
|
'separates a refused copy from a missing prebuilt (read-only node_modules)',
|
|
async () => {
|
|
// A container image with node_modules baked into a read-only layer: the
|
|
// prebuilt binary is right there and readable, only the copy-up is blocked.
|
|
// The lifecycle-script cause list (trustedDependencies / --allow-build /
|
|
// ignore-scripts) is the wrong remedy — no build-script permission makes a
|
|
// read-only filesystem writable — so it must not be what the user is shown.
|
|
const { root, pkgDir, subPkgDir } = await prebuiltFixture('lbug-restore-ro-');
|
|
try {
|
|
await fs.writeFile(path.join(subPkgDir, 'lbugjs.node'), Buffer.from('prebuilt'));
|
|
await fs.chmod(pkgDir, 0o555);
|
|
|
|
const result = checkLbugNative(pkgDir);
|
|
|
|
expect(result.ok).toBe(false);
|
|
// Its own kind, not binary_missing: doctor's status line switches on this,
|
|
// and "missing" would contradict the message printed beneath it (#2672).
|
|
expect(result.kind).toBe('binary_unwritable');
|
|
expect(result.message).toContain('permission problem');
|
|
expect(result.message).toContain('IS present in the platform sub-package');
|
|
// The mechanisms may be NAMED (to say they will not help) but must never
|
|
// be PRESCRIBED — no copy-pasteable line that sends the user round the
|
|
// build-script loop again.
|
|
expect(result.message).toContain('NOT help here');
|
|
expect(result.message).not.toContain('"trustedDependencies": [');
|
|
expect(result.message).not.toContain('pnpm --allow-build=');
|
|
expect(result.message).not.toContain('Common causes:');
|
|
} finally {
|
|
// Restore write permission or the tree cannot be removed.
|
|
await fs.chmod(pkgDir, 0o755).catch(() => {});
|
|
await fs.rm(root, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|
|
|
|
it('returns ok:false when lbugjs.node exists but is unloadable (zero-byte)', async () => {
|
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
|
|
try {
|
|
await fs.writeFile(path.join(tmpDir, 'lbugjs.node'), Buffer.alloc(0));
|
|
|
|
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 {
|
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it('returns ok:false when lbugjs.node is truncated (loader crashes with a signal)', async () => {
|
|
// A partially written .node (valid header, missing pages) SIGBUSes dlopen — a
|
|
// signal, not a catchable throw. The out-of-process probe must observe the
|
|
// crash and report it, instead of the whole process dying with exit 135 (#2441).
|
|
const realPath = checkLbugNative().binaryPath;
|
|
expect(realPath).toBeDefined();
|
|
const truncated = (await fs.readFile(realPath!)).subarray(0, 300_000);
|
|
|
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
|
|
try {
|
|
await fs.writeFile(path.join(tmpDir, 'install.js'), '');
|
|
await fs.writeFile(path.join(tmpDir, 'lbugjs.node'), truncated);
|
|
|
|
const result = checkLbugNative(tmpDir);
|
|
|
|
expect(result.ok).toBe(false);
|
|
expect(result.message).toContain('failed to load');
|
|
expect(result.message).toContain('install.js');
|
|
} finally {
|
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
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,
|
|
// so a healthy one must not be condemned; the command's own load stays
|
|
// authoritative. (Binary content is irrelevant here — the probe never runs.)
|
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
|
|
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'), '');
|
|
process.execPath = path.join(tmpDir, 'definitely-not-node');
|
|
|
|
const result = checkLbugNative(tmpDir);
|
|
|
|
expect(result.ok).toBe(true);
|
|
expect(result.message).toBeUndefined();
|
|
} finally {
|
|
process.execPath = originalExecPath;
|
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|