From c54d34d0002a97e04639a7fc14e4a9fcd78deac1 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 24 Jul 2026 16:42:40 +0100 Subject: [PATCH 1/6] Return `ComputedInput` from `getConfigFileInput` and include in telemetry --- lib/entry-points.js | 91 +++++++++++++++++++++-------------------- src/config/file.test.ts | 8 +++- src/config/file.ts | 7 ++-- src/config/inputs.ts | 1 + src/init-action.ts | 15 ++++--- src/status-report.ts | 6 +-- 6 files changed, 70 insertions(+), 58 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index ab68cf74ee..7b9b155c7d 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -146550,7 +146550,7 @@ async function sendStatusReport(statusReport) { ); } } -async function createInitWithConfigStatusReport(config, initStatusReport, configFile, totalCacheSize, overlayBaseDatabaseStats, dependencyCachingResults) { +async function createInitWithConfigStatusReport(config, initStatusReport, configFileInput, totalCacheSize, overlayBaseDatabaseStats, dependencyCachingResults) { const languages = config.languages.join(","); const paths = (config.originalUserInput.paths || []).join(","); const pathsIgnore = (config.originalUserInput["paths-ignore"] || []).join( @@ -146576,7 +146576,7 @@ async function createInitWithConfigStatusReport(config, initStatusReport, config } return { ...initStatusReport, - config_file: configFile ?? "", + config_file: configFileInput?.value ?? "", disable_default_queries: disableDefaultQueries, paths, paths_ignore: pathsIgnore, @@ -148423,6 +148423,40 @@ function parseUserConfig(logger, pathInput, contents, validateConfig) { } } +// src/config/inputs.ts +async function getToolsInput(action, repositoryProperties) { + const name = "tools" /* Tools */; + const input = action.actions.getOptionalInput(name); + const propertyValue = repositoryProperties["github-codeql-tools" /* TOOLS */]; + const allowRepositoryProperty = await action.features.getValue( + "tools_repository_property" /* ToolsRepositoryProperty */ + ); + if (allowRepositoryProperty && propertyValue?.startsWith("!")) { + action.logger.info( + `Using ${name} input from repository property (enforced): ${propertyValue}` + ); + return { + // Drop the '!' from the value. + value: propertyValue.substring(1), + source: "repository-property" /* RepositoryProperty */ + }; + } + if (input !== void 0) { + action.logger.info(`Using ${name} input from workflow: ${input}`); + return { value: input, source: "workflow" /* Workflow */ }; + } + if (allowRepositoryProperty && propertyValue !== void 0) { + action.logger.info( + `Using ${name} input from repository property: ${propertyValue}` + ); + return { + value: propertyValue, + source: "repository-property" /* RepositoryProperty */ + }; + } + return void 0; +} + // src/config/remote-file.ts var DEFAULT_CONFIG_FILE_NAME = ".github/codeql-action.yaml"; var DEFAULT_CONFIG_FILE_REF = "main"; @@ -148504,7 +148538,7 @@ async function getConfigFileInput({ const input = actions.getOptionalInput("config-file"); if (input !== void 0) { logger.info(`Using configuration file input from workflow: ${input}`); - return input; + return { value: input, source: "workflow" /* Workflow */ }; } const propertyValue = repositoryProperties["github-codeql-config-file" /* CONFIG_FILE */]; if (propertyValue !== void 0 && propertyValue.trim().length > 0) { @@ -148515,7 +148549,7 @@ async function getConfigFileInput({ logger.info( `Using configuration file input from repository property: ${propertyValue}` ); - return propertyValue; + return { value: propertyValue, source: "repository-property" /* RepositoryProperty */ }; } else { logger.info( "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled." @@ -160347,40 +160381,6 @@ var core21 = __toESM(require_core()); var io7 = __toESM(require_io()); var semver10 = __toESM(require_semver2()); -// src/config/inputs.ts -async function getToolsInput(action, repositoryProperties) { - const name = "tools" /* Tools */; - const input = action.actions.getOptionalInput(name); - const propertyValue = repositoryProperties["github-codeql-tools" /* TOOLS */]; - const allowRepositoryProperty = await action.features.getValue( - "tools_repository_property" /* ToolsRepositoryProperty */ - ); - if (allowRepositoryProperty && propertyValue?.startsWith("!")) { - action.logger.info( - `Using ${name} input from repository property (enforced): ${propertyValue}` - ); - return { - // Drop the '!' from the value. - value: propertyValue.substring(1), - source: "repository-property" /* RepositoryProperty */ - }; - } - if (input !== void 0) { - action.logger.info(`Using ${name} input from workflow: ${input}`); - return { value: input, source: "workflow" /* Workflow */ }; - } - if (allowRepositoryProperty && propertyValue !== void 0) { - action.logger.info( - `Using ${name} input from repository property: ${propertyValue}` - ); - return { - value: propertyValue, - source: "repository-property" /* RepositoryProperty */ - }; - } - return void 0; -} - // src/workflow.ts var fs27 = __toESM(require("fs")); var path23 = __toESM(require("path")); @@ -160671,7 +160671,7 @@ async function sendStartingStatusReport(startedAt, config, logger) { await sendStatusReport(statusReportBase); } } -async function sendCompletedStatusReport2(startedAt, config, configFile, toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, overlayBaseDatabaseStats, dependencyCachingResults, logger, error3) { +async function sendCompletedStatusReport2(startedAt, config, configFileInput, toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, toolsSource, toolsVersion, overlayBaseDatabaseStats, dependencyCachingResults, logger, error3) { const statusReportBase = await createStatusReportBase( "init" /* Init */, getActionsStatus(error3), @@ -160696,6 +160696,9 @@ async function sendCompletedStatusReport2(startedAt, config, configFile, toolsIn if (toolsInput !== void 0) { initStatusReport.computed_inputs.tools = toolsInput; } + if (configFileInput !== void 0) { + initStatusReport.computed_inputs["config-file"] = configFileInput; + } const initToolsDownloadFields = {}; if (toolsDownloadStatusReport?.downloadDurationMs !== void 0) { initToolsDownloadFields.tools_download_duration_ms = toolsDownloadStatusReport.downloadDurationMs; @@ -160707,7 +160710,7 @@ async function sendCompletedStatusReport2(startedAt, config, configFile, toolsIn const initWithConfigStatusReport = await createInitWithConfigStatusReport( config, initStatusReport, - configFile, + configFileInput, Math.round( await getTotalCacheSize(Object.values(config.trapCaches), logger) ), @@ -160727,7 +160730,7 @@ async function run3(actionState) { const logger = actionState.logger; let apiDetails; let config; - let configFile; + let configFileInput; let codeql; let features; let sourceRoot; @@ -160765,7 +160768,7 @@ async function run3(actionState) { core21.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); core21.exportVariable("CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */, "true"); const actionStateWithFeatures = { ...actionState, features }; - configFile = await getConfigFileInput( + configFileInput = await getConfigFileInput( actionStateWithFeatures, repositoryProperties ); @@ -160846,7 +160849,7 @@ async function run3(actionState) { packsInput: getOptionalInput("packs"), buildModeInput: getOptionalInput("build-mode"), ramInput: getOptionalInput("ram"), - configFile, + configFile: configFileInput?.value, dbLocation: getOptionalInput("db-location"), configInput: getOptionalInput("config"), dependencyCachingEnabled: getDependencyCachingEnabled(), @@ -161133,7 +161136,7 @@ exec ${goBinaryPath} "$@"` await sendCompletedStatusReport2( startedAt, config, - configFile, + configFileInput, toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, diff --git a/src/config/file.test.ts b/src/config/file.test.ts index 22e2f795f8..c77e8d1797 100644 --- a/src/config/file.test.ts +++ b/src/config/file.test.ts @@ -13,6 +13,7 @@ import { } from "../testing-utils"; import { getConfigFileInput, getRemoteConfig } from "./file"; +import { InputSource } from "./inputs"; setupTests(test); @@ -42,7 +43,7 @@ test("getConfigFileInput returns input value", async (t) => { }) .withArgs(repositoryProperties) .logs(t, "Using configuration file input from workflow") - .passes(t.is, testInput); + .passes(t.deepEqual, { value: testInput, source: InputSource.Workflow }); }); test("getConfigFileInput returns repository property value", async (t) => { @@ -51,7 +52,10 @@ test("getConfigFileInput returns repository property value", async (t) => { .withFeatures([Feature.ConfigFileRepositoryProperty]) .withArgs(repositoryProperties) .logs(t, "Using configuration file input from repository property") - .passes(t.is, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]); + .passes(t.deepEqual, { + value: repositoryProperties[RepositoryPropertyName.CONFIG_FILE], + source: InputSource.RepositoryProperty, + }); }); test("getConfigFileInput ignores empty repository property value", async (t) => { diff --git a/src/config/file.ts b/src/config/file.ts index 8cb7bc3a11..0938386e03 100644 --- a/src/config/file.ts +++ b/src/config/file.ts @@ -9,6 +9,7 @@ import { import { ConfigurationError } from "../util"; import { parseUserConfig, UserConfig } from "./db-config"; +import { InputSource, type ComputedInput } from "./inputs"; import { parseRemoteFileAddress } from "./remote-file"; /** @@ -34,12 +35,12 @@ export async function getConfigFileInput( features, }: ActionState<["Logger", "Actions", "FeatureFlags"]>, repositoryProperties: Partial, -): Promise { +): Promise { const input = actions.getOptionalInput("config-file"); if (input !== undefined) { logger.info(`Using configuration file input from workflow: ${input}`); - return input; + return { value: input, source: InputSource.Workflow }; } const propertyValue = @@ -55,7 +56,7 @@ export async function getConfigFileInput( logger.info( `Using configuration file input from repository property: ${propertyValue}`, ); - return propertyValue; + return { value: propertyValue, source: InputSource.RepositoryProperty }; } else { logger.info( "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled.", diff --git a/src/config/inputs.ts b/src/config/inputs.ts index 32a8dfd6f6..c6d52f6977 100644 --- a/src/config/inputs.ts +++ b/src/config/inputs.ts @@ -7,6 +7,7 @@ import { /** Enumerates input names. */ export enum InputName { + ConfigFile = "config-file", Tools = "tools", } diff --git a/src/init-action.ts b/src/init-action.ts index c7837c6eed..4e59e740de 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -129,7 +129,7 @@ async function sendStartingStatusReport( async function sendCompletedStatusReport( startedAt: Date, config: configUtils.Config | undefined, - configFile: string | undefined, + configFileInput: ComputedInput | undefined, toolsInput: ComputedInput | undefined, toolsDownloadStatusReport: ToolsDownloadStatusReport | undefined, toolsFeatureFlagsValid: boolean | undefined, @@ -168,6 +168,9 @@ async function sendCompletedStatusReport( if (toolsInput !== undefined) { initStatusReport.computed_inputs.tools = toolsInput; } + if (configFileInput !== undefined) { + initStatusReport.computed_inputs["config-file"] = configFileInput; + } const initToolsDownloadFields: InitToolsDownloadFields = {}; @@ -185,7 +188,7 @@ async function sendCompletedStatusReport( await createInitWithConfigStatusReport( config, initStatusReport, - configFile, + configFileInput, Math.round( await getTotalCacheSize(Object.values(config.trapCaches), logger), ), @@ -212,7 +215,7 @@ async function run( let apiDetails: GitHubApiCombinedDetails; let config: configUtils.Config | undefined; - let configFile: string | undefined; + let configFileInput: ComputedInput | undefined; let codeql: CodeQL; let features: FeatureEnablement; let sourceRoot: string; @@ -263,7 +266,7 @@ async function run( core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true"); const actionStateWithFeatures = { ...actionState, features }; - configFile = await getConfigFileInput( + configFileInput = await getConfigFileInput( actionStateWithFeatures, repositoryProperties, ); @@ -376,7 +379,7 @@ async function run( packsInput: getOptionalInput("packs"), buildModeInput: getOptionalInput("build-mode"), ramInput: getOptionalInput("ram"), - configFile, + configFile: configFileInput?.value, dbLocation: getOptionalInput("db-location"), configInput: getOptionalInput("config"), dependencyCachingEnabled: getDependencyCachingEnabled(), @@ -782,7 +785,7 @@ async function run( await sendCompletedStatusReport( startedAt, config, - configFile, + configFileInput, toolsInput, toolsDownloadStatusReport, toolsFeatureFlagsValid, diff --git a/src/status-report.ts b/src/status-report.ts index d9d2a7ba4c..753ac78693 100644 --- a/src/status-report.ts +++ b/src/status-report.ts @@ -554,7 +554,7 @@ export interface InitToolsDownloadFields { * * @param config The CodeQL Action configuration whose values should be added to the base status report. * @param initStatusReport The base status report. - * @param configFile Optionally, the filename of the configuration file that was read. + * @param configFileInput Optionally, the filename of the configuration file that was read. * @param totalCacheSize The computed total TRAP cache size. * @param overlayBaseDatabaseStats Statistics about the overlay database, if any. * @returns @@ -562,7 +562,7 @@ export interface InitToolsDownloadFields { export async function createInitWithConfigStatusReport( config: Config, initStatusReport: InitStatusReport, - configFile: string | undefined, + configFileInput: ComputedInput | undefined, totalCacheSize: number, overlayBaseDatabaseStats: OverlayBaseDatabaseDownloadStats | undefined, dependencyCachingResults: DependencyCacheRestoreStatusReport | undefined, @@ -601,7 +601,7 @@ export async function createInitWithConfigStatusReport( return { ...initStatusReport, - config_file: configFile ?? "", + config_file: configFileInput?.value ?? "", disable_default_queries: disableDefaultQueries, paths, paths_ignore: pathsIgnore, From 6e76342b549341900441990d5e7c954ee96d894b Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 24 Jul 2026 17:02:08 +0100 Subject: [PATCH 2/6] Generalise `getToolsInput` into `getComputedInput` --- lib/entry-points.js | 17 +++++++---- src/config/inputs.ts | 51 +++++++++++++++++++++++++++------ src/feature-flags/properties.ts | 7 +++++ 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 7b9b155c7d..4946140dc4 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -148424,13 +148424,10 @@ function parseUserConfig(logger, pathInput, contents, validateConfig) { } // src/config/inputs.ts -async function getToolsInput(action, repositoryProperties) { - const name = "tools" /* Tools */; +async function getComputedInput(action, repositoryProperties, name, options) { const input = action.actions.getOptionalInput(name); - const propertyValue = repositoryProperties["github-codeql-tools" /* TOOLS */]; - const allowRepositoryProperty = await action.features.getValue( - "tools_repository_property" /* ToolsRepositoryProperty */ - ); + const allowRepositoryProperty = options.repositoryPropertyName !== void 0; + const propertyValue = options.repositoryPropertyName !== void 0 ? repositoryProperties[options.repositoryPropertyName] : void 0; if (allowRepositoryProperty && propertyValue?.startsWith("!")) { action.logger.info( `Using ${name} input from repository property (enforced): ${propertyValue}` @@ -148456,6 +148453,14 @@ async function getToolsInput(action, repositoryProperties) { } return void 0; } +async function getToolsInput(action, repositoryProperties) { + const allowRepositoryProperty = await action.features.getValue( + "tools_repository_property" /* ToolsRepositoryProperty */ + ); + return getComputedInput(action, repositoryProperties, "tools" /* Tools */, { + repositoryPropertyName: allowRepositoryProperty ? "github-codeql-tools" /* TOOLS */ : void 0 + }); +} // src/config/remote-file.ts var DEFAULT_CONFIG_FILE_NAME = ".github/codeql-action.yaml"; diff --git a/src/config/inputs.ts b/src/config/inputs.ts index c6d52f6977..a55ae0caca 100644 --- a/src/config/inputs.ts +++ b/src/config/inputs.ts @@ -3,6 +3,7 @@ import { Feature } from "../feature-flags"; import { RepositoryProperties, RepositoryPropertyName, + StringRepositoryPropertyNames, } from "../feature-flags/properties"; /** Enumerates input names. */ @@ -29,23 +30,35 @@ export type ComputedInput = { }; /** - * Gets the computed `tools` input. This comes from either the workflow or + * Represents options for how to compute an input. + */ +export interface ComputedInputOptions { + repositoryPropertyName?: StringRepositoryPropertyNames; +} + +/** + * Gets the computed input for `name`. This comes from either the workflow or * the repository property. * * @param action The Action state. * @param repositoryProperties The values of known repository properties. + * @param name The name of the input to compute. + * @param options Options for how to compute the input value. + * * @returns The computed input or `undefined` if there is no input. */ -export async function getToolsInput( +export async function getComputedInput( action: ActionState<["Logger", "Actions", "FeatureFlags"]>, - repositoryProperties: Partial, + repositoryProperties: RepositoryProperties, + name: InputName, + options: ComputedInputOptions, ): Promise { - const name = InputName.Tools; const input = action.actions.getOptionalInput(name); - const propertyValue = repositoryProperties[RepositoryPropertyName.TOOLS]; - const allowRepositoryProperty = await action.features.getValue( - Feature.ToolsRepositoryProperty, - ); + const allowRepositoryProperty = options.repositoryPropertyName !== undefined; + const propertyValue = + options.repositoryPropertyName !== undefined + ? repositoryProperties[options.repositoryPropertyName] + : undefined; // The repository property takes precedence if it starts with an '!'. if (allowRepositoryProperty && propertyValue?.startsWith("!")) { @@ -79,3 +92,25 @@ export async function getToolsInput( // There's no input. return undefined; } + +/** + * Gets the computed `tools` input. This comes from either the workflow or + * the repository property. + * + * @param action The Action state. + * @param repositoryProperties The values of known repository properties. + * @returns The computed input or `undefined` if there is no input. + */ +export async function getToolsInput( + action: ActionState<["Logger", "Actions", "FeatureFlags"]>, + repositoryProperties: Partial, +): Promise { + const allowRepositoryProperty = await action.features.getValue( + Feature.ToolsRepositoryProperty, + ); + return getComputedInput(action, repositoryProperties, InputName.Tools, { + repositoryPropertyName: allowRepositoryProperty + ? RepositoryPropertyName.TOOLS + : undefined, + }); +} diff --git a/src/feature-flags/properties.ts b/src/feature-flags/properties.ts index 4c888bd5ec..9b86db4e7c 100644 --- a/src/feature-flags/properties.ts +++ b/src/feature-flags/properties.ts @@ -29,6 +29,13 @@ export type AllRepositoryProperties = { [RepositoryPropertyName.TOOLS]: string; }; +/** The subset of known repository properties which are of type `string`. */ +export type StringRepositoryPropertyNames = keyof { + [K in keyof AllRepositoryProperties as AllRepositoryProperties[K] extends string + ? K + : never]: AllRepositoryProperties[K]; +}; + /** Parsed repository properties. */ export type RepositoryProperties = Partial; From c5d7d6b3ac97b7ce8b250a536d624468c54855eb Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 24 Jul 2026 17:05:43 +0100 Subject: [PATCH 3/6] Add `allowForcedRepositoryPropertyValue` option --- lib/entry-points.js | 3 ++- src/config/inputs.ts | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 4946140dc4..d3276039b4 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -148428,7 +148428,7 @@ async function getComputedInput(action, repositoryProperties, name, options) { const input = action.actions.getOptionalInput(name); const allowRepositoryProperty = options.repositoryPropertyName !== void 0; const propertyValue = options.repositoryPropertyName !== void 0 ? repositoryProperties[options.repositoryPropertyName] : void 0; - if (allowRepositoryProperty && propertyValue?.startsWith("!")) { + if (allowRepositoryProperty && options.allowForcedRepositoryPropertyValue && propertyValue?.startsWith("!")) { action.logger.info( `Using ${name} input from repository property (enforced): ${propertyValue}` ); @@ -148458,6 +148458,7 @@ async function getToolsInput(action, repositoryProperties) { "tools_repository_property" /* ToolsRepositoryProperty */ ); return getComputedInput(action, repositoryProperties, "tools" /* Tools */, { + allowForcedRepositoryPropertyValue: true, repositoryPropertyName: allowRepositoryProperty ? "github-codeql-tools" /* TOOLS */ : void 0 }); } diff --git a/src/config/inputs.ts b/src/config/inputs.ts index a55ae0caca..ae3d0e033b 100644 --- a/src/config/inputs.ts +++ b/src/config/inputs.ts @@ -33,7 +33,16 @@ export type ComputedInput = { * Represents options for how to compute an input. */ export interface ComputedInputOptions { + /** + * The name of the repository property to try and get the input value from. + * Repository properties are ignored if this is `undefined`. + */ repositoryPropertyName?: StringRepositoryPropertyNames; + /** + * Whether the repository property value may start with `!` to take precedence + * over any input value provided in the workflow file. + */ + allowForcedRepositoryPropertyValue?: boolean; } /** @@ -61,7 +70,11 @@ export async function getComputedInput( : undefined; // The repository property takes precedence if it starts with an '!'. - if (allowRepositoryProperty && propertyValue?.startsWith("!")) { + if ( + allowRepositoryProperty && + options.allowForcedRepositoryPropertyValue && + propertyValue?.startsWith("!") + ) { action.logger.info( `Using ${name} input from repository property (enforced): ${propertyValue}`, ); @@ -109,6 +122,7 @@ export async function getToolsInput( Feature.ToolsRepositoryProperty, ); return getComputedInput(action, repositoryProperties, InputName.Tools, { + allowForcedRepositoryPropertyValue: true, repositoryPropertyName: allowRepositoryProperty ? RepositoryPropertyName.TOOLS : undefined, From 479e61c602d96f3e55690f4f2e3bc67f8067b0c2 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 24 Jul 2026 17:17:43 +0100 Subject: [PATCH 4/6] Make FF explicit in `ComputedInputOptions` --- lib/entry-points.js | 14 +++++++++----- src/config/inputs.ts | 28 +++++++++++++++------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index d3276039b4..752f44fefe 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -148426,9 +148426,8 @@ function parseUserConfig(logger, pathInput, contents, validateConfig) { // src/config/inputs.ts async function getComputedInput(action, repositoryProperties, name, options) { const input = action.actions.getOptionalInput(name); - const allowRepositoryProperty = options.repositoryPropertyName !== void 0; - const propertyValue = options.repositoryPropertyName !== void 0 ? repositoryProperties[options.repositoryPropertyName] : void 0; - if (allowRepositoryProperty && options.allowForcedRepositoryPropertyValue && propertyValue?.startsWith("!")) { + const propertyValue = repositoryProperties[options.repositoryPropertyName]; + if (options.repositoryPropertyFeatureEnabled && options.allowForcedRepositoryPropertyValue && propertyValue?.startsWith("!")) { action.logger.info( `Using ${name} input from repository property (enforced): ${propertyValue}` ); @@ -148442,7 +148441,7 @@ async function getComputedInput(action, repositoryProperties, name, options) { action.logger.info(`Using ${name} input from workflow: ${input}`); return { value: input, source: "workflow" /* Workflow */ }; } - if (allowRepositoryProperty && propertyValue !== void 0) { + if (options.repositoryPropertyFeatureEnabled && propertyValue !== void 0) { action.logger.info( `Using ${name} input from repository property: ${propertyValue}` ); @@ -148450,6 +148449,10 @@ async function getComputedInput(action, repositoryProperties, name, options) { value: propertyValue, source: "repository-property" /* RepositoryProperty */ }; + } else if (propertyValue !== void 0) { + action.logger.info( + `Ignoring ${name} input from repository property, because the corresponding feature flag is disabled.` + ); } return void 0; } @@ -148458,8 +148461,9 @@ async function getToolsInput(action, repositoryProperties) { "tools_repository_property" /* ToolsRepositoryProperty */ ); return getComputedInput(action, repositoryProperties, "tools" /* Tools */, { + repositoryPropertyFeatureEnabled: allowRepositoryProperty, allowForcedRepositoryPropertyValue: true, - repositoryPropertyName: allowRepositoryProperty ? "github-codeql-tools" /* TOOLS */ : void 0 + repositoryPropertyName: "github-codeql-tools" /* TOOLS */ }); } diff --git a/src/config/inputs.ts b/src/config/inputs.ts index ae3d0e033b..d027e2a42e 100644 --- a/src/config/inputs.ts +++ b/src/config/inputs.ts @@ -33,16 +33,19 @@ export type ComputedInput = { * Represents options for how to compute an input. */ export interface ComputedInputOptions { + /** + * Whether the FF for the repository property (if any) is enabled. + */ + repositoryPropertyFeatureEnabled: boolean; /** * The name of the repository property to try and get the input value from. - * Repository properties are ignored if this is `undefined`. */ - repositoryPropertyName?: StringRepositoryPropertyNames; + repositoryPropertyName: StringRepositoryPropertyNames; /** * Whether the repository property value may start with `!` to take precedence * over any input value provided in the workflow file. */ - allowForcedRepositoryPropertyValue?: boolean; + allowForcedRepositoryPropertyValue: boolean; } /** @@ -63,15 +66,11 @@ export async function getComputedInput( options: ComputedInputOptions, ): Promise { const input = action.actions.getOptionalInput(name); - const allowRepositoryProperty = options.repositoryPropertyName !== undefined; - const propertyValue = - options.repositoryPropertyName !== undefined - ? repositoryProperties[options.repositoryPropertyName] - : undefined; + const propertyValue = repositoryProperties[options.repositoryPropertyName]; // The repository property takes precedence if it starts with an '!'. if ( - allowRepositoryProperty && + options.repositoryPropertyFeatureEnabled && options.allowForcedRepositoryPropertyValue && propertyValue?.startsWith("!") ) { @@ -92,7 +91,7 @@ export async function getComputedInput( } // Use the repository property if there's no workflow input. - if (allowRepositoryProperty && propertyValue !== undefined) { + if (options.repositoryPropertyFeatureEnabled && propertyValue !== undefined) { action.logger.info( `Using ${name} input from repository property: ${propertyValue}`, ); @@ -100,6 +99,10 @@ export async function getComputedInput( value: propertyValue, source: InputSource.RepositoryProperty, }; + } else if (propertyValue !== undefined) { + action.logger.info( + `Ignoring ${name} input from repository property, because the corresponding feature flag is disabled.`, + ); } // There's no input. @@ -122,9 +125,8 @@ export async function getToolsInput( Feature.ToolsRepositoryProperty, ); return getComputedInput(action, repositoryProperties, InputName.Tools, { + repositoryPropertyFeatureEnabled: allowRepositoryProperty, allowForcedRepositoryPropertyValue: true, - repositoryPropertyName: allowRepositoryProperty - ? RepositoryPropertyName.TOOLS - : undefined, + repositoryPropertyName: RepositoryPropertyName.TOOLS, }); } From 58b7aff402e3403a0c29c2690cc47a618a8bdcd9 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 24 Jul 2026 17:26:50 +0100 Subject: [PATCH 5/6] Add `nonEmptyStringProperty` --- lib/entry-points.js | 9 ++++++++- src/feature-flags/properties.ts | 13 ++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index 752f44fefe..f60a799561 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -147996,13 +147996,20 @@ var stringProperty = { validate: isString2, parse: parseStringRepositoryProperty }; +function isNonEmptyString(value) { + return isString2(value) && value.trim().length > 0; +} +var nonEmptyStringProperty = { + ...stringProperty, + validate: isNonEmptyString +}; var booleanProperty = { // The value from the API should come as a string, which we then parse into a boolean. validate: isString2, parse: parseBooleanRepositoryProperty }; var repositoryPropertyParsers = { - ["github-codeql-config-file" /* CONFIG_FILE */]: stringProperty, + ["github-codeql-config-file" /* CONFIG_FILE */]: nonEmptyStringProperty, ["github-codeql-disable-overlay" /* DISABLE_OVERLAY */]: booleanProperty, ["github-codeql-extra-queries" /* EXTRA_QUERIES */]: stringProperty, ["github-codeql-file-coverage-on-prs" /* FILE_COVERAGE_ON_PRS */]: booleanProperty, diff --git a/src/feature-flags/properties.ts b/src/feature-flags/properties.ts index 9b86db4e7c..4ee487728f 100644 --- a/src/feature-flags/properties.ts +++ b/src/feature-flags/properties.ts @@ -79,6 +79,17 @@ const stringProperty = { parse: parseStringRepositoryProperty, }; +/** Determines whether a value from the API is a non-empty string or not. */ +function isNonEmptyString(value: RepositoryPropertyValue): value is string { + return isString(value) && value.trim().length > 0; +} + +/** A repository property that we expect to contain a non-empty string value. */ +const nonEmptyStringProperty = { + ...stringProperty, + validate: isNonEmptyString, +}; + /** A repository property that we expect to contain a boolean value. */ const booleanProperty = { // The value from the API should come as a string, which we then parse into a boolean. @@ -90,7 +101,7 @@ const booleanProperty = { const repositoryPropertyParsers: { [K in RepositoryPropertyName]: PropertyInfo; } = { - [RepositoryPropertyName.CONFIG_FILE]: stringProperty, + [RepositoryPropertyName.CONFIG_FILE]: nonEmptyStringProperty, [RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty, [RepositoryPropertyName.EXTRA_QUERIES]: stringProperty, [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty, From e780b6e403a2ea57104ab1c713f0d23ad8571dbc Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Fri, 24 Jul 2026 17:29:01 +0100 Subject: [PATCH 6/6] Use `getComputedInput` for `getConfigFileInput` --- lib/entry-points.js | 36 +++++++++------------------------ src/config/file.test.ts | 16 ++++----------- src/config/file.ts | 45 ++++++++++------------------------------- 3 files changed, 24 insertions(+), 73 deletions(-) diff --git a/lib/entry-points.js b/lib/entry-points.js index f60a799561..53c94b78f5 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -148547,33 +148547,15 @@ async function parseRemoteFileAddress(actionState, configFile) { // src/config/file.ts var LOCAL_PATH_PREFIX = "./"; var REMOTE_PATH_PREFIX = "remote="; -async function getConfigFileInput({ - logger, - actions, - features -}, repositoryProperties) { - const input = actions.getOptionalInput("config-file"); - if (input !== void 0) { - logger.info(`Using configuration file input from workflow: ${input}`); - return { value: input, source: "workflow" /* Workflow */ }; - } - const propertyValue = repositoryProperties["github-codeql-config-file" /* CONFIG_FILE */]; - if (propertyValue !== void 0 && propertyValue.trim().length > 0) { - const useRepositoryProperty = await features.getValue( - "config_file_repository_property" /* ConfigFileRepositoryProperty */ - ); - if (useRepositoryProperty) { - logger.info( - `Using configuration file input from repository property: ${propertyValue}` - ); - return { value: propertyValue, source: "repository-property" /* RepositoryProperty */ }; - } else { - logger.info( - "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled." - ); - } - } - return void 0; +async function getConfigFileInput(action, repositoryProperties) { + const useRepositoryProperty = await action.features.getValue( + "config_file_repository_property" /* ConfigFileRepositoryProperty */ + ); + return getComputedInput(action, repositoryProperties, "config-file" /* ConfigFile */, { + repositoryPropertyFeatureEnabled: useRepositoryProperty, + allowForcedRepositoryPropertyValue: false, + repositoryPropertyName: "github-codeql-config-file" /* CONFIG_FILE */ + }); } async function getRemoteConfig(actionState, configFile, apiDetails) { const address = await parseRemoteFileAddress(actionState, configFile); diff --git a/src/config/file.test.ts b/src/config/file.test.ts index c77e8d1797..0659937ad3 100644 --- a/src/config/file.test.ts +++ b/src/config/file.test.ts @@ -42,7 +42,7 @@ test("getConfigFileInput returns input value", async (t) => { .returns(testInput); }) .withArgs(repositoryProperties) - .logs(t, "Using configuration file input from workflow") + .logs(t, "Using config-file input from workflow") .passes(t.deepEqual, { value: testInput, source: InputSource.Workflow }); }); @@ -51,30 +51,22 @@ test("getConfigFileInput returns repository property value", async (t) => { await callee(getConfigFileInput) .withFeatures([Feature.ConfigFileRepositoryProperty]) .withArgs(repositoryProperties) - .logs(t, "Using configuration file input from repository property") + .logs(t, "Using config-file input from repository property") .passes(t.deepEqual, { value: repositoryProperties[RepositoryPropertyName.CONFIG_FILE], source: InputSource.RepositoryProperty, }); }); -test("getConfigFileInput ignores empty repository property value", async (t) => { - // Since the repository property value is an empty/whitespace string, we should ignore it. - await callee(getConfigFileInput) - .withFeatures([Feature.ConfigFileRepositoryProperty]) - .withArgs({ [RepositoryPropertyName.CONFIG_FILE]: " " }) - .passes(t.is, undefined); -}); - test("getConfigFileInput ignores repository property value when FF is off", async (t) => { // Since the FF is off, we should ignore the repository property value. await callee(getConfigFileInput) .withFeatures([]) .withArgs(repositoryProperties) - .notLogs(t, "Using configuration file input from repository property") + .notLogs(t, "Using config-file input from repository property") .logs( t, - "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled.", + "Ignoring config-file input from repository property, because the corresponding feature flag is disabled.", ) .passes(t.is, undefined); }); diff --git a/src/config/file.ts b/src/config/file.ts index 0938386e03..5cb9d1993b 100644 --- a/src/config/file.ts +++ b/src/config/file.ts @@ -9,7 +9,7 @@ import { import { ConfigurationError } from "../util"; import { parseUserConfig, UserConfig } from "./db-config"; -import { InputSource, type ComputedInput } from "./inputs"; +import { getComputedInput, InputName, type ComputedInput } from "./inputs"; import { parseRemoteFileAddress } from "./remote-file"; /** @@ -29,42 +29,19 @@ export const REMOTE_PATH_PREFIX = "remote="; * Gets the value that is configured for the configuration file, if any. */ export async function getConfigFileInput( - { - logger, - actions, - features, - }: ActionState<["Logger", "Actions", "FeatureFlags"]>, + action: ActionState<["Logger", "Actions", "FeatureFlags"]>, repositoryProperties: Partial, ): Promise { - const input = actions.getOptionalInput("config-file"); - - if (input !== undefined) { - logger.info(`Using configuration file input from workflow: ${input}`); - return { value: input, source: InputSource.Workflow }; - } - - const propertyValue = - repositoryProperties[RepositoryPropertyName.CONFIG_FILE]; - - if (propertyValue !== undefined && propertyValue.trim().length > 0) { - // Only use the repository property value if the FF is enabled. - const useRepositoryProperty = await features.getValue( - Feature.ConfigFileRepositoryProperty, - ); - - if (useRepositoryProperty) { - logger.info( - `Using configuration file input from repository property: ${propertyValue}`, - ); - return { value: propertyValue, source: InputSource.RepositoryProperty }; - } else { - logger.info( - "Ignoring configuration file input from repository property, because the corresponding feature flag is disabled.", - ); - } - } + // Only use the repository property value if the FF is enabled. + const useRepositoryProperty = await action.features.getValue( + Feature.ConfigFileRepositoryProperty, + ); - return undefined; + return getComputedInput(action, repositoryProperties, InputName.ConfigFile, { + repositoryPropertyFeatureEnabled: useRepositoryProperty, + allowForcedRepositoryPropertyValue: false, + repositoryPropertyName: RepositoryPropertyName.CONFIG_FILE, + }); } /**