mirror of
https://github.com/github/codeql-action.git
synced 2026-05-11 00:00:30 +00:00
Expose all enabled default CLI versions
This commit is contained in:
+20
-9
@@ -70,7 +70,7 @@ async function installIntoToolcache({
|
||||
tmpDir,
|
||||
util.GitHubVariant.GHES,
|
||||
cliVersion !== undefined
|
||||
? { cliVersion, tagName }
|
||||
? { enabledVersions: [{ cliVersion, tagName }] }
|
||||
: SAMPLE_DEFAULT_CLI_VERSION,
|
||||
createFeatures([]),
|
||||
getRunnerLogger(true),
|
||||
@@ -284,11 +284,11 @@ for (const {
|
||||
for (const toolcacheVersion of [
|
||||
// Test that we use the tools from the toolcache when `SAMPLE_DEFAULT_CLI_VERSION` is requested
|
||||
// and `SAMPLE_DEFAULT_CLI_VERSION-` is in the toolcache.
|
||||
SAMPLE_DEFAULT_CLI_VERSION.cliVersion,
|
||||
`${SAMPLE_DEFAULT_CLI_VERSION.cliVersion}-20230101`,
|
||||
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
|
||||
`${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion}-20230101`,
|
||||
]) {
|
||||
test.serial(
|
||||
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.cliVersion} is requested and ` +
|
||||
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion} is requested and ` +
|
||||
`${toolcacheVersion} is installed`,
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
@@ -312,7 +312,10 @@ for (const toolcacheVersion of [
|
||||
getRunnerLogger(true),
|
||||
false,
|
||||
);
|
||||
t.is(result.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
|
||||
t.is(
|
||||
result.toolsVersion,
|
||||
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
|
||||
);
|
||||
t.is(result.toolsSource, ToolsSource.Toolcache);
|
||||
t.is(result.toolsDownloadStatusReport?.combinedDurationMs, undefined);
|
||||
t.is(result.toolsDownloadStatusReport?.downloadDurationMs, undefined);
|
||||
@@ -342,8 +345,12 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.GHES,
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
},
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
@@ -384,8 +391,12 @@ test.serial(
|
||||
tmpDir,
|
||||
util.GitHubVariant.GHES,
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
},
|
||||
features,
|
||||
getRunnerLogger(true),
|
||||
|
||||
+27
-12
@@ -451,12 +451,16 @@ test.serial(`selects CLI from defaults.json on GHES`, async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const features = setUpFeatureFlagTests(tmpDir);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(
|
||||
const defaultCliVersion = await features.getEnabledDefaultCliVersions(
|
||||
GitHubVariant.GHES,
|
||||
);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -482,10 +486,13 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
|
||||
false;
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(variant);
|
||||
const defaultCliVersion =
|
||||
await features.getEnabledDefaultCliVersions(variant);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: "2.20.1",
|
||||
tagName: "codeql-bundle-v2.20.1",
|
||||
enabledVersions: [
|
||||
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
|
||||
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
|
||||
],
|
||||
toolsFeatureFlagsValid: true,
|
||||
});
|
||||
});
|
||||
@@ -500,10 +507,15 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
|
||||
const expectedFeatureEnablement = initializeFeatures(true);
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(variant);
|
||||
const defaultCliVersion =
|
||||
await features.getEnabledDefaultCliVersions(variant);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
toolsFeatureFlagsValid: false,
|
||||
});
|
||||
});
|
||||
@@ -529,10 +541,13 @@ for (const variant of [GitHubVariant.DOTCOM, GitHubVariant.GHEC_DR]) {
|
||||
] = true;
|
||||
mockFeatureFlagApiEndpoint(200, expectedFeatureEnablement);
|
||||
|
||||
const defaultCliVersion = await features.getDefaultCliVersion(variant);
|
||||
const defaultCliVersion =
|
||||
await features.getEnabledDefaultCliVersions(variant);
|
||||
t.deepEqual(defaultCliVersion, {
|
||||
cliVersion: "2.20.1",
|
||||
tagName: "codeql-bundle-v2.20.1",
|
||||
enabledVersions: [
|
||||
{ cliVersion: "2.20.1", tagName: "codeql-bundle-v2.20.1" },
|
||||
{ cliVersion: "2.20.0", tagName: "codeql-bundle-v2.20.0" },
|
||||
],
|
||||
toolsFeatureFlagsValid: true,
|
||||
});
|
||||
|
||||
|
||||
+56
-23
@@ -29,9 +29,27 @@ const DEFAULT_VERSION_FEATURE_FLAG_SUFFIX = "_enabled";
|
||||
*/
|
||||
export const CODEQL_VERSION_ZSTD_BUNDLE = "2.19.0";
|
||||
|
||||
export interface CodeQLDefaultVersionInfo {
|
||||
export interface CodeQLVersionInfo {
|
||||
/** The version number of the CodeQL CLI, e.g. `2.19.0`. */
|
||||
cliVersion: string;
|
||||
/**
|
||||
* The tag name of the CodeQL Bundle associated with this version, e.g. `codeql-bundle-v2.19.0`.
|
||||
*/
|
||||
tagName: string;
|
||||
}
|
||||
|
||||
export interface CodeQLDefaultVersionInfo {
|
||||
/**
|
||||
* CodeQL CLI versions that are enabled as defaults, sorted from highest to lowest.
|
||||
*
|
||||
* Guaranteed to be non-empty. When feature flags are unavailable, this falls back to a single
|
||||
* entry containing the version pinned in `defaults.json`.
|
||||
*/
|
||||
enabledVersions: CodeQLVersionInfo[];
|
||||
/**
|
||||
* If accessed, whether the tools feature flags are valid, i.e. contain at least one enabled
|
||||
* version.
|
||||
*/
|
||||
toolsFeatureFlagsValid?: boolean;
|
||||
}
|
||||
|
||||
@@ -358,8 +376,12 @@ export type FeatureWithoutCLI = {
|
||||
}[keyof typeof featureConfig];
|
||||
|
||||
export interface FeatureEnablement {
|
||||
/** Gets the default version of the CodeQL tools. */
|
||||
getDefaultCliVersion(
|
||||
/**
|
||||
* Returns the set of default CodeQL CLI versions to consider, sorted from
|
||||
* highest to lowest. The first entry is the version that the CodeQL Action
|
||||
* will use by default. The list is always non-empty.
|
||||
*/
|
||||
getEnabledDefaultCliVersions(
|
||||
variant: util.GitHubVariant,
|
||||
): Promise<CodeQLDefaultVersionInfo>;
|
||||
getValue(feature: FeatureWithoutCLI): Promise<boolean>;
|
||||
@@ -383,12 +405,16 @@ export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
|
||||
class OfflineFeatures implements FeatureEnablement {
|
||||
constructor(protected readonly logger: Logger) {}
|
||||
|
||||
async getDefaultCliVersion(
|
||||
async getEnabledDefaultCliVersions(
|
||||
_variant: util.GitHubVariant,
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
return {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -530,13 +556,13 @@ class Features extends OfflineFeatures {
|
||||
);
|
||||
}
|
||||
|
||||
async getDefaultCliVersion(
|
||||
async getEnabledDefaultCliVersions(
|
||||
variant: util.GitHubVariant,
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
if (supportsFeatureFlags(variant)) {
|
||||
return await this.gitHubFeatureFlags.getDefaultCliVersionFromFlags();
|
||||
return await this.gitHubFeatureFlags.getEnabledDefaultCliVersionsFromFlags();
|
||||
}
|
||||
return super.getDefaultCliVersion(variant);
|
||||
return super.getEnabledDefaultCliVersions(variant);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -612,16 +638,22 @@ class GitHubFeatureFlags {
|
||||
return version;
|
||||
}
|
||||
|
||||
async getDefaultCliVersionFromFlags(): Promise<CodeQLDefaultVersionInfo> {
|
||||
/**
|
||||
* Returns CLI versions enabled by `default_codeql_version_*_enabled` feature
|
||||
* flags, sorted from highest to lowest. Falls back to the version pinned in
|
||||
* `defaults.json` if no such flags are enabled.
|
||||
*/
|
||||
async getEnabledDefaultCliVersionsFromFlags(): Promise<CodeQLDefaultVersionInfo> {
|
||||
const response = await this.getAllFeatures();
|
||||
|
||||
const enabledFeatureFlagCliVersions = Object.entries(response)
|
||||
const sortedCliVersions = Object.entries(response)
|
||||
.map(([f, isEnabled]) =>
|
||||
isEnabled ? this.getCliVersionFromFeatureFlag(f) : undefined,
|
||||
)
|
||||
.filter((f): f is string => f !== undefined);
|
||||
.filter((f): f is string => f !== undefined)
|
||||
.sort(semver.rcompare);
|
||||
|
||||
if (enabledFeatureFlagCliVersions.length === 0) {
|
||||
if (sortedCliVersions.length === 0) {
|
||||
// We expect at least one default CLI version to be enabled on Dotcom at any time. However if
|
||||
// the feature flags are misconfigured, rather than crashing, we fall back to the CLI version
|
||||
// shipped with the Action in defaults.json. This has the effect of immediately rolling out
|
||||
@@ -637,8 +669,12 @@ class GitHubFeatureFlags {
|
||||
`shipped with the Action. This is ${defaults.cliVersion}.`,
|
||||
);
|
||||
const result: CodeQLDefaultVersionInfo = {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
},
|
||||
],
|
||||
};
|
||||
if (this.hasAccessedRemoteFeatureFlags) {
|
||||
result.toolsFeatureFlagsValid = false;
|
||||
@@ -646,17 +682,14 @@ class GitHubFeatureFlags {
|
||||
return result;
|
||||
}
|
||||
|
||||
const maxCliVersion = enabledFeatureFlagCliVersions.reduce(
|
||||
(maxVersion, currentVersion) =>
|
||||
currentVersion > maxVersion ? currentVersion : maxVersion,
|
||||
enabledFeatureFlagCliVersions[0],
|
||||
);
|
||||
this.logger.debug(
|
||||
`Derived default CLI version of ${maxCliVersion} from feature flags.`,
|
||||
`Derived default CLI version of ${sortedCliVersions[0]} from feature flags.`,
|
||||
);
|
||||
return {
|
||||
cliVersion: maxCliVersion,
|
||||
tagName: `codeql-bundle-v${maxCliVersion}`,
|
||||
enabledVersions: sortedCliVersions.map((cliVersion) => ({
|
||||
cliVersion,
|
||||
tagName: `codeql-bundle-v${cliVersion}`,
|
||||
})),
|
||||
toolsFeatureFlagsValid: true,
|
||||
};
|
||||
}
|
||||
|
||||
+2
-3
@@ -298,9 +298,8 @@ async function run(startedAt: Date) {
|
||||
);
|
||||
}
|
||||
|
||||
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
|
||||
gitHubVersion.type,
|
||||
);
|
||||
const codeQLDefaultVersionInfo =
|
||||
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
|
||||
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
|
||||
const initCodeQLResult = await initCodeQL(
|
||||
getOptionalInput("tools"),
|
||||
|
||||
@@ -136,9 +136,8 @@ async function run(startedAt: Date): Promise<void> {
|
||||
if (statusReportBase !== undefined) {
|
||||
await sendStatusReport(statusReportBase);
|
||||
}
|
||||
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
|
||||
gitHubVersion.type,
|
||||
);
|
||||
const codeQLDefaultVersionInfo =
|
||||
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
|
||||
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
|
||||
const initCodeQLResult = await initCodeQL(
|
||||
getOptionalInput("tools"),
|
||||
|
||||
@@ -514,7 +514,10 @@ const toolcacheInputFallbackMacro = test.macro({
|
||||
|
||||
// Check that `sourceType` and `toolsVersion` match expectations.
|
||||
t.is(source.sourceType, "download");
|
||||
t.is(source.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
|
||||
t.is(
|
||||
source.toolsVersion,
|
||||
SAMPLE_DEFAULT_CLI_VERSION.enabledVersions[0].cliVersion,
|
||||
);
|
||||
|
||||
// Check that key messages we would expect to find in the log are present.
|
||||
for (const expectedMessage of expectedMessages) {
|
||||
|
||||
+4
-4
@@ -438,8 +438,8 @@ export async function getCodeQLSource(
|
||||
}
|
||||
}
|
||||
|
||||
cliVersion = defaultCliVersion.cliVersion;
|
||||
tagName = defaultCliVersion.tagName;
|
||||
cliVersion = defaultCliVersion.enabledVersions[0].cliVersion;
|
||||
tagName = defaultCliVersion.enabledVersions[0].tagName;
|
||||
}
|
||||
} else if (toolsInput !== undefined) {
|
||||
// If a tools URL was provided, then use that.
|
||||
@@ -455,8 +455,8 @@ export async function getCodeQLSource(
|
||||
}
|
||||
} else {
|
||||
// Otherwise, use the default CLI version passed in.
|
||||
cliVersion = defaultCliVersion.cliVersion;
|
||||
tagName = defaultCliVersion.tagName;
|
||||
cliVersion = defaultCliVersion.enabledVersions[0].cliVersion;
|
||||
tagName = defaultCliVersion.enabledVersions[0].tagName;
|
||||
}
|
||||
|
||||
const bundleVersion =
|
||||
|
||||
@@ -1019,8 +1019,10 @@ test.serial(
|
||||
return true;
|
||||
});
|
||||
const getDefaultCliVersion = sinon
|
||||
.stub(features, "getDefaultCliVersion")
|
||||
.resolves({ cliVersion: "2.20.1", tagName: expectedTag });
|
||||
.stub(features, "getEnabledDefaultCliVersions")
|
||||
.resolves({
|
||||
enabledVersions: [{ cliVersion: "2.20.1", tagName: expectedTag }],
|
||||
});
|
||||
const path = await startProxyExports.getProxyBinaryPath(logger, features);
|
||||
|
||||
t.assert(getDefaultCliVersion.calledOnce);
|
||||
|
||||
+2
-2
@@ -415,7 +415,7 @@ async function getCliVersionFromFeatures(
|
||||
features: FeatureEnablement,
|
||||
): Promise<CodeQLDefaultVersionInfo> {
|
||||
const gitHubVersion = await getGitHubVersion();
|
||||
return await features.getDefaultCliVersion(gitHubVersion.type);
|
||||
return await features.getEnabledDefaultCliVersions(gitHubVersion.type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,7 +440,7 @@ export async function getDownloadUrl(
|
||||
// Retrieve information about the CLI version we should use. This will be either the linked
|
||||
// version, or the one enabled by FFs.
|
||||
const versionInfo = useFeaturesToDetermineCLI
|
||||
? await getCliVersionFromFeatures(features)
|
||||
? (await getCliVersionFromFeatures(features)).enabledVersions[0]
|
||||
: {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
|
||||
+10
-6
@@ -36,16 +36,20 @@ export const SAMPLE_DOTCOM_API_DETAILS = {
|
||||
apiURL: "https://api.github.com",
|
||||
};
|
||||
|
||||
export const SAMPLE_DEFAULT_CLI_VERSION: CodeQLDefaultVersionInfo = {
|
||||
cliVersion: "2.20.0",
|
||||
tagName: "codeql-bundle-v2.20.0",
|
||||
};
|
||||
|
||||
export const LINKED_CLI_VERSION = {
|
||||
cliVersion: defaults.cliVersion,
|
||||
tagName: defaults.bundleVersion,
|
||||
};
|
||||
|
||||
export const SAMPLE_DEFAULT_CLI_VERSION: CodeQLDefaultVersionInfo = {
|
||||
enabledVersions: [
|
||||
{
|
||||
cliVersion: "2.20.0",
|
||||
tagName: "codeql-bundle-v2.20.0",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
type TestContext = {
|
||||
stdoutWrite: any;
|
||||
stderrWrite: any;
|
||||
@@ -442,7 +446,7 @@ export function mockCodeQLVersion(
|
||||
*/
|
||||
export function createFeatures(enabledFeatures: Feature[]): FeatureEnablement {
|
||||
return {
|
||||
getDefaultCliVersion: async () => {
|
||||
getEnabledDefaultCliVersions: async () => {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
getValue: async (feature) => {
|
||||
|
||||
+2
-3
@@ -156,9 +156,8 @@ async function combineSarifFilesUsingCLI(
|
||||
apiURL: getRequiredEnvParam("GITHUB_API_URL"),
|
||||
};
|
||||
|
||||
const codeQLDefaultVersionInfo = await features.getDefaultCliVersion(
|
||||
gitHubVersion.type,
|
||||
);
|
||||
const codeQLDefaultVersionInfo =
|
||||
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
|
||||
|
||||
const initCodeQLResult = await initCodeQL(
|
||||
undefined, // There is no tools input on the upload action
|
||||
|
||||
Reference in New Issue
Block a user