diff --git a/lib/entry-points.js b/lib/entry-points.js index c69fa78ff8..ff60cfa202 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -148162,6 +148162,9 @@ var Failure = class { }; // src/actions-util.ts +function getActionsEnv() { + return { getOptionalInput }; +} var getRequiredInput = function(name) { const value = core3.getInput(name); if (!value) { @@ -150233,6 +150236,7 @@ function getUnknownLanguagesError(languages) { // src/feature-flags/properties.ts var GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-"; var RepositoryPropertyName = /* @__PURE__ */ ((RepositoryPropertyName2) => { + RepositoryPropertyName2["CONFIG_FILE"] = "github-codeql-config-file"; RepositoryPropertyName2["DISABLE_OVERLAY"] = "github-codeql-disable-overlay"; RepositoryPropertyName2["EXTRA_QUERIES"] = "github-codeql-extra-queries"; RepositoryPropertyName2["FILE_COVERAGE_ON_PRS"] = "github-codeql-file-coverage-on-prs"; @@ -150251,6 +150255,7 @@ var booleanProperty = { parse: parseBooleanRepositoryProperty }; var repositoryPropertyParsers = { + ["github-codeql-config-file" /* CONFIG_FILE */]: stringProperty, ["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 @@ -158526,6 +158531,23 @@ var github3 = __toESM(require_github()); var io7 = __toESM(require_io()); var semver10 = __toESM(require_semver2()); +// src/config/file.ts +function getConfigFileInput(logger, actions, repositoryProperties) { + const input = actions.getOptionalInput("config-file"); + if (input !== void 0) { + logger.info(`Using configuration file input from workflow: ${input}`); + return input; + } + const propertyValue = repositoryProperties["github-codeql-config-file" /* CONFIG_FILE */]; + if (propertyValue !== void 0) { + logger.info( + `Using configuration file input from repository property: ${propertyValue}` + ); + return propertyValue; + } + return void 0; +} + // src/workflow.ts var fs26 = __toESM(require("fs")); var path22 = __toESM(require("path")); @@ -158866,6 +158888,7 @@ async function sendCompletedStatusReport2(startedAt, config, configFile, toolsDo } async function run3(startedAt) { const logger = getActionsLogger(); + const actionsEnv = getActionsEnv(); let apiDetails; let config; let configFile; @@ -158900,11 +158923,12 @@ async function run3(startedAt) { repositoryNwo, logger ); + const repositoryProperties = repositoryPropertiesResult.orElse({}); const jobRunUuid = v4_default(); logger.info(`Job run UUID is ${jobRunUuid}.`); core20.exportVariable("JOB_RUN_UUID" /* JOB_RUN_UUID */, jobRunUuid); core20.exportVariable("CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */, "true"); - configFile = getOptionalInput("config-file"); + configFile = getConfigFileInput(logger, actionsEnv, repositoryProperties); sourceRoot = path23.resolve( getRequiredEnvParam("GITHUB_WORKSPACE"), getOptionalInput("source-root") || "" @@ -158966,7 +158990,6 @@ async function run3(startedAt) { } analysisKinds = await getAnalysisKinds(logger, features); const debugMode = getOptionalInput("debug") === "true" || core20.isDebug(); - const repositoryProperties = repositoryPropertiesResult.orElse({}); const fileCoverageResult = await getFileCoverageInformationEnabled( debugMode, codeql, diff --git a/src/actions-util.ts b/src/actions-util.ts index 592a70ace3..dea22d5c57 100644 --- a/src/actions-util.ts +++ b/src/actions-util.ts @@ -21,6 +21,21 @@ import { */ declare const __CODEQL_ACTION_VERSION__: string; +/** + * Abstracts over GitHub Actions functions so that we do not have to stub + * global functions in tests. + */ +export interface ActionsEnv { + getOptionalInput: (name: string) => string | undefined; +} + +/** + * Gets the real `ActionsEnv` used by production code. + */ +export function getActionsEnv(): ActionsEnv { + return { getOptionalInput }; +} + /** * Wrapper around core.getInput for inputs that always have a value. * Also see getOptionalInput. diff --git a/src/config/file.test.ts b/src/config/file.test.ts new file mode 100644 index 0000000000..c27a3ff5ce --- /dev/null +++ b/src/config/file.test.ts @@ -0,0 +1,58 @@ +import test from "ava"; +import sinon from "sinon"; + +import { RepositoryPropertyName } from "../feature-flags/properties"; +import { + getTestActionsEnv, + RecordingLogger, + setupTests, +} from "../testing-utils"; + +import { getConfigFileInput } from "./file"; + +setupTests(test); + +test("getConfigFileInput returns undefined by default", async (t) => { + const logger = new RecordingLogger(); + const actionsEnv = getTestActionsEnv(); + const result = getConfigFileInput(logger, actionsEnv, {}); + t.is(result, undefined); +}); + +const repositoryProperties = { + [RepositoryPropertyName.CONFIG_FILE]: "/path/from/property", +}; + +test("getConfigFileInput returns input value", async (t) => { + const logger = new RecordingLogger(); + const actionsEnv = getTestActionsEnv(); + const testInput = "/some/path"; + sinon + .stub(actionsEnv, "getOptionalInput") + .withArgs("config-file") + .returns(testInput); + + // Even though both an input and repository property are configured, + // we prefer the direct input to the Action. + const result = getConfigFileInput(logger, actionsEnv, repositoryProperties); + t.is(result, testInput); + + // Check for the expected log message. + t.true(logger.hasMessage("Using configuration file input from workflow")); +}); + +test("getConfigFileInput returns repository property value", async (t) => { + const logger = new RecordingLogger(); + const actionsEnv = getTestActionsEnv(); + + // Since there is no direct input, we should use the repository property. + const result = getConfigFileInput(logger, actionsEnv, repositoryProperties); + t.is(result, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]); + + // Check for the expected log message. + t.true( + logger.hasMessage( + "Using configuration file input from repository property", + ), + ); +}); diff --git a/src/config/file.ts b/src/config/file.ts new file mode 100644 index 0000000000..6197e50cca --- /dev/null +++ b/src/config/file.ts @@ -0,0 +1,34 @@ +import { ActionsEnv } from "../actions-util"; +import { + RepositoryProperties, + RepositoryPropertyName, +} from "../feature-flags/properties"; +import { Logger } from "../logging"; + +/** + * Gets the value that is configured for the configuration file, if any. + */ +export function getConfigFileInput( + logger: Logger, + actions: ActionsEnv, + repositoryProperties: Partial, +): string | undefined { + const input = actions.getOptionalInput("config-file"); + + if (input !== undefined) { + logger.info(`Using configuration file input from workflow: ${input}`); + return input; + } + + const propertyValue = + repositoryProperties[RepositoryPropertyName.CONFIG_FILE]; + + if (propertyValue !== undefined) { + logger.info( + `Using configuration file input from repository property: ${propertyValue}`, + ); + return propertyValue; + } + + return undefined; +} diff --git a/src/feature-flags/properties.test.ts b/src/feature-flags/properties.test.ts index 2676c2dcda..66526b1fb2 100644 --- a/src/feature-flags/properties.test.ts +++ b/src/feature-flags/properties.test.ts @@ -77,6 +77,7 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => { status: 200, url: "", data: [ + { property_name: "github-codeql-config-file", value: "owner/repo" }, { property_name: "github-codeql-extra-queries", value: "+queries" }, { property_name: "unknown-property", value: "something" }, ] satisfies properties.GitHubPropertiesResponse, @@ -87,7 +88,10 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => { logger, mockRepositoryNwo, ); - t.deepEqual(response, { "github-codeql-extra-queries": "+queries" }); + t.deepEqual(response, { + "github-codeql-config-file": "owner/repo", + "github-codeql-extra-queries": "+queries", + }); }); test.serial("loadPropertiesFromApi parses true boolean property", async (t) => { diff --git a/src/feature-flags/properties.ts b/src/feature-flags/properties.ts index 12ba280bec..e239c71947 100644 --- a/src/feature-flags/properties.ts +++ b/src/feature-flags/properties.ts @@ -10,6 +10,7 @@ export const GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-"; * Enumerates repository property names that have some meaning to us. */ export enum RepositoryPropertyName { + CONFIG_FILE = "github-codeql-config-file", DISABLE_OVERLAY = "github-codeql-disable-overlay", EXTRA_QUERIES = "github-codeql-extra-queries", FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs", @@ -17,6 +18,7 @@ export enum RepositoryPropertyName { /** Parsed types of the known repository properties. */ export type AllRepositoryProperties = { + [RepositoryPropertyName.CONFIG_FILE]: string; [RepositoryPropertyName.DISABLE_OVERLAY]: boolean; [RepositoryPropertyName.EXTRA_QUERIES]: string; [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: boolean; @@ -27,6 +29,7 @@ export type RepositoryProperties = Partial; /** Maps known repository properties to the type we expect to get from the API. */ export type RepositoryPropertyApiType = { + [RepositoryPropertyName.CONFIG_FILE]: string; [RepositoryPropertyName.DISABLE_OVERLAY]: string; [RepositoryPropertyName.EXTRA_QUERIES]: string; [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string; @@ -74,6 +77,7 @@ const booleanProperty = { const repositoryPropertyParsers: { [K in RepositoryPropertyName]: PropertyInfo; } = { + [RepositoryPropertyName.CONFIG_FILE]: stringProperty, [RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty, [RepositoryPropertyName.EXTRA_QUERIES]: stringProperty, [RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty, diff --git a/src/init-action.ts b/src/init-action.ts index b7593a51cc..9c9b45556b 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -9,6 +9,7 @@ import { v4 as uuidV4 } from "uuid"; import { FileCmdNotFoundError, + getActionsEnv, getActionVersion, getFileType, getOptionalInput, @@ -24,6 +25,7 @@ import { shouldRestoreCache, } from "./caching-utils"; import { CodeQL } from "./codeql"; +import { getConfigFileInput } from "./config/file"; import * as configUtils from "./config-utils"; import { DependencyCacheRestoreStatusReport, @@ -207,6 +209,7 @@ async function run(startedAt: Date) { // possible, and only use safe functions outside. const logger = getActionsLogger(); + const actionsEnv = getActionsEnv(); let apiDetails: GitHubApiCombinedDetails; let config: configUtils.Config | undefined; @@ -251,6 +254,7 @@ async function run(startedAt: Date) { repositoryNwo, logger, ); + const repositoryProperties = repositoryPropertiesResult.orElse({}); // Create a unique identifier for this run. const jobRunUuid = uuidV4(); @@ -259,7 +263,7 @@ async function run(startedAt: Date) { core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true"); - configFile = getOptionalInput("config-file"); + configFile = getConfigFileInput(logger, actionsEnv, repositoryProperties); // path.resolve() respects the intended semantics of source-root. If // source-root is relative, it is relative to the GITHUB_WORKSPACE. If @@ -350,7 +354,6 @@ async function run(startedAt: Date) { analysisKinds = await getAnalysisKinds(logger, features); const debugMode = getOptionalInput("debug") === "true" || core.isDebug(); - const repositoryProperties = repositoryPropertiesResult.orElse({}); const fileCoverageResult = await getFileCoverageInformationEnabled( debugMode, codeql, diff --git a/src/testing-utils.ts b/src/testing-utils.ts index 1702d6835a..2660c21a69 100644 --- a/src/testing-utils.ts +++ b/src/testing-utils.ts @@ -10,7 +10,7 @@ import test, { import nock from "nock"; import * as sinon from "sinon"; -import { getActionVersion } from "./actions-util"; +import { ActionsEnv, getActionVersion } from "./actions-util"; import { AnalysisKind } from "./analyses"; import * as apiClient from "./api-client"; import { GitHubApiDetails } from "./api-client"; @@ -172,6 +172,15 @@ export function makeMacro( return wrapper; } +/** + * Gets an `ActionsEnv` instance for use in tests. + */ +export function getTestActionsEnv(): ActionsEnv { + return { + getOptionalInput: () => undefined, + }; +} + /** * Default values for environment variables typically set in an Actions * environment. Tests can override individual variables by passing them in the