start-command 0.39.0 → 0.39.2
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/CHANGELOG.md +14 -0
- package/README.md +2 -1
- package/package.json +2 -1
- package/src/lib/cgroup-memory.js +35 -11
- package/src/lib/failure-handler.js +12 -6
- package/src/lib/log-sanitizer.js +303 -220
- package/src/lib/log-uploader.js +6 -3
- package/src/lib/screen-isolation.js +72 -42
- package/test/isolation.js +32 -0
- package/test/log-sanitizer.js +302 -135
- package/test/regression-247.js +140 -0
|
@@ -100,6 +100,28 @@ function supportsLogfileOption() {
|
|
|
100
100
|
return version.patch >= 1;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Polls without the session in `screen -ls` and without an exit code before
|
|
105
|
+
* the session counts as gone. `screen -dmS` can return before the session is
|
|
106
|
+
* listed, so a missing session alone is no proof that the command finished;
|
|
107
|
+
* its exit code is written just before the session ends.
|
|
108
|
+
*/
|
|
109
|
+
const SCREEN_UNLISTED_GRACE_POLLS = 20;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether an attached screen run is over.
|
|
113
|
+
* @param {boolean} listed - The session is in `screen -ls`
|
|
114
|
+
* @param {boolean} exitCodeReady - The exit-code sidecar has content (or no
|
|
115
|
+
* sidecar is expected, as for a bare shell)
|
|
116
|
+
* @param {number} unlistedPolls - Consecutive polls without the session
|
|
117
|
+
* @returns {boolean}
|
|
118
|
+
*/
|
|
119
|
+
function isScreenRunFinished(listed, exitCodeReady, unlistedPolls) {
|
|
120
|
+
return (
|
|
121
|
+
!listed && (exitCodeReady || unlistedPolls >= SCREEN_UNLISTED_GRACE_POLLS)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
103
125
|
/**
|
|
104
126
|
* Run command in GNU Screen using detached mode with log capture.
|
|
105
127
|
*
|
|
@@ -346,58 +368,64 @@ function runScreenWithLogCapture(
|
|
|
346
368
|
const checkInterval = 100; // ms
|
|
347
369
|
const maxWait = 300000; // 5 minutes max
|
|
348
370
|
let waited = 0;
|
|
371
|
+
let unlistedPolls = 0;
|
|
372
|
+
|
|
373
|
+
const exitCodeReady = () => {
|
|
374
|
+
if (isBareShell) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
return fs.readFileSync(exitCodeFile, 'utf8').trim() !== '';
|
|
379
|
+
} catch {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const finish = () => {
|
|
385
|
+
readAndDisplayOutput().then((output) => {
|
|
386
|
+
const exitCode = readExitCode();
|
|
387
|
+
cleanupTempFiles();
|
|
388
|
+
resolve({
|
|
389
|
+
success: exitCode === 0,
|
|
390
|
+
sessionName,
|
|
391
|
+
message: `Screen session "${sessionName}" exited with code ${exitCode}`,
|
|
392
|
+
exitCode,
|
|
393
|
+
output,
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
};
|
|
349
397
|
|
|
350
398
|
const checkCompletion = () => {
|
|
399
|
+
let listed;
|
|
351
400
|
try {
|
|
352
401
|
// Check if session still exists
|
|
353
|
-
|
|
402
|
+
listed = execSync('screen -ls', {
|
|
354
403
|
encoding: 'utf8',
|
|
355
404
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
if (!sessions.includes(sessionName)) {
|
|
359
|
-
// Session ended, read output and exit code
|
|
360
|
-
readAndDisplayOutput().then((output) => {
|
|
361
|
-
const exitCode = readExitCode();
|
|
362
|
-
cleanupTempFiles();
|
|
363
|
-
resolve({
|
|
364
|
-
success: exitCode === 0,
|
|
365
|
-
sessionName,
|
|
366
|
-
message: `Screen session "${sessionName}" exited with code ${exitCode}`,
|
|
367
|
-
exitCode,
|
|
368
|
-
output,
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
waited += checkInterval;
|
|
375
|
-
if (waited >= maxWait) {
|
|
376
|
-
cleanupTempFiles();
|
|
377
|
-
resolve({
|
|
378
|
-
success: false,
|
|
379
|
-
sessionName,
|
|
380
|
-
message: `Screen session "${sessionName}" timed out after ${maxWait / 1000} seconds`,
|
|
381
|
-
exitCode: 1,
|
|
382
|
-
});
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
setTimeout(checkCompletion, checkInterval);
|
|
405
|
+
}).includes(sessionName);
|
|
387
406
|
} catch {
|
|
388
407
|
// screen -ls failed, session probably ended
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
408
|
+
listed = false;
|
|
409
|
+
}
|
|
410
|
+
unlistedPolls = listed ? 0 : unlistedPolls + 1;
|
|
411
|
+
if (isScreenRunFinished(listed, exitCodeReady(), unlistedPolls)) {
|
|
412
|
+
finish();
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
waited += checkInterval;
|
|
417
|
+
if (waited >= maxWait) {
|
|
418
|
+
cleanupTempFiles();
|
|
419
|
+
resolve({
|
|
420
|
+
success: false,
|
|
421
|
+
sessionName,
|
|
422
|
+
message: `Screen session "${sessionName}" timed out after ${maxWait / 1000} seconds`,
|
|
423
|
+
exitCode: 1,
|
|
399
424
|
});
|
|
425
|
+
return;
|
|
400
426
|
}
|
|
427
|
+
|
|
428
|
+
setTimeout(checkCompletion, checkInterval);
|
|
401
429
|
};
|
|
402
430
|
|
|
403
431
|
// Start checking after a brief delay
|
|
@@ -522,7 +550,9 @@ function resetScreenVersionCache() {
|
|
|
522
550
|
}
|
|
523
551
|
|
|
524
552
|
module.exports = {
|
|
553
|
+
SCREEN_UNLISTED_GRACE_POLLS,
|
|
525
554
|
getScreenVersion,
|
|
555
|
+
isScreenRunFinished,
|
|
526
556
|
supportsLogfileOption,
|
|
527
557
|
runScreenWithLogCapture,
|
|
528
558
|
startDetachedScreenWithLogCapture,
|
package/test/isolation.js
CHANGED
|
@@ -812,3 +812,35 @@ describe('isInteractiveShellCommand (issue #84)', () => {
|
|
|
812
812
|
assert.strictEqual(isInteractiveShellCommand(' '), false);
|
|
813
813
|
});
|
|
814
814
|
});
|
|
815
|
+
|
|
816
|
+
describe('isScreenRunFinished', () => {
|
|
817
|
+
// `screen -dmS` can return before `screen -ls` lists the session; a failed
|
|
818
|
+
// command then read as exit 0 (Rust CI run 39873000384).
|
|
819
|
+
const {
|
|
820
|
+
SCREEN_UNLISTED_GRACE_POLLS,
|
|
821
|
+
isScreenRunFinished,
|
|
822
|
+
} = require('../src/lib/screen-isolation');
|
|
823
|
+
|
|
824
|
+
it('waits while the session is listed', () => {
|
|
825
|
+
assert.strictEqual(isScreenRunFinished(true, true, 0), false);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it('finishes once the session is gone and its exit code is written', () => {
|
|
829
|
+
assert.strictEqual(isScreenRunFinished(false, true, 1), true);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it('keeps polling an unlisted session without an exit code', () => {
|
|
833
|
+
assert.strictEqual(isScreenRunFinished(false, false, 1), false);
|
|
834
|
+
assert.strictEqual(
|
|
835
|
+
isScreenRunFinished(false, false, SCREEN_UNLISTED_GRACE_POLLS - 1),
|
|
836
|
+
false
|
|
837
|
+
);
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
it('gives up after the grace polls', () => {
|
|
841
|
+
assert.strictEqual(
|
|
842
|
+
isScreenRunFinished(false, false, SCREEN_UNLISTED_GRACE_POLLS),
|
|
843
|
+
true
|
|
844
|
+
);
|
|
845
|
+
});
|
|
846
|
+
});
|