From 6b4820790706215b5fb892fa241d5d1ea765d388 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Wed, 17 Sep 2025 15:26:20 +0100 Subject: [PATCH] Move check whether there are files for hashing into `getHashPatterns` --- lib/analyze-action.js | 51 +++++++++++---- lib/init-action.js | 50 ++++++++++---- src/dependency-caching.ts | 134 ++++++++++++++++++++++++++++---------- 3 files changed, 176 insertions(+), 59 deletions(-) diff --git a/lib/analyze-action.js b/lib/analyze-action.js index e6770f089..88c0c2ec6 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -91052,6 +91052,12 @@ var os3 = __toESM(require("os")); var import_path = require("path"); var actionsCache3 = __toESM(require_cache3()); var glob = __toESM(require_glob2()); +var NoMatchingFilesError = class extends Error { + constructor(msg) { + super(msg); + this.name = "NoMatchingFilesError"; + } +}; var CODEQL_DEPENDENCY_CACHE_PREFIX = "codeql-dependencies"; var CODEQL_DEPENDENCY_CACHE_VERSION = 1; function getJavaTempDependencyDir() { @@ -91067,10 +91073,17 @@ function getJavaDependencyDirs() { getJavaTempDependencyDir() ]; } +async function makePatternCheck(patterns) { + const globber = await makeGlobber(patterns); + if ((await globber.glob()).length === 0) { + throw new NoMatchingFilesError(); + } + return patterns; +} var defaultCacheConfigs = { java: { getDependencyPaths: getJavaDependencyDirs, - getHashPatterns: async () => [ + getHashPatterns: async () => makePatternCheck([ // Maven "**/pom.xml", // Gradle @@ -91080,25 +91093,38 @@ var defaultCacheConfigs = { "buildSrc/**/Dependencies.kt", "gradle/*.versions.toml", "**/versions.properties" - ] + ]) }, csharp: { getDependencyPaths: () => [(0, import_path.join)(os3.homedir(), ".nuget", "packages")], - getHashPatterns: async () => [ + getHashPatterns: async () => makePatternCheck([ // NuGet "**/packages.lock.json", // Paket "**/paket.lock" - ] + ]) }, go: { getDependencyPaths: () => [(0, import_path.join)(os3.homedir(), "go", "pkg", "mod")], - getHashPatterns: async () => ["**/go.sum"] + getHashPatterns: async () => makePatternCheck(["**/go.sum"]) } }; async function makeGlobber(patterns) { return glob.create(patterns.join("\n")); } +async function checkHashPatterns(codeql, features, language, cacheConfig, logger) { + try { + return cacheConfig.getHashPatterns(codeql, features); + } catch (err) { + if (err instanceof NoMatchingFilesError) { + logger.info( + `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.` + ); + return void 0; + } + throw err; + } +} async function uploadDependencyCaches(codeql, features, config, logger) { const status = []; for (const language of config.languages) { @@ -91109,13 +91135,14 @@ async function uploadDependencyCaches(codeql, features, config, logger) { ); continue; } - const patterns = await cacheConfig.getHashPatterns(codeql, features); - const globber = await makeGlobber(patterns); - if ((await globber.glob()).length === 0) { - status.push({ language, result: "no-hash" /* NoHash */ }); - logger.info( - `Skipping upload of dependency cache for ${language} as we cannot calculate a hash for the cache key.` - ); + const patterns = await checkHashPatterns( + codeql, + features, + language, + cacheConfig, + logger + ); + if (patterns === void 0) { continue; } const size = await getTotalCacheSize( diff --git a/lib/init-action.js b/lib/init-action.js index 648ffd01f..a8d951d20 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87239,6 +87239,12 @@ var os2 = __toESM(require("os")); var import_path = require("path"); var actionsCache3 = __toESM(require_cache3()); var glob = __toESM(require_glob2()); +var NoMatchingFilesError = class extends Error { + constructor(msg) { + super(msg); + this.name = "NoMatchingFilesError"; + } +}; var CODEQL_DEPENDENCY_CACHE_PREFIX = "codeql-dependencies"; var CODEQL_DEPENDENCY_CACHE_VERSION = 1; function getJavaTempDependencyDir() { @@ -87254,10 +87260,17 @@ function getJavaDependencyDirs() { getJavaTempDependencyDir() ]; } +async function makePatternCheck(patterns) { + const globber = await makeGlobber(patterns); + if ((await globber.glob()).length === 0) { + throw new NoMatchingFilesError(); + } + return patterns; +} var defaultCacheConfigs = { java: { getDependencyPaths: getJavaDependencyDirs, - getHashPatterns: async () => [ + getHashPatterns: async () => makePatternCheck([ // Maven "**/pom.xml", // Gradle @@ -87267,25 +87280,38 @@ var defaultCacheConfigs = { "buildSrc/**/Dependencies.kt", "gradle/*.versions.toml", "**/versions.properties" - ] + ]) }, csharp: { getDependencyPaths: () => [(0, import_path.join)(os2.homedir(), ".nuget", "packages")], - getHashPatterns: async () => [ + getHashPatterns: async () => makePatternCheck([ // NuGet "**/packages.lock.json", // Paket "**/paket.lock" - ] + ]) }, go: { getDependencyPaths: () => [(0, import_path.join)(os2.homedir(), "go", "pkg", "mod")], - getHashPatterns: async () => ["**/go.sum"] + getHashPatterns: async () => makePatternCheck(["**/go.sum"]) } }; async function makeGlobber(patterns) { return glob.create(patterns.join("\n")); } +async function checkHashPatterns(codeql, features, language, cacheConfig, logger) { + try { + return cacheConfig.getHashPatterns(codeql, features); + } catch (err) { + if (err instanceof NoMatchingFilesError) { + logger.info( + `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.` + ); + return void 0; + } + throw err; + } +} async function downloadDependencyCaches(codeql, features, languages, logger) { const status = []; for (const language of languages) { @@ -87296,13 +87322,15 @@ async function downloadDependencyCaches(codeql, features, languages, logger) { ); continue; } - const patterns = await cacheConfig.getHashPatterns(codeql, features); - const globber = await makeGlobber(patterns); - if ((await globber.glob()).length === 0) { + const patterns = await checkHashPatterns( + codeql, + features, + language, + cacheConfig, + logger + ); + if (patterns === void 0) { status.push({ language, hit_kind: "no-hash" /* NoHash */ }); - logger.info( - `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.` - ); continue; } const primaryKey = await cacheKey2(codeql, features, language, patterns); diff --git a/src/dependency-caching.ts b/src/dependency-caching.ts index 1aef3f088..b852fa0d4 100644 --- a/src/dependency-caching.ts +++ b/src/dependency-caching.ts @@ -15,6 +15,14 @@ import { KnownLanguage, Language } from "./languages"; import { Logger } from "./logging"; import { getErrorMessage, getRequiredEnvParam } from "./util"; +class NoMatchingFilesError extends Error { + constructor(msg?: string) { + super(msg); + + this.name = "NoMatchingFilesError"; + } +} + /** * Caching configuration for a particular language. */ @@ -22,9 +30,12 @@ interface CacheConfig { /** Gets the paths of directories on the runner that should be included in the cache. */ getDependencyPaths: () => string[]; /** - * Gets patterns for the paths of files whose contents affect which dependencies are used - * by a project. We find all files which match these patterns, calculate a hash for - * their contents, and use that hash as part of the cache key. + * 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 throws + * a `NoMatchingFilesError` error if no files match. + * + * The glob patterns are intended to be used for cache keys, where we find all files which match these + * patterns, calculate a hash for their contents, and use that hash as part of the cache key. */ getHashPatterns: (codeql: CodeQL, features: Features) => Promise; } @@ -60,36 +71,55 @@ export function getJavaDependencyDirs(): string[] { ]; } +/** + * Checks that there are files which match `patterns`. If there are matching files for any of the patterns, + * this function returns all `patterns`. Otherwise, a `NoMatchingFilesError` is thrown. + * + * @param patterns The glob patterns to find matching files for. + * @returns The array of glob patterns if there are matching files. + */ +async function makePatternCheck(patterns: string[]): Promise { + const globber = await makeGlobber(patterns); + + if ((await globber.glob()).length === 0) { + throw new NoMatchingFilesError(); + } + + return patterns; +} + /** * Default caching configurations per language. */ const defaultCacheConfigs: { [language: string]: CacheConfig } = { java: { getDependencyPaths: getJavaDependencyDirs, - getHashPatterns: async () => [ - // Maven - "**/pom.xml", - // Gradle - "**/*.gradle*", - "**/gradle-wrapper.properties", - "buildSrc/**/Versions.kt", - "buildSrc/**/Dependencies.kt", - "gradle/*.versions.toml", - "**/versions.properties", - ], + getHashPatterns: async () => + makePatternCheck([ + // Maven + "**/pom.xml", + // Gradle + "**/*.gradle*", + "**/gradle-wrapper.properties", + "buildSrc/**/Versions.kt", + "buildSrc/**/Dependencies.kt", + "gradle/*.versions.toml", + "**/versions.properties", + ]), }, csharp: { getDependencyPaths: () => [join(os.homedir(), ".nuget", "packages")], - getHashPatterns: async () => [ - // NuGet - "**/packages.lock.json", - // Paket - "**/paket.lock", - ], + getHashPatterns: async () => + makePatternCheck([ + // NuGet + "**/packages.lock.json", + // Paket + "**/paket.lock", + ]), }, go: { getDependencyPaths: () => [join(os.homedir(), "go", "pkg", "mod")], - getHashPatterns: async () => ["**/go.sum"], + getHashPatterns: async () => makePatternCheck(["**/go.sum"]), }, }; @@ -119,6 +149,37 @@ export interface DependencyCacheRestoreStatus { /** An array of `DependencyCacheRestoreStatus` objects for each analysed language with a caching configuration. */ export type DependencyCacheRestoreStatusReport = DependencyCacheRestoreStatus[]; +/** + * A wrapper around `cacheConfig.getHashPatterns` which catches `NoMatchingFilesError` errors, + * and logs that there are no files to calculate a hash for the cache key from. + * + * @param codeql The CodeQL instance to use. + * @param features Information about which FFs are enabled. + * @param language The language the `CacheConfig` is for. For use in the log message. + * @param cacheConfig The caching configuration to call `getHashPatterns` on. + * @param logger The logger to write the log message to if there is an error. + * @returns An array of glob patterns to use for hashing files, or `undefined` if there are no matching files. + */ +async function checkHashPatterns( + codeql: CodeQL, + features: Features, + language: Language, + cacheConfig: CacheConfig, + logger: Logger, +): Promise { + try { + return cacheConfig.getHashPatterns(codeql, features); + } catch (err) { + if (err instanceof NoMatchingFilesError) { + logger.info( + `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`, + ); + return undefined; + } + throw err; + } +} + /** * Attempts to restore dependency caches for the languages being analyzed. * @@ -149,14 +210,15 @@ export async function downloadDependencyCaches( // Check that we can find files to calculate the hash for the cache key from, so we don't end up // with an empty string. - const patterns = await cacheConfig.getHashPatterns(codeql, features); - const globber = await makeGlobber(patterns); - - if ((await globber.glob()).length === 0) { + const patterns = await checkHashPatterns( + codeql, + features, + language, + cacheConfig, + logger, + ); + if (patterns === undefined) { status.push({ language, hit_kind: CacheHitKind.NoHash }); - logger.info( - `Skipping download of dependency cache for ${language} as we cannot calculate a hash for the cache key.`, - ); continue; } @@ -245,14 +307,14 @@ export async function uploadDependencyCaches( // Check that we can find files to calculate the hash for the cache key from, so we don't end up // with an empty string. - const patterns = await cacheConfig.getHashPatterns(codeql, features); - const globber = await makeGlobber(patterns); - - if ((await globber.glob()).length === 0) { - status.push({ language, result: CacheStoreResult.NoHash }); - logger.info( - `Skipping upload of dependency cache for ${language} as we cannot calculate a hash for the cache key.`, - ); + const patterns = await checkHashPatterns( + codeql, + features, + language, + cacheConfig, + logger, + ); + if (patterns === undefined) { continue; }