Rename FeatureFlag -> Feature

This commit is contained in:
Andrew Eisenberg 2022-10-06 12:31:08 -07:00
parent b16314e16c
commit 6de05e4b24
24 changed files with 116 additions and 124 deletions

View file

@ -13,7 +13,7 @@ import { GitHubApiDetails } from "./api-client";
import * as codeql from "./codeql";
import { AugmentationProperties, Config } from "./config-utils";
import * as defaults from "./defaults.json";
import { createFeatureFlags, FeatureFlag, FeatureFlags } from "./feature-flags";
import { createFeatureFlags, Feature, FeatureFlags } from "./feature-flags";
import { Language } from "./languages";
import { getRunnerLogger } from "./logging";
import { setupTests, setupActionsVars } from "./testing-utils";
@ -281,7 +281,7 @@ for (const [
version: defaults.bundleVersion,
apiDetails: sampleApiDetails,
featureFlags: createFeatureFlags(
isFeatureFlagEnabled ? [FeatureFlag.BypassToolcacheEnabled] : []
isFeatureFlagEnabled ? [Feature.BypassToolcacheEnabled] : []
),
toolsInput: { input: toolsInput },
tmpDir,
@ -524,7 +524,7 @@ const injectedConfigMacro = test.macro({
"",
undefined,
undefined,
createFeatureFlags([FeatureFlag.CliConfigFileEnabled]),
createFeatureFlags([Feature.CliConfigFileEnabled]),
getRunnerLogger(true)
);

View file

@ -15,7 +15,7 @@ import * as api from "./api-client";
import { Config } from "./config-utils";
import * as defaults from "./defaults.json"; // Referenced from codeql-action-sync-tool!
import { errorMatchers } from "./error-matcher";
import { FeatureFlag, FeatureFlags } from "./feature-flags";
import { Feature, FeatureFlags } from "./feature-flags";
import { isTracedLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { toolrunnerErrorCatcher } from "./toolrunner-error-catcher";
@ -442,7 +442,7 @@ export async function setupCodeQL(
// allows us to quickly rollback a broken bundle that has made its way
// into the toolcache.
codeqlURL === undefined &&
(await featureFlags.getValue(FeatureFlag.BypassToolcacheEnabled))
(await featureFlags.getValue(Feature.BypassToolcacheEnabled))
? "a specific version of CodeQL was not requested and the bypass toolcache feature flag is enabled"
: undefined;
const forceLatest = forceLatestReason !== undefined;

View file

@ -9,7 +9,7 @@ import * as sinon from "sinon";
import * as api from "./api-client";
import { getCachedCodeQL, PackDownloadOutput, setCodeQL } from "./codeql";
import * as configUtils from "./config-utils";
import { createFeatureFlags, FeatureFlag } from "./feature-flags";
import { createFeatureFlags, Feature } from "./feature-flags";
import { Language } from "./languages";
import { getRunnerLogger, Logger } from "./logging";
import { setupTests } from "./testing-utils";
@ -1936,9 +1936,7 @@ const mlPoweredQueriesMacro = test.macro({
gitHubVersion,
sampleApiDetails,
createFeatureFlags(
isMlPoweredQueriesFlagEnabled
? [FeatureFlag.MlPoweredQueriesEnabled]
: []
isMlPoweredQueriesFlagEnabled ? [Feature.MlPoweredQueriesEnabled] : []
),
getRunnerLogger(true)
);

View file

@ -14,7 +14,7 @@ import {
ResolveQueriesOutput,
} from "./codeql";
import * as externalQueries from "./external-queries";
import { FeatureFlag, FeatureFlags } from "./feature-flags";
import { Feature, FeatureFlags } from "./feature-flags";
import { Language, parseLanguage } from "./languages";
import { Logger } from "./logging";
import { RepositoryNwo } from "./repository";
@ -411,7 +411,7 @@ async function addBuiltinSuiteQueries(
languages.includes("javascript") &&
(found === "security-extended" || found === "security-and-quality") &&
!packs.javascript?.some(isMlPoweredJsQueriesPack) &&
(await featureFlags.getValue(FeatureFlag.MlPoweredQueriesEnabled, codeQL))
(await featureFlags.getValue(Feature.MlPoweredQueriesEnabled, codeQL))
) {
if (!packs.javascript) {
packs.javascript = [];

View file

@ -2,8 +2,8 @@ import test from "ava";
import { GitHubApiDetails } from "./api-client";
import {
FeatureFlag,
featureFlagConfig,
Feature,
featureConfig,
FeatureFlags,
GitHubFeatureFlags,
} from "./feature-flags";
@ -55,7 +55,7 @@ for (const variant of ALL_FEATURE_FLAGS_DISABLED_VARIANTS) {
variant.gitHubVersion
);
for (const flag of Object.values(FeatureFlag)) {
for (const flag of Object.values(Feature)) {
t.assert(
(await featureFlags.getValue(flag, includeCodeQlIfRequired(flag))) ===
false
@ -81,7 +81,7 @@ test("API response missing", async (t) => {
mockFeatureFlagApiEndpoint(403, {});
for (const flag of Object.values(FeatureFlag)) {
for (const flag of Object.values(Feature)) {
t.assert(
(await featureFlags.getValue(flag, includeCodeQlIfRequired(flag))) ===
false
@ -98,7 +98,7 @@ test("Feature flags are disabled if they're not returned in API response", async
mockFeatureFlagApiEndpoint(200, {});
for (const flag of Object.values(FeatureFlag)) {
for (const flag of Object.values(Feature)) {
t.assert(
(await featureFlags.getValue(flag, includeCodeQlIfRequired(flag))) ===
false
@ -118,8 +118,8 @@ test("Feature flags exception is propagated if the API request errors", async (t
await t.throwsAsync(
async () =>
featureFlags.getValue(
FeatureFlag.MlPoweredQueriesEnabled,
includeCodeQlIfRequired(FeatureFlag.MlPoweredQueriesEnabled)
Feature.MlPoweredQueriesEnabled,
includeCodeQlIfRequired(Feature.MlPoweredQueriesEnabled)
),
{
message:
@ -129,23 +129,23 @@ test("Feature flags exception is propagated if the API request errors", async (t
});
});
for (const featureFlag of Object.keys(featureFlagConfig)) {
for (const featureFlag of Object.keys(featureConfig)) {
test(`Only feature flag '${featureFlag}' is enabled if enabled in the API response. Other flags disabled`, async (t) => {
await withTmpDir(async (tmpDir) => {
const featureFlags = setUpTests(tmpDir);
// set all feature flags to false except the one we're testing
const expectedFeatureFlags: { [flag: string]: boolean } = {};
for (const f of Object.keys(featureFlagConfig)) {
for (const f of Object.keys(featureConfig)) {
expectedFeatureFlags[f] = f === featureFlag;
}
mockFeatureFlagApiEndpoint(200, expectedFeatureFlags);
// retrieve the values of the actual feature flags
const actualFeatureFlags: { [flag: string]: boolean } = {};
for (const f of Object.keys(featureFlagConfig)) {
for (const f of Object.keys(featureConfig)) {
actualFeatureFlags[f] = await featureFlags.getValue(
f as FeatureFlag,
f as Feature,
includeCodeQlIfRequired(f)
);
}
@ -165,16 +165,16 @@ for (const featureFlag of Object.keys(featureFlagConfig)) {
// feature flag should be disabled initially
t.assert(
!(await featureFlags.getValue(
featureFlag as FeatureFlag,
featureFlag as Feature,
includeCodeQlIfRequired(featureFlag)
))
);
// set env var to true and check that the feature flag is now enabled
process.env[featureFlagConfig[featureFlag].envVar] = "true";
process.env[featureConfig[featureFlag].envVar] = "true";
t.assert(
await featureFlags.getValue(
featureFlag as FeatureFlag,
featureFlag as Feature,
includeCodeQlIfRequired(featureFlag)
)
);
@ -191,23 +191,23 @@ for (const featureFlag of Object.keys(featureFlagConfig)) {
// feature flag should be enabled initially
t.assert(
await featureFlags.getValue(
featureFlag as FeatureFlag,
featureFlag as Feature,
includeCodeQlIfRequired(featureFlag)
)
);
// set env var to false and check that the feature flag is now disabled
process.env[featureFlagConfig[featureFlag].envVar] = "false";
process.env[featureConfig[featureFlag].envVar] = "false";
t.assert(
!(await featureFlags.getValue(
featureFlag as FeatureFlag,
featureFlag as Feature,
includeCodeQlIfRequired(featureFlag)
))
);
});
});
if (featureFlagConfig[featureFlag].minimumVersion !== undefined) {
if (featureConfig[featureFlag].minimumVersion !== undefined) {
test(`Getting Feature Flag '${featureFlag} should throw if no codeql is provided`, async (t) => {
await withTmpDir(async (tmpDir) => {
const featureFlags = setUpTests(tmpDir);
@ -216,7 +216,7 @@ for (const featureFlag of Object.keys(featureFlagConfig)) {
mockFeatureFlagApiEndpoint(200, expectedFeatureFlags);
await t.throwsAsync(
async () => featureFlags.getValue(featureFlag as FeatureFlag),
async () => featureFlags.getValue(featureFlag as Feature),
{
message: `A minimum version is specified for feature flag ${featureFlag}, but no instance of CodeQL was provided.`,
}
@ -225,8 +225,8 @@ for (const featureFlag of Object.keys(featureFlagConfig)) {
});
}
if (featureFlagConfig[featureFlag].minimumVersion !== undefined) {
test(`Feature flag '${featureFlag}' is disabled if the minimum CLI version is below ${featureFlagConfig[featureFlag].minimumVersion}`, async (t) => {
if (featureConfig[featureFlag].minimumVersion !== undefined) {
test(`Feature flag '${featureFlag}' is disabled if the minimum CLI version is below ${featureConfig[featureFlag].minimumVersion}`, async (t) => {
await withTmpDir(async (tmpDir) => {
const featureFlags = setUpTests(tmpDir);
@ -236,30 +236,26 @@ for (const featureFlag of Object.keys(featureFlagConfig)) {
// 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))
!(await featureFlags.getValue(featureFlag as Feature, 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";
process.env[featureConfig[featureFlag].envVar] = "true";
t.assert(
!(await featureFlags.getValue(featureFlag as FeatureFlag, codeql))
!(await featureFlags.getValue(featureFlag as Feature, 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)
);
process.env[featureConfig[featureFlag].envVar] = "";
codeql = mockCodeQLVersion(featureConfig[featureFlag].minimumVersion);
t.assert(await featureFlags.getValue(featureFlag as Feature, codeql));
// set env var to false and check that the feature flag is now disabled
process.env[featureFlagConfig[featureFlag].envVar] = "false";
process.env[featureConfig[featureFlag].envVar] = "false";
t.assert(
!(await featureFlags.getValue(featureFlag as FeatureFlag, codeql))
!(await featureFlags.getValue(featureFlag as Feature, codeql))
);
});
});
@ -267,7 +263,7 @@ for (const featureFlag of Object.keys(featureFlagConfig)) {
}
function assertAllFeaturesUndefinedInApi(t, loggedMessages: LoggedMessage[]) {
for (const featureFlag of Object.keys(featureFlagConfig)) {
for (const featureFlag of Object.keys(featureConfig)) {
t.assert(
loggedMessages.find(
(v) =>
@ -280,7 +276,7 @@ function assertAllFeaturesUndefinedInApi(t, loggedMessages: LoggedMessage[]) {
}
function initializeFeatures(initialValue: boolean) {
return Object.keys(featureFlagConfig).reduce((features, key) => {
return Object.keys(featureConfig).reduce((features, key) => {
features[key] = initialValue;
return features;
}, {});
@ -302,7 +298,7 @@ function setUpTests(
}
function includeCodeQlIfRequired(featureFlag: string) {
return featureFlagConfig[featureFlag].minimumVersion !== undefined
return featureConfig[featureFlag].minimumVersion !== undefined
? mockCodeQLVersion("9.9.9")
: undefined;
}

View file

@ -5,10 +5,10 @@ import { RepositoryNwo } from "./repository";
import * as util from "./util";
export interface FeatureFlags {
getValue(flag: FeatureFlag, codeql?: CodeQL): Promise<boolean>;
getValue(flag: Feature, codeql?: CodeQL): Promise<boolean>;
}
export enum FeatureFlag {
export enum Feature {
BypassToolcacheEnabled = "bypass_toolcache_enabled",
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
TrapCachingEnabled = "trap_caching_enabled",
@ -16,27 +16,27 @@ export enum FeatureFlag {
CliConfigFileEnabled = "cli_config_file_enabled",
}
export const featureFlagConfig: Record<
FeatureFlag,
export const featureConfig: Record<
Feature,
{ envVar: string; minimumVersion: string | undefined }
> = {
[FeatureFlag.BypassToolcacheEnabled]: {
[Feature.BypassToolcacheEnabled]: {
envVar: "CODEQL_BYPASS_TOOLCACHE",
minimumVersion: undefined,
},
[FeatureFlag.MlPoweredQueriesEnabled]: {
[Feature.MlPoweredQueriesEnabled]: {
envVar: "CODEQL_ML_POWERED_QUERIES",
minimumVersion: "2.7.5",
},
[FeatureFlag.TrapCachingEnabled]: {
[Feature.TrapCachingEnabled]: {
envVar: "CODEQL_TRAP_CACHING",
minimumVersion: undefined,
},
[FeatureFlag.GolangExtractionReconciliationEnabled]: {
[Feature.GolangExtractionReconciliationEnabled]: {
envVar: "CODEQL_GOLANG_EXTRACTION_RECONCILIATION",
minimumVersion: undefined,
},
[FeatureFlag.CliConfigFileEnabled]: {
[Feature.CliConfigFileEnabled]: {
envVar: "CODEQL_PASS_CONFIG_TO_CLI",
minimumVersion: "2.10.1",
},
@ -48,7 +48,7 @@ export const featureFlagConfig: Record<
*
* It maps feature flags to whether they are enabled or not.
*/
type FeatureFlagsApiResponse = Partial<Record<FeatureFlag, boolean>>;
type FeatureFlagsApiResponse = Partial<Record<Feature, boolean>>;
export class GitHubFeatureFlags implements FeatureFlags {
private cachedApiResponse: FeatureFlagsApiResponse | undefined;
@ -72,20 +72,20 @@ export class GitHubFeatureFlags implements FeatureFlags {
*
* @throws if a `minimumVersion` is specified for the feature flag, and `codeql` is not provided.
*/
async getValue(flag: FeatureFlag, codeql?: CodeQL): Promise<boolean> {
if (!codeql && featureFlagConfig[flag].minimumVersion) {
async getValue(flag: Feature, codeql?: CodeQL): Promise<boolean> {
if (!codeql && featureConfig[flag].minimumVersion) {
throw new Error(
`A minimum version is specified for feature flag ${flag}, but no instance of CodeQL was provided.`
);
}
// Bypassing the toolcache is disabled in test mode.
if (flag === FeatureFlag.BypassToolcacheEnabled && util.isInTestMode()) {
if (flag === Feature.BypassToolcacheEnabled && util.isInTestMode()) {
return false;
}
const envVar = (
process.env[featureFlagConfig[flag].envVar] || ""
process.env[featureConfig[flag].envVar] || ""
).toLocaleLowerCase();
// Do not use this feature if user explicitly disables it via an environment variable.
@ -94,7 +94,7 @@ export class GitHubFeatureFlags implements FeatureFlags {
}
// Never use this feature if the CLI version explicitly can't support it.
const minimumVersion = featureFlagConfig[flag].minimumVersion;
const minimumVersion = featureConfig[flag].minimumVersion;
if (codeql && minimumVersion) {
if (!(await util.codeQlVersionAbove(codeql, minimumVersion))) {
return false;
@ -174,7 +174,7 @@ export class GitHubFeatureFlags implements FeatureFlags {
*
* This should be only used within tests.
*/
export function createFeatureFlags(enabledFlags: FeatureFlag[]): FeatureFlags {
export function createFeatureFlags(enabledFlags: Feature[]): FeatureFlags {
return {
getValue: async (flag) => {
return enabledFlags.includes(flag);

View file

@ -15,7 +15,7 @@ import {
import { getGitHubVersionActionsOnly } from "./api-client";
import { CodeQL, CODEQL_VERSION_NEW_TRACING } from "./codeql";
import * as configUtils from "./config-utils";
import { FeatureFlag, FeatureFlags, GitHubFeatureFlags } from "./feature-flags";
import { Feature, FeatureFlags, GitHubFeatureFlags } from "./feature-flags";
import {
initCodeQL,
initConfig,
@ -324,7 +324,7 @@ async function getTrapCachingEnabled(
if (trapCaching !== undefined) {
return trapCaching === "true";
}
return await featureFlags.getValue(FeatureFlag.TrapCachingEnabled);
return await featureFlags.getValue(Feature.TrapCachingEnabled);
}
async function runWrapper() {

View file

@ -18,7 +18,7 @@ import {
parsePacksSpecification,
prettyPrintPack,
} from "./config-utils";
import { FeatureFlag, FeatureFlags } from "./feature-flags";
import { Feature, FeatureFlags } from "./feature-flags";
import { Language } from "./languages";
import { Logger } from "./logging";
@ -781,7 +781,7 @@ export async function useCodeScanningConfigInCli(
codeql: CodeQL,
featureFlags: FeatureFlags
): Promise<boolean> {
return await featureFlags.getValue(FeatureFlag.CliConfigFileEnabled, codeql);
return await featureFlags.getValue(Feature.CliConfigFileEnabled, codeql);
}
export async function logCodeScanningConfigInCli(
@ -835,7 +835,7 @@ export async function isGoExtractionReconciliationEnabled(
featureFlags: FeatureFlags
): Promise<boolean> {
return await featureFlags.getValue(
FeatureFlag.GolangExtractionReconciliationEnabled
Feature.GolangExtractionReconciliationEnabled
);
}