diff --git a/lib/analyze-action-post.js b/lib/analyze-action-post.js index b30ab9f097..323549b8b1 100644 --- a/lib/analyze-action-post.js +++ b/lib/analyze-action-post.js @@ -119928,6 +119928,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", @@ -120763,6 +120768,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) { @@ -120883,14 +120891,19 @@ async function runWrapper() { ); } } - const javaTempDependencyDir = getJavaTempDependencyDir(); - if (fs6.existsSync(javaTempDependencyDir)) { - try { - fs6.rmSync(javaTempDependencyDir, { recursive: true }); - } catch (error4) { - logger.info( - `Failed to remove temporary Java dependencies directory: ${getErrorMessage(error4)}` - ); + const tempDependencyDirs = [ + getJavaTempDependencyDir(), + getCsharpTempDependencyDir() + ]; + for (const tempDependencyDir of tempDependencyDirs) { + if (fs6.existsSync(tempDependencyDir)) { + try { + fs6.rmSync(tempDependencyDir, { recursive: true }); + } catch (error4) { + logger.info( + `Failed to remove temporary dependencies directory: ${getErrorMessage(error4)}` + ); + } } } } catch (error4) { diff --git a/lib/analyze-action.js b/lib/analyze-action.js index fd43a2f778..e5e897ca38 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -88658,6 +88658,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", @@ -91059,7 +91064,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"), @@ -91069,6 +91074,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) { @@ -91113,11 +91131,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"]) } }; @@ -91156,7 +91174,7 @@ async function uploadDependencyCaches(codeql, features, config, logger) { continue; } const size = await getTotalCacheSize( - cacheConfig.getDependencyPaths(), + await cacheConfig.getDependencyPaths(codeql, features), logger, true ); @@ -91173,7 +91191,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, @@ -91216,6 +91237,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)}-`; @@ -91305,7 +91327,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( @@ -91325,6 +91347,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); @@ -91350,9 +91375,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) { @@ -91607,7 +91632,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 (error4) { @@ -91618,6 +91643,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, @@ -93952,6 +93978,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 d09fe07d82..48485a850d 100644 --- a/lib/autobuild-action.js +++ b/lib/autobuild-action.js @@ -83977,6 +83977,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 1d3c4d5d93..b15b16f377 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -123309,6 +123309,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 98c23c88fd..8d63c95de0 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -86072,6 +86072,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", @@ -87246,7 +87251,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"), @@ -87256,6 +87261,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) { @@ -87300,11 +87318,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"]) } }; @@ -87353,7 +87371,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 ); @@ -87390,6 +87408,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 7918ab61f7..766a59c171 100644 --- a/lib/resolve-environment-action.js +++ b/lib/resolve-environment-action.js @@ -83968,6 +83968,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 c9e95730bf..f00d601e7d 100644 --- a/lib/setup-codeql-action.js +++ b/lib/setup-codeql-action.js @@ -83880,6 +83880,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 2386e7c27b..85e0aaeb08 100644 --- a/lib/start-proxy-action-post.js +++ b/lib/start-proxy-action-post.js @@ -119334,6 +119334,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 281341e5aa..5613603cb9 100644 --- a/lib/start-proxy-action.js +++ b/lib/start-proxy-action.js @@ -99996,6 +99996,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 2e980ba467..a0d12250c7 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 1d2a3a44b3..1d63547086 100644 --- a/lib/upload-sarif-action-post.js +++ b/lib/upload-sarif-action-post.js @@ -119500,6 +119500,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 6fd196c32e..1266953f0c 100644 --- a/lib/upload-sarif-action.js +++ b/lib/upload-sarif-action.js @@ -86830,6 +86830,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 3ab1dd1321..0349c13c30 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 eefb8504cd..bf2f7ba74d 100644 --- a/src/dependency-caching.test.ts +++ b/src/dependency-caching.test.ts @@ -20,6 +20,8 @@ import { downloadDependencyCaches, CacheHitKind, cacheKey, + getCsharpDependencyDirs, + getCsharpTempDependencyDir, } from "./dependency-caching"; import { Feature } from "./feature-flags"; import { KnownLanguage } from "./languages"; @@ -38,6 +40,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"), ""); @@ -126,7 +150,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, }; @@ -155,7 +179,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), }; @@ -387,3 +411,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 220f1d5bab..bd39bad751 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. */ -export function getJavaDependencyDirs(): string[] { +export async function getJavaDependencyDirs(): Promise { return [ // Maven join(os.homedir(), ".m2", "repository"), @@ -66,6 +69,38 @@ export 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"]), }, }; @@ -280,7 +315,7 @@ export async function downloadDependencyCaches( const start = performance.now(); const hitKey = await actionsCache.restoreCache( - cacheConfig.getDependencyPaths(), + await cacheConfig.getDependencyPaths(codeql, features), primaryKey, restoreKeys, ); @@ -376,7 +411,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, ); @@ -398,7 +433,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({ @@ -486,6 +524,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 1334969795..27a3c0f4f7 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", @@ -133,6 +134,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",