Support determining Dotcom CLI version from feature flags
This commit is contained in:
parent
6ba0a36550
commit
cdb90196f2
12 changed files with 307 additions and 7 deletions
|
|
@ -1,8 +1,9 @@
|
|||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import test from "ava";
|
||||
import test, { ExecutionContext } from "ava";
|
||||
|
||||
import * as defaults from "./defaults.json"; // Referenced from codeql-action-sync-tool!
|
||||
import {
|
||||
Feature,
|
||||
featureConfig,
|
||||
|
|
@ -371,7 +372,61 @@ test("Environment variable can override feature flag cache", async (t) => {
|
|||
});
|
||||
});
|
||||
|
||||
function assertAllFeaturesUndefinedInApi(t, loggedMessages: LoggedMessage[]) {
|
||||
for (const variant of [GitHubVariant.GHAE, GitHubVariant.GHES]) {
|
||||
test(`selects CLI from defaults.json on ${GitHubVariant[variant]}`, async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const features = setUpFeatureFlagTests(tmpDir);
|
||||
t.deepEqual(await features.getDefaultCliVersion(variant), {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
variant,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test("selects CLI v2.12.1 on Dotcom when feature flags enable v2.12.0 and v2.12.1", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const featureEnablement = setUpFeatureFlagTests(tmpDir);
|
||||
const expectedFeatureEnablement = initializeFeatures(true);
|
||||
expectedFeatureEnablement["default_codeql_version_2_12_0_enabled"] = true;
|
||||
expectedFeatureEnablement["default_codeql_version_2_12_1_enabled"] = true;
|
||||
expectedFeatureEnablement["default_codeql_version_2_12_2_enabled"] = false;
|
||||
expectedFeatureEnablement["default_codeql_version_2_12_3_enabled"] = false;
|
||||
expectedFeatureEnablement["default_codeql_version_2_12_4_enabled"] = false;
|
||||
expectedFeatureEnablement["default_codeql_version_2_12_5_enabled"] = false;
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
t.deepEqual(
|
||||
await featureEnablement.getDefaultCliVersion(GitHubVariant.DOTCOM),
|
||||
{
|
||||
cliVersion: "2.12.1",
|
||||
variant: GitHubVariant.DOTCOM,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test(`selects CLI v2.11.6 on Dotcom when no default version feature flags are enabled`, async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const featureEnablement = setUpFeatureFlagTests(tmpDir);
|
||||
const expectedFeatureEnablement = initializeFeatures(true);
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
t.deepEqual(
|
||||
await featureEnablement.getDefaultCliVersion(GitHubVariant.DOTCOM),
|
||||
{
|
||||
cliVersion: "2.11.6",
|
||||
variant: GitHubVariant.DOTCOM,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function assertAllFeaturesUndefinedInApi(
|
||||
t: ExecutionContext<unknown>,
|
||||
loggedMessages: LoggedMessage[]
|
||||
) {
|
||||
for (const feature of Object.keys(featureConfig)) {
|
||||
t.assert(
|
||||
loggedMessages.find(
|
||||
|
|
|
|||
|
|
@ -3,11 +3,31 @@ import * as path from "path";
|
|||
|
||||
import { getApiClient } from "./api-client";
|
||||
import { CodeQL } from "./codeql";
|
||||
import * as defaults from "./defaults.json"; // Referenced from codeql-action-sync-tool!
|
||||
import { Logger } from "./logging";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import * as util from "./util";
|
||||
|
||||
const DEFAULT_VERSION_FEATURE_FLAG_PREFIX = "default_codeql_version_";
|
||||
const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
|
||||
const MINIMUM_ENABLED_CODEQL_VERSION = "2.11.6";
|
||||
|
||||
export type CodeQLDefaultVersionInfo =
|
||||
| {
|
||||
cliVersion: string;
|
||||
variant: util.GitHubVariant.DOTCOM;
|
||||
}
|
||||
| {
|
||||
cliVersion: string;
|
||||
tagName: string;
|
||||
variant: util.GitHubVariant.GHAE | util.GitHubVariant.GHES;
|
||||
};
|
||||
|
||||
export interface FeatureEnablement {
|
||||
/** Gets the default version of the CodeQL tools. */
|
||||
getDefaultCliVersion(
|
||||
variant: util.GitHubVariant
|
||||
): Promise<CodeQLDefaultVersionInfo>;
|
||||
getValue(feature: Feature, codeql?: CodeQL): Promise<boolean>;
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +111,12 @@ export class Features implements FeatureEnablement {
|
|||
);
|
||||
}
|
||||
|
||||
async getDefaultCliVersion(
|
||||
variant: util.GitHubVariant
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
return await this.gitHubFeatureFlags.getDefaultCliVersion(variant);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param feature The feature to check.
|
||||
|
|
@ -153,6 +179,68 @@ class GitHubFeatureFlags implements FeatureEnablement {
|
|||
/**/
|
||||
}
|
||||
|
||||
private static getCliVersionFromFeatureFlag(f: string): string | undefined {
|
||||
if (
|
||||
!f.startsWith(DEFAULT_VERSION_FEATURE_FLAG_PREFIX) ||
|
||||
!f.endsWith(DEFAULT_VERSION_FEATURE_FLAG_SUFFIX)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return f
|
||||
.substring(
|
||||
DEFAULT_VERSION_FEATURE_FLAG_PREFIX.length,
|
||||
f.length - DEFAULT_VERSION_FEATURE_FLAG_SUFFIX.length
|
||||
)
|
||||
.replace(/_/g, ".");
|
||||
}
|
||||
|
||||
async getDefaultCliVersion(
|
||||
variant: util.GitHubVariant
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
if (variant === util.GitHubVariant.DOTCOM) {
|
||||
return {
|
||||
cliVersion: await this.getDefaultDotcomCliVersion(),
|
||||
variant,
|
||||
};
|
||||
}
|
||||
return {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
variant,
|
||||
};
|
||||
}
|
||||
|
||||
async getDefaultDotcomCliVersion(): Promise<string> {
|
||||
const response = await this.getAllFeatures();
|
||||
|
||||
const enabledFeatureFlagCliVersions = Object.entries(response)
|
||||
.map(([f, isEnabled]) =>
|
||||
isEnabled
|
||||
? GitHubFeatureFlags.getCliVersionFromFeatureFlag(f)
|
||||
: undefined
|
||||
)
|
||||
.filter((f) => f !== undefined)
|
||||
.map((f) => f as string);
|
||||
|
||||
if (enabledFeatureFlagCliVersions.length === 0) {
|
||||
this.logger.debug(
|
||||
"Feature flags do not specify a default CLI version. Falling back to CLI version " +
|
||||
`${MINIMUM_ENABLED_CODEQL_VERSION}.`
|
||||
);
|
||||
return MINIMUM_ENABLED_CODEQL_VERSION;
|
||||
}
|
||||
|
||||
const maxCliVersion = enabledFeatureFlagCliVersions.reduce(
|
||||
(maxVersion, currentVersion) =>
|
||||
currentVersion > maxVersion ? currentVersion : maxVersion,
|
||||
enabledFeatureFlagCliVersions[0]
|
||||
);
|
||||
this.logger.debug(
|
||||
`Derived default CLI version of ${maxCliVersion} from feature flags.`
|
||||
);
|
||||
return maxCliVersion;
|
||||
}
|
||||
|
||||
async getValue(feature: Feature): Promise<boolean> {
|
||||
const response = await this.getAllFeatures();
|
||||
if (response === undefined) {
|
||||
|
|
|
|||
|
|
@ -201,6 +201,9 @@ export function mockCodeQLVersion(version) {
|
|||
*/
|
||||
export function createFeatures(enabledFeatures: Feature[]): FeatureEnablement {
|
||||
return {
|
||||
getDefaultCliVersion: async () => {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
getValue: async (feature) => {
|
||||
return enabledFeatures.includes(feature);
|
||||
},
|
||||
|
|
|
|||
42
src/util.ts
42
src/util.ts
|
|
@ -19,6 +19,7 @@ import {
|
|||
parsePacksSpecification,
|
||||
prettyPrintPack,
|
||||
} from "./config-utils";
|
||||
import * as defaults from "./defaults.json"; // Referenced from codeql-action-sync-tool!
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { KOTLIN_SWIFT_BYPASS, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
|
|
@ -901,3 +902,44 @@ export function parseMatrixInput(
|
|||
}
|
||||
return JSON.parse(matrixInput);
|
||||
}
|
||||
|
||||
/** Version information about a CodeQL bundle. */
|
||||
export interface CodeqlBundleVersionInfo {
|
||||
/** Version number of the CLI contained within this bundle. */
|
||||
cliVersion: string;
|
||||
/** The name of the tag of the GitHub Release containing this bundle. */
|
||||
tagName: string;
|
||||
/**
|
||||
* Version number of this bundle within the toolcache.
|
||||
*
|
||||
* For compatibility with previous runner images and toolcaches, consumers should fallback to
|
||||
* looking up the `0.0.0-pre` version number within the toolcache if this version number is not
|
||||
* found, where `pre` is the prerelease component of this version number.
|
||||
*/
|
||||
toolcacheVersion: string;
|
||||
}
|
||||
|
||||
function computeToolcacheVersion(cliVersion: string, tagName: string): string {
|
||||
return `${cliVersion}-${tagName}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets version information about the CodeQL bundle which was current as of the release of this
|
||||
* Action.
|
||||
*
|
||||
* This should be used in the following circumstances:
|
||||
*
|
||||
* - When running the Action on Enterprise instances.
|
||||
* - When a user requests `tools: latest`.
|
||||
* - When the Action is running on Dotcom and no default CodeQL version feature flags are enabled.
|
||||
*/
|
||||
export function getPinnedCodeqlVersion(): CodeqlBundleVersionInfo {
|
||||
return {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
toolcacheVersion: computeToolcacheVersion(
|
||||
defaults.cliVersion,
|
||||
defaults.bundleVersion
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue