Merge pull request #3107 from github/nickrolfe/minimize-jars

Add feature flag to roll out JAR minimization in the Java extractor
This commit is contained in:
Nick Rolfe
2025-09-12 13:09:42 +01:00
committed by GitHub
16 changed files with 164 additions and 23 deletions
+1
View File
@@ -5,6 +5,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
## [UNRELEASED]
- 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)
- We added support for reducing the size of dependency caches for Java analyses, which will reduce cache usage and speed up workflows. This will be enabled automatically at a later time. [#3107](https://github.com/github/codeql-action/pull/3107)
## 3.30.3 - 10 Sep 2025
+5
View File
@@ -117918,6 +117918,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
+18 -6
View File
@@ -91152,6 +91152,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
var FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
@@ -93241,7 +93246,7 @@ function getDefaultCacheConfig() {
async function makeGlobber(patterns) {
return glob.create(patterns.join("\n"));
}
async function uploadDependencyCaches(config, logger) {
async function uploadDependencyCaches(config, logger, minimizeJavaJars) {
for (const language of config.languages) {
const cacheConfig = getDefaultCacheConfig()[language];
if (cacheConfig === void 0) {
@@ -93264,7 +93269,7 @@ async function uploadDependencyCaches(config, logger) {
);
continue;
}
const key = await cacheKey2(language, cacheConfig);
const key = await cacheKey2(language, cacheConfig, minimizeJavaJars);
logger.info(
`Uploading cache of size ${size} for ${language} with key ${key}...`
);
@@ -93282,17 +93287,20 @@ async function uploadDependencyCaches(config, logger) {
}
}
}
async function cacheKey2(language, cacheConfig) {
async function cacheKey2(language, cacheConfig, minimizeJavaJars = false) {
const hash2 = await glob.hashFiles(cacheConfig.hash.join("\n"));
return `${await cachePrefix2(language)}${hash2}`;
return `${await cachePrefix2(language, minimizeJavaJars)}${hash2}`;
}
async function cachePrefix2(language) {
async function cachePrefix2(language, minimizeJavaJars) {
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const customPrefix = process.env["CODEQL_ACTION_DEPENDENCY_CACHE_PREFIX" /* DEPENDENCY_CACHING_PREFIX */];
let prefix = CODEQL_DEPENDENCY_CACHE_PREFIX;
if (customPrefix !== void 0 && customPrefix.length > 0) {
prefix = `${prefix}-${customPrefix}`;
}
if (language === "java" /* java */ && minimizeJavaJars) {
prefix = `minify-${prefix}`;
}
return `${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
}
@@ -96074,7 +96082,11 @@ async function run() {
logger
);
if (shouldStoreCache(config.dependencyCachingEnabled)) {
await uploadDependencyCaches(config, logger);
const minimizeJavaJars = await features.getValue(
"java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */,
codeql
);
await uploadDependencyCaches(config, logger, minimizeJavaJars);
}
if (isInTestMode()) {
logger.debug("In test mode. Waiting for processing is disabled.");
+5
View File
@@ -78656,6 +78656,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
var FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
+5
View File
@@ -129251,6 +129251,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
var FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
+35 -7
View File
@@ -86751,6 +86751,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
var FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
@@ -87968,7 +87973,7 @@ function getDefaultCacheConfig() {
async function makeGlobber(patterns) {
return glob.create(patterns.join("\n"));
}
async function downloadDependencyCaches(languages, logger) {
async function downloadDependencyCaches(languages, logger, minimizeJavaJars) {
const restoredCaches = [];
for (const language of languages) {
const cacheConfig = getDefaultCacheConfig()[language];
@@ -87985,8 +87990,10 @@ async function downloadDependencyCaches(languages, logger) {
);
continue;
}
const primaryKey = await cacheKey2(language, cacheConfig);
const restoreKeys = [await cachePrefix2(language)];
const primaryKey = await cacheKey2(language, cacheConfig, minimizeJavaJars);
const restoreKeys = [
await cachePrefix2(language, minimizeJavaJars)
];
logger.info(
`Downloading cache for ${language} with key ${primaryKey} and restore keys ${restoreKeys.join(
", "
@@ -88006,17 +88013,20 @@ async function downloadDependencyCaches(languages, logger) {
}
return restoredCaches;
}
async function cacheKey2(language, cacheConfig) {
async function cacheKey2(language, cacheConfig, minimizeJavaJars = false) {
const hash = await glob.hashFiles(cacheConfig.hash.join("\n"));
return `${await cachePrefix2(language)}${hash}`;
return `${await cachePrefix2(language, minimizeJavaJars)}${hash}`;
}
async function cachePrefix2(language) {
async function cachePrefix2(language, minimizeJavaJars) {
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const customPrefix = process.env["CODEQL_ACTION_DEPENDENCY_CACHE_PREFIX" /* DEPENDENCY_CACHING_PREFIX */];
let prefix = CODEQL_DEPENDENCY_CACHE_PREFIX;
if (customPrefix !== void 0 && customPrefix.length > 0) {
prefix = `${prefix}-${customPrefix}`;
}
if (language === "java" /* java */ && minimizeJavaJars) {
prefix = `minify-${prefix}`;
}
return `${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
}
@@ -90622,8 +90632,16 @@ exec ${goBinaryPath} "$@"`
core13.exportVariable(envVar, "false");
}
}
const minimizeJavaJars = await features.getValue(
"java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */,
codeql
);
if (shouldRestoreCache(config.dependencyCachingEnabled)) {
await downloadDependencyCaches(config.languages, logger);
await downloadDependencyCaches(
config.languages,
logger,
minimizeJavaJars
);
}
if (await codeQlVersionAtLeast(codeql, "2.17.1")) {
} else {
@@ -90656,6 +90674,16 @@ exec ${goBinaryPath} "$@"`
core13.exportVariable("CODEQL_EXTRACTOR_PYTHON_EXTRACT_STDLIB", "true");
}
}
if (process.env["CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */]) {
logger.debug(
`${"CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */} is already set to '${process.env["CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */]}', so the Action will not override it.`
);
} else if (minimizeJavaJars && config.buildMode === "none" /* None */ && config.languages.includes("java" /* java */)) {
core13.exportVariable(
"CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS" /* JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS */,
"true"
);
}
const { registriesAuthTokens, qlconfigFile } = await generateRegistries(
getOptionalInput("registries"),
config.tempDir,
+5
View File
@@ -78647,6 +78647,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
+5
View File
@@ -117327,6 +117327,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
+5
View File
@@ -89343,6 +89343,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
+5
View File
@@ -117492,6 +117492,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
+5
View File
@@ -89339,6 +89339,11 @@ var featureConfig = {
envVar: "CODEQL_ACTION_QA_TELEMETRY",
legacyApi: true,
minimumVersion: void 0
},
["java_minimize_dependency_jars" /* JavaMinimizeDependencyJars */]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0"
}
};
var FEATURE_FLAGS_FILE_NAME = "cached-feature-flags.json";
+6 -2
View File
@@ -29,7 +29,7 @@ import { uploadDatabases } from "./database-upload";
import { uploadDependencyCaches } from "./dependency-caching";
import { getDiffInformedAnalysisBranches } from "./diff-informed-analysis-utils";
import { EnvVar } from "./environment";
import { Features } from "./feature-flags";
import { Feature, Features } from "./feature-flags";
import { KnownLanguage } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { uploadOverlayBaseDatabaseToCache } from "./overlay-database-utils";
@@ -384,7 +384,11 @@ async function run() {
// Store dependency cache(s) if dependency caching is enabled.
if (shouldStoreCache(config.dependencyCachingEnabled)) {
await uploadDependencyCaches(config, logger);
const minimizeJavaJars = await features.getValue(
Feature.JavaMinimizeDependencyJars,
codeql,
);
await uploadDependencyCaches(config, logger, minimizeJavaJars);
}
// We don't upload results in test mode, so don't wait for processing
+27 -7
View File
@@ -8,7 +8,7 @@ import { getTemporaryDirectory } from "./actions-util";
import { getTotalCacheSize } from "./caching-utils";
import { Config } from "./config-utils";
import { EnvVar } from "./environment";
import { Language } from "./languages";
import { KnownLanguage, Language } from "./languages";
import { Logger } from "./logging";
import { getRequiredEnvParam } from "./util";
@@ -89,11 +89,13 @@ async function makeGlobber(patterns: string[]): Promise<glob.Globber> {
*
* @param languages The languages being analyzed.
* @param logger A logger to record some informational messages to.
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
* @returns A list of languages for which dependency caches were restored.
*/
export async function downloadDependencyCaches(
languages: Language[],
logger: Logger,
minimizeJavaJars: boolean,
): Promise<Language[]> {
const restoredCaches: Language[] = [];
@@ -118,8 +120,10 @@ export async function downloadDependencyCaches(
continue;
}
const primaryKey = await cacheKey(language, cacheConfig);
const restoreKeys: string[] = [await cachePrefix(language)];
const primaryKey = await cacheKey(language, cacheConfig, minimizeJavaJars);
const restoreKeys: string[] = [
await cachePrefix(language, minimizeJavaJars),
];
logger.info(
`Downloading cache for ${language} with key ${primaryKey} and restore keys ${restoreKeys.join(
@@ -149,8 +153,13 @@ export async function downloadDependencyCaches(
*
* @param config The configuration for this workflow.
* @param logger A logger to record some informational messages to.
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
*/
export async function uploadDependencyCaches(config: Config, logger: Logger) {
export async function uploadDependencyCaches(
config: Config,
logger: Logger,
minimizeJavaJars: boolean,
): Promise<void> {
for (const language of config.languages) {
const cacheConfig = getDefaultCacheConfig()[language];
@@ -192,7 +201,7 @@ export async function uploadDependencyCaches(config: Config, logger: Logger) {
continue;
}
const key = await cacheKey(language, cacheConfig);
const key = await cacheKey(language, cacheConfig, minimizeJavaJars);
logger.info(
`Uploading cache of size ${size} for ${language} with key ${key}...`,
@@ -222,14 +231,16 @@ export async function uploadDependencyCaches(config: Config, logger: Logger) {
*
* @param language The language being analyzed.
* @param cacheConfig The cache configuration for the language.
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
* @returns A cache key capturing information about the project(s) being analyzed in the specified language.
*/
async function cacheKey(
language: Language,
cacheConfig: CacheConfig,
minimizeJavaJars: boolean = false,
): Promise<string> {
const hash = await glob.hashFiles(cacheConfig.hash.join("\n"));
return `${await cachePrefix(language)}${hash}`;
return `${await cachePrefix(language, minimizeJavaJars)}${hash}`;
}
/**
@@ -237,9 +248,13 @@ async function cacheKey(
* can be changed to invalidate old caches, the runner's operating system, and the specified language name.
*
* @param language The language being analyzed.
* @param minimizeJavaJars Whether the Java extractor should rewrite downloaded JARs to minimize their size.
* @returns The prefix that identifies what a cache is for.
*/
async function cachePrefix(language: Language): Promise<string> {
async function cachePrefix(
language: Language,
minimizeJavaJars: boolean,
): Promise<string> {
const runnerOs = getRequiredEnvParam("RUNNER_OS");
const customPrefix = process.env[EnvVar.DEPENDENCY_CACHING_PREFIX];
let prefix = CODEQL_DEPENDENCY_CACHE_PREFIX;
@@ -248,5 +263,10 @@ async function cachePrefix(language: Language): Promise<string> {
prefix = `${prefix}-${customPrefix}`;
}
// To ensure a safe rollout of JAR minimization, we change the key when the feature is enabled.
if (language === KnownLanguage.java && minimizeJavaJars) {
prefix = `minify-${prefix}`;
}
return `${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
}
+3
View File
@@ -115,6 +115,9 @@ export enum EnvVar {
*/
DEPENDENCY_CACHING_PREFIX = "CODEQL_ACTION_DEPENDENCY_CACHE_PREFIX",
/** Used by the Java extractor option to enable minimizing dependency JARs. */
JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS = "CODEQL_EXTRACTOR_JAVA_OPTION_MINIMIZE_DEPENDENCY_JARS",
/**
* Whether to enable experimental extractors for CodeQL.
*/
+6
View File
@@ -50,6 +50,7 @@ export enum Feature {
DisableJavaBuildlessEnabled = "disable_java_buildless_enabled",
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
ExportDiagnosticsEnabled = "export_diagnostics_enabled",
JavaMinimizeDependencyJars = "java_minimize_dependency_jars",
OverlayAnalysis = "overlay_analysis",
OverlayAnalysisActions = "overlay_analysis_actions",
OverlayAnalysisCodeScanningActions = "overlay_analysis_code_scanning_actions",
@@ -269,6 +270,11 @@ export const featureConfig: Record<
legacyApi: true,
minimumVersion: undefined,
},
[Feature.JavaMinimizeDependencyJars]: {
defaultValue: false,
envVar: "CODEQL_ACTION_JAVA_MINIMIZE_DEPENDENCY_JARS",
minimumVersion: "2.23.0",
},
};
/**
+28 -1
View File
@@ -78,6 +78,7 @@ import {
wrapError,
checkActionVersion,
getErrorMessage,
BuildMode,
} from "./util";
import { validateWorkflow } from "./workflow";
@@ -546,8 +547,16 @@ async function run() {
}
// Restore dependency cache(s), if they exist.
const minimizeJavaJars = await features.getValue(
Feature.JavaMinimizeDependencyJars,
codeql,
);
if (shouldRestoreCache(config.dependencyCachingEnabled)) {
await downloadDependencyCaches(config.languages, logger);
await downloadDependencyCaches(
config.languages,
logger,
minimizeJavaJars,
);
}
// Suppress warnings about disabled Python library extraction.
@@ -597,6 +606,24 @@ async function run() {
}
}
// If the feature flag to minimize Java dependency jars is enabled, and we are doing a Java
// `build-mode: none` analysis (i.e. the flag is relevant), then set the environment variable
// that enables the corresponding option in the Java extractor.
if (process.env[EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS]) {
logger.debug(
`${EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS} is already set to '${process.env[EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS]}', so the Action will not override it.`,
);
} else if (
minimizeJavaJars &&
config.buildMode === BuildMode.None &&
config.languages.includes(KnownLanguage.java)
) {
core.exportVariable(
EnvVar.JAVA_EXTRACTOR_MINIMIZE_DEPENDENCY_JARS,
"true",
);
}
const { registriesAuthTokens, qlconfigFile } =
await configUtils.generateRegistries(
getOptionalInput("registries"),