git-watchtower 1.12.0 → 1.12.1
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 +20 -6
- package/package.json +1 -1
- package/src/server/process.js +29 -4
package/bin/git-watchtower.js
CHANGED
|
@@ -389,6 +389,20 @@ let AUTO_PULL = true;
|
|
|
389
389
|
const MAX_LOG_ENTRIES = 10;
|
|
390
390
|
const MAX_SERVER_LOG_LINES = 500;
|
|
391
391
|
|
|
392
|
+
// Timing constants (ms)
|
|
393
|
+
/** Grace period before SIGKILLing a process after SIGTERM. */
|
|
394
|
+
const FORCE_KILL_GRACE_MS = 3000;
|
|
395
|
+
/** Additional grace period added to a command's timeout before SIGKILL. */
|
|
396
|
+
const SIGKILL_GRACE_AFTER_TIMEOUT_MS = 5000;
|
|
397
|
+
/** Delay between stopping and restarting the dev server. */
|
|
398
|
+
const SERVER_RESTART_DELAY_MS = 500;
|
|
399
|
+
/** How long a transient flash message stays on screen. */
|
|
400
|
+
const FLASH_MESSAGE_DURATION_MS = 3000;
|
|
401
|
+
/** Debounce window for file watcher events before notifying clients. */
|
|
402
|
+
const FILE_WATCHER_DEBOUNCE_MS = 100;
|
|
403
|
+
/** Max time to wait for the static HTTP server to close on shutdown. */
|
|
404
|
+
const SERVER_CLOSE_TIMEOUT_MS = 2000;
|
|
405
|
+
|
|
392
406
|
// Telemetry session tracking
|
|
393
407
|
let branchSwitchCount = 0;
|
|
394
408
|
let sessionStartTime = null;
|
|
@@ -742,7 +756,7 @@ function stopServerProcess() {
|
|
|
742
756
|
} catch (e) {
|
|
743
757
|
// Process group may already be dead
|
|
744
758
|
}
|
|
745
|
-
},
|
|
759
|
+
}, FORCE_KILL_GRACE_MS);
|
|
746
760
|
|
|
747
761
|
// Clear the force-kill timer if the process exits cleanly
|
|
748
762
|
proc.once('close', () => {
|
|
@@ -760,7 +774,7 @@ function restartServerProcess() {
|
|
|
760
774
|
setTimeout(() => {
|
|
761
775
|
startServerProcess();
|
|
762
776
|
render();
|
|
763
|
-
},
|
|
777
|
+
}, SERVER_RESTART_DELAY_MS);
|
|
764
778
|
}
|
|
765
779
|
|
|
766
780
|
// Network and polling state
|
|
@@ -857,7 +871,7 @@ function execCli(cmd, args = [], options = {}) {
|
|
|
857
871
|
if (timeout > 0) {
|
|
858
872
|
const killTimer = setTimeout(() => {
|
|
859
873
|
try { child.kill('SIGKILL'); } catch (e) { /* already dead */ }
|
|
860
|
-
}, timeout +
|
|
874
|
+
}, timeout + SIGKILL_GRACE_AFTER_TIMEOUT_MS);
|
|
861
875
|
child.on('close', () => clearTimeout(killTimer));
|
|
862
876
|
}
|
|
863
877
|
});
|
|
@@ -1336,7 +1350,7 @@ function showFlash(message) {
|
|
|
1336
1350
|
flashTimeout = setTimeout(() => {
|
|
1337
1351
|
store.setState({ flashMessage: null });
|
|
1338
1352
|
render();
|
|
1339
|
-
},
|
|
1353
|
+
}, FLASH_MESSAGE_DURATION_MS);
|
|
1340
1354
|
}
|
|
1341
1355
|
|
|
1342
1356
|
function hideFlash() {
|
|
@@ -2194,7 +2208,7 @@ function setupFileWatcher() {
|
|
|
2194
2208
|
addLog(`File changed: ${filename}`, 'info');
|
|
2195
2209
|
notifyClients();
|
|
2196
2210
|
render();
|
|
2197
|
-
},
|
|
2211
|
+
}, FILE_WATCHER_DEBOUNCE_MS);
|
|
2198
2212
|
});
|
|
2199
2213
|
|
|
2200
2214
|
fileWatcher.on('error', (err) => {
|
|
@@ -3250,7 +3264,7 @@ async function shutdown() {
|
|
|
3250
3264
|
clients.clear();
|
|
3251
3265
|
|
|
3252
3266
|
const serverClosePromise = new Promise(resolve => server.close(resolve));
|
|
3253
|
-
const timeoutPromise = new Promise(resolve => setTimeout(resolve,
|
|
3267
|
+
const timeoutPromise = new Promise(resolve => setTimeout(resolve, SERVER_CLOSE_TIMEOUT_MS));
|
|
3254
3268
|
await Promise.race([serverClosePromise, timeoutPromise]);
|
|
3255
3269
|
}
|
|
3256
3270
|
|
package/package.json
CHANGED
package/src/server/process.js
CHANGED
|
@@ -31,8 +31,17 @@ const KILL_GRACE_PERIOD = 3000;
|
|
|
31
31
|
const RESTART_DELAY = 500;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Parse a command string into command and arguments
|
|
35
|
-
* Handles quoted strings
|
|
34
|
+
* Parse a command string into command and arguments.
|
|
35
|
+
* Handles quoted strings, backslash escapes (e.g. `\"`, `\\`, `\ `),
|
|
36
|
+
* and empty quoted arguments (`""`).
|
|
37
|
+
*
|
|
38
|
+
* Rules (POSIX-ish):
|
|
39
|
+
* - Inside single quotes, characters are literal — backslashes do NOT escape.
|
|
40
|
+
* - Inside double quotes or outside any quotes, a backslash causes the next
|
|
41
|
+
* character to be treated literally (so `\"` yields `"`, `\\` yields `\`,
|
|
42
|
+
* and `\ ` yields a literal space that doesn't split the argument).
|
|
43
|
+
* - A trailing backslash with no following character is left literal.
|
|
44
|
+
*
|
|
36
45
|
* @param {string} commandString - Command string to parse
|
|
37
46
|
* @returns {{command: string, args: string[]}}
|
|
38
47
|
*/
|
|
@@ -41,27 +50,43 @@ function parseCommand(commandString) {
|
|
|
41
50
|
let current = '';
|
|
42
51
|
let inQuotes = false;
|
|
43
52
|
let quoteChar = '';
|
|
53
|
+
// Tracks whether we've started accumulating an argument — distinguishes
|
|
54
|
+
// `""` (empty argument) from whitespace between arguments.
|
|
55
|
+
let hasCurrent = false;
|
|
44
56
|
|
|
45
57
|
for (let i = 0; i < commandString.length; i++) {
|
|
46
58
|
const char = commandString[i];
|
|
47
59
|
|
|
60
|
+
// Backslash escapes: unless we're inside single quotes, a backslash
|
|
61
|
+
// causes the next character to be treated literally. A trailing
|
|
62
|
+
// backslash (no following character) falls through and is kept literal.
|
|
63
|
+
if (char === '\\' && quoteChar !== "'" && i + 1 < commandString.length) {
|
|
64
|
+
current += commandString[i + 1];
|
|
65
|
+
hasCurrent = true;
|
|
66
|
+
i++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
if ((char === '"' || char === "'") && !inQuotes) {
|
|
49
71
|
inQuotes = true;
|
|
50
72
|
quoteChar = char;
|
|
73
|
+
hasCurrent = true;
|
|
51
74
|
} else if (char === quoteChar && inQuotes) {
|
|
52
75
|
inQuotes = false;
|
|
53
76
|
quoteChar = '';
|
|
54
77
|
} else if (char === ' ' && !inQuotes) {
|
|
55
|
-
if (
|
|
78
|
+
if (hasCurrent) {
|
|
56
79
|
args.push(current);
|
|
57
80
|
current = '';
|
|
81
|
+
hasCurrent = false;
|
|
58
82
|
}
|
|
59
83
|
} else {
|
|
60
84
|
current += char;
|
|
85
|
+
hasCurrent = true;
|
|
61
86
|
}
|
|
62
87
|
}
|
|
63
88
|
|
|
64
|
-
if (
|
|
89
|
+
if (hasCurrent) {
|
|
65
90
|
args.push(current);
|
|
66
91
|
}
|
|
67
92
|
|