Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions azure-pipelines-gitTests-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
npm run build
npm install -g pnpm
mkdir 'RepoResults$(System.JobPositionInPhase)'
node dist/checkGithubRepos ${{ parameters.ENTRYPOINT }} ${{ parameters.OLD_VERSION }} ${{ parameters.NEW_VERSION }} '$(Pipeline.Workspace)/RepoList/repos.json' $(System.TotalJobsInPhase) $(System.JobPositionInPhase) 'RepoResults$(System.JobPositionInPhase)' ${{ parameters.DIAGNOSTIC_OUTPUT }} ${{ parameters.PRNG_SEED }}
node dist/checkGithubRepos ${{ parameters.ENTRYPOINT }} ${{ parameters.OLD_VERSION }} ${{ parameters.NEW_VERSION }} '$(Pipeline.Workspace)/RepoList/repos.json' $(System.TotalJobsInPhase) $(System.JobPositionInPhase) 'RepoResults$(System.JobPositionInPhase)' ${{ parameters.DIAGNOSTIC_OUTPUT }} ${{ parameters.PRNG_SEED }} $(Build.BuildId) '$(System.TeamFoundationCollectionUri)' '$(System.TeamProject)'
displayName: 'Run TypeScript on repos'
continueOnError: true
env:
Expand All @@ -91,7 +91,7 @@ jobs:
- script: |
npm ci
npm run build
node dist/postGithubIssue ${{ parameters.ENTRYPOINT }} ${{ parameters.LANGUAGE }} ${{ parameters.REPO_COUNT }} ${{ parameters.REPO_START_INDEX }} '$(Pipeline.Workspace)' '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)' '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)&view=artifacts&type=publishedArtifacts' ${{ parameters.POST_RESULT }} '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/artifacts'
node dist/postGithubIssue ${{ parameters.ENTRYPOINT }} ${{ parameters.LANGUAGE }} ${{ parameters.REPO_COUNT }} ${{ parameters.REPO_START_INDEX }} '$(Pipeline.Workspace)' '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)' '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)&view=artifacts&type=publishedArtifacts' ${{ parameters.POST_RESULT }}
displayName: 'Create issue from new errors'
env:
GITHUB_PAT: $(GITHUB_PAT)
4 changes: 2 additions & 2 deletions azure-pipelines-userTests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
npm run build
npm install -g pnpm
mkdir 'RepoResults$(System.JobPositionInPhase)'
node dist/checkUserTestRepos ${{ parameters.ENTRYPOINT }} ${{ parameters.OLD_TS_REPO_URL }} ${{ parameters.OLD_HEAD_REF }} ${{ parameters.SOURCE_ISSUE }} ${{ parameters.TOP_REPOS }} '$(Pipeline.Workspace)/RepoList/repos.json' $(System.TotalJobsInPhase) $(System.JobPositionInPhase) 'RepoResults$(System.JobPositionInPhase)' ${{ parameters.DIAGNOSTIC_OUTPUT }} ${{ parameters.PRNG_SEED }}
node dist/checkUserTestRepos ${{ parameters.ENTRYPOINT }} ${{ parameters.OLD_TS_REPO_URL }} ${{ parameters.OLD_HEAD_REF }} ${{ parameters.SOURCE_ISSUE }} ${{ parameters.TOP_REPOS }} '$(Pipeline.Workspace)/RepoList/repos.json' $(System.TotalJobsInPhase) $(System.JobPositionInPhase) 'RepoResults$(System.JobPositionInPhase)' ${{ parameters.DIAGNOSTIC_OUTPUT }} ${{ parameters.PRNG_SEED }} $(Build.BuildId) '$(System.TeamFoundationCollectionUri)' '$(System.TeamProject)'
displayName: 'Run user tests'
env:
GITHUB_PAT: $(GITHUB_PAT)
Expand All @@ -123,7 +123,7 @@ jobs:
- script: |
npm ci
npm run build
node dist/postGithubComments ${{ parameters.REQUESTING_USER }} ${{ parameters.SOURCE_ISSUE }} ${{ parameters.STATUS_COMMENT }} ${{ parameters.DISTINCT_ID }} ${{ parameters.TOP_REPOS }} '$(Pipeline.Workspace)' '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)&view=artifacts&type=publishedArtifacts' ${{ parameters.POST_RESULT }} ${{ parameters.REPO_COUNT }} '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/artifacts'
node dist/postGithubComments ${{ parameters.REQUESTING_USER }} ${{ parameters.SOURCE_ISSUE }} ${{ parameters.STATUS_COMMENT }} ${{ parameters.DISTINCT_ID }} ${{ parameters.TOP_REPOS }} '$(Pipeline.Workspace)' '$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)&view=artifacts&type=publishedArtifacts' ${{ parameters.POST_RESULT }} ${{ parameters.REPO_COUNT }}
displayName: 'Update PR comment with new errors'
env:
GITHUB_PAT: $(GITHUB_PAT)
348 changes: 343 additions & 5 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@typescript/github-link": "^0.2.2",
"@typescript/server-harness": "^0.3.5",
"@typescript/server-replay": "^0.2.12",
"azure-devops-node-api": "^13.0.0",
"glob": "^10.3.10",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
Expand Down
9 changes: 6 additions & 3 deletions src/checkGithubRepos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { mainAsync, reportError, TsEntrypoint } from "./main";

const { argv } = process;

if (argv.length !== 11) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <ts_entrypoint> <old_ts_npm_version> <new_ts_npm_version> <repo_list_path> <worker_count> <worker_number> <result_dir_name> <diagnostic_output> <prng_seed>`);
if (argv.length !== 14) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <ts_entrypoint> <old_ts_npm_version> <new_ts_npm_version> <repo_list_path> <worker_count> <worker_number> <result_dir_name> <diagnostic_output> <prng_seed> <buildId> <teamFoundationCollectionUri> <teamProject>`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we really need to change how we do this; the positional parameters are totally unweildy and we just keep adding more on 😄

Named parameters or just using env vars would probably be easier.

process.exit(-1);
}

const [,, entrypoint, oldTsNpmVersion, newTsNpmVersion, repoListPath, workerCount, workerNumber, resultDirName, diagnosticOutput, prngSeed] = argv;
const [, , entrypoint, oldTsNpmVersion, newTsNpmVersion, repoListPath, workerCount, workerNumber, resultDirName, diagnosticOutput, prngSeed, buildId, teamFoundationCollectionUri, teamProject] = argv;

mainAsync({
testType: "github",
Expand All @@ -23,6 +23,9 @@ mainAsync({
newTsNpmVersion,
resultDirName,
prngSeed: prngSeed.toLowerCase() === "n/a" ? undefined : prngSeed,
buildId: +buildId,
teamFoundationCollectionUri,
teamProject,
}).catch(err => {
reportError(err, "Unhandled exception");
process.exit(1);
Expand Down
9 changes: 6 additions & 3 deletions src/checkUserTestRepos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { mainAsync, reportError, TsEntrypoint } from "./main";

const { argv } = process;

if (argv.length !== 13) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <ts_entrypoint> <old_ts_repo_url> <old_head_ref> <pr_number> <is_top_repos> <repo_list_path> <worker_count> <worker_number> <result_dir_name> <diagnostic_output> <prng_seed>`);
if (argv.length !== 16) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <ts_entrypoint> <old_ts_repo_url> <old_head_ref> <pr_number> <is_top_repos> <repo_list_path> <worker_count> <worker_number> <result_dir_name> <diagnostic_output> <prng_seed> <buildId> <teamFoundationCollectionUri> <teamProject>`);
process.exit(-1);
}

const [,, entrypoint, oldTsRepoUrl, oldHeadRef, prNumber, buildWithNewWhenOldFails, repoListPath, workerCount, workerNumber, resultDirName, diagnosticOutput, prngSeed] = argv;
const [,, entrypoint, oldTsRepoUrl, oldHeadRef, prNumber, buildWithNewWhenOldFails, repoListPath, workerCount, workerNumber, resultDirName, diagnosticOutput, prngSeed, buildId, teamFoundationCollectionUri, teamProject] = argv;

mainAsync({
testType: "user",
Expand All @@ -24,6 +24,9 @@ mainAsync({
resultDirName,
diagnosticOutput: diagnosticOutput.toLowerCase() === "true",
prngSeed: prngSeed.toLowerCase() === "n/a" ? undefined : prngSeed,
buildId: +buildId,
teamFoundationCollectionUri,
teamProject,
}).catch(err => {
reportError(err, "Unhandled exception");
process.exit(1);
Expand Down
28 changes: 20 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import mdEscape = require("markdown-escape");
import randomSeed = require("random-seed");
import { getErrorMessageFromStack, getHash, getHashForStack } from "./utils/hashStackTrace";
import { createCopyingOverlayFS, createTempOverlayFS, OverlayBaseFS } from "./utils/overlayFS";
import { getReplayScriptDownloadUrl } from "./utils/devOpsUtils";

interface Params {
export interface Params {
/**
* Store test repos on a tmpfs.
* Basically, the only reason not to do this would be lack of `sudo`.
Expand Down Expand Up @@ -54,6 +55,18 @@ interface Params {
* Pass undefined to have a seed generated.
*/
prngSeed: string | undefined;
/**
* Build number to reference when downloading files.
*/
buildId: number;
/**
* URL of the DevOps organization.
*/
teamFoundationCollectionUri: string;
/**
* Name of the project that contains the build.
*/
teamProject: string;
}
export interface GitParams extends Params {
testType: "github";
Expand Down Expand Up @@ -96,7 +109,7 @@ interface TSServerResult {
installCommands: ip.InstallCommand[];
}

interface Summary {
export interface Summary {
tsServerResult: TSServerResult;
repo: git.Repo;
oldTsEntrypointPath: string;
Expand Down Expand Up @@ -429,7 +442,7 @@ ${oldServerError}
return text;
}

async function createNewErrorSummaryAsync(summaries: Summary[]): Promise<string> {
async function createNewErrorSummaryAsync(summaries: Summary[], params: Params): Promise<string> {
let text = `<h2>${getErrorMessage(summaries[0].tsServerResult.newSpawnResult.stdout)}</h2>

\`\`\`
Expand Down Expand Up @@ -483,9 +496,9 @@ ${summary.replayScript}
text += `${command.tool} ${workingDirFlag} "${command.directory}" ${command.arguments.join(" ")}\n`;
}

text += `downloadUrl=$(curl -s "${getArtifactsApiUrlPlaceholder}?artifactName=${summary.resultDirName}&api-version=7.0" | jq -r ".resource.downloadUrl")
wget -O ${summary.resultDirName}.zip "$downloadUrl"
unzip -p ${summary.resultDirName}.zip ${summary.resultDirName}/${summary.replayScriptName} > ${summary.replayScriptName}
const replayScriptUrl = await getReplayScriptDownloadUrl(summary, params);

text += `wget -O ${summary.replayScriptName} "${replayScriptUrl!.toString()}"
npm install --no-save @typescript/server-replay
\`\`\`

Expand Down Expand Up @@ -710,7 +723,6 @@ export const resultFileNameSuffix = "results.txt";
export const replayScriptFileNameSuffix = "replay.txt";
export const rawErrorFileNameSuffix = "rawError.txt";
export const artifactFolderUrlPlaceholder = "PLACEHOLDER_ARTIFACT_FOLDER";
export const getArtifactsApiUrlPlaceholder = "PLACEHOLDER_GETARTIFACTS_API";

export type StatusCounts = {
[P in RepoStatus]?: number
Expand Down Expand Up @@ -846,7 +858,7 @@ export async function mainAsync(params: GitParams | UserParams): Promise<void> {
}

for (let [key, value] of groupedNewErrors) {
const summary = await createNewErrorSummaryAsync(value);
const summary = await createNewErrorSummaryAsync(value, params);
const resultFileName = `${key}.${resultFileNameSuffix}`;

await fs.promises.writeFile(path.join(resultDirPath, resultFileName), summary, { encoding: "utf-8" });
Expand Down
11 changes: 5 additions & 6 deletions src/postGithubComments.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import fs = require("fs");
import path = require("path");
import { artifactFolderUrlPlaceholder, getArtifactsApiUrlPlaceholder, Metadata, metadataFileName, RepoStatus, resultFileNameSuffix } from "./main";
import { artifactFolderUrlPlaceholder, Metadata, metadataFileName, RepoStatus, resultFileNameSuffix } from "./main";
import git = require("./utils/gitUtils");
import pu = require("./utils/packageUtils");

const { argv } = process;

if (argv.length !== 12) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <user_to_tag> <pr_number> <comment_number> <distinct_id> <is_top_repos_run> <result_dir_path> <artifacts_uri> <post_result> <repo_count> <get_artifacts_api>`);
if (argv.length !== 11) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <user_to_tag> <pr_number> <comment_number> <distinct_id> <is_top_repos_run> <result_dir_path> <artifacts_uri> <post_result> <repo_count>`);
process.exit(-1);
}

const [, , userToTag, prNumber, commentNumber, distinctId, isTop, resultDirPath, artifactsUri, post, repoCount, getArtifactsApi] = argv;
const [, , userToTag, prNumber, commentNumber, distinctId, isTop, resultDirPath, artifactsUri, post, repoCount] = argv;
const isTopReposRun = isTop.toLowerCase() === "true";
const postResult = post.toLowerCase() === "true";

Expand Down Expand Up @@ -72,8 +72,7 @@ const hasOldErrors = pu.glob(resultDirPath, `**/!*.${resultFileNameSuffix}`).len
const resultPaths = pu.glob(resultDirPath, `**/*.${resultFileNameSuffix}`).sort((a, b) => path.basename(a).localeCompare(path.basename(b)));
const outputs = resultPaths.map(p =>
fs.readFileSync(p, { encoding: "utf-8" })
.replaceAll(artifactFolderUrlPlaceholder, artifactsUri)
.replaceAll(getArtifactsApiUrlPlaceholder, getArtifactsApi));
.replaceAll(artifactFolderUrlPlaceholder, artifactsUri));

const suiteDescription = isTopReposRun ? `top ${repoCount} repos` : "user tests";
let header = `@${userToTag} Here are the results of running the ${suiteDescription} comparing \`${oldTscResolvedVersion}\` and \`${newTscResolvedVersion}\`:
Expand Down
11 changes: 5 additions & 6 deletions src/postGithubIssue.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import fs = require("fs");
import path = require("path");
import { artifactFolderUrlPlaceholder, getArtifactsApiUrlPlaceholder, Metadata, metadataFileName, RepoStatus, resultFileNameSuffix, StatusCounts, TsEntrypoint } from "./main";
import { artifactFolderUrlPlaceholder, Metadata, metadataFileName, RepoStatus, resultFileNameSuffix, StatusCounts, TsEntrypoint } from "./main";
import git = require("./utils/gitUtils");
import pu = require("./utils/packageUtils");

const { argv } = process;

if (argv.length !== 11) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <ts_entrypoint> <language> <repo_count> <repo_start_index> <result_dir_path> <log_uri> <artifacts_uri> <post_result> <get_artifacts_api>`);
if (argv.length !== 10) {
console.error(`Usage: ${path.basename(argv[0])} ${path.basename(argv[1])} <ts_entrypoint> <language> <repo_count> <repo_start_index> <result_dir_path> <log_uri> <artifacts_uri> <post_result>`);
process.exit(-1);
}

const [, , ep, language, repoCount, repoStartIndex, resultDirPath, logUri, artifactsUri, post, getArtifactsApi] = argv;
const [, , ep, language, repoCount, repoStartIndex, resultDirPath, logUri, artifactsUri, post] = argv;
const postResult = post.toLowerCase() === "true";
const entrypoint = ep as TsEntrypoint;

Expand Down Expand Up @@ -74,8 +74,7 @@ ${Object.keys(statusCounts).sort().map(status => `| ${status} | ${statusCounts[s
const resultPaths = pu.glob(resultDirPath, `**/*.${resultFileNameSuffix}`).sort((a, b) => path.basename(a).localeCompare(path.basename(b)));
const outputs = resultPaths.map(p =>
fs.readFileSync(p, { encoding: "utf-8" })
.replaceAll(artifactFolderUrlPlaceholder, artifactsUri)
.replaceAll(getArtifactsApiUrlPlaceholder, getArtifactsApi));
.replaceAll(artifactFolderUrlPlaceholder, artifactsUri));

for (let i = 0; i < outputs.length; i++) {
const resultPath = resultPaths[i];
Expand Down
31 changes: 31 additions & 0 deletions src/utils/devOpsUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import ado = require("azure-devops-node-api");
import { Params, Summary } from "../main";

interface ArtifactContent {
items: [{
path: string;
blob: {
id: string;
};
}];
};

export async function getReplayScriptDownloadUrl(summary: Summary, params: Params) {
const cli = new ado.WebApi(`${params.teamFoundationCollectionUri}defaultcollection`, ado.getHandlerFromToken("")); // Empty token, anon auth
const build = await cli.getBuildApi();
const artifact = await build.getArtifact(params.teamProject, params.buildId, summary.resultDirName);

if (artifact.resource?.url) {
const repoResultUrl = new URL(artifact.resource.url);
repoResultUrl.search = `artifactName=${summary.resultDirName}&fileId=${artifact.resource.data}&fileName=${summary.resultDirName}`;

const artifactContent = await (await fetch(repoResultUrl)).json() as ArtifactContent;

const item = artifactContent.items.find(x => x.path.endsWith(summary.replayScriptName));

const replayScriptUrl = new URL(artifact.resource.url);
replayScriptUrl.search = `artifactName=${summary.resultDirName}&fileId=${item!.blob.id}&fileName=${summary.replayScriptName}`;

return replayScriptUrl;
}
}
4 changes: 1 addition & 3 deletions test/__snapshots__/main.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ git -C "./MockRepoName" reset --hard 57b462387e88aa7e363af0daf867a5dc1e83a935
npm --prefix "./dirA" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
npm --prefix "./dirB/dirC" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
npm --prefix "./dirD/dirE/dirF" install --prefer-offline --no-audit --no-progress --legacy-peer-deps --ignore-scripts -q
downloadUrl=$(curl -s "PLACEHOLDER_GETARTIFACTS_API?artifactName=RepoResults123&api-version=7.0" | jq -r ".resource.downloadUrl")
wget -O RepoResults123.zip "$downloadUrl"
unzip -p RepoResults123.zip RepoResults123/MockRepoOwner.MockRepoName.replay.txt > MockRepoOwner.MockRepoName.replay.txt
wget -O MockRepoOwner.MockRepoName.replay.txt "http://anazuredevops.buildpage.com_teamproject_atypescriptproject_buildid_1/?artifactName=RepoResults123&fileId=999&fileName=MockRepoOwner.MockRepoName.replay.txt"
npm install --no-save @typescript/server-replay
\`\`\`

Expand Down
15 changes: 13 additions & 2 deletions test/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getTscRepoResult, downloadTsRepoAsync, mainAsync } from '../src/main'
import { getTscRepoResult, downloadTsRepoAsync, mainAsync, Params, Summary } from '../src/main'
import { execSync } from "child_process"
import path = require("path")
import { createCopyingOverlayFS } from '../src/utils/overlayFS'
Expand Down Expand Up @@ -109,6 +109,14 @@ jest.mock('../src/utils/installPackages', () => {
}
}
});
jest.mock('../src/utils/devOpsUtils', () => ({
getReplayScriptDownloadUrl: async (summary: Summary, params: Params) => {
var url = new URL(`${params.teamFoundationCollectionUri}_teamProject_${params.teamProject}_buildId_${params.buildId}`);
url.search = `artifactName=${summary.resultDirName}&fileId=999&fileName=${summary.replayScriptName}`;

return url;
}
}));

describe("main", () => {
jest.setTimeout(10 * 60 * 1000);
Expand Down Expand Up @@ -159,6 +167,9 @@ describe("main", () => {
newTsNpmVersion: 'next',
resultDirName: 'RepoResults123',
prngSeed: 'testSeed',
buildId: 1,
teamFoundationCollectionUri: "http://anAzureDevOps.buildPage.com",
teamProject: "ATypeScriptProject"
});

// Remove all references to the base path so that snapshot pass successfully.
Expand All @@ -168,4 +179,4 @@ describe("main", () => {

expect(mockedFs.promises.writeFile).toMatchSnapshot();
});
})
});