Skip to content

Commit 7bffeb5

Browse files
authored
Abort sl root call if output resembles a steam locomotive (#15053)
Jest detects whether a repository is a sapling repo by calling the `sl` binary, and getting the output. If `sl` (steam locomotive) is installed, the output of `sl root` 1) takes forever to get and 2) is not the root, but a moving image of a steam locomotive. This change monitors the stdout stream, and aborts the `sl` call if the first character is an escape character, which indicates that the terminal is being cleared to make way for a train to come through. See also: #14046
1 parent 85bab0e commit 7bffeb5

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
- `[babel-plugin-jest-hoist]` Use `denylist` instead of the deprecated `blacklist` for Babel 8 support ([#14109](https://github.com/jestjs/jest/pull/14109))
4141
- `[expect]` Check error instance type for `toThrow/toThrowError` ([#14576](https://github.com/jestjs/jest/pull/14576))
4242
- `[jest-changed-files]` Print underlying errors when VCS commands fail ([#15052](https://github.com/jestjs/jest/pull/15052))
43+
- `[jest-changed-files]` Abort `sl root` call if output resembles a steam locomotive ([#15053](https://github.com/jestjs/jest/pull/15053))
4344
- `[jest-circus]` [**BREAKING**] Prevent false test failures caused by promise rejections handled asynchronously ([#14315](https://github.com/jestjs/jest/pull/14315))
4445
- `[jest-circus]` Replace recursive `makeTestResults` implementation with iterative one ([#14760](https://github.com/jestjs/jest/pull/14760))
4546
- `[jest-circus]` Omit `expect.hasAssertions()` errors if a test already has errors ([#14866](https://github.com/jestjs/jest/pull/14866))

packages/jest-changed-files/src/sl.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import type {SCMAdapter} from './types';
1616
*/
1717
const env = {...process.env, HGPLAIN: '1'};
1818

19+
// Whether `sl` is a steam locomotive or not
20+
let isSteamLocomotive = false;
21+
1922
const adapter: SCMAdapter = {
2023
findChangedFiles: async (cwd, options) => {
2124
const includePaths = options.includePaths ?? [];
@@ -42,8 +45,29 @@ const adapter: SCMAdapter = {
4245
},
4346

4447
getRoot: async cwd => {
48+
if (isSteamLocomotive) {
49+
return null;
50+
}
51+
4552
try {
46-
const result = await execa('sl', ['root'], {cwd, env});
53+
const subprocess = execa('sl', ['root'], {cwd, env});
54+
55+
// Check if we're calling sl (steam locomotive) instead of sl (sapling)
56+
// by looking for the escape character in the first chunk of data.
57+
if (subprocess.stdout) {
58+
subprocess.stdout.once('data', (data: Buffer | string) => {
59+
data = Buffer.isBuffer(data) ? data.toString() : data;
60+
if (data.codePointAt(0) === 27) {
61+
subprocess.cancel();
62+
isSteamLocomotive = true;
63+
}
64+
});
65+
}
66+
67+
const result = await subprocess;
68+
if (result.killed && isSteamLocomotive) {
69+
return null;
70+
}
4771

4872
return result.stdout;
4973
} catch {

0 commit comments

Comments
 (0)