Add feature flag to roll out JAR minimization in the Java extractor

This commit is contained in:
Nick Rolfe
2025-09-10 17:26:55 +01:00
parent df1fe23118
commit 0abf548bb3
15 changed files with 163 additions and 23 deletions

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

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}-`;
}

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.
*/

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",
},
};
/**

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"),