Skip to content

Commit 9d2031f

Browse files
committed
Track outcomes of querying FFs with getValue
1 parent b126fac commit 9d2031f

File tree

8 files changed

+117
-1
lines changed

8 files changed

+117
-1
lines changed

lib/analyze-action.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/autobuild-action.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/init-action-post.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/init-action.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/setup-codeql-action.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-sarif-action.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/feature-flags.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,31 @@ test(`Feature flags are requested in GHEC-DR`, async (t) => {
9292
});
9393
});
9494

95+
test("Queried feature flags are recorded", async (t) => {
96+
await withTmpDir(async (tmpDir) => {
97+
const loggedMessages = [];
98+
const features = setUpFeatureFlagTests(
99+
tmpDir,
100+
getRecordingLogger(loggedMessages),
101+
{ type: GitHubVariant.DOTCOM },
102+
);
103+
104+
mockFeatureFlagApiEndpoint(200, initializeFeatures(true));
105+
106+
// No features should have been queried initially.
107+
t.is(Object.keys(features.getQueriedFeatures()).length, 0);
108+
109+
// Query all features.
110+
const allFeatures = Object.values(Feature);
111+
for (const feature of allFeatures) {
112+
await getFeatureIncludingCodeQlIfRequired(features, feature);
113+
}
114+
115+
// All features should have a been queried.
116+
t.is(Object.keys(features.getQueriedFeatures()).length, allFeatures.length);
117+
});
118+
});
119+
95120
test("API response missing and features use default value", async (t) => {
96121
await withTmpDir(async (tmpDir) => {
97122
const loggedMessages: LoggedMessage[] = [];
@@ -562,7 +587,7 @@ function setUpFeatureFlagTests(
562587
tmpDir: string,
563588
logger = getRunnerLogger(true),
564589
gitHubVersion = { type: GitHubVariant.DOTCOM } as util.GitHubVersion,
565-
): FeatureEnablement {
590+
): Features {
566591
setupActionsVars(tmpDir, tmpDir);
567592

568593
return new Features(gitHubVersion, testRepositoryNwo, tmpDir, logger);

src/feature-flags.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,14 @@ export interface FeatureEnablement {
345345
*/
346346
type GitHubFeatureFlagsApiResponse = Partial<Record<Feature, boolean>>;
347347

348+
// Even though we are currently only tracking the value of queried features, we use an object
349+
// here rather than just a boolean to keep open the possibility of also tracking the reason
350+
// for why a particular value was resolved (i.e. because of an environment variable or API result)
351+
// in the future.
352+
export interface QueriedFeatureStatus {
353+
value: boolean;
354+
}
355+
348356
export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
349357

350358
/**
@@ -355,6 +363,9 @@ export const FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
355363
export class Features implements FeatureEnablement {
356364
private gitHubFeatureFlags: GitHubFeatureFlags;
357365

366+
// Tracks features that have been queried at some point and the outcome.
367+
private queriedFeatures: Partial<Record<Feature, QueriedFeatureStatus>> = {};
368+
358369
constructor(
359370
gitHubVersion: util.GitHubVersion,
360371
repositoryNwo: RepositoryNwo,
@@ -369,6 +380,11 @@ export class Features implements FeatureEnablement {
369380
);
370381
}
371382

383+
/** Gets a record of features that were queried and the corresponding outcomes. */
384+
public getQueriedFeatures() {
385+
return this.queriedFeatures;
386+
}
387+
372388
async getDefaultCliVersion(
373389
variant: util.GitHubVariant,
374390
): Promise<CodeQLDefaultVersionInfo> {
@@ -388,6 +404,15 @@ export class Features implements FeatureEnablement {
388404
* @throws if a `minimumVersion` is specified for the feature, and `codeql` is not provided.
389405
*/
390406
async getValue(feature: Feature, codeql?: CodeQL): Promise<boolean> {
407+
const value = await this.getValueInternal(feature, codeql);
408+
this.queriedFeatures[feature] = { value };
409+
return value;
410+
}
411+
412+
private async getValueInternal(
413+
feature: Feature,
414+
codeql?: CodeQL,
415+
): Promise<boolean> {
391416
// Narrow the type to FeatureConfig to avoid type errors. To avoid unsafe use of `as`, we
392417
// check that the required properties exist using `satisfies`.
393418
const config = featureConfig[

0 commit comments

Comments
 (0)