git-watchtower 1.14.12 → 1.14.13
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/bin/git-watchtower.js +22 -0
- package/package.json +1 -1
- package/src/utils/pipe-error.js +43 -0
package/bin/git-watchtower.js
CHANGED
|
@@ -103,6 +103,7 @@ const store = new Store();
|
|
|
103
103
|
const { WebDashboardServer } = require('../src/server/web');
|
|
104
104
|
const { Coordinator, Worker, generateProjectId, getActiveCoordinator, tryAcquireLock, finalizeLock, removeLock, removeSocket, isProcessAlive } = require('../src/server/coordinator');
|
|
105
105
|
const monitorLock = require('../src/utils/monitor-lock');
|
|
106
|
+
const { createPipeErrorHandler } = require('../src/utils/pipe-error');
|
|
106
107
|
|
|
107
108
|
const PROJECT_ROOT = process.cwd();
|
|
108
109
|
|
|
@@ -3602,6 +3603,27 @@ process.on('SIGTERM', shutdown);
|
|
|
3602
3603
|
process.on('exit', () => {
|
|
3603
3604
|
cleanupResources();
|
|
3604
3605
|
});
|
|
3606
|
+
|
|
3607
|
+
// Defense-in-depth against a stdio pipe closing mid-run. The #13 TTY guard
|
|
3608
|
+
// stops `git-watchtower | head` at startup, but a TTY can still disappear
|
|
3609
|
+
// later (SSH drops, terminal window closes, pty tears down). Without this,
|
|
3610
|
+
// the next write() emits an async EPIPE which Node promotes to
|
|
3611
|
+
// uncaughtException — producing a crash report and telemetry noise for
|
|
3612
|
+
// what is a benign pipe-closed condition.
|
|
3613
|
+
const stdioPipeErrorHandler = createPipeErrorHandler({
|
|
3614
|
+
onEpipe: () => {
|
|
3615
|
+
isShuttingDown = true;
|
|
3616
|
+
try { cleanupResources(); } catch (_) { /* best-effort during pipe-close */ }
|
|
3617
|
+
process.exit(0);
|
|
3618
|
+
},
|
|
3619
|
+
onOther: (err) => {
|
|
3620
|
+
// Any other stdio error is unexpected — re-raise so the existing
|
|
3621
|
+
// uncaughtException handler can capture telemetry and restore terminal.
|
|
3622
|
+
setImmediate(() => { throw err; });
|
|
3623
|
+
},
|
|
3624
|
+
});
|
|
3625
|
+
process.stdout.on('error', stdioPipeErrorHandler);
|
|
3626
|
+
process.stderr.on('error', stdioPipeErrorHandler);
|
|
3605
3627
|
process.on('uncaughtException', async (err) => {
|
|
3606
3628
|
isShuttingDown = true;
|
|
3607
3629
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdio pipe error handling.
|
|
3
|
+
*
|
|
4
|
+
* The TUI writes ANSI frames to stdout continuously. If the downstream
|
|
5
|
+
* consumer goes away — SSH drops, the terminal window closes, or the
|
|
6
|
+
* user intentionally short-circuits via `| head` on a non-guarded
|
|
7
|
+
* invocation — the next write() emits an async 'error' event with
|
|
8
|
+
* code EPIPE on the stream. Without a handler, Node promotes that to
|
|
9
|
+
* uncaughtException, which logs a crash report, generates telemetry
|
|
10
|
+
* noise, and clutters the user's prompt on exit.
|
|
11
|
+
*
|
|
12
|
+
* A normal pipe-closed condition is not a crash; the correct response
|
|
13
|
+
* is to clean up and exit quietly with status 0.
|
|
14
|
+
*
|
|
15
|
+
* @module utils/pipe-error
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build an 'error' handler for process.stdout / process.stderr.
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} callbacks
|
|
24
|
+
* @param {() => void} callbacks.onEpipe - Invoked when the pipe is closed
|
|
25
|
+
* from the other end. Typically runs cleanupResources() and exits 0.
|
|
26
|
+
* @param {(err: Error) => void} callbacks.onOther - Invoked for any other
|
|
27
|
+
* stream error. Typically re-raises via setImmediate so the existing
|
|
28
|
+
* uncaughtException handler can capture telemetry.
|
|
29
|
+
* @returns {(err: Error & { code?: string }) => void}
|
|
30
|
+
*/
|
|
31
|
+
function createPipeErrorHandler({ onEpipe, onOther }) {
|
|
32
|
+
return function pipeErrorHandler(err) {
|
|
33
|
+
if (err && err.code === 'EPIPE') {
|
|
34
|
+
onEpipe();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
onOther(err);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = {
|
|
42
|
+
createPipeErrorHandler,
|
|
43
|
+
};
|