mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-20 09:08:16 -06:00
Repair a missing sqlite driver on hosts that have no built-in one
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / Unit tests (node:sqlite fallback, Node 24) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Licence gate + SBOM (production deps) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / Unit tests (node:sqlite fallback, Node 24) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Licence gate + SBOM (production deps) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
1.9.38 made better-sqlite3 optional so a compiler-less host installs cleanly and falls back to node:sqlite. That fallback is Node 24 in practice — absent on 20.x, flagged off on 22.x — and this project's declared floor is 22.9. On those runtimes a missing native driver is not a slower server, it is no server. Found by running the suite the way CI does rather than the way it is convenient to: `npm ci` on a machine that declines to run install scripts reported success, silently left no better-sqlite3 on disk, and 291 tests failed with "server did not boot" and nothing anywhere explaining why. npm drops an optional dependency in that situation where it would have installed a required one. missingDeps() now treats optionalDependencies as required whenever node:sqlite is unavailable, so preflight repairs it as it would any other missing package. The decision is split into requiredDeps(pkg, builtinDriver) so both kinds of host can be tested from either runtime, rather than only the one the suite happens to run on. Full suite 1782 pass / 0 fail on the native driver and on the fallback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014kfhrUPit5MCqxeTQyqr56
This commit is contained in:
parent
52ecef1cf0
commit
24c94deda0
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -12,7 +12,18 @@ attribution, not the operator's own name, so on an instance branded "Acme" it of
|
|||
branding.
|
||||
|
||||
It now reads "Hide platform branding", and the equivalent in the six other translated languages.
|
||||
Nothing else changed.
|
||||
|
||||
### Fixed — a missing database driver is repaired again on Node 20 and 22
|
||||
|
||||
1.9.38 made `better-sqlite3` an optional dependency, so that a host without a compiler installs
|
||||
cleanly and falls back to Node's built-in driver. That fallback needs Node 24: on the 20.x and 22.x
|
||||
lines the built-in is absent or behind a flag, and there a missing `better-sqlite3` means the server
|
||||
cannot start at all.
|
||||
|
||||
npm will quietly skip an optional dependency whose install script it declines to run — leaving an
|
||||
install that reports success and a server that will not boot. The startup check now treats optional
|
||||
dependencies as required on any host that has no built-in driver, and repairs them like any other
|
||||
missing package.
|
||||
|
||||
### Upgrading
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,32 @@ const SERVER_DIR = path.join(__dirname, '..');
|
|||
const NODE_MODULES = path.join(SERVER_DIR, 'node_modules');
|
||||
const INSTALL_TIMEOUT_MS = 10 * 60 * 1000; // a cold install on a Pi is genuinely slow
|
||||
|
||||
/**
|
||||
* Which dependency names this host actually needs on disk.
|
||||
*
|
||||
* ⚠️ OPTIONAL IS NOT OPTIONAL WHERE THERE IS NOTHING TO FALL BACK TO.
|
||||
*
|
||||
* better-sqlite3 is an optionalDependency so a host with no compiler installs cleanly and lets
|
||||
* db/sqlite-driver.js drop to the built-in node:sqlite. That only works on Node 24 (23.4+); on the
|
||||
* 20.x and 22.x lines the built-in is absent or flagged off, and there a missing better-sqlite3 is
|
||||
* a server that cannot start at all.
|
||||
*
|
||||
* npm will quietly skip an optional dependency whose install script it declines to run. Observed
|
||||
* exactly that: `npm ci` reported success, left no better-sqlite3 on disk, and every test that
|
||||
* spawns the server failed with "server did not boot" — with nothing anywhere saying why.
|
||||
*
|
||||
* Split out from missingDeps so the decision can be tested on either kind of host, rather than only
|
||||
* on whichever Node happens to be running the suite.
|
||||
*
|
||||
* @param {{dependencies?: object, optionalDependencies?: object}} pkg
|
||||
* @param {boolean} builtinDriver whether this runtime has node:sqlite
|
||||
*/
|
||||
function requiredDeps(pkg, builtinDriver) {
|
||||
const names = Object.keys((pkg && pkg.dependencies) || {});
|
||||
if (!builtinDriver) names.push(...Object.keys((pkg && pkg.optionalDependencies) || {}));
|
||||
return names;
|
||||
}
|
||||
|
||||
/** Which declared dependencies are not on disk. */
|
||||
function missingDeps() {
|
||||
let pkg;
|
||||
|
|
@ -54,7 +80,9 @@ function missingDeps() {
|
|||
} catch {
|
||||
return []; // no package.json is not our problem to diagnose
|
||||
}
|
||||
const declared = Object.keys(pkg.dependencies || {});
|
||||
let builtinDriver = false;
|
||||
try { require('node:sqlite'); builtinDriver = true; } catch { /* absent on this runtime */ }
|
||||
const declared = requiredDeps(pkg, builtinDriver);
|
||||
return declared.filter((name) => {
|
||||
// A scoped or nested name is still one directory below node_modules.
|
||||
try { return !fs.existsSync(path.join(NODE_MODULES, name, 'package.json')); } catch { return true; }
|
||||
|
|
@ -237,4 +265,4 @@ function preflight() {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = { preflight, missingDeps, nativeModuleBroken };
|
||||
module.exports = { preflight, missingDeps, nativeModuleBroken, requiredDeps };
|
||||
|
|
|
|||
|
|
@ -90,3 +90,33 @@ test('server.js runs the preflight BEFORE requiring anything', () => {
|
|||
assert.ok(preflightAt > -1, 'server.js must run the preflight');
|
||||
assert.ok(preflightAt < firstDep, 'it must come before the first dependency, or it cannot help');
|
||||
});
|
||||
|
||||
/*
|
||||
* ⚠️ AN OPTIONAL DEPENDENCY IS NOT OPTIONAL ON A HOST WITH NO FALLBACK.
|
||||
*
|
||||
* better-sqlite3 became optional so a compiler-less host installs cleanly and drops to the built-in
|
||||
* node:sqlite. But the built-in is Node 24 in practice — absent on 20.x, flagged off on 22.x — and
|
||||
* this project's declared floor is 22.9. On those runtimes a missing better-sqlite3 is not a slower
|
||||
* server, it is no server.
|
||||
*
|
||||
* This is not theoretical: `npm ci` on a machine that declines to run install scripts reported
|
||||
* success, silently left no better-sqlite3 on disk, and every test that spawns the server failed
|
||||
* with "server did not boot". Preflight has to notice and repair that.
|
||||
*/
|
||||
test('an optional dependency counts as required where node:sqlite is unavailable', () => {
|
||||
const pkg = { dependencies: { express: '^4' }, optionalDependencies: { 'better-sqlite3': '12.9.0' } };
|
||||
|
||||
const withBuiltin = preflight.requiredDeps(pkg, true);
|
||||
assert.deepEqual(withBuiltin, ['express'],
|
||||
'on Node 24 the native driver is genuinely optional — the built-in can serve');
|
||||
|
||||
const withoutBuiltin = preflight.requiredDeps(pkg, false);
|
||||
assert.deepEqual(withoutBuiltin.sort(), ['better-sqlite3', 'express'],
|
||||
'on Node 20/22 a missing native driver means the server cannot start, so repair it');
|
||||
});
|
||||
|
||||
test('requiredDeps copes with a package.json missing either section', () => {
|
||||
assert.deepEqual(preflight.requiredDeps({}, false), []);
|
||||
assert.deepEqual(preflight.requiredDeps({ optionalDependencies: { a: '1' } }, false), ['a']);
|
||||
assert.deepEqual(preflight.requiredDeps({ dependencies: { b: '1' } }, false), ['b']);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue