Gate tools: toolcache behind FF

Mainly to allow us to disable it, if needed.
This commit is contained in:
Michael B. Gale
2025-10-10 13:49:06 +01:00
parent 62f0f21c3c
commit a512fe0868
12 changed files with 177 additions and 45 deletions
+28
View File
@@ -74,6 +74,7 @@ async function installIntoToolcache({
cliVersion !== undefined
? { cliVersion, tagName }
: SAMPLE_DEFAULT_CLI_VERSION,
createFeatures([]),
getRunnerLogger(true),
false,
);
@@ -122,6 +123,8 @@ async function stubCodeql(): Promise<codeql.CodeQL> {
}
test("downloads and caches explicitly requested bundles that aren't in the toolcache", async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -140,6 +143,7 @@ test("downloads and caches explicitly requested bundles that aren't in the toolc
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
@@ -154,6 +158,8 @@ test("downloads and caches explicitly requested bundles that aren't in the toolc
});
test("caches semantically versioned bundles using their semantic version number", async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const url = mockBundleDownloadApi({
@@ -166,6 +172,7 @@ test("caches semantically versioned bundles using their semantic version number"
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
@@ -181,6 +188,8 @@ test("caches semantically versioned bundles using their semantic version number"
});
test("downloads an explicitly requested bundle even if a different version is cached", async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -199,6 +208,7 @@ test("downloads an explicitly requested bundle even if a different version is ca
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
@@ -227,6 +237,8 @@ for (const {
expectedToolcacheVersion,
} of EXPLICITLY_REQUESTED_BUNDLE_TEST_CASES) {
test(`caches explicitly requested bundle ${tagName} as ${expectedToolcacheVersion}`, async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -243,6 +255,7 @@ for (const {
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
@@ -266,6 +279,8 @@ for (const toolcacheVersion of [
`uses tools from toolcache when ${SAMPLE_DEFAULT_CLI_VERSION.cliVersion} is requested and ` +
`${toolcacheVersion} is installed`,
async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -281,6 +296,7 @@ for (const toolcacheVersion of [
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
@@ -295,6 +311,8 @@ for (const toolcacheVersion of [
}
test(`uses a cached bundle when no tools input is given on GHES`, async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -313,6 +331,7 @@ test(`uses a cached bundle when no tools input is given on GHES`, async (t) => {
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
features,
getRunnerLogger(true),
false,
);
@@ -328,6 +347,8 @@ test(`uses a cached bundle when no tools input is given on GHES`, async (t) => {
});
test(`downloads bundle if only an unpinned version is cached on GHES`, async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -349,6 +370,7 @@ test(`downloads bundle if only an unpinned version is cached on GHES`, async (t)
cliVersion: defaults.cliVersion,
tagName: defaults.bundleVersion,
},
features,
getRunnerLogger(true),
false,
);
@@ -364,6 +386,8 @@ test(`downloads bundle if only an unpinned version is cached on GHES`, async (t)
});
test('downloads bundle if "latest" tools specified but not cached', async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -382,6 +406,7 @@ test('downloads bundle if "latest" tools specified but not cached', async (t) =>
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
@@ -397,6 +422,8 @@ test('downloads bundle if "latest" tools specified but not cached', async (t) =>
});
test("bundle URL from another repo is cached as 0.0.0-bundleVersion", async (t) => {
const features = createFeatures([]);
await util.withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -417,6 +444,7 @@ test("bundle URL from another repo is cached as 0.0.0-bundleVersion", async (t)
tmpDir,
util.GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
getRunnerLogger(true),
false,
);
+3
View File
@@ -309,6 +309,7 @@ const CODEQL_VERSION_CACHE_CLEANUP = "2.17.1";
* @param tempDir
* @param variant
* @param defaultCliVersion
* @param features Information about the features that are enabled.
* @param logger
* @param checkVersion Whether to check that CodeQL CLI meets the minimum
* version requirement. Must be set to true outside tests.
@@ -320,6 +321,7 @@ export async function setupCodeQL(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
features: FeatureEnablement,
logger: Logger,
checkVersion: boolean,
): Promise<{
@@ -342,6 +344,7 @@ export async function setupCodeQL(
tempDir,
variant,
defaultCliVersion,
features,
logger,
);
+1
View File
@@ -248,6 +248,7 @@ async function run() {
getTemporaryDirectory(),
gitHubVersion.type,
codeQLDefaultVersionInfo,
features,
logger,
);
codeql = initCodeQLResult.codeql;
+3 -1
View File
@@ -9,7 +9,7 @@ import { getOptionalInput, isSelfHostedRunner } from "./actions-util";
import { GitHubApiDetails } from "./api-client";
import { CodeQL, setupCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { CodeQLDefaultVersionInfo } from "./feature-flags";
import { CodeQLDefaultVersionInfo, FeatureEnablement } from "./feature-flags";
import { KnownLanguage, Language } from "./languages";
import { Logger, withGroupAsync } from "./logging";
import { ToolsSource } from "./setup-codeql";
@@ -23,6 +23,7 @@ export async function initCodeQL(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
features: FeatureEnablement,
logger: Logger,
): Promise<{
codeql: CodeQL;
@@ -44,6 +45,7 @@ export async function initCodeQL(
tempDir,
variant,
defaultCliVersion,
features,
logger,
true,
);
+17
View File
@@ -13,6 +13,7 @@ import {
LoggedMessage,
SAMPLE_DEFAULT_CLI_VERSION,
SAMPLE_DOTCOM_API_DETAILS,
createFeatures,
getRecordingLogger,
initializeFeatures,
mockBundleDownloadApi,
@@ -91,6 +92,8 @@ test("getCodeQLActionRepository", (t) => {
});
test("getCodeQLSource sets CLI version for a semver tagged bundle", async (t) => {
const features = createFeatures([]);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const tagName = "codeql-bundle-v1.2.3";
@@ -101,6 +104,7 @@ test("getCodeQLSource sets CLI version for a semver tagged bundle", async (t) =>
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
getRunnerLogger(true),
);
@@ -110,6 +114,8 @@ test("getCodeQLSource sets CLI version for a semver tagged bundle", async (t) =>
});
test("getCodeQLSource correctly returns bundled CLI version when tools == linked", async (t) => {
const features = createFeatures([]);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const source = await setupCodeql.getCodeQLSource(
@@ -118,6 +124,7 @@ test("getCodeQLSource correctly returns bundled CLI version when tools == linked
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
getRunnerLogger(true),
);
@@ -129,6 +136,7 @@ test("getCodeQLSource correctly returns bundled CLI version when tools == linked
test("getCodeQLSource correctly returns bundled CLI version when tools == latest", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([]);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
@@ -138,6 +146,7 @@ test("getCodeQLSource correctly returns bundled CLI version when tools == latest
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
logger,
);
@@ -162,6 +171,7 @@ test("getCodeQLSource correctly returns bundled CLI version when tools == latest
test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to use linked tools", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([]);
// Stub the downloadCodeQL function to prevent downloading artefacts
// during testing from being called.
@@ -186,6 +196,7 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to use
"tmp/codeql_action_test/",
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
logger,
);
@@ -208,6 +219,7 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to use
test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to download a non-default bundle", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([]);
const bundleUrl =
"https://github.com/github/codeql-action/releases/download/codeql-bundle-v2.16.0/codeql-bundle-linux64.tar.gz";
@@ -236,6 +248,7 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to dow
"tmp/codeql_action_test/",
GitHubVariant.DOTCOM,
SAMPLE_DEFAULT_CLI_VERSION,
features,
logger,
);
@@ -258,6 +271,7 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to dow
test("getCodeQLSource correctly returns latest version from toolcache when tools == toolcache", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([Feature.AllowToolcacheInput]);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
@@ -280,6 +294,7 @@ test("getCodeQLSource correctly returns latest version from toolcache when tools
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
logger,
);
@@ -319,6 +334,7 @@ test("getCodeQLSource correctly returns latest version from toolcache when tools
test("getCodeQLSource falls back to downloading the CLI if the toolcache doesn't have a CodeQL CLI when tools == toolcache", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
const features = createFeatures([Feature.AllowToolcacheInput]);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
@@ -335,6 +351,7 @@ test("getCodeQLSource falls back to downloading the CLI if the toolcache doesn't
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
features,
logger,
);
+19 -4
View File
@@ -13,6 +13,8 @@ import * as defaults from "./defaults.json";
import {
CODEQL_VERSION_ZSTD_BUNDLE,
CodeQLDefaultVersionInfo,
Feature,
FeatureEnablement,
} from "./feature-flags";
import { Logger } from "./logging";
import * as tar from "./tar";
@@ -276,6 +278,7 @@ export async function getCodeQLSource(
apiDetails: api.GitHubApiDetails,
variant: util.GitHubVariant,
tarSupportsZstd: boolean,
features: FeatureEnablement,
logger: Logger,
): Promise<CodeQLToolsSource> {
if (
@@ -356,7 +359,11 @@ export async function getCodeQLSource(
// We only allow `toolsInput === "toolcache"` for `dynamic` events. In general, using `toolsInput === "toolcache"`
// can lead to alert wobble and so it shouldn't be used for an analysis where results are intended to be uploaded.
// We also allow this in test mode.
const allowToolcacheValue = isDynamicWorkflow() || util.isInTestMode();
const allowToolcacheValueFF = await features.getValue(
Feature.AllowToolcacheInput,
);
const allowToolcacheValue =
allowToolcacheValueFF && (isDynamicWorkflow() || util.isInTestMode());
if (allowToolcacheValue) {
// If `toolsInput === "toolcache"`, try to find the latest version of the CLI that's available in the toolcache
// and use that. We perform this check here since we can set `cliVersion` directly and don't want to default to
@@ -377,9 +384,15 @@ export async function getCodeQLSource(
`Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...`,
);
} else {
logger.warning(
`Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`,
);
if (allowToolcacheValueFF) {
logger.info(
`Ignoring 'tools: ${toolsInput}' because the feature is not enabled.`,
);
} else {
logger.warning(
`Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`,
);
}
}
cliVersion = defaultCliVersion.cliVersion;
@@ -735,6 +748,7 @@ export async function setupCodeQLBundle(
tempDir: string,
variant: util.GitHubVariant,
defaultCliVersion: CodeQLDefaultVersionInfo,
features: FeatureEnablement,
logger: Logger,
) {
if (!(await util.isBinaryAccessible("tar", logger))) {
@@ -750,6 +764,7 @@ export async function setupCodeQLBundle(
apiDetails,
variant,
zstdAvailability.available,
features,
logger,
);
+1
View File
@@ -262,6 +262,7 @@ async function combineSarifFilesUsingCLI(
tempDir,
gitHubVersion.type,
codeQLDefaultVersionInfo,
features,
logger,
);