More renaming
Avoid usage of "Feature Flag" unless we are talking specifically about the response from github features api. Otherwise, use terms like "Toggleable features". Note both "toggleable" and "togglable" appear to be valid spellings of the word. I chose the first for no good reason.
This commit is contained in:
parent
b27aed78f5
commit
1a17c59fb0
39 changed files with 237 additions and 200 deletions
|
|
@ -4,16 +4,16 @@ import { Logger } from "./logging";
|
|||
import { RepositoryNwo } from "./repository";
|
||||
import * as util from "./util";
|
||||
|
||||
export interface FeatureFlags {
|
||||
getValue(flag: Feature, codeql?: CodeQL): Promise<boolean>;
|
||||
export interface FeatureEnablement {
|
||||
getValue(feaature: Feature, codeql?: CodeQL): Promise<boolean>;
|
||||
}
|
||||
|
||||
export enum Feature {
|
||||
BypassToolcacheEnabled = "bypass_toolcache_enabled",
|
||||
CliConfigFileEnabled = "cli_config_file_enabled",
|
||||
GolangExtractionReconciliationEnabled = "golang_extraction_reconciliation_enabled",
|
||||
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
|
||||
TrapCachingEnabled = "trap_caching_enabled",
|
||||
GolangExtractionReconciliationEnabled = "golang_extraction_reconciliation_enabled",
|
||||
CliConfigFileEnabled = "cli_config_file_enabled",
|
||||
}
|
||||
|
||||
export const featureConfig: Record<
|
||||
|
|
@ -24,6 +24,14 @@ export const featureConfig: Record<
|
|||
envVar: "CODEQL_BYPASS_TOOLCACHE",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.CliConfigFileEnabled]: {
|
||||
envVar: "CODEQL_PASS_CONFIG_TO_CLI",
|
||||
minimumVersion: "2.10.1",
|
||||
},
|
||||
[Feature.GolangExtractionReconciliationEnabled]: {
|
||||
envVar: "CODEQL_GOLANG_EXTRACTION_RECONCILIATION",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.MlPoweredQueriesEnabled]: {
|
||||
envVar: "CODEQL_ML_POWERED_QUERIES",
|
||||
minimumVersion: "2.7.5",
|
||||
|
|
@ -32,14 +40,6 @@ export const featureConfig: Record<
|
|||
envVar: "CODEQL_TRAP_CACHING",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.GolangExtractionReconciliationEnabled]: {
|
||||
envVar: "CODEQL_GOLANG_EXTRACTION_RECONCILIATION",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.CliConfigFileEnabled]: {
|
||||
envVar: "CODEQL_PASS_CONFIG_TO_CLI",
|
||||
minimumVersion: "2.10.1",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -48,14 +48,14 @@ export const featureConfig: Record<
|
|||
*
|
||||
* It maps feature flags to whether they are enabled or not.
|
||||
*/
|
||||
type FeatureFlagsApiResponse = Partial<Record<Feature, boolean>>;
|
||||
type GitHubFeatureFlagsApiResponse = Partial<Record<Feature, boolean>>;
|
||||
|
||||
/**
|
||||
* Determines the enablement status of a number of features.
|
||||
* If feature enablement is not able to be determined locally, a request to the
|
||||
* github API is made to determine the enablement status.
|
||||
*/
|
||||
export class Features implements FeatureFlags {
|
||||
export class Features implements FeatureEnablement {
|
||||
private gitHubFeatureFlags: GitHubFeatureFlags;
|
||||
|
||||
constructor(
|
||||
|
|
@ -74,7 +74,7 @@ export class Features implements FeatureFlags {
|
|||
|
||||
/**
|
||||
*
|
||||
* @param flag The feature flag to check.
|
||||
* @param feature The feature flag to check.
|
||||
* @param codeql An optional CodeQL object. If provided, and a `minimumVersion` is specified for the
|
||||
* feature flag, the version of the CodeQL CLI will be checked against the minimum version.
|
||||
* If the version is less than the minimum version, the feature flag will be considered
|
||||
|
|
@ -84,20 +84,20 @@ export class Features implements FeatureFlags {
|
|||
*
|
||||
* @throws if a `minimumVersion` is specified for the feature flag, and `codeql` is not provided.
|
||||
*/
|
||||
async getValue(flag: Feature, codeql?: CodeQL): Promise<boolean> {
|
||||
if (!codeql && featureConfig[flag].minimumVersion) {
|
||||
async getValue(feature: Feature, codeql?: CodeQL): Promise<boolean> {
|
||||
if (!codeql && featureConfig[feature].minimumVersion) {
|
||||
throw new Error(
|
||||
`Internal error: A minimum version is specified for feature flag ${flag}, but no instance of CodeQL was provided.`
|
||||
`Internal error: A minimum version is specified for feature ${feature}, but no instance of CodeQL was provided.`
|
||||
);
|
||||
}
|
||||
|
||||
// Bypassing the toolcache is disabled in test mode.
|
||||
if (flag === Feature.BypassToolcacheEnabled && util.isInTestMode()) {
|
||||
if (feature === Feature.BypassToolcacheEnabled && util.isInTestMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const envVar = (
|
||||
process.env[featureConfig[flag].envVar] || ""
|
||||
process.env[featureConfig[feature].envVar] || ""
|
||||
).toLocaleLowerCase();
|
||||
|
||||
// Do not use this feature if user explicitly disables it via an environment variable.
|
||||
|
|
@ -106,7 +106,7 @@ export class Features implements FeatureFlags {
|
|||
}
|
||||
|
||||
// Never use this feature if the CLI version explicitly can't support it.
|
||||
const minimumVersion = featureConfig[flag].minimumVersion;
|
||||
const minimumVersion = featureConfig[feature].minimumVersion;
|
||||
if (codeql && minimumVersion) {
|
||||
if (!(await util.codeQlVersionAbove(codeql, minimumVersion))) {
|
||||
return false;
|
||||
|
|
@ -119,12 +119,12 @@ export class Features implements FeatureFlags {
|
|||
}
|
||||
|
||||
// Ask the GitHub API if the feature is enabled.
|
||||
return await this.gitHubFeatureFlags.getValue(flag);
|
||||
return await this.gitHubFeatureFlags.getValue(feature);
|
||||
}
|
||||
}
|
||||
|
||||
class GitHubFeatureFlags implements FeatureFlags {
|
||||
private cachedApiResponse: FeatureFlagsApiResponse | undefined;
|
||||
class GitHubFeatureFlags implements FeatureEnablement {
|
||||
private cachedApiResponse: GitHubFeatureFlagsApiResponse | undefined;
|
||||
|
||||
constructor(
|
||||
private gitHubVersion: util.GitHubVersion,
|
||||
|
|
@ -135,25 +135,25 @@ class GitHubFeatureFlags implements FeatureFlags {
|
|||
/**/
|
||||
}
|
||||
|
||||
async getValue(flag: Feature): Promise<boolean> {
|
||||
async getValue(feature: Feature): Promise<boolean> {
|
||||
const response = await this.getApiResponse();
|
||||
if (response === undefined) {
|
||||
this.logger.debug(
|
||||
`No feature flags API response for ${flag}, considering it disabled.`
|
||||
`No feature flags API response for ${feature}, considering it disabled.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
const flagValue = response[flag];
|
||||
if (flagValue === undefined) {
|
||||
const featureEnablement = response[feature];
|
||||
if (featureEnablement === undefined) {
|
||||
this.logger.debug(
|
||||
`Feature flag '${flag}' undefined in API response, considering it disabled.`
|
||||
`Feature '${feature}' undefined in API response, considering it disabled.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return flagValue || false;
|
||||
return !!featureEnablement;
|
||||
}
|
||||
|
||||
private async getApiResponse(): Promise<FeatureFlagsApiResponse> {
|
||||
private async getApiResponse(): Promise<GitHubFeatureFlagsApiResponse> {
|
||||
const apiResponse =
|
||||
this.cachedApiResponse || (await this.loadApiResponse());
|
||||
this.cachedApiResponse = apiResponse;
|
||||
|
|
@ -164,7 +164,7 @@ class GitHubFeatureFlags implements FeatureFlags {
|
|||
// Do nothing when not running against github.com
|
||||
if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) {
|
||||
this.logger.debug(
|
||||
"Not running against github.com. Disabling all feature flags."
|
||||
"Not running against github.com. Disabling all toggleable features."
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
|
@ -187,27 +187,14 @@ class GitHubFeatureFlags implements FeatureFlags {
|
|||
`please ensure the Action has the 'security-events: write' permission. Details: ${e}`
|
||||
);
|
||||
} else {
|
||||
// Some feature flags, such as `ml_powered_queries_enabled` affect the produced alerts.
|
||||
// Considering these feature flags disabled in the event of a transient error could
|
||||
// Some feature, such as `ml_powered_queries_enabled` affect the produced alerts.
|
||||
// Considering these feature disabled in the event of a transient error could
|
||||
// therefore lead to alert churn. As a result, we crash if we cannot determine the value of
|
||||
// the feature flags.
|
||||
// the feature.
|
||||
throw new Error(
|
||||
`Encountered an error while trying to load feature flags: ${e}`
|
||||
`Encountered an error while trying to determine feature enablement: ${e}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a feature flags instance with the specified set of enabled flags.
|
||||
*
|
||||
* This should be only used within tests.
|
||||
*/
|
||||
export function createFeatureFlags(enabledFlags: Feature[]): FeatureFlags {
|
||||
return {
|
||||
getValue: async (flag) => {
|
||||
return enabledFlags.includes(flag);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue