Skip to content

Commit bf96b26

Browse files
committed
Refactored old server errors
1 parent 9e0ad34 commit bf96b26

File tree

3 files changed

+291
-21
lines changed

3 files changed

+291
-21
lines changed

src/main.ts

+59-10
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,7 @@ async function getTsServerRepoResult(
335335
installCommands,
336336
};
337337

338-
if (oldServerFailed && !newServerFailed) {
339-
return { status: "Detected interesting changes", tsServerResult }
340-
}
341-
if (!newServerFailed) {
338+
if (!oldServerFailed && !newServerFailed) {
342339
return { status: "Detected no interesting changes" };
343340
}
344341

@@ -404,25 +401,77 @@ function createOldErrorSummary(summaries: Summary[]): string {
404401

405402
let text = `
406403
<details>
407-
<summary>${errorMessage}</summary>
404+
<summary>New server no longer reports this error: ${errorMessage}</summary>
408405
409406
\`\`\`
410407
${oldServerError}
411408
\`\`\`
412409
413-
<h4>Repos no longer reporting the error</h4>
414-
<ul>
415-
`;
410+
<h4>Affected repos</h4>`;
416411

417412
for (const summary of summaries) {
418413
const owner = summary.repo.owner ? `${mdEscape(summary.repo.owner)}/` : "";
419414
const url = summary.repo.url ?? "";
420415

421-
text += `<li><a href="${url}">${owner + mdEscape(summary.repo.name)}</a></li>\n`
416+
text += `
417+
<details>
418+
<summary><a href="${url}">${owner + mdEscape(summary.repo.name)}</a></summary>
419+
Raw error text: <code>${summary.rawErrorArtifactPath}</code> in the <a href="${artifactFolderUrlPlaceholder}">artifact folder</a> <br />
420+
Replay commands: <code>${summary.replayScriptArtifactPath}</code> in the <a href="${artifactFolderUrlPlaceholder}">artifact folder</a>
421+
<h4>Last few requests</h4>
422+
423+
\`\`\`json
424+
${summary.replayScript}
425+
\`\`\`
426+
427+
<h4>Repro steps</h4>
428+
429+
\`\`\`bash
430+
#!/bin/bash
431+
432+
`;
433+
// No url means is user test repo
434+
if (!summary.repo.url) {
435+
text += `# Manually download user test \`${summary.repo.name}\`\n`;
436+
}
437+
else {
438+
text += `git clone ${summary.repo.url} --recurse-submodules\n`;
439+
440+
if (summary.commit) {
441+
text += `git -C "./${summary.repo.name}" reset --hard ${summary.commit}\n`;
442+
}
443+
}
444+
445+
if (summary.tsServerResult.installCommands.length > 1) {
446+
text += "# Install packages (exact steps are below, but it might be easier to follow the repo readme)\n";
447+
}
448+
for (const command of summary.tsServerResult.installCommands) {
449+
const workingDirFlag = command.tool === ip.InstallTool.Npm
450+
? "--prefix"
451+
: command.tool === ip.InstallTool.Yarn
452+
? "--cwd"
453+
: "--dir"; // pnpm
454+
455+
text += `${command.tool} ${workingDirFlag} "./${command.prettyDirectory}" ${command.arguments.join(" ")}\n`;
456+
}
457+
458+
text += `downloadUrl=$(curl -s "${getArtifactsApiUrlPlaceholder}?artifactName=${summary.resultDirName}&api-version=7.0" | jq -r ".resource.downloadUrl")
459+
wget -O ${summary.resultDirName}.zip "$downloadUrl"
460+
unzip -p ${summary.resultDirName}.zip ${summary.resultDirName}/${summary.replayScriptName} > ${summary.replayScriptName}
461+
npm install --no-save @typescript/server-replay
462+
\`\`\`
463+
464+
To run the repro:
465+
\`\`\`bash
466+
# \`npx tsreplay --help\` to learn about helpful switches for debugging, logging, etc.
467+
npx tsreplay ./${summary.repo.name} ./${summary.replayScriptName} <PATH_TO_tsserver.js>
468+
\`\`\`
469+
470+
</details>
471+
`;
422472
}
423473

424474
text += `
425-
</ul>
426475
</details>
427476
`;
428477

test/__snapshots__/main.test.ts.snap

+170
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,175 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`main outputs old server errors 1`] = `
4+
[MockFunction] {
5+
"calls": [
6+
[
7+
"<BASE_PATH>/ts_downloads/base/MockRepoOwner.MockRepoName.rawError.txt",
8+
"Req #123 - cursedCommand
9+
Some error. Could not do something.
10+
Maybe a Debug fail.
11+
at a (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:1:1)
12+
at b (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:2:2)
13+
at c (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:3:3)
14+
at d (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:4:4)
15+
at e (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:5:5)",
16+
],
17+
[
18+
"<BASE_PATH>/RepoResults123/718e48b799650d4b66e5d80ad6bac7b9.results.txt",
19+
"<h2>Maybe a Debug fail.</h2>
20+
21+
\`\`\`
22+
Req #123 - cursedCommand
23+
at a (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:1:1)
24+
at b (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:2:2)
25+
at c (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:3:3)
26+
at d (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:4:4)
27+
at e (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:5:5)
28+
\`\`\`
29+
30+
<h4>Affected repos</h4>
31+
<details>
32+
<summary><a href="https://github.com./MockRepoOwner/MockRepoName">MockRepoOwner/MockRepoName</a></summary>
33+
Raw error text: <code>RepoResults123/MockRepoOwner.MockRepoName.rawError.txt</code> in the <a href="PLACEHOLDER_ARTIFACT_FOLDER">artifact folder</a> <br />
34+
Replay commands: <code>RepoResults123/MockRepoOwner.MockRepoName.replay.txt</code> in the <a href="PLACEHOLDER_ARTIFACT_FOLDER">artifact folder</a>
35+
<h4>Last few requests</h4>
36+
37+
\`\`\`json
38+
{"rootDirPlaceholder":"@PROJECT_ROOT@","serverArgs":["--disableAutomaticTypingAcquisition"]}
39+
{"seq":1,"type":"request","command":"configure","arguments":{"preferences":{"disableLineTextInReferences":true,"includePackageJsonAutoImports":"auto","includeCompletionsForImportStatements":true,"includeCompletionsWithSnippetText":true,"includeAutomaticOptionalChainCompletions":true,"includeCompletionsWithInsertText":true,"includeCompletionsWithClassMemberSnippets":true,"allowIncompleteCompletions":true,"includeCompletionsForModuleExports":false},"watchOptions":{"excludeDirectories":["**/node_modules"]}}}
40+
{"seq":2,"type":"request","command":"updateOpen","arguments":{"changedFiles":[],"closedFiles":[],"openFiles":[{"file":"@PROJECT_ROOT@/sample_repoName.config.js","projectRootPath":"@PROJECT_ROOT@"}]}}
41+
{"seq":3,"type":"request","command":"cursedCommand","arguments":{"file":"@PROJECT_ROOT@/src/sampleTsFile.ts","line":1,"offset":1,"includeExternalModuleExports":false,"triggerKind":1}}
42+
\`\`\`
43+
44+
<h4>Repro steps</h4>
45+
46+
\`\`\`bash
47+
#!/bin/bash
48+
49+
git clone https://github.com./MockRepoOwner/MockRepoName --recurse-submodules
50+
git -C "./MockRepoName" reset --hard 57b462387e88aa7e363af0daf867a5dc1e83a935
51+
# Install packages (exact steps are below, but it might be easier to follow the repo readme)
52+
npm --prefix "./dirA" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
53+
npm --prefix "./dirB/dirC" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
54+
npm --prefix "./dirD/dirE/dirF" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
55+
downloadUrl=$(curl -s "PLACEHOLDER_GETARTIFACTS_API?artifactName=RepoResults123&api-version=7.0" | jq -r ".resource.downloadUrl")
56+
wget -O RepoResults123.zip "$downloadUrl"
57+
unzip -p RepoResults123.zip RepoResults123/MockRepoOwner.MockRepoName.replay.txt > MockRepoOwner.MockRepoName.replay.txt
58+
npm install --no-save @typescript/server-replay
59+
\`\`\`
60+
61+
To run the repro:
62+
\`\`\`bash
63+
# \`npx tsreplay --help\` to learn about helpful switches for debugging, logging, etc.
64+
npx tsreplay ./MockRepoName ./MockRepoOwner.MockRepoName.replay.txt <PATH_TO_tsserver.js>
65+
\`\`\`
66+
67+
</details>
68+
",
69+
{
70+
"encoding": "utf-8",
71+
},
72+
],
73+
[
74+
"<BASE_PATH>/RepoResults123/metadata.json",
75+
"{"newTsResolvedVersion":"1.1.1","oldTsResolvedVersion":"0.0.0","statusCounts":{"Detected interesting changes":1}}",
76+
{
77+
"encoding": "utf-8",
78+
},
79+
],
80+
[
81+
"<BASE_PATH>/RepoResults123/!718e48b799650d4b66e5d80ad6bac7b9.results.txt",
82+
"
83+
<details>
84+
<summary>New server no longer reports this error: Maybe a Debug fail.</summary>
85+
86+
\`\`\`
87+
Req #123 - cursedCommand
88+
at a (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:1:1)
89+
at b (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:2:2)
90+
at c (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:3:3)
91+
at d (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:4:4)
92+
at e (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:5:5)
93+
\`\`\`
94+
95+
<h4>Affected repos</h4>
96+
<details>
97+
<summary><a href="https://github.com./MockRepoOwner/MockRepoName">MockRepoOwner/MockRepoName</a></summary>
98+
Raw error text: <code>RepoResults123/MockRepoOwner.MockRepoName.rawError.txt</code> in the <a href="PLACEHOLDER_ARTIFACT_FOLDER">artifact folder</a> <br />
99+
Replay commands: <code>RepoResults123/MockRepoOwner.MockRepoName.replay.txt</code> in the <a href="PLACEHOLDER_ARTIFACT_FOLDER">artifact folder</a>
100+
<h4>Last few requests</h4>
101+
102+
\`\`\`json
103+
{"rootDirPlaceholder":"@PROJECT_ROOT@","serverArgs":["--disableAutomaticTypingAcquisition"]}
104+
{"seq":1,"type":"request","command":"configure","arguments":{"preferences":{"disableLineTextInReferences":true,"includePackageJsonAutoImports":"auto","includeCompletionsForImportStatements":true,"includeCompletionsWithSnippetText":true,"includeAutomaticOptionalChainCompletions":true,"includeCompletionsWithInsertText":true,"includeCompletionsWithClassMemberSnippets":true,"allowIncompleteCompletions":true,"includeCompletionsForModuleExports":false},"watchOptions":{"excludeDirectories":["**/node_modules"]}}}
105+
{"seq":2,"type":"request","command":"updateOpen","arguments":{"changedFiles":[],"closedFiles":[],"openFiles":[{"file":"@PROJECT_ROOT@/sample_repoName.config.js","projectRootPath":"@PROJECT_ROOT@"}]}}
106+
{"seq":3,"type":"request","command":"cursedCommand","arguments":{"file":"@PROJECT_ROOT@/src/sampleTsFile.ts","line":1,"offset":1,"includeExternalModuleExports":false,"triggerKind":1}}
107+
\`\`\`
108+
109+
<h4>Repro steps</h4>
110+
111+
\`\`\`bash
112+
#!/bin/bash
113+
114+
git clone https://github.com./MockRepoOwner/MockRepoName --recurse-submodules
115+
git -C "./MockRepoName" reset --hard 57b462387e88aa7e363af0daf867a5dc1e83a935
116+
# Install packages (exact steps are below, but it might be easier to follow the repo readme)
117+
npm --prefix "./dirA" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
118+
npm --prefix "./dirB/dirC" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
119+
npm --prefix "./dirD/dirE/dirF" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
120+
downloadUrl=$(curl -s "PLACEHOLDER_GETARTIFACTS_API?artifactName=RepoResults123&api-version=7.0" | jq -r ".resource.downloadUrl")
121+
wget -O RepoResults123.zip "$downloadUrl"
122+
unzip -p RepoResults123.zip RepoResults123/MockRepoOwner.MockRepoName.replay.txt > MockRepoOwner.MockRepoName.replay.txt
123+
npm install --no-save @typescript/server-replay
124+
\`\`\`
125+
126+
To run the repro:
127+
\`\`\`bash
128+
# \`npx tsreplay --help\` to learn about helpful switches for debugging, logging, etc.
129+
npx tsreplay ./MockRepoName ./MockRepoOwner.MockRepoName.replay.txt <PATH_TO_tsserver.js>
130+
\`\`\`
131+
132+
</details>
133+
134+
</details>
135+
",
136+
{
137+
"encoding": "utf-8",
138+
},
139+
],
140+
[
141+
"<BASE_PATH>/RepoResults123/metadata.json",
142+
"{"newTsResolvedVersion":"1.1.1","oldTsResolvedVersion":"0.0.0","statusCounts":{"Detected interesting changes":1}}",
143+
{
144+
"encoding": "utf-8",
145+
},
146+
],
147+
],
148+
"results": [
149+
{
150+
"type": "return",
151+
"value": undefined,
152+
},
153+
{
154+
"type": "return",
155+
"value": undefined,
156+
},
157+
{
158+
"type": "return",
159+
"value": undefined,
160+
},
161+
{
162+
"type": "return",
163+
"value": undefined,
164+
},
165+
{
166+
"type": "return",
167+
"value": undefined,
168+
},
169+
],
170+
}
171+
`;
172+
3173
exports[`main outputs server errors 1`] = `
4174
[MockFunction] {
5175
"calls": [

test/main.test.ts

+62-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getTscRepoResult, downloadTsRepoAsync, mainAsync } from '../src/main'
22
import { execSync } from "child_process"
33
import path = require("path")
44
import { createCopyingOverlayFS } from '../src/utils/overlayFS'
5+
import { SpawnResult } from '../src/utils/execUtils';
56

67
jest.mock('random-seed', () => ({
78
create: () => {
@@ -27,17 +28,7 @@ jest.mock("../src/utils/execUtils", () => ({
2728
return {};
2829
}
2930

30-
return {
31-
stdout: JSON.stringify({
32-
"request_seq": "123",
33-
"command": "cursedCommand",
34-
"message": "Some error. Could not do something. \nMaybe a Debug fail.\n at a (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:1:1)\n at b (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:2:2)\n at c (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:3:3)\n at d (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:4:4)\n at e (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:5:5)"
35-
}),
36-
stderr: '',
37-
code: 5,
38-
signal: null,
39-
40-
}
31+
return typeScriptSpawnResult(args);
4132
}),
4233
execAsync: async (cwd: string, command: string) => {
4334
if (command.startsWith('npm pack typescript@latest')) {
@@ -110,6 +101,14 @@ jest.mock('../src/utils/installPackages', () => {
110101
}
111102
});
112103

104+
let typeScriptSpawnResult: (args: readonly string[]) => SpawnResult;
105+
106+
const errorStdout = JSON.stringify({
107+
"request_seq": "123",
108+
"command": "cursedCommand",
109+
"message": "Some error. Could not do something. \nMaybe a Debug fail.\n at a (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:1:1)\n at b (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:2:2)\n at c (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:3:3)\n at d (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:4:4)\n at e (/mnt/vss/_work/1/s/typescript-1.1.1/lib/typescript.js:5:5)"
110+
});
111+
113112
describe("main", () => {
114113
jest.setTimeout(10 * 60 * 1000);
115114

@@ -146,6 +145,58 @@ describe("main", () => {
146145
it("outputs server errors", async () => {
147146
const mockedFs = require('fs');
148147

148+
typeScriptSpawnResult = () => ({
149+
stdout: errorStdout,
150+
stderr: '',
151+
code: 5,
152+
signal: null,
153+
154+
});
155+
156+
await mainAsync({
157+
testType: "github",
158+
tmpfs: false,
159+
entrypoint: 'tsserver',
160+
diagnosticOutput: false,
161+
buildWithNewWhenOldFails: false,
162+
repoListPath: "./artifacts/repos.json",
163+
workerCount: 1,
164+
workerNumber: 1,
165+
oldTsNpmVersion: 'latest',
166+
newTsNpmVersion: 'next',
167+
resultDirName: 'RepoResults123',
168+
prngSeed: 'testSeed',
169+
});
170+
171+
// Remove all references to the base path so that snapshot pass successfully.
172+
mockedFs.promises.writeFile.mock.calls.forEach((e: [string, string]) => {
173+
e[0] = e[0].replace(process.cwd(), "<BASE_PATH>");
174+
});
175+
176+
expect(mockedFs.promises.writeFile).toMatchSnapshot();
177+
});
178+
179+
it("outputs old server errors", async () => {
180+
const mockedFs = require('fs');
181+
182+
typeScriptSpawnResult = args => {
183+
let isOldServer = args.some(x => x.includes('0.0.0'));
184+
185+
// Only "old" reports an error.
186+
return isOldServer ? {
187+
stdout: errorStdout,
188+
stderr: '',
189+
code: 5,
190+
signal: null,
191+
} : {
192+
stdout: '',
193+
stderr: '',
194+
code: 0,
195+
signal: null,
196+
};
197+
198+
};
199+
149200
await mainAsync({
150201
testType: "github",
151202
tmpfs: false,

0 commit comments

Comments
 (0)