Skip to content

Commit f5dbcc8

Browse files
Merge pull request #3127 from github/robertbrignull/nightly-codeql
Provide option to point ReleasesApiConsumer at nightly builds repo
2 parents c210a7f + c423505 commit f5dbcc8

File tree

3 files changed

+106
-23
lines changed

3 files changed

+106
-23
lines changed

extensions/ql-vscode/src/codeql-cli/distribution.ts

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ const DEFAULT_DISTRIBUTION_OWNER_NAME = "github";
5151
*/
5252
const DEFAULT_DISTRIBUTION_REPOSITORY_NAME = "codeql-cli-binaries";
5353

54+
/**
55+
* Owner name of the nightly version of the extension-managed distribution on GitHub.
56+
*/
57+
const NIGHTLY_DISTRIBUTION_OWNER_NAME = "dsp-testing";
58+
59+
/**
60+
* Repository name of the nightly version of the extension-managed distribution on GitHub.
61+
*/
62+
const NIGHTLY_DISTRIBUTION_REPOSITORY_NAME = "codeql-cli-nightlies";
63+
5464
/**
5565
* Range of versions of the CLI that are compatible with the extension.
5666
*
@@ -453,9 +463,18 @@ class ExtensionSpecificDistributionManager {
453463
void extLogger.log(
454464
`Searching for latest release including ${requiredAssetName}.`,
455465
);
466+
467+
const versionRange = this.usingNightlyReleases
468+
? undefined
469+
: this.versionRange;
470+
const orderBySemver = !this.usingNightlyReleases;
471+
const includePrerelease =
472+
this.usingNightlyReleases || this.config.includePrerelease;
473+
456474
return this.createReleasesApiConsumer().getLatestRelease(
457-
this.versionRange,
458-
this.config.includePrerelease,
475+
versionRange,
476+
orderBySemver,
477+
includePrerelease,
459478
(release) => {
460479
// v2.12.3 was released with a bug that causes the extension to fail
461480
// so we force the extension to ignore it.
@@ -485,19 +504,40 @@ class ExtensionSpecificDistributionManager {
485504
}
486505

487506
private createReleasesApiConsumer(): ReleasesApiConsumer {
488-
const ownerName = this.config.ownerName
489-
? this.config.ownerName
490-
: DEFAULT_DISTRIBUTION_OWNER_NAME;
491-
const repositoryName = this.config.repositoryName
492-
? this.config.repositoryName
493-
: DEFAULT_DISTRIBUTION_REPOSITORY_NAME;
494507
return new ReleasesApiConsumer(
495-
ownerName,
496-
repositoryName,
508+
this.distributionOwnerName,
509+
this.distributionRepositoryName,
497510
this.config.personalAccessToken,
498511
);
499512
}
500513

514+
private get distributionOwnerName(): string {
515+
if (this.config.ownerName) {
516+
return this.config.ownerName;
517+
} else if (this.config.channel === "nightly") {
518+
return NIGHTLY_DISTRIBUTION_OWNER_NAME;
519+
} else {
520+
return DEFAULT_DISTRIBUTION_OWNER_NAME;
521+
}
522+
}
523+
524+
private get distributionRepositoryName(): string {
525+
if (this.config.repositoryName) {
526+
return this.config.repositoryName;
527+
} else if (this.config.channel === "nightly") {
528+
return NIGHTLY_DISTRIBUTION_REPOSITORY_NAME;
529+
} else {
530+
return DEFAULT_DISTRIBUTION_REPOSITORY_NAME;
531+
}
532+
}
533+
534+
private get usingNightlyReleases(): boolean {
535+
return (
536+
this.distributionOwnerName === NIGHTLY_DISTRIBUTION_OWNER_NAME &&
537+
this.distributionRepositoryName === NIGHTLY_DISTRIBUTION_REPOSITORY_NAME
538+
);
539+
}
540+
501541
private async bumpDistributionFolderIndex(): Promise<void> {
502542
const index = this.extensionContext.globalState.get(
503543
ExtensionSpecificDistributionManager._currentDistributionFolderIndexStateKey,
@@ -570,7 +610,8 @@ export class ReleasesApiConsumer {
570610
}
571611

572612
public async getLatestRelease(
573-
versionRange: semver.Range,
613+
versionRange: semver.Range | undefined,
614+
orderBySemver = true,
574615
includePrerelease = false,
575616
additionalCompatibilityCheck?: (release: GithubRelease) => boolean,
576617
): Promise<Release> {
@@ -583,12 +624,14 @@ export class ReleasesApiConsumer {
583624
return false;
584625
}
585626

586-
const version = semver.parse(release.tag_name);
587-
if (
588-
version === null ||
589-
!semver.satisfies(version, versionRange, { includePrerelease })
590-
) {
591-
return false;
627+
if (versionRange !== undefined) {
628+
const version = semver.parse(release.tag_name);
629+
if (
630+
version === null ||
631+
!semver.satisfies(version, versionRange, { includePrerelease })
632+
) {
633+
return false;
634+
}
592635
}
593636

594637
return (
@@ -597,10 +640,9 @@ export class ReleasesApiConsumer {
597640
});
598641
// Tag names must all be parsable to semvers due to the previous filtering step.
599642
const latestRelease = compatibleReleases.sort((a, b) => {
600-
const versionComparison = semver.compare(
601-
semver.parse(b.tag_name)!,
602-
semver.parse(a.tag_name)!,
603-
);
643+
const versionComparison = orderBySemver
644+
? semver.compare(semver.parse(b.tag_name)!, semver.parse(a.tag_name)!)
645+
: b.id - a.id;
604646
if (versionComparison !== 0) {
605647
return versionComparison;
606648
}

extensions/ql-vscode/src/config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const PERSONAL_ACCESS_TOKEN_SETTING = new Setting(
9595
"personalAccessToken",
9696
DISTRIBUTION_SETTING,
9797
);
98+
const CLI_CHANNEL_SETTING = new Setting("channel", DISTRIBUTION_SETTING);
9899

99100
// Query History configuration
100101
const QUERY_HISTORY_SETTING = new Setting("queryHistory", ROOT_SETTING);
@@ -111,13 +112,16 @@ const DISTRIBUTION_CHANGE_SETTINGS = [
111112
PERSONAL_ACCESS_TOKEN_SETTING,
112113
];
113114

115+
export type CLIChannel = "stable" | "nightly";
116+
114117
export interface DistributionConfig {
115118
readonly customCodeQlPath?: string;
116119
updateCustomCodeQlPath: (newPath: string | undefined) => Promise<void>;
117120
includePrerelease: boolean;
118121
personalAccessToken?: string;
119122
ownerName?: string;
120123
repositoryName?: string;
124+
channel: CLIChannel;
121125
onDidChangeConfiguration?: Event<void>;
122126
}
123127

@@ -278,6 +282,10 @@ export class DistributionConfigListener
278282
);
279283
}
280284

285+
public get channel(): CLIChannel {
286+
return CLI_CHANNEL_SETTING.getValue() === "nightly" ? "nightly" : "stable";
287+
}
288+
281289
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
282290
this.handleDidChangeConfigurationForRelevantSettings(
283291
DISTRIBUTION_CHANGE_SETTINGS,

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

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ describe("Releases API consumer", () => {
8585
prerelease: true,
8686
tag_name: "v3.1.2-pre-2.0",
8787
},
88+
// Has a tag_name that is not valid semver
89+
{
90+
assets: [],
91+
created_at: "2019-08-010T00:00:00Z",
92+
id: 6,
93+
name: "",
94+
prerelease: true,
95+
tag_name: "codeql-bundle-20231220",
96+
},
8897
];
8998

9099
class MockReleasesApiConsumer extends ReleasesApiConsumer {
@@ -98,15 +107,26 @@ describe("Releases API consumer", () => {
98107
}
99108
}
100109

101-
it("picked release has version with the highest precedence", async () => {
110+
it("picked release is non-prerelease with the highest semver", async () => {
102111
const consumer = new MockReleasesApiConsumer(owner, repo);
103112

104113
const latestRelease = await consumer.getLatestRelease(
105114
unconstrainedVersionRange,
115+
true,
106116
);
107117
expect(latestRelease.id).toBe(2);
108118
});
109119

120+
it("picked release is non-prerelease with highest id", async () => {
121+
const consumer = new MockReleasesApiConsumer(owner, repo);
122+
123+
const latestRelease = await consumer.getLatestRelease(
124+
unconstrainedVersionRange,
125+
false,
126+
);
127+
expect(latestRelease.id).toBe(3);
128+
});
129+
110130
it("version of picked release is within the version range", async () => {
111131
const consumer = new MockReleasesApiConsumer(owner, repo);
112132

@@ -128,6 +148,7 @@ describe("Releases API consumer", () => {
128148
const latestRelease = await consumer.getLatestRelease(
129149
new Range("2.*.*"),
130150
true,
151+
true,
131152
(release) =>
132153
release.assets.some((asset) => asset.name === "exampleAsset.txt"),
133154
);
@@ -138,7 +159,7 @@ describe("Releases API consumer", () => {
138159
const consumer = new MockReleasesApiConsumer(owner, repo);
139160

140161
await expect(
141-
consumer.getLatestRelease(new Range("2.*.*"), true, (release) =>
162+
consumer.getLatestRelease(new Range("2.*.*"), true, true, (release) =>
142163
release.assets.some(
143164
(asset) => asset.name === "otherExampleAsset.txt",
144165
),
@@ -152,9 +173,21 @@ describe("Releases API consumer", () => {
152173
const latestRelease = await consumer.getLatestRelease(
153174
unconstrainedVersionRange,
154175
true,
176+
true,
155177
);
156178
expect(latestRelease.id).toBe(5);
157179
});
180+
181+
it("ignores invalid semver and picks (pre-)release with highest id", async () => {
182+
const consumer = new MockReleasesApiConsumer(owner, repo);
183+
184+
const latestRelease = await consumer.getLatestRelease(
185+
undefined,
186+
false,
187+
true,
188+
);
189+
expect(latestRelease.id).toBe(6);
190+
});
158191
});
159192

160193
it("gets correct assets for a release", async () => {

0 commit comments

Comments
 (0)