Merge pull request #3127 from github/robertbrignull/nightly-codeql

Provide option to point ReleasesApiConsumer at nightly builds repo
This commit is contained in:
Robert 2024-01-02 12:21:38 +00:00 коммит произвёл GitHub
Родитель c210a7fdf4 c423505c04
Коммит f5dbcc8cc1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 106 добавлений и 23 удалений

Просмотреть файл

@ -51,6 +51,16 @@ const DEFAULT_DISTRIBUTION_OWNER_NAME = "github";
*/
const DEFAULT_DISTRIBUTION_REPOSITORY_NAME = "codeql-cli-binaries";
/**
* Owner name of the nightly version of the extension-managed distribution on GitHub.
*/
const NIGHTLY_DISTRIBUTION_OWNER_NAME = "dsp-testing";
/**
* Repository name of the nightly version of the extension-managed distribution on GitHub.
*/
const NIGHTLY_DISTRIBUTION_REPOSITORY_NAME = "codeql-cli-nightlies";
/**
* Range of versions of the CLI that are compatible with the extension.
*
@ -453,9 +463,18 @@ class ExtensionSpecificDistributionManager {
void extLogger.log(
`Searching for latest release including ${requiredAssetName}.`,
);
const versionRange = this.usingNightlyReleases
? undefined
: this.versionRange;
const orderBySemver = !this.usingNightlyReleases;
const includePrerelease =
this.usingNightlyReleases || this.config.includePrerelease;
return this.createReleasesApiConsumer().getLatestRelease(
this.versionRange,
this.config.includePrerelease,
versionRange,
orderBySemver,
includePrerelease,
(release) => {
// v2.12.3 was released with a bug that causes the extension to fail
// so we force the extension to ignore it.
@ -485,19 +504,40 @@ class ExtensionSpecificDistributionManager {
}
private createReleasesApiConsumer(): ReleasesApiConsumer {
const ownerName = this.config.ownerName
? this.config.ownerName
: DEFAULT_DISTRIBUTION_OWNER_NAME;
const repositoryName = this.config.repositoryName
? this.config.repositoryName
: DEFAULT_DISTRIBUTION_REPOSITORY_NAME;
return new ReleasesApiConsumer(
ownerName,
repositoryName,
this.distributionOwnerName,
this.distributionRepositoryName,
this.config.personalAccessToken,
);
}
private get distributionOwnerName(): string {
if (this.config.ownerName) {
return this.config.ownerName;
} else if (this.config.channel === "nightly") {
return NIGHTLY_DISTRIBUTION_OWNER_NAME;
} else {
return DEFAULT_DISTRIBUTION_OWNER_NAME;
}
}
private get distributionRepositoryName(): string {
if (this.config.repositoryName) {
return this.config.repositoryName;
} else if (this.config.channel === "nightly") {
return NIGHTLY_DISTRIBUTION_REPOSITORY_NAME;
} else {
return DEFAULT_DISTRIBUTION_REPOSITORY_NAME;
}
}
private get usingNightlyReleases(): boolean {
return (
this.distributionOwnerName === NIGHTLY_DISTRIBUTION_OWNER_NAME &&
this.distributionRepositoryName === NIGHTLY_DISTRIBUTION_REPOSITORY_NAME
);
}
private async bumpDistributionFolderIndex(): Promise<void> {
const index = this.extensionContext.globalState.get(
ExtensionSpecificDistributionManager._currentDistributionFolderIndexStateKey,
@ -570,7 +610,8 @@ export class ReleasesApiConsumer {
}
public async getLatestRelease(
versionRange: semver.Range,
versionRange: semver.Range | undefined,
orderBySemver = true,
includePrerelease = false,
additionalCompatibilityCheck?: (release: GithubRelease) => boolean,
): Promise<Release> {
@ -583,12 +624,14 @@ export class ReleasesApiConsumer {
return false;
}
const version = semver.parse(release.tag_name);
if (
version === null ||
!semver.satisfies(version, versionRange, { includePrerelease })
) {
return false;
if (versionRange !== undefined) {
const version = semver.parse(release.tag_name);
if (
version === null ||
!semver.satisfies(version, versionRange, { includePrerelease })
) {
return false;
}
}
return (
@ -597,10 +640,9 @@ export class ReleasesApiConsumer {
});
// Tag names must all be parsable to semvers due to the previous filtering step.
const latestRelease = compatibleReleases.sort((a, b) => {
const versionComparison = semver.compare(
semver.parse(b.tag_name)!,
semver.parse(a.tag_name)!,
);
const versionComparison = orderBySemver
? semver.compare(semver.parse(b.tag_name)!, semver.parse(a.tag_name)!)
: b.id - a.id;
if (versionComparison !== 0) {
return versionComparison;
}

Просмотреть файл

@ -95,6 +95,7 @@ const PERSONAL_ACCESS_TOKEN_SETTING = new Setting(
"personalAccessToken",
DISTRIBUTION_SETTING,
);
const CLI_CHANNEL_SETTING = new Setting("channel", DISTRIBUTION_SETTING);
// Query History configuration
const QUERY_HISTORY_SETTING = new Setting("queryHistory", ROOT_SETTING);
@ -111,6 +112,8 @@ const DISTRIBUTION_CHANGE_SETTINGS = [
PERSONAL_ACCESS_TOKEN_SETTING,
];
export type CLIChannel = "stable" | "nightly";
export interface DistributionConfig {
readonly customCodeQlPath?: string;
updateCustomCodeQlPath: (newPath: string | undefined) => Promise<void>;
@ -118,6 +121,7 @@ export interface DistributionConfig {
personalAccessToken?: string;
ownerName?: string;
repositoryName?: string;
channel: CLIChannel;
onDidChangeConfiguration?: Event<void>;
}
@ -278,6 +282,10 @@ export class DistributionConfigListener
);
}
public get channel(): CLIChannel {
return CLI_CHANNEL_SETTING.getValue() === "nightly" ? "nightly" : "stable";
}
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
this.handleDidChangeConfigurationForRelevantSettings(
DISTRIBUTION_CHANGE_SETTINGS,

Просмотреть файл

@ -85,6 +85,15 @@ describe("Releases API consumer", () => {
prerelease: true,
tag_name: "v3.1.2-pre-2.0",
},
// Has a tag_name that is not valid semver
{
assets: [],
created_at: "2019-08-010T00:00:00Z",
id: 6,
name: "",
prerelease: true,
tag_name: "codeql-bundle-20231220",
},
];
class MockReleasesApiConsumer extends ReleasesApiConsumer {
@ -98,15 +107,26 @@ describe("Releases API consumer", () => {
}
}
it("picked release has version with the highest precedence", async () => {
it("picked release is non-prerelease with the highest semver", async () => {
const consumer = new MockReleasesApiConsumer(owner, repo);
const latestRelease = await consumer.getLatestRelease(
unconstrainedVersionRange,
true,
);
expect(latestRelease.id).toBe(2);
});
it("picked release is non-prerelease with highest id", async () => {
const consumer = new MockReleasesApiConsumer(owner, repo);
const latestRelease = await consumer.getLatestRelease(
unconstrainedVersionRange,
false,
);
expect(latestRelease.id).toBe(3);
});
it("version of picked release is within the version range", async () => {
const consumer = new MockReleasesApiConsumer(owner, repo);
@ -128,6 +148,7 @@ describe("Releases API consumer", () => {
const latestRelease = await consumer.getLatestRelease(
new Range("2.*.*"),
true,
true,
(release) =>
release.assets.some((asset) => asset.name === "exampleAsset.txt"),
);
@ -138,7 +159,7 @@ describe("Releases API consumer", () => {
const consumer = new MockReleasesApiConsumer(owner, repo);
await expect(
consumer.getLatestRelease(new Range("2.*.*"), true, (release) =>
consumer.getLatestRelease(new Range("2.*.*"), true, true, (release) =>
release.assets.some(
(asset) => asset.name === "otherExampleAsset.txt",
),
@ -152,9 +173,21 @@ describe("Releases API consumer", () => {
const latestRelease = await consumer.getLatestRelease(
unconstrainedVersionRange,
true,
true,
);
expect(latestRelease.id).toBe(5);
});
it("ignores invalid semver and picks (pre-)release with highest id", async () => {
const consumer = new MockReleasesApiConsumer(owner, repo);
const latestRelease = await consumer.getLatestRelease(
undefined,
false,
true,
);
expect(latestRelease.id).toBe(6);
});
});
it("gets correct assets for a release", async () => {