diff --git a/.github/workflows/__export-file-baseline-information.yml b/.github/workflows/__export-file-baseline-information.yml index 4ce8b40285..acdb099087 100644 --- a/.github/workflows/__export-file-baseline-information.yml +++ b/.github/workflows/__export-file-baseline-information.yml @@ -124,4 +124,5 @@ jobs: env: CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS: false CODEQL_ACTION_SUBLANGUAGE_FILE_COVERAGE: true + CODEQL_ACTION_PER_LANGUAGE_BUNDLES: false CODEQL_ACTION_TEST_MODE: true diff --git a/lib/entry-points.js b/lib/entry-points.js index ad7ac988a1..9b1949238d 100644 --- a/lib/entry-points.js +++ b/lib/entry-points.js @@ -172086,7 +172086,7 @@ var featureConfig = { envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS_SKIP_RESOURCE_CHECKS", minimumVersion: void 0 }, - ["per_language_bundles" /* PerLanguageBundles */]: { + ["per_language_bundles_v2" /* PerLanguageBundles */]: { defaultValue: false, envVar: "CODEQL_ACTION_PER_LANGUAGE_BUNDLES", minimumVersion: void 0 @@ -173213,6 +173213,13 @@ async function loadRepositoryProperties(repositoryNwo, logger2) { } // src/config/db-config.ts +var defaultSuites = /* @__PURE__ */ new Set([ + "security-experimental", + "security-extended", + "security-and-quality", + "code-quality", + "code-scanning" +]); var ORG_SCHEMA = { /** An array of model pack names. */ "model-packs": optional(array(string)) @@ -173226,12 +173233,21 @@ var DEFAULT_SETUP_CONFIG_SCHEMA = { object(DEFAULT_SETUP_SCHEMA) ) }; +function checkDefaultSetupConfig(config) { + return checkSchema(DEFAULT_SETUP_CONFIG_SCHEMA, config); +} +function matchesDefaultSetupConfigSchema(config) { + if (!isObject2(config)) { + return false; + } + const result = checkDefaultSetupConfig(config); + return result.valid && result.unknownKeys.length === 0; +} function mergeDefaultSetupAndUserConfigs(logger2, fromConfigInput, fromConfigFile) { logger2.debug( "Combining configuration files from 'config' and 'config-file' inputs" ); - const schemaCheckResult = checkSchema( - DEFAULT_SETUP_CONFIG_SCHEMA, + const schemaCheckResult = checkDefaultSetupConfig( fromConfigInput ); if (schemaCheckResult.invalidKeys.length > 0) { @@ -173388,52 +173404,41 @@ async function calculateAugmentation(rawPacksInput, rawQueriesInput, repositoryP languages, packsInputCombines ); - const queriesInputCombines = shouldCombine(rawQueriesInput); - const queriesInput = parseQueriesFromInput( - rawQueriesInput, - queriesInputCombines + const queries = parseQueriesFromInput(rawQueriesInput); + const repoPropertyQueries = parseQueriesFromInput( + repositoryProperties["github-codeql-extra-queries" /* EXTRA_QUERIES */], + "github-codeql-extra-queries" /* EXTRA_QUERIES */ ); - const repoExtraQueries = repositoryProperties["github-codeql-extra-queries" /* EXTRA_QUERIES */]; - const repoExtraQueriesCombines = shouldCombine(repoExtraQueries); - const repoPropertyQueries = { - combines: repoExtraQueriesCombines, - input: parseQueriesFromInput( - repoExtraQueries, - repoExtraQueriesCombines, - new ConfigurationError( - getRepoPropertyError( - "github-codeql-extra-queries" /* EXTRA_QUERIES */, - getEmptyCombinesError() - ) - ) - ) - }; return { packsInputCombines, packsInput: packsInput?.[languages[0]], - queriesInput, - queriesInputCombines, + queriesInput: queries.input, + queriesInputCombines: queries.combines, repoPropertyQueries }; } -function parseQueriesFromInput(rawQueriesInput, queriesInputCombines, errorToThrow) { - if (!rawQueriesInput) { - return void 0; +function parseQueriesFromInput(value, repositoryProperty) { + const combines = shouldCombine(value); + if (!value) { + return { combines, input: void 0 }; } - const trimmedInput = queriesInputCombines ? rawQueriesInput.trim().slice(1).trim() : rawQueriesInput?.trim() ?? ""; - if (queriesInputCombines && trimmedInput.length === 0) { - if (errorToThrow) { - throw errorToThrow; - } + const trimmedInput = combines ? value.trim().slice(1).trim() : value.trim(); + if (combines && trimmedInput.length === 0) { throw new ConfigurationError( - getConfigFilePropertyError( + repositoryProperty !== void 0 ? getRepoPropertyError( + repositoryProperty, + getEmptyCombinesError() + ) : getConfigFilePropertyError( void 0, "queries", "A '+' was used in the 'queries' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs." ) ); } - return trimmedInput.split(",").map((query) => ({ uses: query.trim() })); + return { + combines, + input: trimmedInput.split(",").map((query) => ({ uses: query.trim() })) + }; } function combineQueries(logger2, config, augmentationProperties) { const result = []; @@ -174815,20 +174820,22 @@ async function applyIncrementalAnalysisSettings(config, hasDiffRanges, codeql, l }); } } -async function determineUserConfig(action, tempDir, inputs) { - const validateConfig = await action.features.getValue( - "validate_db_config" /* ValidateDbConfig */ +async function parseConfigInput({ logger: logger2, features }, configInput) { + if (configInput === void 0) { + return void 0; + } + return parseUserConfig( + logger2, + "`config` input", + configInput, + await features.getValue("validate_db_config" /* ValidateDbConfig */) ); - if (inputs.configInput) { +} +async function determineUserConfig(action, tempDir, inputs) { + if (inputs.configInput !== void 0) { const computedConfigPath = userConfigFromActionPath(tempDir); const allowMergeConfigs = () => action.features.getValue("allow_merge_config_files" /* AllowMergeConfigFiles */); if (inputs.configFile && isDefaultSetup(action.env) && await allowMergeConfigs()) { - const fromConfigInput = parseUserConfig( - action.logger, - "`config` input", - inputs.configInput, - validateConfig - ); const fromConfigFile = await loadUserConfig( action, inputs.configFile, @@ -174838,7 +174845,7 @@ async function determineUserConfig(action, tempDir, inputs) { ); const mergedConfig = mergeDefaultSetupAndUserConfigs( action.logger, - fromConfigInput, + inputs.configInput, fromConfigFile ); fs10.writeFileSync(computedConfigPath, dump(mergedConfig)); @@ -174853,11 +174860,12 @@ async function determineUserConfig(action, tempDir, inputs) { `Both a config file and config input were provided. Ignoring config file.` ); } - fs10.writeFileSync(computedConfigPath, inputs.configInput); + fs10.writeFileSync(computedConfigPath, dump(inputs.configInput)); inputs.configFile = computedConfigPath; action.logger.debug( `Using config from action input: ${inputs.configFile}` ); + return inputs.configInput; } } if (!inputs.configFile) { @@ -175561,6 +175569,33 @@ var PER_LANGUAGE_BUNDLE_LANGUAGES = { ["osx64" /* Osx64 */]: /* @__PURE__ */ new Set(["swift" /* swift */]), ["win64" /* Win64 */]: /* @__PURE__ */ new Set() }; +function getOtherLanguagePacksReason(inputs) { + if (inputs.configFile !== void 0) { + return `the configuration file '${inputs.configFile}' may use queries that need library packs for other languages`; + } + if (inputs.configInput !== void 0 && !matchesDefaultSetupConfigSchema(inputs.configInput)) { + return "the 'config' input may use queries that need library packs for other languages"; + } + const query = findNonBuiltInQuery( + parseQueriesFromInput(inputs.queriesInput).input + ); + if (query !== void 0) { + return `the query '${query}' from the 'queries' input may need library packs for other languages`; + } + const extraQuery = findNonBuiltInQuery( + parseQueriesFromInput( + inputs.extraQueriesProperty, + "github-codeql-extra-queries" /* EXTRA_QUERIES */ + ).input + ); + if (extraQuery !== void 0) { + return `the query '${extraQuery}' from the '${"github-codeql-extra-queries" /* EXTRA_QUERIES */}' repository property may need library packs for other languages`; + } + return void 0; +} +function findNonBuiltInQuery(queries) { + return queries?.find((query) => !defaultSuites.has(query.uses))?.uses; +} async function getPerLanguageBundleLanguage({ env, features, @@ -175568,6 +175603,7 @@ async function getPerLanguageBundleLanguage({ }, options) { const { rawLanguages, + otherLanguagePacksReason, cliVersion: cliVersion2, compressionMethod, platform: platform2, @@ -175578,8 +175614,11 @@ async function getPerLanguageBundleLanguage({ logger2.debug(`Not using a per-language CodeQL bundle since ${reason}.`); return void 0; }; - if (!await features.getValue("per_language_bundles" /* PerLanguageBundles */)) { - return explain(`the ${"per_language_bundles" /* PerLanguageBundles */} feature is disabled`); + if (!await features.getValue("per_language_bundles_v2" /* PerLanguageBundles */)) { + return explain("the feature is disabled"); + } + if (otherLanguagePacksReason !== void 0) { + return explain(otherLanguagePacksReason); } if (rawLanguages?.length !== 1) { return explain( @@ -176212,7 +176251,7 @@ async function resolveDefaultCliVersion(defaultCliVersion, rawLanguages, useOver } return defaultCliVersion.enabledVersions[0]; } -async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger2) { +async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, apiDetails, variant, tarSupportsZstd, features, logger2) { if (toolsInput && !isReservedToolsValue(toolsInput) && !toolsInput.startsWith("http")) { logger2.info(`Using CodeQL CLI from local path ${toolsInput}`); const compressionMethod2 = inferCompressionMethod(toolsInput); @@ -176265,6 +176304,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO bundle = await getLatestNightlyBundle( { env: getEnv(), features, logger: logger2 }, rawLanguages, + otherLanguagePacksReason, variant ); toolsInput = bundle.url; @@ -176432,6 +176472,7 @@ async function getCodeQLSource(toolsInput, defaultCliVersion, rawLanguages, useO { env: getEnv(), features, logger: logger2 }, { rawLanguages, + otherLanguagePacksReason, cliVersion: cliVersion2, compressionMethod, platform: platform2, @@ -176595,7 +176636,7 @@ function getCanonicalToolcacheVersion(cliVersion2, bundleVersion2, logger2) { } return cliVersion2; } -async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger2) { +async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2) { if (!await isBinaryAccessible("tar", logger2)) { throw new ConfigurationError( "Could not find tar in PATH, so unable to extract CodeQL bundle." @@ -176606,6 +176647,7 @@ async function setupCodeQLBundle(toolsInput, apiDetails, tempDir, variant, defau toolsInput, defaultCliVersion, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, apiDetails, variant, @@ -176704,7 +176746,7 @@ async function useZstdBundle(cliVersion2, tarSupportsZstd) { function getTempExtractionDir(tempDir) { return path13.join(tempDir, v4_default()); } -async function getLatestNightlyBundle(action, rawLanguages, variant) { +async function getLatestNightlyBundle(action, rawLanguages, otherLanguagePacksReason, variant) { const { logger: logger2 } = action; const zstdAvailability = await isZstdAvailable(logger2); const compressionMethod = await useZstdBundle( @@ -176714,6 +176756,7 @@ async function getLatestNightlyBundle(action, rawLanguages, variant) { const platform2 = getBundlePlatform(); const language = await getPerLanguageBundleLanguage(action, { rawLanguages, + otherLanguagePacksReason, cliVersion: void 0, compressionMethod, platform: platform2, @@ -176854,7 +176897,7 @@ function isDiskConfigurationError(e) { e.message.includes("EACCES") ); } -async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger2, checkVersion) { +async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2, checkVersion) { try { const { codeqlFolder, @@ -176868,6 +176911,7 @@ async function setupCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliV variant, defaultCliVersion, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2 @@ -177967,13 +178011,6 @@ ${extensionContents}` ); return diffRangeDir; } -var defaultSuites = /* @__PURE__ */ new Set([ - "security-experimental", - "security-extended", - "security-and-quality", - "code-quality", - "code-scanning" -]); function resolveQuerySuiteAlias(language, maybeSuite) { if (defaultSuites.has(maybeSuite)) { return `${language}-${maybeSuite}.qls`; @@ -179506,7 +179543,7 @@ var core15 = __toESM(require_core()); var toolrunner4 = __toESM(require_toolrunner()); var github3 = __toESM(require_github()); var io6 = __toESM(require_io()); -async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, useOverlayAwareDefaultCliVersion, features, logger2) { +async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVersion, rawLanguages, otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2) { logger2.startGroup("Setup CodeQL tools"); const { codeql, toolsDownloadStatusReport, toolsSource, toolsVersion } = await setupCodeQL( toolsInput, @@ -179515,6 +179552,7 @@ async function initCodeQL(toolsInput, apiDetails, tempDir, variant, defaultCliVe variant, defaultCliVersion, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2, @@ -179876,6 +179914,8 @@ async function combineSarifFilesUsingCLI(sarifFiles, gitHubVersion, features, lo codeQLDefaultVersionInfo, void 0, // rawLanguages: upload-lib does not run analysis + void 0, + // otherLanguagePacksReason: upload-lib does not run analysis false, // useOverlayAwareDefaultCliVersion: upload-lib does not run analysis features, @@ -186330,6 +186370,17 @@ async function run3(actionState) { const rawLanguages = getRawLanguagesNoAutodetect( getOptionalInput("languages") ); + const configInput = await parseConfigInput( + actionStateWithFeatures, + getOptionalInput("config") + ); + const queriesInput = getOptionalInput("queries"); + const otherLanguagePacksReason = getOtherLanguagePacksReason({ + configFile, + configInput, + queriesInput, + extraQueriesProperty: repositoryProperties["github-codeql-extra-queries" /* EXTRA_QUERIES */] + }); const useOverlayAwareDefaultCliVersion = analysisKinds?.length === 1 && analysisKinds[0] === "code-scanning" /* CodeScanning */; const initCodeQLResult = await initCodeQL( toolsInput?.value, @@ -186338,6 +186389,7 @@ async function run3(actionState) { gitHubVersion.type, codeQLDefaultVersionInfo, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger2 @@ -186376,13 +186428,13 @@ async function run3(actionState) { config = await initConfig2(actionStateWithFeatures, { analysisKinds, languagesInput: getOptionalInput("languages"), - queriesInput: getOptionalInput("queries"), + queriesInput, packsInput: getOptionalInput("packs"), buildModeInput: getOptionalInput("build-mode"), ramInput: getOptionalInput("ram"), configFile, dbLocation: getOptionalInput("db-location"), - configInput: getOptionalInput("config"), + configInput, dependencyCachingEnabled: getDependencyCachingEnabled(), // Debug mode is enabled if: // - The `init` Action is passed `debug: true`. @@ -187342,6 +187394,11 @@ async function run6(actionState) { gitHubVersion.type, codeQLDefaultVersionInfo, rawLanguages, + // CodeQL resolves the dependencies of queries that aren't in compiled packs from the bundle, + // so the queries that the workflow runs with this CLI may need library packs for languages + // other than those in the `languages` input. That input therefore only informs the choice of + // CLI version. + "the 'setup-codeql' Action can't tell whether the queries that the workflow runs will need library packs for other languages", analysisKinds.length === 1 && analysisKinds[0] === "code-scanning" /* CodeScanning */, features, logger2 diff --git a/pr-checks/checks/export-file-baseline-information.yml b/pr-checks/checks/export-file-baseline-information.yml index c5d5d12dda..ce22ec5d5f 100644 --- a/pr-checks/checks/export-file-baseline-information.yml +++ b/pr-checks/checks/export-file-baseline-information.yml @@ -11,6 +11,10 @@ installDotNet: true env: CODEQL_ACTION_SKIP_FILE_COVERAGE_ON_PRS: false CODEQL_ACTION_SUBLANGUAGE_FILE_COVERAGE: true + # To balance speed and coverage, we analyze only a single language (JavaScript), but use the + # combined bundle so we can test that baseline information is reported for each language in the + # multi-language source directory. + CODEQL_ACTION_PER_LANGUAGE_BUNDLES: false steps: - uses: ./../action/init id: init diff --git a/setup-codeql/action.yml b/setup-codeql/action.yml index 8d13eeaff0..14083b4f95 100644 --- a/setup-codeql/action.yml +++ b/setup-codeql/action.yml @@ -21,19 +21,16 @@ inputs: required: false languages: description: >- - A comma-separated list of CodeQL languages that will be analyzed in subsequent - `github/codeql-action/init` and `github/codeql-action/analyze` invocations. If specified, the - Action may use this list to select a CodeQL CLI version that is best suited to analyzing those - languages, for example by preferring a version that has a cached overlay-base database for the - specified languages. This input is not remembered and must also be passed to - `github/codeql-action/init`. + A comma-separated list of CodeQL languages that the installed CodeQL CLI will be used to + analyze. If specified, the Action may use this list to select a CodeQL CLI version that is + best suited to analyzing those languages, for example by preferring a version that has a + cached overlay-base database for the specified languages. required: false analysis-kinds: description: >- - [Internal] A comma-separated list of analysis kinds that subsequent - `github/codeql-action/init` invocations will enable. If specified, the Action may use this - list to select a CodeQL CLI version that is best suited to those analysis kinds. This input is - not remembered and must also be passed to `github/codeql-action/init`. + [Internal] A comma-separated list of analysis kinds that the installed CodeQL CLI will be used + for. If specified, the Action may use this list to select a CodeQL CLI version that is best + suited to those analysis kinds. Available options are the same as for the `analysis-kinds` input on the `init` Action. default: 'code-scanning' diff --git a/src/analyze.test.ts b/src/analyze.test.ts index 7523d239bf..80fc7a5474 100644 --- a/src/analyze.test.ts +++ b/src/analyze.test.ts @@ -7,12 +7,12 @@ import * as sinon from "sinon"; import { CodeQuality, CodeScanning, RiskAssessment } from "./analyses"; import { runQueries, - defaultSuites, resolveQuerySuiteAlias, addSarifExtension, diffRangeExtensionPackContents, } from "./analyze"; import { createStubCodeQL } from "./codeql"; +import { defaultSuites } from "./config/db-config"; import { Feature } from "./feature-flags"; import { BuiltInLanguage } from "./languages"; import { getRunnerLogger } from "./logging"; diff --git a/src/analyze.ts b/src/analyze.ts index 8f90711682..d585fd6d83 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -9,6 +9,7 @@ import { getTemporaryDirectory } from "./actions-util"; import * as analyses from "./analyses"; import { setupCppAutobuild } from "./autobuild"; import { type CodeQL } from "./codeql"; +import { defaultSuites } from "./config/db-config"; import * as configUtils from "./config-utils"; import { getCsharpTempDependencyDir, @@ -357,15 +358,6 @@ dataExtensions: return diffRangeDir; } -// A set of default query suite names that are understood by the CLI. -export const defaultSuites: Set = new Set([ - "security-experimental", - "security-extended", - "security-and-quality", - "code-quality", - "code-scanning", -]); - /** * If `maybeSuite` is the name of a default query suite, it is resolved into the corresponding * query suite name for the given `language`. Otherwise, `maybeSuite` is returned as is. diff --git a/src/codeql.test.ts b/src/codeql.test.ts index 5f2eb31156..3237d57723 100644 --- a/src/codeql.test.ts +++ b/src/codeql.test.ts @@ -99,6 +99,7 @@ async function installIntoToolcache({ ? { enabledVersions: [{ cliVersion, tagName }] } : SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), getRunnerLogger(true), @@ -172,6 +173,7 @@ test.serial( util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -207,6 +209,7 @@ test.serial( util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -246,6 +249,7 @@ test.serial( util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -355,6 +359,7 @@ for (const { util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -397,6 +402,7 @@ for (const toolcacheVersion of [ util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -441,6 +447,7 @@ test.serial( ], }, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -487,6 +494,7 @@ test.serial( ], }, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -526,6 +534,7 @@ test.serial( util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), @@ -567,6 +576,7 @@ test.serial( util.GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, getRunnerLogger(true), diff --git a/src/codeql.ts b/src/codeql.ts index a4da8ad68a..6df604e394 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -302,6 +302,9 @@ export function isDiskConfigurationError(e: unknown): boolean { * @param variant * @param defaultCliVersion * @param rawLanguages Raw set of languages. + * @param otherLanguagePacksReason Why the CodeQL CLI may need packs for languages other than + * `rawLanguages`, or `undefined` if it won't. If defined, the combined bundle is used. See + * `PerLanguageBundleOptions.otherLanguagePacksReason`. * @param useOverlayAwareDefaultCliVersion Whether to select an overlay-aware default CLI version. * @param features Information about the features that are enabled. * @param logger @@ -316,6 +319,7 @@ export async function setupCodeQL( variant: util.GitHubVariant, defaultCliVersion: CodeQLDefaultVersionInfo, rawLanguages: string[] | undefined, + otherLanguagePacksReason: string | undefined, useOverlayAwareDefaultCliVersion: boolean, features: FeatureEnablement, logger: Logger, @@ -339,6 +343,7 @@ export async function setupCodeQL( variant, defaultCliVersion, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger, diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index 29d72f3af4..3ea71ebc42 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -473,6 +473,9 @@ const simpleConfigFileContents = ` queries: - uses: ./foo_file`; +/** The configuration in `simpleConfigFileContents`, as parsed from the `config` input. */ +const simpleConfigInput = yaml.load(simpleConfigFileContents) as UserConfig; + /** A less minimal configuration file. */ const otherConfigFileContents = ` name: my config @@ -591,7 +594,7 @@ test.serial( createTestInitConfigInputs({ languagesInput, configFile: configFilePath, - configInput, + configInput: yaml.load(configInput) as UserConfig, tempDir, codeql, workspacePath: tempDir, @@ -2343,6 +2346,40 @@ test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only r ]); }); +test("parseConfigInput - returns undefined when the input isn't set", async (t) => { + await callee(configUtils.parseConfigInput) + .withArgs(undefined) + .passes(t.is, undefined); +}); + +test("parseConfigInput - parses the input as YAML", async (t) => { + await callee(configUtils.parseConfigInput) + .withArgs(simpleConfigFileContents) + .passes(t.deepEqual, { + name: "my config", + queries: [{ uses: "./foo_file" }], + }); +}); + +test("parseConfigInput - throws a ConfigurationError naming the input if it isn't valid YAML", async (t) => { + await callee(configUtils.parseConfigInput) + .withArgs("queries: [") + .throws(t, { + instanceOf: ConfigurationError, + message: /^Cannot parse "`config` input"/, + }); +}); + +test("parseConfigInput - throws a ConfigurationError naming the input if validation fails", async (t) => { + await callee(configUtils.parseConfigInput) + .withFeatures([Feature.ValidateDbConfig]) + .withArgs("queries: 1") + .throws(t, { + instanceOf: ConfigurationError, + message: /^The configuration file "`config` input" is invalid/, + }); +}); + test("determineUserConfig - empty config when neither input is specified", async (t) => { await withTmpDir(async (tmpDir) => { const target = callee(configUtils.determineUserConfig) @@ -2413,7 +2450,7 @@ test("determineUserConfig - loads config input", async (t) => { const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const inputs = createTestInitConfigInputs({ - configInput: simpleConfigFileContents, + configInput: simpleConfigInput, configFile: undefined, workspacePath: tmpDir, }); @@ -2423,17 +2460,15 @@ test("determineUserConfig - loads config input", async (t) => { await target // The input source and path of the generated config file should have been logged. - .logs( - t, - "Using config from action input:", - `Using configuration file: ${expectedConfigPath}`, - ) - // The message about no configuration input and - // the warning about both inputs should not have been logged. + .logs(t, `Using config from action input: ${expectedConfigPath}`) + // The message about no configuration input and the warning about both inputs should not have + // been logged. The generated config file isn't loaded, since the `config` input has already + // been parsed. .notLogs( t, "No configuration file was provided", "Both a config file and config input were provided. Ignoring config file.", + `Using configuration file: ${expectedConfigPath}`, ) // The loaded configuration should match `simpleConfigFileContents`. .passes(t.deepEqual, { @@ -2452,7 +2487,7 @@ test("determineUserConfig - ignores config file input when both specified", asyn const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const inputs = createTestInitConfigInputs({ - configInput: simpleConfigFileContents, + configInput: simpleConfigInput, configFile: configFilePath, workspacePath: tmpDir, }); @@ -2466,10 +2501,14 @@ test("determineUserConfig - ignores config file input when both specified", asyn .logs( t, `Using config from action input: ${expectedConfigPath}`, - `Using configuration file: ${expectedConfigPath}`, "Both a config file and config input were provided. Ignoring config file.", ) - .notLogs(t, "No configuration file was provided") + // The generated config file isn't loaded, since the `config` input has already been parsed. + .notLogs( + t, + "No configuration file was provided", + `Using configuration file: ${expectedConfigPath}`, + ) // The loaded configuration should match `simpleConfigFileContents`. .passes(t.deepEqual, { name: "my config", @@ -2481,12 +2520,12 @@ test("determineUserConfig - ignores config file input when both specified", asyn }); }); -/** A `config` input that we might get from Default Setup. */ -const defaultSetupConfigInput = ` +/** The configuration from a `config` input that we might get from Default Setup. */ +const defaultSetupConfigInput = yaml.load(` threat-models: [local, remote] default-setup: org: - model-packs: [foo, bar]`; + model-packs: [foo, bar]`) as UserConfig; test("determineUserConfig - merges configs if FF is enabled in Default Setup", async (t) => { await withTmpDir(async (tmpDir) => { @@ -2555,7 +2594,7 @@ test("determineUserConfig - ignores config file input in Default Setup if FF is .withArgs( tmpDir, createTestInitConfigInputs({ - configInput: simpleConfigFileContents, + configInput: simpleConfigInput, configFile: configFilePath, workspacePath: tmpDir, }), @@ -2565,10 +2604,14 @@ test("determineUserConfig - ignores config file input in Default Setup if FF is .logs( t, `Using config from action input: ${expectedConfigPath}`, - `Using configuration file: ${expectedConfigPath}`, "Both a config file and config input were provided. Ignoring config file.", ) - .notLogs(t, "No configuration file was provided") + // The generated config file isn't loaded, since the `config` input has already been parsed. + .notLogs( + t, + "No configuration file was provided", + `Using configuration file: ${expectedConfigPath}`, + ) .passes(t.deepEqual, { name: "my config", queries: [{ uses: "./foo_file" }], @@ -2587,7 +2630,7 @@ test("determineUserConfig - ignores config file input outside Default Setup if F .withArgs( tmpDir, createTestInitConfigInputs({ - configInput: simpleConfigFileContents, + configInput: simpleConfigInput, configFile: configFilePath, workspacePath: tmpDir, }), @@ -2597,10 +2640,14 @@ test("determineUserConfig - ignores config file input outside Default Setup if F .logs( t, `Using config from action input: ${expectedConfigPath}`, - `Using configuration file: ${expectedConfigPath}`, "Both a config file and config input were provided. Ignoring config file.", ) - .notLogs(t, "No configuration file was provided") + // The generated config file isn't loaded, since the `config` input has already been parsed. + .notLogs( + t, + "No configuration file was provided", + `Using configuration file: ${expectedConfigPath}`, + ) .passes(t.deepEqual, { name: "my config", queries: [{ uses: "./foo_file" }], diff --git a/src/config-utils.ts b/src/config-utils.ts index 4dd14b2909..ba15138dcc 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -337,7 +337,8 @@ export interface InitConfigInputs { packsInput: string | undefined; configFile: string | undefined; dbLocation: string | undefined; - configInput: string | undefined; + /** The configuration from the `config` input. */ + configInput: UserConfig | undefined; buildModeInput: string | undefined; ramInput: string | undefined; dependencyCachingEnabled: string | undefined; @@ -1043,6 +1044,29 @@ export async function applyIncrementalAnalysisSettings( } } +/** + * Parses the `config` input, which contains a configuration in YAML. + * + * @returns The configuration, or `undefined` if the input isn't set. Unless configuration validation + * is enabled, the configuration might not be a mapping. + * @throws A `ConfigurationError` if the input isn't valid YAML or, when configuration validation is + * enabled, isn't a valid configuration. + */ +export async function parseConfigInput( + { logger, features }: ActionState<["Logger", "FeatureFlags"]>, + configInput: string | undefined, +): Promise { + if (configInput === undefined) { + return undefined; + } + return parseUserConfig( + logger, + "`config` input", + configInput, + await features.getValue(Feature.ValidateDbConfig), + ); +} + /** * Determines where to load the `UserConfig` for the CLI from and loads it. * @@ -1057,17 +1081,13 @@ export async function determineUserConfig( tempDir: string, inputs: InitConfigInputs, ): Promise { - const validateConfig = await action.features.getValue( - Feature.ValidateDbConfig, - ); - // We have the following cases: // 1. A `config` or `config-file` input is provided, but not both: use the provided one. // 2. Both are provided and we are in an advanced workflow: ignore the `config-file` input. // 3. Both are provided and we are in Default Setup: the `config` input uses a limited // set of options, which are supported by `mergeDefaultSetupAndUserConfigs`, // and we merge the two configs. - if (inputs.configInput) { + if (inputs.configInput !== undefined) { const computedConfigPath = userConfigFromActionPath(tempDir); // Get a function which enables us to determine whether the FF that allows us to @@ -1084,12 +1104,6 @@ export async function determineUserConfig( ) { // If the FF is enabled and we are in Default Setup, combine the supported // configuration file properties and write the result to disk. - const fromConfigInput = parseUserConfig( - action.logger, - "`config` input", - inputs.configInput, - validateConfig, - ); const fromConfigFile = await loadUserConfig( action, inputs.configFile, @@ -1102,7 +1116,7 @@ export async function determineUserConfig( // the CLI or other CodeQL Action steps. const mergedConfig = mergeDefaultSetupAndUserConfigs( action.logger, - fromConfigInput, + inputs.configInput, fromConfigFile, ); fs.writeFileSync(computedConfigPath, yaml.dump(mergedConfig)); @@ -1122,12 +1136,13 @@ export async function determineUserConfig( ); } - // Write the `config` input straight to disk. - fs.writeFileSync(computedConfigPath, inputs.configInput); + // Write the `config` input to disk. + fs.writeFileSync(computedConfigPath, yaml.dump(inputs.configInput)); inputs.configFile = computedConfigPath; action.logger.debug( `Using config from action input: ${inputs.configFile}`, ); + return inputs.configInput; } } diff --git a/src/config/db-config.test.ts b/src/config/db-config.test.ts index 63d9d2ffec..76fa918015 100644 --- a/src/config/db-config.test.ts +++ b/src/config/db-config.test.ts @@ -625,3 +625,73 @@ test("mergeDefaultSetupAndUserConfigs - warns about invalid keys from Default Se `Invalid keys in Default Setup configuration: ${expectedInvalidKeys}`, ]); }); + +/** Parses `contents` as a configuration without validating it. */ +function parseUnvalidatedConfig(contents: string): dbConfig.UserConfig { + return dbConfig.parseUserConfig( + getRunnerLogger(true), + "test", + contents, + false, + ); +} + +test("matchesDefaultSetupConfigSchema - returns true for configurations that only use Default Setup properties", (t) => { + for (const contents of [ + [ + "default-setup:", + " org:", + " model-packs: [ github/immutable-actions-list@0.0.1 ]", + "threat-models: [ ]", + ].join("\n"), + "threat-models: [ local ]", + "{}", + ]) { + t.true( + dbConfig.matchesDefaultSetupConfigSchema( + parseUnvalidatedConfig(contents), + ), + contents, + ); + } +}); + +test("matchesDefaultSetupConfigSchema - returns false for configurations that use other properties", (t) => { + for (const contents of [ + "queries: [ { uses: ./queries/show_ifs.ql } ]", + "paths-ignore: [ tests ]", + "default-setup: { org: { model-packs: [], queries: [] } }", + ]) { + t.false( + dbConfig.matchesDefaultSetupConfigSchema( + parseUnvalidatedConfig(contents), + ), + contents, + ); + } +}); + +test("matchesDefaultSetupConfigSchema - returns false for invalid Default Setup properties", (t) => { + for (const contents of [ + "threat-models: local", + "default-setup: { org: { model-packs: [ 1 ] } }", + ]) { + t.false( + dbConfig.matchesDefaultSetupConfigSchema( + parseUnvalidatedConfig(contents), + ), + contents, + ); + } +}); + +test("matchesDefaultSetupConfigSchema - returns false for configurations that aren't mappings", (t) => { + for (const contents of ["- threat-models", "local", "null"]) { + t.false( + dbConfig.matchesDefaultSetupConfigSchema( + parseUnvalidatedConfig(contents), + ), + contents, + ); + } +}); diff --git a/src/config/db-config.ts b/src/config/db-config.ts index 7b5bdbd8ce..eb2cf15eb7 100644 --- a/src/config/db-config.ts +++ b/src/config/db-config.ts @@ -33,6 +33,15 @@ export interface QuerySpec { uses: string; } +// A set of default query suite names that are understood by the CLI. +export const defaultSuites: Set = new Set([ + "security-experimental", + "security-extended", + "security-and-quality", + "code-quality", + "code-scanning", +]); + const ORG_SCHEMA = { /** An array of model pack names. */ "model-packs": json.optional(json.array(json.string)), @@ -77,7 +86,11 @@ export interface UserConfig { "default-setup"?: DefaultSetupConfig; } -/** A subset of the `UserConfig` schema that is used by Default Setup. */ +/** + * A subset of the `UserConfig` schema that is used by Default Setup. None of these properties may + * add queries, since a per-language CodeQL bundle can be used with a `config` input that only sets + * them. + */ const DEFAULT_SETUP_CONFIG_SCHEMA = { "threat-models": json.optional(json.array(json.string)), "default-setup": json.optional( @@ -85,6 +98,31 @@ const DEFAULT_SETUP_CONFIG_SCHEMA = { ), } as const satisfies json.Schema; +/** + * Checks `config` against the properties that Default Setup sets in the `config` input. The result + * lists any other properties in `unknownKeys`, and any properties with invalid values in + * `invalidKeys`. + */ +function checkDefaultSetupConfig( + config: json.UnvalidatedObject, +): json.CheckSchemaResult { + return json.checkSchema(DEFAULT_SETUP_CONFIG_SCHEMA, config); +} + +/** + * Returns whether `config` is a mapping that only sets the properties that Default Setup sets in + * the `config` input, to valid values. + */ +export function matchesDefaultSetupConfigSchema(config: UserConfig): boolean { + // Unless validation is enabled, `parseUserConfig` doesn't check that the YAML is a mapping. + if (!json.isObject(config)) { + return false; + } + const result = checkDefaultSetupConfig(config as json.UnvalidatedObject); + // `valid` doesn't account for unknown properties. + return result.valid && result.unknownKeys.length === 0; +} + /** * Merges supported properties from two configuration files. This is intended only for * use with merging the `config` input provided by Default Setup with a potentially @@ -107,8 +145,7 @@ export function mergeDefaultSetupAndUserConfigs( // Check for unexpected keys in the configuration from the `config` input // that was provided by Default Setup. This should only contain the keys // we would expect to receive from Default Setup. - const schemaCheckResult = json.checkSchema( - DEFAULT_SETUP_CONFIG_SCHEMA, + const schemaCheckResult = checkDefaultSetupConfig( fromConfigInput as json.UnvalidatedObject, ); @@ -437,63 +474,59 @@ export async function calculateAugmentation( languages, packsInputCombines, ); - const queriesInputCombines = shouldCombine(rawQueriesInput); - const queriesInput = parseQueriesFromInput( - rawQueriesInput, - queriesInputCombines, + const queries = parseQueriesFromInput(rawQueriesInput); + const repoPropertyQueries = parseQueriesFromInput( + repositoryProperties[RepositoryPropertyName.EXTRA_QUERIES], + RepositoryPropertyName.EXTRA_QUERIES, ); - const repoExtraQueries = - repositoryProperties[RepositoryPropertyName.EXTRA_QUERIES]; - const repoExtraQueriesCombines = shouldCombine(repoExtraQueries); - const repoPropertyQueries = { - combines: repoExtraQueriesCombines, - input: parseQueriesFromInput( - repoExtraQueries, - repoExtraQueriesCombines, - new ConfigurationError( - errorMessages.getRepoPropertyError( - RepositoryPropertyName.EXTRA_QUERIES, - errorMessages.getEmptyCombinesError(), - ), - ), - ), - }; - return { packsInputCombines, packsInput: packsInput?.[languages[0]], - queriesInput, - queriesInputCombines, + queriesInput: queries.input, + queriesInputCombines: queries.combines, repoPropertyQueries, }; } -function parseQueriesFromInput( - rawQueriesInput: string | undefined, - queriesInputCombines: boolean, - errorToThrow?: ConfigurationError, -) { - if (!rawQueriesInput) { - return undefined; +/** + * Parses a comma-separated list of queries, which may start with '+'. `combines` is whether it + * starts with '+', and `input` holds the queries, or is `undefined` if `value` is unset or empty. + * Entries aren't validated, so an empty entry becomes `{ uses: "" }`. + * + * @param value The list of queries. + * @param repositoryProperty The repository property that `value` comes from, if any. Errors name + * this property, or the `queries` input if it's unset. + * @throws A `ConfigurationError` if `value` is a '+' with no queries after it. + */ +export function parseQueriesFromInput( + value: string | undefined, + repositoryProperty?: RepositoryPropertyName, +): Augmentation { + const combines = shouldCombine(value); + if (!value) { + return { combines, input: undefined }; } - const trimmedInput = queriesInputCombines - ? rawQueriesInput.trim().slice(1).trim() - : (rawQueriesInput?.trim() ?? ""); - if (queriesInputCombines && trimmedInput.length === 0) { - if (errorToThrow) { - throw errorToThrow; - } + const trimmedInput = combines ? value.trim().slice(1).trim() : value.trim(); + if (combines && trimmedInput.length === 0) { throw new ConfigurationError( - errorMessages.getConfigFilePropertyError( - undefined, - "queries", - "A '+' was used in the 'queries' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs.", - ), + repositoryProperty !== undefined + ? errorMessages.getRepoPropertyError( + repositoryProperty, + errorMessages.getEmptyCombinesError(), + ) + : errorMessages.getConfigFilePropertyError( + undefined, + "queries", + "A '+' was used in the 'queries' input to specify that you wished to add some packs to your CodeQL analysis. However, no packs were specified. Please either remove the '+' or specify some packs.", + ), ); } - return trimmedInput.split(",").map((query) => ({ uses: query.trim() })); + return { + combines, + input: trimmedInput.split(",").map((query) => ({ uses: query.trim() })), + }; } /** diff --git a/src/feature-flags.ts b/src/feature-flags.ts index afddaea2a4..59b50e2dfd 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -168,7 +168,7 @@ export enum Feature { * Controls whether we may download a bundle containing only the single language being analysed, * rather than the combined bundle that contains every language. */ - PerLanguageBundles = "per_language_bundles", + PerLanguageBundles = "per_language_bundles_v2", QaTelemetryEnabled = "qa_telemetry_enabled", /** Routes (some) API requests through the registry proxy. */ ProxyApiRequests = "proxy_api_requests", diff --git a/src/init-action.ts b/src/init-action.ts index 41e0b8629d..2b3c95e0ba 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -40,7 +40,10 @@ import { } from "./diagnostics"; import { ActionsEnvVars, EnvVar } from "./environment"; import { Feature, FeatureEnablement, initFeatures } from "./feature-flags"; -import { loadRepositoryProperties } from "./feature-flags/properties"; +import { + loadRepositoryProperties, + RepositoryPropertyName, +} from "./feature-flags/properties"; import { checkInstallPython311, checkPacksForOverlayCompatibility, @@ -58,6 +61,7 @@ import { OverlayBaseDatabaseDownloadStats, } from "./overlay/caching"; import { OverlayDatabaseMode } from "./overlay/overlay-database-mode"; +import { getOtherLanguagePacksReason } from "./per-language-bundles"; import { getRepositoryNwo } from "./repository"; import { ToolsSource } from "./setup-codeql"; import { @@ -302,6 +306,18 @@ async function run( const rawLanguages = configUtils.getRawLanguagesNoAutodetect( getOptionalInput("languages"), ); + const configInput = await configUtils.parseConfigInput( + actionStateWithFeatures, + getOptionalInput("config"), + ); + const queriesInput = getOptionalInput("queries"); + const otherLanguagePacksReason = getOtherLanguagePacksReason({ + configFile, + configInput, + queriesInput, + extraQueriesProperty: + repositoryProperties[RepositoryPropertyName.EXTRA_QUERIES], + }); const useOverlayAwareDefaultCliVersion = analysisKinds?.length === 1 && analysisKinds[0] === AnalysisKind.CodeScanning; @@ -312,6 +328,7 @@ async function run( gitHubVersion.type, codeQLDefaultVersionInfo, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger, @@ -362,13 +379,13 @@ async function run( config = await initConfig(actionStateWithFeatures, { analysisKinds, languagesInput: getOptionalInput("languages"), - queriesInput: getOptionalInput("queries"), + queriesInput, packsInput: getOptionalInput("packs"), buildModeInput: getOptionalInput("build-mode"), ramInput: getOptionalInput("ram"), configFile, dbLocation: getOptionalInput("db-location"), - configInput: getOptionalInput("config"), + configInput, dependencyCachingEnabled: getDependencyCachingEnabled(), // Debug mode is enabled if: // - The `init` Action is passed `debug: true`. diff --git a/src/init.ts b/src/init.ts index c6a258e58c..d757e29976 100644 --- a/src/init.ts +++ b/src/init.ts @@ -40,6 +40,7 @@ export async function initCodeQL( variant: util.GitHubVariant, defaultCliVersion: CodeQLDefaultVersionInfo, rawLanguages: string[] | undefined, + otherLanguagePacksReason: string | undefined, useOverlayAwareDefaultCliVersion: boolean, features: FeatureEnablement, logger: Logger, @@ -58,6 +59,7 @@ export async function initCodeQL( variant, defaultCliVersion, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, features, logger, diff --git a/src/per-language-bundles.test.ts b/src/per-language-bundles.test.ts index 0330b51a63..09500d2f77 100644 --- a/src/per-language-bundles.test.ts +++ b/src/per-language-bundles.test.ts @@ -4,9 +4,11 @@ import { ActionsEnvVars } from "./environment"; import { Feature } from "./feature-flags"; import { BuiltInLanguage } from "./languages"; import { + getOtherLanguagePacksReason, getPerLanguageBundleLanguage, MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION, PerLanguageBundleOptions, + QueryConfigInputs, } from "./per-language-bundles"; import { BundlePlatform } from "./platform"; import { @@ -16,11 +18,12 @@ import { initAllState, LoggedMessage, } from "./testing-utils"; -import { GitHubVariant } from "./util"; +import { ConfigurationError, GitHubVariant } from "./util"; /** Options for which we would use a per-language bundle. */ const ELIGIBLE_OPTIONS: PerLanguageBundleOptions = { rawLanguages: ["java"], + otherLanguagePacksReason: undefined, // Any version at least as new as the minimum will do. cliVersion: MIN_PER_LANGUAGE_BUNDLE_CLI_VERSION, compressionMethod: "zstd", @@ -81,6 +84,23 @@ test("getPerLanguageBundleLanguage requires a known language", async (t) => { t.is(await checkEligibility({ rawLanguages: ["cobol"] }), undefined); }); +test("getPerLanguageBundleLanguage explains why the CodeQL CLI may need packs for other languages before checking the languages", async (t) => { + // Without a language, the explanation would otherwise suggest requesting a single language. + for (const rawLanguages of [["java"], undefined]) { + const messages: LoggedMessage[] = []; + const language = await checkEligibility( + { rawLanguages, otherLanguagePacksReason: "an example reason" }, + { logger: getRecordingLogger(messages, { logToConsole: false }) }, + ); + + t.is(language, undefined); + t.deepEqual( + messages.map((message) => message.message), + ["Not using a per-language CodeQL bundle since an example reason."], + ); + } +}); + test("getPerLanguageBundleLanguage requires a zstd bundle", async (t) => { t.is(await checkEligibility({ compressionMethod: "gzip" }), undefined); }); @@ -142,9 +162,7 @@ test("getPerLanguageBundleLanguage explains a disabled feature before checking e t.is(language, undefined); t.deepEqual( messages.map((message) => message.message), - [ - "Not using a per-language CodeQL bundle since the per_language_bundles feature is disabled.", - ], + ["Not using a per-language CodeQL bundle since the feature is disabled."], ); }); @@ -155,6 +173,7 @@ test("getPerLanguageBundleLanguage skips only the release version check for the for (const overrides of [ { rawLanguages: undefined }, { rawLanguages: ["java", "python"] }, + { otherLanguagePacksReason: "an example reason" }, { compressionMethod: "gzip" as const }, { platform: BundlePlatform.Osx64 }, { variant: GitHubVariant.GHES }, @@ -173,3 +192,106 @@ test("getPerLanguageBundleLanguage skips only the release version check for the undefined, ); }); + +/** Query configuration inputs that configure nothing. */ +const NO_QUERY_CONFIG: QueryConfigInputs = { + configFile: undefined, + configInput: undefined, + queriesInput: undefined, + extraQueriesProperty: undefined, +}; + +test("getOtherLanguagePacksReason returns undefined when no queries are configured", (t) => { + t.is(getOtherLanguagePacksReason(NO_QUERY_CONFIG), undefined); +}); + +test("getOtherLanguagePacksReason returns undefined for built-in query suites", (t) => { + for (const queries of [ + "security-extended", + "+security-and-quality", + " security-extended , code-quality ", + ]) { + t.is( + getOtherLanguagePacksReason({ + ...NO_QUERY_CONFIG, + queriesInput: queries, + extraQueriesProperty: queries, + }), + undefined, + queries, + ); + } +}); + +test("getOtherLanguagePacksReason returns undefined for a config input that only uses default setup properties", (t) => { + t.is( + getOtherLanguagePacksReason({ + ...NO_QUERY_CONFIG, + // The configuration from the `config` input that default setup passes. + configInput: { + "default-setup": { + org: { "model-packs": ["github/immutable-actions-list@0.0.1"] }, + }, + "threat-models": [], + }, + }), + undefined, + ); +}); + +test("getOtherLanguagePacksReason explains a configuration file", (t) => { + t.is( + getOtherLanguagePacksReason({ + ...NO_QUERY_CONFIG, + configFile: "./.github/codeql/codeql-config.yml", + }), + "the configuration file './.github/codeql/codeql-config.yml' may use queries that need " + + "library packs for other languages", + ); +}); + +test("getOtherLanguagePacksReason explains a config input that uses other properties", (t) => { + t.is( + getOtherLanguagePacksReason({ + ...NO_QUERY_CONFIG, + configInput: { queries: [{ uses: "./queries/show_ifs.ql" }] }, + }), + "the 'config' input may use queries that need library packs for other languages", + ); +}); + +test("getOtherLanguagePacksReason explains the first query in the queries input that isn't a built-in query suite", (t) => { + t.is( + getOtherLanguagePacksReason({ + ...NO_QUERY_CONFIG, + queriesInput: + "+security-extended, ./queries/show_ifs.ql, octo-org/queries@main", + }), + "the query './queries/show_ifs.ql' from the 'queries' input may need library packs for " + + "other languages", + ); +}); + +test("getOtherLanguagePacksReason explains a query in the extra queries repository property that isn't a built-in query suite", (t) => { + t.is( + getOtherLanguagePacksReason({ + ...NO_QUERY_CONFIG, + extraQueriesProperty: "+octo-org/queries/show_ifs.ql@main", + }), + "the query 'octo-org/queries/show_ifs.ql@main' from the 'github-codeql-extra-queries' " + + "repository property may need library packs for other languages", + ); +}); + +test("getOtherLanguagePacksReason throws a ConfigurationError for a '+' with no queries after it", (t) => { + // Loading the configuration would throw the same errors. + for (const inputs of [ + { queriesInput: "+" }, + { extraQueriesProperty: " + " }, + ]) { + t.throws( + () => getOtherLanguagePacksReason({ ...NO_QUERY_CONFIG, ...inputs }), + { instanceOf: ConfigurationError }, + ); + } +}); diff --git a/src/per-language-bundles.ts b/src/per-language-bundles.ts index e0db649c7a..9a46d2df66 100644 --- a/src/per-language-bundles.ts +++ b/src/per-language-bundles.ts @@ -2,7 +2,15 @@ import * as semver from "semver"; import { ActionState } from "./action-common"; import { isGitHubHostedRunner } from "./actions-util"; +import { + defaultSuites, + matchesDefaultSetupConfigSchema, + parseQueriesFromInput, + QuerySpec, + UserConfig, +} from "./config/db-config"; import { Feature } from "./feature-flags"; +import { RepositoryPropertyName } from "./feature-flags/properties"; import { BuiltInLanguage, parseBuiltInLanguage } from "./languages"; import { BundlePlatform } from "./platform"; import * as tar from "./tar"; @@ -31,10 +39,95 @@ const PER_LANGUAGE_BUNDLE_LANGUAGES: Readonly< [BundlePlatform.Win64]: new Set(), }; +/** Query configuration that is known before CodeQL is set up. */ +export interface QueryConfigInputs { + /** The configuration file from the `config-file` input or repository property. */ + configFile: string | undefined; + /** The configuration from the `config` input. */ + configInput: UserConfig | undefined; + /** The `queries` input. */ + queriesInput: string | undefined; + /** The `github-codeql-extra-queries` repository property. */ + extraQueriesProperty: string | undefined; +} + +/** + * Explains why the configured queries may need library packs for languages other than the one + * being analyzed, which a per-language bundle doesn't contain. Returns `undefined` if the only + * queries that these inputs add are built-in query suites. The `packs` input doesn't matter, since + * query packs are downloaded together with their dependencies. + * + * Any configuration file is assumed to configure such queries, since reading it may need file or API + * access. So is the `config` input, unless it only sets the properties that default setup sets (see + * `matchesDefaultSetupConfigSchema`). + * + * @throws A `ConfigurationError` if the `queries` input or the `github-codeql-extra-queries` + * repository property is a '+' with no queries after it, unless an input that's checked earlier + * already gives a reason. + */ +export function getOtherLanguagePacksReason( + inputs: QueryConfigInputs, +): string | undefined { + if (inputs.configFile !== undefined) { + return ( + `the configuration file '${inputs.configFile}' may use queries that need library packs ` + + "for other languages" + ); + } + + // The `config` input can configure queries in the same way as a configuration file. The + // properties that default setup sets, listed in `DEFAULT_SETUP_CONFIG_SCHEMA` in + // `config/db-config.ts`, don't add queries. + if ( + inputs.configInput !== undefined && + !matchesDefaultSetupConfigSchema(inputs.configInput) + ) { + return "the 'config' input may use queries that need library packs for other languages"; + } + + // We can't tell which language a local query or a query from another repository is for without + // loading it, and CodeQL resolves the library packs for every configured query, including those + // for languages that aren't being analyzed. + const query = findNonBuiltInQuery( + parseQueriesFromInput(inputs.queriesInput).input, + ); + if (query !== undefined) { + return `the query '${query}' from the 'queries' input may need library packs for other languages`; + } + const extraQuery = findNonBuiltInQuery( + parseQueriesFromInput( + inputs.extraQueriesProperty, + RepositoryPropertyName.EXTRA_QUERIES, + ).input, + ); + if (extraQuery !== undefined) { + return ( + `the query '${extraQuery}' from the '${RepositoryPropertyName.EXTRA_QUERIES}' repository ` + + "property may need library packs for other languages" + ); + } + + return undefined; +} + +/** Returns the `uses` value of the first of `queries` that isn't a built-in query suite. */ +function findNonBuiltInQuery( + queries: QuerySpec[] | undefined, +): string | undefined { + return queries?.find((query) => !defaultSuites.has(query.uses))?.uses; +} + /** Inputs that determine whether we may download a per-language bundle. */ export interface PerLanguageBundleOptions { /** Explicit input only: autodetection needs a CLI instance. */ rawLanguages: string[] | undefined; + /** + * Why the CodeQL CLI may need packs for other languages, for example because of the configured + * queries, or `undefined` if it won't. If defined, the combined bundle is used, and this reason is + * logged to complete the sentence "Not using a per-language CodeQL bundle since ...". See + * `getOtherLanguagePacksReason`. + */ + otherLanguagePacksReason: string | undefined; /** Requested CLI version, if known. Ignored when requesting the latest nightly. */ cliVersion: string | undefined; compressionMethod: tar.CompressionMethod; @@ -56,6 +149,7 @@ export async function getPerLanguageBundleLanguage( ): Promise { const { rawLanguages, + otherLanguagePacksReason, cliVersion, compressionMethod, platform, @@ -69,7 +163,14 @@ export async function getPerLanguageBundleLanguage( }; if (!(await features.getValue(Feature.PerLanguageBundles))) { - return explain(`the ${Feature.PerLanguageBundles} feature is disabled`); + return explain("the feature is disabled"); + } + + // A defined reason means the CodeQL CLI may need packs for other languages, for example because + // of the configured queries. That applies whichever languages were requested, so check it first + // to avoid suggesting that requesting a single language would be enough. + if (otherLanguagePacksReason !== undefined) { + return explain(otherLanguagePacksReason); } if (rawLanguages?.length !== 1) { diff --git a/src/setup-codeql-action.ts b/src/setup-codeql-action.ts index 91666f19cd..23a9c65ebd 100644 --- a/src/setup-codeql-action.ts +++ b/src/setup-codeql-action.ts @@ -165,6 +165,12 @@ async function run( gitHubVersion.type, codeQLDefaultVersionInfo, rawLanguages, + // CodeQL resolves the dependencies of queries that aren't in compiled packs from the bundle, + // so the queries that the workflow runs with this CLI may need library packs for languages + // other than those in the `languages` input. That input therefore only informs the choice of + // CLI version. + "the 'setup-codeql' Action can't tell whether the queries that the workflow runs will need " + + "library packs for other languages", analysisKinds.length === 1 && analysisKinds[0] === AnalysisKind.CodeScanning, features, diff --git a/src/setup-codeql.test.ts b/src/setup-codeql.test.ts index c33ac0700a..3fef11e63e 100644 --- a/src/setup-codeql.test.ts +++ b/src/setup-codeql.test.ts @@ -157,6 +157,7 @@ test.serial( url, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -242,6 +243,7 @@ for (const { "linked", SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -275,6 +277,7 @@ test.serial( "latest", SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -320,6 +323,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, logger, @@ -377,6 +381,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, logger, @@ -425,6 +430,7 @@ test.serial( "nightly", SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -452,6 +458,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, ["javascript"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, logger, @@ -495,6 +502,7 @@ test.serial( undefined, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -550,6 +558,7 @@ for (const bundlePath of [ GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), getRecordingLogger(messages), @@ -603,6 +612,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([Feature.CleanupToolcacheBundles]), getRunnerLogger(true), @@ -636,6 +646,7 @@ for (const toolsInput of ["nightly", "nightly-latest"]) { toolsInput, SAMPLE_DEFAULT_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -671,12 +682,23 @@ test.serial( await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - for (const { languages, features } of [ + for (const { languages, otherLanguagePacksReason, features } of [ // The per-language feature is disabled. - { languages: ["java"], features: createFeatures([]) }, + { + languages: ["java"], + otherLanguagePacksReason: undefined, + features: createFeatures([]), + }, // More than one language requires a combined bundle. { languages: ["java", "python"], + otherLanguagePacksReason: undefined, + features: createFeatures([Feature.PerLanguageBundles]), + }, + // The configured queries may need library packs for other languages. + { + languages: ["java"], + otherLanguagePacksReason: "an example reason", features: createFeatures([Feature.PerLanguageBundles]), }, ]) { @@ -684,6 +706,7 @@ test.serial( "nightly", SAMPLE_DEFAULT_CLI_VERSION, languages, + otherLanguagePacksReason, false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -718,6 +741,7 @@ for (const perLanguageBundles of [false, true]) { undefined, // toolsInput: the nightly is selected by ForceNightly SAMPLE_DEFAULT_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -777,6 +801,7 @@ for (const date of ["20200101", "30260213"]) { url, SAMPLE_DEFAULT_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -804,6 +829,7 @@ for (const date of ["20200101", "30260213"]) { GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, features, logger, @@ -834,6 +860,7 @@ test.serial( "toolcache", SAMPLE_DEFAULT_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -875,6 +902,7 @@ test.serial( "toolcache", SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -944,6 +972,7 @@ const toolcacheInputFallbackMacro = makeMacro({ "toolcache", SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -1081,6 +1110,7 @@ test.serial( undefined, overlayMatchEnabledVersions, ["javascript"], + undefined, // otherLanguagePacksReason true, SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -1119,6 +1149,7 @@ test.serial( undefined, overlayMatchEnabledVersions, ["javascript"], + undefined, // otherLanguagePacksReason false, SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -1156,6 +1187,7 @@ test.serial( undefined, PER_LANGUAGE_CLI_VERSION, ["java-kotlin"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion SAMPLE_DOTCOM_API_DETAILS, GitHubVariant.DOTCOM, @@ -1185,7 +1217,7 @@ test.serial( ); test.serial( - "getCodeQLSource downloads the combined bundle when the feature is disabled", + "getCodeQLSource downloads the combined bundle when per-language selection is ineligible", async (t) => { sinon.stub(process, "platform").value("linux"); sinon.stub(process, "arch").value("x64"); @@ -1193,22 +1225,33 @@ test.serial( await withTmpDir(async (tmpDir) => { setupActionsVars(tmpDir, tmpDir); - const source = await setupCodeql.getCodeQLSource( - undefined, - PER_LANGUAGE_CLI_VERSION, - ["java"], - false, // useOverlayAwareDefaultCliVersion - SAMPLE_DOTCOM_API_DETAILS, - GitHubVariant.DOTCOM, - true, // tarSupportsZstd - createFeatures([]), - getRunnerLogger(true), - ); + for (const { otherLanguagePacksReason, features } of [ + // The per-language feature is disabled. + { otherLanguagePacksReason: undefined, features: createFeatures([]) }, + // The configured queries may need library packs for other languages. + { + otherLanguagePacksReason: "an example reason", + features: createFeatures([Feature.PerLanguageBundles]), + }, + ]) { + const source = await setupCodeql.getCodeQLSource( + undefined, + PER_LANGUAGE_CLI_VERSION, + ["java"], + otherLanguagePacksReason, + false, // useOverlayAwareDefaultCliVersion + SAMPLE_DOTCOM_API_DETAILS, + GitHubVariant.DOTCOM, + true, // tarSupportsZstd + features, + getRunnerLogger(true), + ); - t.is(source.sourceType, "download"); - if (source.sourceType === "download") { - t.true(source.bundle.url.endsWith("/codeql-bundle-linux64.tar.zst")); - t.is(source.bundle.kind, "combined"); + t.is(source.sourceType, "download"); + if (source.sourceType === "download") { + t.true(source.bundle.url.endsWith("/codeql-bundle-linux64.tar.zst")); + t.is(source.bundle.kind, "combined"); + } } }); }, @@ -1268,6 +1311,7 @@ for (const fallback of [false, true]) { GitHubVariant.DOTCOM, PER_LANGUAGE_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([Feature.PerLanguageBundles]), getRunnerLogger(true), @@ -1363,6 +1407,7 @@ for (const bundle of ["per-language", "combined", "fallback"] as const) { GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, bundle === "combined" ? ["javascript", "python"] : ["javascript"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, logger, @@ -1443,6 +1488,7 @@ for (const bundle of ["per-language", "combined", "fallback"] as const) { GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, ["javascript"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion features, logger, @@ -1478,6 +1524,7 @@ for (const asset of [ GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), getRecordingLogger(messages), @@ -1532,6 +1579,7 @@ for (const error of [ GitHubVariant.DOTCOM, PER_LANGUAGE_CLI_VERSION, ["java"], + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([Feature.PerLanguageBundles]), getRunnerLogger(true), @@ -1567,6 +1615,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), getRunnerLogger(true), @@ -2239,6 +2288,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([]), getRunnerLogger(true), @@ -2278,6 +2328,7 @@ test.serial( GitHubVariant.DOTCOM, SAMPLE_DEFAULT_CLI_VERSION, undefined, // rawLanguages + undefined, // otherLanguagePacksReason false, // useOverlayAwareDefaultCliVersion createFeatures([Feature.CleanupToolcacheBundles]), getRunnerLogger(true), diff --git a/src/setup-codeql.ts b/src/setup-codeql.ts index d306d40d63..f2ce959a8d 100644 --- a/src/setup-codeql.ts +++ b/src/setup-codeql.ts @@ -377,6 +377,9 @@ async function resolveDefaultCliVersion( * @param toolsInput The argument provided for the `tools` input, if any. * @param defaultCliVersion The default CLI version that's linked to the CodeQL Action. * @param rawLanguages Raw set of languages. + * @param otherLanguagePacksReason Why the CodeQL CLI may need packs for languages other than + * `rawLanguages`, or `undefined` if it won't. If defined, the combined bundle is used. See + * `PerLanguageBundleOptions.otherLanguagePacksReason`. * @param useOverlayAwareDefaultCliVersion Whether to select an overlay-aware default CLI version. * @param apiDetails Information about the GitHub API. * @param variant The GitHub variant we are running on. @@ -390,6 +393,7 @@ export async function getCodeQLSource( toolsInput: string | undefined, defaultCliVersion: CodeQLDefaultVersionInfo, rawLanguages: string[] | undefined, + otherLanguagePacksReason: string | undefined, useOverlayAwareDefaultCliVersion: boolean, apiDetails: api.GitHubApiDetails, variant: util.GitHubVariant, @@ -478,6 +482,7 @@ export async function getCodeQLSource( bundle = await getLatestNightlyBundle( { env: getEnv(), features, logger }, rawLanguages, + otherLanguagePacksReason, variant, ); toolsInput = bundle.url; @@ -721,6 +726,7 @@ export async function getCodeQLSource( { env: getEnv(), features, logger }, { rawLanguages, + otherLanguagePacksReason, cliVersion, compressionMethod, platform, @@ -1001,6 +1007,7 @@ export async function setupCodeQLBundle( variant: util.GitHubVariant, defaultCliVersion: CodeQLDefaultVersionInfo, rawLanguages: string[] | undefined, + otherLanguagePacksReason: string | undefined, useOverlayAwareDefaultCliVersion: boolean, features: FeatureEnablement, logger: Logger, @@ -1016,6 +1023,7 @@ export async function setupCodeQLBundle( toolsInput, defaultCliVersion, rawLanguages, + otherLanguagePacksReason, useOverlayAwareDefaultCliVersion, apiDetails, variant, @@ -1159,6 +1167,7 @@ function getTempExtractionDir(tempDir: string) { async function getLatestNightlyBundle( action: ActionState<["Logger", "ReadOnlyEnv", "FeatureFlags"]>, rawLanguages: string[] | undefined, + otherLanguagePacksReason: string | undefined, variant: util.GitHubVariant, ): Promise { const { logger } = action; @@ -1174,6 +1183,7 @@ async function getLatestNightlyBundle( const platform = getBundlePlatform(); const language = await getPerLanguageBundleLanguage(action, { rawLanguages, + otherLanguagePacksReason, cliVersion: undefined, compressionMethod, platform, diff --git a/src/upload-lib.ts b/src/upload-lib.ts index da5552cf24..c5b6c6efd5 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -166,6 +166,7 @@ async function combineSarifFilesUsingCLI( gitHubVersion.type, codeQLDefaultVersionInfo, undefined, // rawLanguages: upload-lib does not run analysis + undefined, // otherLanguagePacksReason: upload-lib does not run analysis false, // useOverlayAwareDefaultCliVersion: upload-lib does not run analysis features, logger,