Skip to content

Commit e911bf4

Browse files
committed
Introduce release compatibility check before selecting the most recent
1 parent 7b9e540 commit e911bf4

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

extensions/ql-vscode/src/distribution.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -322,16 +322,22 @@ class ExtensionSpecificDistributionManager {
322322
}
323323

324324
private async getLatestRelease(): Promise<Release> {
325-
const release = await this.createReleasesApiConsumer().getLatestRelease(this._versionRange, this._config.includePrerelease);
326-
// FIXME: Look for platform-specific codeql distribution if available
327-
release.assets = release.assets.filter(asset => asset.name === 'codeql.zip');
328-
if (release.assets.length === 0) {
329-
throw new Error("Release had no asset named codeql.zip");
330-
}
331-
else if (release.assets.length > 1) {
332-
throw new Error("Release had more than one asset named codeql.zip");
333-
}
334-
return release;
325+
return await this.createReleasesApiConsumer().getLatestRelease(
326+
this._versionRange,
327+
this._config.includePrerelease,
328+
release => {
329+
// FIXME: Look for platform-specific codeql distribution if available
330+
// https://github.com/github/vscode-codeql/issues/417
331+
const matchingAssets = release.assets.filter(asset => asset.name === 'codeql.zip');
332+
if (matchingAssets.length !== 1) {
333+
if (matchingAssets.length > 1) {
334+
logger.log("WARNING: Ignoring a release with more than one asset named codeql.zip");
335+
}
336+
return false;
337+
}
338+
return true;
339+
}
340+
);
335341
}
336342

337343
private createReleasesApiConsumer(): ReleasesApiConsumer {
@@ -391,7 +397,7 @@ export class ReleasesApiConsumer {
391397
this._repoName = repoName;
392398
}
393399

394-
public async getLatestRelease(versionRange: semver.Range, includePrerelease = false): Promise<Release> {
400+
public async getLatestRelease(versionRange: semver.Range, includePrerelease = false, additionalCompatibilityCheck?: (release: GithubRelease) => boolean): Promise<Release> {
395401
const apiPath = `/repos/${this._ownerName}/${this._repoName}/releases`;
396402
const allReleases: GithubRelease[] = await (await this.makeApiCall(apiPath)).json();
397403
const compatibleReleases = allReleases.filter(release => {
@@ -400,7 +406,11 @@ export class ReleasesApiConsumer {
400406
}
401407

402408
const version = semver.parse(release.tag_name);
403-
return version !== null && semver.satisfies(version, versionRange);
409+
if (version === null || !semver.satisfies(version, versionRange)) {
410+
return false;
411+
}
412+
413+
return !additionalCompatibilityCheck || additionalCompatibilityCheck(release);
404414
});
405415
// Tag names must all be parsable to semvers due to the previous filtering step.
406416
const latestRelease = compatibleReleases.sort((a, b) => {

extensions/ql-vscode/src/vscode-tests/no-workspace/distribution.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ describe("Releases API consumer", () => {
3535
"tag_name": "v3.1.1"
3636
},
3737
{
38-
"assets": [],
38+
"assets": [{
39+
id: 1,
40+
name: "exampleAsset.txt",
41+
size: 1
42+
}],
3943
"created_at": "2019-09-05T00:00:00Z",
4044
"id": 3,
4145
"name": "",
@@ -85,6 +89,26 @@ describe("Releases API consumer", () => {
8589
expect(latestRelease.id).to.equal(1);
8690
});
8791

92+
it("picking latest release: release passes additional compatibility test if additional compatibility test specified", async () => {
93+
class MockReleasesApiConsumer extends ReleasesApiConsumer {
94+
protected async makeApiCall(apiPath: string): Promise<fetch.Response> {
95+
if (apiPath === `/repos/${owner}/${repo}/releases`) {
96+
return Promise.resolve(new fetch.Response(JSON.stringify(sampleReleaseResponse)));
97+
}
98+
return Promise.reject(new Error(`Unknown API path: ${apiPath}`));
99+
}
100+
}
101+
102+
const consumer = new MockReleasesApiConsumer(owner, repo);
103+
104+
const latestRelease = await consumer.getLatestRelease(
105+
new semver.Range("2.*.*"),
106+
true,
107+
release => release.assets.some(asset => asset.name === "exampleAsset.txt")
108+
);
109+
expect(latestRelease.id).to.equal(3);
110+
});
111+
88112
it("picking latest release: includes prereleases when option set", async () => {
89113
class MockReleasesApiConsumer extends ReleasesApiConsumer {
90114
protected async makeApiCall(apiPath: string): Promise<fetch.Response> {

0 commit comments

Comments
 (0)