Refactor handling of feature flags

This commit centralizes how feature flags are handled. All feature flags
must now add an entry in the `featureFlagConfig` dictionary. This
dictionary associates the flag with an environment variable name and
optionally a minimum version for CodeQL.

The new logic is:

- if the environment variable is set to false: disabled
- if the minimum version requirement specified and met: disabled
- if the environment variable is set to true: enable
- Otherwise check feature flag enablement from the server
This commit is contained in:
Andrew Eisenberg 2022-10-05 15:54:07 -07:00
parent 24c8de16fa
commit e5c3375225
27 changed files with 400 additions and 368 deletions

3
lib/codeql.js generated
View file

@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExtraOptions = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.convertToSemVer = exports.getCodeQLURLVersion = exports.setupCodeQL = exports.getCodeQLActionRepository = exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = exports.CODEQL_VERSION_NEW_TRACING = exports.CODEQL_VERSION_GHES_PACK_DOWNLOAD = exports.CODEQL_VERSION_CONFIG_FILES = exports.CODEQL_VERSION_ML_POWERED_QUERIES = exports.CODEQL_VERSION_COUNTS_LINES = exports.CODEQL_DEFAULT_ACTION_REPOSITORY = exports.CommandInvocationError = void 0;
exports.getExtraOptions = exports.getCodeQLForTesting = exports.getCachedCodeQL = exports.setCodeQL = exports.getCodeQL = exports.convertToSemVer = exports.getCodeQLURLVersion = exports.setupCodeQL = exports.getCodeQLActionRepository = exports.CODEQL_VERSION_BETTER_RESOLVE_LANGUAGES = exports.CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS = exports.CODEQL_VERSION_NEW_TRACING = exports.CODEQL_VERSION_GHES_PACK_DOWNLOAD = exports.CODEQL_VERSION_CONFIG_FILES = exports.CODEQL_VERSION_COUNTS_LINES = exports.CODEQL_DEFAULT_ACTION_REPOSITORY = exports.CommandInvocationError = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const toolrunner = __importStar(require("@actions/exec/lib/toolrunner"));
@ -79,7 +79,6 @@ const CODEQL_VERSION_GROUP_RULES = "2.5.5";
const CODEQL_VERSION_SARIF_GROUP = "2.5.3";
exports.CODEQL_VERSION_COUNTS_LINES = "2.6.2";
const CODEQL_VERSION_CUSTOM_QUERY_HELP = "2.7.1";
exports.CODEQL_VERSION_ML_POWERED_QUERIES = "2.7.5";
const CODEQL_VERSION_LUA_TRACER_CONFIG = "2.10.0";
exports.CODEQL_VERSION_CONFIG_FILES = "2.10.1";
const CODEQL_VERSION_LUA_TRACING_GO_WINDOWS_FIXED = "2.10.4";

File diff suppressed because one or more lines are too long

51
lib/codeql.test.js generated
View file

@ -342,35 +342,28 @@ for (const [isFeatureFlagEnabled, toolsInput, shouldToolcacheBeBypassed,] of TOO
// Test macro for ensuring different variants of injected augmented configurations
const injectedConfigMacro = ava_1.default.macro({
exec: async (t, augmentationProperties, configOverride, expectedConfig) => {
const origCODEQL_PASS_CONFIG_TO_CLI = process.env.CODEQL_PASS_CONFIG_TO_CLI;
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = "true";
try {
await util.withTmpDir(async (tempDir) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon
.stub(codeqlObject, "getVersion")
.resolves(codeql.CODEQL_VERSION_CONFIG_FILES);
const thisStubConfig = {
...stubConfig,
...configOverride,
tempDir,
augmentationProperties,
};
await codeqlObject.databaseInitCluster(thisStubConfig, "", undefined, undefined, (0, feature_flags_1.createFeatureFlags)([]), (0, logging_1.getRunnerLogger)(true));
const args = runnerConstructorStub.firstCall.args[1];
// should have used an config file
const configArg = args.find((arg) => arg.startsWith("--codescanning-config="));
t.truthy(configArg, "Should have injected a codescanning config");
const configFile = configArg.split("=")[1];
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
t.deepEqual(augmentedConfig, expectedConfig);
await (0, del_1.default)(configFile, { force: true });
});
}
finally {
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = origCODEQL_PASS_CONFIG_TO_CLI;
}
await util.withTmpDir(async (tempDir) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon
.stub(codeqlObject, "getVersion")
.resolves(codeql.CODEQL_VERSION_CONFIG_FILES);
const thisStubConfig = {
...stubConfig,
...configOverride,
tempDir,
augmentationProperties,
};
await codeqlObject.databaseInitCluster(thisStubConfig, "", undefined, undefined, (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled]), (0, logging_1.getRunnerLogger)(true));
const args = runnerConstructorStub.firstCall.args[1];
// should have used an config file
const configArg = args.find((arg) => arg.startsWith("--codescanning-config="));
t.truthy(configArg, "Should have injected a codescanning config");
const configFile = configArg.split("=")[1];
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
t.deepEqual(augmentedConfig, expectedConfig);
await (0, del_1.default)(configFile, { force: true });
});
},
title: (providedTitle = "") => `databaseInitCluster() injected config: ${providedTitle}`,
});

File diff suppressed because one or more lines are too long

3
lib/config-utils.js generated
View file

@ -151,8 +151,7 @@ async function addBuiltinSuiteQueries(languages, codeQL, resultMap, packs, suite
languages.includes("javascript") &&
(found === "security-extended" || found === "security-and-quality") &&
!((_a = packs.javascript) === null || _a === void 0 ? void 0 : _a.some(isMlPoweredJsQueriesPack)) &&
(await featureFlags.getValue(feature_flags_1.FeatureFlag.MlPoweredQueriesEnabled)) &&
(await (0, util_1.codeQlVersionAbove)(codeQL, codeql_1.CODEQL_VERSION_ML_POWERED_QUERIES))) {
(await featureFlags.getValue(feature_flags_1.FeatureFlag.MlPoweredQueriesEnabled, codeQL))) {
if (!packs.javascript) {
packs.javascript = [];
}

File diff suppressed because one or more lines are too long

43
lib/feature-flags.js generated
View file

@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFeatureFlags = exports.GitHubFeatureFlags = exports.FeatureFlag = void 0;
exports.createFeatureFlags = exports.GitHubFeatureFlags = exports.featureFlagConfig = exports.FeatureFlag = void 0;
const api_client_1 = require("./api-client");
const util = __importStar(require("./util"));
var FeatureFlag;
@ -30,6 +30,28 @@ var FeatureFlag;
FeatureFlag["GolangExtractionReconciliationEnabled"] = "golang_extraction_reconciliation_enabled";
FeatureFlag["CliConfigFileEnabled"] = "cli_config_file_enabled";
})(FeatureFlag = exports.FeatureFlag || (exports.FeatureFlag = {}));
exports.featureFlagConfig = {
[FeatureFlag.BypassToolcacheEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE",
minimumVersion: undefined,
},
[FeatureFlag.MlPoweredQueriesEnabled]: {
envVar: "CODEQL_VERSION_ML_POWERED_QUERIES",
minimumVersion: "2.7.5",
},
[FeatureFlag.TrapCachingEnabled]: {
envVar: "CODEQL_TRAP_CACHING",
minimumVersion: undefined,
},
[FeatureFlag.GolangExtractionReconciliationEnabled]: {
envVar: "CODEQL_GOLANG_EXTRACTION_RECONCILIATION",
minimumVersion: undefined,
},
[FeatureFlag.CliConfigFileEnabled]: {
envVar: "CODEQL_PASS_CONFIG_TO_CLI",
minimumVersion: "2.10.1",
},
};
class GitHubFeatureFlags {
constructor(gitHubVersion, apiDetails, repositoryNwo, logger) {
this.gitHubVersion = gitHubVersion;
@ -37,11 +59,28 @@ class GitHubFeatureFlags {
this.repositoryNwo = repositoryNwo;
this.logger = logger;
}
async getValue(flag) {
async getValue(flag, codeql) {
// Bypassing the toolcache is disabled in test mode.
if (flag === FeatureFlag.BypassToolcacheEnabled && util.isInTestMode()) {
return false;
}
const envVar = (process.env[exports.featureFlagConfig[flag].envVar] || "").toLocaleLowerCase();
// Do not use this feature if user explicitly disables it via an environment variable.
if (envVar === "false") {
return false;
}
// Never use this feature if the CLI version explicitly can't support it.
const minimumVersion = exports.featureFlagConfig[flag].minimumVersion;
if (codeql && minimumVersion) {
if (!(await util.codeQlVersionAbove(codeql, minimumVersion))) {
return false;
}
}
// Use this feature if user explicitly enables it via an environment variable.
if (envVar === "true") {
return true;
}
// Ask the GitHub API if the feature is enabled.
const response = await this.getApiResponse();
if (response === undefined) {
this.logger.debug(`No feature flags API response for ${flag}, considering it disabled.`);

View file

@ -1 +1 @@
{"version":3,"file":"feature-flags.js","sourceRoot":"","sources":["../src/feature-flags.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"feature-flags.js","sourceRoot":"","sources":["../src/feature-flags.ts"],"names":[],"mappings":"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"}

View file

@ -29,9 +29,8 @@ const ALL_FEATURE_FLAGS_DISABLED_VARIANTS = [
for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
(0, ava_1.default)(`All feature flags are disabled if running against ${variant.description}`, async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
const loggedMessages = [];
const featureFlags = new feature_flags_1.GitHubFeatureFlags(variant.gitHubVersion, testApiDetails, testRepositoryNwo, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
const featureFlags = setUpTmpDir(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages), variant.gitHubVersion);
for (const flag of Object.values(feature_flags_1.FeatureFlag)) {
t.assert((await featureFlags.getValue(flag)) === false);
}
@ -43,14 +42,13 @@ for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
}
(0, ava_1.default)("API response missing", async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
const loggedMessages = [];
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, testRepositoryNwo, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
const featureFlags = setUpTmpDir(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(403, {});
for (const flag of Object.values(feature_flags_1.FeatureFlag)) {
t.assert((await featureFlags.getValue(flag)) === false);
}
for (const featureFlag of ["ml_powered_queries_enabled"]) {
for (const featureFlag of Object.keys(feature_flags_1.featureFlagConfig)) {
t.assert(loggedMessages.find((v) => v.type === "debug" &&
v.message ===
`No feature flags API response for ${featureFlag}, considering it disabled.`) !== undefined);
@ -59,14 +57,13 @@ for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
});
(0, ava_1.default)("Feature flags are disabled if they're not returned in API response", async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
const loggedMessages = [];
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, testRepositoryNwo, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
const featureFlags = setUpTmpDir(tmpDir, (0, testing_utils_1.getRecordingLogger)(loggedMessages));
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, {});
for (const flag of Object.values(feature_flags_1.FeatureFlag)) {
t.assert((await featureFlags.getValue(flag)) === false);
}
for (const featureFlag of ["ml_powered_queries_enabled"]) {
for (const featureFlag of Object.keys(feature_flags_1.featureFlagConfig)) {
t.assert(loggedMessages.find((v) => v.type === "debug" &&
v.message ===
`Feature flag '${featureFlag}' undefined in API response, considering it disabled.`) !== undefined);
@ -75,31 +72,97 @@ for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
});
(0, ava_1.default)("Feature flags exception is propagated if the API request errors", async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, testRepositoryNwo, (0, logging_1.getRunnerLogger)(true));
const featureFlags = setUpTmpDir(tmpDir);
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(500, {});
await t.throwsAsync(async () => featureFlags.getValue(feature_flags_1.FeatureFlag.MlPoweredQueriesEnabled), {
message: "Encountered an error while trying to load feature flags: Error: some error message",
});
});
});
const FEATURE_FLAGS = ["ml_powered_queries_enabled"];
for (const featureFlag of FEATURE_FLAGS) {
for (const featureFlag of Object.keys(feature_flags_1.featureFlagConfig)) {
(0, ava_1.default)(`Feature flag '${featureFlag}' is enabled if enabled in the API response`, async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
const featureFlags = new feature_flags_1.GitHubFeatureFlags({ type: util_1.GitHubVariant.DOTCOM }, testApiDetails, testRepositoryNwo, (0, logging_1.getRunnerLogger)(true));
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to false except the one we're testing
const expectedFeatureFlags = {};
for (const f of FEATURE_FLAGS) {
expectedFeatureFlags[f] = false;
for (const f of Object.keys(feature_flags_1.featureFlagConfig)) {
expectedFeatureFlags[f] = f === featureFlag;
}
expectedFeatureFlags[featureFlag] = true;
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, expectedFeatureFlags);
const actualFeatureFlags = {
ml_powered_queries_enabled: await featureFlags.getValue(feature_flags_1.FeatureFlag.MlPoweredQueriesEnabled),
};
// retrieve the values of the actual feature flags
const actualFeatureFlags = {};
for (const f of Object.keys(feature_flags_1.featureFlagConfig)) {
actualFeatureFlags[f] = await featureFlags.getValue(f);
}
// Alls flags should be false except the one we're testing
t.deepEqual(actualFeatureFlags, expectedFeatureFlags);
});
});
(0, ava_1.default)(`Feature flag '${featureFlag}' is enabled if the associated environment variable is true`, async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to false
const expectedFeatureFlags = {};
for (const f of Object.keys(feature_flags_1.featureFlagConfig)) {
expectedFeatureFlags[f] = false;
}
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, expectedFeatureFlags);
// feature flag should be disabled initially
t.assert(!(await featureFlags.getValue(featureFlag)));
// set env var to true and check that the feature flag is now enabled
process.env[feature_flags_1.featureFlagConfig[featureFlag].envVar] = "true";
t.assert(await featureFlags.getValue(featureFlag));
});
});
(0, ava_1.default)(`Feature flag '${featureFlag}' is disabled if the associated environment variable is false`, async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to true
const expectedFeatureFlags = {};
for (const f of Object.keys(feature_flags_1.featureFlagConfig)) {
expectedFeatureFlags[f] = true;
}
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, expectedFeatureFlags);
// feature flag should be enabled initially
t.assert(await featureFlags.getValue(featureFlag));
// set env var to false and check that the feature flag is now disabled
process.env[feature_flags_1.featureFlagConfig[featureFlag].envVar] = "false";
t.assert(!(await featureFlags.getValue(featureFlag)));
});
});
if (feature_flags_1.featureFlagConfig[featureFlag].minimumVersion !== undefined) {
(0, ava_1.default)(`Feature flag '${featureFlag}' is disabled if the minimum CLI version is below ${feature_flags_1.featureFlagConfig[featureFlag].minimumVersion}`, async (t) => {
await (0, util_1.withTmpDir)(async (tmpDir) => {
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to true
const expectedFeatureFlags = {};
for (const f of Object.keys(feature_flags_1.featureFlagConfig)) {
expectedFeatureFlags[f] = true;
}
(0, testing_utils_1.mockFeatureFlagApiEndpoint)(200, expectedFeatureFlags);
// feature flag should be enabled initially (ignoring the minimum CLI version)
t.assert(await featureFlags.getValue(featureFlag));
// feature flag should be disabled when an old CLI version is set
let codeql = (0, testing_utils_1.mockCodeQLVersion)("2.0.0");
t.assert(!(await featureFlags.getValue(featureFlag, codeql)));
// even setting the env var to true should not enable the feature flag if
// the minimum CLI version is not met
process.env[feature_flags_1.featureFlagConfig[featureFlag].envVar] = "true";
t.assert(!(await featureFlags.getValue(featureFlag, codeql)));
// feature flag should be enabled when a new CLI version is set
// and env var is not set
process.env[feature_flags_1.featureFlagConfig[featureFlag].envVar] = "";
codeql = (0, testing_utils_1.mockCodeQLVersion)(feature_flags_1.featureFlagConfig[featureFlag].minimumVersion);
t.assert(await featureFlags.getValue(featureFlag, codeql));
// set env var to false and check that the feature flag is now disabled
process.env[feature_flags_1.featureFlagConfig[featureFlag].envVar] = "false";
t.assert(!(await featureFlags.getValue(featureFlag, codeql)));
});
});
}
}
function setUpTmpDir(tmpDir, logger = (0, logging_1.getRunnerLogger)(true), gitHubVersion = { type: util_1.GitHubVariant.DOTCOM }) {
(0, testing_utils_1.setupActionsVars)(tmpDir, tmpDir);
return new feature_flags_1.GitHubFeatureFlags(gitHubVersion, testApiDetails, testRepositoryNwo, logger);
}
//# sourceMappingURL=feature-flags.test.js.map

File diff suppressed because one or more lines are too long

3
lib/init-action.js generated
View file

@ -160,8 +160,9 @@ async function run() {
}
async function getTrapCachingEnabled(featureFlags) {
const trapCaching = (0, actions_util_1.getOptionalInput)("trap-caching");
if (trapCaching !== undefined)
if (trapCaching !== undefined) {
return trapCaching === "true";
}
return await featureFlags.getValue(feature_flags_1.FeatureFlag.TrapCachingEnabled);
}
async function runWrapper() {

File diff suppressed because one or more lines are too long

10
lib/testing-utils.js generated
View file

@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mockFeatureFlagApiEndpoint = exports.getRecordingLogger = exports.setupActionsVars = exports.setupTests = void 0;
exports.mockCodeQLVersion = exports.mockFeatureFlagApiEndpoint = exports.getRecordingLogger = exports.setupActionsVars = exports.setupTests = void 0;
const github = __importStar(require("@actions/github"));
const sinon = __importStar(require("sinon"));
const apiClient = __importStar(require("./api-client"));
@ -144,4 +144,12 @@ function mockFeatureFlagApiEndpoint(responseStatusCode, response) {
sinon.stub(apiClient, "getApiClient").value(() => client);
}
exports.mockFeatureFlagApiEndpoint = mockFeatureFlagApiEndpoint;
function mockCodeQLVersion(version) {
return {
async getVersion() {
return version;
},
};
}
exports.mockCodeQLVersion = mockCodeQLVersion;
//# sourceMappingURL=testing-utils.js.map

View file

@ -1 +1 @@
{"version":3,"file":"testing-utils.js","sourceRoot":"","sources":["../src/testing-utils.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"testing-utils.js","sourceRoot":"","sources":["../src/testing-utils.ts"],"names":[],"mappings":"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"}

27
lib/util.js generated
View file

@ -424,12 +424,6 @@ var EnvVar;
* own sandwiched workflow mechanism
*/
EnvVar["FEATURE_SANDWICH"] = "CODEQL_ACTION_FEATURE_SANDWICH";
/**
* If set to the "true" string and the codeql CLI version is greater than
* `CODEQL_VERSION_CONFIG_FILES`, then the codeql-action will pass the
* the codeql-config file to the codeql CLI to be processed there.
*/
EnvVar["CODEQL_PASS_CONFIG_TO_CLI"] = "CODEQL_PASS_CONFIG_TO_CLI";
})(EnvVar = exports.EnvVar || (exports.EnvVar = {}));
const exportVar = (mode, name, value) => {
if (mode === Mode.actions) {
@ -491,9 +485,6 @@ function getRequiredEnvParam(paramName) {
return value;
}
exports.getRequiredEnvParam = getRequiredEnvParam;
function getOptionalEnvParam(paramName) {
return process.env[paramName] || "";
}
class HTTPError extends Error {
constructor(message, status) {
super(message);
@ -663,20 +654,7 @@ exports.isInTestMode = isInTestMode;
* that gets passed to the CLI.
*/
async function useCodeScanningConfigInCli(codeql, featureFlags) {
const envVarIsEnabled = getOptionalEnvParam(EnvVar.CODEQL_PASS_CONFIG_TO_CLI);
// If the user has explicitly turned off the feature, then don't use it.
if (envVarIsEnabled.toLocaleLowerCase() === "false") {
return false;
}
// If the user has explicitly turned on the feature, then use it.
// Or if the feature flag is enabled, then use it.
const isEnabled = envVarIsEnabled.toLocaleLowerCase() === "true" ||
(await featureFlags.getValue(feature_flags_1.FeatureFlag.CliConfigFileEnabled));
if (!isEnabled) {
return false;
}
// If the CLI version is too old, then don't use it.
return await codeQlVersionAbove(codeql, codeql_1.CODEQL_VERSION_CONFIG_FILES);
return await featureFlags.getValue(feature_flags_1.FeatureFlag.CliConfigFileEnabled, codeql);
}
exports.useCodeScanningConfigInCli = useCodeScanningConfigInCli;
async function logCodeScanningConfigInCli(codeql, featureFlags, logger) {
@ -722,8 +700,7 @@ function listFolder(dir) {
}
exports.listFolder = listFolder;
async function isGoExtractionReconciliationEnabled(featureFlags) {
return (process.env["CODEQL_ACTION_RECONCILE_GO_EXTRACTION"] === "true" ||
(await featureFlags.getValue(feature_flags_1.FeatureFlag.GolangExtractionReconciliationEnabled)));
return await featureFlags.getValue(feature_flags_1.FeatureFlag.GolangExtractionReconciliationEnabled);
}
exports.isGoExtractionReconciliationEnabled = isGoExtractionReconciliationEnabled;
/**

File diff suppressed because one or more lines are too long

35
lib/util.test.js generated
View file

@ -31,7 +31,6 @@ const github = __importStar(require("@actions/github"));
const ava_1 = __importDefault(require("ava"));
const sinon = __importStar(require("sinon"));
const api = __importStar(require("./api-client"));
const feature_flags_1 = require("./feature-flags");
const logging_1 = require("./logging");
const testing_utils_1 = require("./testing-utils");
const util = __importStar(require("./util"));
@ -362,40 +361,6 @@ for (const [version, githubVersion, shouldReportWarning,] of CHECK_ACTION_VERSIO
]);
});
});
(0, ava_1.default)("useCodeScanningConfigInCli with no env var", async (t) => {
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.0"), (0, feature_flags_1.createFeatureFlags)([]))));
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.1"), (0, feature_flags_1.createFeatureFlags)([]))));
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.0"), (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled]))));
// Yay! It works!
t.assert(await util.useCodeScanningConfigInCli(mockVersion("2.10.1"), (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled])));
});
for (const val of ["TRUE", "true", "True"]) {
(0, ava_1.default)(`useCodeScanningConfigInCli with env var ${val}`, async (t) => {
process.env[util.EnvVar.CODEQL_PASS_CONFIG_TO_CLI] = val;
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.0"), (0, feature_flags_1.createFeatureFlags)([]))));
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.0"), (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled]))));
// Yay! It works!
t.assert(await util.useCodeScanningConfigInCli(mockVersion("2.10.1"), (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled])));
t.assert(await util.useCodeScanningConfigInCli(mockVersion("2.10.1"), (0, feature_flags_1.createFeatureFlags)([])));
});
}
for (const val of ["FALSE", "false", "False"]) {
(0, ava_1.default)(`useCodeScanningConfigInCli with env var ${val}`, async (t) => {
// Never turned on when env var is false
process.env[util.EnvVar.CODEQL_PASS_CONFIG_TO_CLI] = val;
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.0"), (0, feature_flags_1.createFeatureFlags)([]))));
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.0"), (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled]))));
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.1"), (0, feature_flags_1.createFeatureFlags)([feature_flags_1.FeatureFlag.CliConfigFileEnabled]))));
t.assert(!(await util.useCodeScanningConfigInCli(mockVersion("2.10.1"), (0, feature_flags_1.createFeatureFlags)([]))));
});
}
function mockVersion(version) {
return {
async getVersion() {
return version;
},
};
}
const longTime = 999999;
const shortTime = 10;
(0, ava_1.default)("withTimeout on long task", async (t) => {

File diff suppressed because one or more lines are too long

View file

@ -505,47 +505,41 @@ const injectedConfigMacro = test.macro({
configOverride: Partial<Config>,
expectedConfig: any
) => {
const origCODEQL_PASS_CONFIG_TO_CLI = process.env.CODEQL_PASS_CONFIG_TO_CLI;
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = "true";
try {
await util.withTmpDir(async (tempDir) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon
.stub(codeqlObject, "getVersion")
.resolves(codeql.CODEQL_VERSION_CONFIG_FILES);
await util.withTmpDir(async (tempDir) => {
const runnerConstructorStub = stubToolRunnerConstructor();
const codeqlObject = await codeql.getCodeQLForTesting();
sinon
.stub(codeqlObject, "getVersion")
.resolves(codeql.CODEQL_VERSION_CONFIG_FILES);
const thisStubConfig: Config = {
...stubConfig,
...configOverride,
tempDir,
augmentationProperties,
};
const thisStubConfig: Config = {
...stubConfig,
...configOverride,
tempDir,
augmentationProperties,
};
await codeqlObject.databaseInitCluster(
thisStubConfig,
"",
undefined,
undefined,
createFeatureFlags([]),
getRunnerLogger(true)
);
await codeqlObject.databaseInitCluster(
thisStubConfig,
"",
undefined,
undefined,
createFeatureFlags([FeatureFlag.CliConfigFileEnabled]),
getRunnerLogger(true)
);
const args = runnerConstructorStub.firstCall.args[1];
// should have used an config file
const configArg = args.find((arg: string) =>
arg.startsWith("--codescanning-config=")
);
t.truthy(configArg, "Should have injected a codescanning config");
const configFile = configArg.split("=")[1];
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
t.deepEqual(augmentedConfig, expectedConfig);
const args = runnerConstructorStub.firstCall.args[1];
// should have used an config file
const configArg = args.find((arg: string) =>
arg.startsWith("--codescanning-config=")
);
t.truthy(configArg, "Should have injected a codescanning config");
const configFile = configArg.split("=")[1];
const augmentedConfig = yaml.load(fs.readFileSync(configFile, "utf8"));
t.deepEqual(augmentedConfig, expectedConfig);
await del(configFile, { force: true });
});
} finally {
process.env["CODEQL_PASS_CONFIG_TO_CLI"] = origCODEQL_PASS_CONFIG_TO_CLI;
}
await del(configFile, { force: true });
});
},
title: (providedTitle = "") =>

View file

@ -247,7 +247,6 @@ const CODEQL_VERSION_GROUP_RULES = "2.5.5";
const CODEQL_VERSION_SARIF_GROUP = "2.5.3";
export const CODEQL_VERSION_COUNTS_LINES = "2.6.2";
const CODEQL_VERSION_CUSTOM_QUERY_HELP = "2.7.1";
export const CODEQL_VERSION_ML_POWERED_QUERIES = "2.7.5";
const CODEQL_VERSION_LUA_TRACER_CONFIG = "2.10.0";
export const CODEQL_VERSION_CONFIG_FILES = "2.10.1";
const CODEQL_VERSION_LUA_TRACING_GO_WINDOWS_FIXED = "2.10.4";

View file

@ -10,7 +10,6 @@ import * as api from "./api-client";
import {
CodeQL,
CODEQL_VERSION_GHES_PACK_DOWNLOAD,
CODEQL_VERSION_ML_POWERED_QUERIES,
CODEQL_VERSION_ML_POWERED_QUERIES_WINDOWS,
ResolveQueriesOutput,
} from "./codeql";
@ -412,8 +411,7 @@ async function addBuiltinSuiteQueries(
languages.includes("javascript") &&
(found === "security-extended" || found === "security-and-quality") &&
!packs.javascript?.some(isMlPoweredJsQueriesPack) &&
(await featureFlags.getValue(FeatureFlag.MlPoweredQueriesEnabled)) &&
(await codeQlVersionAbove(codeQL, CODEQL_VERSION_ML_POWERED_QUERIES))
(await featureFlags.getValue(FeatureFlag.MlPoweredQueriesEnabled, codeQL))
) {
if (!packs.javascript) {
packs.javascript = [];

View file

@ -1,12 +1,18 @@
import test from "ava";
import { GitHubApiDetails } from "./api-client";
import { FeatureFlag, GitHubFeatureFlags } from "./feature-flags";
import {
FeatureFlag,
featureFlagConfig,
FeatureFlags,
GitHubFeatureFlags,
} from "./feature-flags";
import { getRunnerLogger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import {
getRecordingLogger,
LoggedMessage,
mockCodeQLVersion,
mockFeatureFlagApiEndpoint,
setupActionsVars,
setupTests,
@ -42,14 +48,11 @@ const ALL_FEATURE_FLAGS_DISABLED_VARIANTS: Array<{
for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
test(`All feature flags are disabled if running against ${variant.description}`, async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const loggedMessages = [];
const featureFlags = new GitHubFeatureFlags(
variant.gitHubVersion,
testApiDetails,
testRepositoryNwo,
getRecordingLogger(loggedMessages)
const featureFlags = setUpTmpDir(
tmpDir,
getRecordingLogger(loggedMessages),
variant.gitHubVersion
);
for (const flag of Object.values(FeatureFlag)) {
@ -70,13 +73,9 @@ for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
test("API response missing", async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const loggedMessages = [];
const featureFlags = new GitHubFeatureFlags(
{ type: GitHubVariant.DOTCOM },
testApiDetails,
testRepositoryNwo,
const featureFlags = setUpTmpDir(
tmpDir,
getRecordingLogger(loggedMessages)
);
@ -86,7 +85,7 @@ test("API response missing", async (t) => {
t.assert((await featureFlags.getValue(flag)) === false);
}
for (const featureFlag of ["ml_powered_queries_enabled"]) {
for (const featureFlag of Object.keys(featureFlagConfig)) {
t.assert(
loggedMessages.find(
(v: LoggedMessage) =>
@ -101,13 +100,9 @@ test("API response missing", async (t) => {
test("Feature flags are disabled if they're not returned in API response", async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const loggedMessages = [];
const featureFlags = new GitHubFeatureFlags(
{ type: GitHubVariant.DOTCOM },
testApiDetails,
testRepositoryNwo,
const featureFlags = setUpTmpDir(
tmpDir,
getRecordingLogger(loggedMessages)
);
@ -117,7 +112,7 @@ test("Feature flags are disabled if they're not returned in API response", async
t.assert((await featureFlags.getValue(flag)) === false);
}
for (const featureFlag of ["ml_powered_queries_enabled"]) {
for (const featureFlag of Object.keys(featureFlagConfig)) {
t.assert(
loggedMessages.find(
(v: LoggedMessage) =>
@ -132,14 +127,7 @@ test("Feature flags are disabled if they're not returned in API response", async
test("Feature flags exception is propagated if the API request errors", async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const featureFlags = new GitHubFeatureFlags(
{ type: GitHubVariant.DOTCOM },
testApiDetails,
testRepositoryNwo,
getRunnerLogger(true)
);
const featureFlags = setUpTmpDir(tmpDir);
mockFeatureFlagApiEndpoint(500, {});
@ -153,34 +141,128 @@ test("Feature flags exception is propagated if the API request errors", async (t
});
});
const FEATURE_FLAGS = ["ml_powered_queries_enabled"];
for (const featureFlag of FEATURE_FLAGS) {
for (const featureFlag of Object.keys(featureFlagConfig)) {
test(`Feature flag '${featureFlag}' is enabled if enabled in the API response`, async (t) => {
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const featureFlags = new GitHubFeatureFlags(
{ type: GitHubVariant.DOTCOM },
testApiDetails,
testRepositoryNwo,
getRunnerLogger(true)
);
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to false except the one we're testing
const expectedFeatureFlags: { [flag: string]: boolean } = {};
for (const f of FEATURE_FLAGS) {
expectedFeatureFlags[f] = false;
for (const f of Object.keys(featureFlagConfig)) {
expectedFeatureFlags[f] = f === featureFlag;
}
expectedFeatureFlags[featureFlag] = true;
mockFeatureFlagApiEndpoint(200, expectedFeatureFlags);
const actualFeatureFlags: { [flag: string]: boolean } = {
ml_powered_queries_enabled: await featureFlags.getValue(
FeatureFlag.MlPoweredQueriesEnabled
),
};
// retrieve the values of the actual feature flags
const actualFeatureFlags: { [flag: string]: boolean } = {};
for (const f of Object.keys(featureFlagConfig)) {
actualFeatureFlags[f] = await featureFlags.getValue(f as FeatureFlag);
}
// Alls flags should be false except the one we're testing
t.deepEqual(actualFeatureFlags, expectedFeatureFlags);
});
});
test(`Feature flag '${featureFlag}' is enabled if the associated environment variable is true`, async (t) => {
await withTmpDir(async (tmpDir) => {
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to false
const expectedFeatureFlags: { [flag: string]: boolean } = {};
for (const f of Object.keys(featureFlagConfig)) {
expectedFeatureFlags[f] = false;
}
mockFeatureFlagApiEndpoint(200, expectedFeatureFlags);
// feature flag should be disabled initially
t.assert(!(await featureFlags.getValue(featureFlag as FeatureFlag)));
// set env var to true and check that the feature flag is now enabled
process.env[featureFlagConfig[featureFlag].envVar] = "true";
t.assert(await featureFlags.getValue(featureFlag as FeatureFlag));
});
});
test(`Feature flag '${featureFlag}' is disabled if the associated environment variable is false`, async (t) => {
await withTmpDir(async (tmpDir) => {
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to true
const expectedFeatureFlags: { [flag: string]: boolean } = {};
for (const f of Object.keys(featureFlagConfig)) {
expectedFeatureFlags[f] = true;
}
mockFeatureFlagApiEndpoint(200, expectedFeatureFlags);
// feature flag should be enabled initially
t.assert(await featureFlags.getValue(featureFlag as FeatureFlag));
// set env var to false and check that the feature flag is now disabled
process.env[featureFlagConfig[featureFlag].envVar] = "false";
t.assert(!(await featureFlags.getValue(featureFlag as FeatureFlag)));
});
});
if (featureFlagConfig[featureFlag].minimumVersion !== undefined) {
test(`Feature flag '${featureFlag}' is disabled if the minimum CLI version is below ${featureFlagConfig[featureFlag].minimumVersion}`, async (t) => {
await withTmpDir(async (tmpDir) => {
const featureFlags = setUpTmpDir(tmpDir);
// set all feature flags to true
const expectedFeatureFlags: { [flag: string]: boolean } = {};
for (const f of Object.keys(featureFlagConfig)) {
expectedFeatureFlags[f] = true;
}
mockFeatureFlagApiEndpoint(200, expectedFeatureFlags);
// feature flag should be enabled initially (ignoring the minimum CLI version)
t.assert(await featureFlags.getValue(featureFlag as FeatureFlag));
// feature flag should be disabled when an old CLI version is set
let codeql = mockCodeQLVersion("2.0.0");
t.assert(
!(await featureFlags.getValue(featureFlag as FeatureFlag, codeql))
);
// even setting the env var to true should not enable the feature flag if
// the minimum CLI version is not met
process.env[featureFlagConfig[featureFlag].envVar] = "true";
t.assert(
!(await featureFlags.getValue(featureFlag as FeatureFlag, codeql))
);
// feature flag should be enabled when a new CLI version is set
// and env var is not set
process.env[featureFlagConfig[featureFlag].envVar] = "";
codeql = mockCodeQLVersion(
featureFlagConfig[featureFlag].minimumVersion
);
t.assert(
await featureFlags.getValue(featureFlag as FeatureFlag, codeql)
);
// set env var to false and check that the feature flag is now disabled
process.env[featureFlagConfig[featureFlag].envVar] = "false";
t.assert(
!(await featureFlags.getValue(featureFlag as FeatureFlag, codeql))
);
});
});
}
}
function setUpTmpDir(
tmpDir: string,
logger = getRunnerLogger(true),
gitHubVersion = { type: GitHubVariant.DOTCOM } as util.GitHubVersion
): FeatureFlags {
setupActionsVars(tmpDir, tmpDir);
return new GitHubFeatureFlags(
gitHubVersion,
testApiDetails,
testRepositoryNwo,
logger
);
}

View file

@ -1,10 +1,11 @@
import { getApiClient, GitHubApiDetails } from "./api-client";
import { CodeQL } from "./codeql";
import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
import * as util from "./util";
export interface FeatureFlags {
getValue(flag: FeatureFlag): Promise<boolean>;
getValue(flag: FeatureFlag, codeql?: CodeQL): Promise<boolean>;
}
export enum FeatureFlag {
@ -15,6 +16,32 @@ export enum FeatureFlag {
CliConfigFileEnabled = "cli_config_file_enabled",
}
export const featureFlagConfig: Record<
FeatureFlag,
{ envVar: string; minimumVersion: string | undefined }
> = {
[FeatureFlag.BypassToolcacheEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE",
minimumVersion: undefined,
},
[FeatureFlag.MlPoweredQueriesEnabled]: {
envVar: "CODEQL_VERSION_ML_POWERED_QUERIES",
minimumVersion: "2.7.5",
},
[FeatureFlag.TrapCachingEnabled]: {
envVar: "CODEQL_TRAP_CACHING",
minimumVersion: undefined,
},
[FeatureFlag.GolangExtractionReconciliationEnabled]: {
envVar: "CODEQL_GOLANG_EXTRACTION_RECONCILIATION",
minimumVersion: undefined,
},
[FeatureFlag.CliConfigFileEnabled]: {
envVar: "CODEQL_PASS_CONFIG_TO_CLI",
minimumVersion: "2.10.1",
},
};
/**
* A response from the GitHub API that contains feature flag enablement information for the CodeQL
* Action.
@ -33,12 +60,35 @@ export class GitHubFeatureFlags implements FeatureFlags {
private logger: Logger
) {}
async getValue(flag: FeatureFlag): Promise<boolean> {
async getValue(flag: FeatureFlag, codeql?: CodeQL): Promise<boolean> {
// Bypassing the toolcache is disabled in test mode.
if (flag === FeatureFlag.BypassToolcacheEnabled && util.isInTestMode()) {
return false;
}
const envVar = (
process.env[featureFlagConfig[flag].envVar] || ""
).toLocaleLowerCase();
// Do not use this feature if user explicitly disables it via an environment variable.
if (envVar === "false") {
return false;
}
// Never use this feature if the CLI version explicitly can't support it.
const minimumVersion = featureFlagConfig[flag].minimumVersion;
if (codeql && minimumVersion) {
if (!(await util.codeQlVersionAbove(codeql, minimumVersion))) {
return false;
}
}
// Use this feature if user explicitly enables it via an environment variable.
if (envVar === "true") {
return true;
}
// Ask the GitHub API if the feature is enabled.
const response = await this.getApiResponse();
if (response === undefined) {
this.logger.debug(

View file

@ -321,7 +321,9 @@ async function getTrapCachingEnabled(
featureFlags: FeatureFlags
): Promise<boolean> {
const trapCaching = getOptionalInput("trap-caching");
if (trapCaching !== undefined) return trapCaching === "true";
if (trapCaching !== undefined) {
return trapCaching === "true";
}
return await featureFlags.getValue(FeatureFlag.TrapCachingEnabled);
}

View file

@ -160,3 +160,11 @@ export function mockFeatureFlagApiEndpoint(
sinon.stub(apiClient, "getApiClient").value(() => client);
}
export function mockCodeQLVersion(version) {
return {
async getVersion() {
return version;
},
} as CodeQL.CodeQL;
}

View file

@ -9,9 +9,7 @@ import test, { ExecutionContext } from "ava";
import * as sinon from "sinon";
import * as api from "./api-client";
import { CodeQL } from "./codeql";
import { Config } from "./config-utils";
import { createFeatureFlags, FeatureFlag } from "./feature-flags";
import { getRunnerLogger, Logger } from "./logging";
import { setupTests } from "./testing-utils";
import * as util from "./util";
@ -495,113 +493,6 @@ test("listFolder", async (t) => {
});
});
test("useCodeScanningConfigInCli with no env var", async (t) => {
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.0"),
createFeatureFlags([])
))
);
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.1"),
createFeatureFlags([])
))
);
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.0"),
createFeatureFlags([FeatureFlag.CliConfigFileEnabled])
))
);
// Yay! It works!
t.assert(
await util.useCodeScanningConfigInCli(
mockVersion("2.10.1"),
createFeatureFlags([FeatureFlag.CliConfigFileEnabled])
)
);
});
for (const val of ["TRUE", "true", "True"]) {
test(`useCodeScanningConfigInCli with env var ${val}`, async (t) => {
process.env[util.EnvVar.CODEQL_PASS_CONFIG_TO_CLI] = val;
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.0"),
createFeatureFlags([])
))
);
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.0"),
createFeatureFlags([FeatureFlag.CliConfigFileEnabled])
))
);
// Yay! It works!
t.assert(
await util.useCodeScanningConfigInCli(
mockVersion("2.10.1"),
createFeatureFlags([FeatureFlag.CliConfigFileEnabled])
)
);
t.assert(
await util.useCodeScanningConfigInCli(
mockVersion("2.10.1"),
createFeatureFlags([])
)
);
});
}
for (const val of ["FALSE", "false", "False"]) {
test(`useCodeScanningConfigInCli with env var ${val}`, async (t) => {
// Never turned on when env var is false
process.env[util.EnvVar.CODEQL_PASS_CONFIG_TO_CLI] = val;
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.0"),
createFeatureFlags([])
))
);
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.0"),
createFeatureFlags([FeatureFlag.CliConfigFileEnabled])
))
);
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.1"),
createFeatureFlags([FeatureFlag.CliConfigFileEnabled])
))
);
t.assert(
!(await util.useCodeScanningConfigInCli(
mockVersion("2.10.1"),
createFeatureFlags([])
))
);
});
}
function mockVersion(version) {
return {
async getVersion() {
return version;
},
} as CodeQL;
}
const longTime = 999_999;
const shortTime = 10;

View file

@ -12,11 +12,7 @@ import * as semver from "semver";
import * as api from "./api-client";
import { getApiClient, GitHubApiDetails } from "./api-client";
import * as apiCompatibility from "./api-compatibility.json";
import {
CodeQL,
CODEQL_VERSION_CONFIG_FILES,
CODEQL_VERSION_NEW_TRACING,
} from "./codeql";
import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql";
import {
Config,
parsePacksSpecification,
@ -524,13 +520,6 @@ export enum EnvVar {
* own sandwiched workflow mechanism
*/
FEATURE_SANDWICH = "CODEQL_ACTION_FEATURE_SANDWICH",
/**
* If set to the "true" string and the codeql CLI version is greater than
* `CODEQL_VERSION_CONFIG_FILES`, then the codeql-action will pass the
* the codeql-config file to the codeql CLI to be processed there.
*/
CODEQL_PASS_CONFIG_TO_CLI = "CODEQL_PASS_CONFIG_TO_CLI",
}
const exportVar = (mode: Mode, name: string, value: string) => {
@ -593,10 +582,6 @@ export function getRequiredEnvParam(paramName: string): string {
return value;
}
function getOptionalEnvParam(paramName: string): string {
return process.env[paramName] || "";
}
export class HTTPError extends Error {
public status: number;
@ -796,25 +781,7 @@ export async function useCodeScanningConfigInCli(
codeql: CodeQL,
featureFlags: FeatureFlags
): Promise<boolean> {
const envVarIsEnabled = getOptionalEnvParam(EnvVar.CODEQL_PASS_CONFIG_TO_CLI);
// If the user has explicitly turned off the feature, then don't use it.
if (envVarIsEnabled.toLocaleLowerCase() === "false") {
return false;
}
// If the user has explicitly turned on the feature, then use it.
// Or if the feature flag is enabled, then use it.
const isEnabled =
envVarIsEnabled.toLocaleLowerCase() === "true" ||
(await featureFlags.getValue(FeatureFlag.CliConfigFileEnabled));
if (!isEnabled) {
return false;
}
// If the CLI version is too old, then don't use it.
return await codeQlVersionAbove(codeql, CODEQL_VERSION_CONFIG_FILES);
return await featureFlags.getValue(FeatureFlag.CliConfigFileEnabled, codeql);
}
export async function logCodeScanningConfigInCli(
@ -867,11 +834,8 @@ export function listFolder(dir: string): string[] {
export async function isGoExtractionReconciliationEnabled(
featureFlags: FeatureFlags
): Promise<boolean> {
return (
process.env["CODEQL_ACTION_RECONCILE_GO_EXTRACTION"] === "true" ||
(await featureFlags.getValue(
FeatureFlag.GolangExtractionReconciliationEnabled
))
return await featureFlags.getValue(
FeatureFlag.GolangExtractionReconciliationEnabled
);
}