hammerkit 1.27.0-next.3 → 1.27.0-next.4
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.
|
@@ -5,6 +5,7 @@ const commander_1 = require("commander");
|
|
|
5
5
|
const log_1 = require("../log");
|
|
6
6
|
const server_config_1 = require("./server-config");
|
|
7
7
|
const server_1 = require("./server");
|
|
8
|
+
const abort_on_signals_1 = require("../utils/abort-on-signals");
|
|
8
9
|
// Runs this machine as a hammerkit server until interrupted. Like `remote`, it
|
|
9
10
|
// only needs its own config file, not a build file.
|
|
10
11
|
function registerServerCommand(program, environment) {
|
|
@@ -13,31 +14,46 @@ function registerServerCommand(program, environment) {
|
|
|
13
14
|
.description('serve this machine to remote runs (experimental)')
|
|
14
15
|
.addOption(new commander_1.Option('-c, --config <file>', 'server config file').env('HAMMERKIT_SERVER_CONFIG'))
|
|
15
16
|
.action(async (options) => {
|
|
17
|
+
// SIGHUP reads the entitlements again, so who may do what changes without a
|
|
18
|
+
// restart; the rest of the config needs one. The server takes it over from the
|
|
19
|
+
// abort of the CLI, which stops other commands on it, before it starts: a SIGHUP
|
|
20
|
+
// while it reads its config and starts waits for it. Windows sends SIGHUP when the
|
|
21
|
+
// console closes, which stops the server there too.
|
|
22
|
+
let server;
|
|
23
|
+
let reloadWhenStarted = false;
|
|
24
|
+
const reload = async (running) => {
|
|
25
|
+
try {
|
|
26
|
+
const result = running.reload(await (0, server_config_1.readServerConfig)(environment, options.config));
|
|
27
|
+
if (result.restart.length > 0) {
|
|
28
|
+
environment.stdout.write(`changed but not reloaded, restart for: ${result.restart.join(', ')}\n`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
environment.stdout.write(`entitlements were not reloaded: ${(0, log_1.getErrorMessage)(e)}\n`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const releaseReload = process.platform !== 'win32'
|
|
36
|
+
? (0, abort_on_signals_1.takeOverSignal)(process, 'SIGHUP', () => {
|
|
37
|
+
if (server) {
|
|
38
|
+
void reload(server);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
reloadWhenStarted = true;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
: () => undefined;
|
|
16
45
|
try {
|
|
17
46
|
if (!options.config) {
|
|
18
47
|
throw new Error('pass the server config with --config or HAMMERKIT_SERVER_CONFIG');
|
|
19
48
|
}
|
|
20
49
|
const config = await (0, server_config_1.readServerConfig)(environment, options.config);
|
|
21
|
-
const
|
|
50
|
+
const running = await (0, server_1.startServer)(config, environment, {
|
|
22
51
|
log: (line) => environment.stdout.write(`${line}\n`),
|
|
23
52
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const result = server.reload(await (0, server_config_1.readServerConfig)(environment, options.config));
|
|
30
|
-
if (result.restart.length > 0) {
|
|
31
|
-
environment.stdout.write(`changed but not reloaded, restart for: ${result.restart.join(', ')}\n`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
catch (e) {
|
|
35
|
-
environment.stdout.write(`entitlements were not reloaded: ${(0, log_1.getErrorMessage)(e)}\n`);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const onReload = () => void reload();
|
|
39
|
-
if (process.platform !== 'win32') {
|
|
40
|
-
process.on('SIGHUP', onReload);
|
|
53
|
+
server = running;
|
|
54
|
+
environment.stdout.write(`hammerkit server listening on ${running.url}\n`);
|
|
55
|
+
if (reloadWhenStarted) {
|
|
56
|
+
void reload(running);
|
|
41
57
|
}
|
|
42
58
|
await new Promise((resolve) => {
|
|
43
59
|
if (environment.abortCtrl.signal.aborted) {
|
|
@@ -45,8 +61,8 @@ function registerServerCommand(program, environment) {
|
|
|
45
61
|
}
|
|
46
62
|
environment.abortCtrl.signal.addEventListener('abort', () => resolve(), { once: true });
|
|
47
63
|
});
|
|
48
|
-
|
|
49
|
-
await
|
|
64
|
+
releaseReload();
|
|
65
|
+
await running.close();
|
|
50
66
|
environment.stdout.write('hammerkit server stopped\n');
|
|
51
67
|
}
|
|
52
68
|
catch (e) {
|
|
@@ -55,6 +71,9 @@ function registerServerCommand(program, environment) {
|
|
|
55
71
|
}
|
|
56
72
|
program.error(`Server was not successful: ${(0, log_1.getErrorMessage)(e)}`, { exitCode: 1 });
|
|
57
73
|
}
|
|
74
|
+
finally {
|
|
75
|
+
releaseReload();
|
|
76
|
+
}
|
|
58
77
|
});
|
|
59
78
|
}
|
|
60
79
|
exports.registerServerCommand = registerServerCommand;
|
|
@@ -1,17 +1,57 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.abortOnSignals = exports.ABORT_SIGNALS = void 0;
|
|
3
|
+
exports.takeOverSignal = exports.abortOnSignals = exports.ABORT_SIGNALS = void 0;
|
|
4
4
|
// Ctrl-C (SIGINT), a CI job being cancelled or a supervisor stopping hammerkit
|
|
5
5
|
// (SIGTERM) and a closed terminal (SIGHUP) all stop the run the same way: abort,
|
|
6
6
|
// so services are removed and local tasks' process groups are killed. Without a
|
|
7
7
|
// handler Node exits on SIGTERM/SIGHUP at once and leaves both behind.
|
|
8
8
|
exports.ABORT_SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
// signals a running command gives a meaning of its own (the server reloads its
|
|
10
|
+
// entitlements on SIGHUP), per source and counted by how many hold them: they don't
|
|
11
|
+
// abort the run
|
|
12
|
+
const takenOver = new WeakMap();
|
|
13
|
+
function heldOn(source) {
|
|
14
|
+
let held = takenOver.get(source);
|
|
15
|
+
if (!held) {
|
|
16
|
+
held = new Map();
|
|
17
|
+
takenOver.set(source, held);
|
|
14
18
|
}
|
|
19
|
+
return held;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Stops the run on the abort signals, all but those a command took over with
|
|
23
|
+
* `takeOverSignal`. Returns what removes the listeners again.
|
|
24
|
+
*/
|
|
25
|
+
function abortOnSignals(source, abortCtrl) {
|
|
26
|
+
const listeners = exports.ABORT_SIGNALS.map((signal) => {
|
|
27
|
+
const listener = () => {
|
|
28
|
+
if (!takenOver.get(source)?.get(signal)) {
|
|
29
|
+
abortCtrl.abort();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
source.on(signal, listener);
|
|
33
|
+
return { signal, listener };
|
|
34
|
+
});
|
|
35
|
+
return () => listeners.forEach(({ signal, listener }) => source.off(signal, listener));
|
|
15
36
|
}
|
|
16
37
|
exports.abortOnSignals = abortOnSignals;
|
|
38
|
+
/**
|
|
39
|
+
* Calls `handler` on `signal` instead of stopping the run, until the returned function
|
|
40
|
+
* gives the signal back.
|
|
41
|
+
*/
|
|
42
|
+
function takeOverSignal(source, signal, handler) {
|
|
43
|
+
const held = heldOn(source);
|
|
44
|
+
held.set(signal, (held.get(signal) ?? 0) + 1);
|
|
45
|
+
source.on(signal, handler);
|
|
46
|
+
let released = false;
|
|
47
|
+
return () => {
|
|
48
|
+
if (released) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
released = true;
|
|
52
|
+
source.off(signal, handler);
|
|
53
|
+
held.set(signal, (held.get(signal) ?? 1) - 1);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
exports.takeOverSignal = takeOverSignal;
|
|
17
57
|
//# sourceMappingURL=abort-on-signals.js.map
|
package/package.json
CHANGED