Skip to content

Commit 97cfe6e

Browse files
Ignore tsserver results when both results have identical request IDs and command names (#98)
Co-authored-by: Daniel Rosenwasser <[email protected]>
1 parent 7ee1590 commit 97cfe6e

File tree

1 file changed

+68
-17
lines changed

1 file changed

+68
-17
lines changed

src/main.ts

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ async function getTsServerRepoResult(
187187
downloadDir: string,
188188
replayScriptArtifactPath: string,
189189
rawErrorArtifactPath: string,
190-
diagnosticOutput: boolean): Promise<RepoResult> {
190+
diagnosticOutput: boolean,
191+
isPr: boolean,
192+
): Promise<RepoResult> {
191193

192194
if (!await cloneRepo(repo, userTestsDir, downloadDir, diagnosticOutput)) {
193195
return { status: "Git clone failed" };
@@ -233,7 +235,6 @@ async function getTsServerRepoResult(
233235
}
234236

235237
console.log("No issues found");
236-
return { status: "Detected no interesting changes" };
237238
case exercise.EXIT_LANGUAGE_SERVICE_DISABLED:
238239
console.log("Skipping since language service was disabled");
239240
return { status: "Language service disabled in new TS" };
@@ -253,9 +254,13 @@ async function getTsServerRepoResult(
253254
return { status: "Unknown failure" };
254255
}
255256

256-
console.log(`Issue found in ${newTsServerPath} (new):`);
257-
console.log(insetLines(prettyPrintServerHarnessOutput(newSpawnResult.stdout, /*filter*/ false)));
258-
await fs.promises.writeFile(rawErrorPath, prettyPrintServerHarnessOutput(newSpawnResult.stdout, /*filter*/ false));
257+
const newServerFailed = !newSpawnResult.code;
258+
259+
if (newServerFailed) {
260+
console.log(`Issue found in ${newTsServerPath} (new):`);
261+
console.log(insetLines(prettyPrintServerHarnessOutput(newSpawnResult.stdout, /*filter*/ false)));
262+
await fs.promises.writeFile(rawErrorPath, prettyPrintServerHarnessOutput(newSpawnResult.stdout, /*filter*/ false));
263+
}
259264

260265
console.log(`Testing with ${oldTsServerPath} (old)`);
261266
const oldSpawnResult = await spawnWithTimeoutAsync(repoDir, process.argv[0], [path.join(__dirname, "..", "node_modules", "@typescript", "server-replay", "replay.js"), repoDir, replayScriptPath, oldTsServerPath, "-u"], executionTimeout);
@@ -268,13 +273,32 @@ async function getTsServerRepoResult(
268273
// NB: Unlike newServerFailed, this includes timeouts because "it used to timeout" is useful context for an error in the new server
269274
const oldServerFailed = !oldSpawnResult || !!oldSpawnResult.code || !!oldSpawnResult.signal;
270275

276+
if (!newServerFailed && !oldServerFailed) {
277+
return { status: "Detected no interesting changes" };
278+
}
279+
271280
if (oldServerFailed) {
272281
console.log(`Issue found in ${oldTsServerPath} (old):`);
273282
console.log(
274283
insetLines(
275284
oldSpawnResult?.stdout
276285
? prettyPrintServerHarnessOutput(oldSpawnResult.stdout, /*filter*/ false)
277286
: `Timed out after ${executionTimeout} ms`));
287+
288+
// We don't want to drown PRs with comments.
289+
// Override the results to say nothing interesting changed.
290+
if (isPr && newServerFailed && oldSpawnResult) {
291+
const oldOut = parseServerHarnessOutput(oldSpawnResult.stdout);
292+
const newOut = parseServerHarnessOutput(newSpawnResult.stdout);
293+
294+
if (
295+
typeof oldOut !== "string" && typeof newOut !== "string"
296+
&& oldOut.request_seq === newOut.request_seq
297+
&& oldOut.command === newOut.command
298+
) {
299+
return { status: "Detected no interesting changes" };
300+
}
301+
}
278302
}
279303

280304
const owner = repo.owner ? `${mdEscape(repo.owner)}/` : "";
@@ -288,7 +312,7 @@ async function getTsServerRepoResult(
288312
: `Timed out after ${executionTimeout} ms`;
289313
summary += `
290314
<details>
291-
<summary>:warning: Note that ${path.basename(path.dirname(path.dirname(oldTsServerPath)))} also had errors :warning:</summary>
315+
<summary>:warning: Note that ${path.basename(path.dirname(path.dirname(oldTsServerPath)))} had errors :warning:</summary>
292316
293317
\`\`\`
294318
${oldServerError}
@@ -297,6 +321,16 @@ ${oldServerError}
297321
</details>
298322
299323
`;
324+
if (!newServerFailed) {
325+
summary += `
326+
:tada: New server no longer has errors :tada:
327+
`;
328+
return { status: "Detected interesting changes", summary }
329+
}
330+
}
331+
332+
if (!newServerFailed) {
333+
return { status: "Detected no interesting changes" };
300334
}
301335

302336
summary += `
@@ -621,6 +655,8 @@ export async function mainAsync(params: GitParams | UserParams): Promise<void> {
621655
// An object is easier to de/serialize than a real map
622656
const statusCounts: { [P in RepoStatus]?: number } = {};
623657

658+
const isPr = params.testType === "user" && !!params.prNumber
659+
624660
let i = 1;
625661
for (const repo of repos) {
626662
console.log(`Starting #${i++} / ${repos.length}: ${repo.url ?? repo.name}`);
@@ -637,7 +673,7 @@ export async function mainAsync(params: GitParams | UserParams): Promise<void> {
637673
const rawErrorFileName = `${repoPrefix}.${rawErrorFileNameSuffix}`;
638674
const { status, summary, replayScriptPath, rawErrorPath } = params.entrypoint === "tsc"
639675
? await getTscRepoResult(repo, userTestsDir, oldTsEntrypointPath, newTsEntrypointPath, params.buildWithNewWhenOldFails, downloadDir, diagnosticOutput)
640-
: await getTsServerRepoResult(repo, userTestsDir, oldTsEntrypointPath, newTsEntrypointPath, downloadDir, path.join(params.resultDirName, replayScriptFileName), path.join(params.resultDirName, rawErrorFileName), diagnosticOutput);
676+
: await getTsServerRepoResult(repo, userTestsDir, oldTsEntrypointPath, newTsEntrypointPath, downloadDir, path.join(params.resultDirName, replayScriptFileName), path.join(params.resultDirName, rawErrorFileName), diagnosticOutput, isPr);
641677
console.log(`Repo ${repo.url ?? repo.name} had status "${status}"`);
642678
statusCounts[status] = (statusCounts[status] ?? 0) + 1;
643679
if (summary) {
@@ -805,24 +841,39 @@ ${spawnResult.stderr}
805841
`);
806842
}
807843

808-
function prettyPrintServerHarnessOutput(error: string, filter: boolean): string {
809-
try {
810-
const errorObj = JSON.parse(error);
811-
if (errorObj.message) {
812-
return `Req #${errorObj.request_seq} - ${errorObj.command}
813-
${filter ? filterToTsserverLines(errorObj.message) : errorObj.message}`;
814-
}
815844

816-
// It's not really clear how this could happen, but reporting the whole repsonse should be fine
817-
// if there's no message property
818-
return JSON.stringify(errorObj, undefined, 2);
845+
export interface ServerHarnessOutput {
846+
request_seq: number;
847+
command: string;
848+
message: string
849+
}
850+
851+
function parseServerHarnessOutput(error: string): ServerHarnessOutput | string {
852+
try {
853+
return JSON.parse(error)
819854
}
820855
catch {
821856
// Sometimes, the response isn't JSON and that's fine
822857
return error;
823858
}
824859
}
825860

861+
function prettyPrintServerHarnessOutput(error: string, filter: boolean): string {
862+
const errorObj = parseServerHarnessOutput(error);
863+
if (typeof errorObj === "string") {
864+
return errorObj;
865+
}
866+
867+
if (errorObj.message) {
868+
return `Req #${errorObj.request_seq} - ${errorObj.command}
869+
${filter ? filterToTsserverLines(errorObj.message) : errorObj.message}`;
870+
}
871+
872+
// It's not really clear how this could happen, but reporting the whole repsonse should be fine
873+
// if there's no message property
874+
return JSON.stringify(errorObj, undefined, 2);
875+
}
876+
826877
function filterToTsserverLines(stackLines: string): string {
827878
const tsserverRegex = /^.*tsserver\.js.*$/mg;
828879
let tsserverLines = "";

0 commit comments

Comments
 (0)