diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index 37725d00bf..65c461c5af 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -120038,6 +120038,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", @@ -120880,6 +120885,9 @@ var glob = __toESM(require_glob3()); function getJavaTempDependencyDir() { return (0, import_path.join)(getTemporaryDirectory(), "codeql_java", "repository"); } +function getCsharpTempDependencyDir() { + return (0, import_path.join)(getTemporaryDirectory(), "codeql_csharp", "repository"); +} // src/debug-artifacts.ts function sanitizeArtifactName(name) { @@ -121000,14 +121008,19 @@ async function runWrapper() { ); } } - const javaTempDependencyDir = getJavaTempDependencyDir(); - if (fs6.existsSync(javaTempDependencyDir)) { - try { - fs6.rmSync(javaTempDependencyDir, { recursive: true }); - } catch (error3) { - logger.info( - `Failed to remove temporary Java dependencies directory: ${getErrorMessage(error3)}` - ); + const tempDependencyDirs = [ + getJavaTempDependencyDir(), + getCsharpTempDependencyDir() + ]; + for (const tempDependencyDir of tempDependencyDirs) { + if (fs6.existsSync(tempDependencyDir)) { + try { + fs6.rmSync(tempDependencyDir, { recursive: true }); + } catch (error3) { + logger.info( + `Failed to remove temporary dependencies directory: ${getErrorMessage(error3)}` + ); + } } } } catch (error3) { diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 8206afce01..0c9e35dcb4 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -88659,6 +88659,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", @@ -91067,7 +91072,7 @@ var CODEQL_DEPENDENCY_CACHE_VERSION = 1; function getJavaTempDependencyDir() { return (0, import_path.join)(getTemporaryDirectory(), "codeql_java", "repository"); } -function getJavaDependencyDirs() { +async function getJavaDependencyDirs() { return [ // Maven (0, import_path.join)(os3.homedir(), ".m2", "repository"), @@ -91077,6 +91082,19 @@ function getJavaDependencyDirs() { getJavaTempDependencyDir() ]; } +function getCsharpTempDependencyDir() { + return (0, import_path.join)(getTemporaryDirectory(), "codeql_csharp", "repository"); +} +async function getCsharpDependencyDirs(codeql, features) { + const dirs = [ + // Nuget + (0, import_path.join)(os3.homedir(), ".nuget", "packages") + ]; + if (await features.getValue("csharp_cache_bmn" /* CsharpCacheBuildModeNone */, codeql)) { + dirs.push(getCsharpTempDependencyDir()); + } + return dirs; +} async function makePatternCheck(patterns) { const globber = await makeGlobber(patterns); if ((await globber.glob()).length === 0) { @@ -91121,11 +91139,11 @@ var defaultCacheConfigs = { ]) }, csharp: { - getDependencyPaths: () => [(0, import_path.join)(os3.homedir(), ".nuget", "packages")], + getDependencyPaths: getCsharpDependencyDirs, getHashPatterns: getCsharpHashPatterns }, go: { - getDependencyPaths: () => [(0, import_path.join)(os3.homedir(), "go", "pkg", "mod")], + getDependencyPaths: async () => [(0, import_path.join)(os3.homedir(), "go", "pkg", "mod")], getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]) } }; @@ -91169,7 +91187,7 @@ async function uploadDependencyCaches(codeql, features, config, logger) { continue; } const size = await getTotalCacheSize( - cacheConfig.getDependencyPaths(), + await cacheConfig.getDependencyPaths(codeql, features), logger, true ); @@ -91185,7 +91203,10 @@ async function uploadDependencyCaches(codeql, features, config, logger) { ); try { const start = performance.now(); - await actionsCache3.saveCache(cacheConfig.getDependencyPaths(), key); + await actionsCache3.saveCache( + await cacheConfig.getDependencyPaths(codeql, features), + key + ); const upload_duration_ms = Math.round(performance.now() - start); status.push({ language, @@ -91228,6 +91249,7 @@ async function getFeaturePrefix(codeql, features, language) { } } else if (language === "csharp" /* csharp */) { await addFeatureIfEnabled("csharp_new_cache_key" /* CsharpNewCacheKey */); + await addFeatureIfEnabled("csharp_cache_bmn" /* CsharpCacheBuildModeNone */); } if (enabledFeatures.length > 0) { return `${createCacheKeyHash(enabledFeatures)}-`; @@ -91317,7 +91339,7 @@ async function setupPythonExtractor(logger) { ); return; } -async function runExtraction(codeql, config, logger) { +async function runExtraction(codeql, features, config, logger) { for (const language of config.languages) { if (dbIsFinalized(config, language, logger)) { logger.debug( @@ -91337,6 +91359,9 @@ async function runExtraction(codeql, config, logger) { if (language === "java" /* java */ && config.buildMode === "none" /* None */) { process.env["CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_DEPENDENCY_DIR"] = getJavaTempDependencyDir(); } + if (language === "csharp" /* csharp */ && config.buildMode === "none" /* None */ && await features.getValue("csharp_cache_bmn" /* CsharpCacheBuildModeNone */)) { + process.env["CODEQL_EXTRACTOR_CSHARP_OPTION_BUILDLESS_DEPENDENCY_DIR"] = getCsharpTempDependencyDir(); + } await codeql.extractUsingBuildMode(config, language); } else { await codeql.extractScannedLanguage(config, language); @@ -91362,9 +91387,9 @@ function dbIsFinalized(config, language, logger) { return false; } } -async function finalizeDatabaseCreation(codeql, config, threadsFlag, memoryFlag, logger) { +async function finalizeDatabaseCreation(codeql, features, config, threadsFlag, memoryFlag, logger) { const extractionStart = import_perf_hooks2.performance.now(); - await runExtraction(codeql, config, logger); + await runExtraction(codeql, features, config, logger); const extractionTime = import_perf_hooks2.performance.now() - extractionStart; const trapImportStart = import_perf_hooks2.performance.now(); for (const language of config.languages) { @@ -91619,7 +91644,7 @@ async function runQueries(sarifFolder, memoryFlag, threadsFlag, diffRangePackDir return perQueryAlertCounts; } } -async function runFinalize(outputDir, threadsFlag, memoryFlag, codeql, config, logger) { +async function runFinalize(features, outputDir, threadsFlag, memoryFlag, codeql, config, logger) { try { await fs12.promises.rm(outputDir, { force: true, recursive: true }); } catch (error3) { @@ -91630,6 +91655,7 @@ async function runFinalize(outputDir, threadsFlag, memoryFlag, codeql, config, l await fs12.promises.mkdir(outputDir, { recursive: true }); const timings = await finalizeDatabaseCreation( codeql, + features, config, threadsFlag, memoryFlag, @@ -93965,6 +93991,7 @@ async function run() { await warnIfGoInstalledAfterInit(config, logger); await runAutobuildIfLegacyGoWorkflow(config, logger); dbCreationTimings = await runFinalize( + features, outputDir, threads, memory, diff --git a/lib/autobuild-action.js b/lib/autobuild-action.js index d4b7fc1f6d..e9f13367e9 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -83978,6 +83978,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 62e78df8a5..2bcddfbfa9 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -123419,6 +123419,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/init-action.js b/lib/init-action.js index 185510e02e..b5bf662169 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -86073,6 +86073,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", @@ -87260,7 +87265,7 @@ var CODEQL_DEPENDENCY_CACHE_VERSION = 1; function getJavaTempDependencyDir() { return (0, import_path.join)(getTemporaryDirectory(), "codeql_java", "repository"); } -function getJavaDependencyDirs() { +async function getJavaDependencyDirs() { return [ // Maven (0, import_path.join)(os2.homedir(), ".m2", "repository"), @@ -87270,6 +87275,19 @@ function getJavaDependencyDirs() { getJavaTempDependencyDir() ]; } +function getCsharpTempDependencyDir() { + return (0, import_path.join)(getTemporaryDirectory(), "codeql_csharp", "repository"); +} +async function getCsharpDependencyDirs(codeql, features) { + const dirs = [ + // Nuget + (0, import_path.join)(os2.homedir(), ".nuget", "packages") + ]; + if (await features.getValue("csharp_cache_bmn" /* CsharpCacheBuildModeNone */, codeql)) { + dirs.push(getCsharpTempDependencyDir()); + } + return dirs; +} async function makePatternCheck(patterns) { const globber = await makeGlobber(patterns); if ((await globber.glob()).length === 0) { @@ -87314,11 +87332,11 @@ var defaultCacheConfigs = { ]) }, csharp: { - getDependencyPaths: () => [(0, import_path.join)(os2.homedir(), ".nuget", "packages")], + getDependencyPaths: getCsharpDependencyDirs, getHashPatterns: getCsharpHashPatterns }, go: { - getDependencyPaths: () => [(0, import_path.join)(os2.homedir(), "go", "pkg", "mod")], + getDependencyPaths: async () => [(0, import_path.join)(os2.homedir(), "go", "pkg", "mod")], getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]) } }; @@ -87368,7 +87386,7 @@ async function downloadDependencyCaches(codeql, features, languages, logger) { ); const start = performance.now(); const hitKey = await actionsCache3.restoreCache( - cacheConfig.getDependencyPaths(), + await cacheConfig.getDependencyPaths(codeql, features), primaryKey, restoreKeys ); @@ -87413,6 +87431,7 @@ async function getFeaturePrefix(codeql, features, language) { } } else if (language === "csharp" /* csharp */) { await addFeatureIfEnabled("csharp_new_cache_key" /* CsharpNewCacheKey */); + await addFeatureIfEnabled("csharp_cache_bmn" /* CsharpCacheBuildModeNone */); } if (enabledFeatures.length > 0) { return `${createCacheKeyHash(enabledFeatures)}-`; diff --git a/lib/resolve-environment-action.js b/lib/resolve-environment-action.js index cd65a4bf1c..96e60bb697 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -83969,6 +83969,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/setup-codeql-action.js b/lib/setup-codeql-action.js index 780d2cc6de..06df5ebf83 100644 --- a/lib/setup-codeql-action.js +++ b/lib/setup-codeql-action.js @@ -83881,6 +83881,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/start-proxy-action-post.js b/lib/start-proxy-action-post.js index c78e8262a6..c3fcc799ef 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -119444,6 +119444,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/start-proxy-action.js b/lib/start-proxy-action.js index f0d7eb5716..b8bed55ea7 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -99997,6 +99997,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/upload-lib.js b/lib/upload-lib.js index de44834ac9..f986748275 100644 --- a/lib/upload-lib.js +++ b/lib/upload-lib.js @@ -87034,6 +87034,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/upload-sarif-action-post.js b/lib/upload-sarif-action-post.js index f95b705faf..6256ed8ed5 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -119610,6 +119610,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/lib/upload-sarif-action.js b/lib/upload-sarif-action.js index f8ea28a2d0..9ee03c88c9 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -86831,6 +86831,11 @@ var featureConfig = { legacyApi: true, minimumVersion: "2.15.0" }, + ["csharp_cache_bmn" /* CsharpCacheBuildModeNone */]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: void 0 + }, ["csharp_new_cache_key" /* CsharpNewCacheKey */]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY", diff --git a/src/analyze-action-env.test.ts b/src/analyze-action-env.test.ts index e4960a5803..aecbae4b02 100644 --- a/src/analyze-action-env.test.ts +++ b/src/analyze-action-env.test.ts @@ -74,11 +74,20 @@ test("analyze action with RAM & threads from environment variables", async (t) = // wait for the action promise to complete before starting verification. await analyzeAction.runPromise; - t.assert(runFinalizeStub.calledOnce); - t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1"); - t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=4992"); - t.assert(runQueriesStub.calledOnce); - t.deepEqual(runQueriesStub.firstCall.args[2], "--threads=-1"); - t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=4992"); + t.assert( + runFinalizeStub.calledOnceWith( + sinon.match.any, + sinon.match.any, + "--threads=-1", + "--ram=4992", + ), + ); + t.assert( + runQueriesStub.calledOnceWith( + sinon.match.any, + "--ram=4992", + "--threads=-1", + ), + ); }); }); diff --git a/src/analyze-action-input.test.ts b/src/analyze-action-input.test.ts index 48fa216ebf..74c03923da 100644 --- a/src/analyze-action-input.test.ts +++ b/src/analyze-action-input.test.ts @@ -72,11 +72,20 @@ test("analyze action with RAM & threads from action inputs", async (t) => { // wait for the action promise to complete before starting verification. await analyzeAction.runPromise; - t.assert(runFinalizeStub.calledOnce); - t.deepEqual(runFinalizeStub.firstCall.args[1], "--threads=-1"); - t.deepEqual(runFinalizeStub.firstCall.args[2], "--ram=3012"); - t.assert(runQueriesStub.calledOnce); - t.deepEqual(runQueriesStub.firstCall.args[2], "--threads=-1"); - t.deepEqual(runQueriesStub.firstCall.args[1], "--ram=3012"); + t.assert( + runFinalizeStub.calledOnceWith( + sinon.match.any, + sinon.match.any, + "--threads=-1", + "--ram=3012", + ), + ); + t.assert( + runQueriesStub.calledOnceWith( + sinon.match.any, + "--ram=3012", + "--threads=-1", + ), + ); }); }); diff --git a/src/analyze-action-post.ts b/src/analyze-action-post.ts index 1f91b4f0fd..ce8ddd31bb 100644 --- a/src/analyze-action-post.ts +++ b/src/analyze-action-post.ts @@ -12,7 +12,10 @@ import { getGitHubVersion } from "./api-client"; import { getCodeQL } from "./codeql"; import { getConfig } from "./config-utils"; import * as debugArtifacts from "./debug-artifacts"; -import { getJavaTempDependencyDir } from "./dependency-caching"; +import { + getCsharpTempDependencyDir, + getJavaTempDependencyDir, +} from "./dependency-caching"; import { EnvVar } from "./environment"; import { getActionsLogger } from "./logging"; import { checkGitHubVersionInRange, getErrorMessage } from "./util"; @@ -42,17 +45,22 @@ async function runWrapper() { } } - // If we analysed Java in build-mode: none, we may have downloaded dependencies + // If we analysed Java or C# in build-mode: none, we may have downloaded dependencies // to the temp directory. Clean these up so they don't persist unnecessarily // long on self-hosted runners. - const javaTempDependencyDir = getJavaTempDependencyDir(); - if (fs.existsSync(javaTempDependencyDir)) { - try { - fs.rmSync(javaTempDependencyDir, { recursive: true }); - } catch (error) { - logger.info( - `Failed to remove temporary Java dependencies directory: ${getErrorMessage(error)}`, - ); + const tempDependencyDirs = [ + getJavaTempDependencyDir(), + getCsharpTempDependencyDir(), + ]; + for (const tempDependencyDir of tempDependencyDirs) { + if (fs.existsSync(tempDependencyDir)) { + try { + fs.rmSync(tempDependencyDir, { recursive: true }); + } catch (error) { + logger.info( + `Failed to remove temporary dependencies directory: ${getErrorMessage(error)}`, + ); + } } } } catch (error) { diff --git a/src/analyze-action.ts b/src/analyze-action.ts index abbf239724..f89eed7d1a 100644 --- a/src/analyze-action.ts +++ b/src/analyze-action.ts @@ -315,6 +315,7 @@ async function run() { await runAutobuildIfLegacyGoWorkflow(config, logger); dbCreationTimings = await runFinalize( + features, outputDir, threads, memory, diff --git a/src/analyze.ts b/src/analyze.ts index cd82ad61b1..dc631ba98f 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -10,7 +10,10 @@ import * as analyses from "./analyses"; import { setupCppAutobuild } from "./autobuild"; import { type CodeQL } from "./codeql"; import * as configUtils from "./config-utils"; -import { getJavaTempDependencyDir } from "./dependency-caching"; +import { + getCsharpTempDependencyDir, + getJavaTempDependencyDir, +} from "./dependency-caching"; import { addDiagnostic, makeDiagnostic } from "./diagnostics"; import { DiffThunkRange, @@ -98,6 +101,7 @@ async function setupPythonExtractor(logger: Logger) { export async function runExtraction( codeql: CodeQL, + features: FeatureEnablement, config: configUtils.Config, logger: Logger, ) { @@ -122,7 +126,7 @@ export async function runExtraction( await setupCppAutobuild(codeql, logger); } - // The Java `build-mode: none` extractor places dependencies (.jar files) in the + // The Java and C# `build-mode: none` extractors place dependencies in the // database scratch directory by default. For dependency caching purposes, we want // a stable path that caches can be restored into and that we can cache at the // end of the workflow (i.e. that does not get removed when the scratch directory is). @@ -133,6 +137,15 @@ export async function runExtraction( process.env["CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_DEPENDENCY_DIR"] = getJavaTempDependencyDir(); } + if ( + language === KnownLanguage.csharp && + config.buildMode === BuildMode.None && + (await features.getValue(Feature.CsharpCacheBuildModeNone)) + ) { + process.env[ + "CODEQL_EXTRACTOR_CSHARP_OPTION_BUILDLESS_DEPENDENCY_DIR" + ] = getCsharpTempDependencyDir(); + } await codeql.extractUsingBuildMode(config, language); } else { @@ -177,13 +190,14 @@ export function dbIsFinalized( async function finalizeDatabaseCreation( codeql: CodeQL, + features: FeatureEnablement, config: configUtils.Config, threadsFlag: string, memoryFlag: string, logger: Logger, ): Promise { const extractionStart = performance.now(); - await runExtraction(codeql, config, logger); + await runExtraction(codeql, features, config, logger); const extractionTime = performance.now() - extractionStart; const trapImportStart = performance.now(); @@ -597,6 +611,7 @@ export async function runQueries( } export async function runFinalize( + features: FeatureEnablement, outputDir: string, threadsFlag: string, memoryFlag: string, @@ -615,6 +630,7 @@ export async function runFinalize( const timings = await finalizeDatabaseCreation( codeql, + features, config, threadsFlag, memoryFlag, diff --git a/src/dependency-caching.test.ts b/src/dependency-caching.test.ts index a9b9e6210f..195cb060c8 100644 --- a/src/dependency-caching.test.ts +++ b/src/dependency-caching.test.ts @@ -21,6 +21,8 @@ import { downloadDependencyCaches, CacheHitKind, cacheKey, + getCsharpDependencyDirs, + getCsharpTempDependencyDir, uploadDependencyCaches, CacheStoreResult, } from "./dependency-caching"; @@ -42,6 +44,28 @@ function makeAbsolutePatterns(tmpDir: string, patterns: string[]): string[] { return patterns.map((pattern) => path.join(tmpDir, pattern)); } +test("getCsharpDependencyDirs - does not include BMN dir if FF is enabled", async (t) => { + await withTmpDir(async (tmpDir) => { + process.env["RUNNER_TEMP"] = tmpDir; + const codeql = createStubCodeQL({}); + const features = createFeatures([]); + + const results = await getCsharpDependencyDirs(codeql, features); + t.false(results.includes(getCsharpTempDependencyDir())); + }); +}); + +test("getCsharpDependencyDirs - includes BMN dir if FF is enabled", async (t) => { + await withTmpDir(async (tmpDir) => { + process.env["RUNNER_TEMP"] = tmpDir; + const codeql = createStubCodeQL({}); + const features = createFeatures([Feature.CsharpCacheBuildModeNone]); + + const results = await getCsharpDependencyDirs(codeql, features); + t.assert(results.includes(getCsharpTempDependencyDir())); + }); +}); + test("makePatternCheck - returns undefined if no patterns match", async (t) => { await withTmpDir(async (tmpDir) => { fs.writeFileSync(path.join(tmpDir, "test.java"), ""); @@ -130,7 +154,7 @@ test("checkHashPatterns - logs when no patterns match", async (t) => { const features = createFeatures([]); const messages: LoggedMessage[] = []; const config: CacheConfig = { - getDependencyPaths: () => [], + getDependencyPaths: async () => [], getHashPatterns: async () => undefined, }; @@ -159,7 +183,7 @@ test("checkHashPatterns - returns patterns when patterns match", async (t) => { fs.writeFileSync(path.join(tmpDir, "test.java"), ""); const config: CacheConfig = { - getDependencyPaths: () => [], + getDependencyPaths: async () => [], getHashPatterns: async () => makePatternCheck(patterns), }; @@ -625,3 +649,28 @@ test("getFeaturePrefix - non-C# - returns '' if CsharpNewCacheKey is enabled", a t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`); } }); + +test("getFeaturePrefix - C# - returns prefix if CsharpCacheBuildModeNone is enabled", async (t) => { + const codeql = createStubCodeQL({}); + const features = createFeatures([Feature.CsharpCacheBuildModeNone]); + + const result = await getFeaturePrefix(codeql, features, KnownLanguage.csharp); + t.notDeepEqual(result, ""); + t.assert(result.endsWith("-")); + // Check the length of the prefix, which should correspond to `cacheKeyHashLength` + 1 for the trailing `-`. + t.is(result.length, cacheKeyHashLength + 1); +}); + +test("getFeaturePrefix - non-C# - returns '' if CsharpCacheBuildModeNone is enabled", async (t) => { + const codeql = createStubCodeQL({}); + const features = createFeatures([Feature.CsharpCacheBuildModeNone]); + + for (const knownLanguage of Object.values(KnownLanguage)) { + // Skip C# since we expect a result for it, which is tested in the previous test. + if (knownLanguage === KnownLanguage.csharp) { + continue; + } + const result = await getFeaturePrefix(codeql, features, knownLanguage); + t.deepEqual(result, "", `Expected no feature prefix for ${knownLanguage}`); + } +}); diff --git a/src/dependency-caching.ts b/src/dependency-caching.ts index 350d8e4687..497a31fa55 100644 --- a/src/dependency-caching.ts +++ b/src/dependency-caching.ts @@ -20,7 +20,10 @@ import { getErrorMessage, getRequiredEnvParam } from "./util"; */ export interface CacheConfig { /** Gets the paths of directories on the runner that should be included in the cache. */ - getDependencyPaths: () => string[]; + getDependencyPaths: ( + codeql: CodeQL, + features: FeatureEnablement, + ) => Promise; /** * Gets an array of glob patterns for the paths of files whose contents affect which dependencies are used * by a project. This function also checks whether there are any matching files and returns @@ -55,7 +58,7 @@ export function getJavaTempDependencyDir(): string { * @returns The paths of directories on the runner that should be included in a dependency cache * for a Java analysis. */ -function getJavaDependencyDirs(): string[] { +export async function getJavaDependencyDirs(): Promise { return [ // Maven join(os.homedir(), ".m2", "repository"), @@ -66,6 +69,38 @@ function getJavaDependencyDirs(): string[] { ]; } +/** + * Returns a path to a directory intended to be used to store dependencies + * for the C# `build-mode: none` extractor. + * @returns The path to the directory that should be used by the `build-mode: none` extractor. + */ +export function getCsharpTempDependencyDir(): string { + return join(getTemporaryDirectory(), "codeql_csharp", "repository"); +} + +/** + * Returns an array of paths of directories on the runner that should be included in a dependency cache + * for a C# analysis. + * + * @returns The paths of directories on the runner that should be included in a dependency cache + * for a C# analysis. + */ +export async function getCsharpDependencyDirs( + codeql: CodeQL, + features: FeatureEnablement, +): Promise { + const dirs = [ + // Nuget + join(os.homedir(), ".nuget", "packages"), + ]; + + if (await features.getValue(Feature.CsharpCacheBuildModeNone, codeql)) { + dirs.push(getCsharpTempDependencyDir()); + } + + return dirs; +} + /** * Checks that there are files which match `patterns`. If there are matching files for any of the patterns, * this function returns all `patterns`. Otherwise, `undefined` is returned. @@ -158,11 +193,11 @@ const defaultCacheConfigs: { [language: string]: CacheConfig } = { ]), }, csharp: { - getDependencyPaths: () => [join(os.homedir(), ".nuget", "packages")], + getDependencyPaths: getCsharpDependencyDirs, getHashPatterns: getCsharpHashPatterns, }, go: { - getDependencyPaths: () => [join(os.homedir(), "go", "pkg", "mod")], + getDependencyPaths: async () => [join(os.homedir(), "go", "pkg", "mod")], getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]), }, }; @@ -289,7 +324,7 @@ export async function downloadDependencyCaches( const start = performance.now(); const hitKey = await actionsCache.restoreCache( - cacheConfig.getDependencyPaths(), + await cacheConfig.getDependencyPaths(codeql, features), primaryKey, restoreKeys, ); @@ -408,7 +443,7 @@ export async function uploadDependencyCaches( // with the dependency caches. For this, we could use the Cache API to check whether other workflows // are using the quota and how full it is. const size = await getTotalCacheSize( - cacheConfig.getDependencyPaths(), + await cacheConfig.getDependencyPaths(codeql, features), logger, true, ); @@ -428,7 +463,10 @@ export async function uploadDependencyCaches( try { const start = performance.now(); - await actionsCache.saveCache(cacheConfig.getDependencyPaths(), key); + await actionsCache.saveCache( + await cacheConfig.getDependencyPaths(codeql, features), + key, + ); const upload_duration_ms = Math.round(performance.now() - start); status.push({ @@ -516,6 +554,7 @@ export async function getFeaturePrefix( } } else if (language === KnownLanguage.csharp) { await addFeatureIfEnabled(Feature.CsharpNewCacheKey); + await addFeatureIfEnabled(Feature.CsharpCacheBuildModeNone); } // If any features that affect the cache are enabled, return a feature prefix by diff --git a/src/feature-flags.ts b/src/feature-flags.ts index 10e2e296c3..8ea1d4c1a4 100644 --- a/src/feature-flags.ts +++ b/src/feature-flags.ts @@ -47,6 +47,7 @@ export enum Feature { AnalyzeUseNewUpload = "analyze_use_new_upload", CleanupTrapCaches = "cleanup_trap_caches", CppDependencyInstallation = "cpp_dependency_installation_enabled", + CsharpCacheBuildModeNone = "csharp_cache_bmn", CsharpNewCacheKey = "csharp_new_cache_key", DiffInformedQueries = "diff_informed_queries", DisableCsharpBuildless = "disable_csharp_buildless", @@ -134,6 +135,11 @@ export const featureConfig: Record< legacyApi: true, minimumVersion: "2.15.0", }, + [Feature.CsharpCacheBuildModeNone]: { + defaultValue: false, + envVar: "CODEQL_ACTION_CSHARP_CACHE_BMN", + minimumVersion: undefined, + }, [Feature.CsharpNewCacheKey]: { defaultValue: false, envVar: "CODEQL_ACTION_CSHARP_NEW_CACHE_KEY",