@andrian.yablonskyy/thub-coordinator 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrian.yablonskyy/thub-coordinator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "TestHub Coordinator — job queue, resource registry, scheduler, heartbeat monitor, log/artifact store and web dashboard",
|
|
5
5
|
"main": "src/server.js",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
6
9
|
"bin": {
|
|
7
10
|
"thub-admin": "./bin/thub-admin.js",
|
|
8
11
|
"thub-coordinator": "./src/server.js"
|
|
@@ -11,7 +14,7 @@
|
|
|
11
14
|
"start": "node src/server.js",
|
|
12
15
|
"dev": "node --watch src/server.js",
|
|
13
16
|
"test": "node --test test/*.test.js",
|
|
14
|
-
"postinstall": "node scripts/install-default-config.js && node scripts/install-systemd-unit.js",
|
|
17
|
+
"postinstall": "node scripts/ensure-native-deps.js && node scripts/install-default-config.js && node scripts/install-systemd-unit.js",
|
|
15
18
|
"lint": "eslint .",
|
|
16
19
|
"lint:fix": "eslint . --fix"
|
|
17
20
|
},
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file scripts/ensure-native-deps.js
|
|
3
|
+
* @description npm postinstall: makes sure better-sqlite3's native addon loads under the Node.js
|
|
4
|
+
* running this install, rebuilding it in place if not
|
|
5
|
+
*
|
|
6
|
+
* @author Andrian Yablonskyy
|
|
7
|
+
* @copyright Copyright (c) 2026 Andrian Yablonskyy. All rights reserved.
|
|
8
|
+
*
|
|
9
|
+
* This file is part of TestHub and is proprietary and confidential.
|
|
10
|
+
* Unauthorized copying, modification, distribution, or use of this file,
|
|
11
|
+
* via any medium, is strictly prohibited without prior written permission
|
|
12
|
+
* from AdSystem.PRO.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const path = require('node:path'),
|
|
18
|
+
{ execFileSync, spawnSync } = require('node:child_process'),
|
|
19
|
+
{ isGlobalInstall } = require('./install-target'),
|
|
20
|
+
|
|
21
|
+
PKG_DIR = path.join(__dirname, '..');
|
|
22
|
+
|
|
23
|
+
// Checked in a child process: once a mismatched addon has been dlopen'ed,
|
|
24
|
+
// this process keeps the old image mapped, so an in-process retry after
|
|
25
|
+
// the rebuild would still see the stale one.
|
|
26
|
+
function loads(){
|
|
27
|
+
const res = spawnSync(process.execPath, ['-e', 'new (require(\'better-sqlite3\'))(\':memory:\').close()'], {
|
|
28
|
+
cwd: PKG_DIR,
|
|
29
|
+
encoding: 'utf8'
|
|
30
|
+
});
|
|
31
|
+
if (res.status === 0){
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (/ERR_DLOPEN_FAILED|NODE_MODULE_VERSION/.test(res.stderr)){
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
throw new Error(res.stderr.trim().split('\n').find((l) => /Error/.test(l)) || `exit ${res.status}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Runs better-sqlite3's own install script (prebuild-install, falling back
|
|
41
|
+
// to node-gyp) in its own directory. Not `npm rebuild`: under `npm i -g`
|
|
42
|
+
// this script inherits npm_config_global=true, which points a nested
|
|
43
|
+
// `npm rebuild` at the global prefix instead of this package, where it
|
|
44
|
+
// "succeeds" without touching the addon at all.
|
|
45
|
+
function rebuild(){
|
|
46
|
+
const dir = path.dirname(require.resolve('better-sqlite3/package.json', { paths: [PKG_DIR] })),
|
|
47
|
+
npmCli = process.env.npm_execpath,
|
|
48
|
+
[cmd, args] = npmCli ? [process.execPath, [npmCli]] : ['npm', []];
|
|
49
|
+
execFileSync(cmd, [...args, 'run-script', 'install'], {
|
|
50
|
+
cwd: dir,
|
|
51
|
+
stdio: 'inherit',
|
|
52
|
+
env: { ...process.env, npm_config_global: 'false' }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Re-running `npm i -g` for an already-installed version after a Node
|
|
57
|
+
// major upgrade leaves unchanged dependencies alone — including
|
|
58
|
+
// better-sqlite3's addon, still built for the old Node ABI — so the server
|
|
59
|
+
// then dies with NODE_MODULE_VERSION mismatch at startup. Rebuild it here
|
|
60
|
+
// with the same node/npm that's running this install. Best-effort: never
|
|
61
|
+
// fails the install itself.
|
|
62
|
+
function main(){
|
|
63
|
+
if (!isGlobalInstall()){
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
if (loads()){
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
console.log(`thub-coordinator: rebuilding better-sqlite3 for Node.js ${process.version} (ABI ${process.versions.modules})`);
|
|
71
|
+
rebuild();
|
|
72
|
+
if (!loads()){
|
|
73
|
+
throw new Error('still fails to load after rebuild');
|
|
74
|
+
}
|
|
75
|
+
console.log('thub-coordinator: better-sqlite3 rebuilt');
|
|
76
|
+
}
|
|
77
|
+
catch (err){
|
|
78
|
+
console.warn(
|
|
79
|
+
`thub-coordinator: better-sqlite3 doesn't load under ${process.execPath} (${process.version}): ${err.message}\n` +
|
|
80
|
+
`Rebuild it manually: cd ${PKG_DIR} && sudo env "PATH=$PATH" npm rebuild better-sqlite3`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file scripts/install-default-config.js
|
|
3
3
|
* @description npm postinstall: on a real global install, creates the Coordinator's directory layout
|
|
4
|
-
* (~/.config/thub,
|
|
4
|
+
* (~/.config/thub, ~/.thub and its artifacts/avatars/work subdirs) and, if it
|
|
5
5
|
* doesn't already exist, ~/.config/thub/coordinator.json with every default option and
|
|
6
6
|
* a freshly generated sessionSecret (README §12, §13)
|
|
7
7
|
*
|
|
@@ -26,7 +26,7 @@ const fs = require('node:fs'),
|
|
|
26
26
|
// can be changed — not a copy of the bundled config.json (which uses a
|
|
27
27
|
// relative dataDir and a fixed sessionSecret for zero-setup `npm run
|
|
28
28
|
// coordinator` in the monorepo). What differs from DEFAULTS:
|
|
29
|
-
// - dataDir: home-anchored (
|
|
29
|
+
// - dataDir: home-anchored (~/.thub), not cwd-relative — a real
|
|
30
30
|
// install shouldn't get a different data directory depending on which
|
|
31
31
|
// directory thub-coordinator happened to be launched from.
|
|
32
32
|
// - sessionSecret: freshly random per install, never the shared
|
|
@@ -51,7 +51,7 @@ function chownToUser(p, user){
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Creates `dir` and chowns every directory this call created along the
|
|
54
|
-
// way (e.g. ~/.config,
|
|
54
|
+
// way (e.g. ~/.config, ~/.thub when they didn't exist yet),
|
|
55
55
|
// plus `dir` itself.
|
|
56
56
|
function mkdirOwned(dir, user){
|
|
57
57
|
const firstCreated = fs.mkdirSync(dir, { recursive: true });
|
|
@@ -52,14 +52,14 @@ function resolveTargetUser(){
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// Default layout (README §13): config under ~/.config/thub, runtime data
|
|
55
|
-
// (SQLite DB, uploaded artifacts, avatars, work files) under
|
|
55
|
+
// (SQLite DB, uploaded artifacts, avatars, work files) under ~/.thub.
|
|
56
56
|
// An existing coordinator.json's own dataDir wins over the default, so a
|
|
57
57
|
// re-install prepares (and the systemd unit allows writes to) the
|
|
58
58
|
// directory the Coordinator will really use.
|
|
59
59
|
function targetPaths(user){
|
|
60
60
|
const configDir = path.join(user.home, '.config', 'thub'),
|
|
61
61
|
configPath = path.join(configDir, 'coordinator.json'),
|
|
62
|
-
defaultDataDir = path.join(user.home, '
|
|
62
|
+
defaultDataDir = path.join(user.home, '.thub'),
|
|
63
63
|
dataDir = configuredDataDir(configPath) || defaultDataDir;
|
|
64
64
|
return {
|
|
65
65
|
configDir,
|
package/src/db/index.js
CHANGED
|
@@ -18,7 +18,7 @@ const fs = require('node:fs'),
|
|
|
18
18
|
Database = require('better-sqlite3');
|
|
19
19
|
|
|
20
20
|
function openDb(dbPath){
|
|
21
|
-
const db =
|
|
21
|
+
const db = openDatabase(dbPath);
|
|
22
22
|
db.pragma('journal_mode = WAL');
|
|
23
23
|
db.pragma('synchronous = NORMAL');
|
|
24
24
|
db.pragma('foreign_keys = ON');
|
|
@@ -26,6 +26,32 @@ function openDb(dbPath){
|
|
|
26
26
|
return db;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// better-sqlite3 is a native addon built for one Node.js ABI at install
|
|
30
|
+
// time; after a Node major upgrade it must be rebuilt. Without this, the
|
|
31
|
+
// only hint is a raw NODE_MODULE_VERSION mismatch from deep in bindings.js.
|
|
32
|
+
// The rebuild command keeps the caller's PATH under sudo, since sudo's own
|
|
33
|
+
// secure_path often finds a different (older) node than the user's shell
|
|
34
|
+
// does — rebuilding with that one just reproduces the same mismatch.
|
|
35
|
+
function openDatabase(dbPath){
|
|
36
|
+
try {
|
|
37
|
+
return new Database(dbPath);
|
|
38
|
+
}
|
|
39
|
+
catch (err){
|
|
40
|
+
if (err.code === 'ERR_DLOPEN_FAILED'){
|
|
41
|
+
const builtFor = err.message.match(/NODE_MODULE_VERSION (\d+)/)?.[1],
|
|
42
|
+
pkgDir = path.join(__dirname, '..', '..');
|
|
43
|
+
throw new Error(
|
|
44
|
+
`better-sqlite3 was built for a different Node.js ABI (${builtFor || 'unknown'}) than this one: ` +
|
|
45
|
+
`${process.version}, ABI ${process.versions.modules}, at ${process.execPath}.\n` +
|
|
46
|
+
`Rebuild it with this same node: cd ${pkgDir} && sudo env "PATH=$PATH" npm rebuild better-sqlite3\n` +
|
|
47
|
+
'If `sudo node -v` and `node -v` differ, sudo was using another Node.js for earlier installs.',
|
|
48
|
+
{ cause: err }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
function migrate(db){
|
|
30
56
|
db.exec(`
|
|
31
57
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|