From f8fb310547051d91315384918991fb38f7f20f73 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 28 Aug 2025 17:22:38 +0100 Subject: [PATCH 01/21] Resolve supported languages using CodeQL CLI --- lib/analyze-action-post.js | 11 ++- lib/analyze-action.js | 11 ++- lib/autobuild-action.js | 11 ++- lib/init-action-post.js | 11 ++- lib/init-action.js | 28 +++++-- lib/resolve-environment-action.js | 11 ++- lib/start-proxy-action-post.js | 6 ++ lib/upload-lib.js | 11 ++- lib/upload-sarif-action-post.js | 6 ++ lib/upload-sarif-action.js | 11 ++- src/codeql.ts | 15 +++- src/config-utils.test.ts | 121 ++++++++++++++++++------------ src/config-utils.ts | 25 ++++-- src/feature-flags.ts | 7 ++ src/tools-features.ts | 3 +- 15 files changed, 215 insertions(+), 73 deletions(-) diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 99d289f8f..a3c4d2fbe 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -117798,6 +117798,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -118245,13 +118251,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 29aef44a7..8a90096ef 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -90993,6 +90993,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -92757,13 +92763,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index ca18e315a..e0ef341b6 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -78536,6 +78536,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -79280,13 +79286,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 1995591ed..bd475e873 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -129115,6 +129115,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -130699,13 +130705,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/init-action.js b/lib/init-action.js index 2edef1980..7d0449824 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -86586,6 +86586,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -87162,11 +87168,17 @@ function getNoLanguagesError() { function getUnknownLanguagesError(languages) { return `Did not recognize the following languages: ${languages.join(", ")}`; } -async function getSupportedLanguageMap(codeql) { - const resolveResult = await codeql.betterResolveLanguages(); +async function getSupportedLanguageMap(codeql, features) { + const resolveSupportedLanguagesUsingCli = await features.getValue( + "resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */, + codeql + ); + const resolveResult = await codeql.betterResolveLanguages({ + filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli + }); const supportedLanguages = {}; for (const extractor of Object.keys(resolveResult.extractors)) { - if (KnownLanguage[extractor] !== void 0) { + if (resolveSupportedLanguagesUsingCli || KnownLanguage[extractor] !== void 0) { supportedLanguages[extractor] = extractor; } } @@ -87202,14 +87214,14 @@ async function getRawLanguagesInRepo(repository, sourceRoot, logger) { logger.debug(`Raw languages in repository: ${result.join(", ")}`); return result; } -async function getLanguages(codeql, languagesInput, repository, sourceRoot, logger) { +async function getLanguages(codeql, languagesInput, repository, sourceRoot, features, logger) { const { rawLanguages, autodetected } = await getRawLanguages( languagesInput, repository, sourceRoot, logger ); - const languageMap = await getSupportedLanguageMap(codeql); + const languageMap = await getSupportedLanguageMap(codeql, features); const languagesSet = /* @__PURE__ */ new Set(); const unknownLanguages = []; for (const language of rawLanguages) { @@ -87277,6 +87289,7 @@ async function getDefaultConfig({ languagesInput, repository, sourceRoot, + features, logger ); const buildMode = await parseBuildModeInput( @@ -89303,13 +89316,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index 7c46b8092..0d63e4dcb 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -78527,6 +78527,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -78980,13 +78986,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index c9317c4cd..e9e40ec48 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -117207,6 +117207,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 825744e8d..fa5bfea51 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -89226,6 +89226,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -90546,13 +90552,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index 4e569eb51..8b7d115db 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -117372,6 +117372,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index e785cef37..453e181a1 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -89195,6 +89195,12 @@ var featureConfig = { legacyApi: true, minimumVersion: void 0 }, + ["resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: void 0, + toolsFeature: "builtinExtractorsSpecifyDefaultQueries" /* BuiltinExtractorsSpecifyDefaultQueries */ + }, ["overlay_analysis" /* OverlayAnalysis */]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", @@ -91226,13 +91232,16 @@ async function getCodeQLForCmd(cmd, checkVersion) { ); } }, - async betterResolveLanguages() { + async betterResolveLanguages({ + filterToLanguagesWithQueries + } = { filterToLanguagesWithQueries: false }) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...filterToLanguagesWithQueries ? ["--filter-to-languages-with-queries"] : [], ...getExtraOptionsFromEnv(["resolve", "languages"]) ]; const output = await runCli(cmd, codeqlArgs); diff --git a/src/codeql.ts b/src/codeql.ts index 81c8e3dec..1256d7fd1 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -127,7 +127,9 @@ export interface CodeQL { /** * Run 'codeql resolve languages' with '--format=betterjson'. */ - betterResolveLanguages(): Promise; + betterResolveLanguages(options?: { + filterToLanguagesWithQueries: boolean; + }): Promise; /** * Run 'codeql resolve build-environment' */ @@ -737,13 +739,22 @@ export async function getCodeQLForCmd( ); } }, - async betterResolveLanguages() { + async betterResolveLanguages( + { + filterToLanguagesWithQueries, + }: { + filterToLanguagesWithQueries: boolean; + } = { filterToLanguagesWithQueries: false }, + ) { const codeqlArgs = [ "resolve", "languages", "--format=betterjson", "--extractor-options-verbosity=4", "--extractor-include-aliases", + ...(filterToLanguagesWithQueries + ? ["--filter-to-languages-with-queries"] + : []), ...getExtraOptionsFromEnv(["resolve", "languages"]), ]; const output = await runCli(cmd, codeqlArgs); diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index e57cb5f6f..d7470673e 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -976,6 +976,13 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); expectedLanguages: ["javascript", "csharp", "cpp"], expectedApiCall: true, }, + { + name: "unsupported languages from github api", + languagesInput: "", + languagesInRepository: ["html"], + expectedApiCall: true, + expectedError: configUtils.getNoLanguagesError(), + }, { name: "no languages", languagesInput: "", @@ -1005,57 +1012,71 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo"); expectedLanguages: ["javascript"], }, ].forEach((args) => { - test(`getLanguages: ${args.name}`, async (t) => { - const mockRequest = mockLanguagesInRepo(args.languagesInRepository); - const stubExtractorEntry = { - extractor_root: "", - }; - const codeQL = createStubCodeQL({ - betterResolveLanguages: () => - Promise.resolve({ - aliases: { - "c#": KnownLanguage.csharp, - c: KnownLanguage.cpp, - kotlin: KnownLanguage.java, - typescript: KnownLanguage.javascript, - }, - extractors: { - cpp: [stubExtractorEntry], - csharp: [stubExtractorEntry], - java: [stubExtractorEntry], - javascript: [stubExtractorEntry], - python: [stubExtractorEntry], - }, - }), + for (const resolveSupportedLanguagesUsingCli of [true, false]) { + test(`getLanguages${resolveSupportedLanguagesUsingCli ? " (supported languages via CLI)" : ""}: ${args.name}`, async (t) => { + const features = createFeatures( + resolveSupportedLanguagesUsingCli + ? [Feature.ResolveSupportedLanguagesUsingCli] + : [], + ); + const mockRequest = mockLanguagesInRepo(args.languagesInRepository); + const stubExtractorEntry = { + extractor_root: "", + }; + const codeQL = createStubCodeQL({ + betterResolveLanguages: (options) => + Promise.resolve({ + aliases: { + "c#": KnownLanguage.csharp, + c: KnownLanguage.cpp, + kotlin: KnownLanguage.java, + typescript: KnownLanguage.javascript, + }, + extractors: { + cpp: [stubExtractorEntry], + csharp: [stubExtractorEntry], + java: [stubExtractorEntry], + javascript: [stubExtractorEntry], + python: [stubExtractorEntry], + ...(options?.filterToLanguagesWithQueries + ? {} + : { + html: [stubExtractorEntry], + }), + }, + }), + }); + + if (args.expectedLanguages) { + // happy path + const actualLanguages = await configUtils.getLanguages( + codeQL, + args.languagesInput, + mockRepositoryNwo, + ".", + features, + mockLogger, + ); + + t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort()); + } else { + // there is an error + await t.throwsAsync( + async () => + await configUtils.getLanguages( + codeQL, + args.languagesInput, + mockRepositoryNwo, + ".", + features, + mockLogger, + ), + { message: args.expectedError }, + ); + } + t.deepEqual(mockRequest.called, args.expectedApiCall); }); - - if (args.expectedLanguages) { - // happy path - const actualLanguages = await configUtils.getLanguages( - codeQL, - args.languagesInput, - mockRepositoryNwo, - ".", - mockLogger, - ); - - t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort()); - } else { - // there is an error - await t.throwsAsync( - async () => - await configUtils.getLanguages( - codeQL, - args.languagesInput, - mockRepositoryNwo, - ".", - mockLogger, - ), - { message: args.expectedError }, - ); - } - t.deepEqual(mockRequest.called, args.expectedApiCall); - }); + } }); for (const { displayName, language, feature } of [ diff --git a/src/config-utils.ts b/src/config-utils.ts index 477cb20e6..6a8631e9a 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -304,16 +304,25 @@ export function getUnknownLanguagesError(languages: string[]): string { export async function getSupportedLanguageMap( codeql: CodeQL, + features: FeatureEnablement, ): Promise> { - const resolveResult = await codeql.betterResolveLanguages(); + const resolveSupportedLanguagesUsingCli = await features.getValue( + Feature.ResolveSupportedLanguagesUsingCli, + codeql, + ); + const resolveResult = await codeql.betterResolveLanguages({ + filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli, + }); const supportedLanguages: Record = {}; // Populate canonical language names for (const extractor of Object.keys(resolveResult.extractors)) { - // Require the language to be a known language. - // This is a temporary workaround since we have extractors that are not - // supported languages, such as `csv`, `html`, `properties`, `xml`, and - // `yaml`. We should replace this with a more robust solution in the future. - if (KnownLanguage[extractor] !== undefined) { + // If the CLI supports resolving languages with default queries, use these + // as the set of supported languages. Otherwise, require the language to be + // a known language. + if ( + resolveSupportedLanguagesUsingCli || + KnownLanguage[extractor] !== undefined + ) { supportedLanguages[extractor] = extractor; } } @@ -395,6 +404,7 @@ export async function getLanguages( languagesInput: string | undefined, repository: RepositoryNwo, sourceRoot: string, + features: FeatureEnablement, logger: Logger, ): Promise { // Obtain languages without filtering them. @@ -405,7 +415,7 @@ export async function getLanguages( logger, ); - const languageMap = await getSupportedLanguageMap(codeql); + const languageMap = await getSupportedLanguageMap(codeql, features); const languagesSet = new Set(); const unknownLanguages: string[] = []; @@ -548,6 +558,7 @@ export async function getDefaultConfig({ languagesInput, repository, sourceRoot, + features, logger, ); diff --git a/src/feature-flags.ts b/src/feature-flags.ts index d9f1a1cc3..ef6747d6d 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -73,6 +73,7 @@ export enum Feature { OverlayAnalysisSwift = "overlay_analysis_swift", PythonDefaultIsToNotExtractStdlib = "python_default_is_to_not_extract_stdlib", QaTelemetryEnabled = "qa_telemetry_enabled", + ResolveSupportedLanguagesUsingCli = "resolve_supported_languages_using_cli", } export const featureConfig: Record< @@ -145,6 +146,12 @@ export const featureConfig: Record< legacyApi: true, minimumVersion: undefined, }, + [Feature.ResolveSupportedLanguagesUsingCli]: { + defaultValue: false, + envVar: "CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI", + minimumVersion: undefined, + toolsFeature: ToolsFeature.BuiltinExtractorsSpecifyDefaultQueries, + }, [Feature.OverlayAnalysis]: { defaultValue: false, envVar: "CODEQL_ACTION_OVERLAY_ANALYSIS", diff --git a/src/tools-features.ts b/src/tools-features.ts index 7db27edf1..08eea97a1 100644 --- a/src/tools-features.ts +++ b/src/tools-features.ts @@ -5,10 +5,11 @@ import type { VersionInfo } from "./codeql"; export enum ToolsFeature { AnalysisSummaryV2IsDefault = "analysisSummaryV2Default", DatabaseInterpretResultsSupportsSarifRunProperty = "databaseInterpretResultsSupportsSarifRunProperty", + ForceOverwrite = "forceOverwrite", IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries", SarifMergeRunsFromEqualCategory = "sarifMergeRunsFromEqualCategory", - ForceOverwrite = "forceOverwrite", PythonDefaultIsToNotExtractStdlib = "pythonDefaultIsToNotExtractStdlib", + BuiltinExtractorsSpecifyDefaultQueries = "builtinExtractorsSpecifyDefaultQueries", } /** From d98150504030be984f379cb3103d619027223946 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Thu, 28 Aug 2025 18:16:51 +0100 Subject: [PATCH 02/21] Add log for supported languages --- lib/init-action.js | 9 +++++++-- src/config-utils.ts | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/init-action.js b/lib/init-action.js index 7d0449824..ac4a004ba 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87168,7 +87168,7 @@ function getNoLanguagesError() { function getUnknownLanguagesError(languages) { return `Did not recognize the following languages: ${languages.join(", ")}`; } -async function getSupportedLanguageMap(codeql, features) { +async function getSupportedLanguageMap(codeql, features, logger) { const resolveSupportedLanguagesUsingCli = await features.getValue( "resolve_supported_languages_using_cli" /* ResolveSupportedLanguagesUsingCli */, codeql @@ -87176,6 +87176,11 @@ async function getSupportedLanguageMap(codeql, features) { const resolveResult = await codeql.betterResolveLanguages({ filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli }); + if (resolveSupportedLanguagesUsingCli) { + logger.debug( + `The CodeQL CLI supports the following languages: ${Object.keys(resolveResult.extractors).join(", ")}` + ); + } const supportedLanguages = {}; for (const extractor of Object.keys(resolveResult.extractors)) { if (resolveSupportedLanguagesUsingCli || KnownLanguage[extractor] !== void 0) { @@ -87221,7 +87226,7 @@ async function getLanguages(codeql, languagesInput, repository, sourceRoot, feat sourceRoot, logger ); - const languageMap = await getSupportedLanguageMap(codeql, features); + const languageMap = await getSupportedLanguageMap(codeql, features, logger); const languagesSet = /* @__PURE__ */ new Set(); const unknownLanguages = []; for (const language of rawLanguages) { diff --git a/src/config-utils.ts b/src/config-utils.ts index 6a8631e9a..dd659aa6b 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -305,6 +305,7 @@ export function getUnknownLanguagesError(languages: string[]): string { export async function getSupportedLanguageMap( codeql: CodeQL, features: FeatureEnablement, + logger: Logger, ): Promise> { const resolveSupportedLanguagesUsingCli = await features.getValue( Feature.ResolveSupportedLanguagesUsingCli, @@ -313,6 +314,11 @@ export async function getSupportedLanguageMap( const resolveResult = await codeql.betterResolveLanguages({ filterToLanguagesWithQueries: resolveSupportedLanguagesUsingCli, }); + if (resolveSupportedLanguagesUsingCli) { + logger.debug( + `The CodeQL CLI supports the following languages: ${Object.keys(resolveResult.extractors).join(", ")}`, + ); + } const supportedLanguages: Record = {}; // Populate canonical language names for (const extractor of Object.keys(resolveResult.extractors)) { @@ -415,7 +421,7 @@ export async function getLanguages( logger, ); - const languageMap = await getSupportedLanguageMap(codeql, features); + const languageMap = await getSupportedLanguageMap(codeql, features, logger); const languagesSet = new Set(); const unknownLanguages: string[] = []; From 71410c6e72dc66c8d6a1a56e0ce0ae835dcad996 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Fri, 5 Sep 2025 16:56:51 +0100 Subject: [PATCH 03/21] Enable feature in CI for testing --- .github/workflows/__multi-language-autodetect.yml | 1 + pr-checks/checks/multi-language-autodetect.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/__multi-language-autodetect.yml b/.github/workflows/__multi-language-autodetect.yml index e5f157881..8bedb73e8 100644 --- a/.github/workflows/__multi-language-autodetect.yml +++ b/.github/workflows/__multi-language-autodetect.yml @@ -164,4 +164,5 @@ jobs: exit 1 fi env: + CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true CODEQL_ACTION_TEST_MODE: true diff --git a/pr-checks/checks/multi-language-autodetect.yml b/pr-checks/checks/multi-language-autodetect.yml index e663c4f8f..794da3567 100644 --- a/pr-checks/checks/multi-language-autodetect.yml +++ b/pr-checks/checks/multi-language-autodetect.yml @@ -1,6 +1,8 @@ name: "Multi-language repository" description: "An end-to-end integration test of a multi-language repository using automatic language detection for macOS" operatingSystems: ["macos", "ubuntu"] +env: + CODEQL_ACTION_RESOLVE_SUPPORTED_LANGUAGES_USING_CLI: true installGo: true steps: - name: Use Xcode 16 From ed9d73bc6fbf9b187e0e4efd5976a43f3f22e968 Mon Sep 17 00:00:00 2001 From: Henry Mercer Date: Tue, 9 Sep 2025 14:42:39 +0100 Subject: [PATCH 04/21] Alphabetically order `ToolsFeature` --- src/tools-features.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools-features.ts b/src/tools-features.ts index 08eea97a1..0e88ccd92 100644 --- a/src/tools-features.ts +++ b/src/tools-features.ts @@ -4,12 +4,12 @@ import type { VersionInfo } from "./codeql"; export enum ToolsFeature { AnalysisSummaryV2IsDefault = "analysisSummaryV2Default", + BuiltinExtractorsSpecifyDefaultQueries = "builtinExtractorsSpecifyDefaultQueries", DatabaseInterpretResultsSupportsSarifRunProperty = "databaseInterpretResultsSupportsSarifRunProperty", ForceOverwrite = "forceOverwrite", IndirectTracingSupportsStaticBinaries = "indirectTracingSupportsStaticBinaries", - SarifMergeRunsFromEqualCategory = "sarifMergeRunsFromEqualCategory", PythonDefaultIsToNotExtractStdlib = "pythonDefaultIsToNotExtractStdlib", - BuiltinExtractorsSpecifyDefaultQueries = "builtinExtractorsSpecifyDefaultQueries", + SarifMergeRunsFromEqualCategory = "sarifMergeRunsFromEqualCategory", } /** From 4366c134578f29f483974573fe07c9f4a1f9b0e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:33:51 +0000 Subject: [PATCH 05/21] Initial plan From 2a4630c7f1bf4065a8530f7b748d798b51c889f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:53:28 +0000 Subject: [PATCH 06/21] Remove --intra-layer-parallelism flag from CodeQL CLI commands Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com> --- lib/analyze-action-post.js | 1 - lib/analyze-action.js | 1 - lib/autobuild-action.js | 1 - lib/init-action-post.js | 1 - lib/init-action.js | 1 - lib/resolve-environment-action.js | 1 - lib/upload-lib.js | 1 - lib/upload-sarif-action.js | 1 - src/codeql.test.ts | 2 +- src/codeql.ts | 1 - 10 files changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index f6a82b731..807ac63a7 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -118251,7 +118251,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/analyze-action.js b/lib/analyze-action.js index fb95211a3..59c83e477 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -92814,7 +92814,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index a885e9107..3d47b4812 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -79286,7 +79286,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/init-action-post.js b/lib/init-action-post.js index cb65274e0..8f9010727 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -130724,7 +130724,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/init-action.js b/lib/init-action.js index d26067571..0f8fc3ec9 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -89408,7 +89408,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index d7a9a4ade..53101dc2c 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -78986,7 +78986,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/upload-lib.js b/lib/upload-lib.js index f18ee06e9..4f8e075ad 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -90552,7 +90552,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 935846248..fda852a52 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -91253,7 +91253,6 @@ ${output}` "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", diff --git a/src/codeql.test.ts b/src/codeql.test.ts index ece6ab878..36775f653 100644 --- a/src/codeql.test.ts +++ b/src/codeql.test.ts @@ -953,7 +953,7 @@ test("runTool recognizes fatal internal errors", async (t) => { await codeqlObject.databaseRunQueries(stubConfig.dbLocation, []), { instanceOf: CliError, - message: `Encountered a fatal error while running "codeql-for-testing database run-queries --intra-layer-parallelism --min-disk-free=1024 -v". Exit code was 1 and error was: Oops! A fatal internal error occurred. Details: + message: `Encountered a fatal error while running "codeql-for-testing database run-queries --min-disk-free=1024 -v". Exit code was 1 and error was: Oops! A fatal internal error occurred. Details: com.semmle.util.exception.CatastrophicError: An error occurred while evaluating ControlFlowGraph::ControlFlow::Root.isRootOf/1#dispred#f610e6ed/2@86282cc8 Severe disk cache trouble (corruption or out of space) at /home/runner/work/_temp/codeql_databases/go/db-go/default/cache/pages/28/33.pack: Failed to write item to disk. See the logs for more details.`, }, diff --git a/src/codeql.ts b/src/codeql.ts index 567eb8087..35c3cc625 100644 --- a/src/codeql.ts +++ b/src/codeql.ts @@ -789,7 +789,6 @@ export async function getCodeQLForCmd( "run-queries", ...flags, databasePath, - "--intra-layer-parallelism", "--min-disk-free=1024", // Try to leave at least 1GB free "-v", ...queries, From dae3742b0a3b9e08acc580e15ef74bdc454d650a Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 10 Sep 2025 07:46:05 +0200 Subject: [PATCH 07/21] Dump soon to be uploaded SARIF on request This introduces a new internal environment variable flag (`CODEQL_ACTION_SARIF_DUMP_DIR`) that, when set to `true`, causes the SARIF file that will be uploaded to be dumped to the specified directory. The filename will be `upload.sarif` or `upload.quality.sarif` depending on the upload target. --- lib/analyze-action.js | 19 +++++++++++++++++++ lib/init-action-post.js | 19 +++++++++++++++++++ lib/upload-lib.js | 19 +++++++++++++++++++ lib/upload-sarif-action.js | 19 +++++++++++++++++++ src/environment.ts | 6 ++++++ src/upload-lib.ts | 30 ++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+) diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 59c83e477..a895f1254 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -95612,6 +95612,10 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features validateUniqueCategory(sarif, uploadTarget.sentinelPrefix); logger.debug(`Serializing SARIF for upload`); const sarifPayload = JSON.stringify(sarif); + const dumpDir = process.env["CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */]; + if (dumpDir) { + dumpSarifFile(sarifPayload, dumpDir, logger, uploadTarget); + } logger.debug(`Compressing serialized SARIF`); const zippedSarif = import_zlib.default.gzipSync(sarifPayload).toString("base64"); const checkoutURI = url.pathToFileURL(checkoutPath).href; @@ -95650,6 +95654,21 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features sarifID }; } +function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { + if (!fs18.existsSync(outputDir)) { + fs18.mkdirSync(outputDir, { recursive: true }); + } else if (!fs18.lstatSync(outputDir).isDirectory()) { + throw new ConfigurationError( + `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + ); + } + const outputFile = path18.resolve( + outputDir, + `upload${uploadTarget.sarifExtension}` + ); + logger.info(`Dumping processed SARIF file to ${outputFile}`); + fs18.writeFileSync(outputFile, sarifPayload); +} var STATUS_CHECK_FREQUENCY_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3; async function waitForProcessing(repositoryNwo, sarifID, logger, options = { diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 8f9010727..d6b6a7a94 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -133049,6 +133049,10 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features validateUniqueCategory(sarif, uploadTarget.sentinelPrefix); logger.debug(`Serializing SARIF for upload`); const sarifPayload = JSON.stringify(sarif); + const dumpDir = process.env["CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */]; + if (dumpDir) { + dumpSarifFile(sarifPayload, dumpDir, logger, uploadTarget); + } logger.debug(`Compressing serialized SARIF`); const zippedSarif = import_zlib.default.gzipSync(sarifPayload).toString("base64"); const checkoutURI = url.pathToFileURL(checkoutPath).href; @@ -133087,6 +133091,21 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features sarifID }; } +function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { + if (!fs17.existsSync(outputDir)) { + fs17.mkdirSync(outputDir, { recursive: true }); + } else if (!fs17.lstatSync(outputDir).isDirectory()) { + throw new ConfigurationError( + `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + ); + } + const outputFile = path17.resolve( + outputDir, + `upload${uploadTarget.sarifExtension}` + ); + logger.info(`Dumping processed SARIF file to ${outputFile}`); + fs17.writeFileSync(outputFile, sarifPayload); +} var STATUS_CHECK_FREQUENCY_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3; async function waitForProcessing(repositoryNwo, sarifID, logger, options = { diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 4f8e075ad..64e89d2e4 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -92421,6 +92421,10 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features validateUniqueCategory(sarif, uploadTarget.sentinelPrefix); logger.debug(`Serializing SARIF for upload`); const sarifPayload = JSON.stringify(sarif); + const dumpDir = process.env["CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */]; + if (dumpDir) { + dumpSarifFile(sarifPayload, dumpDir, logger, uploadTarget); + } logger.debug(`Compressing serialized SARIF`); const zippedSarif = import_zlib.default.gzipSync(sarifPayload).toString("base64"); const checkoutURI = url.pathToFileURL(checkoutPath).href; @@ -92459,6 +92463,21 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features sarifID }; } +function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { + if (!fs13.existsSync(outputDir)) { + fs13.mkdirSync(outputDir, { recursive: true }); + } else if (!fs13.lstatSync(outputDir).isDirectory()) { + throw new ConfigurationError( + `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + ); + } + const outputFile = path14.resolve( + outputDir, + `upload${uploadTarget.sarifExtension}` + ); + logger.info(`Dumping processed SARIF file to ${outputFile}`); + fs13.writeFileSync(outputFile, sarifPayload); +} var STATUS_CHECK_FREQUENCY_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3; async function waitForProcessing(repositoryNwo, sarifID, logger, options = { diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index fda852a52..0a2332126 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -93122,6 +93122,10 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features validateUniqueCategory(sarif, uploadTarget.sentinelPrefix); logger.debug(`Serializing SARIF for upload`); const sarifPayload = JSON.stringify(sarif); + const dumpDir = process.env["CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */]; + if (dumpDir) { + dumpSarifFile(sarifPayload, dumpDir, logger, uploadTarget); + } logger.debug(`Compressing serialized SARIF`); const zippedSarif = import_zlib.default.gzipSync(sarifPayload).toString("base64"); const checkoutURI = url.pathToFileURL(checkoutPath).href; @@ -93160,6 +93164,21 @@ async function uploadSpecifiedFiles(sarifPaths, checkoutPath, category, features sarifID }; } +function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { + if (!fs14.existsSync(outputDir)) { + fs14.mkdirSync(outputDir, { recursive: true }); + } else if (!fs14.lstatSync(outputDir).isDirectory()) { + throw new ConfigurationError( + `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + ); + } + const outputFile = path15.resolve( + outputDir, + `upload${uploadTarget.sarifExtension}` + ); + logger.info(`Dumping processed SARIF file to ${outputFile}`); + fs14.writeFileSync(outputFile, sarifPayload); +} var STATUS_CHECK_FREQUENCY_MILLISECONDS = 5 * 1e3; var STATUS_CHECK_TIMEOUT_MILLISECONDS = 2 * 60 * 1e3; async function waitForProcessing(repositoryNwo, sarifID, logger, options = { diff --git a/src/environment.ts b/src/environment.ts index f25e7270d..e78c36724 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -119,4 +119,10 @@ export enum EnvVar { * Whether to enable experimental extractors for CodeQL. */ EXPERIMENTAL_FEATURES = "CODEQL_ENABLE_EXPERIMENTAL_FEATURES", + + /** + * Whether and where to dump the processed SARIF file that would be uploaded, regardless of + * whether the upload is disabled. This is intended for testing and debugging purposes. + */ + SARIF_DUMP_DIR = "CODEQL_ACTION_SARIF_DUMP_DIR", } diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 8939e1694..8a2ca91c6 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -696,6 +696,12 @@ export async function uploadSpecifiedFiles( validateUniqueCategory(sarif, uploadTarget.sentinelPrefix); logger.debug(`Serializing SARIF for upload`); const sarifPayload = JSON.stringify(sarif); + + const dumpDir = process.env[EnvVar.SARIF_DUMP_DIR]; + if (dumpDir) { + dumpSarifFile(sarifPayload, dumpDir, logger, uploadTarget); + } + logger.debug(`Compressing serialized SARIF`); const zippedSarif = zlib.gzipSync(sarifPayload).toString("base64"); const checkoutURI = url.pathToFileURL(checkoutPath).href; @@ -742,6 +748,30 @@ export async function uploadSpecifiedFiles( }; } +/** + * Dumps the given processed SARIF file contents to `outputDir`. + */ +function dumpSarifFile( + sarifPayload: string, + outputDir: string, + logger: Logger, + uploadTarget: analyses.AnalysisConfig, +) { + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } else if (!fs.lstatSync(outputDir).isDirectory()) { + throw new ConfigurationError( + `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}`, + ); + } + const outputFile = path.resolve( + outputDir, + `upload${uploadTarget.sarifExtension}`, + ); + logger.info(`Dumping processed SARIF file to ${outputFile}`); + fs.writeFileSync(outputFile, sarifPayload); +} + const STATUS_CHECK_FREQUENCY_MILLISECONDS = 5 * 1000; const STATUS_CHECK_TIMEOUT_MILLISECONDS = 2 * 60 * 1000; From 4c534612bf77788909753a5602e96710156f5758 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Wed, 10 Sep 2025 07:52:59 +0200 Subject: [PATCH 08/21] Tweak sarif dump log --- lib/analyze-action.js | 2 +- lib/init-action-post.js | 2 +- lib/upload-lib.js | 2 +- lib/upload-sarif-action.js | 2 +- src/upload-lib.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/analyze-action.js b/lib/analyze-action.js index a895f1254..33c595fdc 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -95659,7 +95659,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { fs18.mkdirSync(outputDir, { recursive: true }); } else if (!fs18.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( - `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + `The path specified by the ${"CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */} environment variable exists and is not a directory: ${outputDir}` ); } const outputFile = path18.resolve( diff --git a/lib/init-action-post.js b/lib/init-action-post.js index d6b6a7a94..98bf415b0 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -133096,7 +133096,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { fs17.mkdirSync(outputDir, { recursive: true }); } else if (!fs17.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( - `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + `The path specified by the ${"CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */} environment variable exists and is not a directory: ${outputDir}` ); } const outputFile = path17.resolve( diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 64e89d2e4..d45968cd0 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -92468,7 +92468,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { fs13.mkdirSync(outputDir, { recursive: true }); } else if (!fs13.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( - `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + `The path specified by the ${"CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */} environment variable exists and is not a directory: ${outputDir}` ); } const outputFile = path14.resolve( diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 0a2332126..7fbbcb3dc 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -93169,7 +93169,7 @@ function dumpSarifFile(sarifPayload, outputDir, logger, uploadTarget) { fs14.mkdirSync(outputDir, { recursive: true }); } else if (!fs14.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( - `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}` + `The path specified by the ${"CODEQL_ACTION_SARIF_DUMP_DIR" /* SARIF_DUMP_DIR */} environment variable exists and is not a directory: ${outputDir}` ); } const outputFile = path15.resolve( diff --git a/src/upload-lib.ts b/src/upload-lib.ts index 8a2ca91c6..650e7a803 100644 --- a/src/upload-lib.ts +++ b/src/upload-lib.ts @@ -761,7 +761,7 @@ function dumpSarifFile( fs.mkdirSync(outputDir, { recursive: true }); } else if (!fs.lstatSync(outputDir).isDirectory()) { throw new ConfigurationError( - `The path specified by the CODEQL_ACTION_SARIF_DUMP_DIR environment variable exists and is not a directory: ${outputDir}`, + `The path specified by the ${EnvVar.SARIF_DUMP_DIR} environment variable exists and is not a directory: ${outputDir}`, ); } const outputFile = path.resolve( From 8a84a62542ea24fd569eb5afdfb2507c25328ab9 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Wed, 10 Sep 2025 11:26:29 +0200 Subject: [PATCH 09/21] Overlay: Increase size limit for cached overlay base database --- lib/analyze-action-post.js | 2 +- lib/analyze-action.js | 2 +- lib/autobuild-action.js | 2 +- lib/init-action-post.js | 2 +- lib/init-action.js | 2 +- lib/resolve-environment-action.js | 2 +- lib/start-proxy-action-post.js | 2 +- lib/upload-lib.js | 2 +- lib/upload-sarif-action-post.js | 2 +- lib/upload-sarif-action.js | 2 +- src/overlay-database-utils.ts | 15 ++++++++++----- 11 files changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 807ac63a7..312e02135 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -117686,7 +117686,7 @@ function withGroup(groupName, f) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 59c83e477..473eed69f 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -90811,7 +90811,7 @@ function formatDuration(durationMs) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index 3d47b4812..38c842dce 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -78426,7 +78426,7 @@ function getActionsLogger() { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 8f9010727..5b9aeb8b5 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -129016,7 +129016,7 @@ function formatDuration(durationMs) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/init-action.js b/lib/init-action.js index 0f8fc3ec9..804f24af1 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -86417,7 +86417,7 @@ function formatDuration(durationMs) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index 53101dc2c..edc2cc242 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -78419,7 +78419,7 @@ function getActionsLogger() { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index 8bb47e562..f4ae973df 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -117158,7 +117158,7 @@ function getActionsLogger() { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; // src/tools-features.ts diff --git a/lib/upload-lib.js b/lib/upload-lib.js index 4f8e075ad..2a81d8771 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -89114,7 +89114,7 @@ function formatDuration(durationMs) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index 59847a4a8..76753057d 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -117319,7 +117319,7 @@ function withGroup(groupName, f) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; // src/tools-features.ts diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index fda852a52..a5d754fb3 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -89108,7 +89108,7 @@ function formatDuration(durationMs) { // src/overlay-database-utils.ts var CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; -var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6e3; +var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15e3; var OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1e6; async function writeBaseDatabaseOidsFile(config, sourceRoot) { const gitFileOids = await getFileOidsUnderPath(sourceRoot); diff --git a/src/overlay-database-utils.ts b/src/overlay-database-utils.ts index c4f6ae7c0..ea43abcaa 100644 --- a/src/overlay-database-utils.ts +++ b/src/overlay-database-utils.ts @@ -22,18 +22,23 @@ export const CODEQL_OVERLAY_MINIMUM_VERSION = "2.22.3"; /** * The maximum (uncompressed) size of the overlay base database that we will - * upload. Actions Cache has an overall capacity of 10 GB, and the Actions Cache - * client library uses zstd compression. + * upload. By default, the Actions Cache has an overall capacity of 10 GB, and + * the Actions Cache client library uses zstd compression. * * Ideally we would apply a size limit to the compressed overlay-base database, * but we cannot do so because compression is handled transparently by the * Actions Cache client library. Instead we place a limit on the uncompressed * size of the overlay-base database. * - * Assuming 2.5:1 compression ratio, the 6 GB limit on uncompressed data would - * translate to a limit of around 2.4 GB after compression. + * Assuming 2.5:1 compression ratio, the 15 GB limit on uncompressed data would + * translate to a limit of around 6 GB after compression. This is a high limit + * compared to the default 10GB Actions Cache capacity, but enforcement of Actions + * Cache quotas is not immediate. + * + * TODO: revisit this limit before removing the restriction for overlay analysis + * to the `github` and `dsp-testing` orgs. */ -const OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 6000; +const OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB = 15000; const OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_BYTES = OVERLAY_BASE_DATABASE_MAX_UPLOAD_SIZE_MB * 1_000_000; From bb98ff4838ad0b291a318f58cf744d74ff43a391 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 10 Sep 2025 12:18:50 +0100 Subject: [PATCH 10/21] Validate workflow to check that all `codeql-action` versions are the same --- lib/init-action-post.js | 3 ++- lib/init-action.js | 19 ++++++++++++- src/workflow.test.ts | 59 +++++++++++++++++++++++++++++++++++++++++ src/workflow.ts | 27 +++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 8f9010727..14f1d6ca8 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -133255,7 +133255,8 @@ function toCodedErrors(errors) { } var WorkflowErrors = toCodedErrors({ MissingPushHook: `Please specify an on.push hook to analyze and see code scanning alerts from the default branch on the Security tab.`, - CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.` + CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.`, + InconsistentActionVersion: `Not all workflow steps that use \`github/codeql-action\` actions use the same version. Please ensure that all such steps use the same version to avoid compatibility issues.` }); async function getWorkflow(logger) { const maybeWorkflow = process.env["CODE_SCANNING_WORKFLOW_FILE"]; diff --git a/lib/init-action.js b/lib/init-action.js index 0f8fc3ec9..23b807806 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -90109,7 +90109,8 @@ function toCodedErrors(errors) { } var WorkflowErrors = toCodedErrors({ MissingPushHook: `Please specify an on.push hook to analyze and see code scanning alerts from the default branch on the Security tab.`, - CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.` + CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.`, + InconsistentActionVersion: `Not all workflow steps that use \`github/codeql-action\` actions use the same version. Please ensure that all such steps use the same version to avoid compatibility issues.` }); async function groupLanguagesByExtractor(languages, codeql) { const resolveResult = await codeql.betterResolveLanguages(); @@ -90163,6 +90164,22 @@ async function getWorkflowErrors(doc, codeql) { } } } + const codeqlStepRefs = []; + for (const job of Object.values(doc?.jobs || {})) { + if (Array.isArray(job.steps)) { + for (const step of job.steps) { + if (step.uses !== void 0 && step.uses.startsWith("github/codeql-action/")) { + const parts = step.uses.split("@"); + if (parts.length >= 2) { + codeqlStepRefs.push(parts[parts.length - 1]); + } + } + } + } + } + if (codeqlStepRefs.length > 0 && !codeqlStepRefs.every((ref) => ref === codeqlStepRefs[0])) { + errors.push(WorkflowErrors.InconsistentActionVersion); + } const hasPushTrigger = hasWorkflowTrigger("push", doc); const hasPullRequestTrigger = hasWorkflowTrigger("pull_request", doc); const hasWorkflowCallTrigger = hasWorkflowTrigger("workflow_call", doc); diff --git a/src/workflow.test.ts b/src/workflow.test.ts index 21a5874f0..9af81459e 100644 --- a/src/workflow.test.ts +++ b/src/workflow.test.ts @@ -655,6 +655,65 @@ test("getWorkflowErrors() should not report a warning if there is a workflow_cal t.deepEqual(...errorCodes(errors, [])); }); +test("getWorkflowErrors() should report a warning if different versions of the CodeQL Action are used", async (t) => { + const errors = await getWorkflowErrors( + yaml.load(` + name: "CodeQL" + on: + push: + branches: [main] + jobs: + analyze: + steps: + - uses: github/codeql-action/init@v2 + - uses: github/codeql-action/analyze@v3 + `) as Workflow, + await getCodeQLForTesting(), + ); + + t.deepEqual( + ...errorCodes(errors, [WorkflowErrors.InconsistentActionVersion]), + ); +}); + +test("getWorkflowErrors() should not report a warning if the same versions of the CodeQL Action are used", async (t) => { + const errors = await getWorkflowErrors( + yaml.load(` + name: "CodeQL" + on: + push: + branches: [main] + jobs: + analyze: + steps: + - uses: github/codeql-action/init@v3 + - uses: github/codeql-action/analyze@v3 + `) as Workflow, + await getCodeQLForTesting(), + ); + + t.deepEqual(...errorCodes(errors, [])); +}); + +test("getWorkflowErrors() should not report a warning involving versions of other actions", async (t) => { + const errors = await getWorkflowErrors( + yaml.load(` + name: "CodeQL" + on: + push: + branches: [main] + jobs: + analyze: + steps: + - uses: actions/checkout@v5 + - uses: github/codeql-action/init@v3 + `) as Workflow, + await getCodeQLForTesting(), + ); + + t.deepEqual(...errorCodes(errors, [])); +}); + test("getCategoryInputOrThrow returns category for simple workflow with category", (t) => { process.env["GITHUB_REPOSITORY"] = "github/codeql-action-fake-repository"; t.is( diff --git a/src/workflow.ts b/src/workflow.ts index 37094154e..330e00c2b 100644 --- a/src/workflow.ts +++ b/src/workflow.ts @@ -72,6 +72,7 @@ function toCodedErrors(errors: { export const WorkflowErrors = toCodedErrors({ MissingPushHook: `Please specify an on.push hook to analyze and see code scanning alerts from the default branch on the Security tab.`, CheckoutWrongHead: `git checkout HEAD^2 is no longer necessary. Please remove this step as Code Scanning recommends analyzing the merge commit for best results.`, + InconsistentActionVersion: `Not all workflow steps that use \`github/codeql-action\` actions use the same version. Please ensure that all such steps use the same version to avoid compatibility issues.`, }); /** @@ -161,6 +162,32 @@ export async function getWorkflowErrors( } } + // Check that all `github/codeql-action` steps use the same ref, i.e. the same version. + // Mixing different versions of the actions can lead to unpredictable behaviour. + const codeqlStepRefs: string[] = []; + for (const job of Object.values(doc?.jobs || {})) { + if (Array.isArray(job.steps)) { + for (const step of job.steps) { + if ( + step.uses !== undefined && + step.uses.startsWith("github/codeql-action/") + ) { + const parts = step.uses.split("@"); + if (parts.length >= 2) { + codeqlStepRefs.push(parts[parts.length - 1]); + } + } + } + } + } + + if ( + codeqlStepRefs.length > 0 && + !codeqlStepRefs.every((ref) => ref === codeqlStepRefs[0]) + ) { + errors.push(WorkflowErrors.InconsistentActionVersion); + } + // If there is no push trigger, we will not be able to analyze the default branch. // So add a warning to the user to add a push trigger. // If there is a workflow_call trigger, we don't need a push trigger since we assume From 5efa438e92992578d794ae4ceed960bf81011677 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 10 Sep 2025 13:29:04 +0100 Subject: [PATCH 11/21] Merge pull request #3101 from github/mbg/public-repo-notice-in-pr-template Add a reminder to the PR template that this is a public repo --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 580719d47..96e7f52f6 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,4 @@ - + ### Risk assessment From 4f56152a481f6d23f3db3b830b30dbb8ccb3e22e Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 10 Sep 2025 12:43:56 +0100 Subject: [PATCH 12/21] Store and check action version in `Config` --- lib/analyze-action-post.js | 13 +++++++- lib/analyze-action.js | 13 +++++++- lib/autobuild-action.js | 13 +++++++- lib/init-action-post.js | 13 +++++++- lib/init-action.js | 1 + lib/resolve-environment-action.js | 13 +++++++- lib/start-proxy-action-post.js | 13 +++++++- lib/upload-lib.js | 13 +++++++- lib/upload-sarif-action.js | 13 +++++++- src/config-utils.test.ts | 51 +++++++++++++++++++++++++++++++ src/config-utils.ts | 23 ++++++++++++-- src/testing-utils.ts | 2 ++ 12 files changed, 171 insertions(+), 10 deletions(-) diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 807ac63a7..bff2f8816 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -117960,7 +117960,18 @@ async function getConfig(tempDir, logger) { const configString = fs3.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 33c595fdc..84b0fc6b8 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -91620,7 +91620,18 @@ async function getConfig(tempDir, logger) { const configString = fs9.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index 3d47b4812..652e6961f 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -78962,7 +78962,18 @@ async function getConfig(tempDir, logger) { const configString = fs4.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 98bf415b0..49473d526 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -129575,7 +129575,18 @@ async function getConfig(tempDir, logger) { const configString = fs9.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/lib/init-action.js b/lib/init-action.js index 0f8fc3ec9..49491873c 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87335,6 +87335,7 @@ async function initActionState({ augmentationProperties ); return { + version: getActionVersion(), analysisKinds, languages, buildMode, diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index 53101dc2c..4895e7da3 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -78689,7 +78689,18 @@ async function getConfig(tempDir, logger) { const configString = fs3.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index 8bb47e562..f8703b67d 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -117369,7 +117369,18 @@ async function getConfig(tempDir, logger) { const configString = fs.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } // src/debug-artifacts.ts diff --git a/lib/upload-lib.js b/lib/upload-lib.js index d45968cd0..977b90633 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -89403,7 +89403,18 @@ async function getConfig(tempDir, logger) { const configString = fs7.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 7fbbcb3dc..304b74a26 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -89677,7 +89677,18 @@ async function getConfig(tempDir, logger) { const configString = fs8.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString); + const config = JSON.parse(configString); + if (config.version === void 0) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.` + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'` + ); + } + return config; } function appendExtraQueryExclusions(extraQueryExclusions, cliConfig) { const augmentedConfig = cloneObject(cliConfig); diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index 3bb9e1e11..c47f6d8a2 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -199,6 +199,7 @@ test("load code quality config", async (t) => { // And the config we expect it to result in const expectedConfig: configUtils.Config = { + version: actionsUtil.getActionVersion(), analysisKinds: [AnalysisKind.CodeQuality], languages: [KnownLanguage.actions], buildMode: undefined, @@ -273,6 +274,55 @@ test("loading config saves config", async (t) => { }); }); +test("loading config with version mismatch throws", async (t) => { + return await withTmpDir(async (tempDir) => { + const logger = getRunnerLogger(true); + + const codeql = createStubCodeQL({ + async betterResolveLanguages() { + return { + extractors: { + javascript: [{ extractor_root: "" }], + python: [{ extractor_root: "" }], + }, + }; + }, + }); + + // Sanity check the saved config file does not already exist + t.false(fs.existsSync(configUtils.getPathToParsedConfigFile(tempDir))); + + // Sanity check that getConfig returns undefined before we have called initConfig + t.deepEqual(await configUtils.getConfig(tempDir, logger), undefined); + + // Stub `getActionVersion` to return some nonsense. + const getActionVersionStub = sinon + .stub(actionsUtil, "getActionVersion") + .resolves("does-not-exist"); + + await configUtils.initConfig( + createTestInitConfigInputs({ + languagesInput: "javascript,python", + tempDir, + codeql, + workspacePath: tempDir, + logger, + }), + ); + + // Restore `getActionVersion`. + getActionVersionStub.restore(); + + // The saved config file should now exist + t.true(fs.existsSync(configUtils.getPathToParsedConfigFile(tempDir))); + + // Trying to read the configuration should now throw an error. + await t.throwsAsync(configUtils.getConfig(tempDir, logger), { + instanceOf: ConfigurationError, + }); + }); +}); + test("load input outside of workspace", async (t) => { return await withTmpDir(async (tempDir) => { try { @@ -389,6 +439,7 @@ test("load non-empty input", async (t) => { // And the config we expect it to parse to const expectedConfig: configUtils.Config = { + version: actionsUtil.getActionVersion(), analysisKinds: [AnalysisKind.CodeScanning], languages: [KnownLanguage.javascript], buildMode: BuildMode.None, diff --git a/src/config-utils.ts b/src/config-utils.ts index 92d36920c..7bdc29a2e 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -5,7 +5,7 @@ import { performance } from "perf_hooks"; import * as yaml from "js-yaml"; import * as semver from "semver"; -import { isAnalyzingPullRequest } from "./actions-util"; +import { getActionVersion, isAnalyzingPullRequest } from "./actions-util"; import { AnalysisConfig, AnalysisKind, @@ -102,6 +102,10 @@ interface IncludeQueryFilter { * Format of the parsed config file. */ export interface Config { + /** + * The version of the CodeQL Action that the configuration is for. + */ + version: string; /** * Set of analysis kinds that are enabled. */ @@ -591,6 +595,7 @@ export async function initActionState( ); return { + version: getActionVersion(), analysisKinds, languages, buildMode, @@ -1308,7 +1313,21 @@ export async function getConfig( const configString = fs.readFileSync(configFile, "utf8"); logger.debug("Loaded config:"); logger.debug(configString); - return JSON.parse(configString) as Config; + + const config = JSON.parse(configString) as Partial; + + if (config.version === undefined) { + throw new ConfigurationError( + `Loaded configuration file, but it does not contain the expected 'version' field.`, + ); + } + if (config.version !== getActionVersion()) { + throw new ConfigurationError( + `Loaded a configuration file for version '${config.version}', but running version '${getActionVersion()}'`, + ); + } + + return config as Config; } /** diff --git a/src/testing-utils.ts b/src/testing-utils.ts index a12c99585..c930d5350 100644 --- a/src/testing-utils.ts +++ b/src/testing-utils.ts @@ -6,6 +6,7 @@ import { TestFn } from "ava"; import nock from "nock"; import * as sinon from "sinon"; +import { getActionVersion } from "./actions-util"; import { AnalysisKind } from "./analyses"; import * as apiClient from "./api-client"; import { GitHubApiDetails } from "./api-client"; @@ -356,6 +357,7 @@ export function createTestConfig(overrides: Partial): Config { return Object.assign( {}, { + version: getActionVersion(), analysisKinds: [AnalysisKind.CodeScanning], languages: [], buildMode: undefined, From 754f2e184f461607b45a1d55105127a48df3a7a9 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 10 Sep 2025 13:35:39 +0100 Subject: [PATCH 13/21] Simplify `step.uses` condition --- lib/init-action.js | 2 +- src/workflow.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/init-action.js b/lib/init-action.js index 23b807806..c852c7529 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -90168,7 +90168,7 @@ async function getWorkflowErrors(doc, codeql) { for (const job of Object.values(doc?.jobs || {})) { if (Array.isArray(job.steps)) { for (const step of job.steps) { - if (step.uses !== void 0 && step.uses.startsWith("github/codeql-action/")) { + if (step.uses?.startsWith("github/codeql-action/")) { const parts = step.uses.split("@"); if (parts.length >= 2) { codeqlStepRefs.push(parts[parts.length - 1]); diff --git a/src/workflow.ts b/src/workflow.ts index 330e00c2b..ee95c337f 100644 --- a/src/workflow.ts +++ b/src/workflow.ts @@ -168,10 +168,7 @@ export async function getWorkflowErrors( for (const job of Object.values(doc?.jobs || {})) { if (Array.isArray(job.steps)) { for (const step of job.steps) { - if ( - step.uses !== undefined && - step.uses.startsWith("github/codeql-action/") - ) { + if (step.uses?.startsWith("github/codeql-action/")) { const parts = step.uses.split("@"); if (parts.length >= 2) { codeqlStepRefs.push(parts[parts.length - 1]); From 0487de31f2ad4c10b69c0828707cbb3cf0d43287 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 10 Sep 2025 13:47:47 +0100 Subject: [PATCH 14/21] Update src/config-utils.test.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/config-utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index c47f6d8a2..f2aa21d12 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -298,7 +298,7 @@ test("loading config with version mismatch throws", async (t) => { // Stub `getActionVersion` to return some nonsense. const getActionVersionStub = sinon .stub(actionsUtil, "getActionVersion") - .resolves("does-not-exist"); + .returns("does-not-exist"); await configUtils.initConfig( createTestInitConfigInputs({ From e49458befe579c5a1088aacda9f2ae384da104ff Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 10 Sep 2025 16:14:19 +0100 Subject: [PATCH 15/21] Fix `runInterpretResultsFor` using the wrong `AnalysisConfig` for `category` fix --- lib/analyze-action.js | 2 +- src/analyze.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/analyze-action.js b/lib/analyze-action.js index b59db2a76..ce00934a0 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -93706,7 +93706,7 @@ async function runQueries(sarifFolder, memoryFlag, addSnippetsFlag, threadsFlag, async function runInterpretResultsFor(analysis, language, queries, enableDebugLogging) { logger.info(`Interpreting ${analysis.name} results for ${language}`); let category = automationDetailsId; - if (dbAnalysisConfig.kind === "code-quality" /* CodeQuality */) { + if (analysis.kind === "code-quality" /* CodeQuality */) { category = fixCodeQualityCategory(logger, automationDetailsId); } const sarifFile = path16.join( diff --git a/src/analyze.ts b/src/analyze.ts index 153b00a1f..b22e9c23d 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -780,7 +780,7 @@ export async function runQueries( // If this is a Code Quality analysis, correct the category to one // accepted by the Code Quality backend. let category = automationDetailsId; - if (dbAnalysisConfig.kind === analyses.AnalysisKind.CodeQuality) { + if (analysis.kind === analyses.AnalysisKind.CodeQuality) { category = fixCodeQualityCategory(logger, automationDetailsId); } From e68956d90b7fe2260904652cd8de5d73563e4944 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 15:34:46 +0000 Subject: [PATCH 16/21] Update changelog for v3.30.3 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82679e074..1c2a10367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs. -## [UNRELEASED] +## 3.30.3 - 10 Sep 2025 No user facing changes. From 191d3de659cf60f0adf000328219b573d31d755c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:32:56 +0000 Subject: [PATCH 17/21] Update changelog and version after v3.30.3 --- CHANGELOG.md | 4 ++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c2a10367..d4dcc3b27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs. +## [UNRELEASED] + +No user facing changes. + ## 3.30.3 - 10 Sep 2025 No user facing changes. diff --git a/package-lock.json b/package-lock.json index 12ab1133b..a50a405a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codeql", - "version": "3.30.3", + "version": "3.30.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codeql", - "version": "3.30.3", + "version": "3.30.4", "license": "MIT", "dependencies": { "@actions/artifact": "^2.3.1", diff --git a/package.json b/package.json index dacadc732..42850ef00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeql", - "version": "3.30.3", + "version": "3.30.4", "private": true, "description": "CodeQL action", "scripts": { From 25c32186df7d3d5425e71fd8d4ba5fbdee533492 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:35:23 +0000 Subject: [PATCH 18/21] Rebuild --- lib/analyze-action-post.js | 2 +- lib/analyze-action.js | 2 +- lib/autobuild-action.js | 2 +- lib/init-action-post.js | 2 +- lib/init-action.js | 2 +- lib/resolve-environment-action.js | 2 +- lib/start-proxy-action-post.js | 2 +- lib/start-proxy-action.js | 2 +- lib/upload-lib.js | 2 +- lib/upload-sarif-action-post.js | 2 +- lib/upload-sarif-action.js | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 312e02135..a98989ee2 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -26438,7 +26438,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/analyze-action.js b/lib/analyze-action.js index ce00934a0..c34bc466e 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -32287,7 +32287,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index 38c842dce..d3fdae571 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -26438,7 +26438,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 1cb50a92d..30bdddb0f 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -32287,7 +32287,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/init-action.js b/lib/init-action.js index 804f24af1..7f1fb7bce 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -32287,7 +32287,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index edc2cc242..c1a6a53cc 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -26438,7 +26438,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index f4ae973df..9fca1f4dc 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -26438,7 +26438,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/start-proxy-action.js b/lib/start-proxy-action.js index ad4e5c882..6e121d96a 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -44966,7 +44966,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/upload-lib.js b/lib/upload-lib.js index d816e145f..6a19099b6 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -33584,7 +33584,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index 76753057d..c4f1fc8ee 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -26438,7 +26438,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index 3335f82c2..55a1f1129 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -32287,7 +32287,7 @@ var require_package = __commonJS({ "package.json"(exports2, module2) { module2.exports = { name: "codeql", - version: "3.30.3", + version: "3.30.4", private: true, description: "CodeQL action", scripts: { From 21a1feb3910bf337f889bae8727d0b68ddf6acea Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Thu, 11 Sep 2025 12:03:09 +0100 Subject: [PATCH 19/21] Add changenote for improved version consistency checks --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4dcc3b27..1baf1076d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th ## [UNRELEASED] -No user facing changes. +- We have improved the CodeQL Action's ability to validate that the workflow it is used in does not use different versions of the CodeQL Action for different workflow steps. Mixing different versions of the CodeQL Action in the same workflow is unsupported and can lead to unpredictable results. A warning will now be emitted from the `codeql-action/init` step if different versions of the CodeQL Action are detected in the workflow file. Additionally, an error will now be thrown by the other CodeQL Action steps if they load a configuration file that was generated by a different version of the `codeql-action/init` step. [#3099](https://github.com/github/codeql-action/pull/3099) and [#3100](https://github.com/github/codeql-action/pull/3100) ## 3.30.3 - 10 Sep 2025 From 4e2e64a92a4b1f28841376f56459f1cf9390d39e Mon Sep 17 00:00:00 2001 From: Chuan-kai Lin Date: Thu, 11 Sep 2025 08:07:50 -0700 Subject: [PATCH 20/21] init-action: save updated config This commit updates the init action to save the config again at the end of run(), so that config updates in run() are correctly propagated to the analyze action. --- lib/init-action.js | 1 + src/config-utils.ts | 2 +- src/init-action.ts | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/init-action.js b/lib/init-action.js index dbd5a8926..dab7782f8 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -90701,6 +90701,7 @@ exec ${goBinaryPath} "$@"` } finally { logUnwrittenDiagnostics(); } + await saveConfig(config, logger); await sendCompletedStatusReport( startedAt, config, diff --git a/src/config-utils.ts b/src/config-utils.ts index 7bdc29a2e..c84e9ecfc 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -1289,7 +1289,7 @@ export function getPathToParsedConfigFile(tempDir: string): string { /** * Store the given config to the path returned from getPathToParsedConfigFile. */ -async function saveConfig(config: Config, logger: Logger) { +export async function saveConfig(config: Config, logger: Logger) { const configString = JSON.stringify(config); const configFile = getPathToParsedConfigFile(config.tempDir); fs.mkdirSync(path.dirname(configFile), { recursive: true }); diff --git a/src/init-action.ts b/src/init-action.ts index 8c9e2f36b..4e9446b84 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -680,6 +680,12 @@ async function run() { } finally { logUnwrittenDiagnostics(); } + + // We may have updated the config returned from `initConfig`, e.g. to revert + // to `OverlayDatabaseMode.None` if we failed to download an overlay-base + // database. So we save the config again, to ensure that the `analyze` step + // reads the correct config. + await configUtils.saveConfig(config, logger); await sendCompletedStatusReport( startedAt, config, From 5c30ae46c128cd17cd9e481541d335b9e7bb4e68 Mon Sep 17 00:00:00 2001 From: Chuan-kai Lin Date: Thu, 11 Sep 2025 12:31:29 -0700 Subject: [PATCH 21/21] Stop saving config in initConfig() --- lib/init-action.js | 1 - src/config-utils.test.ts | 7 +++++-- src/config-utils.ts | 3 --- src/init-action.ts | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/init-action.js b/lib/init-action.js index dab7782f8..209232427 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87687,7 +87687,6 @@ async function initConfig(inputs) { exclude: { tags: "exclude-from-incremental" } }); } - await saveConfig(config, logger); return config; } function parseRegistries(registriesInput) { diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index f2aa21d12..bc7307c19 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -229,7 +229,7 @@ test("load code quality config", async (t) => { }); }); -test("loading config saves config", async (t) => { +test("loading a saved config produces the same config", async (t) => { return await withTmpDir(async (tempDir) => { const logger = getRunnerLogger(true); @@ -259,6 +259,7 @@ test("loading config saves config", async (t) => { logger, }), ); + await configUtils.saveConfig(config1, logger); // The saved config file should now exist t.true(fs.existsSync(configUtils.getPathToParsedConfigFile(tempDir))); @@ -300,7 +301,7 @@ test("loading config with version mismatch throws", async (t) => { .stub(actionsUtil, "getActionVersion") .returns("does-not-exist"); - await configUtils.initConfig( + const config = await configUtils.initConfig( createTestInitConfigInputs({ languagesInput: "javascript,python", tempDir, @@ -309,6 +310,8 @@ test("loading config with version mismatch throws", async (t) => { logger, }), ); + // initConfig does not save the config, so we do it here. + await configUtils.saveConfig(config, logger); // Restore `getActionVersion`. getActionVersionStub.restore(); diff --git a/src/config-utils.ts b/src/config-utils.ts index c84e9ecfc..93a3cce47 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -1189,9 +1189,6 @@ export async function initConfig(inputs: InitConfigInputs): Promise { exclude: { tags: "exclude-from-incremental" }, }); } - - // Save the config so we can easily access it again in the future - await saveConfig(config, logger); return config; } diff --git a/src/init-action.ts b/src/init-action.ts index 4e9446b84..aa97b65fc 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -681,10 +681,10 @@ async function run() { logUnwrittenDiagnostics(); } - // We may have updated the config returned from `initConfig`, e.g. to revert - // to `OverlayDatabaseMode.None` if we failed to download an overlay-base - // database. So we save the config again, to ensure that the `analyze` step - // reads the correct config. + // We save the config here instead of at the end of `initConfig` because we + // may have updated the config returned from `initConfig`, e.g. to revert to + // `OverlayDatabaseMode.None` if we failed to download an overlay-base + // database. await configUtils.saveConfig(config, logger); await sendCompletedStatusReport( startedAt,